* [PATCH v2 1/5] irqchip/irq-vt8500: Split up ack/mask functions
2025-05-06 12:46 [PATCH v2 0/5] irqchip/irq-vt8500: Cleanups and fixes for the irq-vt8500 driver Alexey Charkov
@ 2025-05-06 12:46 ` Alexey Charkov
2025-05-06 14:04 ` [tip: irq/drivers] " tip-bot2 for Alexey Charkov
2025-05-06 12:46 ` [PATCH v2 2/5] irqchip/irq-vt8500: Drop redundant copy of the device node pointer Alexey Charkov
` (3 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Alexey Charkov @ 2025-05-06 12:46 UTC (permalink / raw)
To: Thomas Gleixner, Krzysztof Kozlowski
Cc: linux-kernel, linux-arm-kernel, Alexey Charkov
Original vt8500_irq_mask function really did the ack for edge
triggered interrupts and the mask for level triggered interrupts.
Edge triggered interrupts never really got masked as a result,
and there was unnecessary reading of the status register before
the ack even though it's write-one-to-clear.
Split it up into a proper standalone vt8500_irq_ack and an
unconditional vt8500_irq_mask.
No Fixes tag added, as it has survived this way for 15 years and
nobody complained, so apparently nothing really used edge triggered
interrupts anyway.
Signed-off-by: Alexey Charkov <alchark@gmail.com>
---
drivers/irqchip/irq-vt8500.c | 32 ++++++++++++++++----------------
1 file changed, 16 insertions(+), 16 deletions(-)
diff --git a/drivers/irqchip/irq-vt8500.c b/drivers/irqchip/irq-vt8500.c
index e17dd3a8c2d5a488fedfdea55de842177c314baa..d0580f6577c88ffd7e374d640418d1fc23db623e 100644
--- a/drivers/irqchip/irq-vt8500.c
+++ b/drivers/irqchip/irq-vt8500.c
@@ -67,25 +67,25 @@ struct vt8500_irq_data {
static struct vt8500_irq_data intc[VT8500_INTC_MAX];
static u32 active_cnt = 0;
-static void vt8500_irq_mask(struct irq_data *d)
+static void vt8500_irq_ack(struct irq_data *d)
{
struct vt8500_irq_data *priv = d->domain->host_data;
void __iomem *base = priv->base;
void __iomem *stat_reg = base + VT8500_ICIS + (d->hwirq < 32 ? 0 : 4);
- u8 edge, dctr;
- u32 status;
-
- edge = readb(base + VT8500_ICDC + d->hwirq) & VT8500_EDGE;
- if (edge) {
- status = readl(stat_reg);
-
- status |= (1 << (d->hwirq & 0x1f));
- writel(status, stat_reg);
- } else {
- dctr = readb(base + VT8500_ICDC + d->hwirq);
- dctr &= ~VT8500_INT_ENABLE;
- writeb(dctr, base + VT8500_ICDC + d->hwirq);
- }
+ u32 status = (1 << (d->hwirq & 0x1f));
+
+ writel(status, stat_reg);
+}
+
+static void vt8500_irq_mask(struct irq_data *d)
+{
+ struct vt8500_irq_data *priv = d->domain->host_data;
+ void __iomem *base = priv->base;
+ u8 dctr;
+
+ dctr = readb(base + VT8500_ICDC + d->hwirq);
+ dctr &= ~VT8500_INT_ENABLE;
+ writeb(dctr, base + VT8500_ICDC + d->hwirq);
}
static void vt8500_irq_unmask(struct irq_data *d)
@@ -131,7 +131,7 @@ static int vt8500_irq_set_type(struct irq_data *d, unsigned int flow_type)
static struct irq_chip vt8500_irq_chip = {
.name = "vt8500",
- .irq_ack = vt8500_irq_mask,
+ .irq_ack = vt8500_irq_ack,
.irq_mask = vt8500_irq_mask,
.irq_unmask = vt8500_irq_unmask,
.irq_set_type = vt8500_irq_set_type,
--
2.49.0
^ permalink raw reply [flat|nested] 11+ messages in thread* [tip: irq/drivers] irqchip/irq-vt8500: Split up ack/mask functions
2025-05-06 12:46 ` [PATCH v2 1/5] irqchip/irq-vt8500: Split up ack/mask functions Alexey Charkov
@ 2025-05-06 14:04 ` tip-bot2 for Alexey Charkov
0 siblings, 0 replies; 11+ messages in thread
From: tip-bot2 for Alexey Charkov @ 2025-05-06 14:04 UTC (permalink / raw)
To: linux-tip-commits; +Cc: Alexey Charkov, Thomas Gleixner, x86, linux-kernel
The following commit has been merged into the irq/drivers branch of tip:
Commit-ID: 22111fdf11666e4ff2bb80481a874a6b958323f2
Gitweb: https://git.kernel.org/tip/22111fdf11666e4ff2bb80481a874a6b958323f2
Author: Alexey Charkov <alchark@gmail.com>
AuthorDate: Tue, 06 May 2025 16:46:14 +04:00
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Tue, 06 May 2025 15:58:26 +02:00
irqchip/irq-vt8500: Split up ack/mask functions
vt8500_irq_mask() really does the ACK for edge triggered interrupts and the
MASK for level triggered interrupts. Edge triggered interrupts never
really are masked as a result, and there is unnecessary reading of the
status register before the ACK even though it's write-one-to-clear.
Split it up into a proper standalone vt8500_irq_ack() and an unconditional
vt8500_irq_mask().
No Fixes tag added, as it has survived this way for 15 years and nobody
complained, so apparently nothing really used edge triggered interrupts
anyway.
[ tglx: Tabularize the irqchip struct initializer ]
Signed-off-by: Alexey Charkov <alchark@gmail.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20250506-vt8500-intc-updates-v2-1-a3a0606cf92d@gmail.com
---
drivers/irqchip/irq-vt8500.c | 40 +++++++++++++++++------------------
1 file changed, 20 insertions(+), 20 deletions(-)
diff --git a/drivers/irqchip/irq-vt8500.c b/drivers/irqchip/irq-vt8500.c
index e17dd3a..9fb9d37 100644
--- a/drivers/irqchip/irq-vt8500.c
+++ b/drivers/irqchip/irq-vt8500.c
@@ -67,25 +67,25 @@ struct vt8500_irq_data {
static struct vt8500_irq_data intc[VT8500_INTC_MAX];
static u32 active_cnt = 0;
-static void vt8500_irq_mask(struct irq_data *d)
+static void vt8500_irq_ack(struct irq_data *d)
{
struct vt8500_irq_data *priv = d->domain->host_data;
void __iomem *base = priv->base;
void __iomem *stat_reg = base + VT8500_ICIS + (d->hwirq < 32 ? 0 : 4);
- u8 edge, dctr;
- u32 status;
-
- edge = readb(base + VT8500_ICDC + d->hwirq) & VT8500_EDGE;
- if (edge) {
- status = readl(stat_reg);
-
- status |= (1 << (d->hwirq & 0x1f));
- writel(status, stat_reg);
- } else {
- dctr = readb(base + VT8500_ICDC + d->hwirq);
- dctr &= ~VT8500_INT_ENABLE;
- writeb(dctr, base + VT8500_ICDC + d->hwirq);
- }
+ u32 status = (1 << (d->hwirq & 0x1f));
+
+ writel(status, stat_reg);
+}
+
+static void vt8500_irq_mask(struct irq_data *d)
+{
+ struct vt8500_irq_data *priv = d->domain->host_data;
+ void __iomem *base = priv->base;
+ u8 dctr;
+
+ dctr = readb(base + VT8500_ICDC + d->hwirq);
+ dctr &= ~VT8500_INT_ENABLE;
+ writeb(dctr, base + VT8500_ICDC + d->hwirq);
}
static void vt8500_irq_unmask(struct irq_data *d)
@@ -130,11 +130,11 @@ static int vt8500_irq_set_type(struct irq_data *d, unsigned int flow_type)
}
static struct irq_chip vt8500_irq_chip = {
- .name = "vt8500",
- .irq_ack = vt8500_irq_mask,
- .irq_mask = vt8500_irq_mask,
- .irq_unmask = vt8500_irq_unmask,
- .irq_set_type = vt8500_irq_set_type,
+ .name = "vt8500",
+ .irq_ack = vt8500_irq_ack,
+ .irq_mask = vt8500_irq_mask,
+ .irq_unmask = vt8500_irq_unmask,
+ .irq_set_type = vt8500_irq_set_type,
};
static void __init vt8500_init_irq_hw(void __iomem *base)
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 2/5] irqchip/irq-vt8500: Drop redundant copy of the device node pointer
2025-05-06 12:46 [PATCH v2 0/5] irqchip/irq-vt8500: Cleanups and fixes for the irq-vt8500 driver Alexey Charkov
2025-05-06 12:46 ` [PATCH v2 1/5] irqchip/irq-vt8500: Split up ack/mask functions Alexey Charkov
@ 2025-05-06 12:46 ` Alexey Charkov
2025-05-06 14:04 ` [tip: irq/drivers] " tip-bot2 for Alexey Charkov
2025-05-06 12:46 ` [PATCH v2 3/5] irqchip/irq-vt8500: Don't require 8 interrupts from a chained controller Alexey Charkov
` (2 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Alexey Charkov @ 2025-05-06 12:46 UTC (permalink / raw)
To: Thomas Gleixner, Krzysztof Kozlowski
Cc: linux-kernel, linux-arm-kernel, Alexey Charkov
Inside vt8500_irq_init, np is the same as node. Drop it.
Signed-off-by: Alexey Charkov <alchark@gmail.com>
---
drivers/irqchip/irq-vt8500.c | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/drivers/irqchip/irq-vt8500.c b/drivers/irqchip/irq-vt8500.c
index d0580f6577c88ffd7e374d640418d1fc23db623e..6d46e1a0fda953d76679ad2318674fdf0a977f0b 100644
--- a/drivers/irqchip/irq-vt8500.c
+++ b/drivers/irqchip/irq-vt8500.c
@@ -191,7 +191,6 @@ static int __init vt8500_irq_init(struct device_node *node,
struct device_node *parent)
{
int irq, i;
- struct device_node *np = node;
if (active_cnt == VT8500_INTC_MAX) {
pr_err("%s: Interrupt controllers > VT8500_INTC_MAX\n",
@@ -199,7 +198,7 @@ static int __init vt8500_irq_init(struct device_node *node,
goto out;
}
- intc[active_cnt].base = of_iomap(np, 0);
+ intc[active_cnt].base = of_iomap(node, 0);
intc[active_cnt].domain = irq_domain_add_linear(node, 64,
&vt8500_irq_domain_ops, &intc[active_cnt]);
@@ -222,16 +221,16 @@ static int __init vt8500_irq_init(struct device_node *node,
active_cnt++;
/* check if this is a slaved controller */
- if (of_irq_count(np) != 0) {
+ if (of_irq_count(node) != 0) {
/* check that we have the correct number of interrupts */
- if (of_irq_count(np) != 8) {
+ if (of_irq_count(node) != 8) {
pr_err("%s: Incorrect IRQ map for slaved controller\n",
__func__);
return -EINVAL;
}
for (i = 0; i < 8; i++) {
- irq = irq_of_parse_and_map(np, i);
+ irq = irq_of_parse_and_map(node, i);
enable_irq(irq);
}
--
2.49.0
^ permalink raw reply [flat|nested] 11+ messages in thread* [tip: irq/drivers] irqchip/irq-vt8500: Drop redundant copy of the device node pointer
2025-05-06 12:46 ` [PATCH v2 2/5] irqchip/irq-vt8500: Drop redundant copy of the device node pointer Alexey Charkov
@ 2025-05-06 14:04 ` tip-bot2 for Alexey Charkov
0 siblings, 0 replies; 11+ messages in thread
From: tip-bot2 for Alexey Charkov @ 2025-05-06 14:04 UTC (permalink / raw)
To: linux-tip-commits; +Cc: Alexey Charkov, Thomas Gleixner, x86, linux-kernel
The following commit has been merged into the irq/drivers branch of tip:
Commit-ID: 45453df7f69b0157a14478f635c99216821faeee
Gitweb: https://git.kernel.org/tip/45453df7f69b0157a14478f635c99216821faeee
Author: Alexey Charkov <alchark@gmail.com>
AuthorDate: Tue, 06 May 2025 16:46:15 +04:00
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Tue, 06 May 2025 15:58:26 +02:00
irqchip/irq-vt8500: Drop redundant copy of the device node pointer
Inside vt8500_irq_init(), np is the same as node. Drop it.
Signed-off-by: Alexey Charkov <alchark@gmail.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20250506-vt8500-intc-updates-v2-2-a3a0606cf92d@gmail.com
---
drivers/irqchip/irq-vt8500.c | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/drivers/irqchip/irq-vt8500.c b/drivers/irqchip/irq-vt8500.c
index 9fb9d37..c88aa64 100644
--- a/drivers/irqchip/irq-vt8500.c
+++ b/drivers/irqchip/irq-vt8500.c
@@ -191,7 +191,6 @@ static int __init vt8500_irq_init(struct device_node *node,
struct device_node *parent)
{
int irq, i;
- struct device_node *np = node;
if (active_cnt == VT8500_INTC_MAX) {
pr_err("%s: Interrupt controllers > VT8500_INTC_MAX\n",
@@ -199,7 +198,7 @@ static int __init vt8500_irq_init(struct device_node *node,
goto out;
}
- intc[active_cnt].base = of_iomap(np, 0);
+ intc[active_cnt].base = of_iomap(node, 0);
intc[active_cnt].domain = irq_domain_add_linear(node, 64,
&vt8500_irq_domain_ops, &intc[active_cnt]);
@@ -222,16 +221,16 @@ static int __init vt8500_irq_init(struct device_node *node,
active_cnt++;
/* check if this is a slaved controller */
- if (of_irq_count(np) != 0) {
+ if (of_irq_count(node) != 0) {
/* check that we have the correct number of interrupts */
- if (of_irq_count(np) != 8) {
+ if (of_irq_count(node) != 8) {
pr_err("%s: Incorrect IRQ map for slaved controller\n",
__func__);
return -EINVAL;
}
for (i = 0; i < 8; i++) {
- irq = irq_of_parse_and_map(np, i);
+ irq = irq_of_parse_and_map(node, i);
enable_irq(irq);
}
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 3/5] irqchip/irq-vt8500: Don't require 8 interrupts from a chained controller
2025-05-06 12:46 [PATCH v2 0/5] irqchip/irq-vt8500: Cleanups and fixes for the irq-vt8500 driver Alexey Charkov
2025-05-06 12:46 ` [PATCH v2 1/5] irqchip/irq-vt8500: Split up ack/mask functions Alexey Charkov
2025-05-06 12:46 ` [PATCH v2 2/5] irqchip/irq-vt8500: Drop redundant copy of the device node pointer Alexey Charkov
@ 2025-05-06 12:46 ` Alexey Charkov
2025-05-06 14:04 ` [tip: irq/drivers] " tip-bot2 for Alexey Charkov
2025-05-06 12:46 ` [PATCH v2 4/5] irqchip/irq-vt8500: Use a dedicated chained handler function Alexey Charkov
2025-05-06 12:46 ` [PATCH v2 5/5] irqchip/irq-vt8500: Use fewer global variables and add error handling Alexey Charkov
4 siblings, 1 reply; 11+ messages in thread
From: Alexey Charkov @ 2025-05-06 12:46 UTC (permalink / raw)
To: Thomas Gleixner, Krzysztof Kozlowski
Cc: linux-kernel, linux-arm-kernel, Alexey Charkov
VT8500 chained controller can route its interrupts to either or all
of its 8 interrupt outputs. Current code actually routes all of them
to the first output, so there is no need to create mappings for all
eight.
Drop redundant checks, and only map as many chained controller
interrupts as are defined in the device tree.
Signed-off-by: Alexey Charkov <alchark@gmail.com>
---
drivers/irqchip/irq-vt8500.c | 11 ++---------
1 file changed, 2 insertions(+), 9 deletions(-)
diff --git a/drivers/irqchip/irq-vt8500.c b/drivers/irqchip/irq-vt8500.c
index 6d46e1a0fda953d76679ad2318674fdf0a977f0b..cf24a88f52d1b90f033d429288c88398439b92d1 100644
--- a/drivers/irqchip/irq-vt8500.c
+++ b/drivers/irqchip/irq-vt8500.c
@@ -220,16 +220,9 @@ static int __init vt8500_irq_init(struct device_node *node,
active_cnt++;
- /* check if this is a slaved controller */
+ /* check if this is a chained controller */
if (of_irq_count(node) != 0) {
- /* check that we have the correct number of interrupts */
- if (of_irq_count(node) != 8) {
- pr_err("%s: Incorrect IRQ map for slaved controller\n",
- __func__);
- return -EINVAL;
- }
-
- for (i = 0; i < 8; i++) {
+ for (i = 0; i < of_irq_count(node); i++) {
irq = irq_of_parse_and_map(node, i);
enable_irq(irq);
}
--
2.49.0
^ permalink raw reply [flat|nested] 11+ messages in thread* [tip: irq/drivers] irqchip/irq-vt8500: Don't require 8 interrupts from a chained controller
2025-05-06 12:46 ` [PATCH v2 3/5] irqchip/irq-vt8500: Don't require 8 interrupts from a chained controller Alexey Charkov
@ 2025-05-06 14:04 ` tip-bot2 for Alexey Charkov
0 siblings, 0 replies; 11+ messages in thread
From: tip-bot2 for Alexey Charkov @ 2025-05-06 14:04 UTC (permalink / raw)
To: linux-tip-commits; +Cc: Alexey Charkov, Thomas Gleixner, x86, linux-kernel
The following commit has been merged into the irq/drivers branch of tip:
Commit-ID: 54a1f3eb89ded8114b0bffc3696757cd95665ef9
Gitweb: https://git.kernel.org/tip/54a1f3eb89ded8114b0bffc3696757cd95665ef9
Author: Alexey Charkov <alchark@gmail.com>
AuthorDate: Tue, 06 May 2025 16:46:16 +04:00
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Tue, 06 May 2025 15:58:26 +02:00
irqchip/irq-vt8500: Don't require 8 interrupts from a chained controller
VT8500 chained controller can route its interrupts to either or all
of its 8 interrupt outputs. Current code actually routes all of them
to the first output, so there is no need to create mappings for all
eight.
Drop redundant checks, and only map as many chained controller
interrupts as are defined in the device tree.
Signed-off-by: Alexey Charkov <alchark@gmail.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20250506-vt8500-intc-updates-v2-3-a3a0606cf92d@gmail.com
---
drivers/irqchip/irq-vt8500.c | 11 ++---------
1 file changed, 2 insertions(+), 9 deletions(-)
diff --git a/drivers/irqchip/irq-vt8500.c b/drivers/irqchip/irq-vt8500.c
index c88aa64..debca89 100644
--- a/drivers/irqchip/irq-vt8500.c
+++ b/drivers/irqchip/irq-vt8500.c
@@ -220,16 +220,9 @@ static int __init vt8500_irq_init(struct device_node *node,
active_cnt++;
- /* check if this is a slaved controller */
+ /* check if this is a chained controller */
if (of_irq_count(node) != 0) {
- /* check that we have the correct number of interrupts */
- if (of_irq_count(node) != 8) {
- pr_err("%s: Incorrect IRQ map for slaved controller\n",
- __func__);
- return -EINVAL;
- }
-
- for (i = 0; i < 8; i++) {
+ for (i = 0; i < of_irq_count(node); i++) {
irq = irq_of_parse_and_map(node, i);
enable_irq(irq);
}
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 4/5] irqchip/irq-vt8500: Use a dedicated chained handler function
2025-05-06 12:46 [PATCH v2 0/5] irqchip/irq-vt8500: Cleanups and fixes for the irq-vt8500 driver Alexey Charkov
` (2 preceding siblings ...)
2025-05-06 12:46 ` [PATCH v2 3/5] irqchip/irq-vt8500: Don't require 8 interrupts from a chained controller Alexey Charkov
@ 2025-05-06 12:46 ` Alexey Charkov
2025-05-06 14:04 ` [tip: irq/drivers] " tip-bot2 for Alexey Charkov
2025-05-06 12:46 ` [PATCH v2 5/5] irqchip/irq-vt8500: Use fewer global variables and add error handling Alexey Charkov
4 siblings, 1 reply; 11+ messages in thread
From: Alexey Charkov @ 2025-05-06 12:46 UTC (permalink / raw)
To: Thomas Gleixner, Krzysztof Kozlowski
Cc: linux-kernel, linux-arm-kernel, Alexey Charkov
Current code for the chained interrupt controller maps its interrupts
on the parent but doesn't register a separate chained handler, instead
needlessly calling enable_irq on an unactivated parent interrupt, causing
a boot time WARN_ON from the common code.
The common handler meanwhile loops through all registered interrupt
controllers in an arbitrary order and tries to handle active interrupts
in each of them, which is fragile.
Use common infrastructure for handling chained interrupts instead.
Signed-off-by: Alexey Charkov <alchark@gmail.com>
---
drivers/irqchip/irq-vt8500.c | 60 ++++++++++++++++++++++++++++----------------
1 file changed, 38 insertions(+), 22 deletions(-)
diff --git a/drivers/irqchip/irq-vt8500.c b/drivers/irqchip/irq-vt8500.c
index cf24a88f52d1b90f033d429288c88398439b92d1..aea43c838430d2a541aa9b3127a215531abecad8 100644
--- a/drivers/irqchip/irq-vt8500.c
+++ b/drivers/irqchip/irq-vt8500.c
@@ -15,6 +15,7 @@
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
#include <linux/interrupt.h>
#include <linux/bitops.h>
@@ -66,6 +67,8 @@ struct vt8500_irq_data {
/* Global variable for accessing io-mem addresses */
static struct vt8500_irq_data intc[VT8500_INTC_MAX];
static u32 active_cnt = 0;
+/* Primary interrupt controller data */
+static struct vt8500_irq_data *primary_intc;
static void vt8500_irq_ack(struct irq_data *d)
{
@@ -163,28 +166,38 @@ static const struct irq_domain_ops vt8500_irq_domain_ops = {
.xlate = irq_domain_xlate_onecell,
};
+static inline void vt8500_handle_irq_common(struct vt8500_irq_data *intc)
+{
+ unsigned long irqnr = readl_relaxed(intc->base) & 0x3F;
+ unsigned long stat;
+
+ /*
+ * Highest Priority register default = 63, so check that this
+ * is a real interrupt by checking the status register
+ */
+ if (irqnr == 63) {
+ stat = readl_relaxed(intc->base + VT8500_ICIS + 4);
+ if (!(stat & BIT(31)))
+ return;
+ }
+
+ generic_handle_domain_irq(intc->domain, irqnr);
+}
+
static void __exception_irq_entry vt8500_handle_irq(struct pt_regs *regs)
{
- u32 stat, i;
- int irqnr;
- void __iomem *base;
-
- /* Loop through each active controller */
- for (i=0; i<active_cnt; i++) {
- base = intc[i].base;
- irqnr = readl_relaxed(base) & 0x3F;
- /*
- Highest Priority register default = 63, so check that this
- is a real interrupt by checking the status register
- */
- if (irqnr == 63) {
- stat = readl_relaxed(base + VT8500_ICIS + 4);
- if (!(stat & BIT(31)))
- continue;
- }
+ vt8500_handle_irq_common(primary_intc);
+}
- generic_handle_domain_irq(intc[i].domain, irqnr);
- }
+static void vt8500_handle_irq_chained(struct irq_desc *desc)
+{
+ struct irq_domain *d = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct vt8500_irq_data *intc = d->host_data;
+
+ chained_irq_enter(chip, desc);
+ vt8500_handle_irq_common(intc);
+ chained_irq_exit(chip, desc);
}
static int __init vt8500_irq_init(struct device_node *node,
@@ -212,8 +225,6 @@ static int __init vt8500_irq_init(struct device_node *node,
goto out;
}
- set_handle_irq(vt8500_handle_irq);
-
vt8500_init_irq_hw(intc[active_cnt].base);
pr_info("vt8500-irq: Added interrupt controller\n");
@@ -224,10 +235,15 @@ static int __init vt8500_irq_init(struct device_node *node,
if (of_irq_count(node) != 0) {
for (i = 0; i < of_irq_count(node); i++) {
irq = irq_of_parse_and_map(node, i);
- enable_irq(irq);
+ irq_set_chained_handler_and_data(irq,
+ vt8500_handle_irq_chained,
+ &intc[active_cnt]);
}
pr_info("vt8500-irq: Enabled slave->parent interrupts\n");
+ } else {
+ primary_intc = &intc[active_cnt];
+ set_handle_irq(vt8500_handle_irq);
}
out:
return 0;
--
2.49.0
^ permalink raw reply [flat|nested] 11+ messages in thread* [tip: irq/drivers] irqchip/irq-vt8500: Use a dedicated chained handler function
2025-05-06 12:46 ` [PATCH v2 4/5] irqchip/irq-vt8500: Use a dedicated chained handler function Alexey Charkov
@ 2025-05-06 14:04 ` tip-bot2 for Alexey Charkov
0 siblings, 0 replies; 11+ messages in thread
From: tip-bot2 for Alexey Charkov @ 2025-05-06 14:04 UTC (permalink / raw)
To: linux-tip-commits; +Cc: Alexey Charkov, Thomas Gleixner, x86, linux-kernel
The following commit has been merged into the irq/drivers branch of tip:
Commit-ID: 49f92d3859cdd8534a1cd15037f950c483a5de40
Gitweb: https://git.kernel.org/tip/49f92d3859cdd8534a1cd15037f950c483a5de40
Author: Alexey Charkov <alchark@gmail.com>
AuthorDate: Tue, 06 May 2025 16:46:17 +04:00
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Tue, 06 May 2025 15:58:26 +02:00
irqchip/irq-vt8500: Use a dedicated chained handler function
Current code for the chained interrupt controller maps its interrupts on
the parent but doesn't register a separate chained handler, instead
needlessly calling enable_irq() on an unactivated parent interrupt, causing
a boot time WARN_ON from the common code.
The common handler meanwhile loops through all registered interrupt
controllers in an arbitrary order and tries to handle active interrupts
in each of them, which is fragile.
Use common infrastructure for handling chained interrupts instead.
Signed-off-by: Alexey Charkov <alchark@gmail.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20250506-vt8500-intc-updates-v2-4-a3a0606cf92d@gmail.com
---
drivers/irqchip/irq-vt8500.c | 59 +++++++++++++++++++++--------------
1 file changed, 37 insertions(+), 22 deletions(-)
diff --git a/drivers/irqchip/irq-vt8500.c b/drivers/irqchip/irq-vt8500.c
index debca89..15134cb 100644
--- a/drivers/irqchip/irq-vt8500.c
+++ b/drivers/irqchip/irq-vt8500.c
@@ -15,6 +15,7 @@
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
#include <linux/interrupt.h>
#include <linux/bitops.h>
@@ -66,6 +67,8 @@ struct vt8500_irq_data {
/* Global variable for accessing io-mem addresses */
static struct vt8500_irq_data intc[VT8500_INTC_MAX];
static u32 active_cnt = 0;
+/* Primary interrupt controller data */
+static struct vt8500_irq_data *primary_intc;
static void vt8500_irq_ack(struct irq_data *d)
{
@@ -163,28 +166,38 @@ static const struct irq_domain_ops vt8500_irq_domain_ops = {
.xlate = irq_domain_xlate_onecell,
};
+static inline void vt8500_handle_irq_common(struct vt8500_irq_data *intc)
+{
+ unsigned long irqnr = readl_relaxed(intc->base) & 0x3F;
+ unsigned long stat;
+
+ /*
+ * Highest Priority register default = 63, so check that this
+ * is a real interrupt by checking the status register
+ */
+ if (irqnr == 63) {
+ stat = readl_relaxed(intc->base + VT8500_ICIS + 4);
+ if (!(stat & BIT(31)))
+ return;
+ }
+
+ generic_handle_domain_irq(intc->domain, irqnr);
+}
+
static void __exception_irq_entry vt8500_handle_irq(struct pt_regs *regs)
{
- u32 stat, i;
- int irqnr;
- void __iomem *base;
-
- /* Loop through each active controller */
- for (i=0; i<active_cnt; i++) {
- base = intc[i].base;
- irqnr = readl_relaxed(base) & 0x3F;
- /*
- Highest Priority register default = 63, so check that this
- is a real interrupt by checking the status register
- */
- if (irqnr == 63) {
- stat = readl_relaxed(base + VT8500_ICIS + 4);
- if (!(stat & BIT(31)))
- continue;
- }
+ vt8500_handle_irq_common(primary_intc);
+}
- generic_handle_domain_irq(intc[i].domain, irqnr);
- }
+static void vt8500_handle_irq_chained(struct irq_desc *desc)
+{
+ struct irq_domain *d = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct vt8500_irq_data *intc = d->host_data;
+
+ chained_irq_enter(chip, desc);
+ vt8500_handle_irq_common(intc);
+ chained_irq_exit(chip, desc);
}
static int __init vt8500_irq_init(struct device_node *node,
@@ -212,8 +225,6 @@ static int __init vt8500_irq_init(struct device_node *node,
goto out;
}
- set_handle_irq(vt8500_handle_irq);
-
vt8500_init_irq_hw(intc[active_cnt].base);
pr_info("vt8500-irq: Added interrupt controller\n");
@@ -224,10 +235,14 @@ static int __init vt8500_irq_init(struct device_node *node,
if (of_irq_count(node) != 0) {
for (i = 0; i < of_irq_count(node); i++) {
irq = irq_of_parse_and_map(node, i);
- enable_irq(irq);
+ irq_set_chained_handler_and_data(irq, vt8500_handle_irq_chained,
+ &intc[active_cnt]);
}
pr_info("vt8500-irq: Enabled slave->parent interrupts\n");
+ } else {
+ primary_intc = &intc[active_cnt];
+ set_handle_irq(vt8500_handle_irq);
}
out:
return 0;
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 5/5] irqchip/irq-vt8500: Use fewer global variables and add error handling
2025-05-06 12:46 [PATCH v2 0/5] irqchip/irq-vt8500: Cleanups and fixes for the irq-vt8500 driver Alexey Charkov
` (3 preceding siblings ...)
2025-05-06 12:46 ` [PATCH v2 4/5] irqchip/irq-vt8500: Use a dedicated chained handler function Alexey Charkov
@ 2025-05-06 12:46 ` Alexey Charkov
2025-05-06 14:04 ` [tip: irq/drivers] " tip-bot2 for Alexey Charkov
4 siblings, 1 reply; 11+ messages in thread
From: Alexey Charkov @ 2025-05-06 12:46 UTC (permalink / raw)
To: Thomas Gleixner, Krzysztof Kozlowski
Cc: linux-kernel, linux-arm-kernel, Alexey Charkov
Controller private data doesn't really need to be in a global
statically allocated array - kzalloc it per controller instead,
keeping only one pointer to the primary controller global.
While at that, also add proper error return statuses in the init
path and respective cleanup of resources on errors.
Signed-off-by: Alexey Charkov <alchark@gmail.com>
---
drivers/irqchip/irq-vt8500.c | 49 +++++++++++++++++++++++---------------------
1 file changed, 26 insertions(+), 23 deletions(-)
diff --git a/drivers/irqchip/irq-vt8500.c b/drivers/irqchip/irq-vt8500.c
index aea43c838430d2a541aa9b3127a215531abecad8..0bf478c6541ede30719eb1e403d9cafe6474643a 100644
--- a/drivers/irqchip/irq-vt8500.c
+++ b/drivers/irqchip/irq-vt8500.c
@@ -64,9 +64,6 @@ struct vt8500_irq_data {
struct irq_domain *domain; /* Domain for this controller */
};
-/* Global variable for accessing io-mem addresses */
-static struct vt8500_irq_data intc[VT8500_INTC_MAX];
-static u32 active_cnt = 0;
/* Primary interrupt controller data */
static struct vt8500_irq_data *primary_intc;
@@ -203,50 +200,56 @@ static void vt8500_handle_irq_chained(struct irq_desc *desc)
static int __init vt8500_irq_init(struct device_node *node,
struct device_node *parent)
{
- int irq, i;
+ struct vt8500_irq_data *intc;
+ int irq, i, ret = 0;
- if (active_cnt == VT8500_INTC_MAX) {
- pr_err("%s: Interrupt controllers > VT8500_INTC_MAX\n",
- __func__);
- goto out;
- }
-
- intc[active_cnt].base = of_iomap(node, 0);
- intc[active_cnt].domain = irq_domain_add_linear(node, 64,
- &vt8500_irq_domain_ops, &intc[active_cnt]);
+ intc = kzalloc(sizeof(*intc), GFP_KERNEL);
+ if (!intc)
+ return -ENOMEM;
- if (!intc[active_cnt].base) {
+ intc->base = of_iomap(node, 0);
+ if (!intc->base) {
pr_err("%s: Unable to map IO memory\n", __func__);
- goto out;
+ ret = -ENOMEM;
+ goto err_free;
}
- if (!intc[active_cnt].domain) {
+ intc->domain = irq_domain_add_linear(node,
+ 64,
+ &vt8500_irq_domain_ops,
+ intc);
+ if (!intc->domain) {
pr_err("%s: Unable to add irq domain!\n", __func__);
- goto out;
+ ret = -ENOMEM;
+ goto err_unmap;
}
- vt8500_init_irq_hw(intc[active_cnt].base);
+ vt8500_init_irq_hw(intc->base);
pr_info("vt8500-irq: Added interrupt controller\n");
- active_cnt++;
-
/* check if this is a chained controller */
if (of_irq_count(node) != 0) {
for (i = 0; i < of_irq_count(node); i++) {
irq = irq_of_parse_and_map(node, i);
irq_set_chained_handler_and_data(irq,
vt8500_handle_irq_chained,
- &intc[active_cnt]);
+ intc);
}
pr_info("vt8500-irq: Enabled slave->parent interrupts\n");
} else {
- primary_intc = &intc[active_cnt];
+ primary_intc = intc;
set_handle_irq(vt8500_handle_irq);
}
-out:
+
return 0;
+
+err_unmap:
+ iounmap(intc->base);
+err_free:
+ kfree(intc);
+ return ret;
}
IRQCHIP_DECLARE(vt8500_irq, "via,vt8500-intc", vt8500_irq_init);
--
2.49.0
^ permalink raw reply [flat|nested] 11+ messages in thread* [tip: irq/drivers] irqchip/irq-vt8500: Use fewer global variables and add error handling
2025-05-06 12:46 ` [PATCH v2 5/5] irqchip/irq-vt8500: Use fewer global variables and add error handling Alexey Charkov
@ 2025-05-06 14:04 ` tip-bot2 for Alexey Charkov
0 siblings, 0 replies; 11+ messages in thread
From: tip-bot2 for Alexey Charkov @ 2025-05-06 14:04 UTC (permalink / raw)
To: linux-tip-commits; +Cc: Alexey Charkov, Thomas Gleixner, x86, linux-kernel
The following commit has been merged into the irq/drivers branch of tip:
Commit-ID: 99ad153fbd22fcf7c2bdd774d08fc4bf70029fa6
Gitweb: https://git.kernel.org/tip/99ad153fbd22fcf7c2bdd774d08fc4bf70029fa6
Author: Alexey Charkov <alchark@gmail.com>
AuthorDate: Tue, 06 May 2025 16:46:18 +04:00
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Tue, 06 May 2025 15:58:27 +02:00
irqchip/irq-vt8500: Use fewer global variables and add error handling
Controller private data doesn't really need to be in a global
statically allocated array - kzalloc it per controller instead,
keeping only one pointer to the primary controller global.
While at that, also add proper error return statuses in the init
path and respective cleanup of resources on errors.
Signed-off-by: Alexey Charkov <alchark@gmail.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20250506-vt8500-intc-updates-v2-5-a3a0606cf92d@gmail.com
---
drivers/irqchip/irq-vt8500.c | 48 ++++++++++++++++++-----------------
1 file changed, 25 insertions(+), 23 deletions(-)
diff --git a/drivers/irqchip/irq-vt8500.c b/drivers/irqchip/irq-vt8500.c
index 15134cb..40c2cff 100644
--- a/drivers/irqchip/irq-vt8500.c
+++ b/drivers/irqchip/irq-vt8500.c
@@ -64,9 +64,6 @@ struct vt8500_irq_data {
struct irq_domain *domain; /* Domain for this controller */
};
-/* Global variable for accessing io-mem addresses */
-static struct vt8500_irq_data intc[VT8500_INTC_MAX];
-static u32 active_cnt = 0;
/* Primary interrupt controller data */
static struct vt8500_irq_data *primary_intc;
@@ -203,49 +200,54 @@ static void vt8500_handle_irq_chained(struct irq_desc *desc)
static int __init vt8500_irq_init(struct device_node *node,
struct device_node *parent)
{
- int irq, i;
+ struct vt8500_irq_data *intc;
+ int irq, i, ret = 0;
- if (active_cnt == VT8500_INTC_MAX) {
- pr_err("%s: Interrupt controllers > VT8500_INTC_MAX\n",
- __func__);
- goto out;
- }
-
- intc[active_cnt].base = of_iomap(node, 0);
- intc[active_cnt].domain = irq_domain_add_linear(node, 64,
- &vt8500_irq_domain_ops, &intc[active_cnt]);
+ intc = kzalloc(sizeof(*intc), GFP_KERNEL);
+ if (!intc)
+ return -ENOMEM;
- if (!intc[active_cnt].base) {
+ intc->base = of_iomap(node, 0);
+ if (!intc->base) {
pr_err("%s: Unable to map IO memory\n", __func__);
- goto out;
+ ret = -ENOMEM;
+ goto err_free;
}
- if (!intc[active_cnt].domain) {
+ intc->domain = irq_domain_add_linear(node,
+ 64,
+ &vt8500_irq_domain_ops,
+ intc);
+ if (!intc->domain) {
pr_err("%s: Unable to add irq domain!\n", __func__);
- goto out;
+ ret = -ENOMEM;
+ goto err_unmap;
}
- vt8500_init_irq_hw(intc[active_cnt].base);
+ vt8500_init_irq_hw(intc->base);
pr_info("vt8500-irq: Added interrupt controller\n");
- active_cnt++;
-
/* check if this is a chained controller */
if (of_irq_count(node) != 0) {
for (i = 0; i < of_irq_count(node); i++) {
irq = irq_of_parse_and_map(node, i);
irq_set_chained_handler_and_data(irq, vt8500_handle_irq_chained,
- &intc[active_cnt]);
+ intc);
}
pr_info("vt8500-irq: Enabled slave->parent interrupts\n");
} else {
- primary_intc = &intc[active_cnt];
+ primary_intc = intc;
set_handle_irq(vt8500_handle_irq);
}
-out:
return 0;
+
+err_unmap:
+ iounmap(intc->base);
+err_free:
+ kfree(intc);
+ return ret;
}
IRQCHIP_DECLARE(vt8500_irq, "via,vt8500-intc", vt8500_irq_init);
^ permalink raw reply [flat|nested] 11+ messages in thread