summaryrefslogtreecommitdiff
path: root/drivers/irqchip/irq-loongson-eiointc.c
diff options
context:
space:
mode:
authorBibo Mao <maobibo@loongson.cn>2024-11-13 16:18:27 +0800
committerHuacai Chen <chenhuacai@loongson.cn>2024-11-13 16:18:27 +0800
commit9899b8201025d00b23aee143594a30c55cc4cc35 (patch)
tree24d9334595f8be9a8ce95e0efa7c960fac0a0a04 /drivers/irqchip/irq-loongson-eiointc.c
parent1928254c5ccb7bdffd7f0334e1ce250e9ce4de94 (diff)
irqchip/loongson-eiointc: Add virt extension support
Interrupts can be routed to maximal four virtual CPUs with real HW EIOINTC interrupt controller model, since interrupt routing is encoded with CPU bitmap and EIOINTC node combined method. Here add the EIOINTC virt extension support so that interrupts can be routed to 256 vCPUs in virtual machine mode. CPU bitmap is replaced with normal encoding and EIOINTC node type is removed, so there are 8 bits for cpu selection, at most 256 vCPUs are supported for interrupt routing. Reviewed-by: Thomas Gleixner <tglx@linutronix.de> Co-developed-by: Song Gao <gaosong@loongson.cn> Signed-off-by: Song Gao <gaosong@loongson.cn> Signed-off-by: Bibo Mao <maobibo@loongson.cn> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
Diffstat (limited to 'drivers/irqchip/irq-loongson-eiointc.c')
-rw-r--r--drivers/irqchip/irq-loongson-eiointc.c108
1 files changed, 87 insertions, 21 deletions
diff --git a/drivers/irqchip/irq-loongson-eiointc.c b/drivers/irqchip/irq-loongson-eiointc.c
index e24db71a8783..bb79e19dfb59 100644
--- a/drivers/irqchip/irq-loongson-eiointc.c
+++ b/drivers/irqchip/irq-loongson-eiointc.c
@@ -14,6 +14,7 @@
#include <linux/irqdomain.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/kernel.h>
+#include <linux/kvm_para.h>
#include <linux/syscore_ops.h>
#include <asm/numa.h>
@@ -26,15 +27,37 @@
#define EIOINTC_REG_ISR 0x1800
#define EIOINTC_REG_ROUTE 0x1c00
+#define EXTIOI_VIRT_FEATURES 0x40000000
+#define EXTIOI_HAS_VIRT_EXTENSION BIT(0)
+#define EXTIOI_HAS_ENABLE_OPTION BIT(1)
+#define EXTIOI_HAS_INT_ENCODE BIT(2)
+#define EXTIOI_HAS_CPU_ENCODE BIT(3)
+#define EXTIOI_VIRT_CONFIG 0x40000004
+#define EXTIOI_ENABLE BIT(1)
+#define EXTIOI_ENABLE_INT_ENCODE BIT(2)
+#define EXTIOI_ENABLE_CPU_ENCODE BIT(3)
+
#define VEC_REG_COUNT 4
#define VEC_COUNT_PER_REG 64
#define VEC_COUNT (VEC_REG_COUNT * VEC_COUNT_PER_REG)
#define VEC_REG_IDX(irq_id) ((irq_id) / VEC_COUNT_PER_REG)
#define VEC_REG_BIT(irq_id) ((irq_id) % VEC_COUNT_PER_REG)
#define EIOINTC_ALL_ENABLE 0xffffffff
+#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 MAX_EIO_NODES (NR_CPUS / CORES_PER_EIO_NODE)
+/*
+ * Routing registers are 32bit, and there is 8-bit route setting for every
+ * interrupt vector. So one Route register contains four vectors routing
+ * information.
+ */
+#define EIOINTC_REG_ROUTE_VEC(vector) (EIOINTC_REG_ROUTE + (vector & ~0x03))
+#define EIOINTC_REG_ROUTE_VEC_SHIFT(vector) ((vector & 0x03) << 3)
+#define EIOINTC_REG_ROUTE_VEC_MASK(vector) (0xff << EIOINTC_REG_ROUTE_VEC_SHIFT(vector))
+
static int nr_pics;
struct eiointc_priv {
@@ -44,6 +67,7 @@ struct eiointc_priv {
cpumask_t cpuspan_map;
struct fwnode_handle *domain_handle;
struct irq_domain *eiointc_domain;
+ int flags;
};
static struct eiointc_priv *eiointc_priv[MAX_IO_PICS];
@@ -59,7 +83,10 @@ static void eiointc_enable(void)
static int cpu_to_eio_node(int cpu)
{
- return cpu_logical_map(cpu) / CORES_PER_EIO_NODE;
+ if (!kvm_para_has_feature(KVM_FEATURE_VIRT_EXTIOI))
+ return cpu_logical_map(cpu) / CORES_PER_EIO_NODE;
+ else
+ return cpu_logical_map(cpu) / CORES_PER_VEIO_NODE;
}
#ifdef CONFIG_SMP
@@ -89,6 +116,17 @@ static void eiointc_set_irq_route(int pos, unsigned int cpu, unsigned int mnode,
}
}
+static void veiointc_set_irq_route(unsigned int vector, unsigned int cpu)
+{
+ unsigned long reg = EIOINTC_REG_ROUTE_VEC(vector);
+ unsigned int data;
+
+ data = iocsr_read32(reg);
+ data &= ~EIOINTC_REG_ROUTE_VEC_MASK(vector);
+ data |= cpu_logical_map(cpu) << EIOINTC_REG_ROUTE_VEC_SHIFT(vector);
+ iocsr_write32(data, reg);
+}
+
static DEFINE_RAW_SPINLOCK(affinity_lock);
static int eiointc_set_irq_affinity(struct irq_data *d, const struct cpumask *affinity, bool force)
@@ -107,18 +145,24 @@ static int eiointc_set_irq_affinity(struct irq_data *d, const struct cpumask *af
}
vector = d->hwirq;
- regaddr = EIOINTC_REG_ENABLE + ((vector >> 5) << 2);
-
- /* Mask target vector */
- csr_any_send(regaddr, EIOINTC_ALL_ENABLE & (~BIT(vector & 0x1F)),
- 0x0, priv->node * CORES_PER_EIO_NODE);
-
- /* Set route for target vector */
- eiointc_set_irq_route(vector, cpu, priv->node, &priv->node_map);
-
- /* Unmask target vector */
- csr_any_send(regaddr, EIOINTC_ALL_ENABLE,
- 0x0, priv->node * CORES_PER_EIO_NODE);
+ regaddr = EIOINTC_REG_ENABLE_VEC(vector);
+
+ if (priv->flags & EIOINTC_USE_CPU_ENCODE) {
+ iocsr_write32(EIOINTC_ALL_ENABLE_VEC_MASK(vector), regaddr);
+ veiointc_set_irq_route(vector, cpu);
+ iocsr_write32(EIOINTC_ALL_ENABLE, regaddr);
+ } else {
+ /* Mask target vector */
+ csr_any_send(regaddr, EIOINTC_ALL_ENABLE_VEC_MASK(vector),
+ 0x0, priv->node * CORES_PER_EIO_NODE);
+
+ /* Set route for target vector */
+ eiointc_set_irq_route(vector, cpu, priv->node, &priv->node_map);
+
+ /* Unmask target vector */
+ csr_any_send(regaddr, EIOINTC_ALL_ENABLE,
+ 0x0, priv->node * CORES_PER_EIO_NODE);
+ }
irq_data_update_effective_affinity(d, cpumask_of(cpu));
@@ -142,17 +186,23 @@ static int eiointc_index(int node)
static int eiointc_router_init(unsigned int cpu)
{
- int i, bit;
- uint32_t data;
- uint32_t node = cpu_to_eio_node(cpu);
- int index = eiointc_index(node);
+ int i, bit, cores, index, node;
+ unsigned int data;
+
+ node = cpu_to_eio_node(cpu);
+ index = eiointc_index(node);
if (index < 0) {
pr_err("Error: invalid nodemap!\n");
- return -1;
+ return -EINVAL;
}
- if ((cpu_logical_map(cpu) % CORES_PER_EIO_NODE) == 0) {
+ if (!(eiointc_priv[index]->flags & EIOINTC_USE_CPU_ENCODE))
+ cores = CORES_PER_EIO_NODE;
+ else
+ cores = CORES_PER_VEIO_NODE;
+
+ if ((cpu_logical_map(cpu) % cores) == 0) {
eiointc_enable();
for (i = 0; i < eiointc_priv[0]->vec_count / 32; i++) {
@@ -168,7 +218,9 @@ static int eiointc_router_init(unsigned int cpu)
for (i = 0; i < eiointc_priv[0]->vec_count / 4; i++) {
/* Route to Node-0 Core-0 */
- if (index == 0)
+ if (eiointc_priv[index]->flags & EIOINTC_USE_CPU_ENCODE)
+ bit = cpu_logical_map(0);
+ else if (index == 0)
bit = BIT(cpu_logical_map(0));
else
bit = (eiointc_priv[index]->node << 4) | 1;
@@ -375,7 +427,7 @@ static int __init acpi_cascade_irqdomain_init(void)
static int __init eiointc_init(struct eiointc_priv *priv, int parent_irq,
u64 node_map)
{
- int i;
+ int i, val;
node_map = node_map ? node_map : -1ULL;
for_each_possible_cpu(i) {
@@ -395,6 +447,20 @@ static int __init eiointc_init(struct eiointc_priv *priv, int parent_irq,
return -ENOMEM;
}
+ if (kvm_para_has_feature(KVM_FEATURE_VIRT_EXTIOI)) {
+ val = iocsr_read32(EXTIOI_VIRT_FEATURES);
+ /*
+ * With EXTIOI_ENABLE_CPU_ENCODE set
+ * interrupts can route to 256 vCPUs.
+ */
+ if (val & EXTIOI_HAS_CPU_ENCODE) {
+ val = iocsr_read32(EXTIOI_VIRT_CONFIG);
+ val |= EXTIOI_ENABLE_CPU_ENCODE;
+ iocsr_write32(val, EXTIOI_VIRT_CONFIG);
+ priv->flags = EIOINTC_USE_CPU_ENCODE;
+ }
+ }
+
eiointc_priv[nr_pics++] = priv;
eiointc_router_init(0);
irq_set_chained_handler_and_data(parent_irq, eiointc_irq_dispatch, priv);