forked from mirrors/linux
		
	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
					
				
					 1 changed files with 61 additions and 40 deletions
				
			
		| 
						 | 
					@ -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,12 +460,12 @@ 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 @@ 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;
 | 
				
			||||||
 | 
							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,
 | 
								cpuhp_setup_state(CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING,
 | 
				
			||||||
					  "irqchip/sifive/plic:starting",
 | 
										  "irqchip/sifive/plic:starting",
 | 
				
			||||||
					  plic_starting_cpu, plic_dying_cpu);
 | 
										  plic_starting_cpu, plic_dying_cpu);
 | 
				
			||||||
			register_syscore_ops(&plic_irq_syscore_ops);
 | 
								register_syscore_ops(&plic_irq_syscore_ops);
 | 
				
			||||||
			plic_cpuhp_setup_done = true;
 | 
								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,
 | 
				
			||||||
	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…
	
		Reference in a new issue