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: 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
This commit is contained in:
parent
129993da1b
commit
8ec99b0331
@ -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)
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
|
|
||||||
/* 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 @@ done:
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 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 @@ out_free_priv:
|
|||||||
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);
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user