realtek: irq: backport upstream patches

The interrupt driver downstream patches have been sent
upstream and got accepted. Backport them and make the
driver free of downstream hacks.

Link: https://github.com/openwrt/openwrt/pull/23679
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
This commit is contained in:
Markus Stockhausen committed 2026-06-29 17:50:48 +02:00
1 parent 0a64c7a7b6
commit f4ddf8d7ef
9 files changed
+870 -428

No files matched your search

@@ -0,0 +1,135 @@
From 167883f75f83088a2b32c85ce5e3d0cd1cef157b Mon Sep 17 00:00:00 2001
From: Markus Stockhausen <markus.stockhausen@gmx.de>
Date: Thu, 4 Jun 2026 20:25:05 +0200
Subject: [PATCH 1/8] irqchip/irq-realtek-rtl: Add/simplify register helpers
The Realtek interrupt controller has two important registers that are used
by the driver in several places
- GIMR: global interrupt mask register
- IRR: Interrupt routing registers
The usage of these registers is very inconsistent. GIMR is addressed
directly while IRR has a helper that needs a macro as an input. Harmonize
this by providing consistent helpers that improve code readability.
The callers of these helpers use classic lock/unlock functions and
sometimes use the wrong locking helper. E.g. irqsave variants are used in
mask/unmask although not needed. Adapt and fix the surrounding call
locations.
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Link: https://patch.msgid.link/20260604182506.1113440-2-markus.stockhausen@gmx.de
---
drivers/irqchip/irq-realtek-rtl.c | 64 +++++++++++++++----------------
1 file changed, 32 insertions(+), 32 deletions(-)
--- a/drivers/irqchip/irq-realtek-rtl.c
+++ b/drivers/irqchip/irq-realtek-rtl.c
@@ -37,10 +37,29 @@ static void __iomem *realtek_ictl_base;
#define IRR_OFFSET(idx) (4 * (3 - (idx * 4) / 32))
#define IRR_SHIFT(idx) ((idx * 4) % 32)
-static void write_irr(void __iomem *irr0, int idx, u32 value)
+static inline void enable_gimr(unsigned int hw_irq)
{
- unsigned int offset = IRR_OFFSET(idx);
- unsigned int shift = IRR_SHIFT(idx);
+ u32 gimr;
+
+ gimr = readl(REG(RTL_ICTL_GIMR));
+ gimr |= BIT(hw_irq);
+ writel(gimr, REG(RTL_ICTL_GIMR));
+}
+
+static inline void disable_gimr(unsigned int hw_irq)
+{
+ u32 gimr;
+
+ gimr = readl(REG(RTL_ICTL_GIMR));
+ gimr &= ~BIT(hw_irq);
+ writel(gimr, REG(RTL_ICTL_GIMR));
+}
+
+static void write_irr(int hw_irq, u32 value)
+{
+ void __iomem *irr0 = REG(RTL_ICTL_IRR0);
+ unsigned int offset = IRR_OFFSET(hw_irq);
+ unsigned int shift = IRR_SHIFT(hw_irq);
u32 irr;
irr = readl(irr0 + offset) & ~(0xf << shift);
@@ -50,30 +69,14 @@ static void write_irr(void __iomem *irr0
static void realtek_ictl_unmask_irq(struct irq_data *i)
{
- unsigned long flags;
- u32 value;
-
- raw_spin_lock_irqsave(&irq_lock, flags);
-
- value = readl(REG(RTL_ICTL_GIMR));
- value |= BIT(i->hwirq);
- writel(value, REG(RTL_ICTL_GIMR));
-
- raw_spin_unlock_irqrestore(&irq_lock, flags);
+ guard(raw_spinlock)(&irq_lock);
+ enable_gimr(i->hwirq);
}
static void realtek_ictl_mask_irq(struct irq_data *i)
{
- unsigned long flags;
- u32 value;
-
- raw_spin_lock_irqsave(&irq_lock, flags);
-
- value = readl(REG(RTL_ICTL_GIMR));
- value &= ~BIT(i->hwirq);
- writel(value, REG(RTL_ICTL_GIMR));
-
- raw_spin_unlock_irqrestore(&irq_lock, flags);
+ guard(raw_spinlock)(&irq_lock);
+ disable_gimr(i->hwirq);
}
static struct irq_chip realtek_ictl_irq = {
@@ -84,13 +87,10 @@ static struct irq_chip realtek_ictl_irq
static int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
{
- unsigned long flags;
-
irq_set_chip_and_handler(irq, &realtek_ictl_irq, handle_level_irq);
- raw_spin_lock_irqsave(&irq_lock, flags);
- write_irr(REG(RTL_ICTL_IRR0), hw, 1);
- raw_spin_unlock_irqrestore(&irq_lock, flags);
+ guard(raw_spinlock_irqsave)(&irq_lock);
+ write_irr(hw, 1);
return 0;
}
@@ -127,7 +127,6 @@ static int __init realtek_rtl_of_init(st
{
struct of_phandle_args oirq;
struct irq_domain *domain;
- unsigned int soc_irq;
int parent_irq;
realtek_ictl_base = of_iomap(node, 0);
@@ -135,9 +134,10 @@ static int __init realtek_rtl_of_init(st
return -ENXIO;
/* Disable all cascaded interrupts and clear routing */
- writel(0, REG(RTL_ICTL_GIMR));
- for (soc_irq = 0; soc_irq < RTL_ICTL_NUM_INPUTS; soc_irq++)
- write_irr(REG(RTL_ICTL_IRR0), soc_irq, 0);
+ for (unsigned int hw_irq = 0; hw_irq < RTL_ICTL_NUM_INPUTS; hw_irq++) {
+ disable_gimr(hw_irq);
+ write_irr(hw_irq, 0);
+ }
if (WARN_ON(!of_irq_count(node))) {
/*
@@ -0,0 +1,184 @@
From a1a35c09241f0577cc40f65d7372fed01138619d Mon Sep 17 00:00:00 2001
From: Markus Stockhausen <markus.stockhausen@gmx.de>
Date: Thu, 4 Jun 2026 20:25:06 +0200
Subject: [PATCH 2/8] irqchip/irq-realtek-rtl: Add multicore support
The Realtek interrupt driver currently supports only single core
systems. So the higher end devices like RTL839x and RTL930x with
dual VPEs must be driven with NR_CPU=1. Enhance the driver to
support multicore (dual VPE) systems. For this:
- Extend the register map for multiple cores
- Search for multiple CPU cores in the devicetree
- Improve the register helpers to support multiple cores
- Add an affinity setter
- Enhance the IRQ handler for multiple cores
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Link: https://patch.msgid.link/20260604182506.1113440-3-markus.stockhausen@gmx.de
---
drivers/irqchip/irq-realtek-rtl.c | 82 ++++++++++++++++++++-----------
1 file changed, 54 insertions(+), 28 deletions(-)
--- a/drivers/irqchip/irq-realtek-rtl.c
+++ b/drivers/irqchip/irq-realtek-rtl.c
@@ -23,10 +23,10 @@
#define RTL_ICTL_NUM_INPUTS 32
-#define REG(x) (realtek_ictl_base + x)
+#define REG(cpu, x) (realtek_ictl_base[cpu] + x)
static DEFINE_RAW_SPINLOCK(irq_lock);
-static void __iomem *realtek_ictl_base;
+static void __iomem *realtek_ictl_base[NR_CPUS];
/*
* IRR0-IRR3 store 4 bits per interrupt, but Realtek uses inverted numbering,
@@ -37,27 +37,27 @@ static void __iomem *realtek_ictl_base;
#define IRR_OFFSET(idx) (4 * (3 - (idx * 4) / 32))
#define IRR_SHIFT(idx) ((idx * 4) % 32)
-static inline void enable_gimr(unsigned int hw_irq)
+static inline void enable_gimr(unsigned int cpu, unsigned int hw_irq)
{
u32 gimr;
- gimr = readl(REG(RTL_ICTL_GIMR));
+ gimr = readl(REG(cpu, RTL_ICTL_GIMR));
gimr |= BIT(hw_irq);
- writel(gimr, REG(RTL_ICTL_GIMR));
+ writel(gimr, REG(cpu, RTL_ICTL_GIMR));
}
-static inline void disable_gimr(unsigned int hw_irq)
+static inline void disable_gimr(unsigned int cpu, unsigned int hw_irq)
{
u32 gimr;
- gimr = readl(REG(RTL_ICTL_GIMR));
+ gimr = readl(REG(cpu, RTL_ICTL_GIMR));
gimr &= ~BIT(hw_irq);
- writel(gimr, REG(RTL_ICTL_GIMR));
+ writel(gimr, REG(cpu, RTL_ICTL_GIMR));
}
-static void write_irr(int hw_irq, u32 value)
+static void write_irr(unsigned int cpu, int hw_irq, u32 value)
{
- void __iomem *irr0 = REG(RTL_ICTL_IRR0);
+ void __iomem *irr0 = REG(cpu, RTL_ICTL_IRR0);
unsigned int offset = IRR_OFFSET(hw_irq);
unsigned int shift = IRR_SHIFT(hw_irq);
u32 irr;
@@ -69,28 +69,51 @@ static void write_irr(int hw_irq, u32 va
static void realtek_ictl_unmask_irq(struct irq_data *i)
{
+ unsigned int cpu;
+
guard(raw_spinlock)(&irq_lock);
- enable_gimr(i->hwirq);
+ for_each_cpu(cpu, irq_data_get_effective_affinity_mask(i))
+ enable_gimr(cpu, i->hwirq);
}
static void realtek_ictl_mask_irq(struct irq_data *i)
{
+ unsigned int cpu;
+
guard(raw_spinlock)(&irq_lock);
- disable_gimr(i->hwirq);
+ for_each_cpu(cpu, irq_data_get_effective_affinity_mask(i))
+ disable_gimr(cpu, i->hwirq);
+}
+
+static int realtek_ictl_irq_affinity(struct irq_data *i, const struct cpumask *dest, bool force)
+{
+ if (!irqd_irq_masked(i))
+ realtek_ictl_mask_irq(i);
+
+ irq_data_update_effective_affinity(i, dest);
+
+ if (!irqd_irq_masked(i))
+ realtek_ictl_unmask_irq(i);
+
+ return IRQ_SET_MASK_OK;
}
static struct irq_chip realtek_ictl_irq = {
- .name = "realtek-rtl-intc",
- .irq_mask = realtek_ictl_mask_irq,
- .irq_unmask = realtek_ictl_unmask_irq,
+ .name = "realtek-rtl-intc",
+ .irq_mask = realtek_ictl_mask_irq,
+ .irq_unmask = realtek_ictl_unmask_irq,
+ .irq_set_affinity = realtek_ictl_irq_affinity,
};
static int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
{
+ unsigned int cpu;
+
irq_set_chip_and_handler(irq, &realtek_ictl_irq, handle_level_irq);
guard(raw_spinlock_irqsave)(&irq_lock);
- write_irr(hw, 1);
+ for_each_present_cpu(cpu)
+ write_irr(cpu, hw, 1);
return 0;
}
@@ -103,12 +126,13 @@ static const struct irq_domain_ops irq_d
static void realtek_irq_dispatch(struct irq_desc *desc)
{
struct irq_chip *chip = irq_desc_get_chip(desc);
+ unsigned int cpu = smp_processor_id();
struct irq_domain *domain;
unsigned long pending;
unsigned int soc_int;
chained_irq_enter(chip, desc);
- pending = readl(REG(RTL_ICTL_GIMR)) & readl(REG(RTL_ICTL_GISR));
+ pending = readl(REG(cpu, RTL_ICTL_GIMR)) & readl(REG(cpu, RTL_ICTL_GISR));
if (unlikely(!pending)) {
spurious_interrupt();
@@ -116,7 +140,7 @@ static void realtek_irq_dispatch(struct
}
domain = irq_desc_get_handler_data(desc);
- for_each_set_bit(soc_int, &pending, 32)
+ for_each_set_bit(soc_int, &pending, RTL_ICTL_NUM_INPUTS)
generic_handle_domain_irq(domain, soc_int);
out:
@@ -127,16 +151,18 @@ static int __init realtek_rtl_of_init(st
{
struct of_phandle_args oirq;
struct irq_domain *domain;
- int parent_irq;
+ int cpu, parent_irq;
- realtek_ictl_base = of_iomap(node, 0);
- if (!realtek_ictl_base)
- return -ENXIO;
-
- /* Disable all cascaded interrupts and clear routing */
- for (unsigned int hw_irq = 0; hw_irq < RTL_ICTL_NUM_INPUTS; hw_irq++) {
- disable_gimr(hw_irq);
- write_irr(hw_irq, 0);
+ for_each_present_cpu(cpu) {
+ realtek_ictl_base[cpu] = of_iomap(node, cpu);
+ if (!realtek_ictl_base[cpu])
+ return -ENXIO;
+
+ /* Disable all cascaded interrupts and clear routing */
+ for (unsigned int hw_irq = 0; hw_irq < RTL_ICTL_NUM_INPUTS; hw_irq++) {
+ disable_gimr(cpu, hw_irq);
+ write_irr(cpu, hw_irq, 0);
+ }
}
if (WARN_ON(!of_irq_count(node))) {
@@ -0,0 +1,106 @@
From 62e8298a43e3f0de23cbd52e4ac2399356a724cc Mon Sep 17 00:00:00 2001
From: Markus Stockhausen <markus.stockhausen@gmx.de>
Date: Fri, 5 Jun 2026 16:05:22 +0200
Subject: [PATCH 3/8] irqchip/irq-realtek-rtl: Use helper for parent setup
With the upcoming commits the parent interrupt setup will be extended.
Relocate it into a separate helper. Although it still works only
for a single interrupt prepare the coding so it can be easily
extended with a loop for multi parent support. For this reduce the
line lengths so that the upcoming indentation still leaves the
width below 100 characters.
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
---
drivers/irqchip/irq-realtek-rtl.c | 60 +++++++++++++++++--------------
1 file changed, 33 insertions(+), 27 deletions(-)
--- a/drivers/irqchip/irq-realtek-rtl.c
+++ b/drivers/irqchip/irq-realtek-rtl.c
@@ -147,48 +147,35 @@ out:
chained_irq_exit(chip, desc);
}
-static int __init realtek_rtl_of_init(struct device_node *node, struct device_node *parent)
+static int __init realtek_setup_parents(struct device_node *node)
{
+ int parent_irq, num_parents = of_irq_count(node);
struct of_phandle_args oirq;
struct irq_domain *domain;
- int cpu, parent_irq;
- for_each_present_cpu(cpu) {
- realtek_ictl_base[cpu] = of_iomap(node, cpu);
- if (!realtek_ictl_base[cpu])
- return -ENXIO;
-
- /* Disable all cascaded interrupts and clear routing */
- for (unsigned int hw_irq = 0; hw_irq < RTL_ICTL_NUM_INPUTS; hw_irq++) {
- disable_gimr(cpu, hw_irq);
- write_irr(cpu, hw_irq, 0);
- }
- }
-
- if (WARN_ON(!of_irq_count(node))) {
+ if (WARN_ON(!num_parents)) {
/*
- * If DT contains no parent interrupts, assume MIPS CPU IRQ 2
- * (HW0) is connected to the first output. This is the case for
- * all known hardware anyway. "interrupt-map" is deprecated, so
- * don't bother trying to parse that.
+ * If DT contains no parent interrupts, assume MIPS IRQ 2 (HW0) is
+ * connected to the first output. This is the case for all known hardware.
*/
- oirq.np = of_find_compatible_node(NULL, NULL, "mti,cpu-interrupt-controller");
+ oirq.np = of_find_compatible_node(NULL, NULL,
+ "mti,cpu-interrupt-controller");
+ if (!oirq.np)
+ return -EINVAL;
+
oirq.args_count = 1;
oirq.args[0] = 2;
-
parent_irq = irq_create_of_mapping(&oirq);
-
of_node_put(oirq.np);
} else {
parent_irq = of_irq_get(node, 0);
}
- if (parent_irq < 0)
- return parent_irq;
- else if (!parent_irq)
- return -ENODEV;
+ if (parent_irq <= 0)
+ return parent_irq ? parent_irq : -ENODEV;
- domain = irq_domain_create_linear(of_fwnode_handle(node), RTL_ICTL_NUM_INPUTS, &irq_domain_ops, NULL);
+ domain = irq_domain_create_linear(of_fwnode_handle(node), RTL_ICTL_NUM_INPUTS,
+ &irq_domain_ops, NULL);
if (!domain)
return -ENOMEM;
@@ -197,4 +184,23 @@ static int __init realtek_rtl_of_init(st
return 0;
}
+static int __init realtek_rtl_of_init(struct device_node *node, struct device_node *parent)
+{
+ unsigned int cpu;
+
+ for_each_present_cpu(cpu) {
+ realtek_ictl_base[cpu] = of_iomap(node, cpu);
+ if (!realtek_ictl_base[cpu])
+ return -ENXIO;
+
+ /* Disable all cascaded interrupts and clear routing */
+ for (unsigned int hw_irq = 0; hw_irq < RTL_ICTL_NUM_INPUTS; hw_irq++) {
+ disable_gimr(cpu, hw_irq);
+ write_irr(cpu, hw_irq, 0);
+ }
+ }
+
+ return realtek_setup_parents(node);
+}
+
IRQCHIP_DECLARE(realtek_rtl_intc, "realtek,rtl-intc", realtek_rtl_of_init);
@@ -0,0 +1,117 @@
From 1f922b06f4a1aa292af1c45d00de41f259cc7f17 Mon Sep 17 00:00:00 2001
From: Markus Stockhausen <markus.stockhausen@gmx.de>
Date: Fri, 5 Jun 2026 18:07:24 +0200
Subject: [PATCH 4/8] irqchip/irq-realtek-rtl: Add interrupt data structure
To prepare for multiple parent interrupt domains add an intermediate
data structure. For now this will only host the link to the domain.
Additionally adapt a deviating variable name to driver standard "hw_irq".
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
---
drivers/irqchip/irq-realtek-rtl.c | 48 ++++++++++++++++++++++---------
1 file changed, 34 insertions(+), 14 deletions(-)
--- a/drivers/irqchip/irq-realtek-rtl.c
+++ b/drivers/irqchip/irq-realtek-rtl.c
@@ -25,6 +25,10 @@
#define REG(cpu, x) (realtek_ictl_base[cpu] + x)
+struct realtek_ictl_output {
+ struct irq_domain *domain;
+};
+
static DEFINE_RAW_SPINLOCK(irq_lock);
static void __iomem *realtek_ictl_base[NR_CPUS];
@@ -125,11 +129,11 @@ static const struct irq_domain_ops irq_d
static void realtek_irq_dispatch(struct irq_desc *desc)
{
+ struct realtek_ictl_output *output = irq_desc_get_handler_data(desc);
struct irq_chip *chip = irq_desc_get_chip(desc);
unsigned int cpu = smp_processor_id();
- struct irq_domain *domain;
unsigned long pending;
- unsigned int soc_int;
+ unsigned int hw_irq;
chained_irq_enter(chip, desc);
pending = readl(REG(cpu, RTL_ICTL_GIMR)) & readl(REG(cpu, RTL_ICTL_GISR));
@@ -139,9 +143,8 @@ static void realtek_irq_dispatch(struct
goto out;
}
- domain = irq_desc_get_handler_data(desc);
- for_each_set_bit(soc_int, &pending, RTL_ICTL_NUM_INPUTS)
- generic_handle_domain_irq(domain, soc_int);
+ for_each_set_bit(hw_irq, &pending, RTL_ICTL_NUM_INPUTS)
+ generic_handle_domain_irq(output->domain, hw_irq);
out:
chained_irq_exit(chip, desc);
@@ -149,10 +152,15 @@ out:
static int __init realtek_setup_parents(struct device_node *node)
{
- int parent_irq, num_parents = of_irq_count(node);
+ int err, parent_irq, num_parents = of_irq_count(node);
+ struct realtek_ictl_output *output;
struct of_phandle_args oirq;
struct irq_domain *domain;
+ output = kcalloc(1, sizeof(*output), GFP_KERNEL);
+ if (!output)
+ return -ENOMEM;
+
if (WARN_ON(!num_parents)) {
/*
* If DT contains no parent interrupts, assume MIPS IRQ 2 (HW0) is
@@ -160,8 +168,10 @@ static int __init realtek_setup_parents(
*/
oirq.np = of_find_compatible_node(NULL, NULL,
"mti,cpu-interrupt-controller");
- if (!oirq.np)
- return -EINVAL;
+ if (!oirq.np) {
+ err = -EINVAL;
+ goto err_out;
+ }
oirq.args_count = 1;
oirq.args[0] = 2;
@@ -171,17 +181,27 @@ static int __init realtek_setup_parents(
parent_irq = of_irq_get(node, 0);
}
- if (parent_irq <= 0)
- return parent_irq ? parent_irq : -ENODEV;
+ if (parent_irq <= 0) {
+ err = parent_irq ? parent_irq : -ENODEV;
+ goto err_out;
+ }
domain = irq_domain_create_linear(of_fwnode_handle(node), RTL_ICTL_NUM_INPUTS,
- &irq_domain_ops, NULL);
- if (!domain)
- return -ENOMEM;
+ &irq_domain_ops, output);
+ if (!domain) {
+ err = -ENOMEM;
+ goto err_out;
+ }
- irq_set_chained_handler_and_data(parent_irq, realtek_irq_dispatch, domain);
+ output->domain = domain;
+ irq_set_chained_handler_and_data(parent_irq, realtek_irq_dispatch, output);
return 0;
+
+err_out:
+ kfree(output);
+
+ return err;
}
static int __init realtek_rtl_of_init(struct device_node *node, struct device_node *parent)
@@ -0,0 +1,53 @@
From 79a2befb817a8bf5930d80ea6c2d237e74d16728 Mon Sep 17 00:00:00 2001
From: Markus Stockhausen <markus.stockhausen@gmx.de>
Date: Fri, 5 Jun 2026 18:19:12 +0200
Subject: [PATCH 5/8] irqchip/irq-realtek-rtl: Add mask for interrupt handling
When using multiple domains for the Interrupt controller, each one
must know which hardware interrupts it serves. Add a mask that is
filled during setup and apply it during interrupt handling.
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
---
drivers/irqchip/irq-realtek-rtl.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
--- a/drivers/irqchip/irq-realtek-rtl.c
+++ b/drivers/irqchip/irq-realtek-rtl.c
@@ -27,6 +27,7 @@
struct realtek_ictl_output {
struct irq_domain *domain;
+ u32 mask;
};
static DEFINE_RAW_SPINLOCK(irq_lock);
@@ -109,15 +110,17 @@ static struct irq_chip realtek_ictl_irq
.irq_set_affinity = realtek_ictl_irq_affinity,
};
-static int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
+static int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw_irq)
{
+ struct realtek_ictl_output *output = d->host_data;
unsigned int cpu;
irq_set_chip_and_handler(irq, &realtek_ictl_irq, handle_level_irq);
guard(raw_spinlock_irqsave)(&irq_lock);
+ output->mask |= BIT(hw_irq);
for_each_present_cpu(cpu)
- write_irr(cpu, hw, 1);
+ write_irr(cpu, hw_irq, 1);
return 0;
}
@@ -136,7 +139,7 @@ static void realtek_irq_dispatch(struct
unsigned int hw_irq;
chained_irq_enter(chip, desc);
- pending = readl(REG(cpu, RTL_ICTL_GIMR)) & readl(REG(cpu, RTL_ICTL_GISR));
+ pending = readl(REG(cpu, RTL_ICTL_GIMR)) & readl(REG(cpu, RTL_ICTL_GISR)) & output->mask;
if (unlikely(!pending)) {
spurious_interrupt();
@@ -0,0 +1,81 @@
From 4be0e039e8456b0e3111072ac9c66c1e62d1d8df Mon Sep 17 00:00:00 2001
From: Markus Stockhausen <markus.stockhausen@gmx.de>
Date: Fri, 5 Jun 2026 19:39:41 +0200
Subject: [PATCH 6/8] irqchip/irq-realtek-rtl: Add a select function
When working with multiple domains, the interrupt registration
must know to which domain it attaches. Add a select function that
takes care of the lookup. Logic is as follows.
If a device needs explicit parent routing it can request it by
giving an index as a second argument in the device tree. E.g.
intc: interrupt-controller@3000 {
...
interrupts = <2>, <3>, <4>, <5>, <6>, <7>;
};
uart1: uart@2100 {
...
interrupt-parent = <&intc>;
interrupts = <31 1>;
}
This way the serial console with hardware interrupt 31 will be
routed via SoC interrupt 3. If the second argument is not given,
the first parent interrupt of the controller is selected.
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
---
drivers/irqchip/irq-realtek-rtl.c | 24 ++++++++++++++++++++++--
1 file changed, 22 insertions(+), 2 deletions(-)
--- a/drivers/irqchip/irq-realtek-rtl.c
+++ b/drivers/irqchip/irq-realtek-rtl.c
@@ -26,7 +26,9 @@
#define REG(cpu, x) (realtek_ictl_base[cpu] + x)
struct realtek_ictl_output {
+ struct fwnode_handle *fwnode;
struct irq_domain *domain;
+ unsigned int index;
u32 mask;
};
@@ -125,9 +127,25 @@ static int intc_map(struct irq_domain *d
return 0;
}
+static int intc_select(struct irq_domain *d, struct irq_fwspec *fwspec,
+ enum irq_domain_bus_token bus_token)
+{
+ struct realtek_ictl_output *output = d->host_data;
+ unsigned int index = 0;
+
+ if (fwspec->fwnode != output->fwnode)
+ return false;
+
+ if (fwspec->param_count == 2)
+ index = fwspec->param[1];
+
+ return index == output->index;
+}
+
static const struct irq_domain_ops irq_domain_ops = {
- .map = intc_map,
- .xlate = irq_domain_xlate_onecell,
+ .map = intc_map,
+ .select = intc_select,
+ .xlate = irq_domain_xlate_onecell,
};
static void realtek_irq_dispatch(struct irq_desc *desc)
@@ -197,6 +215,8 @@ static int __init realtek_setup_parents(
}
output->domain = domain;
+ output->fwnode = of_fwnode_handle(node);
+ output->index = 0;
irq_set_chained_handler_and_data(parent_irq, realtek_irq_dispatch, output);
return 0;
@@ -0,0 +1,66 @@
From 18652e0d0527bc17542699d7dbdc14e4054acb2f Mon Sep 17 00:00:00 2001
From: Markus Stockhausen <markus.stockhausen@gmx.de>
Date: Fri, 5 Jun 2026 19:47:35 +0200
Subject: [PATCH 7/8] irqchip/irq-realtek-rtl: Allow shuffled interrupt order
The driver silently assumes that the first given interrupt in
the device tree is nailed to "2". Any deviation from this will
break the driver. Fix this by storing the given interrupt
in the domain data structure and writing the proper value
to the routing register.
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
---
drivers/irqchip/irq-realtek-rtl.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
--- a/drivers/irqchip/irq-realtek-rtl.c
+++ b/drivers/irqchip/irq-realtek-rtl.c
@@ -28,6 +28,8 @@
struct realtek_ictl_output {
struct fwnode_handle *fwnode;
struct irq_domain *domain;
+ unsigned int parent_irq;
+ unsigned int parent_hwirq;
unsigned int index;
u32 mask;
};
@@ -122,7 +124,7 @@ static int intc_map(struct irq_domain *d
guard(raw_spinlock_irqsave)(&irq_lock);
output->mask |= BIT(hw_irq);
for_each_present_cpu(cpu)
- write_irr(cpu, hw_irq, 1);
+ write_irr(cpu, hw_irq, output->parent_hwirq - 1);
return 0;
}
@@ -175,6 +177,7 @@ static int __init realtek_setup_parents(
{
int err, parent_irq, num_parents = of_irq_count(node);
struct realtek_ictl_output *output;
+ struct irq_data *parent_data;
struct of_phandle_args oirq;
struct irq_domain *domain;
@@ -207,6 +210,12 @@ static int __init realtek_setup_parents(
goto err_out;
}
+ parent_data = irq_get_irq_data(parent_irq);
+ if (!parent_data) {
+ err = -EINVAL;
+ goto err_out;
+ }
+
domain = irq_domain_create_linear(of_fwnode_handle(node), RTL_ICTL_NUM_INPUTS,
&irq_domain_ops, output);
if (!domain) {
@@ -217,6 +226,8 @@ static int __init realtek_setup_parents(
output->domain = domain;
output->fwnode = of_fwnode_handle(node);
output->index = 0;
+ output->parent_irq = parent_irq;
+ output->parent_hwirq = irqd_to_hwirq(parent_data);
irq_set_chained_handler_and_data(parent_irq, realtek_irq_dispatch, output);
return 0;
@@ -0,0 +1,128 @@
From 3031d8f878219f446330088193d799a3e67122d5 Mon Sep 17 00:00:00 2001
From: Markus Stockhausen <markus.stockhausen@gmx.de>
Date: Fri, 5 Jun 2026 20:49:47 +0200
Subject: [PATCH 8/8] irqchip/irq-realtek-rtl: Activate multiple parents
Until now the driver exactly registers a single parent interrupt.
Relax this to make use of all defined interrupts in the device tree.
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
---
drivers/irqchip/irq-realtek-rtl.c | 88 +++++++++++++++++--------------
1 file changed, 48 insertions(+), 40 deletions(-)
--- a/drivers/irqchip/irq-realtek-rtl.c
+++ b/drivers/irqchip/irq-realtek-rtl.c
@@ -175,64 +175,72 @@ out:
static int __init realtek_setup_parents(struct device_node *node)
{
- int err, parent_irq, num_parents = of_irq_count(node);
+ int p, cnt, err, parent_irq, num_parents = of_irq_count(node);
struct realtek_ictl_output *output;
struct irq_data *parent_data;
struct of_phandle_args oirq;
struct irq_domain *domain;
- output = kcalloc(1, sizeof(*output), GFP_KERNEL);
+ cnt = max(1, num_parents);
+ output = kcalloc(cnt, sizeof(*output), GFP_KERNEL);
if (!output)
return -ENOMEM;
- if (WARN_ON(!num_parents)) {
- /*
- * If DT contains no parent interrupts, assume MIPS IRQ 2 (HW0) is
- * connected to the first output. This is the case for all known hardware.
- */
- oirq.np = of_find_compatible_node(NULL, NULL,
- "mti,cpu-interrupt-controller");
- if (!oirq.np) {
- err = -EINVAL;
- goto err_out;
+ for (p = 0; p < cnt; p++) {
+ if (WARN_ON(!num_parents)) {
+ /*
+ * If DT contains no parent interrupts, assume MIPS IRQ 2 (HW0) is
+ * connected to the first output. This is the case for all known hardware.
+ */
+ oirq.np = of_find_compatible_node(NULL, NULL,
+ "mti,cpu-interrupt-controller");
+ if (!oirq.np) {
+ err = -EINVAL;
+ goto err_out;
+ }
+
+ oirq.args_count = 1;
+ oirq.args[0] = 2;
+ parent_irq = irq_create_of_mapping(&oirq);
+ of_node_put(oirq.np);
+ } else {
+ parent_irq = of_irq_get(node, p);
}
- oirq.args_count = 1;
- oirq.args[0] = 2;
- parent_irq = irq_create_of_mapping(&oirq);
- of_node_put(oirq.np);
- } else {
- parent_irq = of_irq_get(node, 0);
- }
+ if (parent_irq <= 0) {
+ err = parent_irq ? parent_irq : -ENODEV;
+ goto err_out;
+ }
- if (parent_irq <= 0) {
- err = parent_irq ? parent_irq : -ENODEV;
- goto err_out;
- }
+ parent_data = irq_get_irq_data(parent_irq);
+ if (!parent_data) {
+ err = -EINVAL;
+ goto err_out;
+ }
- parent_data = irq_get_irq_data(parent_irq);
- if (!parent_data) {
- err = -EINVAL;
- goto err_out;
- }
+ domain = irq_domain_create_linear(of_fwnode_handle(node), RTL_ICTL_NUM_INPUTS,
+ &irq_domain_ops, &output[p]);
+ if (!domain) {
+ err = -ENOMEM;
+ goto err_out;
+ }
- domain = irq_domain_create_linear(of_fwnode_handle(node), RTL_ICTL_NUM_INPUTS,
- &irq_domain_ops, output);
- if (!domain) {
- err = -ENOMEM;
- goto err_out;
+ output[p].domain = domain;
+ output[p].fwnode = of_fwnode_handle(node);
+ output[p].index = p;
+ output[p].parent_irq = parent_irq;
+ output[p].parent_hwirq = irqd_to_hwirq(parent_data);
+ irq_set_chained_handler_and_data(parent_irq, realtek_irq_dispatch, &output[p]);
}
- output->domain = domain;
- output->fwnode = of_fwnode_handle(node);
- output->index = 0;
- output->parent_irq = parent_irq;
- output->parent_hwirq = irqd_to_hwirq(parent_data);
- irq_set_chained_handler_and_data(parent_irq, realtek_irq_dispatch, output);
-
return 0;
err_out:
+ while (p--) {
+ irq_set_chained_handler_and_data(output[p].parent_irq, NULL, NULL);
+ irq_domain_remove(output[p].domain);
+ }
+
kfree(output);
return err;
@@ -1,428 +0,0 @@
From 6c18e9c491959ac0674ebe36b09f9ddc3f2c9bce Mon Sep 17 00:00:00 2001
From: Birger Koblitz <git@birger-koblitz.de>
Date: Fri, 31 Dec 2021 11:56:49 +0100
Subject: [PATCH] realtek: Add VPE support for the IRQ driver
In order to support VSMP, enable support for both VPEs of the RTL839X
and RTL930X SoCs in the irq-realtek-rtl driver. Add support for IRQ
affinity setting.
Up to kernel 5.15 this patch was divided into two parts
315-irqchip-irq-realtek-rtl-add-VPE-support.patch
319-irqchip-irq-realtek-rtl-fix-VPE-affinity.patch
As both parts will only work in combination they have been merged into
one patch.
Submitted-by: Birger Koblitz <git@birger-koblitz.de>
Submitted-by: INAGAKI Hiroshi <musashino.open@gmail.com>
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
---
drivers/irqchip/irq-realtek-rtl.c | 296 +++++++++++++++++++++++++-----
1 file changed, 249 insertions(+), 47 deletions(-)
--- a/drivers/irqchip/irq-realtek-rtl.c
+++ b/drivers/irqchip/irq-realtek-rtl.c
@@ -22,22 +22,58 @@
#define RTL_ICTL_IRR3 0x14
#define RTL_ICTL_NUM_INPUTS 32
-
-#define REG(x) (realtek_ictl_base + x)
+#define RTL_ICTL_NUM_OUTPUTS 15
static DEFINE_RAW_SPINLOCK(irq_lock);
-static void __iomem *realtek_ictl_base;
+
+#define REG(offset, cpu) (realtek_ictl_base[cpu] + offset)
+
+static u32 realtek_ictl_unmask[NR_CPUS];
+static void __iomem *realtek_ictl_base[NR_CPUS];
+static cpumask_t realtek_ictl_cpu_configurable;
+
+struct realtek_ictl_output {
+ /* IRQ controller data */
+ struct fwnode_handle *fwnode;
+ /* Output specific data */
+ unsigned int output_index;
+ struct irq_domain *domain;
+ u32 child_mask;
+};
/*
- * IRR0-IRR3 store 4 bits per interrupt, but Realtek uses inverted numbering,
- * placing IRQ 31 in the first four bits. A routing value of '0' means the
- * interrupt is left disconnected. Routing values {1..15} connect to output
- * lines {0..14}.
+ * Per CPU we have a set of 5 registers that determine interrupt handling for
+ * 32 external interrupts. GIMR (enable/disable interrupt) plus IRR0-IRR3 that
+ * contain "routing" or "priority" values. GIMR uses one bit for each interrupt
+ * and IRRx store 4 bits per interrupt. Realtek uses inverted numbering,
+ * placing IRQ 31 in the first four bits. The register combinations give the
+ * following results for a single interrupt in the wild:
+ *
+ * a) GIMR = 0 / IRRx > 0 -> no interrupts
+ * b) GIMR = 0 / IRRx = 0 -> no interrupts
+ * c) GIMR = 1 / IRRx > 0 -> interrupts
+ * d) GIMR = 1 / IRRx = 0 -> rare interrupts in SMP environment
+ *
+ * Combination d) seems to trigger interrupts only on a VPE if the other VPE
+ * has GIMR = 0 and IRRx > 0. E.g. busy without interrupts allowed. To provide
+ * IRQ balancing features in SMP this driver will handle the registers as
+ * follows:
+ *
+ * 1) set IRRx > 0 for VPE where the interrupt is desired
+ * 2) set IRRx = 0 for VPE where the interrupt is not desired
+ * 3) set both GIMR = 0 to mask (disabled) interrupt
+ * 4) set GIMR = 1 to unmask (enable) interrupt but only for VPE where IRRx > 0
*/
+
#define IRR_OFFSET(idx) (4 * (3 - (idx * 4) / 32))
#define IRR_SHIFT(idx) ((idx * 4) % 32)
-static void write_irr(void __iomem *irr0, int idx, u32 value)
+static inline u32 read_irr(void __iomem *irr0, int idx)
+{
+ return (readl(irr0 + IRR_OFFSET(idx)) >> IRR_SHIFT(idx)) & 0xf;
+}
+
+static inline void write_irr(void __iomem *irr0, int idx, u32 value)
{
unsigned int offset = IRR_OFFSET(idx);
unsigned int shift = IRR_SHIFT(idx);
@@ -48,16 +84,33 @@ static void write_irr(void __iomem *irr0
writel(irr, irr0 + offset);
}
+static inline void enable_gimr(int hwirq, int cpu)
+{
+ u32 value;
+
+ value = readl(REG(RTL_ICTL_GIMR, cpu));
+ value |= (BIT(hwirq) & realtek_ictl_unmask[cpu]);
+ writel(value, REG(RTL_ICTL_GIMR, cpu));
+}
+
+static inline void disable_gimr(int hwirq, int cpu)
+{
+ u32 value;
+
+ value = readl(REG(RTL_ICTL_GIMR, cpu));
+ value &= ~BIT(hwirq);
+ writel(value, REG(RTL_ICTL_GIMR, cpu));
+}
+
static void realtek_ictl_unmask_irq(struct irq_data *i)
{
unsigned long flags;
- u32 value;
+ int cpu;
raw_spin_lock_irqsave(&irq_lock, flags);
- value = readl(REG(RTL_ICTL_GIMR));
- value |= BIT(i->hwirq);
- writel(value, REG(RTL_ICTL_GIMR));
+ for_each_cpu(cpu, &realtek_ictl_cpu_configurable)
+ enable_gimr(i->hwirq, cpu);
raw_spin_unlock_irqrestore(&irq_lock, flags);
}
@@ -65,110 +118,261 @@ static void realtek_ictl_unmask_irq(stru
static void realtek_ictl_mask_irq(struct irq_data *i)
{
unsigned long flags;
- u32 value;
+ int cpu;
raw_spin_lock_irqsave(&irq_lock, flags);
- value = readl(REG(RTL_ICTL_GIMR));
- value &= ~BIT(i->hwirq);
- writel(value, REG(RTL_ICTL_GIMR));
+ for_each_cpu(cpu, &realtek_ictl_cpu_configurable)
+ disable_gimr(i->hwirq, cpu);
raw_spin_unlock_irqrestore(&irq_lock, flags);
}
+static int __maybe_unused realtek_ictl_irq_affinity(struct irq_data *i,
+ const struct cpumask *dest,
+ bool force)
+{
+ struct realtek_ictl_output *output = i->domain->host_data;
+ cpumask_t cpu_configure;
+ cpumask_t cpu_disable;
+ cpumask_t cpu_enable;
+ unsigned long flags;
+ int cpu;
+
+ raw_spin_lock_irqsave(&irq_lock, flags);
+
+ cpumask_and(&cpu_configure, cpu_present_mask, &realtek_ictl_cpu_configurable);
+
+ cpumask_and(&cpu_enable, &cpu_configure, dest);
+ cpumask_andnot(&cpu_disable, &cpu_configure, dest);
+
+ for_each_cpu(cpu, &cpu_disable) {
+ write_irr(REG(RTL_ICTL_IRR0, cpu), i->hwirq, 0);
+ realtek_ictl_unmask[cpu] &= ~BIT(i->hwirq);
+ disable_gimr(i->hwirq, cpu);
+ }
+
+ for_each_cpu(cpu, &cpu_enable) {
+ write_irr(REG(RTL_ICTL_IRR0, cpu), i->hwirq, output->output_index + 1);
+ realtek_ictl_unmask[cpu] |= BIT(i->hwirq);
+ enable_gimr(i->hwirq, cpu);
+ }
+
+ irq_data_update_effective_affinity(i, &cpu_enable);
+
+ raw_spin_unlock_irqrestore(&irq_lock, flags);
+
+ return IRQ_SET_MASK_OK;
+}
+
static struct irq_chip realtek_ictl_irq = {
.name = "realtek-rtl-intc",
.irq_mask = realtek_ictl_mask_irq,
.irq_unmask = realtek_ictl_unmask_irq,
+#ifdef CONFIG_SMP
+ .irq_set_affinity = realtek_ictl_irq_affinity,
+#endif
};
static int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
{
+ struct realtek_ictl_output *output = d->host_data;
unsigned long flags;
irq_set_chip_and_handler(irq, &realtek_ictl_irq, handle_level_irq);
raw_spin_lock_irqsave(&irq_lock, flags);
- write_irr(REG(RTL_ICTL_IRR0), hw, 1);
+
+ output->child_mask |= BIT(hw);
+ write_irr(REG(RTL_ICTL_IRR0, 0), hw, output->output_index + 1);
+ realtek_ictl_unmask[0] |= BIT(hw);
+
raw_spin_unlock_irqrestore(&irq_lock, flags);
return 0;
}
+static int intc_select(struct irq_domain *d, struct irq_fwspec *fwspec,
+ enum irq_domain_bus_token bus_token)
+{
+ struct realtek_ictl_output *output = d->host_data;
+ bool routed_elsewhere;
+ unsigned long flags;
+ u32 routing_old;
+ int cpu;
+
+ if (fwspec->fwnode != output->fwnode)
+ return false;
+
+ /* Original specifiers had only one parameter */
+ if (fwspec->param_count < 2)
+ return true;
+
+ raw_spin_lock_irqsave(&irq_lock, flags);
+
+ /*
+ * Inputs can only be routed to one output, so they shouldn't be
+ * allowed to end up in multiple domains.
+ */
+ for_each_cpu(cpu, &realtek_ictl_cpu_configurable) {
+ routing_old = read_irr(REG(RTL_ICTL_IRR0, cpu), fwspec->param[0]);
+ routed_elsewhere = routing_old && fwspec->param[1] != routing_old - 1;
+ if (routed_elsewhere) {
+ pr_warn("soc int %d already routed to output %d\n",
+ fwspec->param[0], routing_old - 1);
+ break;
+ }
+ }
+
+ raw_spin_unlock_irqrestore(&irq_lock, flags);
+
+ return !routed_elsewhere && fwspec->param[1] == output->output_index;
+}
+
static const struct irq_domain_ops irq_domain_ops = {
.map = intc_map,
+ .select = intc_select,
.xlate = irq_domain_xlate_onecell,
};
static void realtek_irq_dispatch(struct irq_desc *desc)
{
+ struct realtek_ictl_output *output = irq_desc_get_handler_data(desc);
struct irq_chip *chip = irq_desc_get_chip(desc);
- struct irq_domain *domain;
+ int cpu = smp_processor_id();
unsigned long pending;
unsigned int soc_int;
chained_irq_enter(chip, desc);
- pending = readl(REG(RTL_ICTL_GIMR)) & readl(REG(RTL_ICTL_GISR));
+ pending = readl(REG(RTL_ICTL_GIMR, cpu)) & readl(REG(RTL_ICTL_GISR, cpu))
+ & output->child_mask;
if (unlikely(!pending)) {
spurious_interrupt();
goto out;
}
- domain = irq_desc_get_handler_data(desc);
- for_each_set_bit(soc_int, &pending, 32)
- generic_handle_domain_irq(domain, soc_int);
+ for_each_set_bit(soc_int, &pending, RTL_ICTL_NUM_INPUTS)
+ generic_handle_domain_irq(output->domain, soc_int);
out:
chained_irq_exit(chip, desc);
}
+/*
+ * SoC interrupts are cascaded to MIPS CPU interrupts according to the
+ * interrupt-map in the device tree. Each SoC interrupt gets 4 bits for
+ * the CPU interrupt in an Interrupt Routing Register. Max 32 SoC interrupts
+ * thus go into 4 IRRs. A routing value of '0' means the interrupt is left
+ * disconnected. Routing values {1..15} connect to output lines {0..14}.
+ */
+static int __init setup_parent_interrupts(struct device_node *node, int *parents,
+ unsigned int num_parents)
+{
+ struct realtek_ictl_output *outputs;
+ struct realtek_ictl_output *output;
+ struct irq_domain *domain;
+ unsigned int p;
+
+ outputs = kcalloc(num_parents, sizeof(*outputs), GFP_KERNEL);
+ if (!outputs)
+ return -ENOMEM;
+
+ for (p = 0; p < num_parents; p++) {
+ output = outputs + p;
+
+ domain = irq_domain_add_linear(node, RTL_ICTL_NUM_INPUTS, &irq_domain_ops, output);
+ if (!domain)
+ goto domain_err;
+
+ output->fwnode = of_fwnode_handle(node);
+ output->output_index = p;
+ output->domain = domain;
+
+ irq_set_chained_handler_and_data(parents[p], realtek_irq_dispatch, output);
+ }
+
+ return 0;
+
+domain_err:
+ while (p--) {
+ irq_set_chained_handler_and_data(parents[p], NULL, NULL);
+ irq_domain_remove(outputs[p].domain);
+ }
+
+ kfree(outputs);
+
+ return -ENOMEM;
+}
+
static int __init realtek_rtl_of_init(struct device_node *node, struct device_node *parent)
{
+ int parent_irqs[RTL_ICTL_NUM_OUTPUTS];
struct of_phandle_args oirq;
- struct irq_domain *domain;
+ unsigned int num_parents;
unsigned int soc_irq;
- int parent_irq;
+ unsigned int p;
+ int cpu;
- realtek_ictl_base = of_iomap(node, 0);
- if (!realtek_ictl_base)
+ cpumask_clear(&realtek_ictl_cpu_configurable);
+
+ for (cpu = 0; cpu < NR_CPUS; cpu++) {
+ realtek_ictl_base[cpu] = of_iomap(node, cpu);
+ if (realtek_ictl_base[cpu]) {
+ cpumask_set_cpu(cpu, &realtek_ictl_cpu_configurable);
+
+ /* Disable all cascaded interrupts and clear routing */
+ for (soc_irq = 0; soc_irq < RTL_ICTL_NUM_INPUTS; soc_irq++) {
+ write_irr(REG(RTL_ICTL_IRR0, cpu), soc_irq, 0);
+ realtek_ictl_unmask[cpu] &= ~BIT(soc_irq);
+ disable_gimr(soc_irq, cpu);
+ }
+ }
+ }
+
+ if (cpumask_empty(&realtek_ictl_cpu_configurable))
return -ENXIO;
- /* Disable all cascaded interrupts and clear routing */
- writel(0, REG(RTL_ICTL_GIMR));
- for (soc_irq = 0; soc_irq < RTL_ICTL_NUM_INPUTS; soc_irq++)
- write_irr(REG(RTL_ICTL_IRR0), soc_irq, 0);
+ num_parents = of_irq_count(node);
+ if (num_parents > RTL_ICTL_NUM_OUTPUTS) {
+ pr_err("too many parent interrupts\n");
+ return -EINVAL;
+ }
+
+ for (p = 0; p < num_parents; p++)
+ parent_irqs[p] = of_irq_get(node, p);
- if (WARN_ON(!of_irq_count(node))) {
+ if (WARN_ON(!num_parents)) {
/*
* If DT contains no parent interrupts, assume MIPS CPU IRQ 2
* (HW0) is connected to the first output. This is the case for
* all known hardware anyway. "interrupt-map" is deprecated, so
* don't bother trying to parse that.
+ * Since this is to account for old devicetrees with one-cell
+ * interrupt specifiers, only one output domain is needed.
*/
oirq.np = of_find_compatible_node(NULL, NULL, "mti,cpu-interrupt-controller");
- oirq.args_count = 1;
- oirq.args[0] = 2;
- parent_irq = irq_create_of_mapping(&oirq);
+ if (oirq.np) {
+ oirq.args_count = 1;
+ oirq.args[0] = 2;
+
+ parent_irqs[0] = irq_create_of_mapping(&oirq);
+ num_parents = 1;
+ }
of_node_put(oirq.np);
- } else {
- parent_irq = of_irq_get(node, 0);
}
- if (parent_irq < 0)
- return parent_irq;
- else if (!parent_irq)
- return -ENODEV;
-
- domain = irq_domain_create_linear(of_fwnode_handle(node), RTL_ICTL_NUM_INPUTS, &irq_domain_ops, NULL);
- if (!domain)
- return -ENOMEM;
-
- irq_set_chained_handler_and_data(parent_irq, realtek_irq_dispatch, domain);
+ /* Ensure we haven't collected any errors before proceeding */
+ for (p = 0; p < num_parents; p++) {
+ if (parent_irqs[p] < 0)
+ return parent_irqs[p];
+ if (!parent_irqs[p])
+ return -ENODEV;
+ }
- return 0;
+ return setup_parent_interrupts(node, &parent_irqs[0], num_parents);
}
IRQCHIP_DECLARE(realtek_rtl_intc, "realtek,rtl-intc", realtek_rtl_of_init);