summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-08-22 16:25:25 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-08-22 16:25:25 -0700
commitd4fd1603f302e6d9a35e4475725479b7fde5c5d9 (patch)
tree78764b2c11bb7ddb01e7d619b403c15b38d576b9
parent81ed8bd71e093fa2bed806c3b19bbcbaa3a85080 (diff)
parent58b34b72b64bfbfc5f8ad4d8f229942aff76597e (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.c4
-rw-r--r--drivers/irqchip/irq-ast2700-intc1.c3
-rw-r--r--drivers/irqchip/irq-gic-v5-irs.c38
-rw-r--r--drivers/irqchip/irq-gic-v5.c29
-rw-r--r--drivers/irqchip/irq-realtek-rtl.c20
-rw-r--r--drivers/irqchip/irq-renesas-rzg2l.c4
-rw-r--r--include/linux/irqchip/arm-gic-v5.h2
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;