[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <175603291668.1420.4658443638751505549.tip-bot2@tip-bot2>
Date: Sun, 24 Aug 2025 10:55:16 -0000
From: "tip-bot2 for Bibo Mao" <tip-bot2@...utronix.de>
To: linux-tip-commits@...r.kernel.org
Cc: Bibo Mao <maobibo@...ngson.cn>, Thomas Gleixner <tglx@...utronix.de>,
x86@...nel.org, linux-kernel@...r.kernel.org
Subject: [tip: irq/drivers] irqchip/loongson-eiointc: Add multiple interrupt
pin routing support
The following commit has been merged into the irq/drivers branch of tip:
Commit-ID: 8ff1c16c753e293c3ba20583cb64f81ea7b9a451
Gitweb: https://git.kernel.org/tip/8ff1c16c753e293c3ba20583cb64f81ea7b9a451
Author: Bibo Mao <maobibo@...ngson.cn>
AuthorDate: Mon, 04 Aug 2025 16:19:46 +08:00
Committer: Thomas Gleixner <tglx@...utronix.de>
CommitterDate: Sun, 24 Aug 2025 12:51:04 +02:00
irqchip/loongson-eiointc: Add multiple interrupt pin routing support
The eiointc interrupt controller supports 256 interrupt vectors at most,
and the interrupt handler gets the interrupt status from the base register
group EIOINTC_REG_ISR at the interrupt specific offset.
It needs to read the register group EIOINTC_REG_ISR four times to get all
256 interrupt vectors status.
Eiointc registers including EIOINTC_REG_ISR are software emulated for
VMs, so there will be VM-exits when accessing eiointc registers.
Introduce a method to make the eiointc interrupt controller route
to different CPU interrupt pins for every 64 interrupt vectors.
The interrupt handler can then reduce the read to one specific
EIOINTC_REG_ISR register instead of all four, which reduces VM exits.
[ tglx: Massage change log ]
Signed-off-by: Bibo Mao <maobibo@...ngson.cn>
Signed-off-by: Thomas Gleixner <tglx@...utronix.de>
Link: https://lore.kernel.org/all/20250804081946.1456573-3-maobibo@loongson.cn
---
drivers/irqchip/irq-loongson-eiointc.c | 88 ++++++++++++++++++++++---
1 file changed, 78 insertions(+), 10 deletions(-)
diff --git a/drivers/irqchip/irq-loongson-eiointc.c b/drivers/irqchip/irq-loongson-eiointc.c
index baa4069..39e5a72 100644
--- a/drivers/irqchip/irq-loongson-eiointc.c
+++ b/drivers/irqchip/irq-loongson-eiointc.c
@@ -46,6 +46,7 @@
#define EIOINTC_ALL_ENABLE_VEC_MASK(vector) (EIOINTC_ALL_ENABLE & ~BIT(vector & 0x1f))
#define EIOINTC_REG_ENABLE_VEC(vector) (EIOINTC_REG_ENABLE + ((vector >> 5) << 2))
#define EIOINTC_USE_CPU_ENCODE BIT(0)
+#define EIOINTC_ROUTE_MULT_IP BIT(1)
#define MAX_EIO_NODES (NR_CPUS / CORES_PER_EIO_NODE)
@@ -59,6 +60,14 @@
#define EIOINTC_REG_ROUTE_VEC_MASK(vector) (0xff << EIOINTC_REG_ROUTE_VEC_SHIFT(vector))
static int nr_pics;
+struct eiointc_priv;
+
+struct eiointc_ip_route {
+ struct eiointc_priv *priv;
+ /* Offset Routed destination IP */
+ int start;
+ int end;
+};
struct eiointc_priv {
u32 node;
@@ -69,6 +78,7 @@ struct eiointc_priv {
struct irq_domain *eiointc_domain;
int flags;
irq_hw_number_t parent_hwirq;
+ struct eiointc_ip_route route_info[VEC_REG_COUNT];
};
static struct eiointc_priv *eiointc_priv[MAX_IO_PICS];
@@ -189,6 +199,7 @@ static int eiointc_router_init(unsigned int cpu)
{
int i, bit, cores, index, node;
unsigned int data;
+ int hwirq, mask;
node = cpu_to_eio_node(cpu);
index = eiointc_index(node);
@@ -198,6 +209,13 @@ static int eiointc_router_init(unsigned int cpu)
return -EINVAL;
}
+ /* Enable cpu interrupt pin from eiointc */
+ hwirq = eiointc_priv[index]->parent_hwirq;
+ mask = BIT(hwirq);
+ if (eiointc_priv[index]->flags & EIOINTC_ROUTE_MULT_IP)
+ mask |= BIT(hwirq + 1) | BIT(hwirq + 2) | BIT(hwirq + 3);
+ set_csr_ecfg(mask);
+
if (!(eiointc_priv[index]->flags & EIOINTC_USE_CPU_ENCODE))
cores = CORES_PER_EIO_NODE;
else
@@ -215,10 +233,28 @@ static int eiointc_router_init(unsigned int cpu)
/*
* Route to interrupt pin, relative offset used here
* Offset 0 means routing to IP0 and so on
- * Every 32 vector routing to one interrupt pin
+ *
+ * If flags is set with EIOINTC_ROUTE_MULT_IP,
+ * every 64 vector routes to different consecutive
+ * IPs, otherwise all vector routes to the same IP
*/
- bit = BIT(eiointc_priv[index]->parent_hwirq - INT_HWI0);
- data = bit | (bit << 8) | (bit << 16) | (bit << 24);
+ if (eiointc_priv[index]->flags & EIOINTC_ROUTE_MULT_IP) {
+ /* The first 64 vectors route to hwirq */
+ bit = BIT(hwirq++ - INT_HWI0);
+ data = bit | (bit << 8);
+
+ /* The second 64 vectors route to hwirq + 1 */
+ bit = BIT(hwirq++ - INT_HWI0);
+ data |= (bit << 16) | (bit << 24);
+
+ /*
+ * Route to hwirq + 2/hwirq + 3 separately
+ * in next loop
+ */
+ } else {
+ bit = BIT(hwirq - INT_HWI0);
+ data = bit | (bit << 8) | (bit << 16) | (bit << 24);
+ }
iocsr_write32(data, EIOINTC_REG_IPMAP + i * 4);
}
@@ -247,15 +283,22 @@ static int eiointc_router_init(unsigned int cpu)
static void eiointc_irq_dispatch(struct irq_desc *desc)
{
- int i;
- u64 pending;
- bool handled = false;
+ struct eiointc_ip_route *info = irq_desc_get_handler_data(desc);
struct irq_chip *chip = irq_desc_get_chip(desc);
- struct eiointc_priv *priv = irq_desc_get_handler_data(desc);
+ bool handled = false;
+ u64 pending;
+ int i;
chained_irq_enter(chip, desc);
- for (i = 0; i < eiointc_priv[0]->vec_count / VEC_COUNT_PER_REG; i++) {
+ /*
+ * If EIOINTC_ROUTE_MULT_IP is set, every 64 interrupt vectors in
+ * eiointc interrupt controller routes to different cpu interrupt pins
+ *
+ * Every cpu interrupt pin has its own irq handler, it is ok to
+ * read ISR for these 64 interrupt vectors rather than all vectors
+ */
+ for (i = info->start; i < info->end; i++) {
pending = iocsr_read64(EIOINTC_REG_ISR + (i << 3));
/* Skip handling if pending bitmap is zero */
@@ -268,7 +311,7 @@ static void eiointc_irq_dispatch(struct irq_desc *desc)
int bit = __ffs(pending);
int irq = bit + VEC_COUNT_PER_REG * i;
- generic_handle_domain_irq(priv->eiointc_domain, irq);
+ generic_handle_domain_irq(info->priv->eiointc_domain, irq);
pending &= ~BIT(bit);
handled = true;
}
@@ -468,8 +511,33 @@ static int __init eiointc_init(struct eiointc_priv *priv, int parent_irq,
}
eiointc_priv[nr_pics++] = priv;
+ /*
+ * Only the first eiointc device on VM supports routing to
+ * different CPU interrupt pins. The later eiointc devices use
+ * generic method if there are multiple eiointc devices in future
+ */
+ if (cpu_has_hypervisor && (nr_pics == 1)) {
+ priv->flags |= EIOINTC_ROUTE_MULT_IP;
+ priv->parent_hwirq = INT_HWI0;
+ }
+
+ if (priv->flags & EIOINTC_ROUTE_MULT_IP) {
+ for (i = 0; i < priv->vec_count / VEC_COUNT_PER_REG; i++) {
+ priv->route_info[i].start = priv->parent_hwirq - INT_HWI0 + i;
+ priv->route_info[i].end = priv->route_info[i].start + 1;
+ priv->route_info[i].priv = priv;
+ parent_irq = get_percpu_irq(priv->parent_hwirq + i);
+ irq_set_chained_handler_and_data(parent_irq, eiointc_irq_dispatch,
+ &priv->route_info[i]);
+ }
+ } else {
+ priv->route_info[0].start = 0;
+ priv->route_info[0].end = priv->vec_count / VEC_COUNT_PER_REG;
+ priv->route_info[0].priv = priv;
+ irq_set_chained_handler_and_data(parent_irq, eiointc_irq_dispatch,
+ &priv->route_info[0]);
+ }
eiointc_router_init(0);
- irq_set_chained_handler_and_data(parent_irq, eiointc_irq_dispatch, priv);
if (nr_pics == 1) {
register_syscore_ops(&eiointc_syscore_ops);
Powered by blists - more mailing lists