diff mbox series

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

Message ID 20240816161828.2979143-1-apatel@ventanamicro.com (mailing list archive)
State Superseded
Headers show
Series irqchip/sifive-plic: Probe plic driver early for Allwinner D1 platform | expand

Checks

Context Check Description
conchuod/vmtest-for-next-PR success PR summary
conchuod/patch-1-test-1 success .github/scripts/patches/tests/build_rv32_defconfig.sh
conchuod/patch-1-test-2 success .github/scripts/patches/tests/build_rv64_clang_allmodconfig.sh
conchuod/patch-1-test-3 success .github/scripts/patches/tests/build_rv64_gcc_allmodconfig.sh
conchuod/patch-1-test-4 success .github/scripts/patches/tests/build_rv64_nommu_k210_defconfig.sh
conchuod/patch-1-test-5 success .github/scripts/patches/tests/build_rv64_nommu_virt_defconfig.sh
conchuod/patch-1-test-6 success .github/scripts/patches/tests/checkpatch.sh
conchuod/patch-1-test-7 success .github/scripts/patches/tests/dtb_warn_rv64.sh
conchuod/patch-1-test-8 success .github/scripts/patches/tests/header_inline.sh
conchuod/patch-1-test-9 success .github/scripts/patches/tests/kdoc.sh
conchuod/patch-1-test-10 success .github/scripts/patches/tests/module_param.sh
conchuod/patch-1-test-11 success .github/scripts/patches/tests/verify_fixes.sh
conchuod/patch-1-test-12 success .github/scripts/patches/tests/verify_signedoff.sh

Commit Message

Anup Patel Aug. 16, 2024, 4:18 p.m. UTC
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(-)

Comments

Anup Patel Aug. 16, 2024, 4:28 p.m. UTC | #1
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
>
Samuel Holland Aug. 16, 2024, 4:43 p.m. UTC | #2
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);
Anup Patel Aug. 17, 2024, 8:09 a.m. UTC | #3
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);
>
diff mbox series

Patch

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);