irqchip/sifive-plic: Convert PLIC driver into a platform driver
authorAnup Patel <apatel@ventanamicro.com>
Thu, 22 Feb 2024 09:39:49 +0000 (15:09 +0530)
committerThomas Gleixner <tglx@linutronix.de>
Fri, 23 Feb 2024 09:18:43 +0000 (10:18 +0100)
The PLIC driver does not require very early initialization so convert
it into a platform driver.

After conversion, the PLIC driver is probed after CPUs are brought-up
so setup cpuhp state after context handler of all online CPUs are
initialized otherwise PLIC driver crashes for platforms with multiple
PLIC instances.

Signed-off-by: Anup Patel <apatel@ventanamicro.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/r/20240222094006.1030709-2-apatel@ventanamicro.com
drivers/irqchip/irq-sifive-plic.c

index 5b7bc4fd9517c8972680ad7a503eebf2ca47a518..7400a07fc4792705bee5dac463403167c74d8722 100644 (file)
@@ -64,6 +64,7 @@
 #define PLIC_QUIRK_EDGE_INTERRUPT      0
 
 struct plic_priv {
+       struct device *dev;
        struct cpumask lmask;
        struct irq_domain *irqdomain;
        void __iomem *regs;
@@ -406,30 +407,50 @@ static int plic_starting_cpu(unsigned int cpu)
        return 0;
 }
 
