Re: [PATCH v2] irqchip/loongson-eiointc: Add extioi virt extension support

From: Huacai Chen
Date: Wed Jun 12 2024 - 00:02:07 EST


Hi, Song,

On Tue, Jun 11, 2024 at 8:51 PM Song Gao <gaosong@xxxxxxxxxxx> wrote:
>
> Interrupts can be routed to maximal four virtual CPUs with one external
> hardware interrupt. Add the extioi virt extension support so that
> Interrupts can be routed to 256 vcpus on hypervior mode.
>
> Signed-off-by: Song Gao <gaosong@xxxxxxxxxxx>
> ---
> v2:
> - Simplify commit messages and code.
> - Follow the documented tip tree coding style.
> - Add the virtual extended interrupt model to the documentation.
> - Link to v1: https://lore.kernel.org/all/20240605070229.2569875-1-gaosong@xxxxxxxxxxx/
>
> .../arch/loongarch/irq-chip-model.rst | 28 +++++
> arch/loongarch/include/asm/irq.h | 1 +
> drivers/irqchip/irq-loongson-eiointc.c | 104 ++++++++++++++----
> 3 files changed, 113 insertions(+), 20 deletions(-)
>
> diff --git a/Documentation/arch/loongarch/irq-chip-model.rst b/Documentation/arch/loongarch/irq-chip-model.rst
> index 7988f4192363..7c10d070b318 100644
> --- a/Documentation/arch/loongarch/irq-chip-model.rst
> +++ b/Documentation/arch/loongarch/irq-chip-model.rst
> @@ -85,6 +85,34 @@ to CPUINTC directly::
> | Devices |
> +---------+
>
> +Virt extended IRQ model
> +=======================
Use "Virtual extended IRQ model".

> +
> +This model only work on hypervior mode. In this model, IPI (Inter-Processor Interrupt)
> +and CPU Local Timer interrupt go to CPUINTC directly, CPU UARTS interrupts go to
> +PCH_PIC, while all other devices interrupts go to PCH-PIC/PCH-LPC/PCH-MSI and
> +gathered by V-EIOINTC(Virt Extended I/O Interrupt Controller), and then go to
> +CPUINTC directly::
> +
> + +-----+ +-------------------+ +-------+
> + | IPI |--> | CPUINTC(0-255vcpu)| <-- | Timer |
> + +-----+ +-------------------+ +-------+
> + ^
> + |
> + +-----------+
> + | V-EIOINTC |
> + +-----------+
> + ^ ^
> + | |
> + +---------+ +---------+
> + | PCH-PIC | | PCH-MSI |
> + +---------+ +---------+
> + ^ ^ ^
> + | | |
> + +--------+ +---------+ +---------+
> + | UARTs | | Devices | | Devices |
> + +--------+ +---------+ +---------+
> +
You need to introduce virtual registers here, since there is no other
place to understand them.

And please also update the Chinese document.

Huacai

