irqchip/sifive-plic: Probe plic driver early for Allwinner D1 platform
Subsystems (3)
PATCH 1/1 irqchip/sifive-plic: Probe plic driver early for Allwinner D1 platform
The latest Linux RISC-V no longer boots on the Allwinner D1 platform because the sun4i_timer driver fails to get an interrupt from PLIC.
The real fix requires enabling the SBI time extension in the platform firmware (OpenSBI) and convert sun4i_timer into platform driver. Unfortunately, the real fix involves changing multiple places and can't be achieved in a short duration.
As a work-around, retrofit plic probing such that plic is probed early only for the Allwinner D1 platform and probed as a regular platform driver for rest of the RISC-V platforms. In the process, partially revert some of the previous patches because PLIC device pointer is not available in all probing paths.
More detailed discussion can found here:
https://lore.kernel.org/lkml/[email protected]/
--- drivers/irqchip/irq-sifive-plic.c | 128 +++++++++++++++++++----------- 1 file changed, 80 insertions(+), 48 deletions(-)
drivers/irqchip/irq-sifive-plic.c +80 -48
@@ -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 0struct 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);
Signature · 1 line
2.34.1
Hi All,
Quoted 15 lines · Anup Patel
The latest Linux RISC-V no longer boots on the Allwinner D1 platform because the
sun4i_timerdriver fails to get an interrupt from PLIC.The real fix requires enabling the SBI time extension in the platform firmware (OpenSBI) and convert
sun4i_timerinto 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.
Quoted 8 lines
More detailed discussion can found here:
https://lore.kernel.org/lkml/[email protected]/Fixes e306a894bd51 ("irqchip/sifive-plic: Chain to parent IRQ after handlers are ready")Fixes 8ec99b033147 ("irqchip/sifive-plic: Convert PLIC driver into a platform driver")Suggested-by Thomas Gleixner <[email protected]>Signed-off-by Anup Patel <[email protected]>---
I don't have the Allwinner D1 platform so it would be great if someone can test this patch on Allwinner D1.
Regards,
Anup
Quoted 294 lines
drivers/irqchip/irq-sifive-plic.c | 128 +++++++++++++++++++----------- 1 file changed, 80 insertions(+), 48 deletions(-)drivers/irqchip/irq-sifive-plic.c +80
-49@@ -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 0struct 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,
Quoted 17 lines · Anup Patel
The latest Linux RISC-V no longer boots on the Allwinner D1 platform because the
sun4i_timerdriver fails to get an interrupt from PLIC.The real fix requires enabling the SBI time extension in the platform firmware (OpenSBI) and convert
sun4i_timerinto platform driver. Unfortunately, the real fix involves changing multiple places and can't be achieved in a short duration.As a work-around, retrofit plic probing such that plic is probed early only for the Allwinner D1 platform and probed as a regular platform driver for rest of the RISC-V platforms. In the process, partially revert some of the previous patches because PLIC device pointer is not available in all probing paths.
More detailed discussion can found here:
https://lore.kernel.org/lkml/[email protected]/
Quoted 7 lines
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 <[email protected]>Signed-off-by Anup Patel <[email protected]>--- drivers/irqchip/irq-sifive-plic.c | 128 +++++++++++++++++++----------- 1 file changed, 80 insertions(+), 48 deletions(-)
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)
Quoted 270 lines
drivers/irqchip/irq-sifive-plic.c +70
-46@@ -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 0struct 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" }, + {} +}; +
drivers/irqchip/irq-sifive-plic.c static int plic_probe(struct platform_device *pdev) · line 674
Quoted 10 lines
drivers/irqchip/irq-sifive-plic.c +2
-2static 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
Quoted 8 lines
drivers/irqchip/irq-sifive-plic.c +8
-0+ +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);
Quoted 318 lines · Samuel Holland
Hi Anup,
Quoted 17 lines · Anup Patel
The latest Linux RISC-V no longer boots on the Allwinner D1 platform because the
sun4i_timerdriver fails to get an interrupt from PLIC.The real fix requires enabling the SBI time extension in the platform firmware (OpenSBI) and convert
sun4i_timerinto platform driver. Unfortunately, the real fix involves changing multiple places and can't be achieved in a short duration.As a work-around, retrofit plic probing such that plic is probed early only for the Allwinner D1 platform and probed as a regular platform driver for rest of the RISC-V platforms. In the process, partially revert some of the previous patches because PLIC device pointer is not available in all probing paths.
More detailed discussion can found here:
https://lore.kernel.org/lkml/[email protected]/Quoted 7 lines
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 <[email protected]>Signed-off-by Anup Patel <[email protected]>--- drivers/irqchip/irq-sifive-plic.c | 128 +++++++++++++++++++----------- 1 file changed, 80 insertions(+), 48 deletions(-)Reviewed-by Samuel Holland <[email protected]>Tested-by Samuel Holland <[email protected]>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)Quoted 275 lines
drivers/irqchip/irq-sifive-plic.c +71
-47@@ -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 0struct 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,
Quoted 8 lines
drivers/irqchip/irq-sifive-plic.c static int plic_probe(struct platform_device *pdev) · line 674
Quoted 5 lines
drivers/irqchip/irq-sifive-plic.c +1
-1}, - .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
Quoted 13 lines
Regards,
SamuelQuoted 8 lines
drivers/irqchip/irq-sifive-plic.c +8
-0+ +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);