drivers/irqchip/irq-sifive-plic.c | 128 +++++++++++++++++++----------- 1 file changed, 80 insertions(+), 48 deletions(-)
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/20240814145642.344485-1-emil.renner.berthing@canonical.com/
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")
Suggested-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Anup Patel <apatel@ventanamicro.com>
---
drivers/irqchip/irq-sifive-plic.c | 128 +++++++++++++++++++-----------
1 file changed, 80 insertions(+), 48 deletions(-)
diff --git a/drivers/irqchip/irq-sifive-plic.c b/drivers/irqchip/irq-sifive-plic.c
index 9e22f7e378f5..357d25662718 100644
--- a/drivers/irqchip/irq-sifive-plic.c
+++ b/drivers/irqchip/irq-sifive-plic.c
@@ -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,8 @@ 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 +574,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 +587,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 +625,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 +640,43 @@ 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,
},
- .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);
--
2.34.1
Hi Anup,
On 2024-08-16 11:18 AM, Anup Patel wrote:
> 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/20240814145642.344485-1-emil.renner.berthing@canonical.com/
>
> 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")
> Suggested-by: Thomas Gleixner <tglx@linutronix.de>
> Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> ---
> drivers/irqchip/irq-sifive-plic.c | 128 +++++++++++++++++++-----------
> 1 file changed, 80 insertions(+), 48 deletions(-)
Reviewed-by: Samuel Holland <samuel.holland@sifive.com>
Tested-by: Samuel Holland <samuel.holland@sifive.com>
v6.11-rc3 + this patch boots on D1 without CLINT in the devicetree:
[ 0.000000][ T0] NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0
[ 0.000000][ T0] riscv-intc: 64 local interrupts mapped
[ 0.000000][ T0] riscv-plic: interrupt-controller@10000000: mapped 175 interrupts with 1 handlers for 2 contexts.
[ 0.000000][ T0] rcu: srcu_init: Setting srcu_struct sizes based on contention.
[ 0.000000][ T0] clocksource: timer: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 79635851949 ns
[ 0.000000][ T0] clocksource: riscv_clocksource: mask: 0xffffffffffffffff max_cycles: 0x588fe9dc0, max_idle_ns: 440795202592 ns
[ 0.000001][ T0] sched_clock: 64 bits at 24MHz, resolution 41ns, wraps every 4398046511097ns
[ 0.009362][ T0] Calibrating delay loop (skipped), value calculated using timer frequency.. 48.00 BogoMIPS (lpj=96000)
> diff --git a/drivers/irqchip/irq-sifive-plic.c b/drivers/irqchip/irq-sifive-plic.c
> index 9e22f7e378f5..357d25662718 100644
> --- a/drivers/irqchip/irq-sifive-plic.c
> +++ b/drivers/irqchip/irq-sifive-plic.c
> @@ -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,8 @@ 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 +574,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 +587,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 +625,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 +640,43 @@ 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,
> },
> - .probe = plic_probe,
> + .probe = plic_platform_probe,
> };
> builtin_platform_driver(plic_driver);
FWIW, we should probably use the IRQCHIP_PLATFORM_DRIVER_* helpers to get
.suppress_bind_attrs = true, since we no longer use devres for cleanup.
Regards,
Samuel
> +
> +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);
On Fri, Aug 16, 2024 at 10:13 PM Samuel Holland
<samuel.holland@sifive.com> wrote:
>
> Hi Anup,
>
> On 2024-08-16 11:18 AM, Anup Patel wrote:
> > 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/20240814145642.344485-1-emil.renner.berthing@canonical.com/
> >
> > 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")
> > Suggested-by: Thomas Gleixner <tglx@linutronix.de>
> > Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> > ---
> > drivers/irqchip/irq-sifive-plic.c | 128 +++++++++++++++++++-----------
> > 1 file changed, 80 insertions(+), 48 deletions(-)
>
> Reviewed-by: Samuel Holland <samuel.holland@sifive.com>
> Tested-by: Samuel Holland <samuel.holland@sifive.com>
>
> v6.11-rc3 + this patch boots on D1 without CLINT in the devicetree:
>
> [ 0.000000][ T0] NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0
> [ 0.000000][ T0] riscv-intc: 64 local interrupts mapped
> [ 0.000000][ T0] riscv-plic: interrupt-controller@10000000: mapped 175 interrupts with 1 handlers for 2 contexts.
> [ 0.000000][ T0] rcu: srcu_init: Setting srcu_struct sizes based on contention.
> [ 0.000000][ T0] clocksource: timer: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 79635851949 ns
> [ 0.000000][ T0] clocksource: riscv_clocksource: mask: 0xffffffffffffffff max_cycles: 0x588fe9dc0, max_idle_ns: 440795202592 ns
> [ 0.000001][ T0] sched_clock: 64 bits at 24MHz, resolution 41ns, wraps every 4398046511097ns
> [ 0.009362][ T0] Calibrating delay loop (skipped), value calculated using timer frequency.. 48.00 BogoMIPS (lpj=96000)
>
> > diff --git a/drivers/irqchip/irq-sifive-plic.c b/drivers/irqchip/irq-sifive-plic.c
> > index 9e22f7e378f5..357d25662718 100644
> > --- a/drivers/irqchip/irq-sifive-plic.c
> > +++ b/drivers/irqchip/irq-sifive-plic.c
> > @@ -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,8 @@ 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 +574,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 +587,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 +625,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 +640,43 @@ 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,
> > },
> > - .probe = plic_probe,
> > + .probe = plic_platform_probe,
> > };
> > builtin_platform_driver(plic_driver);
>
> FWIW, we should probably use the IRQCHIP_PLATFORM_DRIVER_* helpers to get
> .suppress_bind_attrs = true, since we no longer use devres for cleanup.
The callback registered using IRQCHIP_PLATFORM_DRIVER_*()
assumes OF nodes which creates problems extracting the ACPI
fwnode pointer.
Regarding suppress_bind_attrs, I agree we should set it to true. I
will update and send v2 soon.
Regards,
Anup
>
> Regards,
> Samuel
>
> > +
> > +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);
>
Hi All,
On Fri, Aug 16, 2024 at 9:48 PM Anup Patel <apatel@ventanamicro.com> wrote:
>
> 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/20240814145642.344485-1-emil.renner.berthing@canonical.com/
>
> 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")
> Suggested-by: Thomas Gleixner <tglx@linutronix.de>
> Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> ---
I don't have the Allwinner D1 platform so it would be great if someone
can test this patch on Allwinner D1.
Regards,
Anup
> drivers/irqchip/irq-sifive-plic.c | 128 +++++++++++++++++++-----------
> 1 file changed, 80 insertions(+), 48 deletions(-)
>
> diff --git a/drivers/irqchip/irq-sifive-plic.c b/drivers/irqchip/irq-sifive-plic.c
> index 9e22f7e378f5..357d25662718 100644
> --- a/drivers/irqchip/irq-sifive-plic.c
> +++ b/drivers/irqchip/irq-sifive-plic.c
> @@ -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,8 @@ 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 +574,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 +587,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 +625,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 +640,43 @@ 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,
> },
> - .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);
> --
> 2.34.1
>
© 2016 - 2026 Red Hat, Inc.