> ACPI-related definitions
> ========================
>
> diff --git a/arch/loongarch/include/asm/irq.h b/arch/loongarch/include/asm/irq.h
> index 480418bc5071..c97a7ab0e56f 100644
> --- a/arch/loongarch/include/asm/irq.h
> +++ b/arch/loongarch/include/asm/irq.h
> @@ -53,6 +53,7 @@ struct acpi_vector_group {
> extern struct acpi_vector_group pch_group[MAX_IO_PICS];
> extern struct acpi_vector_group msi_group[MAX_IO_PICS];
>
> +#define MAX_CORES_PER_EIO_NODE 256
> #define CORES_PER_EIO_NODE 4
>
> #define LOONGSON_CPU_UART0_VEC 10 /* CPU UART0 */
> diff --git a/drivers/irqchip/irq-loongson-eiointc.c b/drivers/irqchip/irq-loongson-eiointc.c
> index c7ddebf312ad..430fda9e8ce5 100644
> --- a/drivers/irqchip/irq-loongson-eiointc.c
> +++ b/drivers/irqchip/irq-loongson-eiointc.c
> @@ -23,15 +23,43 @@
> #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 MAX_EIO_NODES (NR_CPUS / CORES_PER_EIO_NODE)
>
> +/*
> + * Routing registers contain four vectors and have an offset of four to
> + * the base. The routing information is 8 bit wide.
> + */
> +
> +#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 {
> @@ -41,6 +69,7 @@ struct eiointc_priv {
> cpumask_t cpuspan_map;
> struct fwnode_handle *domain_handle;
> struct irq_domain *eiointc_domain;
> + bool cpu_encoded;
> };
>
> static struct eiointc_priv *eiointc_priv[MAX_IO_PICS];
> @@ -56,7 +85,9 @@ static void eiointc_enable(void)
>
> static int cpu_to_eio_node(int cpu)
> {
> - return cpu_logical_map(cpu) / CORES_PER_EIO_NODE;
> + int cores = cpu_has_hypervisor ? MAX_CORES_PER_EIO_NODE : CORES_PER_EIO_NODE;
> +
> + return cpu_logical_map(cpu) / cores;
> }
>
> #ifdef CONFIG_SMP
> @@ -88,6 +119,16 @@ static void eiointc_set_irq_route(int pos, unsigned int cpu, unsigned int mnode,
>
> static DEFINE_RAW_SPINLOCK(affinity_lock);
>
> +static void virt_extioi_set_irq_route(unsigned int vector, unsigned int cpu)
> +{
> + unsigned long reg = EIOINTC_REG_ROUTE_VEC(vector);
> + u32 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 int eiointc_set_irq_affinity(struct irq_data *d, const struct cpumask *affinity, bool force)
> {
> unsigned int cpu;
> @@ -104,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->cpu_encoded) {
> + iocsr_write32(EIOINTC_ALL_ENABLE_VEC_MASK(vector), regaddr);
> + virt_extioi_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));
>
> @@ -139,17 +186,20 @@ 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);
> + uint32_t data, node;
> + int i, bit, cores, index;
> +
> + 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) {
> + cores = (cpu_has_hypervisor ? MAX_CORES_PER_EIO_NODE : CORES_PER_EIO_NODE);
> +
> + if ((cpu_logical_map(cpu) % cores) == 0) {
> eiointc_enable();
>
> for (i = 0; i < eiointc_priv[0]->vec_count / 32; i++) {
> @@ -165,7 +215,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]->cpu_encoded)
> + bit = cpu_logical_map(0);
> + else if (index == 0)
> bit = BIT(cpu_logical_map(0));
> else
> bit = (eiointc_priv[index]->node << 4) | 1;
> @@ -369,6 +421,7 @@ static int __init acpi_cascade_irqdomain_init(void)
> static int __init eiointc_init(struct eiointc_priv *priv, int parent_irq,
> u64 node_map)
> {
> + u32 val;
> int i;
>
> node_map = node_map ? node_map : -1ULL;
> @@ -389,6 +442,17 @@ static int __init eiointc_init(struct eiointc_priv *priv, int parent_irq,
> return -ENOMEM;
> }
>
> + if (cpu_has_hypervisor) {
> + val = iocsr_read32(EXTIOI_VIRT_FEATURES);
> + if (val & BIT(EXTIOI_HAS_CPU_ENCODE)) {
> + val = iocsr_read32(EXTIOI_VIRT_CONFIG);
> + val |= BIT(EXTIOI_ENABLE_CPU_ENCODE);
> + iocsr_write32(val, EXTIOI_VIRT_CONFIG);
> + priv->cpu_encoded = true;
> + pr_info("loongson-extioi: enable cpu encodig \n");
> + }
> + }
> +
> eiointc_priv[nr_pics++] = priv;
> eiointc_router_init(0);
> irq_set_chained_handler_and_data(parent_irq, eiointc_irq_dispatch, priv);
> --
> 2.39.3
>