-static int __init __plic_init(struct device_node *node,
-                             struct device_node *parent,
-                             unsigned long plic_quirks)
+static const struct of_device_id plic_match[] = {
+       { .compatible = "sifive,plic-1.0.0" },
+       { .compatible = "riscv,plic0" },
+       { .compatible = "andestech,nceplic100",
+         .data = (const void *)BIT(PLIC_QUIRK_EDGE_INTERRUPT) },
+       { .compatible = "thead,c900-plic",
+         .data = (const void *)BIT(PLIC_QUIRK_EDGE_INTERRUPT) },
+       {}
+};
+
+static int plic_probe(struct platform_device *pdev)
 {
        int error = 0, nr_contexts, nr_handlers = 0, i;
-       u32 nr_irqs;
-       struct plic_priv *priv;
+       struct device *dev = &pdev->dev;
+       unsigned long plic_quirks = 0;
        struct plic_handler *handler;
+       struct plic_priv *priv;
+       bool cpuhp_setup;
        unsigned int cpu;
+       u32 nr_irqs;
+
+       if (is_of_node(dev->fwnode)) {
+               const struct of_device_id *id;
+
+               id = of_match_node(plic_match, to_of_node(dev->fwnode));
+               if (id)
+                       plic_quirks = (unsigned long)id->data;
+       }
 
        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;
 
+       priv->dev = dev;
        priv->plic_quirks = plic_quirks;
 
-       priv->regs = of_iomap(node, 0);
+       priv->regs = of_iomap(to_of_node(dev->fwnode), 0);
        if (WARN_ON(!priv->regs)) {
                error = -EIO;
                goto out_free_priv;
        }
 
        error = -EINVAL;
-       of_property_read_u32(node, "riscv,ndev", &nr_irqs);
+       of_property_read_u32(to_of_node(dev->fwnode), "riscv,ndev", &nr_irqs);
        if (WARN_ON(!nr_irqs))
                goto out_iounmap;
 
@@ -439,13 +460,13 @@ static int __init __plic_init(struct device_node *node,
        if (!priv->prio_save)
                goto out_free_priority_reg;
 
-       nr_contexts = of_irq_count(node);
+       nr_contexts = of_irq_count(to_of_node(dev->fwnode));
        if (WARN_ON(!nr_contexts))
                goto out_free_priority_reg;
 
        error = -ENOMEM;
-       priv->irqdomain = irq_domain_add_linear(node, nr_irqs + 1,
-                       &plic_irqdomain_ops, priv);
+       priv->irqdomain = irq_domain_add_linear(to_of_node(dev->fwnode), nr_irqs + 1,
+                                               &plic_irqdomain_ops, priv);
        if (WARN_ON(!priv->irqdomain))
                goto out_free_priority_reg;
 
@@ -455,7 +476,7 @@ static int __init __plic_init(struct device_node *node,
                int cpu;
                unsigned long hartid;
 
-               if (of_irq_parse_one(node, i, &parent)) {
+               if (of_irq_parse_one(to_of_node(dev->fwnode), i, &parent)) {
                        pr_err("failed to parse parent for context %d.\n", i);
                        continue;
                }
@@ -491,7 +512,7 @@ static int __init __plic_init(struct device_node *node,
 
                /* Find parent domain and register chained handler */
                if (!plic_parent_irq && irq_find_host(parent.np)) {
-                       plic_parent_irq = irq_of_parse_and_map(node, i);
+                       plic_parent_irq = irq_of_parse_and_map(to_of_node(dev->fwnode), i);
                        if (plic_parent_irq)
                                irq_set_chained_handler(plic_parent_irq,
                                                        plic_handle_irq);
@@ -533,20 +554,29 @@ done:
 
        /*
         * We can have multiple PLIC instances so setup cpuhp state
-        * and register syscore operations only when context handler
-        * for current/boot CPU is present.
+        * and register syscore operations only once after context
+        * handlers of all online CPUs are initialized.
         */
-       handler = this_cpu_ptr(&plic_handlers);
-       if (handler->present && !plic_cpuhp_setup_done) {
-               cpuhp_setup_state(CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING,
-                                 "irqchip/sifive/plic:starting",
-                                 plic_starting_cpu, plic_dying_cpu);
-               register_syscore_ops(&plic_irq_syscore_ops);
-               plic_cpuhp_setup_done = true;
+       if (!plic_cpuhp_setup_done) {
+               cpuhp_setup = true;
+               for_each_online_cpu(cpu) {
+                       handler = per_cpu_ptr(&plic_handlers, cpu);
+                       if (!handler->present) {
+                               cpuhp_setup = false;
+                               break;
+                       }
+               }
+               if (cpuhp_setup) {
+                       cpuhp_setup_state(CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING,
+                                         "irqchip/sifive/plic:starting",
+                                         plic_starting_cpu, plic_dying_cpu);
+                       register_syscore_ops(&plic_irq_syscore_ops);
+                       plic_cpuhp_setup_done = true;
+               }
        }
 
-       pr_info("%pOFP: mapped %d interrupts with %d handlers for"
-               " %d contexts.\n", node, nr_irqs, nr_handlers, nr_contexts);
+       pr_info("%pOFP: mapped %d interrupts with %d handlers for %d contexts.\n",
+               to_of_node(dev->fwnode), nr_irqs, nr_handlers, nr_contexts);
        return 0;
 
 out_free_enable_reg:
@@ -563,20 +593,11 @@ out_free_priv:
        return error;
 }
 
-static int __init plic_init(struct device_node *node,
-                           struct device_node *parent)
-{
-       return __plic_init(node, parent, 0);
-}
-
-IRQCHIP_DECLARE(sifive_plic, "sifive,plic-1.0.0", plic_init);
-IRQCHIP_DECLARE(riscv_plic0, "riscv,plic0", plic_init); /* for legacy systems */
-
-static int __init plic_edge_init(struct device_node *node,
-                                struct device_node *parent)
-{
-       return __plic_init(node, parent, BIT(PLIC_QUIRK_EDGE_INTERRUPT));
-}
-
-IRQCHIP_DECLARE(andestech_nceplic100, "andestech,nceplic100", plic_edge_init);
-IRQCHIP_DECLARE(thead_c900_plic, "thead,c900-plic", plic_edge_init);
+static struct platform_driver plic_driver = {
+       .driver = {
+               .name           = "riscv-plic",
+               .of_match_table = plic_match,
+       },
+       .probe = plic_probe,
+};
+builtin_platform_driver(plic_driver);