mirror of
https://github.com/torvalds/linux.git
synced 2026-05-29 17:43:52 +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@loongson.cn> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Link: https://lore.kernel.org/all/20250804081946.1456573-3-maobibo@loongson.cn
This commit is contained in:
parent
7fb83eb664
commit
8ff1c16c75
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user