Skip to contentSkip to conversation

irqchip/sifive-plic: Probe plic driver early for Allwinner D1 platform

PATCH Superseded Anup Patel 3 participants 4 messages Aug 17, 2024 – Aug 19, 2024

Subsystems (3)

v2 of 3 Superseded Latest is v3 →

lore
Share & tools
lore

Anup Patel

author
#0 lore raw

PATCH v2 1/1 irqchip/sifive-plic: Probe plic driver early for Allwinner D1 platform

The latest Linux RISC-V no longer boots on the Allwinner D1 platform because the sun4i_timer driver fails to get an interrupt from PLIC.

The real fix requires enabling the SBI time extension in the platform firmware (OpenSBI) and convert sun4i_timer into platform driver. Unfortunately, the real fix involves changing multiple places and can't be achieved in a short duration.

As a work-around, retrofit plic probing such that plic is probed early only for the Allwinner D1 platform and probed as a regular platform driver for rest of the RISC-V platforms. In the process, partially revert some of the previous patches because PLIC device pointer is not available in all probing paths.

More detailed discussion can found here:
https://lore.kernel.org/lkml/[email protected]/

Fixes e306a894bd51 ("irqchip/sifive-plic: Chain to parent IRQ after handlers are ready")
Fixes 8ec99b033147 ("irqchip/sifive-plic: Convert PLIC driver into a platform driver")

---
Changes since v1:
- Set suppress_bind_attrs for PLIC platform driver

---
 drivers/irqchip/irq-sifive-plic.c | 128 +++++++++++++++++++-----------
 1 file changed, 80 insertions(+), 48 deletions(-)
drivers/irqchip/irq-sifive-plic.c +80 -48
index 9e22f7e378f5..33395c5a9b5b 100644
@@ -3,6 +3,7 @@
  * Copyright (C) 2017 SiFive
  * Copyright (C) 2018 Christoph Hellwig
  */
+#define pr_fmt(fmt) "riscv-plic: " fmt
 #include <linux/cpu.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
@@ -63,7 +64,7 @@
 #define PLIC_QUIRK_EDGE_INTERRUPT	0
 
 struct plic_priv {
-	struct device *dev;
+	struct fwnode_handle *fwnode;
 	struct cpumask lmask;
 	struct irq_domain *irqdomain;
 	void __iomem *regs;
@@ -378,8 +379,8 @@ static void plic_handle_irq(struct irq_desc *desc)
 		int err = generic_handle_domain_irq(handler->priv->irqdomain,
 						    hwirq);
 		if (unlikely(err)) {
-			dev_warn_ratelimited(handler->priv->dev,
-					     "can't find mapping for hwirq %lu\n", hwirq);
+			pr_warn_ratelimited("%pfwP: can't find mapping for hwirq %lu\n",
+					    handler->priv->fwnode, hwirq);
 		}
 	}
 
@@ -408,15 +409,14 @@ static int plic_starting_cpu(unsigned int cpu)
 		enable_percpu_irq(plic_parent_irq,
 				  irq_get_trigger_type(plic_parent_irq));
 	else
-		dev_warn(handler->priv->dev, "cpu%d: parent irq not available\n", cpu);
+		pr_warn("%pfwP: cpu%d: parent irq not available\n",
+			handler->priv->fwnode, cpu);
 	plic_set_threshold(handler, PLIC_ENABLE_THRESHOLD);
 
 	return 0;
 }
 
