From 86be659415b0ddefebc3120e309091aa215a9064 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sun, 22 Feb 2026 18:43:44 -0500 Subject: irqchip/irq-pic32-evic: Address warning related to wrong printf() formatter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This driver is currently only build on 32 bit MIPS systems. When building it on x86_64, the following warning occurs: drivers/irqchip/irq-pic32-evic.c: In function ‘pic32_ext_irq_of_init’: ./include/linux/kern_levels.h:5:25: error: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘long unsigned int’ [-Werror=format=] Update the printf() formatter in preparation for allowing this driver to be compiled on all architectures. Fixes: aaa8666ada780 ("IRQCHIP: irq-pic32-evic: Add support for PIC32 interrupt controller") Signed-off-by: Brian Masney Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260222-irqchip-pic32-v1-1-37f50d1f14af@redhat.com --- drivers/irqchip/irq-pic32-evic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-pic32-evic.c b/drivers/irqchip/irq-pic32-evic.c index e85c3e300701..325b97a0287f 100644 --- a/drivers/irqchip/irq-pic32-evic.c +++ b/drivers/irqchip/irq-pic32-evic.c @@ -196,7 +196,7 @@ static void __init pic32_ext_irq_of_init(struct irq_domain *domain) of_property_for_each_u32(node, pname, hwirq) { if (i >= ARRAY_SIZE(priv->ext_irqs)) { - pr_warn("More than %d external irq, skip rest\n", + pr_warn("More than %zu external irq, skip rest\n", ARRAY_SIZE(priv->ext_irqs)); break; } -- cgit v1.2.3 From 15f9b251fe404997eef1d7685877479364c4cfcb Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sun, 22 Feb 2026 18:43:45 -0500 Subject: irqchip/irq-pic32-evic: Don't define plat_irq_dispatch() for !MIPS builds plat_irq_dispatch() is specific to the MIPS architecture, so only include it when the driver is compiled on that architecture. This is in preparation for allowing this driver to be compiled on all architectures. Signed-off-by: Brian Masney Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260222-irqchip-pic32-v1-2-37f50d1f14af@redhat.com --- drivers/irqchip/irq-pic32-evic.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/irqchip/irq-pic32-evic.c b/drivers/irqchip/irq-pic32-evic.c index 325b97a0287f..1eeb0e63d670 100644 --- a/drivers/irqchip/irq-pic32-evic.c +++ b/drivers/irqchip/irq-pic32-evic.c @@ -40,6 +40,7 @@ struct evic_chip_data { static struct irq_domain *evic_irq_domain; static void __iomem *evic_base; +#ifdef CONFIG_MIPS asmlinkage void __weak plat_irq_dispatch(void) { unsigned int hwirq; @@ -47,6 +48,7 @@ asmlinkage void __weak plat_irq_dispatch(void) hwirq = readl(evic_base + REG_INTSTAT) & 0xFF; do_domain_IRQ(evic_irq_domain, hwirq); } +#endif static struct evic_chip_data *irqd_to_priv(struct irq_data *data) { -- cgit v1.2.3 From 282f8b547d51d8a33b9230ba58e2324babe0a96a Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sun, 22 Feb 2026 18:43:46 -0500 Subject: irqchip/irq-pic32-evic: Define board_bind_eic_interrupt for !MIPS builds The board_bind_eic_interrupt() pointer is MIPS specific. When compiling for other architectures it is undefined which breaks the build. Define it as a static variable when building for non MIPS architectures with COMPILE_TEST. Signed-off-by: Brian Masney Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260222-irqchip-pic32-v1-3-37f50d1f14af@redhat.com --- drivers/irqchip/irq-pic32-evic.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/irqchip/irq-pic32-evic.c b/drivers/irqchip/irq-pic32-evic.c index 1eeb0e63d670..afb700265451 100644 --- a/drivers/irqchip/irq-pic32-evic.c +++ b/drivers/irqchip/irq-pic32-evic.c @@ -48,6 +48,8 @@ asmlinkage void __weak plat_irq_dispatch(void) hwirq = readl(evic_base + REG_INTSTAT) & 0xFF; do_domain_IRQ(evic_irq_domain, hwirq); } +#else +static void (*board_bind_eic_interrupt)(int irq, int regset); #endif static struct evic_chip_data *irqd_to_priv(struct irq_data *data) -- cgit v1.2.3 From 6096f427ed8efff6198f2330f1756f9381c8a8ec Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sun, 22 Feb 2026 18:43:47 -0500 Subject: irqchip/irq-pic32-evic: Only include asm headers when compiling for MIPS The asm headers are not actually needed when compiling on architectures other than MIPS, and traps.h is not available on all architectures. Include them on MIPS systems so that this driver can be compiled on other architectures. [ tglx: Massaged change log ] Signed-off-by: Brian Masney Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260222-irqchip-pic32-v1-4-37f50d1f14af@redhat.com --- drivers/irqchip/irq-pic32-evic.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/irqchip/irq-pic32-evic.c b/drivers/irqchip/irq-pic32-evic.c index afb700265451..ecea7cb3ba84 100644 --- a/drivers/irqchip/irq-pic32-evic.c +++ b/drivers/irqchip/irq-pic32-evic.c @@ -15,8 +15,10 @@ #include #include +#ifdef CONFIG_MIPS #include #include +#endif #define REG_INTCON 0x0000 #define REG_INTSTAT 0x0020 -- cgit v1.2.3 From 4b52df1b4e1d9cf4cb5a8e1b5287d1e3d1a6aa0c Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sun, 22 Feb 2026 18:43:48 -0500 Subject: irqchip/irq-pic32-evic: Allow driver to be compiled with COMPILE_TEST This driver currently only supports builds against a PIC32 target. To avoid future breakage in the future update Kconfig so that it can be built with COMPILE_TEST enabled. [ tglx: Drop the now pointless select in the pic32 Kconfig ] Signed-off-by: Brian Masney Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260222-irqchip-pic32-v1-5-37f50d1f14af@redhat.com --- drivers/irqchip/Kconfig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index f07b00d7fef9..dc26effa5b36 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -252,9 +252,12 @@ config ORION_IRQCHIP select IRQ_DOMAIN config PIC32_EVIC - bool + def_bool MACH_PIC32 || COMPILE_TEST select GENERIC_IRQ_CHIP select IRQ_DOMAIN + help + Enable support for the interrupt controller on the Microchip PIC32 + family of platforms. config JCORE_AIC bool "J-Core integrated AIC" if COMPILE_TEST -- cgit v1.2.3 From 9fcd9ffe94da4a34a451e4cc0e3e007b4ed7f114 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 4 Mar 2026 11:33:11 +0000 Subject: irqchip/renesas-rzv2h: Use local node pointer Avoid dereferencing pdev->dev.of_node again in rzv2h_icu_probe_common(). Reuse the already available local node pointer when mapping the ICU register space. Signed-off-by: Lad Prabhakar Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260304113317.129339-2-prabhakar.mahadev-lad.rj@bp.renesas.com --- drivers/irqchip/irq-renesas-rzv2h.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-rzv2h.c b/drivers/irqchip/irq-renesas-rzv2h.c index da2bc43a0e12..20c0cd11ef25 100644 --- a/drivers/irqchip/irq-renesas-rzv2h.c +++ b/drivers/irqchip/irq-renesas-rzv2h.c @@ -570,7 +570,7 @@ static int rzv2h_icu_probe_common(struct platform_device *pdev, struct device_no platform_set_drvdata(pdev, rzv2h_icu_data); - rzv2h_icu_data->base = devm_of_iomap(&pdev->dev, pdev->dev.of_node, 0, NULL); + rzv2h_icu_data->base = devm_of_iomap(&pdev->dev, node, 0, NULL); if (IS_ERR(rzv2h_icu_data->base)) return PTR_ERR(rzv2h_icu_data->base); -- cgit v1.2.3 From bbe78cb1399b4ba6b6d14943fb9dadefe9c17b5b Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 4 Mar 2026 11:33:12 +0000 Subject: irqchip/renesas-rzv2h: Use local device pointer in ICU probe Use a local struct device pointer in rzv2h_icu_probe_common() to avoid repeated dereferencing of pdev->dev. Signed-off-by: Lad Prabhakar Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260304113317.129339-3-prabhakar.mahadev-lad.rj@bp.renesas.com --- drivers/irqchip/irq-renesas-rzv2h.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-rzv2h.c b/drivers/irqchip/irq-renesas-rzv2h.c index 20c0cd11ef25..766b981cf3d8 100644 --- a/drivers/irqchip/irq-renesas-rzv2h.c +++ b/drivers/irqchip/irq-renesas-rzv2h.c @@ -555,57 +555,58 @@ static int rzv2h_icu_probe_common(struct platform_device *pdev, struct device_no { struct irq_domain *irq_domain, *parent_domain; struct device_node *node = pdev->dev.of_node; + struct device *dev = &pdev->dev; struct reset_control *resetn; int ret; parent_domain = irq_find_host(parent); if (!parent_domain) { - dev_err(&pdev->dev, "cannot find parent domain\n"); + dev_err(dev, "cannot find parent domain\n"); return -ENODEV; } - rzv2h_icu_data = devm_kzalloc(&pdev->dev, sizeof(*rzv2h_icu_data), GFP_KERNEL); + rzv2h_icu_data = devm_kzalloc(dev, sizeof(*rzv2h_icu_data), GFP_KERNEL); if (!rzv2h_icu_data) return -ENOMEM; platform_set_drvdata(pdev, rzv2h_icu_data); - rzv2h_icu_data->base = devm_of_iomap(&pdev->dev, node, 0, NULL); + rzv2h_icu_data->base = devm_of_iomap(dev, node, 0, NULL); if (IS_ERR(rzv2h_icu_data->base)) return PTR_ERR(rzv2h_icu_data->base); ret = rzv2h_icu_parse_interrupts(rzv2h_icu_data, node); if (ret) { - dev_err(&pdev->dev, "cannot parse interrupts: %d\n", ret); + dev_err(dev, "cannot parse interrupts: %d\n", ret); return ret; } - resetn = devm_reset_control_get_exclusive_deasserted(&pdev->dev, NULL); + resetn = devm_reset_control_get_exclusive_deasserted(dev, NULL); if (IS_ERR(resetn)) { ret = PTR_ERR(resetn); - dev_err(&pdev->dev, "failed to acquire deasserted reset: %d\n", ret); + dev_err(dev, "failed to acquire deasserted reset: %d\n", ret); return ret; } - ret = devm_pm_runtime_enable(&pdev->dev); + ret = devm_pm_runtime_enable(dev); if (ret < 0) { - dev_err(&pdev->dev, "devm_pm_runtime_enable failed, %d\n", ret); + dev_err(dev, "devm_pm_runtime_enable failed, %d\n", ret); return ret; } - ret = pm_runtime_resume_and_get(&pdev->dev); + ret = pm_runtime_resume_and_get(dev); if (ret < 0) { - dev_err(&pdev->dev, "pm_runtime_resume_and_get failed: %d\n", ret); + dev_err(dev, "pm_runtime_resume_and_get failed: %d\n", ret); return ret; } raw_spin_lock_init(&rzv2h_icu_data->lock); irq_domain = irq_domain_create_hierarchy(parent_domain, 0, ICU_NUM_IRQ, - dev_fwnode(&pdev->dev), &rzv2h_icu_domain_ops, + dev_fwnode(dev), &rzv2h_icu_domain_ops, rzv2h_icu_data); if (!irq_domain) { - dev_err(&pdev->dev, "failed to add irq domain\n"); + dev_err(dev, "failed to add irq domain\n"); ret = -ENOMEM; goto pm_put; } @@ -616,12 +617,12 @@ static int rzv2h_icu_probe_common(struct platform_device *pdev, struct device_no /* * coccicheck complains about a missing put_device call before returning, but it's a false - * positive. We still need &pdev->dev after successfully returning from this function. + * positive. We still need dev after successfully returning from this function. */ return 0; pm_put: - pm_runtime_put(&pdev->dev); + pm_runtime_put(dev); return ret; } -- cgit v1.2.3 From c34368b0404b8fd610b4f589481a1339bab76e0f Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 4 Mar 2026 11:33:13 +0000 Subject: irqchip/renesas-rzv2h: Switch to using dev_err_probe() Make use of dev_err_probe() to simplify rzv2h_icu_probe_common(). Keep dev_err() for -ENOMEM paths, as dev_err_probe() does not print for allocation failures, ensuring they remain visible in logs. Signed-off-by: Lad Prabhakar Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260304113317.129339-4-prabhakar.mahadev-lad.rj@bp.renesas.com --- drivers/irqchip/irq-renesas-rzv2h.c | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-rzv2h.c b/drivers/irqchip/irq-renesas-rzv2h.c index 766b981cf3d8..444da7804f15 100644 --- a/drivers/irqchip/irq-renesas-rzv2h.c +++ b/drivers/irqchip/irq-renesas-rzv2h.c @@ -560,10 +560,8 @@ static int rzv2h_icu_probe_common(struct platform_device *pdev, struct device_no int ret; parent_domain = irq_find_host(parent); - if (!parent_domain) { - dev_err(dev, "cannot find parent domain\n"); - return -ENODEV; - } + if (!parent_domain) + return dev_err_probe(dev, -ENODEV, "cannot find parent domain\n"); rzv2h_icu_data = devm_kzalloc(dev, sizeof(*rzv2h_icu_data), GFP_KERNEL); if (!rzv2h_icu_data) @@ -576,29 +574,20 @@ static int rzv2h_icu_probe_common(struct platform_device *pdev, struct device_no return PTR_ERR(rzv2h_icu_data->base); ret = rzv2h_icu_parse_interrupts(rzv2h_icu_data, node); - if (ret) { - dev_err(dev, "cannot parse interrupts: %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "cannot parse interrupts\n"); resetn = devm_reset_control_get_exclusive_deasserted(dev, NULL); - if (IS_ERR(resetn)) { - ret = PTR_ERR(resetn); - dev_err(dev, "failed to acquire deasserted reset: %d\n", ret); - return ret; - } + if (IS_ERR(resetn)) + return dev_err_probe(dev, PTR_ERR(resetn), "failed to acquire deasserted reset\n"); ret = devm_pm_runtime_enable(dev); - if (ret < 0) { - dev_err(dev, "devm_pm_runtime_enable failed, %d\n", ret); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "devm_pm_runtime_enable failed\n"); ret = pm_runtime_resume_and_get(dev); - if (ret < 0) { - dev_err(dev, "pm_runtime_resume_and_get failed: %d\n", ret); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "pm_runtime_resume_and_get failed\n"); raw_spin_lock_init(&rzv2h_icu_data->lock); -- cgit v1.2.3 From 9dc4335758c983045ab38871e2411fa1ae7e438d Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 4 Mar 2026 11:33:14 +0000 Subject: irqchip/renesas-rzv2h: Clarify IRQ range definitions and tighten TINT validation Introduce ICU_IRQ_LAST and ICU_TINT_LAST macros to make range boundaries explicit and reduce the chance of off-by-one errors. Extract the TINT information up front in rzv2h_icu_alloc() and validate the resulting hardware IRQ against the full TINT range [ICU_TINT_START, ICU_TINT_LAST]. [ tglx: Convert the hard to parse inverse conditions to use a simple helper macro hwirq_within() which is easy to read, less error prone and avoids a lot of typing ] Signed-off-by: Lad Prabhakar Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260304113317.129339-5-prabhakar.mahadev-lad.rj@bp.renesas.com --- drivers/irqchip/irq-renesas-rzv2h.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-rzv2h.c b/drivers/irqchip/irq-renesas-rzv2h.c index 444da7804f15..7ebc0f9801d7 100644 --- a/drivers/irqchip/irq-renesas-rzv2h.c +++ b/drivers/irqchip/irq-renesas-rzv2h.c @@ -25,9 +25,11 @@ /* DT "interrupts" indexes */ #define ICU_IRQ_START 1 #define ICU_IRQ_COUNT 16 -#define ICU_TINT_START (ICU_IRQ_START + ICU_IRQ_COUNT) +#define ICU_IRQ_LAST (ICU_IRQ_START + ICU_IRQ_COUNT - 1) +#define ICU_TINT_START (ICU_IRQ_LAST + 1) #define ICU_TINT_COUNT 32 -#define ICU_NUM_IRQ (ICU_TINT_START + ICU_TINT_COUNT) +#define ICU_TINT_LAST (ICU_TINT_START + ICU_TINT_COUNT - 1) +#define ICU_NUM_IRQ (ICU_TINT_LAST + 1) /* Registers */ #define ICU_NSCNT 0x00 @@ -489,6 +491,8 @@ static const struct irq_chip rzv2h_icu_chip = { IRQCHIP_SKIP_SET_WAKE, }; +#define hwirq_within(hwirq, which) ((hwirq) >= which##_START && (hwirq) <= which##_LAST) + static int rzv2h_icu_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs, void *arg) { @@ -508,11 +512,11 @@ static int rzv2h_icu_alloc(struct irq_domain *domain, unsigned int virq, unsigne * hwirq is embedded in bits 0-15. * TINT is embedded in bits 16-31. */ - if (hwirq >= ICU_TINT_START) { - tint = ICU_TINT_EXTRACT_GPIOINT(hwirq); + tint = ICU_TINT_EXTRACT_GPIOINT(hwirq); + if (tint || hwirq_within(hwirq, ICU_TINT)) { hwirq = ICU_TINT_EXTRACT_HWIRQ(hwirq); - if (hwirq < ICU_TINT_START) + if (!hwirq_within(hwirq, ICU_TINT)) return -EINVAL; } -- cgit v1.2.3 From f3ebae6dc025d0e45e9240ed85f9909a5a91e03c Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 4 Mar 2026 11:33:15 +0000 Subject: irqchip/renesas-rzv2h: Replace single irq_chip with per-region irq_chip instances Replace the single rzv2h_icu_chip and its dispatcher callbacks with dedicated irq_chip instances for each interrupt region: NMI, IRQ, and TINT. Move the irqd_is_level_type() check ahead of the scoped_guard in rzv2h_icu_tint_eoi() and rzv2h_icu_irq_eoi() to avoid acquiring the spinlock unnecessarily for level-type interrupts. Drop the ICU_TINT_START guard from rzv2h_tint_irq_endisable() since it is now only reachable via the TINT chip path. [ tglx: Convert to hwirq_within() ] Signed-off-by: Lad Prabhakar Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260304113317.129339-6-prabhakar.mahadev-lad.rj@bp.renesas.com --- drivers/irqchip/irq-renesas-rzv2h.c | 174 +++++++++++++++++++++--------------- 1 file changed, 104 insertions(+), 70 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-rzv2h.c b/drivers/irqchip/irq-renesas-rzv2h.c index 7ebc0f9801d7..a90107d1c9d3 100644 --- a/drivers/irqchip/irq-renesas-rzv2h.c +++ b/drivers/irqchip/irq-renesas-rzv2h.c @@ -169,32 +169,47 @@ static inline struct rzv2h_icu_priv *irq_data_to_priv(struct irq_data *data) return data->domain->host_data; } -static void rzv2h_icu_eoi(struct irq_data *d) +static void rzv2h_icu_tint_eoi(struct irq_data *d) { struct rzv2h_icu_priv *priv = irq_data_to_priv(d); unsigned int hw_irq = irqd_to_hwirq(d); unsigned int tintirq_nr; u32 bit; - scoped_guard(raw_spinlock, &priv->lock) { - if (hw_irq >= ICU_TINT_START) { - tintirq_nr = hw_irq - ICU_TINT_START; - bit = BIT(tintirq_nr); - if (!irqd_is_level_type(d)) - writel_relaxed(bit, priv->base + priv->info->t_offs + ICU_TSCLR); - } else if (hw_irq >= ICU_IRQ_START) { - tintirq_nr = hw_irq - ICU_IRQ_START; - bit = BIT(tintirq_nr); - if (!irqd_is_level_type(d)) - writel_relaxed(bit, priv->base + ICU_ISCLR); - } else { - writel_relaxed(ICU_NSCLR_NCLR, priv->base + ICU_NSCLR); - } + if (!irqd_is_level_type(d)) { + tintirq_nr = hw_irq - ICU_TINT_START; + bit = BIT(tintirq_nr); + writel_relaxed(bit, priv->base + priv->info->t_offs + ICU_TSCLR); + } + + irq_chip_eoi_parent(d); +} + +static void rzv2h_icu_irq_eoi(struct irq_data *d) +{ + struct rzv2h_icu_priv *priv = irq_data_to_priv(d); + unsigned int hw_irq = irqd_to_hwirq(d); + unsigned int tintirq_nr; + u32 bit; + + if (!irqd_is_level_type(d)) { + tintirq_nr = hw_irq - ICU_IRQ_START; + bit = BIT(tintirq_nr); + writel_relaxed(bit, priv->base + ICU_ISCLR); } irq_chip_eoi_parent(d); } +static void rzv2h_icu_nmi_eoi(struct irq_data *d) +{ + struct rzv2h_icu_priv *priv = irq_data_to_priv(d); + + writel_relaxed(ICU_NSCLR_NCLR, priv->base + ICU_NSCLR); + + irq_chip_eoi_parent(d); +} + static void rzv2h_tint_irq_endisable(struct irq_data *d, bool enable) { struct rzv2h_icu_priv *priv = irq_data_to_priv(d); @@ -202,9 +217,6 @@ static void rzv2h_tint_irq_endisable(struct irq_data *d, bool enable) u32 tint_nr, tssel_n, k, tssr; u8 nr_tint; - if (hw_irq < ICU_TINT_START) - return; - tint_nr = hw_irq - ICU_TINT_START; nr_tint = 32 / priv->info->field_width; k = tint_nr / nr_tint; @@ -227,13 +239,13 @@ static void rzv2h_tint_irq_endisable(struct irq_data *d, bool enable) writel_relaxed(BIT(tint_nr), priv->base + priv->info->t_offs + ICU_TSCLR); } -static void rzv2h_icu_irq_disable(struct irq_data *d) +static void rzv2h_icu_tint_disable(struct irq_data *d) { irq_chip_disable_parent(d); rzv2h_tint_irq_endisable(d, false); } -static void rzv2h_icu_irq_enable(struct irq_data *d) +static void rzv2h_icu_tint_enable(struct irq_data *d) { rzv2h_tint_irq_endisable(d, true); irq_chip_enable_parent(d); @@ -259,7 +271,7 @@ static int rzv2h_nmi_set_type(struct irq_data *d, unsigned int type) writel_relaxed(sense, priv->base + ICU_NITSR); - return 0; + return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH); } static void rzv2h_clear_irq_int(struct rzv2h_icu_priv *priv, unsigned int hwirq) @@ -309,14 +321,15 @@ static int rzv2h_irq_set_type(struct irq_data *d, unsigned int type) return -EINVAL; } - guard(raw_spinlock)(&priv->lock); - iitsr = readl_relaxed(priv->base + ICU_IITSR); - iitsr &= ~ICU_IITSR_IITSEL_MASK(irq_nr); - iitsr |= ICU_IITSR_IITSEL_PREP(sense, irq_nr); - rzv2h_clear_irq_int(priv, hwirq); - writel_relaxed(iitsr, priv->base + ICU_IITSR); + scoped_guard(raw_spinlock, &priv->lock) { + iitsr = readl_relaxed(priv->base + ICU_IITSR); + iitsr &= ~ICU_IITSR_IITSEL_MASK(irq_nr); + iitsr |= ICU_IITSR_IITSEL_PREP(sense, irq_nr); + rzv2h_clear_irq_int(priv, hwirq); + writel_relaxed(iitsr, priv->base + ICU_IITSR); + } - return 0; + return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH); } static void rzv2h_clear_tint_int(struct rzv2h_icu_priv *priv, unsigned int hwirq) @@ -391,48 +404,30 @@ static int rzv2h_tint_set_type(struct irq_data *d, unsigned int type) titsr_k = ICU_TITSR_K(tint_nr); titsel_n = ICU_TITSR_TITSEL_N(tint_nr); - guard(raw_spinlock)(&priv->lock); - - tssr = readl_relaxed(priv->base + priv->info->t_offs + ICU_TSSR(tssr_k)); - titsr = readl_relaxed(priv->base + priv->info->t_offs + ICU_TITSR(titsr_k)); - - tssr_cur = field_get(ICU_TSSR_TSSEL_MASK(tssel_n, priv->info->field_width), tssr); - titsr_cur = field_get(ICU_TITSR_TITSEL_MASK(titsel_n), titsr); - if (tssr_cur == tint && titsr_cur == sense) - return 0; - - tssr &= ~(ICU_TSSR_TSSEL_MASK(tssel_n, priv->info->field_width) | tien); - tssr |= ICU_TSSR_TSSEL_PREP(tint, tssel_n, priv->info->field_width); - - writel_relaxed(tssr, priv->base + priv->info->t_offs + ICU_TSSR(tssr_k)); - - titsr &= ~ICU_TITSR_TITSEL_MASK(titsel_n); - titsr |= ICU_TITSR_TITSEL_PREP(sense, titsel_n); + scoped_guard(raw_spinlock, &priv->lock) { + tssr = readl_relaxed(priv->base + priv->info->t_offs + ICU_TSSR(tssr_k)); + titsr = readl_relaxed(priv->base + priv->info->t_offs + ICU_TITSR(titsr_k)); - writel_relaxed(titsr, priv->base + priv->info->t_offs + ICU_TITSR(titsr_k)); + tssr_cur = field_get(ICU_TSSR_TSSEL_MASK(tssel_n, priv->info->field_width), tssr); + titsr_cur = field_get(ICU_TITSR_TITSEL_MASK(titsel_n), titsr); + if (tssr_cur == tint && titsr_cur == sense) + goto set_parent_type; - rzv2h_clear_tint_int(priv, hwirq); + tssr &= ~(ICU_TSSR_TSSEL_MASK(tssel_n, priv->info->field_width) | tien); + tssr |= ICU_TSSR_TSSEL_PREP(tint, tssel_n, priv->info->field_width); - writel_relaxed(tssr | tien, priv->base + priv->info->t_offs + ICU_TSSR(tssr_k)); + writel_relaxed(tssr, priv->base + priv->info->t_offs + ICU_TSSR(tssr_k)); - return 0; -} + titsr &= ~ICU_TITSR_TITSEL_MASK(titsel_n); + titsr |= ICU_TITSR_TITSEL_PREP(sense, titsel_n); -static int rzv2h_icu_set_type(struct irq_data *d, unsigned int type) -{ - unsigned int hw_irq = irqd_to_hwirq(d); - int ret; - - if (hw_irq >= ICU_TINT_START) - ret = rzv2h_tint_set_type(d, type); - else if (hw_irq >= ICU_IRQ_START) - ret = rzv2h_irq_set_type(d, type); - else - ret = rzv2h_nmi_set_type(d, type); + writel_relaxed(titsr, priv->base + priv->info->t_offs + ICU_TITSR(titsr_k)); - if (ret) - return ret; + rzv2h_clear_tint_int(priv, hwirq); + writel_relaxed(tssr | tien, priv->base + priv->info->t_offs + ICU_TSSR(tssr_k)); + } +set_parent_type: return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH); } @@ -474,17 +469,51 @@ static struct syscore rzv2h_irqc_syscore = { .ops = &rzv2h_irqc_syscore_ops, }; -static const struct irq_chip rzv2h_icu_chip = { +static const struct irq_chip rzv2h_icu_tint_chip = { + .name = "rzv2h-icu", + .irq_eoi = rzv2h_icu_tint_eoi, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_disable = rzv2h_icu_tint_disable, + .irq_enable = rzv2h_icu_tint_enable, + .irq_get_irqchip_state = irq_chip_get_parent_state, + .irq_set_irqchip_state = irq_chip_set_parent_state, + .irq_retrigger = irq_chip_retrigger_hierarchy, + .irq_set_type = rzv2h_tint_set_type, + .irq_set_affinity = irq_chip_set_affinity_parent, + .flags = IRQCHIP_MASK_ON_SUSPEND | + IRQCHIP_SET_TYPE_MASKED | + IRQCHIP_SKIP_SET_WAKE, +}; + +static const struct irq_chip rzv2h_icu_irq_chip = { + .name = "rzv2h-icu", + .irq_eoi = rzv2h_icu_irq_eoi, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_disable = irq_chip_disable_parent, + .irq_enable = irq_chip_enable_parent, + .irq_get_irqchip_state = irq_chip_get_parent_state, + .irq_set_irqchip_state = irq_chip_set_parent_state, + .irq_retrigger = irq_chip_retrigger_hierarchy, + .irq_set_type = rzv2h_irq_set_type, + .irq_set_affinity = irq_chip_set_affinity_parent, + .flags = IRQCHIP_MASK_ON_SUSPEND | + IRQCHIP_SET_TYPE_MASKED | + IRQCHIP_SKIP_SET_WAKE, +}; + +static const struct irq_chip rzv2h_icu_nmi_chip = { .name = "rzv2h-icu", - .irq_eoi = rzv2h_icu_eoi, + .irq_eoi = rzv2h_icu_nmi_eoi, .irq_mask = irq_chip_mask_parent, .irq_unmask = irq_chip_unmask_parent, - .irq_disable = rzv2h_icu_irq_disable, - .irq_enable = rzv2h_icu_irq_enable, + .irq_disable = irq_chip_disable_parent, + .irq_enable = irq_chip_enable_parent, .irq_get_irqchip_state = irq_chip_get_parent_state, .irq_set_irqchip_state = irq_chip_set_parent_state, .irq_retrigger = irq_chip_retrigger_hierarchy, - .irq_set_type = rzv2h_icu_set_type, + .irq_set_type = rzv2h_nmi_set_type, .irq_set_affinity = irq_chip_set_affinity_parent, .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SET_TYPE_MASKED | @@ -497,6 +526,7 @@ static int rzv2h_icu_alloc(struct irq_domain *domain, unsigned int virq, unsigne void *arg) { struct rzv2h_icu_priv *priv = domain->host_data; + const struct irq_chip *chip; unsigned long tint = 0; irq_hw_number_t hwirq; unsigned int type; @@ -518,13 +548,17 @@ static int rzv2h_icu_alloc(struct irq_domain *domain, unsigned int virq, unsigne if (!hwirq_within(hwirq, ICU_TINT)) return -EINVAL; + chip = &rzv2h_icu_tint_chip; + } else if (hwirq_within(hwirq, ICU_IRQ)) { + chip = &rzv2h_icu_irq_chip; + } else { + chip = &rzv2h_icu_nmi_chip; } if (hwirq > (ICU_NUM_IRQ - 1)) return -EINVAL; - ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, &rzv2h_icu_chip, - (void *)(uintptr_t)tint); + ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, chip, (void *)(uintptr_t)tint); if (ret) return ret; -- cgit v1.2.3 From 61adc4813d67990f6f9c20ab8c4a57fb3d969322 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 4 Mar 2026 11:33:16 +0000 Subject: irqchip/renesas-rzv2h: Add CA55 software interrupt support The RZ/V2H ICU exposes four software-triggerable interrupts targeting the CA55 cores (int-ca55-0 to int-ca55-3). Add support for these interrupts to enable IRQ injection via the generic IRQ injection framework. Add a dedicated rzv2h_icu_swint_chip irq_chip for the CA55 region and implement rzv2h_icu_irq_set_irqchip_state() to handle software interrupt injection. [ tglx: Convert to hwirq_within() ] Signed-off-by: Lad Prabhakar Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260304113317.129339-7-prabhakar.mahadev-lad.rj@bp.renesas.com --- drivers/irqchip/irq-renesas-rzv2h.c | 95 ++++++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-rzv2h.c b/drivers/irqchip/irq-renesas-rzv2h.c index a90107d1c9d3..b10f77ccad49 100644 --- a/drivers/irqchip/irq-renesas-rzv2h.c +++ b/drivers/irqchip/irq-renesas-rzv2h.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -29,7 +30,10 @@ #define ICU_TINT_START (ICU_IRQ_LAST + 1) #define ICU_TINT_COUNT 32 #define ICU_TINT_LAST (ICU_TINT_START + ICU_TINT_COUNT - 1) -#define ICU_NUM_IRQ (ICU_TINT_LAST + 1) +#define ICU_CA55_INT_START (ICU_TINT_LAST + 1) +#define ICU_CA55_INT_COUNT 4 +#define ICU_CA55_INT_LAST (ICU_CA55_INT_START + ICU_CA55_INT_COUNT - 1) +#define ICU_NUM_IRQ (ICU_CA55_INT_LAST + 1) /* Registers */ #define ICU_NSCNT 0x00 @@ -42,6 +46,7 @@ #define ICU_TSCLR 0x24 #define ICU_TITSR(k) (0x28 + (k) * 4) #define ICU_TSSR(k) (0x30 + (k) * 4) +#define ICU_SWINT 0x130 #define ICU_DMkSELy(k, y) (0x420 + (k) * 0x20 + (y) * 4) #define ICU_DMACKSELk(k) (0x500 + (k) * 4) @@ -431,6 +436,27 @@ set_parent_type: return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH); } +static int rzv2h_icu_swint_set_irqchip_state(struct irq_data *d, enum irqchip_irq_state which, + bool state) +{ + unsigned int hwirq = irqd_to_hwirq(d); + struct rzv2h_icu_priv *priv; + unsigned int bit; + + if (which != IRQCHIP_STATE_PENDING) + return irq_chip_set_parent_state(d, which, state); + + if (!state) + return 0; + + priv = irq_data_to_priv(d); + bit = BIT(hwirq - ICU_CA55_INT_START); + + /* Trigger the software interrupt */ + writel_relaxed(bit, priv->base + ICU_SWINT); + return 0; +} + static int rzv2h_irqc_irq_suspend(void *data) { struct rzv2h_irqc_reg_cache *cache = &rzv2h_icu_data->cache; @@ -520,6 +546,23 @@ static const struct irq_chip rzv2h_icu_nmi_chip = { IRQCHIP_SKIP_SET_WAKE, }; +static const struct irq_chip rzv2h_icu_swint_chip = { + .name = "rzv2h-icu", + .irq_eoi = irq_chip_eoi_parent, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_disable = irq_chip_disable_parent, + .irq_enable = irq_chip_enable_parent, + .irq_get_irqchip_state = irq_chip_get_parent_state, + .irq_set_irqchip_state = rzv2h_icu_swint_set_irqchip_state, + .irq_retrigger = irq_chip_retrigger_hierarchy, + .irq_set_type = irq_chip_set_type_parent, + .irq_set_affinity = irq_chip_set_affinity_parent, + .flags = IRQCHIP_MASK_ON_SUSPEND | + IRQCHIP_SET_TYPE_MASKED | + IRQCHIP_SKIP_SET_WAKE, +}; + #define hwirq_within(hwirq, which) ((hwirq) >= which##_START && (hwirq) <= which##_LAST) static int rzv2h_icu_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs, @@ -551,6 +594,8 @@ static int rzv2h_icu_alloc(struct irq_domain *domain, unsigned int virq, unsigne chip = &rzv2h_icu_tint_chip; } else if (hwirq_within(hwirq, ICU_IRQ)) { chip = &rzv2h_icu_irq_chip; + } else if (hwirq_within(hwirq, ICU_CA55_INT)) { + chip = &rzv2h_icu_swint_chip; } else { chip = &rzv2h_icu_nmi_chip; } @@ -588,6 +633,50 @@ static int rzv2h_icu_parse_interrupts(struct rzv2h_icu_priv *priv, struct device return 0; } +static irqreturn_t rzv2h_icu_swint_irq(int irq, void *data) +{ + u8 cpu = *(u8 *)data; + + pr_info("SWINT interrupt for CA55 core %u\n", cpu); + return IRQ_HANDLED; +} + +static int rzv2h_icu_setup_irqs(struct platform_device *pdev, struct irq_domain *irq_domain) +{ + bool irq_inject = IS_ENABLED(CONFIG_GENERIC_IRQ_INJECTION); + static const char * const rzv2h_swint_names[] = { + "int-ca55-0", "int-ca55-1", + "int-ca55-2", "int-ca55-3", + }; + static const u8 swint_idx[] = { 0, 1, 2, 3 }; + struct device *dev = &pdev->dev; + struct irq_fwspec fwspec; + unsigned int i, virq; + int ret; + + for (i = 0; i < ICU_CA55_INT_COUNT && irq_inject; i++) { + fwspec.fwnode = irq_domain->fwnode; + fwspec.param_count = 2; + fwspec.param[0] = ICU_CA55_INT_START + i; + fwspec.param[1] = IRQ_TYPE_EDGE_RISING; + + virq = irq_create_fwspec_mapping(&fwspec); + if (!virq) { + return dev_err_probe(dev, -EINVAL, "failed to create IRQ mapping for %s\n", + rzv2h_swint_names[i]); + } + + ret = devm_request_irq(dev, virq, rzv2h_icu_swint_irq, 0, dev_name(dev), + (void *)&swint_idx[i]); + if (ret) { + return dev_err_probe(dev, ret, "Failed to request %s IRQ\n", + rzv2h_swint_names[i]); + } + } + + return 0; +} + static int rzv2h_icu_probe_common(struct platform_device *pdev, struct device_node *parent, const struct rzv2h_hw_info *hw_info) { @@ -642,6 +731,10 @@ static int rzv2h_icu_probe_common(struct platform_device *pdev, struct device_no register_syscore(&rzv2h_irqc_syscore); + ret = rzv2h_icu_setup_irqs(pdev, irq_domain); + if (ret) + goto pm_put; + /* * coccicheck complains about a missing put_device call before returning, but it's a false * positive. We still need dev after successfully returning from this function. -- cgit v1.2.3 From 7585a27644f338b3e764ceeda4be10e7047331a7 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 4 Mar 2026 11:33:17 +0000 Subject: irqchip/renesas-rzv2h: Handle ICU error IRQ and add SWPE trigger Handle the RZ/V2H ICU error interrupt to help diagnose latched bus, ECC RAM, and CA55/IP error conditions. Support error injection via ICU_SWPE to allow testing the pseudo error error interrupts. Account for SoC differences in ECC RAM error register coverage so the handler only iterates over valid ECC status/clear banks, and route the RZ/V2N compatible to a probe path with the correct ECC range while keeping the existing RZ/V2H and RZ/G3E handling. [ tglx: Convert to hwirq_within() and upgrade to pr_warn() for those errors ] Signed-off-by: Lad Prabhakar Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260304113317.129339-8-prabhakar.mahadev-lad.rj@bp.renesas.com --- drivers/irqchip/irq-renesas-rzv2h.c | 163 +++++++++++++++++++++++++++++++++++- 1 file changed, 161 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-rzv2h.c b/drivers/irqchip/irq-renesas-rzv2h.c index b10f77ccad49..ce790590f7ca 100644 --- a/drivers/irqchip/irq-renesas-rzv2h.c +++ b/drivers/irqchip/irq-renesas-rzv2h.c @@ -33,7 +33,10 @@ #define ICU_CA55_INT_START (ICU_TINT_LAST + 1) #define ICU_CA55_INT_COUNT 4 #define ICU_CA55_INT_LAST (ICU_CA55_INT_START + ICU_CA55_INT_COUNT - 1) -#define ICU_NUM_IRQ (ICU_CA55_INT_LAST + 1) +#define ICU_ERR_INT_START (ICU_CA55_INT_LAST + 1) +#define ICU_ERR_INT_COUNT 1 +#define ICU_ERR_INT_LAST (ICU_ERR_INT_START + ICU_ERR_INT_COUNT - 1) +#define ICU_NUM_IRQ (ICU_ERR_INT_LAST + 1) /* Registers */ #define ICU_NSCNT 0x00 @@ -46,7 +49,15 @@ #define ICU_TSCLR 0x24 #define ICU_TITSR(k) (0x28 + (k) * 4) #define ICU_TSSR(k) (0x30 + (k) * 4) +#define ICU_BEISR(k) (0x70 + (k) * 4) +#define ICU_BECLR(k) (0x80 + (k) * 4) +#define ICU_EREISR(k) (0x90 + (k) * 4) +#define ICU_ERCLR(k) (0xE0 + (k) * 4) #define ICU_SWINT 0x130 +#define ICU_ERINTA55CTL(k) (0x338 + (k) * 4) +#define ICU_ERINTA55CRL(k) (0x348 + (k) * 4) +#define ICU_ERINTA55MSK(k) (0x358 + (k) * 4) +#define ICU_SWPE 0x370 #define ICU_DMkSELy(k, y) (0x420 + (k) * 0x20 + (y) * 4) #define ICU_DMACKSELk(k) (0x500 + (k) * 4) @@ -97,6 +108,10 @@ #define ICU_RZG3E_TSSEL_MAX_VAL 0x8c #define ICU_RZV2H_TSSEL_MAX_VAL 0x55 +#define ICU_SWPE_NUM 16 +#define ICU_NUM_BE 4 +#define ICU_NUM_A55ERR 4 + /** * struct rzv2h_irqc_reg_cache - registers cache (necessary for suspend/resume) * @nitsr: ICU_NITSR register @@ -115,12 +130,16 @@ struct rzv2h_irqc_reg_cache { * @t_offs: TINT offset * @max_tssel: TSSEL max value * @field_width: TSSR field width + * @ecc_start: Start index of ECC RAM interrupts + * @ecc_end: End index of ECC RAM interrupts */ struct rzv2h_hw_info { const u8 *tssel_lut; u16 t_offs; u8 max_tssel; u8 field_width; + u8 ecc_start; + u8 ecc_end; }; /* DMAC */ @@ -454,6 +473,36 @@ static int rzv2h_icu_swint_set_irqchip_state(struct irq_data *d, enum irqchip_ir /* Trigger the software interrupt */ writel_relaxed(bit, priv->base + ICU_SWINT); + + return 0; +} + +static int rzv2h_icu_swpe_set_irqchip_state(struct irq_data *d, enum irqchip_irq_state which, + bool state) +{ + struct rzv2h_icu_priv *priv; + unsigned int bit; + static u8 swpe; + + if (which != IRQCHIP_STATE_PENDING) + return irq_chip_set_parent_state(d, which, state); + + if (!state) + return 0; + + priv = irq_data_to_priv(d); + + bit = BIT(swpe); + /* + * SWPE has 16 bits; the bit position is rotated on each trigger + * and wraps around once all bits have been used. + */ + if (++swpe >= ICU_SWPE_NUM) + swpe = 0; + + /* Trigger the pseudo error interrupt */ + writel_relaxed(bit, priv->base + ICU_SWPE); + return 0; } @@ -563,6 +612,23 @@ static const struct irq_chip rzv2h_icu_swint_chip = { IRQCHIP_SKIP_SET_WAKE, }; +static const struct irq_chip rzv2h_icu_swpe_err_chip = { + .name = "rzv2h-icu", + .irq_eoi = irq_chip_eoi_parent, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_disable = irq_chip_disable_parent, + .irq_enable = irq_chip_enable_parent, + .irq_get_irqchip_state = irq_chip_get_parent_state, + .irq_set_irqchip_state = rzv2h_icu_swpe_set_irqchip_state, + .irq_retrigger = irq_chip_retrigger_hierarchy, + .irq_set_type = irq_chip_set_type_parent, + .irq_set_affinity = irq_chip_set_affinity_parent, + .flags = IRQCHIP_MASK_ON_SUSPEND | + IRQCHIP_SET_TYPE_MASKED | + IRQCHIP_SKIP_SET_WAKE, +}; + #define hwirq_within(hwirq, which) ((hwirq) >= which##_START && (hwirq) <= which##_LAST) static int rzv2h_icu_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs, @@ -596,6 +662,8 @@ static int rzv2h_icu_alloc(struct irq_domain *domain, unsigned int virq, unsigne chip = &rzv2h_icu_irq_chip; } else if (hwirq_within(hwirq, ICU_CA55_INT)) { chip = &rzv2h_icu_swint_chip; + } else if (hwirq_within(hwirq, ICU_ERR_INT)) { + chip = &rzv2h_icu_swpe_err_chip; } else { chip = &rzv2h_icu_nmi_chip; } @@ -633,6 +701,48 @@ static int rzv2h_icu_parse_interrupts(struct rzv2h_icu_priv *priv, struct device return 0; } +static irqreturn_t rzv2h_icu_error_irq(int irq, void *data) +{ + struct rzv2h_icu_priv *priv = data; + const struct rzv2h_hw_info *hw_info = priv->info; + void __iomem *base = priv->base; + unsigned int k; + u32 st; + + /* 1) Bus errors (BEISR0..3) */ + for (k = 0; k < ICU_NUM_BE; k++) { + st = readl(base + ICU_BEISR(k)); + if (!st) + continue; + + writel_relaxed(st, base + ICU_BECLR(k)); + pr_warn("rzv2h-icu: BUS error k=%u status=0x%08x\n", k, st); + } + + /* 2) ECC RAM errors (EREISR0..X) */ + for (k = hw_info->ecc_start; k <= hw_info->ecc_end; k++) { + st = readl(base + ICU_EREISR(k)); + if (!st) + continue; + + writel_relaxed(st, base + ICU_ERCLR(k)); + pr_warn("rzv2h-icu: ECC error k=%u status=0x%08x\n", k, st); + } + + /* 3) IP/CA55 error interrupt status (ERINTA55CTL0..3) */ + for (k = 0; k < ICU_NUM_A55ERR; k++) { + st = readl(base + ICU_ERINTA55CTL(k)); + if (!st) + continue; + + /* there is no relation with status bits so clear all the interrupts */ + writel_relaxed(0xffffffff, base + ICU_ERINTA55CRL(k)); + pr_warn("rzv2h-icu: IP/CA55 error k=%u status=0x%08x\n", k, st); + } + + return IRQ_HANDLED; +} + static irqreturn_t rzv2h_icu_swint_irq(int irq, void *data) { u8 cpu = *(u8 *)data; @@ -643,12 +753,15 @@ static irqreturn_t rzv2h_icu_swint_irq(int irq, void *data) static int rzv2h_icu_setup_irqs(struct platform_device *pdev, struct irq_domain *irq_domain) { + const struct rzv2h_hw_info *hw_info = rzv2h_icu_data->info; bool irq_inject = IS_ENABLED(CONFIG_GENERIC_IRQ_INJECTION); static const char * const rzv2h_swint_names[] = { "int-ca55-0", "int-ca55-1", "int-ca55-2", "int-ca55-3", }; + static const char *icu_err = "icu-error-ca55"; static const u8 swint_idx[] = { 0, 1, 2, 3 }; + void __iomem *base = rzv2h_icu_data->base; struct device *dev = &pdev->dev; struct irq_fwspec fwspec; unsigned int i, virq; @@ -674,6 +787,35 @@ static int rzv2h_icu_setup_irqs(struct platform_device *pdev, struct irq_domain } } + /* Unmask and clear all IP/CA55 error interrupts */ + for (i = 0; i < ICU_NUM_A55ERR; i++) { + writel_relaxed(0xffffff, base + ICU_ERINTA55CRL(i)); + writel_relaxed(0x0, base + ICU_ERINTA55MSK(i)); + } + + /* Clear all Bus errors */ + for (i = 0; i < ICU_NUM_BE; i++) + writel_relaxed(0xffffffff, base + ICU_BECLR(i)); + + /* Clear all ECCRAM errors */ + for (i = hw_info->ecc_start; i <= hw_info->ecc_end; i++) + writel_relaxed(0xffffffff, base + ICU_ERCLR(i)); + + fwspec.fwnode = irq_domain->fwnode; + fwspec.param_count = 2; + fwspec.param[0] = ICU_ERR_INT_START; + fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH; + + virq = irq_create_fwspec_mapping(&fwspec); + if (!virq) { + return dev_err_probe(dev, -EINVAL, "failed to create IRQ mapping for %s\n", + icu_err); + } + + ret = devm_request_irq(dev, virq, rzv2h_icu_error_irq, 0, dev_name(dev), rzv2h_icu_data); + if (ret) + return dev_err_probe(dev, ret, "Failed to request %s IRQ\n", icu_err); + return 0; } @@ -778,12 +920,24 @@ static const struct rzv2h_hw_info rzg3e_hw_params = { .t_offs = ICU_RZG3E_TINT_OFFSET, .max_tssel = ICU_RZG3E_TSSEL_MAX_VAL, .field_width = 16, + .ecc_start = 1, + .ecc_end = 4, +}; + +static const struct rzv2h_hw_info rzv2n_hw_params = { + .t_offs = 0, + .max_tssel = ICU_RZV2H_TSSEL_MAX_VAL, + .field_width = 8, + .ecc_start = 0, + .ecc_end = 2, }; static const struct rzv2h_hw_info rzv2h_hw_params = { .t_offs = 0, .max_tssel = ICU_RZV2H_TSSEL_MAX_VAL, .field_width = 8, + .ecc_start = 0, + .ecc_end = 11, }; static int rzg3e_icu_probe(struct platform_device *pdev, struct device_node *parent) @@ -791,6 +945,11 @@ static int rzg3e_icu_probe(struct platform_device *pdev, struct device_node *par return rzv2h_icu_probe_common(pdev, parent, &rzg3e_hw_params); } +static int rzv2n_icu_probe(struct platform_device *pdev, struct device_node *parent) +{ + return rzv2h_icu_probe_common(pdev, parent, &rzv2n_hw_params); +} + static int rzv2h_icu_probe(struct platform_device *pdev, struct device_node *parent) { return rzv2h_icu_probe_common(pdev, parent, &rzv2h_hw_params); @@ -798,7 +957,7 @@ static int rzv2h_icu_probe(struct platform_device *pdev, struct device_node *par IRQCHIP_PLATFORM_DRIVER_BEGIN(rzv2h_icu) IRQCHIP_MATCH("renesas,r9a09g047-icu", rzg3e_icu_probe) -IRQCHIP_MATCH("renesas,r9a09g056-icu", rzv2h_icu_probe) +IRQCHIP_MATCH("renesas,r9a09g056-icu", rzv2n_icu_probe) IRQCHIP_MATCH("renesas,r9a09g057-icu", rzv2h_icu_probe) IRQCHIP_PLATFORM_DRIVER_END(rzv2h_icu) MODULE_AUTHOR("Fabrizio Castro "); -- cgit v1.2.3 From d50590de0c646332713aa00a8be0012e089028e8 Mon Sep 17 00:00:00 2001 From: Philipp Hahn Date: Tue, 10 Mar 2026 12:49:05 +0100 Subject: irqchip: Use IS_ERR_OR_NULL() instead of NULL and IS_ERR() checks Prefer using IS_ERR_OR_NULL() over using IS_ERR() and a open coded NULL pointer check. Change generated with coccinelle. To: Marc Zyngier To: Thomas Gleixner To: Andrew Lunn To: Gregory Clement To: Sebastian Hesselbarth Signed-off-by: Philipp Hahn Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260310-b4-is_err_or_null-v1-39-bd63b656022d@avm.de --- drivers/irqchip/irq-gic-v3.c | 2 +- drivers/irqchip/irq-mvebu-odmi.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 20f13b686ab2..6dc9827357a2 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -2252,7 +2252,7 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare out_unmap_rdist: for (i = 0; i < nr_redist_regions; i++) - if (rdist_regs[i].redist_base && !IS_ERR(rdist_regs[i].redist_base)) + if (!IS_ERR_OR_NULL(rdist_regs[i].redist_base)) iounmap(rdist_regs[i].redist_base); kfree(rdist_regs); out_unmap_dist: diff --git a/drivers/irqchip/irq-mvebu-odmi.c b/drivers/irqchip/irq-mvebu-odmi.c index b99ab9dcc14b..94e7eda46e81 100644 --- a/drivers/irqchip/irq-mvebu-odmi.c +++ b/drivers/irqchip/irq-mvebu-odmi.c @@ -217,7 +217,7 @@ err_unmap: for (i = 0; i < odmis_count; i++) { struct odmi_data *odmi = &odmis[i]; - if (odmi->base && !IS_ERR(odmi->base)) + if (!IS_ERR_OR_NULL(odmi->base)) iounmap(odmis[i].base); } bitmap_free(odmis_bm); -- cgit v1.2.3 From 5e72917802dd65ad1ff57f2158a9d221b4fddf0b Mon Sep 17 00:00:00 2001 From: Ciprian Marian Costea Date: Wed, 11 Mar 2026 09:11:52 +0100 Subject: irqchip/imx-irqsteer: Add NXP S32N79 support Add support for the interrupt steering controller found in NXP S32N79 series automotive SoCs. The S32N79 IRQ_STEER variant differs from the i.MX version by not implementing the CHANCTRL register. To handle this hardware difference, introduce a device type data structure with quirks field. The IRQSTEER_QUIRK_NO_CHANCTRL quirk skips CHANCTRL register access for S32N79 variants. The interrupt routing functionality and register layout are otherwise identical between the two variants. Co-developed-by: Larisa Grigore Signed-off-by: Larisa Grigore Signed-off-by: Ciprian Marian Costea Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260311081154.381881-4-ciprianmarian.costea@oss.nxp.com --- drivers/irqchip/Kconfig | 6 ++--- drivers/irqchip/irq-imx-irqsteer.c | 53 ++++++++++++++++++++++++++++---------- 2 files changed, 43 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index dc26effa5b36..2feecfb5a28d 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -544,11 +544,11 @@ config CSKY_APB_INTC config IMX_IRQSTEER bool "i.MX IRQSTEER support" - depends on ARCH_MXC || COMPILE_TEST - default ARCH_MXC + depends on ARCH_MXC || ARCH_S32 || COMPILE_TEST + default y if ARCH_MXC || ARCH_S32 select IRQ_DOMAIN help - Support for the i.MX IRQSTEER interrupt multiplexer/remapper. + Support for the i.MX and S32 IRQSTEER interrupt multiplexer/remapper. config IMX_INTMUX bool "i.MX INTMUX support" if COMPILE_TEST diff --git a/drivers/irqchip/irq-imx-irqsteer.c b/drivers/irqchip/irq-imx-irqsteer.c index 4682ce5bf8d3..87b07f517be3 100644 --- a/drivers/irqchip/irq-imx-irqsteer.c +++ b/drivers/irqchip/irq-imx-irqsteer.c @@ -26,19 +26,38 @@ #define CHAN_MAX_OUTPUT_INT 0xF +/* SoC does not implement the CHANCTRL register */ +#define IRQSTEER_QUIRK_NO_CHANCTRL BIT(0) + +struct irqsteer_devtype_data { + u32 quirks; +}; + struct irqsteer_data { - void __iomem *regs; - struct clk *ipg_clk; - int irq[CHAN_MAX_OUTPUT_INT]; - int irq_count; - raw_spinlock_t lock; - int reg_num; - int channel; - struct irq_domain *domain; - u32 *saved_reg; - struct device *dev; + void __iomem *regs; + struct clk *ipg_clk; + int irq[CHAN_MAX_OUTPUT_INT]; + int irq_count; + raw_spinlock_t lock; + int reg_num; + int channel; + struct irq_domain *domain; + u32 *saved_reg; + struct device *dev; + const struct irqsteer_devtype_data *devtype_data; +}; + +static const struct irqsteer_devtype_data imx_data = { }; + +static const struct irqsteer_devtype_data s32n79_data = { + .quirks = IRQSTEER_QUIRK_NO_CHANCTRL, }; +static bool irqsteer_has_chanctrl(const struct irqsteer_devtype_data *data) +{ + return !(data->quirks & IRQSTEER_QUIRK_NO_CHANCTRL); +} + static int imx_irqsteer_get_reg_index(struct irqsteer_data *data, unsigned long irqnum) { @@ -188,6 +207,10 @@ static int imx_irqsteer_probe(struct platform_device *pdev) if (ret) return ret; + data->devtype_data = device_get_match_data(&pdev->dev); + if (!data->devtype_data) + return dev_err_probe(&pdev->dev, -ENODEV, "failed to match device data\n"); + /* * There is one output irq for each group of 64 inputs. * One register bit map can represent 32 input interrupts. @@ -210,7 +233,8 @@ static int imx_irqsteer_probe(struct platform_device *pdev) } /* steer all IRQs into configured channel */ - writel_relaxed(BIT(data->channel), data->regs + CHANCTRL); + if (irqsteer_has_chanctrl(data->devtype_data)) + writel_relaxed(BIT(data->channel), data->regs + CHANCTRL); data->domain = irq_domain_create_linear(dev_fwnode(&pdev->dev), data->reg_num * 32, &imx_irqsteer_domain_ops, data); @@ -279,7 +303,9 @@ static void imx_irqsteer_restore_regs(struct irqsteer_data *data) { int i; - writel_relaxed(BIT(data->channel), data->regs + CHANCTRL); + if (irqsteer_has_chanctrl(data->devtype_data)) + writel_relaxed(BIT(data->channel), data->regs + CHANCTRL); + for (i = 0; i < data->reg_num; i++) writel_relaxed(data->saved_reg[i], data->regs + CHANMASK(i, data->reg_num)); @@ -319,7 +345,8 @@ static const struct dev_pm_ops imx_irqsteer_pm_ops = { }; static const struct of_device_id imx_irqsteer_dt_ids[] = { - { .compatible = "fsl,imx-irqsteer", }, + { .compatible = "fsl,imx-irqsteer", .data = &imx_data }, + { .compatible = "nxp,s32n79-irqsteer", .data = &s32n79_data }, {}, }; -- cgit v1.2.3 From 1f0cf05155175849e2f747d26ef1d59e97e280db Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 23 Feb 2026 21:42:47 +0100 Subject: irqchip/apple-aic: Add support for "apple,t8122-aic3" Introduce support for the new AICv3 hardware block in t8122 and t603x SoCs. AICv3 is similar to AICv2 but has an increased IRQ config offset. These MMIO offsets are coded as properties of the "aic,3" node in Apple's device tree. The actual offsets are the same for all SoCs starting from M3 through at least M5. So do not bother to follow suit but use AICv3 specific defines in the driver. The compatible string is SoC specific so future SoCs with AICv3 and different offsets would just use their own compatible string as base and add their new offsets. Signed-off-by: Janne Grunau Signed-off-by: Thomas Gleixner Reviewed-by: Sven Peter Link: https://patch.msgid.link/20260223-irq-apple-aic3-v3-2-2b7328076b8d@jannau.net --- drivers/irqchip/irq-apple-aic.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-apple-aic.c b/drivers/irqchip/irq-apple-aic.c index 2b24c82bb0df..4a3141d9f914 100644 --- a/drivers/irqchip/irq-apple-aic.c +++ b/drivers/irqchip/irq-apple-aic.c @@ -134,8 +134,12 @@ #define AIC2_IRQ_CFG 0x2000 +/* AIC v3 registers (MMIO) */ +#define AIC3_IRQ_CFG 0x10000 + /* * AIC2 registers are laid out like this, starting at AIC2_IRQ_CFG: + * AIC3 registers use the same layout but start at AIC3_IRQ_CFG: * * Repeat for each die: * IRQ_CFG: u32 * MAX_IRQS @@ -293,6 +297,15 @@ static const struct aic_info aic2_info __initconst = { .local_fast_ipi = true, }; +static const struct aic_info aic3_info __initconst = { + .version = 3, + + .irq_cfg = AIC3_IRQ_CFG, + + .fast_ipi = true, + .local_fast_ipi = true, +}; + static const struct of_device_id aic_info_match[] = { { .compatible = "apple,t8103-aic", @@ -310,6 +323,10 @@ static const struct of_device_id aic_info_match[] = { .compatible = "apple,aic2", .data = &aic2_info, }, + { + .compatible = "apple,t8122-aic3", + .data = &aic3_info, + }, {} }; @@ -620,7 +637,7 @@ static int aic_irq_domain_map(struct irq_domain *id, unsigned int irq, u32 type = FIELD_GET(AIC_EVENT_TYPE, hw); struct irq_chip *chip = &aic_chip; - if (ic->info.version == 2) + if (ic->info.version == 2 || ic->info.version == 3) chip = &aic2_chip; if (type == AIC_EVENT_TYPE_IRQ) { @@ -991,7 +1008,7 @@ static int __init aic_of_ic_init(struct device_node *node, struct device_node *p break; } - case 2: { + case 2 ... 3: { u32 info1, info3; info1 = aic_ic_read(irqc, AIC2_INFO1); @@ -1065,7 +1082,7 @@ static int __init aic_of_ic_init(struct device_node *node, struct device_node *p off += irqc->info.die_stride; } - if (irqc->info.version == 2) { + if (irqc->info.version == 2 || irqc->info.version == 3) { u32 config = aic_ic_read(irqc, AIC2_CONFIG); config |= AIC2_CONFIG_ENABLE; @@ -1116,3 +1133,4 @@ err_unmap: IRQCHIP_DECLARE(apple_aic, "apple,aic", aic_of_ic_init); IRQCHIP_DECLARE(apple_aic2, "apple,aic2", aic_of_ic_init); +IRQCHIP_DECLARE(apple_aic3, "apple,t8122-aic3", aic_of_ic_init); -- cgit v1.2.3 From 29c10a0af04e9b3eb7b06e72e9f75fccca45205e Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Sat, 21 Mar 2026 17:20:30 +0800 Subject: irqchip/loongson-pch-lpc: Extract non-ACPI-related code from ACPI init A lot of code can be shared between the existing ACPI init flow with the upcoming OF init flow. Extract it into a dedicated function. The re-ordering of parent interrupt allocation requires the architecture code to reserve legacy interrupts from the dynamic allocation by overriding arch_dynirq_lower_bound(), otherwise the parent of LPC irqchip will be allocated in the intended static range of LPC interrupts, which leads to allocation failure of LPC interrupts. Co-developed-by: Jiaxun Yang Signed-off-by: Jiaxun Yang Signed-off-by: Icenowy Zheng Signed-off-by: Thomas Gleixner Reviewed-by: Huacai Chen Link: https://patch.msgid.link/20260321092032.3502701-5-zhengxingda@iscas.ac.cn --- drivers/irqchip/irq-loongson-pch-lpc.c | 57 ++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-loongson-pch-lpc.c b/drivers/irqchip/irq-loongson-pch-lpc.c index 3ad46ec94e3c..2bb6659e9a93 100644 --- a/drivers/irqchip/irq-loongson-pch-lpc.c +++ b/drivers/irqchip/irq-loongson-pch-lpc.c @@ -175,13 +175,10 @@ static struct syscore pch_lpc_syscore = { .ops = &pch_lpc_syscore_ops, }; -int __init pch_lpc_acpi_init(struct irq_domain *parent, - struct acpi_madt_lpc_pic *acpi_pchlpc) +static int __init pch_lpc_init(phys_addr_t addr, unsigned long size, + struct fwnode_handle *irq_handle, int parent_irq) { - int parent_irq; struct pch_lpc *priv; - struct irq_fwspec fwspec; - struct fwnode_handle *irq_handle; priv = kzalloc_obj(*priv); if (!priv) @@ -189,7 +186,7 @@ int __init pch_lpc_acpi_init(struct irq_domain *parent, raw_spin_lock_init(&priv->lpc_lock); - priv->base = ioremap(acpi_pchlpc->address, acpi_pchlpc->size); + priv->base = ioremap(addr, size); if (!priv->base) goto free_priv; @@ -198,12 +195,6 @@ int __init pch_lpc_acpi_init(struct irq_domain *parent, goto iounmap_base; } - irq_handle = irq_domain_alloc_named_fwnode("lpcintc"); - if (!irq_handle) { - pr_err("Unable to allocate domain handle\n"); - goto iounmap_base; - } - /* * The LPC interrupt controller is a legacy i8259-compatible device, * which requires a static 1:1 mapping for IRQs 0-15. @@ -213,15 +204,10 @@ int __init pch_lpc_acpi_init(struct irq_domain *parent, &pch_lpc_domain_ops, priv); if (!priv->lpc_domain) { pr_err("Failed to create IRQ domain\n"); - goto free_irq_handle; + goto iounmap_base; } pch_lpc_reset(priv); - fwspec.fwnode = parent->fwnode; - fwspec.param[0] = acpi_pchlpc->cascade + GSI_MIN_PCH_IRQ; - fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH; - fwspec.param_count = 2; - parent_irq = irq_create_fwspec_mapping(&fwspec); irq_set_chained_handler_and_data(parent_irq, lpc_irq_dispatch, priv); pch_lpc_priv = priv; @@ -230,8 +216,6 @@ int __init pch_lpc_acpi_init(struct irq_domain *parent, return 0; -free_irq_handle: - irq_domain_free_fwnode(irq_handle); iounmap_base: iounmap(priv->base); free_priv: @@ -239,3 +223,36 @@ free_priv: return -ENOMEM; } + +int __init pch_lpc_acpi_init(struct irq_domain *parent, struct acpi_madt_lpc_pic *acpi_pchlpc) +{ + struct fwnode_handle *irq_handle; + struct irq_fwspec fwspec; + int parent_irq, ret; + + irq_handle = irq_domain_alloc_named_fwnode("lpcintc"); + if (!irq_handle) { + pr_err("Unable to allocate domain handle\n"); + return -ENOMEM; + } + + fwspec.fwnode = parent->fwnode; + fwspec.param[0] = acpi_pchlpc->cascade + GSI_MIN_PCH_IRQ; + fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH; + fwspec.param_count = 2; + parent_irq = irq_create_fwspec_mapping(&fwspec); + if (parent_irq <= 0) { + pr_err("Unable to map LPC parent interrupt\n"); + irq_domain_free_fwnode(irq_handle); + return -ENOMEM; + } + + ret = pch_lpc_init(acpi_pchlpc->address, acpi_pchlpc->size, irq_handle, parent_irq); + if (ret) { + irq_dispose_mapping(parent_irq); + irq_domain_free_fwnode(irq_handle); + return ret; + } + + return 0; +} -- cgit v1.2.3 From ed3772aa48cf84ae313156cfe6544e8cba5aa043 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Sat, 21 Mar 2026 17:20:31 +0800 Subject: irqchip/loongson-pch-lpc: Add OF init code The OF-based MIPS Loongson-3 systems can also have a PCH LPC interrupt controller. Add OF-based initialization code for this driver. Co-developed-by: Jiaxun Yang Signed-off-by: Jiaxun Yang Signed-off-by: Icenowy Zheng Signed-off-by: Thomas Gleixner Reviewed-by: Huacai Chen Link: https://patch.msgid.link/20260321092032.3502701-6-zhengxingda@iscas.ac.cn --- drivers/irqchip/irq-loongson-pch-lpc.c | 35 ++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'drivers') diff --git a/drivers/irqchip/irq-loongson-pch-lpc.c b/drivers/irqchip/irq-loongson-pch-lpc.c index 2bb6659e9a93..7117ca6fc2f0 100644 --- a/drivers/irqchip/irq-loongson-pch-lpc.c +++ b/drivers/irqchip/irq-loongson-pch-lpc.c @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include "irq-loongson.h" @@ -224,6 +226,7 @@ free_priv: return -ENOMEM; } +#ifdef CONFIG_ACPI int __init pch_lpc_acpi_init(struct irq_domain *parent, struct acpi_madt_lpc_pic *acpi_pchlpc) { struct fwnode_handle *irq_handle; @@ -256,3 +259,35 @@ int __init pch_lpc_acpi_init(struct irq_domain *parent, struct acpi_madt_lpc_pic return 0; } +#endif /* CONFIG_ACPI */ + +#ifdef CONFIG_OF +static int __init pch_lpc_of_init(struct device_node *node, struct device_node *parent) +{ + struct fwnode_handle *irq_handle; + struct resource res; + int parent_irq, ret; + + if (of_address_to_resource(node, 0, &res)) + return -EINVAL; + + parent_irq = irq_of_parse_and_map(node, 0); + if (!parent_irq) { + pr_err("Failed to get the parent IRQ for LPC IRQs\n"); + return -EINVAL; + } + + irq_handle = of_fwnode_handle(node); + + ret = pch_lpc_init(res.start, resource_size(&res), irq_handle, + parent_irq); + if (ret) { + irq_dispose_mapping(parent_irq); + return ret; + } + + return 0; +} + +IRQCHIP_DECLARE(pch_lpc, "loongson,ls7a-lpc", pch_lpc_of_init); +#endif /* CONFIG_OF */ -- cgit v1.2.3 From 5d994fd7e2f2e11f134fce0abd900bd02b655f70 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Sat, 21 Mar 2026 17:20:32 +0800 Subject: irqchip/loongson-pch-lpc: Enable building on MIPS Loongson64 As the driver now supports OF-based platforms, it's now possible to use it on MIPS Loongson64 machines. Drop the requirement of LOONGARCH for this driver, to allow build on both MIPS-based and LoongArch-based Loongson systems. Signed-off-by: Icenowy Zheng Signed-off-by: Thomas Gleixner Reviewed-by: Jiaxun Yang Reviewed-by: Huacai Chen Link: https://patch.msgid.link/20260321092032.3502701-7-zhengxingda@iscas.ac.cn --- drivers/irqchip/Kconfig | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 2feecfb5a28d..e755a2a05209 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -764,7 +764,6 @@ config LOONGSON_PCH_MSI config LOONGSON_PCH_LPC bool "Loongson PCH LPC Controller" - depends on LOONGARCH depends on MACH_LOONGSON64 || LOONGARCH default MACH_LOONGSON64 select IRQ_DOMAIN_HIERARCHY -- cgit v1.2.3 From fb74e35f78105efd8635c89b39f4389f567edbdc Mon Sep 17 00:00:00 2001 From: Biju Das Date: Wed, 25 Mar 2026 19:24:18 +0000 Subject: irqchip/renesas-rzg2l: Fix error path in rzg2l_irqc_common_probe() Replace pm_runtime_put() with pm_runtime_put_sync() when irq_domain_create_hierarchy() fails to ensure the device suspends synchronously before devres cleanup disables runtime PM via pm_runtime_disable(). [ tglx: Fix up subject and change log to be precise ] Fixes: 7de11369ef30 ("irqchip/renesas-rzg2l: Use devm_pm_runtime_enable()") Signed-off-by: Biju Das Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260325192451.172562-4-biju.das.jz@bp.renesas.com --- drivers/irqchip/irq-renesas-rzg2l.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-rzg2l.c b/drivers/irqchip/irq-renesas-rzg2l.c index e73d426cea6d..eb01d4c5aca7 100644 --- a/drivers/irqchip/irq-renesas-rzg2l.c +++ b/drivers/irqchip/irq-renesas-rzg2l.c @@ -577,7 +577,7 @@ static int rzg2l_irqc_common_probe(struct platform_device *pdev, struct device_n irq_domain = irq_domain_create_hierarchy(parent_domain, 0, IRQC_NUM_IRQ, dev_fwnode(dev), &rzg2l_irqc_domain_ops, rzg2l_irqc_data); if (!irq_domain) { - pm_runtime_put(dev); + pm_runtime_put_sync(dev); return -ENOMEM; } -- cgit v1.2.3 From 0109d24b4cdc2cdbc127f86cbede83adae2187d7 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Wed, 25 Mar 2026 19:24:19 +0000 Subject: irqchip/renesas-rzg2l: Drop redundant IRQC_TINT_START check in rzg2l_irqc_alloc() The check `hwirq < IRQC_TINT_START` in rzg2l_irqc_alloc() is unnecessary as the condition is already guaranteed to be false at that point in the code. The outer `if (hwirq > IRQC_IRQ_COUNT)` block ensures that hwirq is always above IRQC_IRQ_COUNT before reaching this check, and since IRQC_TINT_START <= IRQC_IRQ_COUNT, the guard can never trigger. Remove the dead code to simplify the allocation path. Signed-off-by: Biju Das Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260325192451.172562-5-biju.das.jz@bp.renesas.com --- drivers/irqchip/irq-renesas-rzg2l.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-rzg2l.c b/drivers/irqchip/irq-renesas-rzg2l.c index eb01d4c5aca7..8587d4c5f110 100644 --- a/drivers/irqchip/irq-renesas-rzg2l.c +++ b/drivers/irqchip/irq-renesas-rzg2l.c @@ -491,9 +491,6 @@ static int rzg2l_irqc_alloc(struct irq_domain *domain, unsigned int virq, if (hwirq > IRQC_IRQ_COUNT) { tint = TINT_EXTRACT_GPIOINT(hwirq); hwirq = TINT_EXTRACT_HWIRQ(hwirq); - - if (hwirq < IRQC_TINT_START) - return -EINVAL; } if (hwirq > (IRQC_NUM_IRQ - 1)) -- cgit v1.2.3 From 4b11d14cac6ae198ea855b871c73dc208a177a0e Mon Sep 17 00:00:00 2001 From: Biju Das Date: Wed, 25 Mar 2026 19:24:20 +0000 Subject: irqchip/renesas-rzg2l: Replace single irq_chip with per-region irq_chip instances The driver uses a single irq_chip instance shared across all interrupt types, relying on dispatcher callbacks to differentiate between IRQ and TINT regions at runtime. Replace the per-SoC irq_chip and its dispatcher callbacks with dedicated irq_chip instances for each interrupt region: IRQ and TINT. Subsequent patches will add per-region callbacks for IRQ and TINT from the common code. Signed-off-by: Biju Das Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260325192451.172562-6-biju.das.jz@bp.renesas.com --- drivers/irqchip/irq-renesas-rzg2l.c | 61 +++++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-rzg2l.c b/drivers/irqchip/irq-renesas-rzg2l.c index 8587d4c5f110..1d1df4953368 100644 --- a/drivers/irqchip/irq-renesas-rzg2l.c +++ b/drivers/irqchip/irq-renesas-rzg2l.c @@ -71,14 +71,16 @@ struct rzg2l_irqc_reg_cache { /** * struct rzg2l_irqc_priv - IRQ controller private data structure * @base: Controller's base address - * @irqchip: Pointer to struct irq_chip + * @irq_chip: Pointer to struct irq_chip for irq + * @tint_chip: Pointer to struct irq_chip for tint * @fwspec: IRQ firmware specific data * @lock: Lock to serialize access to hardware registers * @cache: Registers cache for suspend/resume */ static struct rzg2l_irqc_priv { void __iomem *base; - const struct irq_chip *irqchip; + const struct irq_chip *irq_chip; + const struct irq_chip *tint_chip; struct irq_fwspec fwspec[IRQC_NUM_IRQ]; raw_spinlock_t lock; struct rzg2l_irqc_reg_cache cache; @@ -434,7 +436,7 @@ static struct syscore rzg2l_irqc_syscore = { .ops = &rzg2l_irqc_syscore_ops, }; -static const struct irq_chip rzg2l_irqc_chip = { +static const struct irq_chip rzg2l_irqc_irq_chip = { .name = "rzg2l-irqc", .irq_eoi = rzg2l_irqc_eoi, .irq_mask = irq_chip_mask_parent, @@ -451,7 +453,41 @@ static const struct irq_chip rzg2l_irqc_chip = { IRQCHIP_SKIP_SET_WAKE, }; -static const struct irq_chip rzfive_irqc_chip = { +static const struct irq_chip rzg2l_irqc_tint_chip = { + .name = "rzg2l-irqc", + .irq_eoi = rzg2l_irqc_eoi, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_disable = rzg2l_irqc_irq_disable, + .irq_enable = rzg2l_irqc_irq_enable, + .irq_get_irqchip_state = irq_chip_get_parent_state, + .irq_set_irqchip_state = irq_chip_set_parent_state, + .irq_retrigger = irq_chip_retrigger_hierarchy, + .irq_set_type = rzg2l_irqc_set_type, + .irq_set_affinity = irq_chip_set_affinity_parent, + .flags = IRQCHIP_MASK_ON_SUSPEND | + IRQCHIP_SET_TYPE_MASKED | + IRQCHIP_SKIP_SET_WAKE, +}; + +static const struct irq_chip rzfive_irqc_irq_chip = { + .name = "rzfive-irqc", + .irq_eoi = rzg2l_irqc_eoi, + .irq_mask = rzfive_irqc_mask, + .irq_unmask = rzfive_irqc_unmask, + .irq_disable = rzfive_irqc_irq_disable, + .irq_enable = rzfive_irqc_irq_enable, + .irq_get_irqchip_state = irq_chip_get_parent_state, + .irq_set_irqchip_state = irq_chip_set_parent_state, + .irq_retrigger = irq_chip_retrigger_hierarchy, + .irq_set_type = rzg2l_irqc_set_type, + .irq_set_affinity = irq_chip_set_affinity_parent, + .flags = IRQCHIP_MASK_ON_SUSPEND | + IRQCHIP_SET_TYPE_MASKED | + IRQCHIP_SKIP_SET_WAKE, +}; + +static const struct irq_chip rzfive_irqc_tint_chip = { .name = "rzfive-irqc", .irq_eoi = rzg2l_irqc_eoi, .irq_mask = rzfive_irqc_mask, @@ -472,6 +508,7 @@ static int rzg2l_irqc_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs, void *arg) { struct rzg2l_irqc_priv *priv = domain->host_data; + const struct irq_chip *chip; unsigned long tint = 0; irq_hw_number_t hwirq; unsigned int type; @@ -491,13 +528,15 @@ static int rzg2l_irqc_alloc(struct irq_domain *domain, unsigned int virq, if (hwirq > IRQC_IRQ_COUNT) { tint = TINT_EXTRACT_GPIOINT(hwirq); hwirq = TINT_EXTRACT_HWIRQ(hwirq); + chip = priv->tint_chip; + } else { + chip = priv->irq_chip; } if (hwirq > (IRQC_NUM_IRQ - 1)) return -EINVAL; - ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, priv->irqchip, - (void *)(uintptr_t)tint); + ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, chip, (void *)(uintptr_t)tint); if (ret) return ret; @@ -529,7 +568,8 @@ static int rzg2l_irqc_parse_interrupts(struct rzg2l_irqc_priv *priv, } static int rzg2l_irqc_common_probe(struct platform_device *pdev, struct device_node *parent, - const struct irq_chip *irq_chip) + const struct irq_chip *irq_chip, + const struct irq_chip *tint_chip) { struct irq_domain *irq_domain, *parent_domain; struct device_node *node = pdev->dev.of_node; @@ -545,7 +585,8 @@ static int rzg2l_irqc_common_probe(struct platform_device *pdev, struct device_n if (!rzg2l_irqc_data) return -ENOMEM; - rzg2l_irqc_data->irqchip = irq_chip; + rzg2l_irqc_data->irq_chip = irq_chip; + rzg2l_irqc_data->tint_chip = tint_chip; rzg2l_irqc_data->base = devm_of_iomap(dev, dev->of_node, 0, NULL); if (IS_ERR(rzg2l_irqc_data->base)) @@ -585,12 +626,12 @@ static int rzg2l_irqc_common_probe(struct platform_device *pdev, struct device_n static int rzg2l_irqc_probe(struct platform_device *pdev, struct device_node *parent) { - return rzg2l_irqc_common_probe(pdev, parent, &rzg2l_irqc_chip); + return rzg2l_irqc_common_probe(pdev, parent, &rzg2l_irqc_irq_chip, &rzg2l_irqc_tint_chip); } static int rzfive_irqc_probe(struct platform_device *pdev, struct device_node *parent) { - return rzg2l_irqc_common_probe(pdev, parent, &rzfive_irqc_chip); + return rzg2l_irqc_common_probe(pdev, parent, &rzfive_irqc_irq_chip, &rzfive_irqc_tint_chip); } IRQCHIP_PLATFORM_DRIVER_BEGIN(rzg2l_irqc) -- cgit v1.2.3 From 6e5e0331dea48bbd6955af562aab4972a3950fa2 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Wed, 25 Mar 2026 19:24:21 +0000 Subject: irqchip/renesas-rzg2l: Split EOI handler into separate IRQ and TINT functions The common rzg2l_irqc_eoi() handler uses a conditional to determine whether to clear an IRQ or an TINT interrupt. Split this into two dedicated handlers, rzg2l_irqc_irq_eoi() and rzg2l_irqc_tint_eoi(), each handling only their respective interrupt type without the need for range checks. While at it, simplify rzg2l_irqc_{irq,tint}_eoi() by replacing raw_spin_lock locking/unlocking with scoped_guard(). Signed-off-by: Biju Das Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260325192451.172562-7-biju.das.jz@bp.renesas.com --- drivers/irqchip/irq-renesas-rzg2l.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-rzg2l.c b/drivers/irqchip/irq-renesas-rzg2l.c index 1d1df4953368..664599acbeb6 100644 --- a/drivers/irqchip/irq-renesas-rzg2l.c +++ b/drivers/irqchip/irq-renesas-rzg2l.c @@ -130,17 +130,25 @@ static void rzg2l_clear_tint_int(struct rzg2l_irqc_priv *priv, unsigned int hwir } } -static void rzg2l_irqc_eoi(struct irq_data *d) +static void rzg2l_irqc_irq_eoi(struct irq_data *d) { struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); unsigned int hw_irq = irqd_to_hwirq(d); - raw_spin_lock(&priv->lock); - if (hw_irq >= IRQC_IRQ_START && hw_irq <= IRQC_IRQ_COUNT) + scoped_guard(raw_spinlock, &priv->lock) rzg2l_clear_irq_int(priv, hw_irq); - else if (hw_irq >= IRQC_TINT_START && hw_irq < IRQC_NUM_IRQ) + + irq_chip_eoi_parent(d); +} + +static void rzg2l_irqc_tint_eoi(struct irq_data *d) +{ + struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); + unsigned int hw_irq = irqd_to_hwirq(d); + + scoped_guard(raw_spinlock, &priv->lock) rzg2l_clear_tint_int(priv, hw_irq); - raw_spin_unlock(&priv->lock); + irq_chip_eoi_parent(d); } @@ -438,7 +446,7 @@ static struct syscore rzg2l_irqc_syscore = { static const struct irq_chip rzg2l_irqc_irq_chip = { .name = "rzg2l-irqc", - .irq_eoi = rzg2l_irqc_eoi, + .irq_eoi = rzg2l_irqc_irq_eoi, .irq_mask = irq_chip_mask_parent, .irq_unmask = irq_chip_unmask_parent, .irq_disable = rzg2l_irqc_irq_disable, @@ -455,7 +463,7 @@ static const struct irq_chip rzg2l_irqc_irq_chip = { static const struct irq_chip rzg2l_irqc_tint_chip = { .name = "rzg2l-irqc", - .irq_eoi = rzg2l_irqc_eoi, + .irq_eoi = rzg2l_irqc_tint_eoi, .irq_mask = irq_chip_mask_parent, .irq_unmask = irq_chip_unmask_parent, .irq_disable = rzg2l_irqc_irq_disable, @@ -472,7 +480,7 @@ static const struct irq_chip rzg2l_irqc_tint_chip = { static const struct irq_chip rzfive_irqc_irq_chip = { .name = "rzfive-irqc", - .irq_eoi = rzg2l_irqc_eoi, + .irq_eoi = rzg2l_irqc_irq_eoi, .irq_mask = rzfive_irqc_mask, .irq_unmask = rzfive_irqc_unmask, .irq_disable = rzfive_irqc_irq_disable, @@ -489,7 +497,7 @@ static const struct irq_chip rzfive_irqc_irq_chip = { static const struct irq_chip rzfive_irqc_tint_chip = { .name = "rzfive-irqc", - .irq_eoi = rzg2l_irqc_eoi, + .irq_eoi = rzg2l_irqc_tint_eoi, .irq_mask = rzfive_irqc_mask, .irq_unmask = rzfive_irqc_unmask, .irq_disable = rzfive_irqc_irq_disable, -- cgit v1.2.3 From d196aeb35efa2f2339b31f107c0ce35387946009 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Wed, 25 Mar 2026 19:24:22 +0000 Subject: irqchip/renesas-rzg2l: Split set_type handler into separate IRQ and TINT functions The common rzg2l_irqc_set_type() handler uses hw_irq range checks to dispatch to either rzg2l_irq_set_type() or rzg2l_tint_set_edge(). Split this into two dedicated handlers, rzg2l_irqc_irq_set_type() and rzg2l_irqc_tint_set_type(), each calling only their respective type configuration function without runtime conditionals. Signed-off-by: Biju Das Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260325192451.172562-8-biju.das.jz@bp.renesas.com --- drivers/irqchip/irq-renesas-rzg2l.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-rzg2l.c b/drivers/irqchip/irq-renesas-rzg2l.c index 664599acbeb6..3d48491c2f06 100644 --- a/drivers/irqchip/irq-renesas-rzg2l.c +++ b/drivers/irqchip/irq-renesas-rzg2l.c @@ -393,15 +393,20 @@ static int rzg2l_tint_set_edge(struct irq_data *d, unsigned int type) return 0; } -static int rzg2l_irqc_set_type(struct irq_data *d, unsigned int type) +static int rzg2l_irqc_irq_set_type(struct irq_data *d, unsigned int type) { - unsigned int hw_irq = irqd_to_hwirq(d); - int ret = -EINVAL; + int ret = rzg2l_irq_set_type(d, type); + + if (ret) + return ret; + + return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH); +} + +static int rzg2l_irqc_tint_set_type(struct irq_data *d, unsigned int type) +{ + int ret = rzg2l_tint_set_edge(d, type); - if (hw_irq >= IRQC_IRQ_START && hw_irq <= IRQC_IRQ_COUNT) - ret = rzg2l_irq_set_type(d, type); - else if (hw_irq >= IRQC_TINT_START && hw_irq < IRQC_NUM_IRQ) - ret = rzg2l_tint_set_edge(d, type); if (ret) return ret; @@ -454,7 +459,7 @@ static const struct irq_chip rzg2l_irqc_irq_chip = { .irq_get_irqchip_state = irq_chip_get_parent_state, .irq_set_irqchip_state = irq_chip_set_parent_state, .irq_retrigger = irq_chip_retrigger_hierarchy, - .irq_set_type = rzg2l_irqc_set_type, + .irq_set_type = rzg2l_irqc_irq_set_type, .irq_set_affinity = irq_chip_set_affinity_parent, .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SET_TYPE_MASKED | @@ -471,7 +476,7 @@ static const struct irq_chip rzg2l_irqc_tint_chip = { .irq_get_irqchip_state = irq_chip_get_parent_state, .irq_set_irqchip_state = irq_chip_set_parent_state, .irq_retrigger = irq_chip_retrigger_hierarchy, - .irq_set_type = rzg2l_irqc_set_type, + .irq_set_type = rzg2l_irqc_tint_set_type, .irq_set_affinity = irq_chip_set_affinity_parent, .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SET_TYPE_MASKED | @@ -488,7 +493,7 @@ static const struct irq_chip rzfive_irqc_irq_chip = { .irq_get_irqchip_state = irq_chip_get_parent_state, .irq_set_irqchip_state = irq_chip_set_parent_state, .irq_retrigger = irq_chip_retrigger_hierarchy, - .irq_set_type = rzg2l_irqc_set_type, + .irq_set_type = rzg2l_irqc_irq_set_type, .irq_set_affinity = irq_chip_set_affinity_parent, .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SET_TYPE_MASKED | @@ -505,7 +510,7 @@ static const struct irq_chip rzfive_irqc_tint_chip = { .irq_get_irqchip_state = irq_chip_get_parent_state, .irq_set_irqchip_state = irq_chip_set_parent_state, .irq_retrigger = irq_chip_retrigger_hierarchy, - .irq_set_type = rzg2l_irqc_set_type, + .irq_set_type = rzg2l_irqc_tint_set_type, .irq_set_affinity = irq_chip_set_affinity_parent, .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SET_TYPE_MASKED | -- cgit v1.2.3 From 83ed8efca07a076906c4c24749ee2fd5835c0ade Mon Sep 17 00:00:00 2001 From: Biju Das Date: Wed, 25 Mar 2026 19:24:23 +0000 Subject: irqchip/renesas-rzg2l: Replace rzg2l_irqc_irq_{enable,disable} with TINT-specific handlers rzg2l_irqc_irq_disable() and rzg2l_irqc_irq_enable() are used by both the IRQ and TINT chips, but only perform TINT-specific work via rzg2l_tint_irq_endisable(), guarded by a hw_irq range check. Since the IRQ chip does not require this extra enable/disable handling, replace its callbacks with the generic irq_chip_disable_parent() and irq_chip_enable_parent() directly. While at it, simplify rzfive_irqc_irq_enable() by replacing raw_spin_lock locking/unlocking with guard() and update the variable types of offset, tssr_offset, and tssr_index to unsigned int, as these variables are used only for calculation. Signed-off-by: Biju Das Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260325192451.172562-9-biju.das.jz@bp.renesas.com --- drivers/irqchip/irq-renesas-rzg2l.c | 40 +++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-rzg2l.c b/drivers/irqchip/irq-renesas-rzg2l.c index 3d48491c2f06..ffb53161191a 100644 --- a/drivers/irqchip/irq-renesas-rzg2l.c +++ b/drivers/irqchip/irq-renesas-rzg2l.c @@ -259,33 +259,29 @@ static void rzfive_irqc_irq_enable(struct irq_data *d) static void rzg2l_tint_irq_endisable(struct irq_data *d, bool enable) { + struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); unsigned int hw_irq = irqd_to_hwirq(d); + unsigned int offset = hw_irq - IRQC_TINT_START; + unsigned int tssr_offset = TSSR_OFFSET(offset); + unsigned int tssr_index = TSSR_INDEX(offset); + u32 reg; - if (hw_irq >= IRQC_TINT_START && hw_irq < IRQC_NUM_IRQ) { - struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); - u32 offset = hw_irq - IRQC_TINT_START; - u32 tssr_offset = TSSR_OFFSET(offset); - u8 tssr_index = TSSR_INDEX(offset); - u32 reg; - - raw_spin_lock(&priv->lock); - reg = readl_relaxed(priv->base + TSSR(tssr_index)); - if (enable) - reg |= TIEN << TSSEL_SHIFT(tssr_offset); - else - reg &= ~(TIEN << TSSEL_SHIFT(tssr_offset)); - writel_relaxed(reg, priv->base + TSSR(tssr_index)); - raw_spin_unlock(&priv->lock); - } + guard(raw_spinlock)(&priv->lock); + reg = readl_relaxed(priv->base + TSSR(tssr_index)); + if (enable) + reg |= TIEN << TSSEL_SHIFT(tssr_offset); + else + reg &= ~(TIEN << TSSEL_SHIFT(tssr_offset)); + writel_relaxed(reg, priv->base + TSSR(tssr_index)); } -static void rzg2l_irqc_irq_disable(struct irq_data *d) +static void rzg2l_irqc_tint_disable(struct irq_data *d) { irq_chip_disable_parent(d); rzg2l_tint_irq_endisable(d, false); } -static void rzg2l_irqc_irq_enable(struct irq_data *d) +static void rzg2l_irqc_tint_enable(struct irq_data *d) { rzg2l_tint_irq_endisable(d, true); irq_chip_enable_parent(d); @@ -454,8 +450,8 @@ static const struct irq_chip rzg2l_irqc_irq_chip = { .irq_eoi = rzg2l_irqc_irq_eoi, .irq_mask = irq_chip_mask_parent, .irq_unmask = irq_chip_unmask_parent, - .irq_disable = rzg2l_irqc_irq_disable, - .irq_enable = rzg2l_irqc_irq_enable, + .irq_disable = irq_chip_disable_parent, + .irq_enable = irq_chip_enable_parent, .irq_get_irqchip_state = irq_chip_get_parent_state, .irq_set_irqchip_state = irq_chip_set_parent_state, .irq_retrigger = irq_chip_retrigger_hierarchy, @@ -471,8 +467,8 @@ static const struct irq_chip rzg2l_irqc_tint_chip = { .irq_eoi = rzg2l_irqc_tint_eoi, .irq_mask = irq_chip_mask_parent, .irq_unmask = irq_chip_unmask_parent, - .irq_disable = rzg2l_irqc_irq_disable, - .irq_enable = rzg2l_irqc_irq_enable, + .irq_disable = rzg2l_irqc_tint_disable, + .irq_enable = rzg2l_irqc_tint_enable, .irq_get_irqchip_state = irq_chip_get_parent_state, .irq_set_irqchip_state = irq_chip_set_parent_state, .irq_retrigger = irq_chip_retrigger_hierarchy, -- cgit v1.2.3 From 0b2675fadf4fdfdb69c492b38cc0d6f6a3e2f937 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Wed, 25 Mar 2026 19:24:24 +0000 Subject: irqchip/renesas-rzg2l: Split rzfive_tint_irq_endisable() into separate IRQ and TINT helpers rzfive_tint_irq_endisable() handles both IRQ and TINT enable/disable paths via a hw_irq range check. Split this into two dedicated helpers, rzfive_irq_endisable() for IRQ interrupts and rzfive_tint_endisable() for TINT interrupts, each operating unconditionally on their respective interrupt type. While at it, simplify rzfive_{irq,tint}_endisable by replacing raw_spin_lock locking/unlocking with guard() and update the variable types of offset, tssr_offset, and tssr_index to unsigned int, as these variables are used only for calculation. Signed-off-by: Biju Das Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260325192451.172562-10-biju.das.jz@bp.renesas.com --- drivers/irqchip/irq-renesas-rzg2l.c | 75 ++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-rzg2l.c b/drivers/irqchip/irq-renesas-rzg2l.c index ffb53161191a..5417f01b9246 100644 --- a/drivers/irqchip/irq-renesas-rzg2l.c +++ b/drivers/irqchip/irq-renesas-rzg2l.c @@ -212,48 +212,61 @@ static void rzfive_irqc_unmask(struct irq_data *d) irq_chip_unmask_parent(d); } -static void rzfive_tint_irq_endisable(struct irq_data *d, bool enable) +static void rzfive_irq_endisable(struct irq_data *d, bool enable) { struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); unsigned int hwirq = irqd_to_hwirq(d); - if (hwirq >= IRQC_TINT_START && hwirq < IRQC_NUM_IRQ) { - u32 offset = hwirq - IRQC_TINT_START; - u32 tssr_offset = TSSR_OFFSET(offset); - u8 tssr_index = TSSR_INDEX(offset); - u32 reg; - - raw_spin_lock(&priv->lock); - if (enable) - rzfive_irqc_unmask_tint_interrupt(priv, hwirq); - else - rzfive_irqc_mask_tint_interrupt(priv, hwirq); - reg = readl_relaxed(priv->base + TSSR(tssr_index)); - if (enable) - reg |= TIEN << TSSEL_SHIFT(tssr_offset); - else - reg &= ~(TIEN << TSSEL_SHIFT(tssr_offset)); - writel_relaxed(reg, priv->base + TSSR(tssr_index)); - raw_spin_unlock(&priv->lock); - } else { - raw_spin_lock(&priv->lock); - if (enable) - rzfive_irqc_unmask_irq_interrupt(priv, hwirq); - else - rzfive_irqc_mask_irq_interrupt(priv, hwirq); - raw_spin_unlock(&priv->lock); - } + guard(raw_spinlock)(&priv->lock); + if (enable) + rzfive_irqc_unmask_irq_interrupt(priv, hwirq); + else + rzfive_irqc_mask_irq_interrupt(priv, hwirq); +} + +static void rzfive_tint_endisable(struct irq_data *d, bool enable) +{ + struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); + unsigned int hwirq = irqd_to_hwirq(d); + unsigned int offset = hwirq - IRQC_TINT_START; + unsigned int tssr_offset = TSSR_OFFSET(offset); + unsigned int tssr_index = TSSR_INDEX(offset); + u32 reg; + + guard(raw_spinlock)(&priv->lock); + if (enable) + rzfive_irqc_unmask_tint_interrupt(priv, hwirq); + else + rzfive_irqc_mask_tint_interrupt(priv, hwirq); + reg = readl_relaxed(priv->base + TSSR(tssr_index)); + if (enable) + reg |= TIEN << TSSEL_SHIFT(tssr_offset); + else + reg &= ~(TIEN << TSSEL_SHIFT(tssr_offset)); + writel_relaxed(reg, priv->base + TSSR(tssr_index)); } static void rzfive_irqc_irq_disable(struct irq_data *d) { irq_chip_disable_parent(d); - rzfive_tint_irq_endisable(d, false); + rzfive_irq_endisable(d, false); } static void rzfive_irqc_irq_enable(struct irq_data *d) { - rzfive_tint_irq_endisable(d, true); + rzfive_irq_endisable(d, true); + irq_chip_enable_parent(d); +} + +static void rzfive_irqc_tint_disable(struct irq_data *d) +{ + irq_chip_disable_parent(d); + rzfive_tint_endisable(d, false); +} + +static void rzfive_irqc_tint_enable(struct irq_data *d) +{ + rzfive_tint_endisable(d, true); irq_chip_enable_parent(d); } @@ -501,8 +514,8 @@ static const struct irq_chip rzfive_irqc_tint_chip = { .irq_eoi = rzg2l_irqc_tint_eoi, .irq_mask = rzfive_irqc_mask, .irq_unmask = rzfive_irqc_unmask, - .irq_disable = rzfive_irqc_irq_disable, - .irq_enable = rzfive_irqc_irq_enable, + .irq_disable = rzfive_irqc_tint_disable, + .irq_enable = rzfive_irqc_tint_enable, .irq_get_irqchip_state = irq_chip_get_parent_state, .irq_set_irqchip_state = irq_chip_set_parent_state, .irq_retrigger = irq_chip_retrigger_hierarchy, -- cgit v1.2.3 From bcb30669088a69cc8de2a5cf446b8b72f6b6e8eb Mon Sep 17 00:00:00 2001 From: Biju Das Date: Wed, 25 Mar 2026 19:24:25 +0000 Subject: irqchip/renesas-rzg2l: Split rzfive_irqc_{mask,unmask} into separate IRQ and TINT handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rzfive_irqc_mask() and rzfive_irqc_unmask() use hw_irq range checks to dispatch between IRQ and TINT masking operations. Split each into two dedicated handlers — rzfive_irqc_irq_mask(), rzfive_irqc_tint_mask(), rzfive_irqc_irq_unmask(), and rzfive_irqc_tint_unmask() — each operating unconditionally on its respective interrupt type, removing the runtime conditionals. Assign the IRQ-specific handlers to rzfive_irqc_irq_chip and the TINT-specific handlers to rzfive_irqc_tint_chip, consistent with the separation applied to the EOI, set_type, and enable/disable callbacks in previous patches. While at it, simplify rzfive_irqc_{irq,tint}_{mask,unmask}() by replacing raw_spin_lock locking/unlocking with scoped_guard(). Signed-off-by: Biju Das Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260325192451.172562-11-biju.das.jz@bp.renesas.com --- drivers/irqchip/irq-renesas-rzg2l.c | 44 +++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-rzg2l.c b/drivers/irqchip/irq-renesas-rzg2l.c index 5417f01b9246..bb6400ceb617 100644 --- a/drivers/irqchip/irq-renesas-rzg2l.c +++ b/drivers/irqchip/irq-renesas-rzg2l.c @@ -184,31 +184,47 @@ static void rzfive_irqc_unmask_tint_interrupt(struct rzg2l_irqc_priv *priv, writel_relaxed(readl_relaxed(priv->base + TMSK) & ~bit, priv->base + TMSK); } -static void rzfive_irqc_mask(struct irq_data *d) +static void rzfive_irqc_irq_mask(struct irq_data *d) { struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); unsigned int hwirq = irqd_to_hwirq(d); - raw_spin_lock(&priv->lock); - if (hwirq >= IRQC_IRQ_START && hwirq <= IRQC_IRQ_COUNT) + scoped_guard(raw_spinlock, &priv->lock) rzfive_irqc_mask_irq_interrupt(priv, hwirq); - else if (hwirq >= IRQC_TINT_START && hwirq < IRQC_NUM_IRQ) + + irq_chip_mask_parent(d); +} + +static void rzfive_irqc_tint_mask(struct irq_data *d) +{ + struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); + unsigned int hwirq = irqd_to_hwirq(d); + + scoped_guard(raw_spinlock, &priv->lock) rzfive_irqc_mask_tint_interrupt(priv, hwirq); - raw_spin_unlock(&priv->lock); + irq_chip_mask_parent(d); } -static void rzfive_irqc_unmask(struct irq_data *d) +static void rzfive_irqc_irq_unmask(struct irq_data *d) { struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); unsigned int hwirq = irqd_to_hwirq(d); - raw_spin_lock(&priv->lock); - if (hwirq >= IRQC_IRQ_START && hwirq <= IRQC_IRQ_COUNT) + scoped_guard(raw_spinlock, &priv->lock) rzfive_irqc_unmask_irq_interrupt(priv, hwirq); - else if (hwirq >= IRQC_TINT_START && hwirq < IRQC_NUM_IRQ) + + irq_chip_unmask_parent(d); +} + +static void rzfive_irqc_tint_unmask(struct irq_data *d) +{ + struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); + unsigned int hwirq = irqd_to_hwirq(d); + + scoped_guard(raw_spinlock, &priv->lock) rzfive_irqc_unmask_tint_interrupt(priv, hwirq); - raw_spin_unlock(&priv->lock); + irq_chip_unmask_parent(d); } @@ -495,8 +511,8 @@ static const struct irq_chip rzg2l_irqc_tint_chip = { static const struct irq_chip rzfive_irqc_irq_chip = { .name = "rzfive-irqc", .irq_eoi = rzg2l_irqc_irq_eoi, - .irq_mask = rzfive_irqc_mask, - .irq_unmask = rzfive_irqc_unmask, + .irq_mask = rzfive_irqc_irq_mask, + .irq_unmask = rzfive_irqc_irq_unmask, .irq_disable = rzfive_irqc_irq_disable, .irq_enable = rzfive_irqc_irq_enable, .irq_get_irqchip_state = irq_chip_get_parent_state, @@ -512,8 +528,8 @@ static const struct irq_chip rzfive_irqc_irq_chip = { static const struct irq_chip rzfive_irqc_tint_chip = { .name = "rzfive-irqc", .irq_eoi = rzg2l_irqc_tint_eoi, - .irq_mask = rzfive_irqc_mask, - .irq_unmask = rzfive_irqc_unmask, + .irq_mask = rzfive_irqc_tint_mask, + .irq_unmask = rzfive_irqc_tint_unmask, .irq_disable = rzfive_irqc_tint_disable, .irq_enable = rzfive_irqc_tint_enable, .irq_get_irqchip_state = irq_chip_get_parent_state, -- cgit v1.2.3 From b8e06e4e419bd3dd99f832d728f17007ada3359c Mon Sep 17 00:00:00 2001 From: Biju Das Date: Wed, 25 Mar 2026 19:24:26 +0000 Subject: irqchip/renesas-rzg2l: Dynamically allocate fwspec array The total number of interrupts in RZ/G2L and RZ/G3L SoC are different. The RZ/G3L has 16 external interrupts whereas RZ/G2L has only 8 external interrupts. Dynamically allocate fwspec memory instead of static allocation to support both SoCs. Signed-off-by: Biju Das Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260325192451.172562-12-biju.das.jz@bp.renesas.com --- drivers/irqchip/irq-renesas-rzg2l.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-rzg2l.c b/drivers/irqchip/irq-renesas-rzg2l.c index bb6400ceb617..db5d1a5de87e 100644 --- a/drivers/irqchip/irq-renesas-rzg2l.c +++ b/drivers/irqchip/irq-renesas-rzg2l.c @@ -81,7 +81,7 @@ static struct rzg2l_irqc_priv { void __iomem *base; const struct irq_chip *irq_chip; const struct irq_chip *tint_chip; - struct irq_fwspec fwspec[IRQC_NUM_IRQ]; + struct irq_fwspec *fwspec; raw_spinlock_t lock; struct rzg2l_irqc_reg_cache cache; } *rzg2l_irqc_data; @@ -630,6 +630,11 @@ static int rzg2l_irqc_common_probe(struct platform_device *pdev, struct device_n if (IS_ERR(rzg2l_irqc_data->base)) return PTR_ERR(rzg2l_irqc_data->base); + rzg2l_irqc_data->fwspec = devm_kcalloc(&pdev->dev, IRQC_NUM_IRQ, + sizeof(*rzg2l_irqc_data->fwspec), GFP_KERNEL); + if (!rzg2l_irqc_data->fwspec) + return -ENOMEM; + ret = rzg2l_irqc_parse_interrupts(rzg2l_irqc_data, node); if (ret) return dev_err_probe(dev, ret, "cannot parse interrupts: %d\n", ret); -- cgit v1.2.3 From 2f814778950ecc99ca5e71472b626dddb2d2b756 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Wed, 25 Mar 2026 19:24:27 +0000 Subject: irqchip/renesas-rzg2l: Drop IRQC_NUM_IRQ macro The total number of interrupts in RZ/G2L and RZ/G3L SoC are different. Introduce struct rzg2l_hw_info to handle the hardware differences and replace the macro IRQC_NUM_IRQ with num_irq variable in struct rzg2l_hw_info. Signed-off-by: Biju Das Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260325192451.172562-13-biju.das.jz@bp.renesas.com --- drivers/irqchip/irq-renesas-rzg2l.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-rzg2l.c b/drivers/irqchip/irq-renesas-rzg2l.c index db5d1a5de87e..2b6dac7e72b7 100644 --- a/drivers/irqchip/irq-renesas-rzg2l.c +++ b/drivers/irqchip/irq-renesas-rzg2l.c @@ -24,7 +24,6 @@ #define IRQC_IRQ_COUNT 8 #define IRQC_TINT_START (IRQC_IRQ_START + IRQC_IRQ_COUNT) #define IRQC_TINT_COUNT 32 -#define IRQC_NUM_IRQ (IRQC_TINT_START + IRQC_TINT_COUNT) #define ISCR 0x10 #define IITSR 0x14 @@ -68,6 +67,14 @@ struct rzg2l_irqc_reg_cache { u32 titsr[2]; }; +/** + * struct rzg2l_hw_info - Interrupt Control Unit controller hardware info structure. + * @num_irq: Total Number of interrupts + */ +struct rzg2l_hw_info { + unsigned int num_irq; +}; + /** * struct rzg2l_irqc_priv - IRQ controller private data structure * @base: Controller's base address @@ -75,6 +82,7 @@ struct rzg2l_irqc_reg_cache { * @tint_chip: Pointer to struct irq_chip for tint * @fwspec: IRQ firmware specific data * @lock: Lock to serialize access to hardware registers + * @info: Hardware specific data * @cache: Registers cache for suspend/resume */ static struct rzg2l_irqc_priv { @@ -83,6 +91,7 @@ static struct rzg2l_irqc_priv { const struct irq_chip *tint_chip; struct irq_fwspec *fwspec; raw_spinlock_t lock; + struct rzg2l_hw_info info; struct rzg2l_irqc_reg_cache cache; } *rzg2l_irqc_data; @@ -571,7 +580,7 @@ static int rzg2l_irqc_alloc(struct irq_domain *domain, unsigned int virq, chip = priv->irq_chip; } - if (hwirq > (IRQC_NUM_IRQ - 1)) + if (hwirq >= priv->info.num_irq) return -EINVAL; ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, chip, (void *)(uintptr_t)tint); @@ -594,7 +603,7 @@ static int rzg2l_irqc_parse_interrupts(struct rzg2l_irqc_priv *priv, unsigned int i; int ret; - for (i = 0; i < IRQC_NUM_IRQ; i++) { + for (i = 0; i < priv->info.num_irq; i++) { ret = of_irq_parse_one(np, i, &map); if (ret) return ret; @@ -607,7 +616,8 @@ static int rzg2l_irqc_parse_interrupts(struct rzg2l_irqc_priv *priv, static int rzg2l_irqc_common_probe(struct platform_device *pdev, struct device_node *parent, const struct irq_chip *irq_chip, - const struct irq_chip *tint_chip) + const struct irq_chip *tint_chip, + const struct rzg2l_hw_info info) { struct irq_domain *irq_domain, *parent_domain; struct device_node *node = pdev->dev.of_node; @@ -630,7 +640,9 @@ static int rzg2l_irqc_common_probe(struct platform_device *pdev, struct device_n if (IS_ERR(rzg2l_irqc_data->base)) return PTR_ERR(rzg2l_irqc_data->base); - rzg2l_irqc_data->fwspec = devm_kcalloc(&pdev->dev, IRQC_NUM_IRQ, + rzg2l_irqc_data->info = info; + + rzg2l_irqc_data->fwspec = devm_kcalloc(&pdev->dev, info.num_irq, sizeof(*rzg2l_irqc_data->fwspec), GFP_KERNEL); if (!rzg2l_irqc_data->fwspec) return -ENOMEM; @@ -655,7 +667,7 @@ static int rzg2l_irqc_common_probe(struct platform_device *pdev, struct device_n raw_spin_lock_init(&rzg2l_irqc_data->lock); - irq_domain = irq_domain_create_hierarchy(parent_domain, 0, IRQC_NUM_IRQ, dev_fwnode(dev), + irq_domain = irq_domain_create_hierarchy(parent_domain, 0, info.num_irq, dev_fwnode(dev), &rzg2l_irqc_domain_ops, rzg2l_irqc_data); if (!irq_domain) { pm_runtime_put_sync(dev); @@ -667,14 +679,20 @@ static int rzg2l_irqc_common_probe(struct platform_device *pdev, struct device_n return 0; } +static const struct rzg2l_hw_info rzg2l_hw_params = { + .num_irq = IRQC_IRQ_START + IRQC_IRQ_COUNT + IRQC_TINT_COUNT, +}; + static int rzg2l_irqc_probe(struct platform_device *pdev, struct device_node *parent) { - return rzg2l_irqc_common_probe(pdev, parent, &rzg2l_irqc_irq_chip, &rzg2l_irqc_tint_chip); + return rzg2l_irqc_common_probe(pdev, parent, &rzg2l_irqc_irq_chip, &rzg2l_irqc_tint_chip, + rzg2l_hw_params); } static int rzfive_irqc_probe(struct platform_device *pdev, struct device_node *parent) { - return rzg2l_irqc_common_probe(pdev, parent, &rzfive_irqc_irq_chip, &rzfive_irqc_tint_chip); + return rzg2l_irqc_common_probe(pdev, parent, &rzfive_irqc_irq_chip, &rzfive_irqc_tint_chip, + rzg2l_hw_params); } IRQCHIP_PLATFORM_DRIVER_BEGIN(rzg2l_irqc) -- cgit v1.2.3 From 87404cb8cae43b680caaaa621280e71969a09147 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Wed, 25 Mar 2026 19:24:28 +0000 Subject: irqchip/renesas-rzg2l: Drop IRQC_TINT_START macro The IRQC_TINT_START value is different for RZ/G3L and RZ/G2L SoC. Add tint_start variable in struct rzg2l_hw_info to handle this difference and drop the macro IRQC_TINT_START. While at it, update the variable type of titseln, tssr_offset, tssr_index, index, and sense to unsigned int, in rzg2l_tint_set_edge() as these variables are used only for calculation. Signed-off-by: Biju Das Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260325192451.172562-14-biju.das.jz@bp.renesas.com --- drivers/irqchip/irq-renesas-rzg2l.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-rzg2l.c b/drivers/irqchip/irq-renesas-rzg2l.c index 2b6dac7e72b7..06caa2258334 100644 --- a/drivers/irqchip/irq-renesas-rzg2l.c +++ b/drivers/irqchip/irq-renesas-rzg2l.c @@ -22,7 +22,6 @@ #define IRQC_IRQ_START 1 #define IRQC_IRQ_COUNT 8 -#define IRQC_TINT_START (IRQC_IRQ_START + IRQC_IRQ_COUNT) #define IRQC_TINT_COUNT 32 #define ISCR 0x10 @@ -69,9 +68,11 @@ struct rzg2l_irqc_reg_cache { /** * struct rzg2l_hw_info - Interrupt Control Unit controller hardware info structure. + * @tint_start: Start of TINT interrupts * @num_irq: Total Number of interrupts */ struct rzg2l_hw_info { + unsigned int tint_start; unsigned int num_irq; }; @@ -125,7 +126,7 @@ static void rzg2l_clear_irq_int(struct rzg2l_irqc_priv *priv, unsigned int hwirq static void rzg2l_clear_tint_int(struct rzg2l_irqc_priv *priv, unsigned int hwirq) { - u32 bit = BIT(hwirq - IRQC_TINT_START); + u32 bit = BIT(hwirq - priv->info.tint_start); u32 reg; reg = readl_relaxed(priv->base + TSCR); @@ -180,7 +181,7 @@ static void rzfive_irqc_unmask_irq_interrupt(struct rzg2l_irqc_priv *priv, static void rzfive_irqc_mask_tint_interrupt(struct rzg2l_irqc_priv *priv, unsigned int hwirq) { - u32 bit = BIT(hwirq - IRQC_TINT_START); + u32 bit = BIT(hwirq - priv->info.tint_start); writel_relaxed(readl_relaxed(priv->base + TMSK) | bit, priv->base + TMSK); } @@ -188,7 +189,7 @@ static void rzfive_irqc_mask_tint_interrupt(struct rzg2l_irqc_priv *priv, static void rzfive_irqc_unmask_tint_interrupt(struct rzg2l_irqc_priv *priv, unsigned int hwirq) { - u32 bit = BIT(hwirq - IRQC_TINT_START); + u32 bit = BIT(hwirq - priv->info.tint_start); writel_relaxed(readl_relaxed(priv->base + TMSK) & ~bit, priv->base + TMSK); } @@ -253,7 +254,7 @@ static void rzfive_tint_endisable(struct irq_data *d, bool enable) { struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); unsigned int hwirq = irqd_to_hwirq(d); - unsigned int offset = hwirq - IRQC_TINT_START; + unsigned int offset = hwirq - priv->info.tint_start; unsigned int tssr_offset = TSSR_OFFSET(offset); unsigned int tssr_index = TSSR_INDEX(offset); u32 reg; @@ -299,7 +300,7 @@ static void rzg2l_tint_irq_endisable(struct irq_data *d, bool enable) { struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); unsigned int hw_irq = irqd_to_hwirq(d); - unsigned int offset = hw_irq - IRQC_TINT_START; + unsigned int offset = hw_irq - priv->info.tint_start; unsigned int tssr_offset = TSSR_OFFSET(offset); unsigned int tssr_index = TSSR_INDEX(offset); u32 reg; @@ -388,10 +389,10 @@ static int rzg2l_tint_set_edge(struct irq_data *d, unsigned int type) { struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); unsigned int hwirq = irqd_to_hwirq(d); - u32 titseln = hwirq - IRQC_TINT_START; - u32 tssr_offset = TSSR_OFFSET(titseln); - u8 tssr_index = TSSR_INDEX(titseln); - u8 index, sense; + unsigned int titseln = hwirq - priv->info.tint_start; + unsigned int tssr_offset = TSSR_OFFSET(titseln); + unsigned int tssr_index = TSSR_INDEX(titseln); + unsigned int index, sense; u32 reg, tssr; switch (type & IRQ_TYPE_SENSE_MASK) { @@ -680,6 +681,7 @@ static int rzg2l_irqc_common_probe(struct platform_device *pdev, struct device_n } static const struct rzg2l_hw_info rzg2l_hw_params = { + .tint_start = IRQC_IRQ_START + IRQC_IRQ_COUNT, .num_irq = IRQC_IRQ_START + IRQC_IRQ_COUNT + IRQC_TINT_COUNT, }; -- cgit v1.2.3 From f9544cad3600f251a7d24c2fb77e7f2abdceb42e Mon Sep 17 00:00:00 2001 From: Biju Das Date: Wed, 25 Mar 2026 19:24:29 +0000 Subject: irqchip/renesas-rzg2l: Drop IRQC_IRQ_COUNT macro The total number of external interrupts in RZ/G2L and RZ/G3L SoC are different. The RZ/G3L has 16 external interrupts whereas RZ/G2L has only 8 external interrupts. Add irq_count variable in struct rzg2l_hw_info to handle these differences and drop the macro IRQC_IRQ_COUNT. Signed-off-by: Biju Das Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260325192451.172562-15-biju.das.jz@bp.renesas.com --- drivers/irqchip/irq-renesas-rzg2l.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-rzg2l.c b/drivers/irqchip/irq-renesas-rzg2l.c index 06caa2258334..5387e901075e 100644 --- a/drivers/irqchip/irq-renesas-rzg2l.c +++ b/drivers/irqchip/irq-renesas-rzg2l.c @@ -21,7 +21,6 @@ #include #define IRQC_IRQ_START 1 -#define IRQC_IRQ_COUNT 8 #define IRQC_TINT_COUNT 32 #define ISCR 0x10 @@ -68,10 +67,12 @@ struct rzg2l_irqc_reg_cache { /** * struct rzg2l_hw_info - Interrupt Control Unit controller hardware info structure. + * @irq_count: Number of IRQC interrupts * @tint_start: Start of TINT interrupts * @num_irq: Total Number of interrupts */ struct rzg2l_hw_info { + unsigned int irq_count; unsigned int tint_start; unsigned int num_irq; }; @@ -573,7 +574,7 @@ static int rzg2l_irqc_alloc(struct irq_domain *domain, unsigned int virq, * from 16-31 bits. TINT from the pinctrl driver needs to be programmed * in IRQC registers to enable a given gpio pin as interrupt. */ - if (hwirq > IRQC_IRQ_COUNT) { + if (hwirq > priv->info.irq_count) { tint = TINT_EXTRACT_GPIOINT(hwirq); hwirq = TINT_EXTRACT_HWIRQ(hwirq); chip = priv->tint_chip; @@ -681,8 +682,9 @@ static int rzg2l_irqc_common_probe(struct platform_device *pdev, struct device_n } static const struct rzg2l_hw_info rzg2l_hw_params = { - .tint_start = IRQC_IRQ_START + IRQC_IRQ_COUNT, - .num_irq = IRQC_IRQ_START + IRQC_IRQ_COUNT + IRQC_TINT_COUNT, + .irq_count = 8, + .tint_start = IRQC_IRQ_START + 8, + .num_irq = IRQC_IRQ_START + 8 + IRQC_TINT_COUNT, }; static int rzg2l_irqc_probe(struct platform_device *pdev, struct device_node *parent) -- cgit v1.2.3 From 98b24d39c852d2498aae24c9aa0a3b11edb8cc2c Mon Sep 17 00:00:00 2001 From: Biju Das Date: Wed, 25 Mar 2026 19:24:30 +0000 Subject: irqchip/renesas-rzg2l: Add RZ/G3L support The IRQC block on the RZ/G3L SoC is almost identical to the one found on the RZ/G2L SoC, with the following differences: - The number of GPIO interrupts for TINT selection is 113 instead of 123. - The pin index and TINT selection index are not in the 1:1 map. - The number of external interrupts are 16 instead of 8, out of these 8 external interrupts are shared with TINT. Add support for the RZ/G3L driver by filling the rzg2l_hw_info table and adding LUT for mapping between pin index and TINT selection index. Signed-off-by: Biju Das Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260325192451.172562-16-biju.das.jz@bp.renesas.com --- drivers/irqchip/irq-renesas-rzg2l.c | 48 +++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-rzg2l.c b/drivers/irqchip/irq-renesas-rzg2l.c index 5387e901075e..970126b5eec4 100644 --- a/drivers/irqchip/irq-renesas-rzg2l.c +++ b/drivers/irqchip/irq-renesas-rzg2l.c @@ -67,11 +67,13 @@ struct rzg2l_irqc_reg_cache { /** * struct rzg2l_hw_info - Interrupt Control Unit controller hardware info structure. + * @tssel_lut: TINT lookup table * @irq_count: Number of IRQC interrupts * @tint_start: Start of TINT interrupts * @num_irq: Total Number of interrupts */ struct rzg2l_hw_info { + const u8 *tssel_lut; unsigned int irq_count; unsigned int tint_start; unsigned int num_irq; @@ -331,9 +333,9 @@ static int rzg2l_irq_set_type(struct irq_data *d, unsigned int type) { struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); unsigned int hwirq = irqd_to_hwirq(d); - u32 iitseln = hwirq - IRQC_IRQ_START; + unsigned int iitseln = hwirq - IRQC_IRQ_START; bool clear_irq_int = false; - u16 sense, tmp; + unsigned int sense, tmp; switch (type & IRQ_TYPE_SENSE_MASK) { case IRQ_TYPE_LEVEL_LOW: @@ -377,6 +379,11 @@ static u32 rzg2l_disable_tint_and_set_tint_source(struct irq_data *d, struct rzg u32 tint = (u32)(uintptr_t)irq_data_get_irq_chip_data(d); u32 tien = reg & (TIEN << TSSEL_SHIFT(tssr_offset)); + if (priv->info.tssel_lut) + tint = priv->info.tssel_lut[tint]; + else + tint = (u32)(uintptr_t)irq_data_get_irq_chip_data(d); + /* Clear the relevant byte in reg */ reg &= ~(TSSEL_MASK << TSSEL_SHIFT(tssr_offset)); /* Set TINT and leave TIEN clear */ @@ -681,6 +688,36 @@ static int rzg2l_irqc_common_probe(struct platform_device *pdev, struct device_n return 0; } +/* Mapping based on port index on Table 4.2-1 and GPIOINT on Table 4.6-7 */ +static const u8 rzg3l_tssel_lut[] = { + 83, 84, /* P20-P21 */ + 7, 8, 9, 10, 11, 12, 13, /* P30-P36 */ + 85, 86, 87, 88, 89, 90, 91, /* P50-P56 */ + 92, 93, 94, 95, 96, 97, 98, /* P60-P66 */ + 99, 100, 101, 102, 103, 104, 105, 106, /* P70-P77 */ + 107, 108, 109, 110, 111, 112, /* P80-P85 */ + 45, 46, 47, 48, 49, 50, 51, 52, /* PA0-PA7 */ + 53, 54, 55, 56, 57, 58, 59, 60, /* PB0-PB7 */ + 61, 62, 63, /* PC0-PC2 */ + 64, 65, 66, 67, 68, 69, 70, 71, /* PD0-PD7 */ + 72, 73, 74, 75, 76, 77, 78, 79, /* PE0-PE7 */ + 80, 81, 82, /* PF0-PF2 */ + 27, 28, 29, 30, 31, 32, 33, 34, /* PG0-PG7 */ + 35, 36, 37, 38, 39, 40, /* PH0-PH5 */ + 2, 3, 4, 5, 6, /* PJ0-PJ4 */ + 41, 42, 43, 44, /* PK0-PK3 */ + 14, 15, 16, 17, 26, /* PL0-PL4 */ + 18, 19, 20, 21, 22, 23, 24, 25, /* PM0-PM7 */ + 0, 1 /* PS0-PS1 */ +}; + +static const struct rzg2l_hw_info rzg3l_hw_params = { + .tssel_lut = rzg3l_tssel_lut, + .irq_count = 16, + .tint_start = IRQC_IRQ_START + 16, + .num_irq = IRQC_IRQ_START + 16 + IRQC_TINT_COUNT, +}; + static const struct rzg2l_hw_info rzg2l_hw_params = { .irq_count = 8, .tint_start = IRQC_IRQ_START + 8, @@ -693,6 +730,12 @@ static int rzg2l_irqc_probe(struct platform_device *pdev, struct device_node *pa rzg2l_hw_params); } +static int rzg3l_irqc_probe(struct platform_device *pdev, struct device_node *parent) +{ + return rzg2l_irqc_common_probe(pdev, parent, &rzg2l_irqc_irq_chip, &rzg2l_irqc_tint_chip, + rzg3l_hw_params); +} + static int rzfive_irqc_probe(struct platform_device *pdev, struct device_node *parent) { return rzg2l_irqc_common_probe(pdev, parent, &rzfive_irqc_irq_chip, &rzfive_irqc_tint_chip, @@ -701,6 +744,7 @@ static int rzfive_irqc_probe(struct platform_device *pdev, struct device_node *p IRQCHIP_PLATFORM_DRIVER_BEGIN(rzg2l_irqc) IRQCHIP_MATCH("renesas,rzg2l-irqc", rzg2l_irqc_probe) +IRQCHIP_MATCH("renesas,r9a08g046-irqc", rzg3l_irqc_probe) IRQCHIP_MATCH("renesas,r9a07g043f-irqc", rzfive_irqc_probe) IRQCHIP_PLATFORM_DRIVER_END(rzg2l_irqc) MODULE_AUTHOR("Lad Prabhakar "); -- cgit v1.2.3 From e0fcae27ff572212c39b1078e7aa0795ce5970e7 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Wed, 25 Mar 2026 19:24:31 +0000 Subject: irqchip/renesas-rzg2l: Add shared interrupt support The RZ/G3L SoC has 16 external interrupts, of which 8 are shared with TINT (GPIO interrupts), whereas RZ/G2L has only 8 external interrupts with no sharing. The shared interrupt line selection between external interrupt and GPIO interrupt is based on the INTTSEL register. Add shared_irq_cnt variable to struct rzg2l_hw_info handle these differences. Add used_irqs bitmap to struct rzg2l_irqc_priv to track allocation state. In the alloc callback, use test_and_set_bit() to enforce mutual exclusion and configure the INTTSEL register to route to either the external interrupt or TINT. In the free callback, use test_and_clear_bit() to release the shared interrupt line and reset the INTTSEL. Also add INTTSEL register save/restore support to the suspend/resume path. Signed-off-by: Biju Das Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260325192451.172562-17-biju.das.jz@bp.renesas.com --- drivers/irqchip/irq-renesas-rzg2l.c | 118 +++++++++++++++++++++++++++++++++++- 1 file changed, 115 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-rzg2l.c b/drivers/irqchip/irq-renesas-rzg2l.c index 970126b5eec4..f5c4d7e0aec3 100644 --- a/drivers/irqchip/irq-renesas-rzg2l.c +++ b/drivers/irqchip/irq-renesas-rzg2l.c @@ -22,6 +22,8 @@ #define IRQC_IRQ_START 1 #define IRQC_TINT_COUNT 32 +#define IRQC_SHARED_IRQ_COUNT 8 +#define IRQC_IRQ_SHARED_START (IRQC_IRQ_START + IRQC_SHARED_IRQ_COUNT) #define ISCR 0x10 #define IITSR 0x14 @@ -29,6 +31,7 @@ #define TITSR(n) (0x24 + (n) * 4) #define TITSR0_MAX_INT 16 #define TITSEL_WIDTH 0x2 +#define INTTSEL 0x2c #define TSSR(n) (0x30 + ((n) * 4)) #define TIEN BIT(7) #define TSSEL_SHIFT(n) (8 * (n)) @@ -52,16 +55,21 @@ #define IITSR_IITSEL_EDGE_BOTH 3 #define IITSR_IITSEL_MASK(n) IITSR_IITSEL((n), 3) +#define INTTSEL_TINTSEL(n) BIT(n) +#define INTTSEL_TINTSEL_START 24 + #define TINT_EXTRACT_HWIRQ(x) FIELD_GET(GENMASK(15, 0), (x)) #define TINT_EXTRACT_GPIOINT(x) FIELD_GET(GENMASK(31, 16), (x)) /** * struct rzg2l_irqc_reg_cache - registers cache (necessary for suspend/resume) * @iitsr: IITSR register + * @inttsel: INTTSEL register * @titsr: TITSR registers */ struct rzg2l_irqc_reg_cache { u32 iitsr; + u32 inttsel; u32 titsr[2]; }; @@ -71,12 +79,14 @@ struct rzg2l_irqc_reg_cache { * @irq_count: Number of IRQC interrupts * @tint_start: Start of TINT interrupts * @num_irq: Total Number of interrupts + * @shared_irq_cnt: Number of shared interrupts */ struct rzg2l_hw_info { const u8 *tssel_lut; unsigned int irq_count; unsigned int tint_start; unsigned int num_irq; + unsigned int shared_irq_cnt; }; /** @@ -88,6 +98,7 @@ struct rzg2l_hw_info { * @lock: Lock to serialize access to hardware registers * @info: Hardware specific data * @cache: Registers cache for suspend/resume + * @used_irqs: Bitmap to manage the shared interrupts */ static struct rzg2l_irqc_priv { void __iomem *base; @@ -97,6 +108,7 @@ static struct rzg2l_irqc_priv { raw_spinlock_t lock; struct rzg2l_hw_info info; struct rzg2l_irqc_reg_cache cache; + DECLARE_BITMAP(used_irqs, IRQC_SHARED_IRQ_COUNT); } *rzg2l_irqc_data; static struct rzg2l_irqc_priv *irq_data_to_priv(struct irq_data *data) @@ -462,6 +474,8 @@ static int rzg2l_irqc_irq_suspend(void *data) void __iomem *base = rzg2l_irqc_data->base; cache->iitsr = readl_relaxed(base + IITSR); + if (rzg2l_irqc_data->info.shared_irq_cnt) + cache->inttsel = readl_relaxed(base + INTTSEL); for (u8 i = 0; i < 2; i++) cache->titsr[i] = readl_relaxed(base + TITSR(i)); @@ -480,6 +494,8 @@ static void rzg2l_irqc_irq_resume(void *data) */ for (u8 i = 0; i < 2; i++) writel_relaxed(cache->titsr[i], base + TITSR(i)); + if (rzg2l_irqc_data->info.shared_irq_cnt) + writel_relaxed(cache->inttsel, base + INTTSEL); writel_relaxed(cache->iitsr, base + IITSR); } @@ -560,6 +576,72 @@ static const struct irq_chip rzfive_irqc_tint_chip = { IRQCHIP_SKIP_SET_WAKE, }; +static bool rzg2l_irqc_is_shared_irqc(const struct rzg2l_hw_info info, unsigned int hw_irq) +{ + return ((hw_irq >= (info.tint_start - info.shared_irq_cnt)) && hw_irq < info.tint_start); +} + +static bool rzg2l_irqc_is_shared_tint(const struct rzg2l_hw_info info, unsigned int hw_irq) +{ + return ((hw_irq >= (info.num_irq - info.shared_irq_cnt)) && hw_irq < info.num_irq); +} + +static bool rzg2l_irqc_is_shared_and_get_irq_num(struct rzg2l_irqc_priv *priv, + irq_hw_number_t hwirq, unsigned int *irq_num) +{ + bool is_shared = false; + + if (rzg2l_irqc_is_shared_irqc(priv->info, hwirq)) { + *irq_num = hwirq - IRQC_IRQ_SHARED_START; + is_shared = true; + } else if (rzg2l_irqc_is_shared_tint(priv->info, hwirq)) { + *irq_num = hwirq - IRQC_TINT_COUNT - IRQC_IRQ_SHARED_START; + is_shared = true; + } + + return is_shared; +} + +static void rzg2l_irqc_set_inttsel(struct rzg2l_irqc_priv *priv, unsigned int offset, + unsigned int select_irq) +{ + u32 reg; + + guard(raw_spinlock_irqsave)(&priv->lock); + reg = readl_relaxed(priv->base + INTTSEL); + if (select_irq) + reg |= INTTSEL_TINTSEL(offset); + else + reg &= ~INTTSEL_TINTSEL(offset); + writel_relaxed(reg, priv->base + INTTSEL); +} + +static int rzg2l_irqc_shared_irq_alloc(struct rzg2l_irqc_priv *priv, irq_hw_number_t hwirq) +{ + unsigned int irq_num; + + if (rzg2l_irqc_is_shared_and_get_irq_num(priv, hwirq, &irq_num)) { + if (test_and_set_bit(irq_num, priv->used_irqs)) + return -EBUSY; + + if (hwirq < priv->info.tint_start) + rzg2l_irqc_set_inttsel(priv, INTTSEL_TINTSEL_START + irq_num, 1); + else + rzg2l_irqc_set_inttsel(priv, INTTSEL_TINTSEL_START + irq_num, 0); + } + + return 0; +} + +static void rzg2l_irqc_shared_irq_free(struct rzg2l_irqc_priv *priv, irq_hw_number_t hwirq) +{ + unsigned int irq_num; + + if (rzg2l_irqc_is_shared_and_get_irq_num(priv, hwirq, &irq_num) && + test_and_clear_bit(irq_num, priv->used_irqs)) + rzg2l_irqc_set_inttsel(priv, INTTSEL_TINTSEL_START + irq_num, 0); +} + static int rzg2l_irqc_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs, void *arg) { @@ -592,16 +674,45 @@ static int rzg2l_irqc_alloc(struct irq_domain *domain, unsigned int virq, if (hwirq >= priv->info.num_irq) return -EINVAL; + if (priv->info.shared_irq_cnt) { + ret = rzg2l_irqc_shared_irq_alloc(priv, hwirq); + if (ret) + return ret; + } + ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, chip, (void *)(uintptr_t)tint); if (ret) - return ret; + goto shared_irq_free; + + ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &priv->fwspec[hwirq]); + if (ret) + goto shared_irq_free; + + return 0; + +shared_irq_free: + if (priv->info.shared_irq_cnt) + rzg2l_irqc_shared_irq_free(priv, hwirq); + + return ret; +} - return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &priv->fwspec[hwirq]); +static void rzg2l_irqc_free(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs) +{ + struct rzg2l_irqc_priv *priv = domain->host_data; + + irq_domain_free_irqs_common(domain, virq, nr_irqs); + + if (priv->info.shared_irq_cnt) { + struct irq_data *d = irq_domain_get_irq_data(domain, virq); + + rzg2l_irqc_shared_irq_free(priv, irqd_to_hwirq(d)); + } } static const struct irq_domain_ops rzg2l_irqc_domain_ops = { .alloc = rzg2l_irqc_alloc, - .free = irq_domain_free_irqs_common, + .free = rzg2l_irqc_free, .translate = irq_domain_translate_twocell, }; @@ -716,6 +827,7 @@ static const struct rzg2l_hw_info rzg3l_hw_params = { .irq_count = 16, .tint_start = IRQC_IRQ_START + 16, .num_irq = IRQC_IRQ_START + 16 + IRQC_TINT_COUNT, + .shared_irq_cnt = IRQC_SHARED_IRQ_COUNT, }; static const struct rzg2l_hw_info rzg2l_hw_params = { -- cgit v1.2.3 From 42972b5464295450ce55f457645917aba4d54ead Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 6 Mar 2026 13:13:32 +0100 Subject: irqchip/gic-v3: Print a warning for out-of-range interrupt numbers gic_irq_domain_translate() does not check if an interrupt number lies within the valid range of the specified interrupt type. Add these checks, and print a warning if the interrupt number is out of range. This can help flagging incorrectly described Extended SPI and PPI interrupts in DT. Signed-off-by: Geert Uytterhoeven Signed-off-by: Thomas Gleixner Acked-by: Marc Zyngier Link: https://patch.msgid.link/ce695ea46decc816974179314a86f2b9b5cad6a9.1772799134.git.geert+renesas@glider.be --- drivers/irqchip/irq-gic-v3.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 6dc9827357a2..99444a1b2ffa 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -1603,15 +1603,23 @@ static int gic_irq_domain_translate(struct irq_domain *d, switch (fwspec->param[0]) { case 0: /* SPI */ + if (fwspec->param[1] > 987) + pr_warn_once("SPI %u out of range (use ESPI?)\n", fwspec->param[1]); *hwirq = fwspec->param[1] + 32; break; case 1: /* PPI */ + if (fwspec->param[1] > 15) + pr_warn_once("PPI %u out of range (use EPPI?)\n", fwspec->param[1]); *hwirq = fwspec->param[1] + 16; break; case 2: /* ESPI */ + if (fwspec->param[1] > 1023) + pr_warn_once("ESPI %u out of range\n", fwspec->param[1]); *hwirq = fwspec->param[1] + ESPI_BASE_INTID; break; case 3: /* EPPI */ + if (fwspec->param[1] > 63) + pr_warn_once("EPPI %u out of range\n", fwspec->param[1]); *hwirq = fwspec->param[1] + EPPI_BASE_INTID; break; case GIC_IRQ_TYPE_LPI: /* LPI */ -- cgit v1.2.3 From 9fd2170d70178faa0427adaa9d2dfdbfa231d1b7 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sat, 28 Mar 2026 10:33:19 +0000 Subject: irqchip/renesas-rzg2l: Replace raw_spin_{lock,unlock} with guard() in rzg2l_irq_set_type() Simplify the locking logic in rzg2l_irq_set_type() by using guard(), eliminating the need for an explicit unlock call. [ tglx: Remove the pointless cleanup.h include. The spinlock guards come from spinlock.h ] Signed-off-by: Biju Das Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260328103324.134131-3-biju.das.jz@bp.renesas.com --- drivers/irqchip/irq-renesas-rzg2l.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-rzg2l.c b/drivers/irqchip/irq-renesas-rzg2l.c index f5c4d7e0aec3..755fac39de0c 100644 --- a/drivers/irqchip/irq-renesas-rzg2l.c +++ b/drivers/irqchip/irq-renesas-rzg2l.c @@ -373,14 +373,13 @@ static int rzg2l_irq_set_type(struct irq_data *d, unsigned int type) return -EINVAL; } - raw_spin_lock(&priv->lock); + guard(raw_spinlock)(&priv->lock); tmp = readl_relaxed(priv->base + IITSR); tmp &= ~IITSR_IITSEL_MASK(iitseln); tmp |= IITSR_IITSEL(iitseln, sense); if (clear_irq_int) rzg2l_clear_irq_int(priv, hwirq); writel_relaxed(tmp, priv->base + IITSR); - raw_spin_unlock(&priv->lock); return 0; } -- cgit v1.2.3 From d3689cd02c5de52ff5f3044169c482aee0dd5a78 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sat, 28 Mar 2026 10:33:18 +0000 Subject: irqchip/renesas-rzg2l: Clear the shared interrupt bit in rzg2l_irqc_free() rzg2l_irqc_free() invokes irq_domain_free_irqs_common(), which internally calls irq_domain_reset_irq_data(). That explicitly sets irq_data->hwirq to 0. Consequently, irqd_to_hwirq(d) returns 0 when called after it. Since 0 falls outside the valid shared IRQ ranges, rzg2l_irqc_is_shared_and_get_irq_num() evaluates to false, completely bypassing the test_and_clear_bit() operation. This leaves the bit set in priv->used_irqs, causing future allocations to fail with -EBUSY. Fix this by retrieving irq_data and caching hwirq before calling irq_domain_free_irqs_common(). Fixes: e0fcae27ff57 ("irqchip/renesas-rzg2l: Add shared interrupt support") Signed-off-by: Biju Das Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260328103324.134131-2-biju.das.jz@bp.renesas.com --- drivers/irqchip/irq-renesas-rzg2l.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-rzg2l.c b/drivers/irqchip/irq-renesas-rzg2l.c index 755fac39de0c..199b3c6b02c8 100644 --- a/drivers/irqchip/irq-renesas-rzg2l.c +++ b/drivers/irqchip/irq-renesas-rzg2l.c @@ -698,15 +698,14 @@ shared_irq_free: static void rzg2l_irqc_free(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs) { + struct irq_data *d = irq_domain_get_irq_data(domain, virq); struct rzg2l_irqc_priv *priv = domain->host_data; + irq_hw_number_t hwirq = irqd_to_hwirq(d); irq_domain_free_irqs_common(domain, virq, nr_irqs); - if (priv->info.shared_irq_cnt) { - struct irq_data *d = irq_domain_get_irq_data(domain, virq); - - rzg2l_irqc_shared_irq_free(priv, irqd_to_hwirq(d)); - } + if (priv->info.shared_irq_cnt) + rzg2l_irqc_shared_irq_free(priv, hwirq); } static const struct irq_domain_ops rzg2l_irqc_domain_ops = { -- cgit v1.2.3 From 0e5988549dc73d985cc12c4cb438771ced74f522 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Wed, 1 Apr 2026 12:45:00 +0100 Subject: irqchip/renesas-rzg2l: Add NMI support The RZ/G2L SoC has an NMI interrupt. Add support for the NMI interrupt. Signed-off-by: Biju Das Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260401114504.332825-1-biju.das.jz@bp.renesas.com --- drivers/irqchip/irq-renesas-rzg2l.c | 99 +++++++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-rzg2l.c b/drivers/irqchip/irq-renesas-rzg2l.c index 199b3c6b02c8..f6b2e69a2f4e 100644 --- a/drivers/irqchip/irq-renesas-rzg2l.c +++ b/drivers/irqchip/irq-renesas-rzg2l.c @@ -20,11 +20,14 @@ #include #include +#define IRQC_NMI 0 #define IRQC_IRQ_START 1 #define IRQC_TINT_COUNT 32 #define IRQC_SHARED_IRQ_COUNT 8 #define IRQC_IRQ_SHARED_START (IRQC_IRQ_START + IRQC_SHARED_IRQ_COUNT) +#define NSCR 0x0 +#define NITSR 0x4 #define ISCR 0x10 #define IITSR 0x14 #define TSCR 0x20 @@ -43,6 +46,10 @@ #define TSSR_OFFSET(n) ((n) % 4) #define TSSR_INDEX(n) ((n) / 4) +#define NSCR_NSTAT 0 +#define NITSR_NTSEL_EDGE_FALLING 0 +#define NITSR_NTSEL_EDGE_RISING 1 + #define TITSR_TITSEL_EDGE_RISING 0 #define TITSR_TITSEL_EDGE_FALLING 1 #define TITSR_TITSEL_LEVEL_HIGH 2 @@ -63,11 +70,13 @@ /** * struct rzg2l_irqc_reg_cache - registers cache (necessary for suspend/resume) - * @iitsr: IITSR register - * @inttsel: INTTSEL register - * @titsr: TITSR registers + * @nitsr: NITSR register + * @iitsr: IITSR register + * @inttsel: INTTSEL register + * @titsr: TITSR registers */ struct rzg2l_irqc_reg_cache { + u32 nitsr; u32 iitsr; u32 inttsel; u32 titsr[2]; @@ -116,6 +125,28 @@ static struct rzg2l_irqc_priv *irq_data_to_priv(struct irq_data *data) return data->domain->host_data; } +static void rzg2l_clear_nmi_int(struct rzg2l_irqc_priv *priv) +{ + u32 bit = BIT(NSCR_NSTAT); + u32 reg; + + /* + * No locking required as the register is not shared + * with other interrupts. + * + * Writing is allowed only when NSTAT is 1 + */ + reg = readl_relaxed(priv->base + NSCR); + if (reg & bit) { + writel_relaxed(reg & ~bit, priv->base + NSCR); + /* + * Enforce that the posted write is flushed to prevent that the + * just handled interrupt is raised again. + */ + readl_relaxed(priv->base + NSCR); + } +} + static void rzg2l_clear_irq_int(struct rzg2l_irqc_priv *priv, unsigned int hwirq) { unsigned int hw_irq = hwirq - IRQC_IRQ_START; @@ -155,6 +186,14 @@ static void rzg2l_clear_tint_int(struct rzg2l_irqc_priv *priv, unsigned int hwir } } +static void rzg2l_irqc_nmi_eoi(struct irq_data *d) +{ + struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); + + rzg2l_clear_nmi_int(priv); + irq_chip_eoi_parent(d); +} + static void rzg2l_irqc_irq_eoi(struct irq_data *d) { struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); @@ -341,6 +380,26 @@ static void rzg2l_irqc_tint_enable(struct irq_data *d) irq_chip_enable_parent(d); } +static int rzg2l_nmi_set_type(struct irq_data *d, unsigned int type) +{ + struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); + u32 sense; + + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_EDGE_FALLING: + sense = NITSR_NTSEL_EDGE_FALLING; + break; + case IRQ_TYPE_EDGE_RISING: + sense = NITSR_NTSEL_EDGE_RISING; + break; + default: + return -EINVAL; + } + + writel_relaxed(sense, priv->base + NITSR); + return 0; +} + static int rzg2l_irq_set_type(struct irq_data *d, unsigned int type) { struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); @@ -467,11 +526,23 @@ static int rzg2l_irqc_tint_set_type(struct irq_data *d, unsigned int type) return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH); } +static int rzg2l_irqc_nmi_set_type(struct irq_data *d, unsigned int type) +{ + int ret; + + ret = rzg2l_nmi_set_type(d, type); + if (ret) + return ret; + + return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH); +} + static int rzg2l_irqc_irq_suspend(void *data) { struct rzg2l_irqc_reg_cache *cache = &rzg2l_irqc_data->cache; void __iomem *base = rzg2l_irqc_data->base; + cache->nitsr = readl_relaxed(base + NITSR); cache->iitsr = readl_relaxed(base + IITSR); if (rzg2l_irqc_data->info.shared_irq_cnt) cache->inttsel = readl_relaxed(base + INTTSEL); @@ -496,6 +567,7 @@ static void rzg2l_irqc_irq_resume(void *data) if (rzg2l_irqc_data->info.shared_irq_cnt) writel_relaxed(cache->inttsel, base + INTTSEL); writel_relaxed(cache->iitsr, base + IITSR); + writel_relaxed(cache->nitsr, base + NITSR); } static const struct syscore_ops rzg2l_irqc_syscore_ops = { @@ -507,6 +579,23 @@ static struct syscore rzg2l_irqc_syscore = { .ops = &rzg2l_irqc_syscore_ops, }; +static const struct irq_chip rzg2l_irqc_nmi_chip = { + .name = "rzg2l-irqc", + .irq_eoi = rzg2l_irqc_nmi_eoi, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_disable = irq_chip_disable_parent, + .irq_enable = irq_chip_enable_parent, + .irq_get_irqchip_state = irq_chip_get_parent_state, + .irq_set_irqchip_state = irq_chip_set_parent_state, + .irq_retrigger = irq_chip_retrigger_hierarchy, + .irq_set_type = rzg2l_irqc_nmi_set_type, + .irq_set_affinity = irq_chip_set_affinity_parent, + .flags = IRQCHIP_MASK_ON_SUSPEND | + IRQCHIP_SET_TYPE_MASKED | + IRQCHIP_SKIP_SET_WAKE, +}; + static const struct irq_chip rzg2l_irqc_irq_chip = { .name = "rzg2l-irqc", .irq_eoi = rzg2l_irqc_irq_eoi, @@ -662,7 +751,9 @@ static int rzg2l_irqc_alloc(struct irq_domain *domain, unsigned int virq, * from 16-31 bits. TINT from the pinctrl driver needs to be programmed * in IRQC registers to enable a given gpio pin as interrupt. */ - if (hwirq > priv->info.irq_count) { + if (hwirq == IRQC_NMI) { + chip = &rzg2l_irqc_nmi_chip; + } else if (hwirq > priv->info.irq_count) { tint = TINT_EXTRACT_GPIOINT(hwirq); hwirq = TINT_EXTRACT_HWIRQ(hwirq); chip = priv->tint_chip; -- cgit v1.2.3 From fc4c926ccd34d4ee75669991693b47a9b8645a6c Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 3 Apr 2026 10:48:21 +0200 Subject: irqchip/renesas-rzv2h: Kill swint_idx[] The array swint_idx[] just contains an identity mapping. Replace it by using the index directly, to simplify the code. Signed-off-by: Geert Uytterhoeven Signed-off-by: Thomas Gleixner Reviewed-by: Biju Das Link: https://patch.msgid.link/0f32ba2a4701311710d02ff4fa2fd472b56745c4.1775205874.git.geert+renesas@glider.be --- drivers/irqchip/irq-renesas-rzv2h.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-rzv2h.c b/drivers/irqchip/irq-renesas-rzv2h.c index 31f191641ff8..9461f19fc38c 100644 --- a/drivers/irqchip/irq-renesas-rzv2h.c +++ b/drivers/irqchip/irq-renesas-rzv2h.c @@ -745,7 +745,7 @@ static irqreturn_t rzv2h_icu_error_irq(int irq, void *data) static irqreturn_t rzv2h_icu_swint_irq(int irq, void *data) { - u8 cpu = *(u8 *)data; + unsigned int cpu = (uintptr_t)data; pr_info("SWINT interrupt for CA55 core %u\n", cpu); return IRQ_HANDLED; @@ -760,7 +760,6 @@ static int rzv2h_icu_setup_irqs(struct platform_device *pdev, struct irq_domain "int-ca55-2", "int-ca55-3", }; static const char *icu_err = "icu-error-ca55"; - static const u8 swint_idx[] = { 0, 1, 2, 3 }; void __iomem *base = rzv2h_icu_data->base; struct device *dev = &pdev->dev; struct irq_fwspec fwspec; @@ -780,7 +779,7 @@ static int rzv2h_icu_setup_irqs(struct platform_device *pdev, struct irq_domain } ret = devm_request_irq(dev, virq, rzv2h_icu_swint_irq, 0, dev_name(dev), - (void *)&swint_idx[i]); + (void *)(uintptr_t)i); if (ret) { return dev_err_probe(dev, ret, "Failed to request %s IRQ\n", rzv2h_swint_names[i]); -- cgit v1.2.3 From 8c7ffedff0db88837de11cfaf3e5367e0b77bc4c Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 3 Apr 2026 10:48:22 +0200 Subject: irqchip/renesas-rzv2h: Kill swint_names[] The array swint_names[] just contains expansions of "int-ca55-%u". Replace it by formatting the strings where needed, to improve readability. Despite the two error messages can no longer be shared with the ICU error cases, this reduces generated code size by 56 bytes. Signed-off-by: Geert Uytterhoeven Signed-off-by: Thomas Gleixner Reviewed-by: Biju Das Link: https://patch.msgid.link/aceab3fbc307ef428dfd62d8d846b68704dea012.1775205874.git.geert+renesas@glider.be --- drivers/irqchip/irq-renesas-rzv2h.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-rzv2h.c b/drivers/irqchip/irq-renesas-rzv2h.c index 9461f19fc38c..c701e468261e 100644 --- a/drivers/irqchip/irq-renesas-rzv2h.c +++ b/drivers/irqchip/irq-renesas-rzv2h.c @@ -755,10 +755,6 @@ static int rzv2h_icu_setup_irqs(struct platform_device *pdev, struct irq_domain { const struct rzv2h_hw_info *hw_info = rzv2h_icu_data->info; bool irq_inject = IS_ENABLED(CONFIG_GENERIC_IRQ_INJECTION); - static const char * const rzv2h_swint_names[] = { - "int-ca55-0", "int-ca55-1", - "int-ca55-2", "int-ca55-3", - }; static const char *icu_err = "icu-error-ca55"; void __iomem *base = rzv2h_icu_data->base; struct device *dev = &pdev->dev; @@ -774,16 +770,14 @@ static int rzv2h_icu_setup_irqs(struct platform_device *pdev, struct irq_domain virq = irq_create_fwspec_mapping(&fwspec); if (!virq) { - return dev_err_probe(dev, -EINVAL, "failed to create IRQ mapping for %s\n", - rzv2h_swint_names[i]); + return dev_err_probe(dev, -EINVAL, + "failed to create int-ca55-%u IRQ mapping\n", i); } ret = devm_request_irq(dev, virq, rzv2h_icu_swint_irq, 0, dev_name(dev), (void *)(uintptr_t)i); - if (ret) { - return dev_err_probe(dev, ret, "Failed to request %s IRQ\n", - rzv2h_swint_names[i]); - } + if (ret) + return dev_err_probe(dev, ret, "Failed to request int-ca55-%u IRQ\n", i); } /* Unmask and clear all IP/CA55 error interrupts */ -- cgit v1.2.3 From 669d2067e3d095d525c69e675d8b9c738e3616c6 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 3 Apr 2026 10:48:23 +0200 Subject: irqchip/renesas-rzv2h: Kill icu_err string Replace the string variable icu_err by its expanded value where needed, to improve readability. This reduces generated code size by 16 bytes. Signed-off-by: Geert Uytterhoeven Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/c7472bec20dea2c4d63e390e8e293b7d7003ef39.1775205874.git.geert+renesas@glider.be --- drivers/irqchip/irq-renesas-rzv2h.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-rzv2h.c b/drivers/irqchip/irq-renesas-rzv2h.c index c701e468261e..31c543c876b1 100644 --- a/drivers/irqchip/irq-renesas-rzv2h.c +++ b/drivers/irqchip/irq-renesas-rzv2h.c @@ -755,7 +755,6 @@ static int rzv2h_icu_setup_irqs(struct platform_device *pdev, struct irq_domain { const struct rzv2h_hw_info *hw_info = rzv2h_icu_data->info; bool irq_inject = IS_ENABLED(CONFIG_GENERIC_IRQ_INJECTION); - static const char *icu_err = "icu-error-ca55"; void __iomem *base = rzv2h_icu_data->base; struct device *dev = &pdev->dev; struct irq_fwspec fwspec; @@ -800,14 +799,12 @@ static int rzv2h_icu_setup_irqs(struct platform_device *pdev, struct irq_domain fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH; virq = irq_create_fwspec_mapping(&fwspec); - if (!virq) { - return dev_err_probe(dev, -EINVAL, "failed to create IRQ mapping for %s\n", - icu_err); - } + if (!virq) + return dev_err_probe(dev, -EINVAL, "failed to create icu-error-ca55 IRQ mapping\n"); ret = devm_request_irq(dev, virq, rzv2h_icu_error_irq, 0, dev_name(dev), rzv2h_icu_data); if (ret) - return dev_err_probe(dev, ret, "Failed to request %s IRQ\n", icu_err); + return dev_err_probe(dev, ret, "Failed to request icu-error-ca55 IRQ\n"); return 0; } -- cgit v1.2.3 From 1fac04a0a4737c4da3d55d7708931166a4a7136a Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Fri, 3 Apr 2026 17:12:17 -0400 Subject: irqchip/irq-pic32-evic: Add __maybe_unused for board_bind_eic_interrupt in COMPILE_TEST There are a few ifdefs in this driver so that it can be compiled on all architectures when COMPILE_TEST is set. board_bind_eic_interrupt is defined in arch/mips/ for normal usage, however when this driver is compiled with COMPILE_TEST on other architectures, it is defined as a static variable inside this driver. This causes the following warning: drivers/irqchip/irq-pic32-evic.c:54:15: warning: variable 'board_bind_eic_interrupt' set but not used [-Wunused-but-set-global] 54 | static void (*board_bind_eic_interrupt)(int irq, int regset); | ^ Annotate the static variable with __maybe_unused to avoid having to put even more ifdefs into this driver. Fixes: 282f8b547d51d ("irqchip/irq-pic32-evic: Define board_bind_eic_interrupt for !MIPS builds") Reported-by: kernel test robot Signed-off-by: Brian Masney Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260403-irq-pic32-evic-unused-v1-1-447cdc0675ec@redhat.com Closes: https://lore.kernel.org/oe-kbuild-all/202603300715.4HuMMAFb-lkp@intel.com/ --- drivers/irqchip/irq-pic32-evic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-pic32-evic.c b/drivers/irqchip/irq-pic32-evic.c index ecea7cb3ba84..3c48288c9e6c 100644 --- a/drivers/irqchip/irq-pic32-evic.c +++ b/drivers/irqchip/irq-pic32-evic.c @@ -51,7 +51,7 @@ asmlinkage void __weak plat_irq_dispatch(void) do_domain_IRQ(evic_irq_domain, hwirq); } #else -static void (*board_bind_eic_interrupt)(int irq, int regset); +static __maybe_unused void (*board_bind_eic_interrupt)(int irq, int regset); #endif static struct evic_chip_data *irqd_to_priv(struct irq_data *data) -- cgit v1.2.3