diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-22 16:25:25 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-22 16:25:25 -0700 |
| commit | d4fd1603f302e6d9a35e4475725479b7fde5c5d9 (patch) | |
| tree | 78764b2c11bb7ddb01e7d619b403c15b38d576b9 | |
| parent | 81ed8bd71e093fa2bed806c3b19bbcbaa3a85080 (diff) | |
| parent | 58b34b72b64bfbfc5f8ad4d8f229942aff76597e (diff) | |
Merge tag 'irq-urgent-2026-08-22' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull irqchip driver fixes from Ingo Molnar:
- Fix potential loss of interrupt in the renesas-rzg2l irqchip driver
(Biju Das)
- Fix a series of mostly corner case & error handling bugs in the
gic-v5 irqchip driver (Lorenzo Pieralisi)
- Fix hardware probing on the ast2700-intc irqchip driver (Michael
Pesa)
- Fix endianness bug on the Realtek Interrupt Controller (Rustam
Adilov)
- Fix incorrect sleeping allocation in critical section in the
ast2700-intc irqchip driver (Ryan Chen)
- Fix two bugs in the GICv5 irqchip driver: clear IRQ affinity properly
on teardown, and fix IRQ serialization bug on disabling the IRQ
(Sascha Bischoff)
* tag 'irq-urgent-2026-08-22' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
irqchip/irq-realtek-rtl: Use readl_be()/writel_be() instead of readl()/writel()
irqchip/gic-v5: Defer default SPI and LPI IAFFID programming
irqchip/gic-v5: Use logical cpu 0 irs_data for dynamic IST allocation
irqchip/gic-v5: Release IRS iomem region on driver init failure
irqchip/gic-v5: Fix gicv5_init_common() error paths
irqchip/gic-v5: Disable IRSes on probe failures
irqchip/gic-v5: Check for NULL LPI domain on domain teardown
irqchip/gic-v5: Check get_logical_index() return value in MADT IAFFID parsing
irqchip/gic-v5: Synchronize CPU interface disable
irqchip/gic-v5: Clear per-CPU IRS data on teardown
irqchip/ast2700-intc: Disable all interrupt merge banks on probe
irqchip/ast2700-intc: Avoid allocating in the irq_domain activate() callback
irqchip/renesas-rzg2l: Fix loss of interrupt
| -rw-r--r-- | drivers/irqchip/irq-ast2700-intc0.c | 4 | ||||
| -rw-r--r-- | drivers/irqchip/irq-ast2700-intc1.c | 3 | ||||
| -rw-r--r-- | drivers/irqchip/irq-gic-v5-irs.c | 38 | ||||
| -rw-r--r-- | drivers/irqchip/irq-gic-v5.c | 29 | ||||
| -rw-r--r-- | drivers/irqchip/irq-realtek-rtl.c | 20 | ||||
| -rw-r--r-- | drivers/irqchip/irq-renesas-rzg2l.c | 4 | ||||
| -rw-r--r-- | include/linux/irqchip/arm-gic-v5.h | 2 |
7 files changed, 70 insertions, 30 deletions
diff --git a/drivers/irqchip/irq-ast2700-intc0.c b/drivers/irqchip/irq-ast2700-intc0.c index 14b8b88f1179..1e375e9dffae 100644 --- a/drivers/irqchip/irq-ast2700-intc0.c +++ b/drivers/irqchip/irq-ast2700-intc0.c @@ -48,9 +48,9 @@ #define INTC0_INTMX_IER 0x1b00 #define INTC0_INTMX_ISR 0x1b04 #define INTC0_INTMX_BANK_SIZE 0x10 -#define INTC0_INTM_BANK_NUM 3 #define INTC0_IRQS_PER_BANK 32 #define INTM_IRQS_PER_BANK 10 +#define INTC0_INTM_BANK_NUM (INTC0_INTM_NUM / INTM_IRQS_PER_BANK) #define INTC0_SEL_BASE 0x200 #define INTC0_SEL_BANK_SIZE 0x4 #define INTC0_SEL_ROUTE_SIZE 0x100 @@ -312,7 +312,7 @@ int aspeed_intc0_resolve_route(const struct irq_domain *c0domain, size_t nc1outs return -ENOENT; if (!IS_ENABLED(CONFIG_ASPEED_AST2700_INTC_TEST) && - !fwnode_device_is_compatible(c0domain->fwnode, "aspeed,ast2700-intc0")) + !of_device_is_compatible(to_of_node(c0domain->fwnode), "aspeed,ast2700-intc0")) return -ENODEV; intc0 = c0domain->host_data; diff --git a/drivers/irqchip/irq-ast2700-intc1.c b/drivers/irqchip/irq-ast2700-intc1.c index 59e8f0d5ddcd..df6acbafce2d 100644 --- a/drivers/irqchip/irq-ast2700-intc1.c +++ b/drivers/irqchip/irq-ast2700-intc1.c @@ -157,7 +157,8 @@ static int aspeed_intc1_irq_domain_activate(struct irq_domain *domain, intc1->ranges.nranges, intc1->ranges.ranges, &resolved); if (rc < 0) { - if (!fwnode_device_is_compatible(intc1->upstream->fwnode, "riscv,aplic")) { + if (!of_device_is_compatible(to_of_node(intc1->upstream->fwnode), + "riscv,aplic")) { dev_warn(intc1->dev, "Failed to resolve interrupt route for hwirq %lu in domain %s\n", data->hwirq, domain->name); diff --git a/drivers/irqchip/irq-gic-v5-irs.c b/drivers/irqchip/irq-gic-v5-irs.c index f3fce0b1e25d..b3feb6340b59 100644 --- a/drivers/irqchip/irq-gic-v5-irs.c +++ b/drivers/irqchip/irq-gic-v5-irs.c @@ -199,7 +199,7 @@ int gicv5_irs_iste_alloc(const u32 lpi) if (!gicv5_global_data.ist.l2) return 0; - irs_data = per_cpu(per_cpu_irs_data, smp_processor_id()); + irs_data = per_cpu(per_cpu_irs_data, 0); if (!irs_data) return -ENOENT; @@ -488,6 +488,12 @@ static int gicv5_irs_wait_for_idle(struct gicv5_irs_chip_data *irs_data) GICV5_IRS_CR0_IDLE, NULL); } +static void __init gicv5_irs_disable(struct gicv5_irs_chip_data *irs_data) +{ + irs_writel_relaxed(irs_data, 0, GICV5_IRS_CR0); + gicv5_irs_wait_for_idle(irs_data); +} + void gicv5_irs_syncr(void) { struct gicv5_irs_chip_data *irs_data; @@ -651,6 +657,18 @@ static int __init gicv5_irs_of_init_affinity(struct device_node *node, return ret; } +static void __init gicv5_irs_clear_affinity(struct gicv5_irs_chip_data *irs_data) +{ + int cpu; + + for_each_possible_cpu(cpu) { + if (per_cpu(per_cpu_irs_data, cpu) == irs_data) { + per_cpu(cpu_iaffid, cpu).valid = false; + per_cpu(per_cpu_irs_data, cpu) = NULL; + } + } +} + static void irs_setup_pri_bits(u32 idr1) { switch (FIELD_GET(GICV5_IRS_IDR1_PRIORITY_BITS, idr1)) { @@ -722,6 +740,7 @@ static int __init gicv5_irs_init(struct gicv5_irs_chip_data *irs_data) static int __init gicv5_irs_of_init(struct device_node *node) { struct gicv5_irs_chip_data *irs_data; + struct resource res; void __iomem *irs_base; u8 iaffid_bits; u32 idr; @@ -739,6 +758,11 @@ static int __init gicv5_irs_of_init(struct device_node *node) goto out_err; } + if (of_address_to_resource(node, ret, &res)) { + ret = -EINVAL; + goto out_err; + } + irs_base = of_io_request_and_map(node, ret, of_node_full_name(node)); if (IS_ERR(irs_base)) { pr_err("%pOF: unable to map GICv5 IRS registers\n", node); @@ -747,6 +771,7 @@ static int __init gicv5_irs_of_init(struct device_node *node) } irs_data->fwnode = of_fwnode_handle(node); + irs_data->res = res; gicv5_irs_init_bases(irs_data, irs_base, of_property_read_bool(node, "dma-noncoherent")); idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR1); @@ -773,7 +798,10 @@ static int __init gicv5_irs_of_init(struct device_node *node) return ret; out_iomem: + gicv5_irs_clear_affinity(irs_data); + gicv5_irs_disable(irs_data); iounmap(irs_base); + release_mem_region(res.start, resource_size(&res)); out_err: kfree(irs_data); return ret; @@ -787,7 +815,10 @@ void __init gicv5_irs_remove(void) gicv5_deinit_lpis(); list_for_each_entry_safe(irs_data, tmp_data, &irs_nodes, entry) { + gicv5_irs_clear_affinity(irs_data); + gicv5_irs_disable(irs_data); iounmap(irs_data->irs_base); + release_mem_region(irs_data->res.start, resource_size(&irs_data->res)); list_del(&irs_data->entry); kfree(irs_data); } @@ -860,6 +891,8 @@ static int __init gic_acpi_parse_iaffid(union acpi_subtable_headers *header, return 0; cpu = get_logical_index(gicc->arm_mpidr); + if (cpu < 0) + return 0; if (gicc->iaffid & ~GENMASK(current_iaffid_bits - 1, 0)) { pr_warn("CPU %d iaffid 0x%x exceeds IRS iaffid bits\n", cpu, gicc->iaffid); @@ -934,6 +967,7 @@ static int __init gic_acpi_parse_madt_irs(union acpi_subtable_headers *header, } gicv5_irs_init_bases(irs_data, irs_base, irs->flags & ACPI_MADT_IRS_NON_COHERENT); + irs_data->res = *r; gicv5_irs_acpi_init_affinity(irs->irs_id, irs_data); @@ -951,6 +985,8 @@ static int __init gic_acpi_parse_madt_irs(union acpi_subtable_headers *header, return 0; out_map: + gicv5_irs_clear_affinity(irs_data); + gicv5_irs_disable(irs_data); iounmap(irs_base); out_release: release_mem_region(r->start, resource_size(r)); diff --git a/drivers/irqchip/irq-gic-v5.c b/drivers/irqchip/irq-gic-v5.c index 5dcf404ab4c8..ac2d423b1723 100644 --- a/drivers/irqchip/irq-gic-v5.c +++ b/drivers/irqchip/irq-gic-v5.c @@ -87,25 +87,13 @@ static void gicv5_ppi_priority_init(void) static void gicv5_hwirq_init(irq_hw_number_t hwirq, u8 priority, u8 hwirq_type) { - u64 cdpri, cdaff; - u16 iaffid; - int ret; + u64 cdpri; if (hwirq_type == GICV5_HWIRQ_TYPE_LPI || hwirq_type == GICV5_HWIRQ_TYPE_SPI) { cdpri = FIELD_PREP(GICV5_GIC_CDPRI_PRIORITY_MASK, priority) | FIELD_PREP(GICV5_GIC_CDPRI_TYPE_MASK, hwirq_type) | FIELD_PREP(GICV5_GIC_CDPRI_ID_MASK, hwirq); gic_insn(cdpri, CDPRI); - - ret = gicv5_irs_cpu_to_iaffid(smp_processor_id(), &iaffid); - - if (WARN_ON_ONCE(ret)) - return; - - cdaff = FIELD_PREP(GICV5_GIC_CDAFF_IAFFID_MASK, iaffid) | - FIELD_PREP(GICV5_GIC_CDAFF_TYPE_MASK, hwirq_type) | - FIELD_PREP(GICV5_GIC_CDAFF_ID_MASK, hwirq); - gic_insn(cdaff, CDAFF); } } @@ -548,6 +536,7 @@ static const struct irq_chip gicv5_spi_irq_chip = { .irq_get_irqchip_state = gicv5_spi_irq_get_irqchip_state, .irq_set_irqchip_state = gicv5_spi_irq_set_irqchip_state, .flags = IRQCHIP_SET_TYPE_MASKED | + IRQCHIP_AFFINITY_PRE_STARTUP | IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND, }; @@ -561,7 +550,8 @@ static const struct irq_chip gicv5_lpi_irq_chip = { .irq_retrigger = gicv5_lpi_irq_retrigger, .irq_get_irqchip_state = gicv5_lpi_irq_get_irqchip_state, .irq_set_irqchip_state = gicv5_lpi_irq_set_irqchip_state, - .flags = IRQCHIP_SKIP_SET_WAKE | + .flags = IRQCHIP_AFFINITY_PRE_STARTUP | + IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND, }; @@ -862,6 +852,9 @@ void __init gicv5_init_lpi_domain(void) void __init gicv5_free_lpi_domain(void) { + if (!gicv5_global_data.lpi_domain) + return; + irq_domain_remove(gicv5_global_data.lpi_domain); gicv5_global_data.lpi_domain = NULL; } @@ -983,6 +976,7 @@ static void gicv5_cpu_disable_interrupts(void) cr0 = FIELD_PREP(ICC_CR0_EL1_EN, 0); write_sysreg_s(cr0, SYS_ICC_CR0_EL1); + isb(); } static void gicv5_cpu_enable_interrupts(void) @@ -1170,7 +1164,7 @@ static int __init gicv5_init_common(struct fwnode_handle *parent_domain) ret = gicv5_starting_cpu(smp_processor_id()); if (ret) - goto out_dom; + goto out_int; ret = set_handle_irq(gicv5_handle_irq); if (ret) @@ -1178,16 +1172,17 @@ static int __init gicv5_init_common(struct fwnode_handle *parent_domain) ret = gicv5_irs_enable(); if (ret) - goto out_int; + goto out_handle; gicv5_smp_init(); gicv5_irs_its_probe(); return 0; +out_handle: + set_handle_irq(NULL); out_int: gicv5_cpu_disable_interrupts(); -out_dom: gicv5_free_domains(); return ret; } diff --git a/drivers/irqchip/irq-realtek-rtl.c b/drivers/irqchip/irq-realtek-rtl.c index c8becb458da2..26e52c3f8c68 100644 --- a/drivers/irqchip/irq-realtek-rtl.c +++ b/drivers/irqchip/irq-realtek-rtl.c @@ -1,5 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-only /* + * Realtek Interrupt controller. + * + * The Realtek Interrupt controller is a big endian device found in the + * Realtek MIPS SoCs. + * * Copyright (C) 2020 Birger Koblitz <mail@birger-koblitz.de> * Copyright (C) 2020 Bert Vermeulen <bert@biot.com> * Copyright (C) 2020 John Crispin <john@phrozen.org> @@ -50,18 +55,18 @@ static inline void enable_gimr(unsigned int cpu, unsigned int hw_irq) { u32 gimr; - gimr = readl(REG(cpu, RTL_ICTL_GIMR)); + gimr = readl_be(REG(cpu, RTL_ICTL_GIMR)); gimr |= BIT(hw_irq); - writel(gimr, REG(cpu, RTL_ICTL_GIMR)); + writel_be(gimr, REG(cpu, RTL_ICTL_GIMR)); } static inline void disable_gimr(unsigned int cpu, unsigned int hw_irq) { u32 gimr; - gimr = readl(REG(cpu, RTL_ICTL_GIMR)); + gimr = readl_be(REG(cpu, RTL_ICTL_GIMR)); gimr &= ~BIT(hw_irq); - writel(gimr, REG(cpu, RTL_ICTL_GIMR)); + writel_be(gimr, REG(cpu, RTL_ICTL_GIMR)); } static void write_irr(unsigned int cpu, int hw_irq, u32 value) @@ -71,9 +76,9 @@ static void write_irr(unsigned int cpu, int hw_irq, u32 value) unsigned int shift = IRR_SHIFT(hw_irq); u32 irr; - irr = readl(irr0 + offset) & ~(0xf << shift); + irr = readl_be(irr0 + offset) & ~(0xf << shift); irr |= (value & 0xf) << shift; - writel(irr, irr0 + offset); + writel_be(irr, irr0 + offset); } static void realtek_ictl_unmask_irq(struct irq_data *i) @@ -159,7 +164,8 @@ static void realtek_irq_dispatch(struct irq_desc *desc) unsigned int hw_irq; chained_irq_enter(chip, desc); - pending = readl(REG(cpu, RTL_ICTL_GIMR)) & readl(REG(cpu, RTL_ICTL_GISR)) & output->mask; + pending = readl_be(REG(cpu, RTL_ICTL_GIMR)) & + readl_be(REG(cpu, RTL_ICTL_GISR)) & output->mask; if (unlikely(!pending)) { spurious_interrupt(); diff --git a/drivers/irqchip/irq-renesas-rzg2l.c b/drivers/irqchip/irq-renesas-rzg2l.c index f6b2e69a2f4e..b3774f82855a 100644 --- a/drivers/irqchip/irq-renesas-rzg2l.c +++ b/drivers/irqchip/irq-renesas-rzg2l.c @@ -161,7 +161,7 @@ static void rzg2l_clear_irq_int(struct rzg2l_irqc_priv *priv, unsigned int hwirq * falling/rising-edge. */ if ((iscr & bit) && (iitsr & IITSR_IITSEL_MASK(hw_irq))) { - writel_relaxed(iscr & ~bit, priv->base + ISCR); + writel_relaxed(~bit, priv->base + ISCR); /* * Enforce that the posted write is flushed to prevent that the * just handled interrupt is raised again. @@ -177,7 +177,7 @@ static void rzg2l_clear_tint_int(struct rzg2l_irqc_priv *priv, unsigned int hwir reg = readl_relaxed(priv->base + TSCR); if (reg & bit) { - writel_relaxed(reg & ~bit, priv->base + TSCR); + writel_relaxed(~bit, priv->base + TSCR); /* * Enforce that the posted write is flushed to prevent that the * just handled interrupt is raised again. diff --git a/include/linux/irqchip/arm-gic-v5.h b/include/linux/irqchip/arm-gic-v5.h index f78787e654f4..2c2fb39f049c 100644 --- a/include/linux/irqchip/arm-gic-v5.h +++ b/include/linux/irqchip/arm-gic-v5.h @@ -6,6 +6,7 @@ #define __LINUX_IRQCHIP_ARM_GIC_V5_H #include <linux/iopoll.h> +#include <linux/ioport.h> #include <asm/cacheflush.h> #include <asm/smp.h> @@ -324,6 +325,7 @@ struct gicv5_irs_chip_data { struct list_head entry; struct fwnode_handle *fwnode; void __iomem *irs_base; + struct resource res; u32 flags; u32 spi_min; u32 spi_range; |