-static const struct of_device_id plic_match[] = {
-	{ .compatible = "sifive,plic-1.0.0" },
-	{ .compatible = "riscv,plic0" },
+static const struct of_device_id plic_quirks_match[] = {
 	{ .compatible = "andestech,nceplic100",
 	  .data = (const void *)BIT(PLIC_QUIRK_EDGE_INTERRUPT) },
 	{ .compatible = "thead,c900-plic",
@@ -424,38 +424,36 @@ static const struct of_device_id plic_match[] = {
 	{}
 };
 
-static int plic_parse_nr_irqs_and_contexts(struct platform_device *pdev,
+static int plic_parse_nr_irqs_and_contexts(struct fwnode_handle *fwnode,
 					   u32 *nr_irqs, u32 *nr_contexts)
 {
-	struct device *dev = &pdev->dev;
 	int rc;
 
 	/*
 	 * Currently, only OF fwnode is supported so extend this
 	 * function for ACPI support.
 	 */
-	if (!is_of_node(dev->fwnode))
+	if (!is_of_node(fwnode))
 		return -EINVAL;
 
-	rc = of_property_read_u32(to_of_node(dev->fwnode), "riscv,ndev", nr_irqs);
+	rc = of_property_read_u32(to_of_node(fwnode), "riscv,ndev", nr_irqs);
 	if (rc) {
-		dev_err(dev, "riscv,ndev property not available\n");
+		pr_err("%pfwP: riscv,ndev property not available\n", fwnode);
 		return rc;
 	}
 
-	*nr_contexts = of_irq_count(to_of_node(dev->fwnode));
+	*nr_contexts = of_irq_count(to_of_node(fwnode));
 	if (WARN_ON(!(*nr_contexts))) {
-		dev_err(dev, "no PLIC context available\n");
+		pr_err("%pfwP: no PLIC context available\n", fwnode);
 		return -EINVAL;
 	}
 
 	return 0;
 }
 
-static int plic_parse_context_parent(struct platform_device *pdev, u32 context,
+static int plic_parse_context_parent(struct fwnode_handle *fwnode, u32 context,
 				     u32 *parent_hwirq, int *parent_cpu)
 {
-	struct device *dev = &pdev->dev;
 	struct of_phandle_args parent;
 	unsigned long hartid;
 	int rc;
@@ -464,10 +462,10 @@ static int plic_parse_context_parent(struct platform_device *pdev, u32 context,
 	 * Currently, only OF fwnode is supported so extend this
 	 * function for ACPI support.
 	 */
-	if (!is_of_node(dev->fwnode))
+	if (!is_of_node(fwnode))
 		return -EINVAL;
 
-	rc = of_irq_parse_one(to_of_node(dev->fwnode), context, &parent);
+	rc = of_irq_parse_one(to_of_node(fwnode), context, &parent);
 	if (rc)
 		return rc;
 
@@ -480,48 +478,55 @@ static int plic_parse_context_parent(struct platform_device *pdev, u32 context,
 	return 0;
 }
 
-static int plic_probe(struct platform_device *pdev)
+static int plic_probe(struct fwnode_handle *fwnode)
 {
 	int error = 0, nr_contexts, nr_handlers = 0, cpu, i;
-	struct device *dev = &pdev->dev;
 	unsigned long plic_quirks = 0;
 	struct plic_handler *handler;
 	u32 nr_irqs, parent_hwirq;
 	struct plic_priv *priv;
 	irq_hw_number_t hwirq;
+	void __iomem *regs;
 
-	if (is_of_node(dev->fwnode)) {
+	if (is_of_node(fwnode)) {
 		const struct of_device_id *id;
 
-		id = of_match_node(plic_match, to_of_node(dev->fwnode));
+		id = of_match_node(plic_quirks_match, to_of_node(fwnode));
 		if (id)
 			plic_quirks = (unsigned long)id->data;
+
+		regs = of_iomap(to_of_node(fwnode), 0);
+		if (!regs)
+			return -ENOMEM;
+	} else {
+		return -ENODEV;
 	}
 
-	error = plic_parse_nr_irqs_and_contexts(pdev, &nr_irqs, &nr_contexts);
+	error = plic_parse_nr_irqs_and_contexts(fwnode, &nr_irqs, &nr_contexts);
 	if (error)
-		return error;
+		goto fail_free_regs;
 
-	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
-	if (!priv)
-		return -ENOMEM;
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		error = -ENOMEM;
+		goto fail_free_regs;
+	}
 
-	priv->dev = dev;
+	priv->fwnode = fwnode;
 	priv->plic_quirks = plic_quirks;
 	priv->nr_irqs = nr_irqs;
+	priv->regs = regs;
 
-	priv->regs = devm_platform_ioremap_resource(pdev, 0);
-	if (WARN_ON(!priv->regs))
-		return -EIO;
-
-	priv->prio_save = devm_bitmap_zalloc(dev, nr_irqs, GFP_KERNEL);
-	if (!priv->prio_save)
-		return -ENOMEM;
+	priv->prio_save = bitmap_zalloc(nr_irqs, GFP_KERNEL);
+	if (!priv->prio_save) {
+		error = -ENOMEM;
+		goto fail_free_priv;
+	}
 
 	for (i = 0; i < nr_contexts; i++) {
-		error = plic_parse_context_parent(pdev, i, &parent_hwirq, &cpu);
+		error = plic_parse_context_parent(fwnode, i, &parent_hwirq, &cpu);
 		if (error) {
-			dev_warn(dev, "hwirq for context%d not found\n", i);
+			pr_warn("%pfwP: hwirq for context%d not found\n", fwnode, i);
 			continue;
 		}
 
@@ -543,7 +548,7 @@ static int plic_probe(struct platform_device *pdev)
 		}
 
 		if (cpu < 0) {
-			dev_warn(dev, "Invalid cpuid for context %d\n", i);
+			pr_warn("%pfwP: Invalid cpuid for context %d\n", fwnode, i);
 			continue;
 		}
 
@@ -554,7 +559,7 @@ static int plic_probe(struct platform_device *pdev)
 		 */
 		handler = per_cpu_ptr(&plic_handlers, cpu);
 		if (handler->present) {
-			dev_warn(dev, "handler already present for context %d.\n", i);
+			pr_warn("%pfwP: handler already present for context %d.\n", fwnode, i);
 			plic_set_threshold(handler, PLIC_DISABLE_THRESHOLD);
 			goto done;
 		}
@@ -568,8 +573,8 @@ static int plic_probe(struct platform_device *pdev)
 			i * CONTEXT_ENABLE_SIZE;
 		handler->priv = priv;
 
-		handler->enable_save = devm_kcalloc(dev, DIV_ROUND_UP(nr_irqs, 32),
-						    sizeof(*handler->enable_save), GFP_KERNEL);
+		handler->enable_save = kcalloc(DIV_ROUND_UP(nr_irqs, 32),
+					       sizeof(*handler->enable_save), GFP_KERNEL);
 		if (!handler->enable_save)
 			goto fail_cleanup_contexts;
 done:
@@ -581,7 +586,7 @@ static int plic_probe(struct platform_device *pdev)
 		nr_handlers++;
 	}
 
-	priv->irqdomain = irq_domain_add_linear(to_of_node(dev->fwnode), nr_irqs + 1,
+	priv->irqdomain = irq_domain_add_linear(to_of_node(fwnode), nr_irqs + 1,
 						&plic_irqdomain_ops, priv);
 	if (WARN_ON(!priv->irqdomain))
 		goto fail_cleanup_contexts;
@@ -619,13 +624,13 @@ static int plic_probe(struct platform_device *pdev)
 		}
 	}
 
-	dev_info(dev, "mapped %d interrupts with %d handlers for %d contexts.\n",
-		 nr_irqs, nr_handlers, nr_contexts);
+	pr_info("%pfwP: mapped %d interrupts with %d handlers for %d contexts.\n",
+		fwnode, nr_irqs, nr_handlers, nr_contexts);
 	return 0;
 
 fail_cleanup_contexts:
 	for (i = 0; i < nr_contexts; i++) {
-		if (plic_parse_context_parent(pdev, i, &parent_hwirq, &cpu))
+		if (plic_parse_context_parent(fwnode, i, &parent_hwirq, &cpu))
 			continue;
 		if (parent_hwirq != RV_IRQ_EXT || cpu < 0)
 			continue;
@@ -634,17 +639,44 @@ static int plic_probe(struct platform_device *pdev)
 		handler->present = false;
 		handler->hart_base = NULL;
 		handler->enable_base = NULL;
+		kfree(handler->enable_save);
 		handler->enable_save = NULL;
 		handler->priv = NULL;
 	}
-	return -ENOMEM;
+	bitmap_free(priv->prio_save);
+fail_free_priv:
+	kfree(priv);
+fail_free_regs:
+	iounmap(regs);
+	return error;
+}
+
+static int plic_platform_probe(struct platform_device *pdev)
+{
+	return plic_probe(pdev->dev.fwnode);
 }
 
+static const struct of_device_id plic_platform_match[] = {
+	{ .compatible = "sifive,plic-1.0.0" },
+	{ .compatible = "riscv,plic0" },
+	{ .compatible = "andestech,nceplic100" },
+	{}
+};
+
 static struct platform_driver plic_driver = {
 	.driver = {
 		.name		= "riscv-plic",
-		.of_match_table	= plic_match,
+		.of_match_table	= plic_platform_match,
+		.suppress_bind_attrs = true,
 	},
-	.probe = plic_probe,
+	.probe = plic_platform_probe,
 };
 builtin_platform_driver(plic_driver);
+
+static int __init plic_early_probe(struct device_node *node,
+				   struct device_node *parent)
+{
+	return plic_probe(&node->fwnode);
+}
+
+IRQCHIP_DECLARE(riscv, "thead,c900-plic", plic_early_probe);
Signature · 1 line
2.34.1
Tested-by on v2 1/1
#1 lore raw
Quoted 160 lines · Anup Patel

The latest Linux RISC-V no longer boots on the Allwinner D1 platform because the sun4i_timer driver fails to get an interrupt from PLIC.

The real fix requires enabling the SBI time extension in the platform firmware (OpenSBI) and convert sun4i_timer into platform driver. Unfortunately, the real fix involves changing multiple places and can't be achieved in a short duration.

As a work-around, retrofit plic probing such that plic is probed early only for the Allwinner D1 platform and probed as a regular platform driver for rest of the RISC-V platforms. In the process, partially revert some of the previous patches because PLIC device pointer is not available in all probing paths.

More detailed discussion can found here:
https://lore.kernel.org/lkml/[email protected]/

Fixes e306a894bd51 ("irqchip/sifive-plic: Chain to parent IRQ after handlers are ready")
Fixes 8ec99b033147 ("irqchip/sifive-plic: Convert PLIC driver into a platform driver")

---
Changes since v1:
- Set suppress_bind_attrs for PLIC platform driver

---
 drivers/irqchip/irq-sifive-plic.c | 128 +++++++++++++++++++-----------
 1 file changed, 80 insertions(+), 48 deletions(-)
drivers/irqchip/irq-sifive-plic.c +19 -21
index 9e22f7e378f5..33395c5a9b5b 100644
@@ -3,6 +3,7 @@
  * Copyright (C) 2017 SiFive
  * Copyright (C) 2018 Christoph Hellwig
  */
+#define pr_fmt(fmt) "riscv-plic: " fmt
 #include <linux/cpu.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
@@ -63,7 +64,7 @@
 #define PLIC_QUIRK_EDGE_INTERRUPT	0
 
 struct plic_priv {
-	struct device *dev;
+	struct fwnode_handle *fwnode;
 	struct cpumask lmask;
 	struct irq_domain *irqdomain;
 	void __iomem *regs;
@@ -378,8 +379,8 @@ static void plic_handle_irq(struct irq_desc *desc)
 		int err = generic_handle_domain_irq(handler->priv->irqdomain,
 						    hwirq);
 		if (unlikely(err)) {
-			dev_warn_ratelimited(handler->priv->dev,
-					     "can't find mapping for hwirq %lu\n", hwirq);
+			pr_warn_ratelimited("%pfwP: can't find mapping for hwirq %lu\n",
+					    handler->priv->fwnode, hwirq);
 		}
 	}
 
@@ -408,15 +409,14 @@ static int plic_starting_cpu(unsigned int cpu)
 		enable_percpu_irq(plic_parent_irq,
 				  irq_get_trigger_type(plic_parent_irq));
 	else
-		dev_warn(handler->priv->dev, "cpu%d: parent irq not available\n", cpu);
+		pr_warn("%pfwP: cpu%d: parent irq not available\n",
+			handler->priv->fwnode, cpu);
 	plic_set_threshold(handler, PLIC_ENABLE_THRESHOLD);
 
 	return 0;
 }
 
-static const struct of_device_id plic_match[] = {
-	{ .compatible = "sifive,plic-1.0.0" },
-	{ .compatible = "riscv,plic0" },
+static const struct of_device_id plic_quirks_match[] = {
 	{ .compatible = "andestech,nceplic100",
 	  .data = (const void *)BIT(PLIC_QUIRK_EDGE_INTERRUPT) },
 	{ .compatible = "thead,c900-plic",
@@ -424,38 +424,36 @@ static const struct of_device_id plic_match[] = {
 	{}
 };
 
-static int plic_parse_nr_irqs_and_contexts(struct platform_device *pdev,
+static int plic_parse_nr_irqs_and_contexts(struct fwnode_handle *fwnode,
 					   u32 *nr_irqs, u32 *nr_contexts)
 {
-	struct device *dev = &pdev->dev;
 	int rc;
 
 	/*
 	 * Currently, only OF fwnode is supported so extend this
 	 * function for ACPI support.
 	 */
-	if (!is_of_node(dev->fwnode))
+	if (!is_of_node(fwnode))
 		return -EINVAL;
 
-	rc = of_property_read_u32(to_of_node(dev->fwnode), "riscv,ndev", nr_irqs);
+	rc = of_property_read_u32(to_of_node(fwnode), "riscv,ndev", nr_irqs);
 	if (rc) {
-		dev_err(dev, "riscv,ndev property not available\n");
+		pr_err("%pfwP: riscv,ndev property not available\n", fwnode);
 		return rc;
 	}
 
-	*nr_contexts = of_irq_count(to_of_node(dev->fwnode));
+	*nr_contexts = of_irq_count(to_of_node(fwnode));
 	if (WARN_ON(!(*nr_contexts))) {
-		dev_err(dev, "no PLIC context available\n");
+		pr_err("%pfwP: no PLIC context available\n", fwnode);
 		return -EINVAL;
 	}
 
 	return 0;
 }
 
-static int plic_parse_context_parent(struct platform_device *pdev, u32 context,
+static int plic_parse_context_parent(struct fwnode_handle *fwnode, u32 context,
 				     u32 *parent_hwirq, int *parent_cpu)
 {
-	struct device *dev = &pdev->dev;
 	struct of_phandle_args parent;
 	unsigned long hartid;
 	int rc;
@@ -464,10 +462,10 @@ static int plic_parse_context_parent(struct platform_device *pdev, u32 context,
 	 * Currently, only OF fwnode is supported so extend this
 	 * function for ACPI support.
 	 */
-	if (!is_of_node(dev->fwnode))
+	if (!is_of_node(fwnode))
 		return -EINVAL;
 
-	rc = of_irq_parse_one(to_of_node(dev->fwnode), context, &parent);
+	rc = of_irq_parse_one(to_of_node(fwnode), context, &parent);
 	if (rc)
 		return rc;
 
@@ -480,48 +478,55 @@ static int plic_parse_context_parent(struct platform_device *pdev, u32 context,
 	return 0;
 }
 
-static int plic_probe(struct platform_device *pdev)
+static int plic_probe(struct fwnode_handle *fwnode)
 {
 	int error = 0, nr_contexts, nr_handlers = 0, cpu, i;
-	struct device *dev = &pdev->dev;
 	unsigned long plic_quirks = 0;
 	struct plic_handler *handler;
 	u32 nr_irqs, parent_hwirq;
 	struct plic_priv *priv;
 	irq_hw_number_t hwirq;
+	void __iomem *regs;
 
-	if (is_of_node(dev->fwnode)) {
+	if (is_of_node(fwnode)) {
 		const struct of_device_id *id;
 

drivers/irqchip/irq-sifive-plic.c static int plic_parse_context_parent(struct platform_device *pdev, u32 context, · line 502

Quoted 10 lines
drivers/irqchip/irq-sifive-plic.c +7 -1
-		id = of_match_node(plic_match, to_of_node(dev->fwnode));
+		id = of_match_node(plic_quirks_match, to_of_node(fwnode));
 		if (id)
 			plic_quirks = (unsigned long)id->data;
+
+		regs = of_iomap(to_of_node(fwnode), 0);
+		if (!regs)
+			return -ENOMEM;
+	} else {
+		return -ENODEV;

This driver never worked with ACPI anyways?

Quoted 138 lines
drivers/irqchip/irq-sifive-plic.c +46 -26
 	}
 
-	error = plic_parse_nr_irqs_and_contexts(pdev, &nr_irqs, &nr_contexts);
+	error = plic_parse_nr_irqs_and_contexts(fwnode, &nr_irqs, &nr_contexts);
 	if (error)
-		return error;
+		goto fail_free_regs;
 
-	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
-	if (!priv)
-		return -ENOMEM;
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		error = -ENOMEM;
+		goto fail_free_regs;
+	}
 
-	priv->dev = dev;
+	priv->fwnode = fwnode;
 	priv->plic_quirks = plic_quirks;
 	priv->nr_irqs = nr_irqs;
+	priv->regs = regs;
 
-	priv->regs = devm_platform_ioremap_resource(pdev, 0);
-	if (WARN_ON(!priv->regs))
-		return -EIO;
-
-	priv->prio_save = devm_bitmap_zalloc(dev, nr_irqs, GFP_KERNEL);
-	if (!priv->prio_save)
-		return -ENOMEM;
+	priv->prio_save = bitmap_zalloc(nr_irqs, GFP_KERNEL);
+	if (!priv->prio_save) {
+		error = -ENOMEM;
+		goto fail_free_priv;
+	}
 
 	for (i = 0; i < nr_contexts; i++) {
-		error = plic_parse_context_parent(pdev, i, &parent_hwirq, &cpu);
+		error = plic_parse_context_parent(fwnode, i, &parent_hwirq, &cpu);
 		if (error) {
-			dev_warn(dev, "hwirq for context%d not found\n", i);
+			pr_warn("%pfwP: hwirq for context%d not found\n", fwnode, i);
 			continue;
 		}
 
@@ -543,7 +548,7 @@ static int plic_probe(struct platform_device *pdev)
 		}
 
 		if (cpu < 0) {
-			dev_warn(dev, "Invalid cpuid for context %d\n", i);
+			pr_warn("%pfwP: Invalid cpuid for context %d\n", fwnode, i);
 			continue;
 		}
 
@@ -554,7 +559,7 @@ static int plic_probe(struct platform_device *pdev)
 		 */
 		handler = per_cpu_ptr(&plic_handlers, cpu);
 		if (handler->present) {
-			dev_warn(dev, "handler already present for context %d.\n", i);
+			pr_warn("%pfwP: handler already present for context %d.\n", fwnode, i);
 			plic_set_threshold(handler, PLIC_DISABLE_THRESHOLD);
 			goto done;
 		}
@@ -568,8 +573,8 @@ static int plic_probe(struct platform_device *pdev)
 			i * CONTEXT_ENABLE_SIZE;
 		handler->priv = priv;
 
-		handler->enable_save = devm_kcalloc(dev, DIV_ROUND_UP(nr_irqs, 32),
-						    sizeof(*handler->enable_save), GFP_KERNEL);
+		handler->enable_save = kcalloc(DIV_ROUND_UP(nr_irqs, 32),
+					       sizeof(*handler->enable_save), GFP_KERNEL);
 		if (!handler->enable_save)
 			goto fail_cleanup_contexts;
 done:
@@ -581,7 +586,7 @@ static int plic_probe(struct platform_device *pdev)
 		nr_handlers++;
 	}
 
-	priv->irqdomain = irq_domain_add_linear(to_of_node(dev->fwnode), nr_irqs + 1,
+	priv->irqdomain = irq_domain_add_linear(to_of_node(fwnode), nr_irqs + 1,
 						&plic_irqdomain_ops, priv);
 	if (WARN_ON(!priv->irqdomain))
 		goto fail_cleanup_contexts;
@@ -619,13 +624,13 @@ static int plic_probe(struct platform_device *pdev)
 		}
 	}
 
-	dev_info(dev, "mapped %d interrupts with %d handlers for %d contexts.\n",
-		 nr_irqs, nr_handlers, nr_contexts);
+	pr_info("%pfwP: mapped %d interrupts with %d handlers for %d contexts.\n",
+		fwnode, nr_irqs, nr_handlers, nr_contexts);
 	return 0;
 
 fail_cleanup_contexts:
 	for (i = 0; i < nr_contexts; i++) {
-		if (plic_parse_context_parent(pdev, i, &parent_hwirq, &cpu))
+		if (plic_parse_context_parent(fwnode, i, &parent_hwirq, &cpu))
 			continue;
 		if (parent_hwirq != RV_IRQ_EXT || cpu < 0)
 			continue;
@@ -634,17 +639,44 @@ static int plic_probe(struct platform_device *pdev)
 		handler->present = false;
 		handler->hart_base = NULL;
 		handler->enable_base = NULL;
+		kfree(handler->enable_save);
 		handler->enable_save = NULL;
 		handler->priv = NULL;
 	}
-	return -ENOMEM;
+	bitmap_free(priv->prio_save);
+fail_free_priv:
+	kfree(priv);
+fail_free_regs:
+	iounmap(regs);
+	return error;
+}
+
+static int plic_platform_probe(struct platform_device *pdev)
+{
+	return plic_probe(pdev->dev.fwnode);
 }
 
+static const struct of_device_id plic_platform_match[] = {
+	{ .compatible = "sifive,plic-1.0.0" },
+	{ .compatible = "riscv,plic0" },
+	{ .compatible = "andestech,nceplic100" },
+	{}
+};
+
 static struct platform_driver plic_driver = {
 	.driver = {
 		.name		= "riscv-plic",
-		.of_match_table	= plic_match,
+		.of_match_table	= plic_platform_match,
+		.suppress_bind_attrs = true,
 	},
-	.probe = plic_probe,
+	.probe = plic_platform_probe,

drivers/irqchip/irq-sifive-plic.c static int plic_probe(struct platform_device *pdev) · line 682

Quoted 10 lines
drivers/irqchip/irq-sifive-plic.c +8 -0
 };
 builtin_platform_driver(plic_driver);
+
+static int __init plic_early_probe(struct device_node *node,
+				   struct device_node *parent)
+{
+	return plic_probe(&node->fwnode);
+}
+
+IRQCHIP_DECLARE(riscv, "thead,c900-plic", plic_early_probe);

If this is only needed on the Allwinner D1 maybe this should only match the more specific "allwinner,sun20i-d1-plic"?

In any case this works by itself, but not with Samuel's patch[1] applied, so

[1]: https://lore.kernel.org/r/[email protected]

_______________________________________________
linux-riscv mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-riscv

author
#2 lore raw
Quoted 327 lines · Emil Renner Berthing
Quoted 160 lines · Anup Patel

The latest Linux RISC-V no longer boots on the Allwinner D1 platform because the sun4i_timer driver fails to get an interrupt from PLIC.

The real fix requires enabling the SBI time extension in the platform firmware (OpenSBI) and convert sun4i_timer into platform driver. Unfortunately, the real fix involves changing multiple places and can't be achieved in a short duration.

As a work-around, retrofit plic probing such that plic is probed early only for the Allwinner D1 platform and probed as a regular platform driver for rest of the RISC-V platforms. In the process, partially revert some of the previous patches because PLIC device pointer is not available in all probing paths.

More detailed discussion can found here:
https://lore.kernel.org/lkml/[email protected]/

Fixes e306a894bd51 ("irqchip/sifive-plic: Chain to parent IRQ after handlers are ready")
Fixes 8ec99b033147 ("irqchip/sifive-plic: Convert PLIC driver into a platform driver")

---
Changes since v1:
- Set suppress_bind_attrs for PLIC platform driver

---
 drivers/irqchip/irq-sifive-plic.c | 128 +++++++++++++++++++-----------
 1 file changed, 80 insertions(+), 48 deletions(-)
drivers/irqchip/irq-sifive-plic.c +19 -21
index 9e22f7e378f5..33395c5a9b5b 100644
@@ -3,6 +3,7 @@
  * Copyright (C) 2017 SiFive
  * Copyright (C) 2018 Christoph Hellwig
  */
+#define pr_fmt(fmt) "riscv-plic: " fmt
 #include <linux/cpu.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
@@ -63,7 +64,7 @@
 #define PLIC_QUIRK_EDGE_INTERRUPT    0
 
 struct plic_priv {
-     struct device *dev;
+     struct fwnode_handle *fwnode;
      struct cpumask lmask;
      struct irq_domain *irqdomain;
      void __iomem *regs;
@@ -378,8 +379,8 @@ static void plic_handle_irq(struct irq_desc *desc)
              int err = generic_handle_domain_irq(handler->priv->irqdomain,
                                                  hwirq);
              if (unlikely(err)) {
-                     dev_warn_ratelimited(handler->priv->dev,
-                                          "can't find mapping for hwirq %lu\n", hwirq);
+                     pr_warn_ratelimited("%pfwP: can't find mapping for hwirq %lu\n",
+                                         handler->priv->fwnode, hwirq);
              }
      }
 
@@ -408,15 +409,14 @@ static int plic_starting_cpu(unsigned int cpu)
              enable_percpu_irq(plic_parent_irq,
                                irq_get_trigger_type(plic_parent_irq));
      else
-             dev_warn(handler->priv->dev, "cpu%d: parent irq not available\n", cpu);
+             pr_warn("%pfwP: cpu%d: parent irq not available\n",
+                     handler->priv->fwnode, cpu);
      plic_set_threshold(handler, PLIC_ENABLE_THRESHOLD);
 
      return 0;
 }
 
-static const struct of_device_id plic_match[] = {
-     { .compatible = "sifive,plic-1.0.0" },
-     { .compatible = "riscv,plic0" },
+static const struct of_device_id plic_quirks_match[] = {
      { .compatible = "andestech,nceplic100",
        .data = (const void *)BIT(PLIC_QUIRK_EDGE_INTERRUPT) },
      { .compatible = "thead,c900-plic",
@@ -424,38 +424,36 @@ static const struct of_device_id plic_match[] = {
      {}
 };
 
-static int plic_parse_nr_irqs_and_contexts(struct platform_device *pdev,
+static int plic_parse_nr_irqs_and_contexts(struct fwnode_handle *fwnode,
                                         u32 *nr_irqs, u32 *nr_contexts)
 {
-     struct device *dev = &pdev->dev;
      int rc;
 
      /*
       * Currently, only OF fwnode is supported so extend this
       * function for ACPI support.
       */
-     if (!is_of_node(dev->fwnode))
+     if (!is_of_node(fwnode))
              return -EINVAL;
 
-     rc = of_property_read_u32(to_of_node(dev->fwnode), "riscv,ndev", nr_irqs);
+     rc = of_property_read_u32(to_of_node(fwnode), "riscv,ndev", nr_irqs);
      if (rc) {
-             dev_err(dev, "riscv,ndev property not available\n");
+             pr_err("%pfwP: riscv,ndev property not available\n", fwnode);
              return rc;
      }
 
-     *nr_contexts = of_irq_count(to_of_node(dev->fwnode));
+     *nr_contexts = of_irq_count(to_of_node(fwnode));
      if (WARN_ON(!(*nr_contexts))) {
-             dev_err(dev, "no PLIC context available\n");
+             pr_err("%pfwP: no PLIC context available\n", fwnode);
              return -EINVAL;
      }
 
      return 0;
 }
 
-static int plic_parse_context_parent(struct platform_device *pdev, u32 context,
+static int plic_parse_context_parent(struct fwnode_handle *fwnode, u32 context,
                                   u32 *parent_hwirq, int *parent_cpu)
 {
-     struct device *dev = &pdev->dev;
      struct of_phandle_args parent;
      unsigned long hartid;
      int rc;
@@ -464,10 +462,10 @@ static int plic_parse_context_parent(struct platform_device *pdev, u32 context,
       * Currently, only OF fwnode is supported so extend this
       * function for ACPI support.
       */
-     if (!is_of_node(dev->fwnode))
+     if (!is_of_node(fwnode))
              return -EINVAL;
 
-     rc = of_irq_parse_one(to_of_node(dev->fwnode), context, &parent);
+     rc = of_irq_parse_one(to_of_node(fwnode), context, &parent);
      if (rc)
              return rc;
 
@@ -480,48 +478,55 @@ static int plic_parse_context_parent(struct platform_device *pdev, u32 context,
      return 0;
 }
 
-static int plic_probe(struct platform_device *pdev)
+static int plic_probe(struct fwnode_handle *fwnode)
 {
      int error = 0, nr_contexts, nr_handlers = 0, cpu, i;
-     struct device *dev = &pdev->dev;
      unsigned long plic_quirks = 0;
      struct plic_handler *handler;
      u32 nr_irqs, parent_hwirq;
      struct plic_priv *priv;
      irq_hw_number_t hwirq;
+     void __iomem *regs;
 
-     if (is_of_node(dev->fwnode)) {
+     if (is_of_node(fwnode)) {
              const struct of_device_id *id;
 

drivers/irqchip/irq-sifive-plic.c static int plic_parse_context_parent(struct platform_device *pdev, u32 context, · line 502

Quoted 10 lines
drivers/irqchip/irq-sifive-plic.c +7 -1
-             id = of_match_node(plic_match, to_of_node(dev->fwnode));
+             id = of_match_node(plic_quirks_match, to_of_node(fwnode));
              if (id)
                      plic_quirks = (unsigned long)id->data;
+
+             regs = of_iomap(to_of_node(fwnode), 0);
+             if (!regs)
+                     return -ENOMEM;
+     } else {
+             return -ENODEV;

This driver never worked with ACPI anyways?

Quoted 138 lines
drivers/irqchip/irq-sifive-plic.c +46 -26
      }
 
-     error = plic_parse_nr_irqs_and_contexts(pdev, &nr_irqs, &nr_contexts);
+     error = plic_parse_nr_irqs_and_contexts(fwnode, &nr_irqs, &nr_contexts);
      if (error)
-             return error;
+             goto fail_free_regs;
 
-     priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
-     if (!priv)
-             return -ENOMEM;
+     priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+     if (!priv) {
+             error = -ENOMEM;
+             goto fail_free_regs;
+     }
 
-     priv->dev = dev;
+     priv->fwnode = fwnode;
      priv->plic_quirks = plic_quirks;
      priv->nr_irqs = nr_irqs;
+     priv->regs = regs;
 
-     priv->regs = devm_platform_ioremap_resource(pdev, 0);
-     if (WARN_ON(!priv->regs))
-             return -EIO;
-
-     priv->prio_save = devm_bitmap_zalloc(dev, nr_irqs, GFP_KERNEL);
-     if (!priv->prio_save)
-             return -ENOMEM;
+     priv->prio_save = bitmap_zalloc(nr_irqs, GFP_KERNEL);
+     if (!priv->prio_save) {
+             error = -ENOMEM;
+             goto fail_free_priv;
+     }
 
      for (i = 0; i < nr_contexts; i++) {
-             error = plic_parse_context_parent(pdev, i, &parent_hwirq, &cpu);
+             error = plic_parse_context_parent(fwnode, i, &parent_hwirq, &cpu);
              if (error) {
-                     dev_warn(dev, "hwirq for context%d not found\n", i);
+                     pr_warn("%pfwP: hwirq for context%d not found\n", fwnode, i);
                      continue;
              }
 
@@ -543,7 +548,7 @@ static int plic_probe(struct platform_device *pdev)
              }
 
              if (cpu < 0) {
-                     dev_warn(dev, "Invalid cpuid for context %d\n", i);
+                     pr_warn("%pfwP: Invalid cpuid for context %d\n", fwnode, i);
                      continue;
              }
 
@@ -554,7 +559,7 @@ static int plic_probe(struct platform_device *pdev)
               */
              handler = per_cpu_ptr(&plic_handlers, cpu);
              if (handler->present) {
-                     dev_warn(dev, "handler already present for context %d.\n", i);
+                     pr_warn("%pfwP: handler already present for context %d.\n", fwnode, i);
                      plic_set_threshold(handler, PLIC_DISABLE_THRESHOLD);
                      goto done;
              }
@@ -568,8 +573,8 @@ static int plic_probe(struct platform_device *pdev)
                      i * CONTEXT_ENABLE_SIZE;
              handler->priv = priv;
 
-             handler->enable_save = devm_kcalloc(dev, DIV_ROUND_UP(nr_irqs, 32),
-                                                 sizeof(*handler->enable_save), GFP_KERNEL);
+             handler->enable_save = kcalloc(DIV_ROUND_UP(nr_irqs, 32),
+                                            sizeof(*handler->enable_save), GFP_KERNEL);
              if (!handler->enable_save)
                      goto fail_cleanup_contexts;
 done:
@@ -581,7 +586,7 @@ static int plic_probe(struct platform_device *pdev)
              nr_handlers++;
      }
 
-     priv->irqdomain = irq_domain_add_linear(to_of_node(dev->fwnode), nr_irqs + 1,
+     priv->irqdomain = irq_domain_add_linear(to_of_node(fwnode), nr_irqs + 1,
                                              &plic_irqdomain_ops, priv);
      if (WARN_ON(!priv->irqdomain))
              goto fail_cleanup_contexts;
@@ -619,13 +624,13 @@ static int plic_probe(struct platform_device *pdev)
              }
      }
 
-     dev_info(dev, "mapped %d interrupts with %d handlers for %d contexts.\n",
-              nr_irqs, nr_handlers, nr_contexts);
+     pr_info("%pfwP: mapped %d interrupts with %d handlers for %d contexts.\n",
+             fwnode, nr_irqs, nr_handlers, nr_contexts);
      return 0;
 
 fail_cleanup_contexts:
      for (i = 0; i < nr_contexts; i++) {
-             if (plic_parse_context_parent(pdev, i, &parent_hwirq, &cpu))
+             if (plic_parse_context_parent(fwnode, i, &parent_hwirq, &cpu))
                      continue;
              if (parent_hwirq != RV_IRQ_EXT || cpu < 0)
                      continue;
@@ -634,17 +639,44 @@ static int plic_probe(struct platform_device *pdev)
              handler->present = false;
              handler->hart_base = NULL;
              handler->enable_base = NULL;
+             kfree(handler->enable_save);
              handler->enable_save = NULL;
              handler->priv = NULL;
      }
-     return -ENOMEM;
+     bitmap_free(priv->prio_save);
+fail_free_priv:
+     kfree(priv);
+fail_free_regs:
+     iounmap(regs);
+     return error;
+}
+
+static int plic_platform_probe(struct platform_device *pdev)
+{
+     return plic_probe(pdev->dev.fwnode);
 }
 
+static const struct of_device_id plic_platform_match[] = {
+     { .compatible = "sifive,plic-1.0.0" },
+     { .compatible = "riscv,plic0" },
+     { .compatible = "andestech,nceplic100" },
+     {}
+};
+
 static struct platform_driver plic_driver = {
      .driver = {
              .name           = "riscv-plic",
-             .of_match_table = plic_match,
+             .of_match_table = plic_platform_match,
+             .suppress_bind_attrs = true,
      },
-     .probe = plic_probe,
+     .probe = plic_platform_probe,

drivers/irqchip/irq-sifive-plic.c static int plic_probe(struct platform_device *pdev) · line 682

Quoted 10 lines
drivers/irqchip/irq-sifive-plic.c +8 -0
 };
 builtin_platform_driver(plic_driver);
+
+static int __init plic_early_probe(struct device_node *node,
+                                struct device_node *parent)
+{
+     return plic_probe(&node->fwnode);
+}
+
+IRQCHIP_DECLARE(riscv, "thead,c900-plic", plic_early_probe);

If this is only needed on the Allwinner D1 maybe this should only match the more specific "allwinner,sun20i-d1-plic"?

Quoted 4 lines

In any case this works by itself, but not with Samuel's patch[1] applied, so

[1]: https://lore.kernel.org/r/[email protected]

Thanks for testing.

Can you confirm that using "allwinner,sun20i-d1-plic" for early probe works on the Allwinner D1 board ? If yes, the I will quickly send a v3.

Regards,
Anup

Reviewed-by Tested-by
#3 lore raw
Quoted 332 lines · Anup Patel
Quoted 330 lines · Emil Renner Berthing
Quoted 160 lines · Anup Patel

The latest Linux RISC-V no longer boots on the Allwinner D1 platform because the sun4i_timer driver fails to get an interrupt from PLIC.

The real fix requires enabling the SBI time extension in the platform firmware (OpenSBI) and convert sun4i_timer into platform driver. Unfortunately, the real fix involves changing multiple places and can't be achieved in a short duration.

As a work-around, retrofit plic probing such that plic is probed early only for the Allwinner D1 platform and probed as a regular platform driver for rest of the RISC-V platforms. In the process, partially revert some of the previous patches because PLIC device pointer is not available in all probing paths.

More detailed discussion can found here:
https://lore.kernel.org/lkml/[email protected]/

Fixes e306a894bd51 ("irqchip/sifive-plic: Chain to parent IRQ after handlers are ready")
Fixes 8ec99b033147 ("irqchip/sifive-plic: Convert PLIC driver into a platform driver")

---
Changes since v1:
- Set suppress_bind_attrs for PLIC platform driver

---
 drivers/irqchip/irq-sifive-plic.c | 128 +++++++++++++++++++-----------
 1 file changed, 80 insertions(+), 48 deletions(-)
drivers/irqchip/irq-sifive-plic.c +19 -21
index 9e22f7e378f5..33395c5a9b5b 100644
@@ -3,6 +3,7 @@
  * Copyright (C) 2017 SiFive
  * Copyright (C) 2018 Christoph Hellwig
  */
+#define pr_fmt(fmt) "riscv-plic: " fmt
 #include <linux/cpu.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
@@ -63,7 +64,7 @@
 #define PLIC_QUIRK_EDGE_INTERRUPT    0
 
 struct plic_priv {
-     struct device *dev;
+     struct fwnode_handle *fwnode;
      struct cpumask lmask;
      struct irq_domain *irqdomain;
      void __iomem *regs;
@@ -378,8 +379,8 @@ static void plic_handle_irq(struct irq_desc *desc)
              int err = generic_handle_domain_irq(handler->priv->irqdomain,
                                                  hwirq);
              if (unlikely(err)) {
-                     dev_warn_ratelimited(handler->priv->dev,
-                                          "can't find mapping for hwirq %lu\n", hwirq);
+                     pr_warn_ratelimited("%pfwP: can't find mapping for hwirq %lu\n",
+                                         handler->priv->fwnode, hwirq);
              }
      }
 
@@ -408,15 +409,14 @@ static int plic_starting_cpu(unsigned int cpu)
              enable_percpu_irq(plic_parent_irq,
                                irq_get_trigger_type(plic_parent_irq));
      else
-             dev_warn(handler->priv->dev, "cpu%d: parent irq not available\n", cpu);
+             pr_warn("%pfwP: cpu%d: parent irq not available\n",
+                     handler->priv->fwnode, cpu);
      plic_set_threshold(handler, PLIC_ENABLE_THRESHOLD);
 
      return 0;
 }
 
-static const struct of_device_id plic_match[] = {
-     { .compatible = "sifive,plic-1.0.0" },
-     { .compatible = "riscv,plic0" },
+static const struct of_device_id plic_quirks_match[] = {
      { .compatible = "andestech,nceplic100",
        .data = (const void *)BIT(PLIC_QUIRK_EDGE_INTERRUPT) },
      { .compatible = "thead,c900-plic",
@@ -424,38 +424,36 @@ static const struct of_device_id plic_match[] = {
      {}
 };
 
-static int plic_parse_nr_irqs_and_contexts(struct platform_device *pdev,
+static int plic_parse_nr_irqs_and_contexts(struct fwnode_handle *fwnode,
                                         u32 *nr_irqs, u32 *nr_contexts)
 {
-     struct device *dev = &pdev->dev;
      int rc;
 
      /*
       * Currently, only OF fwnode is supported so extend this
       * function for ACPI support.
       */
-     if (!is_of_node(dev->fwnode))
+     if (!is_of_node(fwnode))
              return -EINVAL;
 
-     rc = of_property_read_u32(to_of_node(dev->fwnode), "riscv,ndev", nr_irqs);
+     rc = of_property_read_u32(to_of_node(fwnode), "riscv,ndev", nr_irqs);
      if (rc) {
-             dev_err(dev, "riscv,ndev property not available\n");
+             pr_err("%pfwP: riscv,ndev property not available\n", fwnode);
              return rc;
      }
 
-     *nr_contexts = of_irq_count(to_of_node(dev->fwnode));
+     *nr_contexts = of_irq_count(to_of_node(fwnode));
      if (WARN_ON(!(*nr_contexts))) {
-             dev_err(dev, "no PLIC context available\n");
+             pr_err("%pfwP: no PLIC context available\n", fwnode);
              return -EINVAL;
      }
 
      return 0;
 }
 
-static int plic_parse_context_parent(struct platform_device *pdev, u32 context,
+static int plic_parse_context_parent(struct fwnode_handle *fwnode, u32 context,
                                   u32 *parent_hwirq, int *parent_cpu)
 {
-     struct device *dev = &pdev->dev;
      struct of_phandle_args parent;
      unsigned long hartid;
      int rc;
@@ -464,10 +462,10 @@ static int plic_parse_context_parent(struct platform_device *pdev, u32 context,
       * Currently, only OF fwnode is supported so extend this
       * function for ACPI support.
       */
-     if (!is_of_node(dev->fwnode))
+     if (!is_of_node(fwnode))
              return -EINVAL;
 
-     rc = of_irq_parse_one(to_of_node(dev->fwnode), context, &parent);
+     rc = of_irq_parse_one(to_of_node(fwnode), context, &parent);
      if (rc)
              return rc;
 
@@ -480,48 +478,55 @@ static int plic_parse_context_parent(struct platform_device *pdev, u32 context,
      return 0;
 }
 
-static int plic_probe(struct platform_device *pdev)
+static int plic_probe(struct fwnode_handle *fwnode)
 {
      int error = 0, nr_contexts, nr_handlers = 0, cpu, i;
-     struct device *dev = &pdev->dev;
      unsigned long plic_quirks = 0;
      struct plic_handler *handler;
      u32 nr_irqs, parent_hwirq;
      struct plic_priv *priv;
      irq_hw_number_t hwirq;
+     void __iomem *regs;
 
-     if (is_of_node(dev->fwnode)) {
+     if (is_of_node(fwnode)) {
              const struct of_device_id *id;
 

drivers/irqchip/irq-sifive-plic.c static int plic_parse_context_parent(struct platform_device *pdev, u32 context, · line 502

Quoted 10 lines
drivers/irqchip/irq-sifive-plic.c +7 -1
-             id = of_match_node(plic_match, to_of_node(dev->fwnode));
+             id = of_match_node(plic_quirks_match, to_of_node(fwnode));
              if (id)
                      plic_quirks = (unsigned long)id->data;
+
+             regs = of_iomap(to_of_node(fwnode), 0);
+             if (!regs)
+                     return -ENOMEM;
+     } else {
+             return -ENODEV;

This driver never worked with ACPI anyways?

Quoted 138 lines
drivers/irqchip/irq-sifive-plic.c +46 -26
      }
 
-     error = plic_parse_nr_irqs_and_contexts(pdev, &nr_irqs, &nr_contexts);
+     error = plic_parse_nr_irqs_and_contexts(fwnode, &nr_irqs, &nr_contexts);
      if (error)
-             return error;
+             goto fail_free_regs;
 
-     priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
-     if (!priv)
-             return -ENOMEM;
+     priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+     if (!priv) {
+             error = -ENOMEM;
+             goto fail_free_regs;
+     }
 
-     priv->dev = dev;
+     priv->fwnode = fwnode;
      priv->plic_quirks = plic_quirks;
      priv->nr_irqs = nr_irqs;
+     priv->regs = regs;
 
-     priv->regs = devm_platform_ioremap_resource(pdev, 0);
-     if (WARN_ON(!priv->regs))
-             return -EIO;
-
-     priv->prio_save = devm_bitmap_zalloc(dev, nr_irqs, GFP_KERNEL);
-     if (!priv->prio_save)
-             return -ENOMEM;
+     priv->prio_save = bitmap_zalloc(nr_irqs, GFP_KERNEL);
+     if (!priv->prio_save) {
+             error = -ENOMEM;
+             goto fail_free_priv;
+     }
 
      for (i = 0; i < nr_contexts; i++) {
-             error = plic_parse_context_parent(pdev, i, &parent_hwirq, &cpu);
+             error = plic_parse_context_parent(fwnode, i, &parent_hwirq, &cpu);
              if (error) {
-                     dev_warn(dev, "hwirq for context%d not found\n", i);
+                     pr_warn("%pfwP: hwirq for context%d not found\n", fwnode, i);
                      continue;
              }
 
@@ -543,7 +548,7 @@ static int plic_probe(struct platform_device *pdev)
              }
 
              if (cpu < 0) {
-                     dev_warn(dev, "Invalid cpuid for context %d\n", i);
+                     pr_warn("%pfwP: Invalid cpuid for context %d\n", fwnode, i);
                      continue;
              }
 
@@ -554,7 +559,7 @@ static int plic_probe(struct platform_device *pdev)
               */
              handler = per_cpu_ptr(&plic_handlers, cpu);
              if (handler->present) {
-                     dev_warn(dev, "handler already present for context %d.\n", i);
+                     pr_warn("%pfwP: handler already present for context %d.\n", fwnode, i);
                      plic_set_threshold(handler, PLIC_DISABLE_THRESHOLD);
                      goto done;
              }
@@ -568,8 +573,8 @@ static int plic_probe(struct platform_device *pdev)
                      i * CONTEXT_ENABLE_SIZE;
              handler->priv = priv;
 
-             handler->enable_save = devm_kcalloc(dev, DIV_ROUND_UP(nr_irqs, 32),
-                                                 sizeof(*handler->enable_save), GFP_KERNEL);
+             handler->enable_save = kcalloc(DIV_ROUND_UP(nr_irqs, 32),
+                                            sizeof(*handler->enable_save), GFP_KERNEL);
              if (!handler->enable_save)
                      goto fail_cleanup_contexts;
 done:
@@ -581,7 +586,7 @@ static int plic_probe(struct platform_device *pdev)
              nr_handlers++;
      }
 
-     priv->irqdomain = irq_domain_add_linear(to_of_node(dev->fwnode), nr_irqs + 1,
+     priv->irqdomain = irq_domain_add_linear(to_of_node(fwnode), nr_irqs + 1,
                                              &plic_irqdomain_ops, priv);
      if (WARN_ON(!priv->irqdomain))
              goto fail_cleanup_contexts;
@@ -619,13 +624,13 @@ static int plic_probe(struct platform_device *pdev)
              }
      }
 
-     dev_info(dev, "mapped %d interrupts with %d handlers for %d contexts.\n",
-              nr_irqs, nr_handlers, nr_contexts);
+     pr_info("%pfwP: mapped %d interrupts with %d handlers for %d contexts.\n",
+             fwnode, nr_irqs, nr_handlers, nr_contexts);
      return 0;
 
 fail_cleanup_contexts:
      for (i = 0; i < nr_contexts; i++) {
-             if (plic_parse_context_parent(pdev, i, &parent_hwirq, &cpu))
+             if (plic_parse_context_parent(fwnode, i, &parent_hwirq, &cpu))
                      continue;
              if (parent_hwirq != RV_IRQ_EXT || cpu < 0)
                      continue;
@@ -634,17 +639,44 @@ static int plic_probe(struct platform_device *pdev)
              handler->present = false;
              handler->hart_base = NULL;
              handler->enable_base = NULL;
+             kfree(handler->enable_save);
              handler->enable_save = NULL;
              handler->priv = NULL;
      }
-     return -ENOMEM;
+     bitmap_free(priv->prio_save);
+fail_free_priv:
+     kfree(priv);
+fail_free_regs:
+     iounmap(regs);
+     return error;
+}
+
+static int plic_platform_probe(struct platform_device *pdev)
+{
+     return plic_probe(pdev->dev.fwnode);
 }
 
+static const struct of_device_id plic_platform_match[] = {
+     { .compatible = "sifive,plic-1.0.0" },
+     { .compatible = "riscv,plic0" },
+     { .compatible = "andestech,nceplic100" },
+     {}
+};
+
 static struct platform_driver plic_driver = {
      .driver = {
              .name           = "riscv-plic",
-             .of_match_table = plic_match,
+             .of_match_table = plic_platform_match,
+             .suppress_bind_attrs = true,
      },
-     .probe = plic_probe,
+     .probe = plic_platform_probe,

drivers/irqchip/irq-sifive-plic.c static int plic_probe(struct platform_device *pdev) · line 682

Quoted 10 lines
drivers/irqchip/irq-sifive-plic.c +8 -0
 };
 builtin_platform_driver(plic_driver);
+
+static int __init plic_early_probe(struct device_node *node,
+                                struct device_node *parent)
+{
+     return plic_probe(&node->fwnode);
+}
+
+IRQCHIP_DECLARE(riscv, "thead,c900-plic", plic_early_probe);

If this is only needed on the Allwinner D1 maybe this should only match the more specific "allwinner,sun20i-d1-plic"?

In any case this works by itself, but not with Samuel's patch[1] applied, so

Quoted 7 lines
Quoted 1 line

[1]: https://lore.kernel.org/r/[email protected]

Thanks for testing.

Can you confirm that using "allwinner,sun20i-d1-plic" for early probe works on the Allwinner D1 board ? If yes, the I will quickly send a v3.

I can confirm that this patch works as-is, and with "allwinner,sun20i-d1-plic" for early probe on the Allwiner D1 board. Thank you for this fix Anup!

Quoted 8 lines

Regards,
Anup

_______________________________________________
linux-riscv mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-riscv