Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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@loongson.cn>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20250804081946.1456573-3-maobibo@loongson.cn

authored by

Bibo Mao and committed by
Thomas Gleixner
8ff1c16c 7fb83eb6

+78 -10
+78 -10
drivers/irqchip/irq-loongson-eiointc.c
··· 46 46 #define EIOINTC_ALL_ENABLE_VEC_MASK(vector) (EIOINTC_ALL_ENABLE & ~BIT(vector & 0x1f)) 47 47 #define EIOINTC_REG_ENABLE_VEC(vector) (EIOINTC_REG_ENABLE + ((vector >> 5) << 2)) 48 48 #define EIOINTC_USE_CPU_ENCODE BIT(0) 49 + #define EIOINTC_ROUTE_MULT_IP BIT(1) 49 50 50 51 #define MAX_EIO_NODES (NR_CPUS / CORES_PER_EIO_NODE) 51 52 ··· 60 59 #define EIOINTC_REG_ROUTE_VEC_MASK(vector) (0xff << EIOINTC_REG_ROUTE_VEC_SHIFT(vector)) 61 60 62 61 static int nr_pics; 62 + struct eiointc_priv; 63 + 64 + struct eiointc_ip_route { 65 + struct eiointc_priv *priv; 66 + /* Offset Routed destination IP */ 67 + int start; 68 + int end; 69 + }; 63 70 64 71 struct eiointc_priv { 65 72 u32 node; ··· 78 69 struct irq_domain *eiointc_domain; 79 70 int flags; 80 71 irq_hw_number_t parent_hwirq; 72 + struct eiointc_ip_route route_info[VEC_REG_COUNT]; 81 73 }; 82 74 83 75 static struct eiointc_priv *eiointc_priv[MAX_IO_PICS]; ··· 199 189 { 200 190 int i, bit, cores, index, node; 201 191 unsigned int data; 192 + int hwirq, mask; 202 193 203 194 node = cpu_to_eio_node(cpu); 204 195 index = eiointc_index(node); ··· 208 197 pr_err("Error: invalid nodemap!\n"); 209 198 return -EINVAL; 210 199 } 200 + 201 + /* Enable cpu interrupt pin from eiointc */ 202 + hwirq = eiointc_priv[index]->parent_hwirq; 203 + mask = BIT(hwirq); 204 + if (eiointc_priv[index]->flags & EIOINTC_ROUTE_MULT_IP) 205 + mask |= BIT(hwirq + 1) | BIT(hwirq + 2) | BIT(hwirq + 3); 206 + set_csr_ecfg(mask); 211 207 212 208 if (!(eiointc_priv[index]->flags & EIOINTC_USE_CPU_ENCODE)) 213 209 cores = CORES_PER_EIO_NODE; ··· 233 215 /* 234 216 * Route to interrupt pin, relative offset used here 235 217 * Offset 0 means routing to IP0 and so on 236 - * Every 32 vector routing to one interrupt pin 218 + * 219 + * If flags is set with EIOINTC_ROUTE_MULT_IP, 220 + * every 64 vector routes to different consecutive 221 + * IPs, otherwise all vector routes to the same IP 237 222 */ 238 - bit = BIT(eiointc_priv[index]->parent_hwirq - INT_HWI0); 239 - data = bit | (bit << 8) | (bit << 16) | (bit << 24); 223 + if (eiointc_priv[index]->flags & EIOINTC_ROUTE_MULT_IP) { 224 + /* The first 64 vectors route to hwirq */ 225 + bit = BIT(hwirq++ - INT_HWI0); 226 + data = bit | (bit << 8); 227 + 228 + /* The second 64 vectors route to hwirq + 1 */ 229 + bit = BIT(hwirq++ - INT_HWI0); 230 + data |= (bit << 16) | (bit << 24); 231 + 232 + /* 233 + * Route to hwirq + 2/hwirq + 3 separately 234 + * in next loop 235 + */ 236 + } else { 237 + bit = BIT(hwirq - INT_HWI0); 238 + data = bit | (bit << 8) | (bit << 16) | (bit << 24); 239 + } 240 240 iocsr_write32(data, EIOINTC_REG_IPMAP + i * 4); 241 241 } 242 242 ··· 283 247 284 248 static void eiointc_irq_dispatch(struct irq_desc *desc) 285 249 { 286 - int i; 287 - u64 pending; 288 - bool handled = false; 250 + struct eiointc_ip_route *info = irq_desc_get_handler_data(desc); 289 251 struct irq_chip *chip = irq_desc_get_chip(desc); 290 - struct eiointc_priv *priv = irq_desc_get_handler_data(desc); 252 + bool handled = false; 253 + u64 pending; 254 + int i; 291 255 292 256 chained_irq_enter(chip, desc); 293 257 294 - for (i = 0; i < eiointc_priv[0]->vec_count / VEC_COUNT_PER_REG; i++) { 258 + /* 259 + * If EIOINTC_ROUTE_MULT_IP is set, every 64 interrupt vectors in 260 + * eiointc interrupt controller routes to different cpu interrupt pins 261 + * 262 + * Every cpu interrupt pin has its own irq handler, it is ok to 263 + * read ISR for these 64 interrupt vectors rather than all vectors 264 + */ 265 + for (i = info->start; i < info->end; i++) { 295 266 pending = iocsr_read64(EIOINTC_REG_ISR + (i << 3)); 296 267 297 268 /* Skip handling if pending bitmap is zero */ ··· 311 268 int bit = __ffs(pending); 312 269 int irq = bit + VEC_COUNT_PER_REG * i; 313 270 314 - generic_handle_domain_irq(priv->eiointc_domain, irq); 271 + generic_handle_domain_irq(info->priv->eiointc_domain, irq); 315 272 pending &= ~BIT(bit); 316 273 handled = true; 317 274 } ··· 511 468 } 512 469 513 470 eiointc_priv[nr_pics++] = priv; 471 + /* 472 + * Only the first eiointc device on VM supports routing to 473 + * different CPU interrupt pins. The later eiointc devices use 474 + * generic method if there are multiple eiointc devices in future 475 + */ 476 + if (cpu_has_hypervisor && (nr_pics == 1)) { 477 + priv->flags |= EIOINTC_ROUTE_MULT_IP; 478 + priv->parent_hwirq = INT_HWI0; 479 + } 480 + 481 + if (priv->flags & EIOINTC_ROUTE_MULT_IP) { 482 + for (i = 0; i < priv->vec_count / VEC_COUNT_PER_REG; i++) { 483 + priv->route_info[i].start = priv->parent_hwirq - INT_HWI0 + i; 484 + priv->route_info[i].end = priv->route_info[i].start + 1; 485 + priv->route_info[i].priv = priv; 486 + parent_irq = get_percpu_irq(priv->parent_hwirq + i); 487 + irq_set_chained_handler_and_data(parent_irq, eiointc_irq_dispatch, 488 + &priv->route_info[i]); 489 + } 490 + } else { 491 + priv->route_info[0].start = 0; 492 + priv->route_info[0].end = priv->vec_count / VEC_COUNT_PER_REG; 493 + priv->route_info[0].priv = priv; 494 + irq_set_chained_handler_and_data(parent_irq, eiointc_irq_dispatch, 495 + &priv->route_info[0]); 496 + } 514 497 eiointc_router_init(0); 515 - irq_set_chained_handler_and_data(parent_irq, eiointc_irq_dispatch, priv); 516 498 517 499 if (nr_pics == 1) { 518 500 register_syscore_ops(&eiointc_syscore_ops);