From 8a7f030df89746842094334cdf55114d0fbb0234 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Tue, 9 Dec 2025 16:23:33 +0200 Subject: irqchip/aslint-sswi: Request IO memory resource Make an aclint_sswi instance visible in the resource list, i.e. /proc/iomem Signed-off-by: Vladimir Kondratiev Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20251209142336.1061606-1-vladimir.kondratiev@mobileye.com --- drivers/irqchip/irq-aclint-sswi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-aclint-sswi.c b/drivers/irqchip/irq-aclint-sswi.c index fee30f3bc5ac..325501f34a9b 100644 --- a/drivers/irqchip/irq-aclint-sswi.c +++ b/drivers/irqchip/irq-aclint-sswi.c @@ -109,7 +109,7 @@ static int __init aclint_sswi_probe(struct fwnode_handle *fwnode) if (!is_of_node(fwnode)) return -EINVAL; - reg = of_iomap(to_of_node(fwnode), 0); + reg = of_io_request_and_map(to_of_node(fwnode), 0, NULL); if (!reg) return -ENOMEM; -- cgit v1.2.3 From 8d4c354bef3cc438db93f362e4657b317db03392 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Thu, 27 Nov 2025 16:24:47 +0000 Subject: irqchip/renesas-rzv2h: Add support for RZ/V2N SoC Add support for the RZ/V2N Interrupt Control Unit (ICU) by introducing a dedicated compatible string in the irqchip driver. While the RZ/V2N ICU differs from the RZ/V2H(P) version in its register layout primarily due to a reduced set of ECCRAM related registers the irqchip driver does not currently access these registers. As a result, the RZ/V2N ICU can be safely handled by rzv2h_icu_probe for now, but it still requires a distinct compatible so that future changes can differentiate the SoCs when needed. [ tglx: Fixed up the made up subject prefix ] Signed-off-by: Lad Prabhakar Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20251127162447.320971-3-prabhakar.mahadev-lad.rj@bp.renesas.com --- drivers/irqchip/irq-renesas-rzv2h.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-rzv2h.c b/drivers/irqchip/irq-renesas-rzv2h.c index 899a423b5da8..0c44b6109842 100644 --- a/drivers/irqchip/irq-renesas-rzv2h.c +++ b/drivers/irqchip/irq-renesas-rzv2h.c @@ -616,6 +616,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,r9a09g057-icu", rzv2h_icu_probe) IRQCHIP_PLATFORM_DRIVER_END(rzv2h_icu) MODULE_AUTHOR("Fabrizio Castro "); -- cgit v1.2.3 From 13e7b3305b647cf58c47c979fe8a04e08caa6098 Mon Sep 17 00:00:00 2001 From: Cosmin Tanislav Date: Mon, 1 Dec 2025 13:29:31 +0200 Subject: irqchip: Add RZ/{T2H,N2H} Interrupt Controller (ICU) driver The Renesas RZ/T2H (R9A09G077) and Renesas RZ/N2H (R9A09G087) SoCs have an Interrupt Controller (ICU) that supports interrupts from external pins IRQ0 to IRQ15, and SEI, and software-triggered interrupts INTCPU0 to INTCPU15. INTCPU0 to INTCPU13, IRQ0 to IRQ13 are non-safety interrupts, while INTCPU14, INTCPU15, IRQ14, IRQ15 and SEI are safety interrupts, and are exposed via a separate register space. Signed-off-by: Cosmin Tanislav Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20251201112933.488801-3-cosmin-gabriel.tanislav.xa@renesas.com --- drivers/irqchip/Kconfig | 8 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-renesas-rzt2h.c | 280 ++++++++++++++++++++++++++++++ drivers/soc/renesas/Kconfig | 1 + include/linux/irqchip/irq-renesas-rzt2h.h | 23 +++ 5 files changed, 313 insertions(+) create mode 100644 drivers/irqchip/irq-renesas-rzt2h.c create mode 100644 include/linux/irqchip/irq-renesas-rzt2h.h (limited to 'drivers') diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index f334f49c9791..118d0c16e633 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -297,6 +297,14 @@ config RENESAS_RZG2L_IRQC Enable support for the Renesas RZ/G2L (and alike SoC) Interrupt Controller for external devices. +config RENESAS_RZT2H_ICU + bool "Renesas RZ/{T2H,N2H} ICU support" if COMPILE_TEST + select GENERIC_IRQ_CHIP + select IRQ_DOMAIN_HIERARCHY + help + Enable support for the Renesas RZ/{T2H,N2H} Interrupt Controller + (ICU). + config RENESAS_RZV2H_ICU bool "Renesas RZ/V2H(P) ICU support" if COMPILE_TEST select GENERIC_IRQ_CHIP diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 6a229443efe0..26aa3b6ec99f 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -54,6 +54,7 @@ obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o obj-$(CONFIG_RENESAS_RZA1_IRQC) += irq-renesas-rza1.o obj-$(CONFIG_RENESAS_RZG2L_IRQC) += irq-renesas-rzg2l.o +obj-$(CONFIG_RENESAS_RZT2H_ICU) += irq-renesas-rzt2h.o obj-$(CONFIG_RENESAS_RZV2H_ICU) += irq-renesas-rzv2h.o obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o obj-$(CONFIG_ARCH_NSPIRE) += irq-zevio.o diff --git a/drivers/irqchip/irq-renesas-rzt2h.c b/drivers/irqchip/irq-renesas-rzt2h.c new file mode 100644 index 000000000000..53cf80e1155a --- /dev/null +++ b/drivers/irqchip/irq-renesas-rzt2h.c @@ -0,0 +1,280 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RZT2H_ICU_INTCPU_NS_START 0 +#define RZT2H_ICU_INTCPU_NS_COUNT 14 + +#define RZT2H_ICU_INTCPU_S_START (RZT2H_ICU_INTCPU_NS_START + \ + RZT2H_ICU_INTCPU_NS_COUNT) +#define RZT2H_ICU_INTCPU_S_COUNT 2 + +#define RZT2H_ICU_IRQ_NS_START (RZT2H_ICU_INTCPU_S_START + \ + RZT2H_ICU_INTCPU_S_COUNT) +#define RZT2H_ICU_IRQ_NS_COUNT 14 + +#define RZT2H_ICU_IRQ_S_START (RZT2H_ICU_IRQ_NS_START + \ + RZT2H_ICU_IRQ_NS_COUNT) +#define RZT2H_ICU_IRQ_S_COUNT 2 + +#define RZT2H_ICU_SEI_START (RZT2H_ICU_IRQ_S_START + \ + RZT2H_ICU_IRQ_S_COUNT) +#define RZT2H_ICU_SEI_COUNT 1 + +#define RZT2H_ICU_NUM_IRQ (RZT2H_ICU_INTCPU_NS_COUNT + \ + RZT2H_ICU_INTCPU_S_COUNT + \ + RZT2H_ICU_IRQ_NS_COUNT + \ + RZT2H_ICU_IRQ_S_COUNT + \ + RZT2H_ICU_SEI_COUNT) + +#define RZT2H_ICU_IRQ_IN_RANGE(n, type) \ + ((n) >= RZT2H_ICU_##type##_START && \ + (n) < RZT2H_ICU_##type##_START + RZT2H_ICU_##type##_COUNT) + +#define RZT2H_ICU_PORTNF_MD 0xc +#define RZT2H_ICU_PORTNF_MDi_MASK(i) (GENMASK(1, 0) << ((i) * 2)) +#define RZT2H_ICU_PORTNF_MDi_PREP(i, val) (FIELD_PREP(GENMASK(1, 0), val) << ((i) * 2)) + +#define RZT2H_ICU_MD_LOW_LEVEL 0b00 +#define RZT2H_ICU_MD_FALLING_EDGE 0b01 +#define RZT2H_ICU_MD_RISING_EDGE 0b10 +#define RZT2H_ICU_MD_BOTH_EDGES 0b11 + +#define RZT2H_ICU_DMACn_RSSELi(n, i) (0x7d0 + 0x18 * (n) + 0x4 * (i)) +#define RZT2H_ICU_DMAC_REQ_SELx_MASK(x) (GENMASK(9, 0) << ((x) * 10)) +#define RZT2H_ICU_DMAC_REQ_SELx_PREP(x, val) (FIELD_PREP(GENMASK(9, 0), val) << ((x) * 10)) + +struct rzt2h_icu_priv { + void __iomem *base_ns; + void __iomem *base_s; + struct irq_fwspec fwspec[RZT2H_ICU_NUM_IRQ]; + raw_spinlock_t lock; +}; + +void rzt2h_icu_register_dma_req(struct platform_device *icu_dev, u8 dmac_index, u8 dmac_channel, + u16 req_no) +{ + struct rzt2h_icu_priv *priv = platform_get_drvdata(icu_dev); + u8 y, upper; + u32 val; + + y = dmac_channel / 3; + upper = dmac_channel % 3; + + guard(raw_spinlock_irqsave)(&priv->lock); + val = readl(priv->base_ns + RZT2H_ICU_DMACn_RSSELi(dmac_index, y)); + val &= ~RZT2H_ICU_DMAC_REQ_SELx_MASK(upper); + val |= RZT2H_ICU_DMAC_REQ_SELx_PREP(upper, req_no); + writel(val, priv->base_ns + RZT2H_ICU_DMACn_RSSELi(dmac_index, y)); +} +EXPORT_SYMBOL_GPL(rzt2h_icu_register_dma_req); + +static inline struct rzt2h_icu_priv *irq_data_to_priv(struct irq_data *data) +{ + return data->domain->host_data; +} + +static inline int rzt2h_icu_irq_to_offset(struct irq_data *d, void __iomem **base, + unsigned int *offset) +{ + struct rzt2h_icu_priv *priv = irq_data_to_priv(d); + unsigned int hwirq = irqd_to_hwirq(d); + + /* + * Safety IRQs and SEI use a separate register space from the non-safety IRQs. + * SEI interrupt number follows immediately after the safety IRQs. + */ + if (RZT2H_ICU_IRQ_IN_RANGE(hwirq, IRQ_NS)) { + *offset = hwirq - RZT2H_ICU_IRQ_NS_START; + *base = priv->base_ns; + } else if (RZT2H_ICU_IRQ_IN_RANGE(hwirq, IRQ_S) || RZT2H_ICU_IRQ_IN_RANGE(hwirq, SEI)) { + *offset = hwirq - RZT2H_ICU_IRQ_S_START; + *base = priv->base_s; + } else { + return -EINVAL; + } + return 0; +} + +static int rzt2h_icu_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct rzt2h_icu_priv *priv = irq_data_to_priv(d); + unsigned int offset, parent_type; + void __iomem *base; + u32 val, md; + int ret; + + ret = rzt2h_icu_irq_to_offset(d, &base, &offset); + if (ret) + return ret; + + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_LEVEL_LOW: + md = RZT2H_ICU_MD_LOW_LEVEL; + parent_type = IRQ_TYPE_LEVEL_HIGH; + break; + case IRQ_TYPE_EDGE_FALLING: + md = RZT2H_ICU_MD_FALLING_EDGE; + parent_type = IRQ_TYPE_EDGE_RISING; + break; + case IRQ_TYPE_EDGE_RISING: + md = RZT2H_ICU_MD_RISING_EDGE; + parent_type = IRQ_TYPE_EDGE_RISING; + break; + case IRQ_TYPE_EDGE_BOTH: + md = RZT2H_ICU_MD_BOTH_EDGES; + parent_type = IRQ_TYPE_EDGE_RISING; + break; + default: + return -EINVAL; + } + + scoped_guard(raw_spinlock, &priv->lock) { + val = readl_relaxed(base + RZT2H_ICU_PORTNF_MD); + val &= ~RZT2H_ICU_PORTNF_MDi_MASK(offset); + val |= RZT2H_ICU_PORTNF_MDi_PREP(offset, md); + writel_relaxed(val, base + RZT2H_ICU_PORTNF_MD); + } + + return irq_chip_set_type_parent(d, parent_type); +} + +static int rzt2h_icu_set_type(struct irq_data *d, unsigned int type) +{ + unsigned int hw_irq = irqd_to_hwirq(d); + + /* IRQn and SEI are selectable, others are edge-only. */ + if (RZT2H_ICU_IRQ_IN_RANGE(hw_irq, IRQ_NS) || + RZT2H_ICU_IRQ_IN_RANGE(hw_irq, IRQ_S) || + RZT2H_ICU_IRQ_IN_RANGE(hw_irq, SEI)) + return rzt2h_icu_irq_set_type(d, type); + + if ((type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_EDGE_RISING) + return -EINVAL; + + return irq_chip_set_type_parent(d, IRQ_TYPE_EDGE_RISING); +} + +static const struct irq_chip rzt2h_icu_chip = { + .name = "rzt2h-icu", + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_eoi = irq_chip_eoi_parent, + .irq_set_type = rzt2h_icu_set_type, + .irq_set_wake = irq_chip_set_wake_parent, + .irq_set_affinity = irq_chip_set_affinity_parent, + .irq_retrigger = irq_chip_retrigger_hierarchy, + .irq_get_irqchip_state = irq_chip_get_parent_state, + .irq_set_irqchip_state = irq_chip_set_parent_state, + .flags = IRQCHIP_MASK_ON_SUSPEND | + IRQCHIP_SET_TYPE_MASKED | + IRQCHIP_SKIP_SET_WAKE, +}; + +static int rzt2h_icu_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs, + void *arg) +{ + struct rzt2h_icu_priv *priv = domain->host_data; + irq_hw_number_t hwirq; + unsigned int type; + int ret; + + ret = irq_domain_translate_twocell(domain, arg, &hwirq, &type); + if (ret) + return ret; + + ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, &rzt2h_icu_chip, NULL); + if (ret) + return ret; + + return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &priv->fwspec[hwirq]); +} + +static const struct irq_domain_ops rzt2h_icu_domain_ops = { + .alloc = rzt2h_icu_alloc, + .free = irq_domain_free_irqs_common, + .translate = irq_domain_translate_twocell, +}; + +static int rzt2h_icu_parse_interrupts(struct rzt2h_icu_priv *priv, struct device_node *np) +{ + struct of_phandle_args map; + unsigned int i; + int ret; + + for (i = 0; i < RZT2H_ICU_NUM_IRQ; i++) { + ret = of_irq_parse_one(np, i, &map); + if (ret) + return ret; + + of_phandle_args_to_fwspec(np, map.args, map.args_count, &priv->fwspec[i]); + } + + return 0; +} + +static int rzt2h_icu_init(struct platform_device *pdev, struct device_node *parent) +{ + struct irq_domain *irq_domain, *parent_domain; + struct device_node *node = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct rzt2h_icu_priv *priv; + int ret; + + parent_domain = irq_find_host(parent); + if (!parent_domain) + return dev_err_probe(dev, -ENODEV, "cannot find parent domain\n"); + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + raw_spin_lock_init(&priv->lock); + + platform_set_drvdata(pdev, priv); + + priv->base_ns = devm_of_iomap(dev, dev->of_node, 0, NULL); + if (IS_ERR(priv->base_ns)) + return PTR_ERR(priv->base_ns); + + priv->base_s = devm_of_iomap(dev, dev->of_node, 1, NULL); + if (IS_ERR(priv->base_s)) + return PTR_ERR(priv->base_s); + + ret = rzt2h_icu_parse_interrupts(priv, node); + if (ret) + return dev_err_probe(dev, ret, "cannot parse interrupts: %d\n", ret); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return dev_err_probe(dev, ret, "devm_pm_runtime_enable failed: %d\n", ret); + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return dev_err_probe(dev, ret, "pm_runtime_resume_and_get failed: %d\n", ret); + + irq_domain = irq_domain_create_hierarchy(parent_domain, 0, RZT2H_ICU_NUM_IRQ, + dev_fwnode(dev), &rzt2h_icu_domain_ops, priv); + if (!irq_domain) { + pm_runtime_put(dev); + return -ENOMEM; + } + + return 0; +} + +IRQCHIP_PLATFORM_DRIVER_BEGIN(rzt2h_icu) +IRQCHIP_MATCH("renesas,r9a09g077-icu", rzt2h_icu_init) +IRQCHIP_PLATFORM_DRIVER_END(rzt2h_icu) +MODULE_AUTHOR("Cosmin Tanislav "); +MODULE_DESCRIPTION("Renesas RZ/T2H ICU Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 340a1ff7e92b..198baf890b14 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -423,6 +423,7 @@ config ARCH_R9A09G057 config ARCH_R9A09G077 bool "ARM64 Platform support for R9A09G077 (RZ/T2H)" default y if ARCH_RENESAS + select RENESAS_RZT2H_ICU help This enables support for the Renesas RZ/T2H SoC variants. diff --git a/include/linux/irqchip/irq-renesas-rzt2h.h b/include/linux/irqchip/irq-renesas-rzt2h.h new file mode 100644 index 000000000000..853fd5ee0b22 --- /dev/null +++ b/include/linux/irqchip/irq-renesas-rzt2h.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Renesas RZ/T2H Interrupt Control Unit (ICU) + * + * Copyright (C) 2025 Renesas Electronics Corporation. + */ + +#ifndef __LINUX_IRQ_RENESAS_RZT2H +#define __LINUX_IRQ_RENESAS_RZT2H + +#include + +#define RZT2H_ICU_DMAC_REQ_NO_DEFAULT 0x3ff + +#ifdef CONFIG_RENESAS_RZT2H_ICU +void rzt2h_icu_register_dma_req(struct platform_device *icu_dev, u8 dmac_index, u8 dmac_channel, + u16 req_no); +#else +static inline void rzt2h_icu_register_dma_req(struct platform_device *icu_dev, u8 dmac_index, + u8 dmac_channel, u16 req_no) { } +#endif + +#endif /* __LINUX_IRQ_RENESAS_RZT2H */ -- cgit v1.2.3 From f48b4bd0915bf61ac12b8c65c7939ebd03bc8abf Mon Sep 17 00:00:00 2001 From: Nick Hu Date: Tue, 2 Dec 2025 14:07:40 +0800 Subject: irqchip/riscv-imsic: Add a CPU pm notifier to restore the IMSIC on exit The IMSIC might be reset when the system enters a low power state, but on exit nothing restores the registers, which prevents interrupt delivery. Solve this by registering a CPU power management notifier, which restores the IMSIC on exit. Signed-off-by: Nick Hu Signed-off-by: Thomas Gleixner Reviewed-by: Yong-Xuan Wang Reviewed-by: Cyan Yang Reviewed-by: Anup Patel Reviewed-by: Nutty Liu Link: https://patch.msgid.link/20251202-preserve-aplic-imsic-v3-1-1844fbf1fe92@sifive.com --- drivers/irqchip/irq-riscv-imsic-early.c | 39 ++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-riscv-imsic-early.c b/drivers/irqchip/irq-riscv-imsic-early.c index 6bac67cc0b6d..ba903fa689bd 100644 --- a/drivers/irqchip/irq-riscv-imsic-early.c +++ b/drivers/irqchip/irq-riscv-imsic-early.c @@ -7,6 +7,7 @@ #define pr_fmt(fmt) "riscv-imsic: " fmt #include #include +#include #include #include #include @@ -123,14 +124,8 @@ static void imsic_handle_irq(struct irq_desc *desc) chained_irq_exit(chip, desc); } -static int imsic_starting_cpu(unsigned int cpu) +static void imsic_hw_states_init(void) { - /* Mark per-CPU IMSIC state as online */ - imsic_state_online(); - - /* Enable per-CPU parent interrupt */ - enable_percpu_irq(imsic_parent_irq, irq_get_trigger_type(imsic_parent_irq)); - /* Setup IPIs */ imsic_ipi_starting_cpu(); @@ -142,6 +137,18 @@ static int imsic_starting_cpu(unsigned int cpu) /* Enable local interrupt delivery */ imsic_local_delivery(true); +} + +static int imsic_starting_cpu(unsigned int cpu) +{ + /* Mark per-CPU IMSIC state as online */ + imsic_state_online(); + + /* Enable per-CPU parent interrupt */ + enable_percpu_irq(imsic_parent_irq, irq_get_trigger_type(imsic_parent_irq)); + + /* Initialize the IMSIC registers to enable the interrupt delivery */ + imsic_hw_states_init(); return 0; } @@ -157,6 +164,22 @@ static int imsic_dying_cpu(unsigned int cpu) return 0; } +static int imsic_pm_notifier(struct notifier_block *self, unsigned long cmd, void *v) +{ + switch (cmd) { + case CPU_PM_EXIT: + /* Initialize the IMSIC registers to enable the interrupt delivery */ + imsic_hw_states_init(); + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block imsic_pm_notifier_block = { + .notifier_call = imsic_pm_notifier, +}; + static int __init imsic_early_probe(struct fwnode_handle *fwnode) { struct irq_domain *domain; @@ -194,7 +217,7 @@ static int __init imsic_early_probe(struct fwnode_handle *fwnode) cpuhp_setup_state(CPUHP_AP_IRQ_RISCV_IMSIC_STARTING, "irqchip/riscv/imsic:starting", imsic_starting_cpu, imsic_dying_cpu); - return 0; + return cpu_pm_register_notifier(&imsic_pm_notifier_block); } static int __init imsic_early_dt_init(struct device_node *node, struct device_node *parent) -- cgit v1.2.3 From 95a8ddde36601d0a645475fb080ed118db59c8c3 Mon Sep 17 00:00:00 2001 From: Nick Hu Date: Tue, 2 Dec 2025 14:07:41 +0800 Subject: irqchip/riscv-aplic: Preserve APLIC states across suspend/resume The APLIC states might be reset when the platform enters a low power state, but the register states are not being preserved and restored, which prevents interrupt delivery after the platform resumes. Solve this by adding a syscore ops and a power management notifier to preserve and restore the APLIC states on suspend and resume. [ tglx: Folded the build fix provided by Geert ] Signed-off-by: Nick Hu Signed-off-by: Thomas Gleixner Reviewed-by: Yong-Xuan Wang Reviewed-by: Cyan Yang Reviewed-by: Nutty Liu Reviewed-by: Anup Patel Link: https://patch.msgid.link/20251202-preserve-aplic-imsic-v3-2-1844fbf1fe92@sifive.com --- drivers/irqchip/irq-riscv-aplic-direct.c | 10 ++ drivers/irqchip/irq-riscv-aplic-main.c | 170 ++++++++++++++++++++++++++++++- drivers/irqchip/irq-riscv-aplic-main.h | 19 ++++ 3 files changed, 198 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-riscv-aplic-direct.c b/drivers/irqchip/irq-riscv-aplic-direct.c index c2a75bf3d20c..5a9650225dd8 100644 --- a/drivers/irqchip/irq-riscv-aplic-direct.c +++ b/drivers/irqchip/irq-riscv-aplic-direct.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -171,6 +172,15 @@ static void aplic_idc_set_delivery(struct aplic_idc *idc, bool en) writel(de, idc->regs + APLIC_IDC_IDELIVERY); } +void aplic_direct_restore_states(struct aplic_priv *priv) +{ + struct aplic_direct *direct = container_of(priv, struct aplic_direct, priv); + int cpu; + + for_each_cpu(cpu, &direct->lmask) + aplic_idc_set_delivery(per_cpu_ptr(&aplic_idcs, cpu), true); +} + static int aplic_direct_dying_cpu(unsigned int cpu) { if (aplic_direct_parent_irq) diff --git a/drivers/irqchip/irq-riscv-aplic-main.c b/drivers/irqchip/irq-riscv-aplic-main.c index 93e7c51f944a..4495ca26abf5 100644 --- a/drivers/irqchip/irq-riscv-aplic-main.c +++ b/drivers/irqchip/irq-riscv-aplic-main.c @@ -12,10 +12,169 @@ #include #include #include +#include +#include #include +#include #include "irq-riscv-aplic-main.h" +static LIST_HEAD(aplics); + +static void aplic_restore_states(struct aplic_priv *priv) +{ + struct aplic_saved_regs *saved_regs = &priv->saved_hw_regs; + struct aplic_src_ctrl *srcs; + void __iomem *regs; + u32 nr_irqs, i; + + regs = priv->regs; + writel(saved_regs->domaincfg, regs + APLIC_DOMAINCFG); +#ifdef CONFIG_RISCV_M_MODE + writel(saved_regs->msiaddr, regs + APLIC_xMSICFGADDR); + writel(saved_regs->msiaddrh, regs + APLIC_xMSICFGADDRH); +#endif + /* + * The sourcecfg[i] has to be restored prior to the target[i], interrupt-pending and + * interrupt-enable bits. The AIA specification states that "Whenever interrupt source i is + * inactive in an interrupt domain, the corresponding interrupt-pending and interrupt-enable + * bits within the domain are read-only zeros, and register target[i] is also read-only + * zero." + */ + nr_irqs = priv->nr_irqs; + for (i = 0; i < nr_irqs; i++) { + srcs = &priv->saved_hw_regs.srcs[i]; + writel(srcs->sourcecfg, regs + APLIC_SOURCECFG_BASE + i * sizeof(u32)); + writel(srcs->target, regs + APLIC_TARGET_BASE + i * sizeof(u32)); + } + + for (i = 0; i <= nr_irqs; i += 32) { + srcs = &priv->saved_hw_regs.srcs[i]; + writel(-1U, regs + APLIC_CLRIE_BASE + (i / 32) * sizeof(u32)); + writel(srcs->ie, regs + APLIC_SETIE_BASE + (i / 32) * sizeof(u32)); + + /* Re-trigger the interrupts if it forwards interrupts to target harts by MSIs */ + if (!priv->nr_idcs) + writel(readl(regs + APLIC_CLRIP_BASE + (i / 32) * sizeof(u32)), + regs + APLIC_SETIP_BASE + (i / 32) * sizeof(u32)); + } + + if (priv->nr_idcs) + aplic_direct_restore_states(priv); +} + +static void aplic_save_states(struct aplic_priv *priv) +{ + struct aplic_src_ctrl *srcs; + void __iomem *regs; + u32 i, nr_irqs; + + regs = priv->regs; + nr_irqs = priv->nr_irqs; + /* The valid interrupt source IDs range from 1 to N, where N is priv->nr_irqs */ + for (i = 0; i < nr_irqs; i++) { + srcs = &priv->saved_hw_regs.srcs[i]; + srcs->target = readl(regs + APLIC_TARGET_BASE + i * sizeof(u32)); + + if (i % 32) + continue; + + srcs->ie = readl(regs + APLIC_SETIE_BASE + (i / 32) * sizeof(u32)); + } + + /* Save the nr_irqs bit if needed */ + if (!(nr_irqs % 32)) { + srcs = &priv->saved_hw_regs.srcs[nr_irqs]; + srcs->ie = readl(regs + APLIC_SETIE_BASE + (nr_irqs / 32) * sizeof(u32)); + } +} + +static int aplic_syscore_suspend(void *data) +{ + struct aplic_priv *priv; + + list_for_each_entry(priv, &aplics, head) + aplic_save_states(priv); + + return 0; +} + +static void aplic_syscore_resume(void *data) +{ + struct aplic_priv *priv; + + list_for_each_entry(priv, &aplics, head) + aplic_restore_states(priv); +} + +static struct syscore_ops aplic_syscore_ops = { + .suspend = aplic_syscore_suspend, + .resume = aplic_syscore_resume, +}; + +static struct syscore aplic_syscore = { + .ops = &aplic_syscore_ops, +}; + +static int aplic_pm_notifier(struct notifier_block *nb, unsigned long action, void *data) +{ + struct aplic_priv *priv = container_of(nb, struct aplic_priv, genpd_nb); + + switch (action) { + case GENPD_NOTIFY_PRE_OFF: + aplic_save_states(priv); + break; + case GENPD_NOTIFY_ON: + aplic_restore_states(priv); + break; + default: + break; + } + + return 0; +} + +static void aplic_pm_remove(void *data) +{ + struct aplic_priv *priv = data; + struct device *dev = priv->dev; + + list_del(&priv->head); + if (dev->pm_domain) + dev_pm_genpd_remove_notifier(dev); +} + +static int aplic_pm_add(struct device *dev, struct aplic_priv *priv) +{ + struct aplic_src_ctrl *srcs; + int ret; + + srcs = devm_kzalloc(dev, (priv->nr_irqs + 1) * sizeof(*srcs), GFP_KERNEL); + if (!srcs) + return -ENOMEM; + + priv->saved_hw_regs.srcs = srcs; + list_add(&priv->head, &aplics); + if (dev->pm_domain) { + priv->genpd_nb.notifier_call = aplic_pm_notifier; + ret = dev_pm_genpd_add_notifier(dev, &priv->genpd_nb); + if (ret) + goto remove_head; + + ret = devm_pm_runtime_enable(dev); + if (ret) + goto remove_notifier; + } + + return devm_add_action_or_reset(dev, aplic_pm_remove, priv); + +remove_notifier: + dev_pm_genpd_remove_notifier(dev); +remove_head: + list_del(&priv->head); + return ret; +} + void aplic_irq_unmask(struct irq_data *d) { struct aplic_priv *priv = irq_data_get_irq_chip_data(d); @@ -60,6 +219,8 @@ int aplic_irq_set_type(struct irq_data *d, unsigned int type) sourcecfg += (d->hwirq - 1) * sizeof(u32); writel(val, sourcecfg); + priv->saved_hw_regs.srcs[d->hwirq - 1].sourcecfg = val; + return 0; } @@ -82,6 +243,7 @@ int aplic_irqdomain_translate(struct irq_fwspec *fwspec, u32 gsi_base, void aplic_init_hw_global(struct aplic_priv *priv, bool msi_mode) { + struct aplic_saved_regs *saved_regs = &priv->saved_hw_regs; u32 val; #ifdef CONFIG_RISCV_M_MODE u32 valh; @@ -95,6 +257,8 @@ void aplic_init_hw_global(struct aplic_priv *priv, bool msi_mode) valh |= FIELD_PREP(APLIC_xMSICFGADDRH_HHXS, priv->msicfg.hhxs); writel(val, priv->regs + APLIC_xMSICFGADDR); writel(valh, priv->regs + APLIC_xMSICFGADDRH); + saved_regs->msiaddr = val; + saved_regs->msiaddrh = valh; } #endif @@ -106,6 +270,8 @@ void aplic_init_hw_global(struct aplic_priv *priv, bool msi_mode) writel(val, priv->regs + APLIC_DOMAINCFG); if (readl(priv->regs + APLIC_DOMAINCFG) != val) dev_warn(priv->dev, "unable to write 0x%x in domaincfg\n", val); + + saved_regs->domaincfg = val; } static void aplic_init_hw_irqs(struct aplic_priv *priv) @@ -176,7 +342,7 @@ int aplic_setup_priv(struct aplic_priv *priv, struct device *dev, void __iomem * /* Setup initial state APLIC interrupts */ aplic_init_hw_irqs(priv); - return 0; + return aplic_pm_add(dev, priv); } static int aplic_probe(struct platform_device *pdev) @@ -209,6 +375,8 @@ static int aplic_probe(struct platform_device *pdev) if (rc) dev_err_probe(dev, rc, "failed to setup APLIC in %s mode\n", msi_mode ? "MSI" : "direct"); + else + register_syscore(&aplic_syscore); #ifdef CONFIG_ACPI if (!acpi_disabled) diff --git a/drivers/irqchip/irq-riscv-aplic-main.h b/drivers/irqchip/irq-riscv-aplic-main.h index b0ad8cde69b1..2d8ad7138541 100644 --- a/drivers/irqchip/irq-riscv-aplic-main.h +++ b/drivers/irqchip/irq-riscv-aplic-main.h @@ -23,7 +23,25 @@ struct aplic_msicfg { u32 lhxw; }; +struct aplic_src_ctrl { + u32 sourcecfg; + u32 target; + u32 ie; +}; + +struct aplic_saved_regs { + u32 domaincfg; +#ifdef CONFIG_RISCV_M_MODE + u32 msiaddr; + u32 msiaddrh; +#endif + struct aplic_src_ctrl *srcs; +}; + struct aplic_priv { + struct list_head head; + struct notifier_block genpd_nb; + struct aplic_saved_regs saved_hw_regs; struct device *dev; u32 gsi_base; u32 nr_irqs; @@ -40,6 +58,7 @@ int aplic_irqdomain_translate(struct irq_fwspec *fwspec, u32 gsi_base, unsigned long *hwirq, unsigned int *type); void aplic_init_hw_global(struct aplic_priv *priv, bool msi_mode); int aplic_setup_priv(struct aplic_priv *priv, struct device *dev, void __iomem *regs); +void aplic_direct_restore_states(struct aplic_priv *priv); int aplic_direct_setup(struct device *dev, void __iomem *regs); #ifdef CONFIG_RISCV_APLIC_MSI int aplic_msi_setup(struct device *dev, void __iomem *regs); -- cgit v1.2.3 From d9e7035a51b89ef6041ce7c00b629e7877134a51 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Tue, 13 Jan 2026 16:59:34 +0800 Subject: irqchip/loongarch-avec: Adjust irqchip driver for 32BIT/64BIT csr_read64() is only available on 64BIT LoongArch platform, so use the recently added adaptive csr_read() instead to make the driver work on both 32BIT and 64BIT platforms. This makes avecintc_enable() a no-op for 32-bit as it is only required on 64-bit systems. Co-developed-by: Jiaxun Yang Signed-off-by: Jiaxun Yang Signed-off-by: Huacai Chen Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260113085940.3344837-2-chenhuacai@loongson.cn --- drivers/irqchip/irq-loongarch-avec.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-loongarch-avec.c b/drivers/irqchip/irq-loongarch-avec.c index ba556c008cf3..fb8efde95393 100644 --- a/drivers/irqchip/irq-loongarch-avec.c +++ b/drivers/irqchip/irq-loongarch-avec.c @@ -58,11 +58,13 @@ struct avecintc_data { static inline void avecintc_enable(void) { +#ifdef CONFIG_MACH_LOONGSON64 u64 value; value = iocsr_read64(LOONGARCH_IOCSR_MISC_FUNC); value |= IOCSR_MISC_FUNC_AVEC_EN; iocsr_write64(value, LOONGARCH_IOCSR_MISC_FUNC); +#endif } static inline void avecintc_ack_irq(struct irq_data *d) @@ -167,7 +169,7 @@ void complete_irq_moving(void) struct pending_list *plist = this_cpu_ptr(&pending_list); struct avecintc_data *adata, *tdata; int cpu, vector, bias; - uint64_t isr; + unsigned long isr; guard(raw_spinlock)(&loongarch_avec.lock); @@ -177,16 +179,16 @@ void complete_irq_moving(void) bias = vector / VECTORS_PER_REG; switch (bias) { case 0: - isr = csr_read64(LOONGARCH_CSR_ISR0); + isr = csr_read(LOONGARCH_CSR_ISR0); break; case 1: - isr = csr_read64(LOONGARCH_CSR_ISR1); + isr = csr_read(LOONGARCH_CSR_ISR1); break; case 2: - isr = csr_read64(LOONGARCH_CSR_ISR2); + isr = csr_read(LOONGARCH_CSR_ISR2); break; case 3: - isr = csr_read64(LOONGARCH_CSR_ISR3); + isr = csr_read(LOONGARCH_CSR_ISR3); break; } @@ -234,7 +236,7 @@ static void avecintc_irq_dispatch(struct irq_desc *desc) chained_irq_enter(chip, desc); while (true) { - unsigned long vector = csr_read64(LOONGARCH_CSR_IRR); + unsigned long vector = csr_read(LOONGARCH_CSR_IRR); if (vector & IRR_INVALID_MASK) break; -- cgit v1.2.3 From 57e05137ac3b37fd9b7b8714839d25b924073aef Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Tue, 13 Jan 2026 16:59:35 +0800 Subject: irqchip/loongson-liointc: Adjust irqchip driver for 32BIT/64BIT irq_domain_alloc_fwnode() takes a parameter with the phys_addr_t type. Currently the code passes acpi_liointc->address to it. This can only work on 64BIT platforms because its type is u64, so cast it to phys_addr_t and then the driver works on both 32BIT and 64BIT platform. [ tglx: Make the cast explicit and use the casted address as argument for liointc_init() which takes a phys_addr_t as well. Sigh... ] Co-developed-by: Jiaxun Yang Signed-off-by: Jiaxun Yang Signed-off-by: Huacai Chen Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260113085940.3344837-3-chenhuacai@loongson.cn --- drivers/irqchip/irq-loongson-liointc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-loongson-liointc.c b/drivers/irqchip/irq-loongson-liointc.c index 0033c2188abc..551597e2c428 100644 --- a/drivers/irqchip/irq-loongson-liointc.c +++ b/drivers/irqchip/irq-loongson-liointc.c @@ -394,8 +394,9 @@ static int __init acpi_cascade_irqdomain_init(void) int __init liointc_acpi_init(struct irq_domain *parent, struct acpi_madt_lio_pic *acpi_liointc) { - int ret; + phys_addr_t addr = (phys_addr_t)acpi_liointc->address; struct fwnode_handle *domain_handle; + int ret; parent_int_map[0] = acpi_liointc->cascade_map[0]; parent_int_map[1] = acpi_liointc->cascade_map[1]; @@ -403,14 +404,13 @@ int __init liointc_acpi_init(struct irq_domain *parent, struct acpi_madt_lio_pic parent_irq[0] = irq_create_mapping(parent, acpi_liointc->cascade[0]); parent_irq[1] = irq_create_mapping(parent, acpi_liointc->cascade[1]); - domain_handle = irq_domain_alloc_fwnode(&acpi_liointc->address); + domain_handle = irq_domain_alloc_fwnode(&addr); if (!domain_handle) { pr_err("Unable to allocate domain handle\n"); return -ENOMEM; } - ret = liointc_init(acpi_liointc->address, acpi_liointc->size, - 1, domain_handle, NULL); + ret = liointc_init(addr, acpi_liointc->size, 1, domain_handle, NULL); if (ret == 0) ret = acpi_cascade_irqdomain_init(); else -- cgit v1.2.3 From 61fb5e517ec457c76211f03ab0b379882248706d Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Tue, 13 Jan 2026 16:59:36 +0800 Subject: irqchip/loongson-eiointc: Adjust irqchip driver for 32BIT/64BIT iocsr_read64()/iocsr_write64() are only available on 64BIT LoongArch platform, so add and use a pair of helpers, i.e. read_isr()/write_isr() instead to make the driver work on both 32BIT and 64BIT platforms. This makes eoiintc_enable() a no-op for 32-bit as it is only required on 64-bit systems. [ tglx: Make the helpers inline and fixup the variable declaration order ] Co-developed-by: Jiaxun Yang Signed-off-by: Jiaxun Yang Signed-off-by: Huacai Chen Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260113085940.3344837-4-chenhuacai@loongson.cn --- drivers/irqchip/irq-loongson-eiointc.c | 36 ++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-loongson-eiointc.c b/drivers/irqchip/irq-loongson-eiointc.c index ad2105685b48..37e7e1f9bbe3 100644 --- a/drivers/irqchip/irq-loongson-eiointc.c +++ b/drivers/irqchip/irq-loongson-eiointc.c @@ -37,9 +37,9 @@ #define EXTIOI_ENABLE_INT_ENCODE BIT(2) #define EXTIOI_ENABLE_CPU_ENCODE BIT(3) -#define VEC_REG_COUNT 4 -#define VEC_COUNT_PER_REG 64 -#define VEC_COUNT (VEC_REG_COUNT * VEC_COUNT_PER_REG) +#define VEC_COUNT 256 +#define VEC_COUNT_PER_REG BITS_PER_LONG +#define VEC_REG_COUNT (VEC_COUNT / BITS_PER_LONG) #define VEC_REG_IDX(irq_id) ((irq_id) / VEC_COUNT_PER_REG) #define VEC_REG_BIT(irq_id) ((irq_id) % VEC_COUNT_PER_REG) #define EIOINTC_ALL_ENABLE 0xffffffff @@ -85,11 +85,13 @@ static struct eiointc_priv *eiointc_priv[MAX_IO_PICS]; static void eiointc_enable(void) { +#ifdef CONFIG_MACH_LOONGSON64 uint64_t misc; misc = iocsr_read64(LOONGARCH_IOCSR_MISC_FUNC); misc |= IOCSR_MISC_FUNC_EXT_IOI_EN; iocsr_write64(misc, LOONGARCH_IOCSR_MISC_FUNC); +#endif } static int cpu_to_eio_node(int cpu) @@ -281,12 +283,34 @@ static int eiointc_router_init(unsigned int cpu) return 0; } +#if VEC_COUNT_PER_REG == 32 +static inline unsigned long read_isr(int i) +{ + return iocsr_read32(EIOINTC_REG_ISR + (i << 2)); +} + +static inline void write_isr(int i, unsigned long val) +{ + iocsr_write32(val, EIOINTC_REG_ISR + (i << 2)); +} +#else +static inline unsigned long read_isr(int i) +{ + return iocsr_read64(EIOINTC_REG_ISR + (i << 3)); +} + +static inline void write_isr(int i, unsigned long val) +{ + iocsr_write64(val, EIOINTC_REG_ISR + (i << 3)); +} +#endif + static void eiointc_irq_dispatch(struct irq_desc *desc) { struct eiointc_ip_route *info = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); + unsigned long pending; bool handled = false; - u64 pending; int i; chained_irq_enter(chip, desc); @@ -299,14 +323,14 @@ static void eiointc_irq_dispatch(struct irq_desc *desc) * read ISR for these 64 interrupt vectors rather than all vectors */ for (i = info->start; i < info->end; i++) { - pending = iocsr_read64(EIOINTC_REG_ISR + (i << 3)); + pending = read_isr(i); /* Skip handling if pending bitmap is zero */ if (!pending) continue; /* Clear the IRQs */ - iocsr_write64(pending, EIOINTC_REG_ISR + (i << 3)); + write_isr(i, pending); while (pending) { int bit = __ffs(pending); int irq = bit + VEC_COUNT_PER_REG * i; -- cgit v1.2.3 From 04f1f17d28ce24a7b40039c8d8ee053a777661a7 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Tue, 13 Jan 2026 16:59:37 +0800 Subject: irqchip/loongson-htvec: Adjust irqchip driver for 32BIT/64BIT irq_domain_alloc_fwnode() takes a parameter with the phys_addr_t type. Currently the code passes acpi_htvec->address to it. This can only work on 64BIT platform because its type is u64, so cast it to phys_addr_t and then the driver works on both 32BIT and 64BIT platforms. [ tglx: Dereference _after_ the NULL pointer check, make the cast explicit and use the casted address as argument for htvec_init() which takes a phys_addr_t as well. Sigh... ] Co-developed-by: Jiaxun Yang Signed-off-by: Jiaxun Yang Signed-off-by: Huacai Chen Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260113085940.3344837-5-chenhuacai@loongson.cn --- drivers/irqchip/irq-loongson-htvec.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-loongson-htvec.c b/drivers/irqchip/irq-loongson-htvec.c index d2be8e954e92..5a3339da97ad 100644 --- a/drivers/irqchip/irq-loongson-htvec.c +++ b/drivers/irqchip/irq-loongson-htvec.c @@ -295,19 +295,19 @@ static int __init acpi_cascade_irqdomain_init(void) return 0; } -int __init htvec_acpi_init(struct irq_domain *parent, - struct acpi_madt_ht_pic *acpi_htvec) +int __init htvec_acpi_init(struct irq_domain *parent, struct acpi_madt_ht_pic *acpi_htvec) { - int i, ret; - int num_parents, parent_irq[8]; + int i, ret, num_parents, parent_irq[8]; struct fwnode_handle *domain_handle; + phys_addr_t addr; if (!acpi_htvec) return -EINVAL; num_parents = HTVEC_MAX_PARENT_IRQ; + addr = (phys_addr_t)acpi_htvec->address; - domain_handle = irq_domain_alloc_fwnode(&acpi_htvec->address); + domain_handle = irq_domain_alloc_fwnode(&addr); if (!domain_handle) { pr_err("Unable to allocate domain handle\n"); return -ENOMEM; @@ -317,9 +317,7 @@ int __init htvec_acpi_init(struct irq_domain *parent, for (i = 0; i < HTVEC_MAX_PARENT_IRQ; i++) parent_irq[i] = irq_create_mapping(parent, acpi_htvec->cascade[i]); - ret = htvec_init(acpi_htvec->address, acpi_htvec->size, - num_parents, parent_irq, domain_handle); - + ret = htvec_init(addr, acpi_htvec->size, num_parents, parent_irq, domain_handle); if (ret == 0) ret = acpi_cascade_irqdomain_init(); else -- cgit v1.2.3 From 4093b0e55b39422bbdae108a1be06292714a994d Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Tue, 13 Jan 2026 16:59:38 +0800 Subject: irqchip/loongson-pch-msi: Adjust irqchip driver for 32BIT/64BIT irq_domain_alloc_fwnode() takes a parameter with the phys_addr_t type. Currently the code passe acpi_pchmsi->msg_address to it. This can only work on 64BIT platform because its type is u64, so cast it to phys_addr_t and then the driver works on both 32BIT and 64BIT platform. [ tglx: Make the cast explicit and fixup coding style. ] Co-developed-by: Jiaxun Yang Signed-off-by: Jiaxun Yang Signed-off-by: Huacai Chen Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260113085940.3344837-6-chenhuacai@loongson.cn --- drivers/irqchip/irq-loongson-pch-msi.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-loongson-pch-msi.c b/drivers/irqchip/irq-loongson-pch-msi.c index 4aedc9b90ff7..91c856c65d9d 100644 --- a/drivers/irqchip/irq-loongson-pch-msi.c +++ b/drivers/irqchip/irq-loongson-pch-msi.c @@ -263,12 +263,13 @@ struct fwnode_handle *get_pch_msi_handle(int pci_segment) int __init pch_msi_acpi_init(struct irq_domain *parent, struct acpi_madt_msi_pic *acpi_pchmsi) { - int ret; + phys_addr_t msg_address = (phys_addr_t)acpi_pchmsi->msg_address; struct fwnode_handle *domain_handle; + int ret; - domain_handle = irq_domain_alloc_fwnode(&acpi_pchmsi->msg_address); - ret = pch_msi_init(acpi_pchmsi->msg_address, acpi_pchmsi->start, - acpi_pchmsi->count, parent, domain_handle); + domain_handle = irq_domain_alloc_fwnode(&msg_address); + ret = pch_msi_init(msg_address, acpi_pchmsi->start, acpi_pchmsi->count, + parent, domain_handle); if (ret < 0) irq_domain_free_fwnode(domain_handle); -- cgit v1.2.3 From 0370a5e740f2a078ac3cd3e20dae2dc95c6b92f3 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Tue, 13 Jan 2026 16:59:39 +0800 Subject: irqchip/loongson-pch-pic: Adjust irqchip driver for 32BIT/64BIT irq_domain_alloc_fwnode() takes a parameter with the phys_addr_t type. Currently we pass acpi_pchpic->address to it. This can only work on 64BIT platform because its type is u64, so cast it to phys_addr_t and then the driver works on both 32BIT and 64BIT platforms. Also use readl() to read vec_count because readq() is only available on 64BIT platform. [ tglx: Make the cast explicit and use the casted address as argument for pch_pic_init() which takes a phys_addr_t as well. Fixup coding style. More sigh... ] Co-developed-by: Jiaxun Yang Signed-off-by: Jiaxun Yang Signed-off-by: Huacai Chen Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260113085940.3344837-7-chenhuacai@loongson.cn --- drivers/irqchip/irq-loongson-pch-pic.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-loongson-pch-pic.c b/drivers/irqchip/irq-loongson-pch-pic.c index c6b369a974a7..f2acaf93f552 100644 --- a/drivers/irqchip/irq-loongson-pch-pic.c +++ b/drivers/irqchip/irq-loongson-pch-pic.c @@ -343,7 +343,7 @@ static int pch_pic_init(phys_addr_t addr, unsigned long size, int vec_base, priv->table[i] = PIC_UNDEF_VECTOR; priv->ht_vec_base = vec_base; - priv->vec_count = ((readq(priv->base) >> 48) & 0xff) + 1; + priv->vec_count = ((readl(priv->base + 4) >> 16) & 0xff) + 1; priv->gsi_base = gsi_base; priv->pic_domain = irq_domain_create_hierarchy(parent_domain, 0, @@ -446,23 +446,23 @@ static int __init acpi_cascade_irqdomain_init(void) return 0; } -int __init pch_pic_acpi_init(struct irq_domain *parent, - struct acpi_madt_bio_pic *acpi_pchpic) +int __init pch_pic_acpi_init(struct irq_domain *parent, struct acpi_madt_bio_pic *acpi_pchpic) { - int ret; + phys_addr_t addr = (phys_addr_t)acpi_pchpic->address; struct fwnode_handle *domain_handle; + int ret; if (find_pch_pic(acpi_pchpic->gsi_base) >= 0) return 0; - domain_handle = irq_domain_alloc_fwnode(&acpi_pchpic->address); + domain_handle = irq_domain_alloc_fwnode(&addr); if (!domain_handle) { pr_err("Unable to allocate domain handle\n"); return -ENOMEM; } - ret = pch_pic_init(acpi_pchpic->address, acpi_pchpic->size, - 0, parent, domain_handle, acpi_pchpic->gsi_base); + ret = pch_pic_init(addr, acpi_pchpic->size, 0, parent, + domain_handle, acpi_pchpic->gsi_base); if (ret < 0) { irq_domain_free_fwnode(domain_handle); -- cgit v1.2.3 From a34d398c83a4a4bc00513c00f6eecc34267f834f Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Tue, 13 Jan 2026 16:59:40 +0800 Subject: irqchip: Allow LoongArch irqchip drivers on both 32BIT/64BIT All LoongArch irqchip drivers are adjusted, allow them to be built on both 32BIT and 64BIT platforms. Co-developed-by: Jiaxun Yang Signed-off-by: Jiaxun Yang Signed-off-by: Huacai Chen Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260113085940.3344837-8-chenhuacai@loongson.cn --- drivers/irqchip/Kconfig | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 118d0c16e633..f07b00d7fef9 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -706,7 +706,7 @@ config IRQ_LOONGARCH_CPU config LOONGSON_LIOINTC bool "Loongson Local I/O Interrupt Controller" - depends on MACH_LOONGSON64 + depends on MACH_LOONGSON64 || LOONGARCH default y select IRQ_DOMAIN select GENERIC_IRQ_CHIP @@ -716,7 +716,6 @@ config LOONGSON_LIOINTC config LOONGSON_EIOINTC bool "Loongson Extend I/O Interrupt Controller" depends on LOONGARCH - depends on MACH_LOONGSON64 default MACH_LOONGSON64 select IRQ_DOMAIN_HIERARCHY select GENERIC_IRQ_CHIP @@ -734,7 +733,7 @@ config LOONGSON_HTPIC config LOONGSON_HTVEC bool "Loongson HyperTransport Interrupt Vector Controller" - depends on MACH_LOONGSON64 + depends on MACH_LOONGSON64 || LOONGARCH default MACH_LOONGSON64 select IRQ_DOMAIN_HIERARCHY help @@ -742,7 +741,7 @@ config LOONGSON_HTVEC config LOONGSON_PCH_PIC bool "Loongson PCH PIC Controller" - depends on MACH_LOONGSON64 + depends on MACH_LOONGSON64 || LOONGARCH default MACH_LOONGSON64 select IRQ_DOMAIN_HIERARCHY select IRQ_FASTEOI_HIERARCHY_HANDLERS @@ -751,7 +750,7 @@ config LOONGSON_PCH_PIC config LOONGSON_PCH_MSI bool "Loongson PCH MSI Controller" - depends on MACH_LOONGSON64 + depends on MACH_LOONGSON64 || LOONGARCH depends on PCI default MACH_LOONGSON64 select IRQ_DOMAIN_HIERARCHY @@ -763,7 +762,7 @@ config LOONGSON_PCH_MSI config LOONGSON_PCH_LPC bool "Loongson PCH LPC Controller" depends on LOONGARCH - depends on MACH_LOONGSON64 + depends on MACH_LOONGSON64 || LOONGARCH default MACH_LOONGSON64 select IRQ_DOMAIN_HIERARCHY help -- cgit v1.2.3 From a384f2ed886d4417d50fdad78aaf1ccf870d62e6 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Sun, 18 Jan 2026 10:28:43 +0200 Subject: irqchip/aslint-sswi: Fix error check of of_io_request_and_map() result of_io_request_and_map() returns IOMEM_ERR_PTR() on failure which is non-NULL. Fixes: 8a7f030df897 ("irqchip/aslint-sswi: Request IO memory resource") Reported-by: Chris Mason Signed-off-by: Vladimir Kondratiev Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260118082843.2786630-1-vladimir.kondratiev@mobileye.com Closes: https://lore.kernel.org/all/20260116124257.78357-1-clm@meta.com --- drivers/irqchip/irq-aclint-sswi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-aclint-sswi.c b/drivers/irqchip/irq-aclint-sswi.c index 325501f34a9b..ca06efd86fa1 100644 --- a/drivers/irqchip/irq-aclint-sswi.c +++ b/drivers/irqchip/irq-aclint-sswi.c @@ -110,8 +110,10 @@ static int __init aclint_sswi_probe(struct fwnode_handle *fwnode) return -EINVAL; reg = of_io_request_and_map(to_of_node(fwnode), 0, NULL); - if (!reg) - return -ENOMEM; + if (IS_ERR(reg)) { + pr_err("%pfwP: Failed to map MMIO region\n", fwnode); + return PTR_ERR(reg); + } /* Parse SSWI setting */ rc = aclint_sswi_parse_irq(fwnode, reg); -- cgit v1.2.3 From 3a74e73b863a2493c0502a08e20ab026a0134ca1 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Tue, 13 Jan 2026 12:53:12 +0000 Subject: irqchip/renesas-rzv2h: Add suspend/resume support On RZ/G3E using PSCI, s2ram powers down the SoC. Add suspend/resume callbacks to restore IRQ type for NMI, TINT and external IRQ interrupts. Signed-off-by: Biju Das Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260113125315.359967-3-biju.das.jz@bp.renesas.com --- drivers/irqchip/irq-renesas-rzv2h.c | 60 +++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-rzv2h.c b/drivers/irqchip/irq-renesas-rzv2h.c index 0c44b6109842..69980a8ecccc 100644 --- a/drivers/irqchip/irq-renesas-rzv2h.c +++ b/drivers/irqchip/irq-renesas-rzv2h.c @@ -20,6 +20,7 @@ #include #include #include +#include /* DT "interrupts" indexes */ #define ICU_IRQ_START 1 @@ -89,6 +90,18 @@ #define ICU_RZG3E_TSSEL_MAX_VAL 0x8c #define ICU_RZV2H_TSSEL_MAX_VAL 0x55 +/** + * struct rzv2h_irqc_reg_cache - registers cache (necessary for suspend/resume) + * @nitsr: ICU_NITSR register + * @iitsr: ICU_IITSR register + * @titsr: ICU_TITSR registers + */ +struct rzv2h_irqc_reg_cache { + u32 nitsr; + u32 iitsr; + u32 titsr[2]; +}; + /** * struct rzv2h_hw_info - Interrupt Control Unit controller hardware info structure. * @tssel_lut: TINT lookup table @@ -118,13 +131,15 @@ struct rzv2h_hw_info { * @fwspec: IRQ firmware specific data * @lock: Lock to serialize access to hardware registers * @info: Pointer to struct rzv2h_hw_info + * @cache: Registers cache for suspend/resume */ -struct rzv2h_icu_priv { +static struct rzv2h_icu_priv { void __iomem *base; struct irq_fwspec fwspec[ICU_NUM_IRQ]; raw_spinlock_t lock; const struct rzv2h_hw_info *info; -}; + struct rzv2h_irqc_reg_cache cache; +} *rzv2h_icu_data; void rzv2h_icu_register_dma_req(struct platform_device *icu_dev, u8 dmac_index, u8 dmac_channel, u16 req_no) @@ -412,6 +427,44 @@ static int rzv2h_icu_set_type(struct irq_data *d, unsigned int type) return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH); } +static int rzv2h_irqc_irq_suspend(void *data) +{ + struct rzv2h_irqc_reg_cache *cache = &rzv2h_icu_data->cache; + void __iomem *base = rzv2h_icu_data->base; + + cache->nitsr = readl_relaxed(base + ICU_NITSR); + cache->iitsr = readl_relaxed(base + ICU_IITSR); + for (unsigned int i = 0; i < 2; i++) + cache->titsr[i] = readl_relaxed(base + rzv2h_icu_data->info->t_offs + ICU_TITSR(i)); + + return 0; +} + +static void rzv2h_irqc_irq_resume(void *data) +{ + struct rzv2h_irqc_reg_cache *cache = &rzv2h_icu_data->cache; + void __iomem *base = rzv2h_icu_data->base; + + /* + * Restore only interrupt type. TSSRx will be restored at the + * request of pin controller to avoid spurious interrupts due + * to invalid PIN states. + */ + for (unsigned int i = 0; i < 2; i++) + writel_relaxed(cache->titsr[i], base + rzv2h_icu_data->info->t_offs + ICU_TITSR(i)); + writel_relaxed(cache->iitsr, base + ICU_IITSR); + writel_relaxed(cache->nitsr, base + ICU_NITSR); +} + +static const struct syscore_ops rzv2h_irqc_syscore_ops = { + .suspend = rzv2h_irqc_irq_suspend, + .resume = rzv2h_irqc_irq_resume, +}; + +static struct syscore rzv2h_irqc_syscore = { + .ops = &rzv2h_irqc_syscore_ops, +}; + static const struct irq_chip rzv2h_icu_chip = { .name = "rzv2h-icu", .irq_eoi = rzv2h_icu_eoi, @@ -495,7 +548,6 @@ 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 rzv2h_icu_priv *rzv2h_icu_data; struct reset_control *resetn; int ret; @@ -553,6 +605,8 @@ static int rzv2h_icu_probe_common(struct platform_device *pdev, struct device_no rzv2h_icu_data->info = hw_info; + register_syscore(&rzv2h_irqc_syscore); + /* * 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. -- cgit v1.2.3 From 3d9617ea8ab5ca779a227d1e7d23741f5f8400c1 Mon Sep 17 00:00:00 2001 From: Aniket Limaye Date: Fri, 23 Jan 2026 12:25:46 +0530 Subject: irqchip/ti-sci-intr: Allow parsing interrupt-types per-line Some INTR router instances act as simple passthroughs that preserve the source interrupt type unchanged at the output line, rather than converting all interrupts to a fixed type. When interrupt sources are not homogeneous with respect to trigger type, the driver needs to read each source's interrupt type from DT and pass it unchanged to its interrupt parent. Add support to check for absence of "ti,intr-trigger-type" to indicate passthrough mode. When this property is absent, parse interrupt type per-line from the DT fwspec provided by the interrupt source. Else, use the global setting for all interrupt lines. Signed-off-by: Aniket Limaye Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260123-ul-driver-i2c-j722s-v4-2-b08625c487d5@ti.com --- drivers/irqchip/irq-ti-sci-intr.c | 54 ++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-ti-sci-intr.c b/drivers/irqchip/irq-ti-sci-intr.c index 354613e74ad0..0ea17040e934 100644 --- a/drivers/irqchip/irq-ti-sci-intr.c +++ b/drivers/irqchip/irq-ti-sci-intr.c @@ -61,12 +61,21 @@ static int ti_sci_intr_irq_domain_translate(struct irq_domain *domain, { struct ti_sci_intr_irq_domain *intr = domain->host_data; - if (fwspec->param_count != 1) - return -EINVAL; + if (intr->type) { + /* Global interrupt-type */ + if (fwspec->param_count != 1) + return -EINVAL; - *hwirq = fwspec->param[0]; - *type = intr->type; + *hwirq = fwspec->param[0]; + *type = intr->type; + } else { + /* Per-Line interrupt-type */ + if (fwspec->param_count != 2) + return -EINVAL; + *hwirq = fwspec->param[0]; + *type = fwspec->param[1]; + } return 0; } @@ -128,11 +137,12 @@ static void ti_sci_intr_irq_domain_free(struct irq_domain *domain, * @domain: Pointer to the interrupt router IRQ domain * @virq: Corresponding Linux virtual IRQ number * @hwirq: Corresponding hwirq for the IRQ within this IRQ domain + * @hwirq_type: Corresponding hwirq trigger type for the IRQ within this IRQ domain * * Returns intr output irq if all went well else appropriate error pointer. */ -static int ti_sci_intr_alloc_parent_irq(struct irq_domain *domain, - unsigned int virq, u32 hwirq) +static int ti_sci_intr_alloc_parent_irq(struct irq_domain *domain, unsigned int virq, + u32 hwirq, u32 hwirq_type) { struct ti_sci_intr_irq_domain *intr = domain->host_data; struct device_node *parent_node; @@ -156,11 +166,22 @@ static int ti_sci_intr_alloc_parent_irq(struct irq_domain *domain, fwspec.param_count = 3; fwspec.param[0] = 0; /* SPI */ fwspec.param[1] = p_hwirq - 32; /* SPI offset */ - fwspec.param[2] = intr->type; + fwspec.param[2] = hwirq_type; } else { /* Parent is Interrupt Router */ - fwspec.param_count = 1; - fwspec.param[0] = p_hwirq; + u32 parent_trigger_type; + + if (!of_property_read_u32(parent_node, "ti,intr-trigger-type", + &parent_trigger_type)) { + /* Parent has global trigger type */ + fwspec.param_count = 1; + fwspec.param[0] = p_hwirq; + } else { + /* Parent supports per-line trigger types */ + fwspec.param_count = 2; + fwspec.param[0] = p_hwirq; + fwspec.param[1] = hwirq_type; + } } err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec); @@ -196,15 +217,15 @@ static int ti_sci_intr_irq_domain_alloc(struct irq_domain *domain, void *data) { struct irq_fwspec *fwspec = data; + unsigned int hwirq_type; unsigned long hwirq; - unsigned int flags; int err, out_irq; - err = ti_sci_intr_irq_domain_translate(domain, fwspec, &hwirq, &flags); + err = ti_sci_intr_irq_domain_translate(domain, fwspec, &hwirq, &hwirq_type); if (err) return err; - out_irq = ti_sci_intr_alloc_parent_irq(domain, virq, hwirq); + out_irq = ti_sci_intr_alloc_parent_irq(domain, virq, hwirq, hwirq_type); if (out_irq < 0) return out_irq; @@ -247,12 +268,9 @@ static int ti_sci_intr_irq_domain_probe(struct platform_device *pdev) return -ENOMEM; intr->dev = dev; - ret = of_property_read_u32(dev_of_node(dev), "ti,intr-trigger-type", - &intr->type); - if (ret) { - dev_err(dev, "missing ti,intr-trigger-type property\n"); - return -EINVAL; - } + + if (of_property_read_u32(dev_of_node(dev), "ti,intr-trigger-type", &intr->type)) + intr->type = IRQ_TYPE_NONE; intr->sci = devm_ti_sci_get_by_phandle(dev, "ti,sci"); if (IS_ERR(intr->sci)) -- cgit v1.2.3 From e1f94662d759411fb7da3e4e662ec588c268e1a5 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 1 Feb 2026 16:38:36 +0100 Subject: irqchip/aspeed-scu-ic: Remove unused variable mask The kernel test robot reports: drivers/irqchip/irq-aspeed-scu-ic.c:107:27: warning: variable 'mask' set but not used 107 | unsigned int sts, mask; Remove the leftover. Fixes: b2a0c13f8b4f ("irqchip/aspeed-scu-ic: Add support for AST2700 SCU interrupt controllers") Reported-by: kernel test robot Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/874io0h3sz.ffs@tglx Closes: https://lore.kernel.org/oe-kbuild-all/202602010957.9uuKqUkG-lkp@intel.com/ --- drivers/irqchip/irq-aspeed-scu-ic.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-aspeed-scu-ic.c b/drivers/irqchip/irq-aspeed-scu-ic.c index bee59c8c4c93..7398c3b9eace 100644 --- a/drivers/irqchip/irq-aspeed-scu-ic.c +++ b/drivers/irqchip/irq-aspeed-scu-ic.c @@ -104,11 +104,10 @@ static void aspeed_scu_ic_irq_handler_split(struct irq_desc *desc) struct aspeed_scu_ic *scu_ic = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); unsigned long bit, enabled, max, status; - unsigned int sts, mask; + unsigned int sts; chained_irq_enter(chip, desc); - mask = scu_ic->irq_enable; sts = readl(scu_ic->base + scu_ic->isr); enabled = sts & scu_ic->irq_enable; sts = readl(scu_ic->base + scu_ic->isr); -- cgit v1.2.3 From 42e025b719c128bdf8ff88584589a1e4a2448c81 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 3 Feb 2026 20:16:12 +0100 Subject: irqchip/sifive-plic: Handle number of hardware interrupts correctly The driver is handling the number of hardware interrupts inconsistently. The reason is that the firmware enumerates the maximum number of device interrupts, but the actual number of hardware interrupts is one more because hardware interrupt 0 is reserved. There are two loop variants where this matters: 1) Iterating over the device interrupts for (irq = 1; irq < total_irqs; irq++) 2) Iterating over the number of interrupt register groups for (grp = 0; grp < irq_groups; grp++) The current code stores the number of device interrupts and that requires to write the loops as: 1) for (irq = 1; irq <= device_irqs; irq++) 2) for (grp = 0; grp < DIV_ROUND_UP(device_irqs + 1); grp++) But the code gets it wrong all over the place. Just fixing up the conditions and off by ones is not a sustainable solution as the next changes will reintroduce the same bugs over and over. Sanitize it by storing the total number of hardware interrupts during probe and precalculating the number of groups. To future proof it mark priv::total_irqs __private, provide a correct iterator macro and adjust the code to this. Marking it private allows sparse (C=1 build) to catch direct access to this member: drivers/irqchip/irq-sifive-plic.c:270:9: warning: dereference of noderef expression That should prevent at least the most obvious future damage in that area. Fixes: e80f0b6a2cf3 ("irqchip/irq-sifive-plic: Add syscore callbacks for hibernation") Reported-by: Yangyu Chen Signed-off-by: Thomas Gleixner Tested-by: Yangyu Chen Link: https://patch.msgid.link/87ikcd36i9.ffs@tglx --- drivers/irqchip/irq-sifive-plic.c | 82 +++++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-sifive-plic.c b/drivers/irqchip/irq-sifive-plic.c index 210a57959637..60fd8f91762b 100644 --- a/drivers/irqchip/irq-sifive-plic.c +++ b/drivers/irqchip/irq-sifive-plic.c @@ -68,15 +68,17 @@ #define PLIC_QUIRK_CP100_CLAIM_REGISTER_ERRATUM 1 struct plic_priv { - struct fwnode_handle *fwnode; - struct cpumask lmask; - struct irq_domain *irqdomain; - void __iomem *regs; - unsigned long plic_quirks; - unsigned int nr_irqs; - unsigned long *prio_save; - u32 gsi_base; - int acpi_plic_id; + struct fwnode_handle *fwnode; + struct cpumask lmask; + struct irq_domain *irqdomain; + void __iomem *regs; + unsigned long plic_quirks; + /* device interrupts + 1 to compensate for the reserved hwirq 0 */ + unsigned int __private total_irqs; + unsigned int irq_groups; + unsigned long *prio_save; + u32 gsi_base; + int acpi_plic_id; }; struct plic_handler { @@ -91,6 +93,12 @@ struct plic_handler { u32 *enable_save; struct plic_priv *priv; }; + +/* + * Macro to deal with the insanity of hardware interrupt 0 being reserved */ +#define for_each_device_irq(iter, priv) \ + for (unsigned int iter = 1; iter < ACCESS_PRIVATE(priv, total_irqs); iter++) + static int plic_parent_irq __ro_after_init; static bool plic_global_setup_done __ro_after_init; static DEFINE_PER_CPU(struct plic_handler, plic_handlers); @@ -257,14 +265,11 @@ static int plic_irq_set_type(struct irq_data *d, unsigned int type) static int plic_irq_suspend(void *data) { - struct plic_priv *priv; - - priv = per_cpu_ptr(&plic_handlers, smp_processor_id())->priv; + struct plic_priv *priv = this_cpu_ptr(&plic_handlers)->priv; - /* irq ID 0 is reserved */ - for (unsigned int i = 1; i < priv->nr_irqs; i++) { - __assign_bit(i, priv->prio_save, - readl(priv->regs + PRIORITY_BASE + i * PRIORITY_PER_ID)); + for_each_device_irq(irq, priv) { + __assign_bit(irq, priv->prio_save, + readl(priv->regs + PRIORITY_BASE + irq * PRIORITY_PER_ID)); } return 0; @@ -272,18 +277,15 @@ static int plic_irq_suspend(void *data) static void plic_irq_resume(void *data) { - unsigned int i, index, cpu; + struct plic_priv *priv = this_cpu_ptr(&plic_handlers)->priv; + unsigned int index, cpu; unsigned long flags; u32 __iomem *reg; - struct plic_priv *priv; - - priv = per_cpu_ptr(&plic_handlers, smp_processor_id())->priv; - /* irq ID 0 is reserved */ - for (i = 1; i < priv->nr_irqs; i++) { - index = BIT_WORD(i); - writel((priv->prio_save[index] & BIT_MASK(i)) ? 1 : 0, - priv->regs + PRIORITY_BASE + i * PRIORITY_PER_ID); + for_each_device_irq(irq, priv) { + index = BIT_WORD(irq); + writel((priv->prio_save[index] & BIT_MASK(irq)) ? 1 : 0, + priv->regs + PRIORITY_BASE + irq * PRIORITY_PER_ID); } for_each_present_cpu(cpu) { @@ -293,7 +295,7 @@ static void plic_irq_resume(void *data) continue; raw_spin_lock_irqsave(&handler->enable_lock, flags); - for (i = 0; i < DIV_ROUND_UP(priv->nr_irqs, 32); i++) { + for (unsigned int i = 0; i < priv->irq_groups; i++) { reg = handler->enable_base + i * sizeof(u32); writel(handler->enable_save[i], reg); } @@ -431,7 +433,7 @@ static u32 cp100_isolate_pending_irq(int nr_irq_groups, struct plic_handler *han static irq_hw_number_t cp100_get_hwirq(struct plic_handler *handler, void __iomem *claim) { - int nr_irq_groups = DIV_ROUND_UP(handler->priv->nr_irqs, 32); + int nr_irq_groups = handler->priv->irq_groups; u32 __iomem *enable = handler->enable_base; irq_hw_number_t hwirq = 0; u32 iso_mask; @@ -614,7 +616,6 @@ static int plic_probe(struct fwnode_handle *fwnode) struct plic_handler *handler; u32 nr_irqs, parent_hwirq; struct plic_priv *priv; - irq_hw_number_t hwirq; void __iomem *regs; int id, context_id; u32 gsi_base; @@ -647,7 +648,16 @@ static int plic_probe(struct fwnode_handle *fwnode) priv->fwnode = fwnode; priv->plic_quirks = plic_quirks; - priv->nr_irqs = nr_irqs; + /* + * The firmware provides the number of device interrupts. As + * hardware interrupt 0 is reserved, the number of total interrupts + * is nr_irqs + 1. + */ + nr_irqs++; + ACCESS_PRIVATE(priv, total_irqs) = nr_irqs; + /* Precalculate the number of register groups */ + priv->irq_groups = DIV_ROUND_UP(nr_irqs, 32); + priv->regs = regs; priv->gsi_base = gsi_base; priv->acpi_plic_id = id; @@ -686,7 +696,7 @@ static int plic_probe(struct fwnode_handle *fwnode) u32 __iomem *enable_base = priv->regs + CONTEXT_ENABLE_BASE + i * CONTEXT_ENABLE_SIZE; - for (int j = 0; j <= nr_irqs / 32; j++) + for (int j = 0; j < priv->irq_groups; j++) writel(0, enable_base + j); } continue; @@ -718,23 +728,21 @@ static int plic_probe(struct fwnode_handle *fwnode) context_id * CONTEXT_ENABLE_SIZE; handler->priv = priv; - handler->enable_save = kcalloc(DIV_ROUND_UP(nr_irqs, 32), - sizeof(*handler->enable_save), GFP_KERNEL); + handler->enable_save = kcalloc(priv->irq_groups, sizeof(*handler->enable_save), + GFP_KERNEL); if (!handler->enable_save) { error = -ENOMEM; goto fail_cleanup_contexts; } done: - for (hwirq = 1; hwirq <= nr_irqs; hwirq++) { + for_each_device_irq(hwirq, priv) { plic_toggle(handler, hwirq, 0); - writel(1, priv->regs + PRIORITY_BASE + - hwirq * PRIORITY_PER_ID); + writel(1, priv->regs + PRIORITY_BASE + hwirq * PRIORITY_PER_ID); } nr_handlers++; } - priv->irqdomain = irq_domain_create_linear(fwnode, nr_irqs + 1, - &plic_irqdomain_ops, priv); + priv->irqdomain = irq_domain_create_linear(fwnode, nr_irqs, &plic_irqdomain_ops, priv); if (WARN_ON(!priv->irqdomain)) { error = -ENOMEM; goto fail_cleanup_contexts; -- cgit v1.2.3 From 6054b10c328813e88bca31ac0d02eaff06057db0 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 3 Feb 2026 21:07:35 +0000 Subject: irqchip/gic-v5: Fix spelling mistake "ouside" -> "outside" There is a spelling mistake in a pr_err message. Fix it. Signed-off-by: Colin Ian King Signed-off-by: Thomas Gleixner Reviewed-by: Jonathan Cameron Acked-by: Lorenzo Pieralisi Link: https://patch.msgid.link/20260203210735.5036-1-colin.i.king@gmail.com --- drivers/irqchip/irq-gic-v5-its.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-gic-v5-its.c b/drivers/irqchip/irq-gic-v5-its.c index 554485f0be1f..f410e178c841 100644 --- a/drivers/irqchip/irq-gic-v5-its.c +++ b/drivers/irqchip/irq-gic-v5-its.c @@ -900,7 +900,7 @@ static int gicv5_its_alloc_eventid(struct gicv5_its_dev *its_dev, msi_alloc_info event_id_base = info->hwirq; if (event_id_base >= its_dev->num_events) { - pr_err("EventID ouside of ITT range; cannot allocate an ITT entry!\n"); + pr_err("EventID outside of ITT range; cannot allocate an ITT entry!\n"); return -EINVAL; } -- cgit v1.2.3