Commit 8ec99b03 authored by Anup Patel's avatar Anup Patel Committed by Thomas Gleixner

irqchip/sifive-plic: Convert PLIC driver into a platform driver

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: default avatarAnup Patel <apatel@ventanamicro.com>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/r/20240222094006.1030709-2-apatel@ventanamicro.com
parent 129993da
...@@ -64,6 +64,7 @@ ...@@ -64,6 +64,7 @@
#define PLIC_QUIRK_EDGE_INTERRUPT 0 #define PLIC_QUIRK_EDGE_INTERRUPT 0
struct plic_priv { struct plic_priv {
struct device *dev;
struct cpumask lmask; struct cpumask lmask;
struct irq_domain *irqdomain; struct irq_domain *irqdomain;
void __iomem *regs; void __iomem *regs;
...@@ -406,30 +407,50 @@ static int plic_starting_cpu(unsigned int cpu) ...@@ -406,30 +407,50 @@ static int plic_starting_cpu(unsigned int cpu)
return 0; return 0;
} }
static int __init __plic_init(struct device_node *node, static const struct of_device_id plic_match[] = {
struct device_node *parent, { .compatible = "sifive,plic-1.0.0" },
unsigned long plic_quirks) { .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; int error = 0, nr_contexts, nr_handlers = 0, i;
u32 nr_irqs; struct device *dev = &pdev->dev;
struct plic_priv *priv; unsigned long plic_quirks = 0;
struct plic_handler *handler; struct plic_handler *handler;
struct plic_priv *priv;
bool cpuhp_setup;
unsigned int cpu; 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); priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) if (!priv)
return -ENOMEM; return -ENOMEM;
priv->dev = dev;
priv->plic_quirks = plic_quirks; 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)) { if (WARN_ON(!priv->regs)) {
error = -EIO; error = -EIO;
goto out_free_priv; goto out_free_priv;
} }
error = -EINVAL; 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)) if (WARN_ON(!nr_irqs))
goto out_iounmap; goto out_iounmap;
...@@ -439,13 +460,13 @@ static int __init __plic_init(struct device_node *node, ...@@ -439,13 +460,13 @@ static int __init __plic_init(struct device_node *node,
if (!priv->prio_save) if (!priv->prio_save)
goto out_free_priority_reg; 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)) if (WARN_ON(!nr_contexts))
goto out_free_priority_reg; goto out_free_priority_reg;
error = -ENOMEM; error = -ENOMEM;
priv->irqdomain = irq_domain_add_linear(node, nr_irqs + 1, priv->irqdomain = irq_domain_add_linear(to_of_node(dev->fwnode), nr_irqs + 1,
&plic_irqdomain_ops, priv); &plic_irqdomain_ops, priv);
if (WARN_ON(!priv->irqdomain)) if (WARN_ON(!priv->irqdomain))
goto out_free_priority_reg; goto out_free_priority_reg;
...@@ -455,7 +476,7 @@ static int __init __plic_init(struct device_node *node, ...@@ -455,7 +476,7 @@ static int __init __plic_init(struct device_node *node,
int cpu; int cpu;
unsigned long hartid; 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); pr_err("failed to parse parent for context %d.\n", i);
continue; continue;
} }
...@@ -491,7 +512,7 @@ static int __init __plic_init(struct device_node *node, ...@@ -491,7 +512,7 @@ static int __init __plic_init(struct device_node *node,
/* Find parent domain and register chained handler */ /* Find parent domain and register chained handler */
if (!plic_parent_irq && irq_find_host(parent.np)) { 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) if (plic_parent_irq)
irq_set_chained_handler(plic_parent_irq, irq_set_chained_handler(plic_parent_irq,
plic_handle_irq); plic_handle_irq);
...@@ -533,20 +554,29 @@ static int __init __plic_init(struct device_node *node, ...@@ -533,20 +554,29 @@ static int __init __plic_init(struct device_node *node,
/* /*
* We can have multiple PLIC instances so setup cpuhp state * We can have multiple PLIC instances so setup cpuhp state
* and register syscore operations only when context handler * and register syscore operations only once after context
* for current/boot CPU is present. * handlers of all online CPUs are initialized.
*/ */
handler = this_cpu_ptr(&plic_handlers); if (!plic_cpuhp_setup_done) {
if (handler->present && !plic_cpuhp_setup_done) { cpuhp_setup = true;
cpuhp_setup_state(CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING, for_each_online_cpu(cpu) {
"irqchip/sifive/plic:starting", handler = per_cpu_ptr(&plic_handlers, cpu);
plic_starting_cpu, plic_dying_cpu); if (!handler->present) {
register_syscore_ops(&plic_irq_syscore_ops); cpuhp_setup = false;
plic_cpuhp_setup_done = true; 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" pr_info("%pOFP: mapped %d interrupts with %d handlers for %d contexts.\n",
" %d contexts.\n", node, nr_irqs, nr_handlers, nr_contexts); to_of_node(dev->fwnode), nr_irqs, nr_handlers, nr_contexts);
return 0; return 0;
out_free_enable_reg: out_free_enable_reg:
...@@ -563,20 +593,11 @@ static int __init __plic_init(struct device_node *node, ...@@ -563,20 +593,11 @@ static int __init __plic_init(struct device_node *node,
return error; return error;
} }
static int __init plic_init(struct device_node *node, static struct platform_driver plic_driver = {
struct device_node *parent) .driver = {
{ .name = "riscv-plic",
return __plic_init(node, parent, 0); .of_match_table = plic_match,
} },
.probe = plic_probe,
IRQCHIP_DECLARE(sifive_plic, "sifive,plic-1.0.0", plic_init); };
IRQCHIP_DECLARE(riscv_plic0, "riscv,plic0", plic_init); /* for legacy systems */ builtin_platform_driver(plic_driver);
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);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment