* [PATCH 0/2] Loongarch irq-redirect supprot
@ 2025-03-03 10:15 Tianyang Zhang
2025-03-03 10:15 ` [PATCH 1/2] Docs/LoongArch: Add Advanced Extended-Redirect IRQ model description Tianyang Zhang
2025-03-03 10:15 ` [PATCH 2/2] irq/irq-loongarch-ir:Add Redirect irqchip support Tianyang Zhang
0 siblings, 2 replies; 13+ messages in thread
From: Tianyang Zhang @ 2025-03-03 10:15 UTC (permalink / raw)
To: chenhuacai, kernel, corbet, alexs, si.yanteng, tglx, jiaxun.yang,
peterz, wangliupu, lvjianmin, maobibo, siyanteng, gaosong,
yangtiezhu
Cc: loongarch, linux-doc, linux-kernel, Tianyang Zhang
This series of patches introduces support for interrupt-redirect
controllers, and this hardware feature will be supported on 3C6000
for the first time
Tianyang Zhang (2):
Docs/LoongArch: Add Advanced Extended-Redirect IRQ model description
irq/irq-loongarch-ir:Add Redirect irqchip support
.../arch/loongarch/irq-chip-model.rst | 38 ++
.../zh_CN/arch/loongarch/irq-chip-model.rst | 37 ++
arch/loongarch/include/asm/cpu-features.h | 1 +
arch/loongarch/include/asm/cpu.h | 2 +
arch/loongarch/include/asm/loongarch.h | 7 +
arch/loongarch/kernel/cpu-probe.c | 2 +
drivers/irqchip/Makefile | 2 +-
drivers/irqchip/irq-loongarch-avec.c | 24 +-
drivers/irqchip/irq-loongarch-ir.c | 576 ++++++++++++++++++
drivers/irqchip/irq-loongson.h | 12 +
include/linux/cpuhotplug.h | 1 +
11 files changed, 687 insertions(+), 15 deletions(-)
create mode 100644 drivers/irqchip/irq-loongarch-ir.c
--
2.43.0
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 1/2] Docs/LoongArch: Add Advanced Extended-Redirect IRQ model description
2025-03-03 10:15 [PATCH 0/2] Loongarch irq-redirect supprot Tianyang Zhang
@ 2025-03-03 10:15 ` Tianyang Zhang
2025-03-03 13:36 ` Huacai Chen
2025-03-03 10:15 ` [PATCH 2/2] irq/irq-loongarch-ir:Add Redirect irqchip support Tianyang Zhang
1 sibling, 1 reply; 13+ messages in thread
From: Tianyang Zhang @ 2025-03-03 10:15 UTC (permalink / raw)
To: chenhuacai, kernel, corbet, alexs, si.yanteng, tglx, jiaxun.yang,
peterz, wangliupu, lvjianmin, maobibo, siyanteng, gaosong,
yangtiezhu
Cc: loongarch, linux-doc, linux-kernel, Tianyang Zhang
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=yes, Size: 4469 bytes --]
Introduce the Redirect interrupt controllers.When the redirected interrupt
controller is enabled, the routing target of MSI interrupts is no longer a
specific CPU and vector number, but a specific redirected entry. The actual
CPU and vector number used are described by the redirected entry.
Signed-off-by: Tianyang Zhang <zhangtianyang@loongson.cn>
---
.../arch/loongarch/irq-chip-model.rst | 38 +++++++++++++++++++
.../zh_CN/arch/loongarch/irq-chip-model.rst | 37 ++++++++++++++++++
2 files changed, 75 insertions(+)
diff --git a/Documentation/arch/loongarch/irq-chip-model.rst b/Documentation/arch/loongarch/irq-chip-model.rst
index a7ecce11e445..45cba22ff181 100644
--- a/Documentation/arch/loongarch/irq-chip-model.rst
+++ b/Documentation/arch/loongarch/irq-chip-model.rst
@@ -181,6 +181,44 @@ go to PCH-PIC/PCH-LPC and gathered by EIOINTC, and then go to CPUINTC directly::
| Devices |
+---------+
+Advanced Extended-Redirect IRQ model
+===============
+
+In this model, IPI (Inter-Processor Interrupt) and CPU Local Timer interrupt go
+to CPUINTC directly, CPU UARTS interrupts go to LIOINTC, PCH-MSI interrupts go
+to REDIRECT for remapping it to AVEC, and then go to CPUINTC directly, while all
+other devices interrupts go to PCH-PIC/PCH-LPC and gathered by EIOINTC, and then
+go to CPUINTC directly::
+
+ +-----+ +-----------------------+ +-------+
+ | IPI | --> | CPUINTC | <-- | Timer |
+ +-----+ +-----------------------+ +-------+
+ ^ ^ ^
+ | | |
+ +---------+ +----------+ +---------+ +-------+
+ | EIOINTC | | AVECINTC | | LIOINTC | <-- | UARTs |
+ +---------+ +----------+ +---------+ +-------+
+ ^ ^
+ | |
+ | +----------+
+ | | REDIRECT |
+ | +----------+
+ | ^
+ | |
+ +---------+ +---------+
+ | PCH-PIC | | PCH-MSI |
+ +---------+ +---------+
+ ^ ^ ^
+ | | |
+ +---------+ +---------+ +---------+
+ | Devices | | PCH-LPC | | Devices |
+ +---------+ +---------+ +---------+
+ ^
+ |
+ +---------+
+ | Devices |
+ +---------+
+
ACPI-related definitions
========================
diff --git a/Documentation/translations/zh_CN/arch/loongarch/irq-chip-model.rst b/Documentation/translations/zh_CN/arch/loongarch/irq-chip-model.rst
index d4ff80de47b6..d935da47ce3b 100644
--- a/Documentation/translations/zh_CN/arch/loongarch/irq-chip-model.rst
+++ b/Documentation/translations/zh_CN/arch/loongarch/irq-chip-model.rst
@@ -174,6 +174,43 @@ CPU串口(UARTs)中断发送到LIOINTC,PCH-MSI中断发送到AVECINTC,
| Devices |
+---------+
+高级扩展-重定向IRQ模型
+===============
+
+在这种模型里面,IPI(Inter-Processor Interrupt)和CPU本地时钟中断直接发送到CPUINTC,
+CPU串口(UARTs)中断发送到LIOINTC,PCH-MSI中断首先发送到REDIRECT模块,完成重定向后发
+送到AVECINTC,而后通过AVECINTC直接送达CPUINTC,而其他所有设备的中断则分别发送到所连
+接的PCH-PIC/PCH-LPC,然后由EIOINTC统一收集,再直接到达CPUINTC::
+
+ +-----+ +-----------------------+ +-------+
+ | IPI | --> | CPUINTC | <-- | Timer |
+ +-----+ +-----------------------+ +-------+
+ ^ ^ ^
+ | | |
+ +---------+ +----------+ +---------+ +-------+
+ | EIOINTC | | AVECINTC | | LIOINTC | <-- | UARTs |
+ +---------+ +----------+ +---------+ +-------+
+ ^ ^
+ | |
+ | +----------+
+ | | REDIRECT |
+ | +----------+
+ | ^
+ | |
+ +---------+ +---------+
+ | PCH-PIC | | PCH-MSI |
+ +---------+ +---------+
+ ^ ^ ^
+ | | |
+ +---------+ +---------+ +---------+
+ | Devices | | PCH-LPC | | Devices |
+ +---------+ +---------+ +---------+
+ ^
+ |
+ +---------+
+ | Devices |
+ +---------+
+
ACPI相关的定义
==============
--
2.43.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 2/2] irq/irq-loongarch-ir:Add Redirect irqchip support
2025-03-03 10:15 [PATCH 0/2] Loongarch irq-redirect supprot Tianyang Zhang
2025-03-03 10:15 ` [PATCH 1/2] Docs/LoongArch: Add Advanced Extended-Redirect IRQ model description Tianyang Zhang
@ 2025-03-03 10:15 ` Tianyang Zhang
2025-03-03 14:34 ` Huacai Chen
` (2 more replies)
1 sibling, 3 replies; 13+ messages in thread
From: Tianyang Zhang @ 2025-03-03 10:15 UTC (permalink / raw)
To: chenhuacai, kernel, corbet, alexs, si.yanteng, tglx, jiaxun.yang,
peterz, wangliupu, lvjianmin, maobibo, siyanteng, gaosong,
yangtiezhu
Cc: loongarch, linux-doc, linux-kernel, Tianyang Zhang
The main function of the Redirected interrupt controller is to manage the
redirected-interrupt table, which consists of many redirected entries.
When MSI interrupts are requested, the driver creates a corresponding
redirected entry that describes the target CPU/vector number and the
operating mode of the interrupt. The redirected interrupt module has an
independent cache, and during the interrupt routing process, it will
prioritize the redirected entries that hit the cache. The driver
invalidates certain entry caches via a command queue.
Co-developed-by: Liupu Wang <wangliupu@loongson.cn>
Signed-off-by: Liupu Wang <wangliupu@loongson.cn>
Signed-off-by: Tianyang Zhang <zhangtianyang@loongson.cn>
---
arch/loongarch/include/asm/cpu-features.h | 1 +
arch/loongarch/include/asm/cpu.h | 2 +
arch/loongarch/include/asm/loongarch.h | 7 +
arch/loongarch/kernel/cpu-probe.c | 2 +
drivers/irqchip/Makefile | 2 +-
drivers/irqchip/irq-loongarch-avec.c | 24 +-
drivers/irqchip/irq-loongarch-ir.c | 576 ++++++++++++++++++++++
drivers/irqchip/irq-loongson.h | 12 +
include/linux/cpuhotplug.h | 1 +
9 files changed, 612 insertions(+), 15 deletions(-)
create mode 100644 drivers/irqchip/irq-loongarch-ir.c
diff --git a/arch/loongarch/include/asm/cpu-features.h b/arch/loongarch/include/asm/cpu-features.h
index fc83bb32f9f0..03f7e93e81e0 100644
--- a/arch/loongarch/include/asm/cpu-features.h
+++ b/arch/loongarch/include/asm/cpu-features.h
@@ -68,5 +68,6 @@
#define cpu_has_ptw cpu_opt(LOONGARCH_CPU_PTW)
#define cpu_has_lspw cpu_opt(LOONGARCH_CPU_LSPW)
#define cpu_has_avecint cpu_opt(LOONGARCH_CPU_AVECINT)
+#define cpu_has_redirectint cpu_opt(LOONGARCH_CPU_REDIRECTINT)
#endif /* __ASM_CPU_FEATURES_H */
diff --git a/arch/loongarch/include/asm/cpu.h b/arch/loongarch/include/asm/cpu.h
index 98cf4d7b4b0a..33cd96e569d8 100644
--- a/arch/loongarch/include/asm/cpu.h
+++ b/arch/loongarch/include/asm/cpu.h
@@ -102,6 +102,7 @@ enum cpu_type_enum {
#define CPU_FEATURE_PTW 27 /* CPU has hardware page table walker */
#define CPU_FEATURE_LSPW 28 /* CPU has LSPW (lddir/ldpte instructions) */
#define CPU_FEATURE_AVECINT 29 /* CPU has AVEC interrupt */
+#define CPU_FEATURE_REDIRECTINT 30 /* CPU has interrupt remmap */
#define LOONGARCH_CPU_CPUCFG BIT_ULL(CPU_FEATURE_CPUCFG)
#define LOONGARCH_CPU_LAM BIT_ULL(CPU_FEATURE_LAM)
@@ -133,5 +134,6 @@ enum cpu_type_enum {
#define LOONGARCH_CPU_PTW BIT_ULL(CPU_FEATURE_PTW)
#define LOONGARCH_CPU_LSPW BIT_ULL(CPU_FEATURE_LSPW)
#define LOONGARCH_CPU_AVECINT BIT_ULL(CPU_FEATURE_AVECINT)
+#define LOONGARCH_CPU_REDIRECTINT BIT_ULL(CPU_FEATURE_REDIRECTINT)
#endif /* _ASM_CPU_H */
diff --git a/arch/loongarch/include/asm/loongarch.h b/arch/loongarch/include/asm/loongarch.h
index 52651aa0e583..3a27a5f6e460 100644
--- a/arch/loongarch/include/asm/loongarch.h
+++ b/arch/loongarch/include/asm/loongarch.h
@@ -1130,6 +1130,7 @@
#define IOCSRF_FLATMODE BIT_ULL(10)
#define IOCSRF_VM BIT_ULL(11)
#define IOCSRF_AVEC BIT_ULL(15)
+#define IOCSRF_REDIRECTINT BIT_ULL(16)
#define LOONGARCH_IOCSR_VENDOR 0x10
@@ -1142,6 +1143,7 @@
#define IOCSR_MISC_FUNC_TIMER_RESET BIT_ULL(21)
#define IOCSR_MISC_FUNC_EXT_IOI_EN BIT_ULL(48)
#define IOCSR_MISC_FUNC_AVEC_EN BIT_ULL(51)
+#define IOCSR_MISC_FUNC_REDIRECT_EN IOCSR_MISC_FUNC_AVEC_EN
#define LOONGARCH_IOCSR_CPUTEMP 0x428
@@ -1189,6 +1191,11 @@
#define LOONGARCH_IOCSR_EXTIOI_NODEMAP_BASE 0x14a0
#define LOONGARCH_IOCSR_EXTIOI_IPMAP_BASE 0x14c0
+#define LOONGARCH_IOCSR_REDIRECT_CFG 0x15e0
+#define LOONGARCH_IOCSR_REDIRECT_TBR 0x15e8 /* IRT BASE REG*/
+#define LOONGARCH_IOCSR_REDIRECT_CQB 0x15f0 /* IRT CACHE QUEUE BASE */
+#define LOONGARCH_IOCSR_REDIRECT_CQH 0x15f8 /* IRT_CACHE_QUEUE_HEAD 32bit */
+#define LOONGARCH_IOCSR_REDIRECT_CQT 0x15fc /* IRT_CACHE_QUEUE_TAIL 32bit */
#define LOONGARCH_IOCSR_EXTIOI_EN_BASE 0x1600
#define LOONGARCH_IOCSR_EXTIOI_BOUNCE_BASE 0x1680
#define LOONGARCH_IOCSR_EXTIOI_ISR_BASE 0x1800
diff --git a/arch/loongarch/kernel/cpu-probe.c b/arch/loongarch/kernel/cpu-probe.c
index fedaa67cde41..543474fd1399 100644
--- a/arch/loongarch/kernel/cpu-probe.c
+++ b/arch/loongarch/kernel/cpu-probe.c
@@ -289,6 +289,8 @@ static inline void cpu_probe_loongson(struct cpuinfo_loongarch *c, unsigned int
c->options |= LOONGARCH_CPU_EIODECODE;
if (config & IOCSRF_AVEC)
c->options |= LOONGARCH_CPU_AVECINT;
+ if (config & IOCSRF_REDIRECTINT)
+ c->options |= LOONGARCH_CPU_REDIRECTINT;
if (config & IOCSRF_VM)
c->options |= LOONGARCH_CPU_HYPERVISOR;
}
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 25e9ad29b8c4..5dd7d6b151d9 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -113,7 +113,7 @@ obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o
obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o
obj-$(CONFIG_TI_SCI_INTA_IRQCHIP) += irq-ti-sci-inta.o
obj-$(CONFIG_TI_PRUSS_INTC) += irq-pruss-intc.o
-obj-$(CONFIG_IRQ_LOONGARCH_CPU) += irq-loongarch-cpu.o irq-loongarch-avec.o
+obj-$(CONFIG_IRQ_LOONGARCH_CPU) += irq-loongarch-cpu.o irq-loongarch-avec.o irq-loongarch-ir.o
obj-$(CONFIG_LOONGSON_LIOINTC) += irq-loongson-liointc.o
obj-$(CONFIG_LOONGSON_EIOINTC) += irq-loongson-eiointc.o
obj-$(CONFIG_LOONGSON_HTPIC) += irq-loongson-htpic.o
diff --git a/drivers/irqchip/irq-loongarch-avec.c b/drivers/irqchip/irq-loongarch-avec.c
index 80e55955a29f..101bae6f981c 100644
--- a/drivers/irqchip/irq-loongarch-avec.c
+++ b/drivers/irqchip/irq-loongarch-avec.c
@@ -24,7 +24,6 @@
#define VECTORS_PER_REG 64
#define IRR_VECTOR_MASK 0xffUL
#define IRR_INVALID_MASK 0x80000000UL
-#define AVEC_MSG_OFFSET 0x100000
#ifdef CONFIG_SMP
struct pending_list {
@@ -47,15 +46,6 @@ struct avecintc_chip {
static struct avecintc_chip loongarch_avec;
-struct avecintc_data {
- struct list_head entry;
- unsigned int cpu;
- unsigned int vec;
- unsigned int prev_cpu;
- unsigned int prev_vec;
- unsigned int moving;
-};
-
static inline void avecintc_enable(void)
{
u64 value;
@@ -85,7 +75,7 @@ static inline void pending_list_init(int cpu)
INIT_LIST_HEAD(&plist->head);
}
-static void avecintc_sync(struct avecintc_data *adata)
+void avecintc_sync(struct avecintc_data *adata)
{
struct pending_list *plist;
@@ -109,7 +99,7 @@ static int avecintc_set_affinity(struct irq_data *data, const struct cpumask *de
return -EBUSY;
if (cpu_online(adata->cpu) && cpumask_test_cpu(adata->cpu, dest))
- return 0;
+ return IRQ_SET_MASK_OK_DONE;
cpumask_and(&intersect_mask, dest, cpu_online_mask);
@@ -121,7 +111,8 @@ static int avecintc_set_affinity(struct irq_data *data, const struct cpumask *de
adata->cpu = cpu;
adata->vec = vector;
per_cpu_ptr(irq_map, adata->cpu)[adata->vec] = irq_data_to_desc(data);
- avecintc_sync(adata);
+ if (!cpu_has_redirectint)
+ avecintc_sync(adata);
}
irq_data_update_effective_affinity(data, cpumask_of(cpu));
@@ -136,7 +127,8 @@ static int avecintc_cpu_online(unsigned int cpu)
guard(raw_spinlock)(&loongarch_avec.lock);
- avecintc_enable();
+ if (!cpu_has_redirectint)
+ avecintc_enable();
irq_matrix_online(loongarch_avec.vector_matrix);
@@ -242,6 +234,7 @@ static void avecintc_irq_dispatch(struct irq_desc *desc)
d = this_cpu_read(irq_map[vector]);
if (d) {
generic_handle_irq_desc(d);
+
} else {
spurious_interrupt();
pr_warn("Unexpected IRQ occurs on CPU#%d [vector %ld]\n", smp_processor_id(), vector);
@@ -412,6 +405,9 @@ static int __init pch_msi_parse_madt(union acpi_subtable_headers *header,
static inline int __init acpi_cascade_irqdomain_init(void)
{
+ if (cpu_has_redirectint)
+ return loongarch_irq_redirect_init(loongarch_avec.domain);
+
return acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, pch_msi_parse_madt, 1);
}
diff --git a/drivers/irqchip/irq-loongarch-ir.c b/drivers/irqchip/irq-loongarch-ir.c
new file mode 100644
index 000000000000..d3c664d6f98e
--- /dev/null
+++ b/drivers/irqchip/irq-loongarch-ir.c
@@ -0,0 +1,576 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Loongson Technologies, Inc.
+ */
+
+#include <linux/cpuhotplug.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <linux/spinlock.h>
+#include <linux/msi.h>
+
+#include <asm/irq.h>
+#include <asm/loongarch.h>
+#include <asm/setup.h>
+#include <larchintrin.h>
+
+#include "irq-loongson.h"
+#include "irq-msi-lib.h"
+
+#define IRD_ENTRIES 65536
+
+/* redirect entry size 128bits */
+#if (defined(CONFIG_PAGE_SIZE_4KB))
+#define IRD_PAGE_ORDER 8
+#elif (defined(CONFIG_PAGE_SIZE_16KB))
+#define IRD_PAGE_ORDER 6
+#endif
+
+/* irt cache invalid queue */
+#define INVALID_QUEUE_SIZE 4096
+
+#if (defined(CONFIG_PAGE_SIZE_4KB))
+#define INVALID_QUEUE_PAGE_ORDER 4
+#elif (defined(CONFIG_PAGE_SIZE_16KB))
+#define INVALID_QUEUE_PAGE_ORDER 2
+#endif
+
+#define GPID_ADDR_MASK 0x3ffffffffffULL
+#define GPID_ADDR_SHIFT 6
+
+#define CQB_SIZE_SHIFT 0
+#define CQB_SIZE_MASK 0xf
+#define CQB_ADDR_SHIFT 12
+#define CQB_ADDR_MASK (0xfffffffffULL)
+
+#define CFG_DISABLE_IDLE 2
+#define INVALID_INDEX 0
+
+#define MAX_IR_ENGINES 16
+
+struct irq_domain *redirect_domain;
+struct redirect_entry {
+ struct {
+ __u64 valid : 1,
+ res1 : 5,
+ gpid : 42,
+ res2 : 8,
+ vector : 8;
+ } lo;
+ __u64 hi;
+};
+
+struct redirect_gpid {
+ u64 pir[4]; /* Pending interrupt requested */
+ u8 en : 1, /* doorbell */
+ res0 : 7;
+ u8 irqnum;
+ u16 res1;
+ u32 dst;
+ u32 rsvd[6];
+} __aligned(64);
+
+struct irde_inv_cmd {
+ union {
+ __u64 cmd_info;
+ struct {
+ __u64 res1 : 4,
+ type : 1,
+ need_notice : 1,
+ pad : 2,
+ index : 16,
+ pad2 : 40;
+ } index;
+ };
+ __u64 notice_addr;
+};
+
+struct redirect_item {
+ int index;
+ struct redirect_entry *entry;
+ struct redirect_gpid *gpid;
+ struct redirect_table *table;
+};
+
+struct redirect_table {
+ int node;
+ struct redirect_entry *table;
+ unsigned long *bitmap;
+ unsigned int nr_ird;
+ struct page *page;
+ raw_spinlock_t lock;
+};
+
+struct redirect_queue {
+ int node;
+ u64 base;
+ u32 max_size;
+ int head;
+ int tail;
+ struct page *page;
+ raw_spinlock_t lock;
+};
+
+struct la_irde_desc {
+ struct redirect_table ird_table;
+ struct redirect_queue inv_queue;
+};
+
+struct smp_invalid_arg {
+ struct redirect_queue *queue;
+ struct irde_inv_cmd *cmd;
+};
+
+static struct la_irde_desc irde_descs[MAX_IR_ENGINES];
+static phys_addr_t msi_base_addr;
+
+static inline void redirect_enable(int node)
+{
+ u64 value;
+
+ if (redirect_domain) {
+ value = iocsr_read64(LOONGARCH_IOCSR_MISC_FUNC);
+ value |= IOCSR_MISC_FUNC_REDIRECT_EN;
+ iocsr_write64(value, LOONGARCH_IOCSR_MISC_FUNC);
+ }
+}
+
+static inline bool invalid_queue_is_full(int node_id, u32 *tail)
+{
+ u32 head;
+
+ head = iocsr_read32(LOONGARCH_IOCSR_REDIRECT_CQH);
+ *tail = iocsr_read32(LOONGARCH_IOCSR_REDIRECT_CQT);
+
+ return !!(head == ((*tail + 1) % INVALID_QUEUE_SIZE));
+}
+
+static void invalid_enqueue(struct redirect_queue *rqueue, struct irde_inv_cmd *cmd)
+{
+ struct irde_inv_cmd *inv_addr;
+ u32 tail;
+
+ guard(raw_spinlock_irqsave)(&rqueue->lock);
+
+ while (invalid_queue_is_full(rqueue->node, &tail))
+ cpu_relax();
+
+ inv_addr = (struct irde_inv_cmd *)(rqueue->base + tail * sizeof(struct irde_inv_cmd));
+ memcpy(inv_addr, cmd, sizeof(struct irde_inv_cmd));
+ tail = (tail + 1) % INVALID_QUEUE_SIZE;
+
+ wmb();
+
+ iocsr_write32(tail, LOONGARCH_IOCSR_REDIRECT_CQT);
+}
+
+static void smp_call_invalid_enqueue(void *arg)
+{
+ struct smp_invalid_arg *s_arg = (struct smp_invalid_arg *)arg;
+
+ invalid_enqueue(s_arg->queue, s_arg->cmd);
+}
+
+static void irde_invlid_entry_node(struct redirect_item *item)
+{
+ struct redirect_queue *rqueue;
+ struct smp_invalid_arg arg;
+ struct irde_inv_cmd cmd;
+ volatile u64 raddr = 0;
+ int node = item->table->node, cpu;
+
+ rqueue = &(irde_descs[node].inv_queue);
+ cmd.cmd_info = 0;
+ cmd.index.type = INVALID_INDEX;
+ cmd.index.need_notice = 1;
+ cmd.index.index = item->index;
+ cmd.notice_addr = (u64)(__pa(&raddr));
+
+ if (cpu_to_node(smp_processor_id()) == node)
+ invalid_enqueue(rqueue, &cmd);
+ else {
+ for_each_cpu(cpu, cpumask_of_node(node)) {
+ if (cpu_online(cpu))
+ break;
+ }
+ arg.queue = rqueue;
+ arg.cmd = &cmd;
+ smp_call_function_single(cpu, smp_call_invalid_enqueue, &arg, 0);
+ }
+
+ while (!raddr)
+ cpu_relax();
+
+}
+
+static inline struct avecintc_data *irq_data_get_avec_data(struct irq_data *data)
+{
+ return data->parent_data->chip_data;
+}
+
+static int redirect_table_alloc(struct redirect_item *item, struct redirect_table *ird_table)
+{
+ int index;
+
+ guard(raw_spinlock_irqsave)(&ird_table->lock);
+
+ index = find_first_zero_bit(ird_table->bitmap, IRD_ENTRIES);
+ if (index > IRD_ENTRIES) {
+ pr_err("No redirect entry to use\n");
+ return -ENOMEM;
+ }
+
+ __set_bit(index, ird_table->bitmap);
+
+ item->index = index;
+ item->entry = &ird_table->table[index];
+ item->table = ird_table;
+
+ return 0;
+}
+
+static int redirect_table_free(struct redirect_item *item)
+{
+ struct redirect_table *ird_table;
+ struct redirect_entry *entry;
+ unsigned long flags;
+
+ ird_table = item->table;
+
+ entry = item->entry;
+ memset(entry, 0, sizeof(struct redirect_entry));
+
+ raw_spin_lock_irqsave(&ird_table->lock, flags);
+ bitmap_release_region(ird_table->bitmap, item->index, 0);
+ raw_spin_unlock_irqrestore(&ird_table->lock, flags);
+
+ kfree(item->gpid);
+
+ irde_invlid_entry_node(item);
+
+ return 0;
+}
+
+static inline void redirect_domain_prepare_entry(struct redirect_item *item, struct avecintc_data *adata)
+{
+ struct redirect_entry *entry = item->entry;
+
+ item->gpid->en = 1;
+ item->gpid->irqnum = adata->vec;
+ item->gpid->dst = adata->cpu;
+
+ entry->lo.valid = 1;
+ entry->lo.gpid = ((long)item->gpid >> GPID_ADDR_SHIFT) & (GPID_ADDR_MASK);
+ entry->lo.vector = 0xff;
+ wmb();
+}
+
+static int loongarch_redirect_set_affinity(struct irq_data *data, const struct cpumask *dest, bool force)
+{
+ struct redirect_item *item = data->chip_data;
+ struct avecintc_data *adata;
+ int ret;
+
+ ret = irq_chip_set_affinity_parent(data, dest, force);
+ if (ret == IRQ_SET_MASK_OK_DONE)
+ return IRQ_SET_MASK_OK;
+ else if (ret) {
+ pr_err("IRDE:set_affinity error %d\n", ret);
+ return ret;
+ }
+
+ adata = irq_data_get_avec_data(data);
+
+ redirect_domain_prepare_entry(item, adata);
+
+ irde_invlid_entry_node(item);
+
+ avecintc_sync(adata);
+ return IRQ_SET_MASK_OK;
+}
+
+static void loongarch_redirect_compose_msg(struct irq_data *d, struct msi_msg *msg)
+{
+ struct redirect_item *item;
+
+ item = irq_data_get_irq_chip_data(d);
+ msg->address_lo = (msi_base_addr | 1 << 2 | ((item->index & 0xffff) << 4));
+ msg->address_hi = 0x0;
+ msg->data = 0x0;
+}
+
+static inline void loongarch_redirect_ack_irq(struct irq_data *d)
+{
+}
+
+static inline void loongarch_redirect_unmask_irq(struct irq_data *d)
+{
+}
+
+static inline void loongarch_redirect_mask_irq(struct irq_data *d)
+{
+}
+
+static struct irq_chip loongarch_redirect_chip = {
+ .name = "redirect",
+ .irq_ack = loongarch_redirect_ack_irq,
+ .irq_mask = loongarch_redirect_mask_irq,
+ .irq_unmask = loongarch_redirect_unmask_irq,
+ .irq_set_affinity = loongarch_redirect_set_affinity,
+ .irq_compose_msi_msg = loongarch_redirect_compose_msg,
+};
+
+static void loongarch_irq_redirect_free_resources(struct irq_domain *domain,
+ unsigned int virq, unsigned int nr_irqs)
+{
+ struct irq_data *irq_data;
+ struct redirect_item *item;
+
+ for (int i = 0; i < nr_irqs; i++) {
+ irq_data = irq_domain_get_irq_data(domain, virq + i);
+ if (irq_data && irq_data->chip_data) {
+ item = irq_data->chip_data;
+ redirect_table_free(item);
+ kfree(item);
+ }
+ }
+}
+
+static int loongarch_irq_redirect_alloc(struct irq_domain *domain,
+ unsigned int virq, unsigned int nr_irqs,
+ void *arg)
+{
+ struct redirect_table *ird_table;
+ struct avecintc_data *avec_data;
+ struct irq_data *irq_data;
+ int ret, i, node;
+
+ node = ((msi_alloc_info_t *)arg)->desc->dev->numa_node;
+ ird_table = &irde_descs[node].ird_table;
+ ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < nr_irqs; i++) {
+ struct redirect_item *item;
+
+ item = kzalloc(sizeof(struct redirect_item), GFP_KERNEL);
+ if (!item) {
+ pr_err("Alloc redirect descriptor failed\n");
+ goto out_free_resources;
+ }
+
+ irq_data = irq_domain_get_irq_data(domain, virq + i);
+
+ avec_data = irq_data_get_avec_data(irq_data);
+ ret = redirect_table_alloc(item, ird_table);
+ if (ret) {
+ pr_err("Alloc redirect table entry failed\n");
+ goto out_free_resources;
+ }
+
+ item->gpid = kzalloc_node(sizeof(struct redirect_gpid), GFP_KERNEL, node);
+ if (!item->gpid) {
+ pr_err("Alloc redirect GPID failed\n");
+ goto out_free_resources;
+ }
+
+ irq_data->chip_data = item;
+ irq_data->chip = &loongarch_redirect_chip;
+ redirect_domain_prepare_entry(item, avec_data);
+ }
+ return 0;
+
+out_free_resources:
+ loongarch_irq_redirect_free_resources(domain, virq, nr_irqs);
+ irq_domain_free_irqs_common(domain, virq, nr_irqs);
+
+ return -EINVAL;
+}
+
+static void loongarch_irq_redirect_free(struct irq_domain *domain,
+ unsigned int virq, unsigned int nr_irqs)
+{
+ loongarch_irq_redirect_free_resources(domain, virq, nr_irqs);
+ return irq_domain_free_irqs_common(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops loongarch_redirect_domain_ops = {
+ .alloc = loongarch_irq_redirect_alloc,
+ .free = loongarch_irq_redirect_free,
+ .select = msi_lib_irq_domain_select,
+};
+
+static int redirect_queue_init(int node)
+{
+ struct redirect_queue *rqueue = &(irde_descs[node].inv_queue);
+ struct page *pages;
+
+ pages = alloc_pages_node(0, GFP_KERNEL | __GFP_ZERO, INVALID_QUEUE_PAGE_ORDER);
+ if (!pages) {
+ pr_err("Node [%d] Invalid Queue alloc pages failed!\n", node);
+ return -ENOMEM;
+ }
+
+ rqueue->page = pages;
+ rqueue->base = (u64)page_address(pages);
+ rqueue->max_size = INVALID_QUEUE_SIZE;
+ rqueue->head = 0;
+ rqueue->tail = 0;
+ rqueue->node = node;
+ raw_spin_lock_init(&rqueue->lock);
+
+ iocsr_write32(0, LOONGARCH_IOCSR_REDIRECT_CQH);
+ iocsr_write32(0, LOONGARCH_IOCSR_REDIRECT_CQT);
+ iocsr_write64(((rqueue->base & (CQB_ADDR_MASK << CQB_ADDR_SHIFT)) |
+ (CQB_SIZE_MASK << CQB_SIZE_SHIFT)), LOONGARCH_IOCSR_REDIRECT_CQB);
+ return 0;
+}
+
+static int redirect_table_init(int node)
+{
+ struct redirect_table *ird_table = &(irde_descs[node].ird_table);
+ struct page *pages;
+ unsigned long *bitmap;
+
+ pages = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, IRD_PAGE_ORDER);
+ if (!pages) {
+ pr_err("Node [%d] redirect table alloc pages failed!\n", node);
+ return -ENOMEM;
+ }
+ ird_table->page = pages;
+ ird_table->table = page_address(pages);
+
+ bitmap = bitmap_zalloc(IRD_ENTRIES, GFP_KERNEL);
+ if (!bitmap) {
+ pr_err("Node [%d] redirect table bitmap alloc pages failed!\n", node);
+ return -ENOMEM;
+ }
+
+ ird_table->bitmap = bitmap;
+ ird_table->nr_ird = IRD_ENTRIES;
+ ird_table->node = node;
+
+ raw_spin_lock_init(&ird_table->lock);
+
+ if (redirect_queue_init(node))
+ return -EINVAL;
+
+ iocsr_write64(CFG_DISABLE_IDLE, LOONGARCH_IOCSR_REDIRECT_CFG);
+ iocsr_write64(__pa(ird_table->table), LOONGARCH_IOCSR_REDIRECT_TBR);
+
+ return 0;
+}
+
+static void redirect_table_fini(int node)
+{
+ struct redirect_table *ird_table = &(irde_descs[node].ird_table);
+ struct redirect_queue *rqueue = &(irde_descs[node].inv_queue);
+
+ if (ird_table->page) {
+ __free_pages(ird_table->page, IRD_PAGE_ORDER);
+ ird_table->table = NULL;
+ ird_table->page = NULL;
+ }
+
+ if (ird_table->page) {
+ bitmap_free(ird_table->bitmap);
+ ird_table->bitmap = NULL;
+ }
+
+ if (rqueue->page) {
+ __free_pages(rqueue->page, INVALID_QUEUE_PAGE_ORDER);
+ rqueue->page = NULL;
+ rqueue->base = 0;
+ }
+
+ iocsr_write64(0, LOONGARCH_IOCSR_REDIRECT_CQB);
+ iocsr_write64(0, LOONGARCH_IOCSR_REDIRECT_TBR);
+}
+
+static int redirect_cpu_online(unsigned int cpu)
+{
+ int ret, node = cpu_to_node(cpu);
+
+ if (cpu != cpumask_first(cpumask_of_node(node)))
+ return 0;
+
+ ret = redirect_table_init(node);
+ if (ret) {
+ redirect_table_fini(node);
+ return -EINVAL;
+ }
+
+ redirect_enable(node);
+ return 0;
+}
+
+#if defined(CONFIG_ACPI)
+static int __init pch_msi_parse_madt(union acpi_subtable_headers *header,
+ const unsigned long end)
+{
+ struct acpi_madt_msi_pic *pchmsi_entry = (struct acpi_madt_msi_pic *)header;
+
+ msi_base_addr = pchmsi_entry->msg_address - AVEC_MSG_OFFSET;
+
+ return pch_msi_acpi_init_avec(redirect_domain);
+}
+
+static int __init acpi_cascade_irqdomain_init(void)
+{
+ return acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, pch_msi_parse_madt, 1);
+}
+
+int __init loongarch_irq_redirect_init(struct irq_domain *parent)
+{
+ struct fwnode_handle *fwnode;
+ struct irq_domain *domain;
+ int ret;
+
+ fwnode = irq_domain_alloc_named_fwnode("redirect");
+ if (!fwnode) {
+ pr_err("Unable to alloc redirect domain handle\n");
+ goto fail;
+ }
+
+ domain = irq_domain_create_hierarchy(parent, 0, IRD_ENTRIES, fwnode,
+ &loongarch_redirect_domain_ops, irde_descs);
+ if (!domain) {
+ pr_err("Unable to alloc redirect domain\n");
+ goto out_free_fwnode;
+ }
+
+ redirect_domain = domain;
+
+ ret = redirect_table_init(0);
+ if (ret)
+ goto out_free_table;
+
+ ret = acpi_cascade_irqdomain_init();
+ if (ret < 0) {
+ pr_err("Failed to cascade IRQ domain, ret=%d\n", ret);
+ goto out_free_table;
+ }
+
+ cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_REDIRECT_STARTING,
+ "irqchip/loongarch/redirect:starting",
+ redirect_cpu_online, NULL);
+
+ pr_info("loongarch irq redirect modules init succeeded\n");
+ redirect_enable(0);
+ return 0;
+
+out_free_table:
+ redirect_table_fini(0);
+ irq_domain_remove(redirect_domain);
+ redirect_domain = NULL;
+out_free_fwnode:
+ irq_domain_free_fwnode(fwnode);
+fail:
+ return -EINVAL;
+}
+#endif
diff --git a/drivers/irqchip/irq-loongson.h b/drivers/irqchip/irq-loongson.h
index 11fa138d1f44..f29ab8ec368e 100644
--- a/drivers/irqchip/irq-loongson.h
+++ b/drivers/irqchip/irq-loongson.h
@@ -5,6 +5,15 @@
#ifndef _DRIVERS_IRQCHIP_IRQ_LOONGSON_H
#define _DRIVERS_IRQCHIP_IRQ_LOONGSON_H
+#define AVEC_MSG_OFFSET 0x100000
+struct avecintc_data {
+ struct list_head entry;
+ unsigned int cpu;
+ unsigned int vec;
+ unsigned int prev_cpu;
+ unsigned int prev_vec;
+ unsigned int moving;
+};
int find_pch_pic(u32 gsi);
@@ -24,4 +33,7 @@ int pch_msi_acpi_init(struct irq_domain *parent,
struct acpi_madt_msi_pic *acpi_pchmsi);
int pch_msi_acpi_init_avec(struct irq_domain *parent);
+int loongarch_irq_redirect_init(struct irq_domain *parent);
+
+void avecintc_sync(struct avecintc_data *adata);
#endif /* _DRIVERS_IRQCHIP_IRQ_LOONGSON_H */
diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
index 6cc5e484547c..2fd5531fa378 100644
--- a/include/linux/cpuhotplug.h
+++ b/include/linux/cpuhotplug.h
@@ -146,6 +146,7 @@ enum cpuhp_state {
CPUHP_AP_IRQ_MIPS_GIC_STARTING,
CPUHP_AP_IRQ_EIOINTC_STARTING,
CPUHP_AP_IRQ_AVECINTC_STARTING,
+ CPUHP_AP_IRQ_REDIRECT_STARTING,
CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING,
CPUHP_AP_IRQ_THEAD_ACLINT_SSWI_STARTING,
CPUHP_AP_IRQ_RISCV_IMSIC_STARTING,
--
2.43.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH 1/2] Docs/LoongArch: Add Advanced Extended-Redirect IRQ model description
2025-03-03 10:15 ` [PATCH 1/2] Docs/LoongArch: Add Advanced Extended-Redirect IRQ model description Tianyang Zhang
@ 2025-03-03 13:36 ` Huacai Chen
2025-03-06 2:09 ` Tianyang Zhang
0 siblings, 1 reply; 13+ messages in thread
From: Huacai Chen @ 2025-03-03 13:36 UTC (permalink / raw)
To: Tianyang Zhang
Cc: kernel, corbet, alexs, si.yanteng, tglx, jiaxun.yang, peterz,
wangliupu, lvjianmin, maobibo, siyanteng, gaosong, yangtiezhu,
loongarch, linux-doc, linux-kernel
Hi, Tianyang,
On Mon, Mar 3, 2025 at 6:15 PM Tianyang Zhang <zhangtianyang@loongson.cn> wrote:
>
> Introduce the Redirect interrupt controllers.When the redirected interrupt
> controller is enabled, the routing target of MSI interrupts is no longer a
> specific CPU and vector number, but a specific redirected entry. The actual
> CPU and vector number used are described by the redirected entry.
You call it "redirect interrupt controller", then don't call
"redirected interrupt controller" in other place.
>
> Signed-off-by: Tianyang Zhang <zhangtianyang@loongson.cn>
> ---
> .../arch/loongarch/irq-chip-model.rst | 38 +++++++++++++++++++
> .../zh_CN/arch/loongarch/irq-chip-model.rst | 37 ++++++++++++++++++
> 2 files changed, 75 insertions(+)
>
> diff --git a/Documentation/arch/loongarch/irq-chip-model.rst b/Documentation/arch/loongarch/irq-chip-model.rst
> index a7ecce11e445..45cba22ff181 100644
> --- a/Documentation/arch/loongarch/irq-chip-model.rst
> +++ b/Documentation/arch/loongarch/irq-chip-model.rst
> @@ -181,6 +181,44 @@ go to PCH-PIC/PCH-LPC and gathered by EIOINTC, and then go to CPUINTC directly::
> | Devices |
> +---------+
>
> +Advanced Extended-Redirect IRQ model
Call it as "Advanced Extended IRQ model (with redirection)" and
"高级扩展IRQ模型 (带重定向)"
Huacai
> +===============
> +
> +In this model, IPI (Inter-Processor Interrupt) and CPU Local Timer interrupt go
> +to CPUINTC directly, CPU UARTS interrupts go to LIOINTC, PCH-MSI interrupts go
> +to REDIRECT for remapping it to AVEC, and then go to CPUINTC directly, while all
> +other devices interrupts go to PCH-PIC/PCH-LPC and gathered by EIOINTC, and then
> +go to CPUINTC directly::
> +
> + +-----+ +-----------------------+ +-------+
> + | IPI | --> | CPUINTC | <-- | Timer |
> + +-----+ +-----------------------+ +-------+
> + ^ ^ ^
> + | | |
> + +---------+ +----------+ +---------+ +-------+
> + | EIOINTC | | AVECINTC | | LIOINTC | <-- | UARTs |
> + +---------+ +----------+ +---------+ +-------+
> + ^ ^
> + | |
> + | +----------+
> + | | REDIRECT |
> + | +----------+
> + | ^
> + | |
> + +---------+ +---------+
> + | PCH-PIC | | PCH-MSI |
> + +---------+ +---------+
> + ^ ^ ^
> + | | |
> + +---------+ +---------+ +---------+
> + | Devices | | PCH-LPC | | Devices |
> + +---------+ +---------+ +---------+
> + ^
> + |
> + +---------+
> + | Devices |
> + +---------+
> +
> ACPI-related definitions
> ========================
>
> diff --git a/Documentation/translations/zh_CN/arch/loongarch/irq-chip-model.rst b/Documentation/translations/zh_CN/arch/loongarch/irq-chip-model.rst
> index d4ff80de47b6..d935da47ce3b 100644
> --- a/Documentation/translations/zh_CN/arch/loongarch/irq-chip-model.rst
> +++ b/Documentation/translations/zh_CN/arch/loongarch/irq-chip-model.rst
> @@ -174,6 +174,43 @@ CPU串口(UARTs)中断发送到LIOINTC,PCH-MSI中断发送到AVECINTC,
> | Devices |
> +---------+
>
> +高级扩展-重定向IRQ模型
> +===============
> +
> +在这种模型里面,IPI(Inter-Processor Interrupt)和CPU本地时钟中断直接发送到CPUINTC,
> +CPU串口(UARTs)中断发送到LIOINTC,PCH-MSI中断首先发送到REDIRECT模块,完成重定向后发
> +送到AVECINTC,而后通过AVECINTC直接送达CPUINTC,而其他所有设备的中断则分别发送到所连
> +接的PCH-PIC/PCH-LPC,然后由EIOINTC统一收集,再直接到达CPUINTC::
> +
> + +-----+ +-----------------------+ +-------+
> + | IPI | --> | CPUINTC | <-- | Timer |
> + +-----+ +-----------------------+ +-------+
> + ^ ^ ^
> + | | |
> + +---------+ +----------+ +---------+ +-------+
> + | EIOINTC | | AVECINTC | | LIOINTC | <-- | UARTs |
> + +---------+ +----------+ +---------+ +-------+
> + ^ ^
> + | |
> + | +----------+
> + | | REDIRECT |
> + | +----------+
> + | ^
> + | |
> + +---------+ +---------+
> + | PCH-PIC | | PCH-MSI |
> + +---------+ +---------+
> + ^ ^ ^
> + | | |
> + +---------+ +---------+ +---------+
> + | Devices | | PCH-LPC | | Devices |
> + +---------+ +---------+ +---------+
> + ^
> + |
> + +---------+
> + | Devices |
> + +---------+
> +
> ACPI相关的定义
> ==============
>
> --
> 2.43.0
>
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/2] irq/irq-loongarch-ir:Add Redirect irqchip support
2025-03-03 10:15 ` [PATCH 2/2] irq/irq-loongarch-ir:Add Redirect irqchip support Tianyang Zhang
@ 2025-03-03 14:34 ` Huacai Chen
2025-03-03 21:28 ` Thomas Gleixner
2025-03-06 2:06 ` Tianyang Zhang
2025-03-04 11:24 ` kernel test robot
2025-03-05 14:47 ` kernel test robot
2 siblings, 2 replies; 13+ messages in thread
From: Huacai Chen @ 2025-03-03 14:34 UTC (permalink / raw)
To: Tianyang Zhang
Cc: kernel, corbet, alexs, si.yanteng, tglx, jiaxun.yang, peterz,
wangliupu, lvjianmin, maobibo, siyanteng, gaosong, yangtiezhu,
loongarch, linux-doc, linux-kernel
Hi, Tianyang,
The title can be "irq/loongarch-ir: Add Redirect irqchip support".
On Mon, Mar 3, 2025 at 6:15 PM Tianyang Zhang <zhangtianyang@loongson.cn> wrote:
>
> The main function of the Redirected interrupt controller is to manage the
> redirected-interrupt table, which consists of many redirected entries.
> When MSI interrupts are requested, the driver creates a corresponding
> redirected entry that describes the target CPU/vector number and the
> operating mode of the interrupt. The redirected interrupt module has an
> independent cache, and during the interrupt routing process, it will
> prioritize the redirected entries that hit the cache. The driver
> invalidates certain entry caches via a command queue.
>
> Co-developed-by: Liupu Wang <wangliupu@loongson.cn>
> Signed-off-by: Liupu Wang <wangliupu@loongson.cn>
> Signed-off-by: Tianyang Zhang <zhangtianyang@loongson.cn>
> ---
> arch/loongarch/include/asm/cpu-features.h | 1 +
> arch/loongarch/include/asm/cpu.h | 2 +
> arch/loongarch/include/asm/loongarch.h | 7 +
> arch/loongarch/kernel/cpu-probe.c | 2 +
> drivers/irqchip/Makefile | 2 +-
> drivers/irqchip/irq-loongarch-avec.c | 24 +-
> drivers/irqchip/irq-loongarch-ir.c | 576 ++++++++++++++++++++++
> drivers/irqchip/irq-loongson.h | 12 +
> include/linux/cpuhotplug.h | 1 +
> 9 files changed, 612 insertions(+), 15 deletions(-)
> create mode 100644 drivers/irqchip/irq-loongarch-ir.c
>
> diff --git a/arch/loongarch/include/asm/cpu-features.h b/arch/loongarch/include/asm/cpu-features.h
> index fc83bb32f9f0..03f7e93e81e0 100644
> --- a/arch/loongarch/include/asm/cpu-features.h
> +++ b/arch/loongarch/include/asm/cpu-features.h
> @@ -68,5 +68,6 @@
> #define cpu_has_ptw cpu_opt(LOONGARCH_CPU_PTW)
> #define cpu_has_lspw cpu_opt(LOONGARCH_CPU_LSPW)
> #define cpu_has_avecint cpu_opt(LOONGARCH_CPU_AVECINT)
> +#define cpu_has_redirectint cpu_opt(LOONGARCH_CPU_REDIRECTINT)
>
> #endif /* __ASM_CPU_FEATURES_H */
> diff --git a/arch/loongarch/include/asm/cpu.h b/arch/loongarch/include/asm/cpu.h
> index 98cf4d7b4b0a..33cd96e569d8 100644
> --- a/arch/loongarch/include/asm/cpu.h
> +++ b/arch/loongarch/include/asm/cpu.h
> @@ -102,6 +102,7 @@ enum cpu_type_enum {
> #define CPU_FEATURE_PTW 27 /* CPU has hardware page table walker */
> #define CPU_FEATURE_LSPW 28 /* CPU has LSPW (lddir/ldpte instructions) */
> #define CPU_FEATURE_AVECINT 29 /* CPU has AVEC interrupt */
> +#define CPU_FEATURE_REDIRECTINT 30 /* CPU has interrupt remmap */
>
> #define LOONGARCH_CPU_CPUCFG BIT_ULL(CPU_FEATURE_CPUCFG)
> #define LOONGARCH_CPU_LAM BIT_ULL(CPU_FEATURE_LAM)
> @@ -133,5 +134,6 @@ enum cpu_type_enum {
> #define LOONGARCH_CPU_PTW BIT_ULL(CPU_FEATURE_PTW)
> #define LOONGARCH_CPU_LSPW BIT_ULL(CPU_FEATURE_LSPW)
> #define LOONGARCH_CPU_AVECINT BIT_ULL(CPU_FEATURE_AVECINT)
> +#define LOONGARCH_CPU_REDIRECTINT BIT_ULL(CPU_FEATURE_REDIRECTINT)
>
> #endif /* _ASM_CPU_H */
> diff --git a/arch/loongarch/include/asm/loongarch.h b/arch/loongarch/include/asm/loongarch.h
> index 52651aa0e583..3a27a5f6e460 100644
> --- a/arch/loongarch/include/asm/loongarch.h
> +++ b/arch/loongarch/include/asm/loongarch.h
> @@ -1130,6 +1130,7 @@
> #define IOCSRF_FLATMODE BIT_ULL(10)
> #define IOCSRF_VM BIT_ULL(11)
> #define IOCSRF_AVEC BIT_ULL(15)
> +#define IOCSRF_REDIRECTINT BIT_ULL(16)
>
> #define LOONGARCH_IOCSR_VENDOR 0x10
>
> @@ -1142,6 +1143,7 @@
> #define IOCSR_MISC_FUNC_TIMER_RESET BIT_ULL(21)
> #define IOCSR_MISC_FUNC_EXT_IOI_EN BIT_ULL(48)
> #define IOCSR_MISC_FUNC_AVEC_EN BIT_ULL(51)
> +#define IOCSR_MISC_FUNC_REDIRECT_EN IOCSR_MISC_FUNC_AVEC_EN
Don't define IOCSR_MISC_FUNC_REDIRECT_EN, since redirect cannot exist
without avec, you just need to enable avec in its own driver.
>
> #define LOONGARCH_IOCSR_CPUTEMP 0x428
>
> @@ -1189,6 +1191,11 @@
>
> #define LOONGARCH_IOCSR_EXTIOI_NODEMAP_BASE 0x14a0
> #define LOONGARCH_IOCSR_EXTIOI_IPMAP_BASE 0x14c0
> +#define LOONGARCH_IOCSR_REDIRECT_CFG 0x15e0
> +#define LOONGARCH_IOCSR_REDIRECT_TBR 0x15e8 /* IRT BASE REG*/
> +#define LOONGARCH_IOCSR_REDIRECT_CQB 0x15f0 /* IRT CACHE QUEUE BASE */
> +#define LOONGARCH_IOCSR_REDIRECT_CQH 0x15f8 /* IRT_CACHE_QUEUE_HEAD 32bit */
> +#define LOONGARCH_IOCSR_REDIRECT_CQT 0x15fc /* IRT_CACHE_QUEUE_TAIL 32bit */
Keep the same style for all definitions, remove "_" in the comments,
and you can add "," before 32bit (or remove 32bit).
> #define LOONGARCH_IOCSR_EXTIOI_EN_BASE 0x1600
> #define LOONGARCH_IOCSR_EXTIOI_BOUNCE_BASE 0x1680
> #define LOONGARCH_IOCSR_EXTIOI_ISR_BASE 0x1800
> diff --git a/arch/loongarch/kernel/cpu-probe.c b/arch/loongarch/kernel/cpu-probe.c
> index fedaa67cde41..543474fd1399 100644
> --- a/arch/loongarch/kernel/cpu-probe.c
> +++ b/arch/loongarch/kernel/cpu-probe.c
> @@ -289,6 +289,8 @@ static inline void cpu_probe_loongson(struct cpuinfo_loongarch *c, unsigned int
> c->options |= LOONGARCH_CPU_EIODECODE;
> if (config & IOCSRF_AVEC)
> c->options |= LOONGARCH_CPU_AVECINT;
> + if (config & IOCSRF_REDIRECTINT)
> + c->options |= LOONGARCH_CPU_REDIRECTINT;
> if (config & IOCSRF_VM)
> c->options |= LOONGARCH_CPU_HYPERVISOR;
> }
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index 25e9ad29b8c4..5dd7d6b151d9 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -113,7 +113,7 @@ obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o
> obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o
> obj-$(CONFIG_TI_SCI_INTA_IRQCHIP) += irq-ti-sci-inta.o
> obj-$(CONFIG_TI_PRUSS_INTC) += irq-pruss-intc.o
> -obj-$(CONFIG_IRQ_LOONGARCH_CPU) += irq-loongarch-cpu.o irq-loongarch-avec.o
> +obj-$(CONFIG_IRQ_LOONGARCH_CPU) += irq-loongarch-cpu.o irq-loongarch-avec.o irq-loongarch-ir.o
> obj-$(CONFIG_LOONGSON_LIOINTC) += irq-loongson-liointc.o
> obj-$(CONFIG_LOONGSON_EIOINTC) += irq-loongson-eiointc.o
> obj-$(CONFIG_LOONGSON_HTPIC) += irq-loongson-htpic.o
> diff --git a/drivers/irqchip/irq-loongarch-avec.c b/drivers/irqchip/irq-loongarch-avec.c
> index 80e55955a29f..101bae6f981c 100644
> --- a/drivers/irqchip/irq-loongarch-avec.c
> +++ b/drivers/irqchip/irq-loongarch-avec.c
> @@ -24,7 +24,6 @@
> #define VECTORS_PER_REG 64
> #define IRR_VECTOR_MASK 0xffUL
> #define IRR_INVALID_MASK 0x80000000UL
> -#define AVEC_MSG_OFFSET 0x100000
>
> #ifdef CONFIG_SMP
> struct pending_list {
> @@ -47,15 +46,6 @@ struct avecintc_chip {
>
> static struct avecintc_chip loongarch_avec;
>
> -struct avecintc_data {
> - struct list_head entry;
> - unsigned int cpu;
> - unsigned int vec;
> - unsigned int prev_cpu;
> - unsigned int prev_vec;
> - unsigned int moving;
> -};
> -
> static inline void avecintc_enable(void)
> {
> u64 value;
> @@ -85,7 +75,7 @@ static inline void pending_list_init(int cpu)
> INIT_LIST_HEAD(&plist->head);
> }
>
> -static void avecintc_sync(struct avecintc_data *adata)
> +void avecintc_sync(struct avecintc_data *adata)
> {
> struct pending_list *plist;
>
> @@ -109,7 +99,7 @@ static int avecintc_set_affinity(struct irq_data *data, const struct cpumask *de
> return -EBUSY;
>
> if (cpu_online(adata->cpu) && cpumask_test_cpu(adata->cpu, dest))
> - return 0;
> + return IRQ_SET_MASK_OK_DONE;
>
> cpumask_and(&intersect_mask, dest, cpu_online_mask);
>
> @@ -121,7 +111,8 @@ static int avecintc_set_affinity(struct irq_data *data, const struct cpumask *de
> adata->cpu = cpu;
> adata->vec = vector;
> per_cpu_ptr(irq_map, adata->cpu)[adata->vec] = irq_data_to_desc(data);
> - avecintc_sync(adata);
> + if (!cpu_has_redirectint)
> + avecintc_sync(adata);
> }
>
> irq_data_update_effective_affinity(data, cpumask_of(cpu));
> @@ -136,7 +127,8 @@ static int avecintc_cpu_online(unsigned int cpu)
>
> guard(raw_spinlock)(&loongarch_avec.lock);
>
> - avecintc_enable();
> + if (!cpu_has_redirectint)
> + avecintc_enable();
You can do it unconditionally, as said before (then no need to enable
in the ir driver).
>
> irq_matrix_online(loongarch_avec.vector_matrix);
>
> @@ -242,6 +234,7 @@ static void avecintc_irq_dispatch(struct irq_desc *desc)
> d = this_cpu_read(irq_map[vector]);
> if (d) {
> generic_handle_irq_desc(d);
> +
> } else {
> spurious_interrupt();
> pr_warn("Unexpected IRQ occurs on CPU#%d [vector %ld]\n", smp_processor_id(), vector);
> @@ -412,6 +405,9 @@ static int __init pch_msi_parse_madt(union acpi_subtable_headers *header,
>
> static inline int __init acpi_cascade_irqdomain_init(void)
> {
> + if (cpu_has_redirectint)
> + return loongarch_irq_redirect_init(loongarch_avec.domain);
> +
> return acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, pch_msi_parse_madt, 1);
> }
>
> diff --git a/drivers/irqchip/irq-loongarch-ir.c b/drivers/irqchip/irq-loongarch-ir.c
> new file mode 100644
> index 000000000000..d3c664d6f98e
> --- /dev/null
> +++ b/drivers/irqchip/irq-loongarch-ir.c
> @@ -0,0 +1,576 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2020 Loongson Technologies, Inc.
> + */
> +
> +#include <linux/cpuhotplug.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/irq.h>
> +#include <linux/irqchip.h>
> +#include <linux/irqdomain.h>
> +#include <linux/spinlock.h>
> +#include <linux/msi.h>
> +
> +#include <asm/irq.h>
> +#include <asm/loongarch.h>
> +#include <asm/setup.h>
> +#include <larchintrin.h>
> +
> +#include "irq-loongson.h"
> +#include "irq-msi-lib.h"
> +
> +#define IRD_ENTRIES 65536
> +
> +/* redirect entry size 128bits */
> +#if (defined(CONFIG_PAGE_SIZE_4KB))
> +#define IRD_PAGE_ORDER 8
> +#elif (defined(CONFIG_PAGE_SIZE_16KB))
> +#define IRD_PAGE_ORDER 6
> +#endif
#define IRD_PAGE_ORDER (20 - PAGE_SHIFT)
> +
> +/* irt cache invalid queue */
> +#define INVALID_QUEUE_SIZE 4096
> +
> +#if (defined(CONFIG_PAGE_SIZE_4KB))
> +#define INVALID_QUEUE_PAGE_ORDER 4
> +#elif (defined(CONFIG_PAGE_SIZE_16KB))
> +#define INVALID_QUEUE_PAGE_ORDER 2
> +#endif
#define INVALID_QUEUE_PAGE_ORDER (16 - PAGE_SHIFT)
> +
> +#define GPID_ADDR_MASK 0x3ffffffffffULL
> +#define GPID_ADDR_SHIFT 6
> +
> +#define CQB_SIZE_SHIFT 0
> +#define CQB_SIZE_MASK 0xf
> +#define CQB_ADDR_SHIFT 12
> +#define CQB_ADDR_MASK (0xfffffffffULL)
> +
> +#define CFG_DISABLE_IDLE 2
> +#define INVALID_INDEX 0
> +
> +#define MAX_IR_ENGINES 16
> +
> +struct irq_domain *redirect_domain;
> +struct redirect_entry {
> + struct {
> + __u64 valid : 1,
> + res1 : 5,
> + gpid : 42,
> + res2 : 8,
> + vector : 8;
> + } lo;
> + __u64 hi;
> +};
> +
> +struct redirect_gpid {
What does gpid stand for?
> + u64 pir[4]; /* Pending interrupt requested */
> + u8 en : 1, /* doorbell */
> + res0 : 7;
> + u8 irqnum;
> + u16 res1;
> + u32 dst;
> + u32 rsvd[6];
> +} __aligned(64);
> +
> +struct irde_inv_cmd {
What does irde stand for?
> + union {
> + __u64 cmd_info;
> + struct {
> + __u64 res1 : 4,
> + type : 1,
> + need_notice : 1,
> + pad : 2,
> + index : 16,
> + pad2 : 40;
> + } index;
> + };
> + __u64 notice_addr;
> +};
> +
> +struct redirect_item {
> + int index;
> + struct redirect_entry *entry;
> + struct redirect_gpid *gpid;
> + struct redirect_table *table;
> +};
> +
> +struct redirect_table {
> + int node;
> + struct redirect_entry *table;
> + unsigned long *bitmap;
> + unsigned int nr_ird;
> + struct page *page;
> + raw_spinlock_t lock;
> +};
> +
> +struct redirect_queue {
> + int node;
> + u64 base;
> + u32 max_size;
> + int head;
> + int tail;
> + struct page *page;
> + raw_spinlock_t lock;
> +};
> +
> +struct la_irde_desc {
> + struct redirect_table ird_table;
> + struct redirect_queue inv_queue;
> +};
Remove the la_ prefix, and then change the order of these structures as:
redirect_entry, redirect_gpid, redirect_table, redirect_item,
redirect_queue, irde_desc, irde_inv_cmd.
> +
> +struct smp_invalid_arg {
> + struct redirect_queue *queue;
> + struct irde_inv_cmd *cmd;
> +};
> +
> +static struct la_irde_desc irde_descs[MAX_IR_ENGINES];
> +static phys_addr_t msi_base_addr;
> +
> +static inline void redirect_enable(int node)
> +{
> + u64 value;
> +
> + if (redirect_domain) {
> + value = iocsr_read64(LOONGARCH_IOCSR_MISC_FUNC);
> + value |= IOCSR_MISC_FUNC_REDIRECT_EN;
> + iocsr_write64(value, LOONGARCH_IOCSR_MISC_FUNC);
> + }
> +}
This function can be removed, as said before.
> +
> +static inline bool invalid_queue_is_full(int node_id, u32 *tail)
> +{
> + u32 head;
> +
> + head = iocsr_read32(LOONGARCH_IOCSR_REDIRECT_CQH);
> + *tail = iocsr_read32(LOONGARCH_IOCSR_REDIRECT_CQT);
> +
> + return !!(head == ((*tail + 1) % INVALID_QUEUE_SIZE));
> +}
> +
> +static void invalid_enqueue(struct redirect_queue *rqueue, struct irde_inv_cmd *cmd)
> +{
> + struct irde_inv_cmd *inv_addr;
> + u32 tail;
> +
> + guard(raw_spinlock_irqsave)(&rqueue->lock);
> +
> + while (invalid_queue_is_full(rqueue->node, &tail))
> + cpu_relax();
> +
> + inv_addr = (struct irde_inv_cmd *)(rqueue->base + tail * sizeof(struct irde_inv_cmd));
> + memcpy(inv_addr, cmd, sizeof(struct irde_inv_cmd));
> + tail = (tail + 1) % INVALID_QUEUE_SIZE;
> +
> + wmb();
> +
> + iocsr_write32(tail, LOONGARCH_IOCSR_REDIRECT_CQT);
> +}
> +
> +static void smp_call_invalid_enqueue(void *arg)
> +{
> + struct smp_invalid_arg *s_arg = (struct smp_invalid_arg *)arg;
> +
> + invalid_enqueue(s_arg->queue, s_arg->cmd);
> +}
> +
> +static void irde_invlid_entry_node(struct redirect_item *item)
> +{
> + struct redirect_queue *rqueue;
> + struct smp_invalid_arg arg;
> + struct irde_inv_cmd cmd;
> + volatile u64 raddr = 0;
> + int node = item->table->node, cpu;
> +
> + rqueue = &(irde_descs[node].inv_queue);
> + cmd.cmd_info = 0;
> + cmd.index.type = INVALID_INDEX;
> + cmd.index.need_notice = 1;
> + cmd.index.index = item->index;
> + cmd.notice_addr = (u64)(__pa(&raddr));
> +
> + if (cpu_to_node(smp_processor_id()) == node)
> + invalid_enqueue(rqueue, &cmd);
> + else {
> + for_each_cpu(cpu, cpumask_of_node(node)) {
> + if (cpu_online(cpu))
> + break;
> + }
> + arg.queue = rqueue;
> + arg.cmd = &cmd;
> + smp_call_function_single(cpu, smp_call_invalid_enqueue, &arg, 0);
> + }
> +
> + while (!raddr)
> + cpu_relax();
> +
> +}
> +
> +static inline struct avecintc_data *irq_data_get_avec_data(struct irq_data *data)
> +{
> + return data->parent_data->chip_data;
> +}
> +
> +static int redirect_table_alloc(struct redirect_item *item, struct redirect_table *ird_table)
> +{
> + int index;
> +
> + guard(raw_spinlock_irqsave)(&ird_table->lock);
> +
> + index = find_first_zero_bit(ird_table->bitmap, IRD_ENTRIES);
> + if (index > IRD_ENTRIES) {
> + pr_err("No redirect entry to use\n");
> + return -ENOMEM;
> + }
> +
> + __set_bit(index, ird_table->bitmap);
> +
> + item->index = index;
> + item->entry = &ird_table->table[index];
> + item->table = ird_table;
> +
> + return 0;
> +}
> +
> +static int redirect_table_free(struct redirect_item *item)
> +{
> + struct redirect_table *ird_table;
> + struct redirect_entry *entry;
> + unsigned long flags;
> +
> + ird_table = item->table;
> +
> + entry = item->entry;
> + memset(entry, 0, sizeof(struct redirect_entry));
> +
> + raw_spin_lock_irqsave(&ird_table->lock, flags);
> + bitmap_release_region(ird_table->bitmap, item->index, 0);
> + raw_spin_unlock_irqrestore(&ird_table->lock, flags);
> +
> + kfree(item->gpid);
> +
> + irde_invlid_entry_node(item);
> +
> + return 0;
> +}
> +
> +static inline void redirect_domain_prepare_entry(struct redirect_item *item, struct avecintc_data *adata)
> +{
> + struct redirect_entry *entry = item->entry;
> +
> + item->gpid->en = 1;
> + item->gpid->irqnum = adata->vec;
> + item->gpid->dst = adata->cpu;
> +
> + entry->lo.valid = 1;
> + entry->lo.gpid = ((long)item->gpid >> GPID_ADDR_SHIFT) & (GPID_ADDR_MASK);
> + entry->lo.vector = 0xff;
> + wmb();
> +}
> +
> +static int loongarch_redirect_set_affinity(struct irq_data *data, const struct cpumask *dest, bool force)
Remove the loongarch_ prefix since it is an internal function.
> +{
> + struct redirect_item *item = data->chip_data;
> + struct avecintc_data *adata;
> + int ret;
> +
> + ret = irq_chip_set_affinity_parent(data, dest, force);
> + if (ret == IRQ_SET_MASK_OK_DONE)
> + return IRQ_SET_MASK_OK;
> + else if (ret) {
> + pr_err("IRDE:set_affinity error %d\n", ret);
> + return ret;
> + }
> +
> + adata = irq_data_get_avec_data(data);
> +
> + redirect_domain_prepare_entry(item, adata);
> +
> + irde_invlid_entry_node(item);
> +
> + avecintc_sync(adata);
> + return IRQ_SET_MASK_OK;
> +}
> +
> +static void loongarch_redirect_compose_msg(struct irq_data *d, struct msi_msg *msg)
Use redirect_compose_msi_msg() to keep consistency with other
loongarch-specific drivers.
> +{
> + struct redirect_item *item;
> +
> + item = irq_data_get_irq_chip_data(d);
> + msg->address_lo = (msi_base_addr | 1 << 2 | ((item->index & 0xffff) << 4));
> + msg->address_hi = 0x0;
> + msg->data = 0x0;
> +}
> +
> +static inline void loongarch_redirect_ack_irq(struct irq_data *d)
> +{
> +}
> +
> +static inline void loongarch_redirect_unmask_irq(struct irq_data *d)
> +{
> +}
> +
> +static inline void loongarch_redirect_mask_irq(struct irq_data *d)
> +{
> +}
Remove the loongarch_ prefix since they are internal functions.
> +
> +static struct irq_chip loongarch_redirect_chip = {
Use redirect_irq_controller to keep consistency with other
loongarch-specific drivers.
> + .name = "redirect",
Use "REDIRECT" for consistency, too.
> + .irq_ack = loongarch_redirect_ack_irq,
> + .irq_mask = loongarch_redirect_mask_irq,
> + .irq_unmask = loongarch_redirect_unmask_irq,
> + .irq_set_affinity = loongarch_redirect_set_affinity,
> + .irq_compose_msi_msg = loongarch_redirect_compose_msg,
> +};
> +
> +static void loongarch_irq_redirect_free_resources(struct irq_domain *domain,
> + unsigned int virq, unsigned int nr_irqs)
> +{
> + struct irq_data *irq_data;
> + struct redirect_item *item;
> +
> + for (int i = 0; i < nr_irqs; i++) {
> + irq_data = irq_domain_get_irq_data(domain, virq + i);
> + if (irq_data && irq_data->chip_data) {
> + item = irq_data->chip_data;
> + redirect_table_free(item);
> + kfree(item);
> + }
> + }
> +}
> +
> +static int loongarch_irq_redirect_alloc(struct irq_domain *domain,
> + unsigned int virq, unsigned int nr_irqs,
> + void *arg)
> +{
> + struct redirect_table *ird_table;
> + struct avecintc_data *avec_data;
> + struct irq_data *irq_data;
> + int ret, i, node;
> +
> + node = ((msi_alloc_info_t *)arg)->desc->dev->numa_node;
> + ird_table = &irde_descs[node].ird_table;
> + ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
> + if (ret < 0)
> + return ret;
> +
> + for (i = 0; i < nr_irqs; i++) {
> + struct redirect_item *item;
> +
> + item = kzalloc(sizeof(struct redirect_item), GFP_KERNEL);
> + if (!item) {
> + pr_err("Alloc redirect descriptor failed\n");
> + goto out_free_resources;
> + }
> +
> + irq_data = irq_domain_get_irq_data(domain, virq + i);
> +
> + avec_data = irq_data_get_avec_data(irq_data);
> + ret = redirect_table_alloc(item, ird_table);
> + if (ret) {
> + pr_err("Alloc redirect table entry failed\n");
> + goto out_free_resources;
> + }
> +
> + item->gpid = kzalloc_node(sizeof(struct redirect_gpid), GFP_KERNEL, node);
> + if (!item->gpid) {
> + pr_err("Alloc redirect GPID failed\n");
> + goto out_free_resources;
> + }
> +
> + irq_data->chip_data = item;
> + irq_data->chip = &loongarch_redirect_chip;
> + redirect_domain_prepare_entry(item, avec_data);
> + }
> + return 0;
> +
> +out_free_resources:
> + loongarch_irq_redirect_free_resources(domain, virq, nr_irqs);
> + irq_domain_free_irqs_common(domain, virq, nr_irqs);
> +
> + return -EINVAL;
> +}
> +
> +static void loongarch_irq_redirect_free(struct irq_domain *domain,
> + unsigned int virq, unsigned int nr_irqs)
> +{
> + loongarch_irq_redirect_free_resources(domain, virq, nr_irqs);
> + return irq_domain_free_irqs_common(domain, virq, nr_irqs);
> +}
> +
> +static const struct irq_domain_ops loongarch_redirect_domain_ops = {
> + .alloc = loongarch_irq_redirect_alloc,
> + .free = loongarch_irq_redirect_free,
> + .select = msi_lib_irq_domain_select,
> +};
For consistency, please use:
static const struct irq_domain_ops redirect_domain_ops = {
.alloc = redirect_domain_alloc,
.free = redirect_domain_free,
.select = msi_lib_irq_domain_select,
};
> +
> +static int redirect_queue_init(int node)
> +{
> + struct redirect_queue *rqueue = &(irde_descs[node].inv_queue);
> + struct page *pages;
> +
> + pages = alloc_pages_node(0, GFP_KERNEL | __GFP_ZERO, INVALID_QUEUE_PAGE_ORDER);
> + if (!pages) {
> + pr_err("Node [%d] Invalid Queue alloc pages failed!\n", node);
> + return -ENOMEM;
> + }
> +
> + rqueue->page = pages;
> + rqueue->base = (u64)page_address(pages);
> + rqueue->max_size = INVALID_QUEUE_SIZE;
> + rqueue->head = 0;
> + rqueue->tail = 0;
> + rqueue->node = node;
> + raw_spin_lock_init(&rqueue->lock);
> +
> + iocsr_write32(0, LOONGARCH_IOCSR_REDIRECT_CQH);
> + iocsr_write32(0, LOONGARCH_IOCSR_REDIRECT_CQT);
> + iocsr_write64(((rqueue->base & (CQB_ADDR_MASK << CQB_ADDR_SHIFT)) |
> + (CQB_SIZE_MASK << CQB_SIZE_SHIFT)), LOONGARCH_IOCSR_REDIRECT_CQB);
> + return 0;
> +}
> +
> +static int redirect_table_init(int node)
> +{
> + struct redirect_table *ird_table = &(irde_descs[node].ird_table);
> + struct page *pages;
> + unsigned long *bitmap;
> +
> + pages = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, IRD_PAGE_ORDER);
> + if (!pages) {
> + pr_err("Node [%d] redirect table alloc pages failed!\n", node);
> + return -ENOMEM;
> + }
> + ird_table->page = pages;
> + ird_table->table = page_address(pages);
> +
> + bitmap = bitmap_zalloc(IRD_ENTRIES, GFP_KERNEL);
> + if (!bitmap) {
> + pr_err("Node [%d] redirect table bitmap alloc pages failed!\n", node);
> + return -ENOMEM;
> + }
> +
> + ird_table->bitmap = bitmap;
> + ird_table->nr_ird = IRD_ENTRIES;
> + ird_table->node = node;
> +
> + raw_spin_lock_init(&ird_table->lock);
> +
> + if (redirect_queue_init(node))
> + return -EINVAL;
> +
> + iocsr_write64(CFG_DISABLE_IDLE, LOONGARCH_IOCSR_REDIRECT_CFG);
> + iocsr_write64(__pa(ird_table->table), LOONGARCH_IOCSR_REDIRECT_TBR);
> +
> + return 0;
> +}
> +
> +static void redirect_table_fini(int node)
> +{
> + struct redirect_table *ird_table = &(irde_descs[node].ird_table);
> + struct redirect_queue *rqueue = &(irde_descs[node].inv_queue);
> +
> + if (ird_table->page) {
> + __free_pages(ird_table->page, IRD_PAGE_ORDER);
> + ird_table->table = NULL;
> + ird_table->page = NULL;
> + }
> +
> + if (ird_table->page) {
> + bitmap_free(ird_table->bitmap);
> + ird_table->bitmap = NULL;
> + }
> +
> + if (rqueue->page) {
> + __free_pages(rqueue->page, INVALID_QUEUE_PAGE_ORDER);
> + rqueue->page = NULL;
> + rqueue->base = 0;
> + }
> +
> + iocsr_write64(0, LOONGARCH_IOCSR_REDIRECT_CQB);
> + iocsr_write64(0, LOONGARCH_IOCSR_REDIRECT_TBR);
> +}
> +
> +static int redirect_cpu_online(unsigned int cpu)
> +{
> + int ret, node = cpu_to_node(cpu);
> +
> + if (cpu != cpumask_first(cpumask_of_node(node)))
> + return 0;
> +
> + ret = redirect_table_init(node);
> + if (ret) {
> + redirect_table_fini(node);
> + return -EINVAL;
> + }
> +
> + redirect_enable(node);
> + return 0;
> +}
> +
> +#if defined(CONFIG_ACPI)
> +static int __init pch_msi_parse_madt(union acpi_subtable_headers *header,
> + const unsigned long end)
> +{
> + struct acpi_madt_msi_pic *pchmsi_entry = (struct acpi_madt_msi_pic *)header;
> +
> + msi_base_addr = pchmsi_entry->msg_address - AVEC_MSG_OFFSET;
> +
> + return pch_msi_acpi_init_avec(redirect_domain);
> +}
> +
> +static int __init acpi_cascade_irqdomain_init(void)
> +{
> + return acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, pch_msi_parse_madt, 1);
> +}
> +
> +int __init loongarch_irq_redirect_init(struct irq_domain *parent)
Use redirect_acpi_init() for consistency.
Huacai
> +{
> + struct fwnode_handle *fwnode;
> + struct irq_domain *domain;
> + int ret;
> +
> + fwnode = irq_domain_alloc_named_fwnode("redirect");
> + if (!fwnode) {
> + pr_err("Unable to alloc redirect domain handle\n");
> + goto fail;
> + }
> +
> + domain = irq_domain_create_hierarchy(parent, 0, IRD_ENTRIES, fwnode,
> + &loongarch_redirect_domain_ops, irde_descs);
> + if (!domain) {
> + pr_err("Unable to alloc redirect domain\n");
> + goto out_free_fwnode;
> + }
> +
> + redirect_domain = domain;
> +
> + ret = redirect_table_init(0);
> + if (ret)
> + goto out_free_table;
> +
> + ret = acpi_cascade_irqdomain_init();
> + if (ret < 0) {
> + pr_err("Failed to cascade IRQ domain, ret=%d\n", ret);
> + goto out_free_table;
> + }
> +
> + cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_REDIRECT_STARTING,
> + "irqchip/loongarch/redirect:starting",
> + redirect_cpu_online, NULL);
> +
> + pr_info("loongarch irq redirect modules init succeeded\n");
> + redirect_enable(0);
> + return 0;
> +
> +out_free_table:
> + redirect_table_fini(0);
> + irq_domain_remove(redirect_domain);
> + redirect_domain = NULL;
> +out_free_fwnode:
> + irq_domain_free_fwnode(fwnode);
> +fail:
> + return -EINVAL;
> +}
> +#endif
> diff --git a/drivers/irqchip/irq-loongson.h b/drivers/irqchip/irq-loongson.h
> index 11fa138d1f44..f29ab8ec368e 100644
> --- a/drivers/irqchip/irq-loongson.h
> +++ b/drivers/irqchip/irq-loongson.h
> @@ -5,6 +5,15 @@
>
> #ifndef _DRIVERS_IRQCHIP_IRQ_LOONGSON_H
> #define _DRIVERS_IRQCHIP_IRQ_LOONGSON_H
> +#define AVEC_MSG_OFFSET 0x100000
> +struct avecintc_data {
> + struct list_head entry;
> + unsigned int cpu;
> + unsigned int vec;
> + unsigned int prev_cpu;
> + unsigned int prev_vec;
> + unsigned int moving;
> +};
>
> int find_pch_pic(u32 gsi);
>
> @@ -24,4 +33,7 @@ int pch_msi_acpi_init(struct irq_domain *parent,
> struct acpi_madt_msi_pic *acpi_pchmsi);
> int pch_msi_acpi_init_avec(struct irq_domain *parent);
>
> +int loongarch_irq_redirect_init(struct irq_domain *parent);
> +
> +void avecintc_sync(struct avecintc_data *adata);
> #endif /* _DRIVERS_IRQCHIP_IRQ_LOONGSON_H */
> diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
> index 6cc5e484547c..2fd5531fa378 100644
> --- a/include/linux/cpuhotplug.h
> +++ b/include/linux/cpuhotplug.h
> @@ -146,6 +146,7 @@ enum cpuhp_state {
> CPUHP_AP_IRQ_MIPS_GIC_STARTING,
> CPUHP_AP_IRQ_EIOINTC_STARTING,
> CPUHP_AP_IRQ_AVECINTC_STARTING,
> + CPUHP_AP_IRQ_REDIRECT_STARTING,
> CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING,
> CPUHP_AP_IRQ_THEAD_ACLINT_SSWI_STARTING,
> CPUHP_AP_IRQ_RISCV_IMSIC_STARTING,
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/2] irq/irq-loongarch-ir:Add Redirect irqchip support
2025-03-03 14:34 ` Huacai Chen
@ 2025-03-03 21:28 ` Thomas Gleixner
2025-03-06 2:07 ` Tianyang Zhang
2025-03-06 2:06 ` Tianyang Zhang
1 sibling, 1 reply; 13+ messages in thread
From: Thomas Gleixner @ 2025-03-03 21:28 UTC (permalink / raw)
To: Huacai Chen, Tianyang Zhang
Cc: kernel, corbet, alexs, si.yanteng, jiaxun.yang, peterz, wangliupu,
lvjianmin, maobibo, siyanteng, gaosong, yangtiezhu, loongarch,
linux-doc, linux-kernel
On Mon, Mar 03 2025 at 22:34, Huacai Chen wrote:
> On Mon, Mar 3, 2025 at 6:15 PM Tianyang Zhang <zhangtianyang@loongson.cn> wrote:
>> +static const struct irq_domain_ops loongarch_redirect_domain_ops = {
>> + .alloc = loongarch_irq_redirect_alloc,
>> + .free = loongarch_irq_redirect_free,
>> + .select = msi_lib_irq_domain_select,
>> +};
> For consistency, please use:
> static const struct irq_domain_ops redirect_domain_ops = {
> .alloc = redirect_domain_alloc,
> .free = redirect_domain_free,
> .select = msi_lib_irq_domain_select,
> };
This formatting requirement applies to all structures in this patch. See
https://www.kernel.org/doc/html/latest/process/maintainer-tip.html#coding-style-notes
for further information.
Thanks,
tglx
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/2] irq/irq-loongarch-ir:Add Redirect irqchip support
2025-03-03 10:15 ` [PATCH 2/2] irq/irq-loongarch-ir:Add Redirect irqchip support Tianyang Zhang
2025-03-03 14:34 ` Huacai Chen
@ 2025-03-04 11:24 ` kernel test robot
2025-03-05 14:47 ` kernel test robot
2 siblings, 0 replies; 13+ messages in thread
From: kernel test robot @ 2025-03-04 11:24 UTC (permalink / raw)
To: Tianyang Zhang, chenhuacai, kernel, corbet, alexs, si.yanteng,
tglx, jiaxun.yang, peterz, wangliupu, lvjianmin, maobibo,
siyanteng, gaosong, yangtiezhu
Cc: oe-kbuild-all, loongarch, linux-doc, linux-kernel, Tianyang Zhang
Hi Tianyang,
kernel test robot noticed the following build errors:
[auto build test ERROR on tip/irq/core]
[also build test ERROR on lwn/docs-next linus/master v6.14-rc5 next-20250303]
[cannot apply to tip/smp/core]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Tianyang-Zhang/Docs-LoongArch-Add-Advanced-Extended-Redirect-IRQ-model-description/20250303-181832
base: tip/irq/core
patch link: https://lore.kernel.org/r/20250303101533.31462-3-zhangtianyang%40loongson.cn
patch subject: [PATCH 2/2] irq/irq-loongarch-ir:Add Redirect irqchip support
config: loongarch-randconfig-002-20250304 (https://download.01.org/0day-ci/archive/20250304/202503041913.hIveXcmc-lkp@intel.com/config)
compiler: loongarch64-linux-gcc (GCC) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250304/202503041913.hIveXcmc-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202503041913.hIveXcmc-lkp@intel.com/
All errors (new ones prefixed by >>):
drivers/irqchip/irq-loongarch-ir.c: In function 'loongarch_irq_redirect_alloc':
>> drivers/irqchip/irq-loongarch-ir.c:352:52: error: 'struct device' has no member named 'numa_node'
352 | node = ((msi_alloc_info_t *)arg)->desc->dev->numa_node;
| ^~
vim +352 drivers/irqchip/irq-loongarch-ir.c
342
343 static int loongarch_irq_redirect_alloc(struct irq_domain *domain,
344 unsigned int virq, unsigned int nr_irqs,
345 void *arg)
346 {
347 struct redirect_table *ird_table;
348 struct avecintc_data *avec_data;
349 struct irq_data *irq_data;
350 int ret, i, node;
351
> 352 node = ((msi_alloc_info_t *)arg)->desc->dev->numa_node;
353 ird_table = &irde_descs[node].ird_table;
354 ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
355 if (ret < 0)
356 return ret;
357
358 for (i = 0; i < nr_irqs; i++) {
359 struct redirect_item *item;
360
361 item = kzalloc(sizeof(struct redirect_item), GFP_KERNEL);
362 if (!item) {
363 pr_err("Alloc redirect descriptor failed\n");
364 goto out_free_resources;
365 }
366
367 irq_data = irq_domain_get_irq_data(domain, virq + i);
368
369 avec_data = irq_data_get_avec_data(irq_data);
370 ret = redirect_table_alloc(item, ird_table);
371 if (ret) {
372 pr_err("Alloc redirect table entry failed\n");
373 goto out_free_resources;
374 }
375
376 item->gpid = kzalloc_node(sizeof(struct redirect_gpid), GFP_KERNEL, node);
377 if (!item->gpid) {
378 pr_err("Alloc redirect GPID failed\n");
379 goto out_free_resources;
380 }
381
382 irq_data->chip_data = item;
383 irq_data->chip = &loongarch_redirect_chip;
384 redirect_domain_prepare_entry(item, avec_data);
385 }
386 return 0;
387
388 out_free_resources:
389 loongarch_irq_redirect_free_resources(domain, virq, nr_irqs);
390 irq_domain_free_irqs_common(domain, virq, nr_irqs);
391
392 return -EINVAL;
393 }
394
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/2] irq/irq-loongarch-ir:Add Redirect irqchip support
2025-03-03 10:15 ` [PATCH 2/2] irq/irq-loongarch-ir:Add Redirect irqchip support Tianyang Zhang
2025-03-03 14:34 ` Huacai Chen
2025-03-04 11:24 ` kernel test robot
@ 2025-03-05 14:47 ` kernel test robot
2 siblings, 0 replies; 13+ messages in thread
From: kernel test robot @ 2025-03-05 14:47 UTC (permalink / raw)
To: Tianyang Zhang, chenhuacai, kernel, corbet, alexs, si.yanteng,
tglx, jiaxun.yang, peterz, wangliupu, lvjianmin, maobibo,
siyanteng, gaosong, yangtiezhu
Cc: oe-kbuild-all, loongarch, linux-doc, linux-kernel, Tianyang Zhang
Hi Tianyang,
kernel test robot noticed the following build errors:
[auto build test ERROR on tip/irq/core]
[also build test ERROR on lwn/docs-next linus/master v6.14-rc5 next-20250305]
[cannot apply to tip/smp/core]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Tianyang-Zhang/Docs-LoongArch-Add-Advanced-Extended-Redirect-IRQ-model-description/20250303-181832
base: tip/irq/core
patch link: https://lore.kernel.org/r/20250303101533.31462-3-zhangtianyang%40loongson.cn
patch subject: [PATCH 2/2] irq/irq-loongarch-ir:Add Redirect irqchip support
config: loongarch-randconfig-002-20250305 (https://download.01.org/0day-ci/archive/20250305/202503052234.SiXLJuat-lkp@intel.com/config)
compiler: loongarch64-linux-gcc (GCC) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250305/202503052234.SiXLJuat-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202503052234.SiXLJuat-lkp@intel.com/
All errors (new ones prefixed by >>):
In file included from include/linux/percpu.h:5,
from include/linux/context_tracking_state.h:5,
from include/linux/hardirq.h:5,
from include/linux/interrupt.h:11,
from drivers/irqchip/irq-loongarch-ir.c:8:
drivers/irqchip/irq-loongarch-ir.c: In function 'redirect_queue_init':
>> drivers/irqchip/irq-loongarch-ir.c:413:62: error: 'INVALID_QUEUE_PAGE_ORDER' undeclared (first use in this function)
413 | pages = alloc_pages_node(0, GFP_KERNEL | __GFP_ZERO, INVALID_QUEUE_PAGE_ORDER);
| ^~~~~~~~~~~~~~~~~~~~~~~~
include/linux/alloc_tag.h:227:16: note: in definition of macro 'alloc_hooks_tag'
227 | typeof(_do_alloc) _res; \
| ^~~~~~~~~
include/linux/gfp.h:295:49: note: in expansion of macro 'alloc_hooks'
295 | #define alloc_pages_node(...) alloc_hooks(alloc_pages_node_noprof(__VA_ARGS__))
| ^~~~~~~~~~~
drivers/irqchip/irq-loongarch-ir.c:413:17: note: in expansion of macro 'alloc_pages_node'
413 | pages = alloc_pages_node(0, GFP_KERNEL | __GFP_ZERO, INVALID_QUEUE_PAGE_ORDER);
| ^~~~~~~~~~~~~~~~
drivers/irqchip/irq-loongarch-ir.c:413:62: note: each undeclared identifier is reported only once for each function it appears in
413 | pages = alloc_pages_node(0, GFP_KERNEL | __GFP_ZERO, INVALID_QUEUE_PAGE_ORDER);
| ^~~~~~~~~~~~~~~~~~~~~~~~
include/linux/alloc_tag.h:227:16: note: in definition of macro 'alloc_hooks_tag'
227 | typeof(_do_alloc) _res; \
| ^~~~~~~~~
include/linux/gfp.h:295:49: note: in expansion of macro 'alloc_hooks'
295 | #define alloc_pages_node(...) alloc_hooks(alloc_pages_node_noprof(__VA_ARGS__))
| ^~~~~~~~~~~
drivers/irqchip/irq-loongarch-ir.c:413:17: note: in expansion of macro 'alloc_pages_node'
413 | pages = alloc_pages_node(0, GFP_KERNEL | __GFP_ZERO, INVALID_QUEUE_PAGE_ORDER);
| ^~~~~~~~~~~~~~~~
>> drivers/irqchip/irq-loongarch-ir.c:413:15: error: assignment to 'struct page *' from 'int' makes pointer from integer without a cast [-Wint-conversion]
413 | pages = alloc_pages_node(0, GFP_KERNEL | __GFP_ZERO, INVALID_QUEUE_PAGE_ORDER);
| ^
drivers/irqchip/irq-loongarch-ir.c: In function 'redirect_table_init':
>> drivers/irqchip/irq-loongarch-ir.c:440:65: error: 'IRD_PAGE_ORDER' undeclared (first use in this function); did you mean 'NR_PAGE_ORDERS'?
440 | pages = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, IRD_PAGE_ORDER);
| ^~~~~~~~~~~~~~
include/linux/alloc_tag.h:227:16: note: in definition of macro 'alloc_hooks_tag'
227 | typeof(_do_alloc) _res; \
| ^~~~~~~~~
include/linux/gfp.h:295:49: note: in expansion of macro 'alloc_hooks'
295 | #define alloc_pages_node(...) alloc_hooks(alloc_pages_node_noprof(__VA_ARGS__))
| ^~~~~~~~~~~
drivers/irqchip/irq-loongarch-ir.c:440:17: note: in expansion of macro 'alloc_pages_node'
440 | pages = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, IRD_PAGE_ORDER);
| ^~~~~~~~~~~~~~~~
drivers/irqchip/irq-loongarch-ir.c:440:15: error: assignment to 'struct page *' from 'int' makes pointer from integer without a cast [-Wint-conversion]
440 | pages = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, IRD_PAGE_ORDER);
| ^
drivers/irqchip/irq-loongarch-ir.c: In function 'redirect_table_fini':
drivers/irqchip/irq-loongarch-ir.c:475:47: error: 'IRD_PAGE_ORDER' undeclared (first use in this function); did you mean 'NR_PAGE_ORDERS'?
475 | __free_pages(ird_table->page, IRD_PAGE_ORDER);
| ^~~~~~~~~~~~~~
| NR_PAGE_ORDERS
drivers/irqchip/irq-loongarch-ir.c:486:44: error: 'INVALID_QUEUE_PAGE_ORDER' undeclared (first use in this function)
486 | __free_pages(rqueue->page, INVALID_QUEUE_PAGE_ORDER);
| ^~~~~~~~~~~~~~~~~~~~~~~~
vim +/INVALID_QUEUE_PAGE_ORDER +413 drivers/irqchip/irq-loongarch-ir.c
407
408 static int redirect_queue_init(int node)
409 {
410 struct redirect_queue *rqueue = &(irde_descs[node].inv_queue);
411 struct page *pages;
412
> 413 pages = alloc_pages_node(0, GFP_KERNEL | __GFP_ZERO, INVALID_QUEUE_PAGE_ORDER);
414 if (!pages) {
415 pr_err("Node [%d] Invalid Queue alloc pages failed!\n", node);
416 return -ENOMEM;
417 }
418
419 rqueue->page = pages;
420 rqueue->base = (u64)page_address(pages);
421 rqueue->max_size = INVALID_QUEUE_SIZE;
422 rqueue->head = 0;
423 rqueue->tail = 0;
424 rqueue->node = node;
425 raw_spin_lock_init(&rqueue->lock);
426
427 iocsr_write32(0, LOONGARCH_IOCSR_REDIRECT_CQH);
428 iocsr_write32(0, LOONGARCH_IOCSR_REDIRECT_CQT);
429 iocsr_write64(((rqueue->base & (CQB_ADDR_MASK << CQB_ADDR_SHIFT)) |
430 (CQB_SIZE_MASK << CQB_SIZE_SHIFT)), LOONGARCH_IOCSR_REDIRECT_CQB);
431 return 0;
432 }
433
434 static int redirect_table_init(int node)
435 {
436 struct redirect_table *ird_table = &(irde_descs[node].ird_table);
437 struct page *pages;
438 unsigned long *bitmap;
439
> 440 pages = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, IRD_PAGE_ORDER);
441 if (!pages) {
442 pr_err("Node [%d] redirect table alloc pages failed!\n", node);
443 return -ENOMEM;
444 }
445 ird_table->page = pages;
446 ird_table->table = page_address(pages);
447
448 bitmap = bitmap_zalloc(IRD_ENTRIES, GFP_KERNEL);
449 if (!bitmap) {
450 pr_err("Node [%d] redirect table bitmap alloc pages failed!\n", node);
451 return -ENOMEM;
452 }
453
454 ird_table->bitmap = bitmap;
455 ird_table->nr_ird = IRD_ENTRIES;
456 ird_table->node = node;
457
458 raw_spin_lock_init(&ird_table->lock);
459
460 if (redirect_queue_init(node))
461 return -EINVAL;
462
463 iocsr_write64(CFG_DISABLE_IDLE, LOONGARCH_IOCSR_REDIRECT_CFG);
464 iocsr_write64(__pa(ird_table->table), LOONGARCH_IOCSR_REDIRECT_TBR);
465
466 return 0;
467 }
468
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/2] irq/irq-loongarch-ir:Add Redirect irqchip support
2025-03-03 14:34 ` Huacai Chen
2025-03-03 21:28 ` Thomas Gleixner
@ 2025-03-06 2:06 ` Tianyang Zhang
1 sibling, 0 replies; 13+ messages in thread
From: Tianyang Zhang @ 2025-03-06 2:06 UTC (permalink / raw)
To: Huacai Chen
Cc: kernel, corbet, alexs, si.yanteng, tglx, jiaxun.yang, peterz,
wangliupu, lvjianmin, maobibo, siyanteng, gaosong, yangtiezhu,
loongarch, linux-doc, linux-kernel
Hi, Huacai
在 2025/3/3 下午10:34, Huacai Chen 写道:
> Hi, Tianyang,
>
> The title can be "irq/loongarch-ir: Add Redirect irqchip support".
OK, I got it
>
> On Mon, Mar 3, 2025 at 6:15 PM Tianyang Zhang <zhangtianyang@loongson.cn> wrote:
>> The main function of the Redirected interrupt controller is to manage the
>> redirected-interrupt table, which consists of many redirected entries.
>> When MSI interrupts are requested, the driver creates a corresponding
>> redirected entry that describes the target CPU/vector number and the
>> operating mode of the interrupt. The redirected interrupt module has an
>> independent cache, and during the interrupt routing process, it will
>> prioritize the redirected entries that hit the cache. The driver
>> invalidates certain entry caches via a command queue.
>>
>> Co-developed-by: Liupu Wang <wangliupu@loongson.cn>
>> Signed-off-by: Liupu Wang <wangliupu@loongson.cn>
>> Signed-off-by: Tianyang Zhang <zhangtianyang@loongson.cn>
>> ---
>> arch/loongarch/include/asm/cpu-features.h | 1 +
>> arch/loongarch/include/asm/cpu.h | 2 +
>> arch/loongarch/include/asm/loongarch.h | 7 +
>> arch/loongarch/kernel/cpu-probe.c | 2 +
>> drivers/irqchip/Makefile | 2 +-
>> drivers/irqchip/irq-loongarch-avec.c | 24 +-
>> drivers/irqchip/irq-loongarch-ir.c | 576 ++++++++++++++++++++++
>> drivers/irqchip/irq-loongson.h | 12 +
>> include/linux/cpuhotplug.h | 1 +
>> 9 files changed, 612 insertions(+), 15 deletions(-)
>> create mode 100644 drivers/irqchip/irq-loongarch-ir.c
>>
>> diff --git a/arch/loongarch/include/asm/cpu-features.h b/arch/loongarch/include/asm/cpu-features.h
>> index fc83bb32f9f0..03f7e93e81e0 100644
>> --- a/arch/loongarch/include/asm/cpu-features.h
>> +++ b/arch/loongarch/include/asm/cpu-features.h
>> @@ -68,5 +68,6 @@
>> #define cpu_has_ptw cpu_opt(LOONGARCH_CPU_PTW)
>> #define cpu_has_lspw cpu_opt(LOONGARCH_CPU_LSPW)
>> #define cpu_has_avecint cpu_opt(LOONGARCH_CPU_AVECINT)
>> +#define cpu_has_redirectint cpu_opt(LOONGARCH_CPU_REDIRECTINT)
>>
>> #endif /* __ASM_CPU_FEATURES_H */
>> diff --git a/arch/loongarch/include/asm/cpu.h b/arch/loongarch/include/asm/cpu.h
>> index 98cf4d7b4b0a..33cd96e569d8 100644
>> --- a/arch/loongarch/include/asm/cpu.h
>> +++ b/arch/loongarch/include/asm/cpu.h
>> @@ -102,6 +102,7 @@ enum cpu_type_enum {
>> #define CPU_FEATURE_PTW 27 /* CPU has hardware page table walker */
>> #define CPU_FEATURE_LSPW 28 /* CPU has LSPW (lddir/ldpte instructions) */
>> #define CPU_FEATURE_AVECINT 29 /* CPU has AVEC interrupt */
>> +#define CPU_FEATURE_REDIRECTINT 30 /* CPU has interrupt remmap */
>>
>> #define LOONGARCH_CPU_CPUCFG BIT_ULL(CPU_FEATURE_CPUCFG)
>> #define LOONGARCH_CPU_LAM BIT_ULL(CPU_FEATURE_LAM)
>> @@ -133,5 +134,6 @@ enum cpu_type_enum {
>> #define LOONGARCH_CPU_PTW BIT_ULL(CPU_FEATURE_PTW)
>> #define LOONGARCH_CPU_LSPW BIT_ULL(CPU_FEATURE_LSPW)
>> #define LOONGARCH_CPU_AVECINT BIT_ULL(CPU_FEATURE_AVECINT)
>> +#define LOONGARCH_CPU_REDIRECTINT BIT_ULL(CPU_FEATURE_REDIRECTINT)
>>
>> #endif /* _ASM_CPU_H */
>> diff --git a/arch/loongarch/include/asm/loongarch.h b/arch/loongarch/include/asm/loongarch.h
>> index 52651aa0e583..3a27a5f6e460 100644
>> --- a/arch/loongarch/include/asm/loongarch.h
>> +++ b/arch/loongarch/include/asm/loongarch.h
>> @@ -1130,6 +1130,7 @@
>> #define IOCSRF_FLATMODE BIT_ULL(10)
>> #define IOCSRF_VM BIT_ULL(11)
>> #define IOCSRF_AVEC BIT_ULL(15)
>> +#define IOCSRF_REDIRECTINT BIT_ULL(16)
>>
>> #define LOONGARCH_IOCSR_VENDOR 0x10
>>
>> @@ -1142,6 +1143,7 @@
>> #define IOCSR_MISC_FUNC_TIMER_RESET BIT_ULL(21)
>> #define IOCSR_MISC_FUNC_EXT_IOI_EN BIT_ULL(48)
>> #define IOCSR_MISC_FUNC_AVEC_EN BIT_ULL(51)
>> +#define IOCSR_MISC_FUNC_REDIRECT_EN IOCSR_MISC_FUNC_AVEC_EN
> Don't define IOCSR_MISC_FUNC_REDIRECT_EN, since redirect cannot exist
> without avec, you just need to enable avec in its own driver.
OK, I initially planned to enable interrupts after the software
completes all initialization. But there's some redundant logic here.
Your suggestion is more appropriate. I'll modify the code and test this
part, thanks
>
>> #define LOONGARCH_IOCSR_CPUTEMP 0x428
>>
>> @@ -1189,6 +1191,11 @@
>>
>> #define LOONGARCH_IOCSR_EXTIOI_NODEMAP_BASE 0x14a0
>> #define LOONGARCH_IOCSR_EXTIOI_IPMAP_BASE 0x14c0
>> +#define LOONGARCH_IOCSR_REDIRECT_CFG 0x15e0
>> +#define LOONGARCH_IOCSR_REDIRECT_TBR 0x15e8 /* IRT BASE REG*/
>> +#define LOONGARCH_IOCSR_REDIRECT_CQB 0x15f0 /* IRT CACHE QUEUE BASE */
>> +#define LOONGARCH_IOCSR_REDIRECT_CQH 0x15f8 /* IRT_CACHE_QUEUE_HEAD 32bit */
>> +#define LOONGARCH_IOCSR_REDIRECT_CQT 0x15fc /* IRT_CACHE_QUEUE_TAIL 32bit */
> Keep the same style for all definitions, remove "_" in the comments,
> and you can add "," before 32bit (or remove 32bit).
OK, I got it
>
>> #define LOONGARCH_IOCSR_EXTIOI_EN_BASE 0x1600
>> #define LOONGARCH_IOCSR_EXTIOI_BOUNCE_BASE 0x1680
>> #define LOONGARCH_IOCSR_EXTIOI_ISR_BASE 0x1800
>> diff --git a/arch/loongarch/kernel/cpu-probe.c b/arch/loongarch/kernel/cpu-probe.c
>> index fedaa67cde41..543474fd1399 100644
>> --- a/arch/loongarch/kernel/cpu-probe.c
>> +++ b/arch/loongarch/kernel/cpu-probe.c
>> @@ -289,6 +289,8 @@ static inline void cpu_probe_loongson(struct cpuinfo_loongarch *c, unsigned int
>> c->options |= LOONGARCH_CPU_EIODECODE;
>> if (config & IOCSRF_AVEC)
>> c->options |= LOONGARCH_CPU_AVECINT;
>> + if (config & IOCSRF_REDIRECTINT)
>> + c->options |= LOONGARCH_CPU_REDIRECTINT;
>> if (config & IOCSRF_VM)
>> c->options |= LOONGARCH_CPU_HYPERVISOR;
>> }
>> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
>> index 25e9ad29b8c4..5dd7d6b151d9 100644
>> --- a/drivers/irqchip/Makefile
>> +++ b/drivers/irqchip/Makefile
>> @@ -113,7 +113,7 @@ obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o
>> obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o
>> obj-$(CONFIG_TI_SCI_INTA_IRQCHIP) += irq-ti-sci-inta.o
>> obj-$(CONFIG_TI_PRUSS_INTC) += irq-pruss-intc.o
>> -obj-$(CONFIG_IRQ_LOONGARCH_CPU) += irq-loongarch-cpu.o irq-loongarch-avec.o
>> +obj-$(CONFIG_IRQ_LOONGARCH_CPU) += irq-loongarch-cpu.o irq-loongarch-avec.o irq-loongarch-ir.o
>> obj-$(CONFIG_LOONGSON_LIOINTC) += irq-loongson-liointc.o
>> obj-$(CONFIG_LOONGSON_EIOINTC) += irq-loongson-eiointc.o
>> obj-$(CONFIG_LOONGSON_HTPIC) += irq-loongson-htpic.o
>> diff --git a/drivers/irqchip/irq-loongarch-avec.c b/drivers/irqchip/irq-loongarch-avec.c
>> index 80e55955a29f..101bae6f981c 100644
>> --- a/drivers/irqchip/irq-loongarch-avec.c
>> +++ b/drivers/irqchip/irq-loongarch-avec.c
>> @@ -24,7 +24,6 @@
>> #define VECTORS_PER_REG 64
>> #define IRR_VECTOR_MASK 0xffUL
>> #define IRR_INVALID_MASK 0x80000000UL
>> -#define AVEC_MSG_OFFSET 0x100000
>>
>> #ifdef CONFIG_SMP
>> struct pending_list {
>> @@ -47,15 +46,6 @@ struct avecintc_chip {
>>
>> static struct avecintc_chip loongarch_avec;
>>
>> -struct avecintc_data {
>> - struct list_head entry;
>> - unsigned int cpu;
>> - unsigned int vec;
>> - unsigned int prev_cpu;
>> - unsigned int prev_vec;
>> - unsigned int moving;
>> -};
>> -
>> static inline void avecintc_enable(void)
>> {
>> u64 value;
>> @@ -85,7 +75,7 @@ static inline void pending_list_init(int cpu)
>> INIT_LIST_HEAD(&plist->head);
>> }
>>
>> -static void avecintc_sync(struct avecintc_data *adata)
>> +void avecintc_sync(struct avecintc_data *adata)
>> {
>> struct pending_list *plist;
>>
>> @@ -109,7 +99,7 @@ static int avecintc_set_affinity(struct irq_data *data, const struct cpumask *de
>> return -EBUSY;
>>
>> if (cpu_online(adata->cpu) && cpumask_test_cpu(adata->cpu, dest))
>> - return 0;
>> + return IRQ_SET_MASK_OK_DONE;
>>
>> cpumask_and(&intersect_mask, dest, cpu_online_mask);
>>
>> @@ -121,7 +111,8 @@ static int avecintc_set_affinity(struct irq_data *data, const struct cpumask *de
>> adata->cpu = cpu;
>> adata->vec = vector;
>> per_cpu_ptr(irq_map, adata->cpu)[adata->vec] = irq_data_to_desc(data);
>> - avecintc_sync(adata);
>> + if (!cpu_has_redirectint)
>> + avecintc_sync(adata);
>> }
>>
>> irq_data_update_effective_affinity(data, cpumask_of(cpu));
>> @@ -136,7 +127,8 @@ static int avecintc_cpu_online(unsigned int cpu)
>>
>> guard(raw_spinlock)(&loongarch_avec.lock);
>>
>> - avecintc_enable();
>> + if (!cpu_has_redirectint)
>> + avecintc_enable();
> You can do it unconditionally, as said before (then no need to enable
> in the ir driver).
OK , I got it
>
>> irq_matrix_online(loongarch_avec.vector_matrix);
>>
>> @@ -242,6 +234,7 @@ static void avecintc_irq_dispatch(struct irq_desc *desc)
>> d = this_cpu_read(irq_map[vector]);
>> if (d) {
>> generic_handle_irq_desc(d);
>> +
>> } else {
>> spurious_interrupt();
>> pr_warn("Unexpected IRQ occurs on CPU#%d [vector %ld]\n", smp_processor_id(), vector);
>> @@ -412,6 +405,9 @@ static int __init pch_msi_parse_madt(union acpi_subtable_headers *header,
>>
>> static inline int __init acpi_cascade_irqdomain_init(void)
>> {
>> + if (cpu_has_redirectint)
>> + return loongarch_irq_redirect_init(loongarch_avec.domain);
>> +
>> return acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, pch_msi_parse_madt, 1);
>> }
>>
>> diff --git a/drivers/irqchip/irq-loongarch-ir.c b/drivers/irqchip/irq-loongarch-ir.c
>> new file mode 100644
>> index 000000000000..d3c664d6f98e
>> --- /dev/null
>> +++ b/drivers/irqchip/irq-loongarch-ir.c
>> @@ -0,0 +1,576 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2020 Loongson Technologies, Inc.
>> + */
>> +
>> +#include <linux/cpuhotplug.h>
>> +#include <linux/init.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/kernel.h>
>> +#include <linux/irq.h>
>> +#include <linux/irqchip.h>
>> +#include <linux/irqdomain.h>
>> +#include <linux/spinlock.h>
>> +#include <linux/msi.h>
>> +
>> +#include <asm/irq.h>
>> +#include <asm/loongarch.h>
>> +#include <asm/setup.h>
>> +#include <larchintrin.h>
>> +
>> +#include "irq-loongson.h"
>> +#include "irq-msi-lib.h"
>> +
>> +#define IRD_ENTRIES 65536
>> +
>> +/* redirect entry size 128bits */
>> +#if (defined(CONFIG_PAGE_SIZE_4KB))
>> +#define IRD_PAGE_ORDER 8
>> +#elif (defined(CONFIG_PAGE_SIZE_16KB))
>> +#define IRD_PAGE_ORDER 6
>> +#endif
> #define IRD_PAGE_ORDER (20 - PAGE_SHIFT)
OK , I got it
>
>> +
>> +/* irt cache invalid queue */
>> +#define INVALID_QUEUE_SIZE 4096
>> +
>> +#if (defined(CONFIG_PAGE_SIZE_4KB))
>> +#define INVALID_QUEUE_PAGE_ORDER 4
>> +#elif (defined(CONFIG_PAGE_SIZE_16KB))
>> +#define INVALID_QUEUE_PAGE_ORDER 2
>> +#endif
> #define INVALID_QUEUE_PAGE_ORDER (16 - PAGE_SHIFT)
OK , I got it
>
>> +
>> +#define GPID_ADDR_MASK 0x3ffffffffffULL
>> +#define GPID_ADDR_SHIFT 6
>> +
>> +#define CQB_SIZE_SHIFT 0
>> +#define CQB_SIZE_MASK 0xf
>> +#define CQB_ADDR_SHIFT 12
>> +#define CQB_ADDR_MASK (0xfffffffffULL)
>> +
>> +#define CFG_DISABLE_IDLE 2
>> +#define INVALID_INDEX 0
>> +
>> +#define MAX_IR_ENGINES 16
>> +
>> +struct irq_domain *redirect_domain;
>> +struct redirect_entry {
>> + struct {
>> + __u64 valid : 1,
>> + res1 : 5,
>> + gpid : 42,
>> + res2 : 8,
>> + vector : 8;
>> + } lo;
>> + __u64 hi;
>> +};
>> +
>> +struct redirect_gpid {
> What does gpid stand for?
The global process interrupt descriptor(GPID)is a secondary descriptor
of an interrupt redirect entry. It mainly describes the relevant
information of the target CPU for interrupt routing.
The relationship between redirect_entry and redirect_gpid is some
similar to that between PMD and PTE. this driver code submitted here
only uses part of the functionality, and will be gradually improved later.
>> + u64 pir[4]; /* Pending interrupt requested */
>> + u8 en : 1, /* doorbell */
>> + res0 : 7;
>> + u8 irqnum;
>> + u16 res1;
>> + u32 dst;
>> + u32 rsvd[6];
>> +} __aligned(64);
>> +
>> +struct irde_inv_cmd {
> What does irde stand for?
Interrupt redirect enqueue.....maybe it's a bit awkward.
Actually, this is a descriptor for an interrupt controller's command
queue. Do you have any suggestions?
>
>> + union {
>> + __u64 cmd_info;
>> + struct {
>> + __u64 res1 : 4,
>> + type : 1,
>> + need_notice : 1,
>> + pad : 2,
>> + index : 16,
>> + pad2 : 40;
>> + } index;
>> + };
>> + __u64 notice_addr;
>> +};
>> +
>> +struct redirect_item {
>> + int index;
>> + struct redirect_entry *entry;
>> + struct redirect_gpid *gpid;
>> + struct redirect_table *table;
>> +};
>> +
>> +struct redirect_table {
>> + int node;
>> + struct redirect_entry *table;
>> + unsigned long *bitmap;
>> + unsigned int nr_ird;
>> + struct page *page;
>> + raw_spinlock_t lock;
>> +};
>> +
>> +struct redirect_queue {
>> + int node;
>> + u64 base;
>> + u32 max_size;
>> + int head;
>> + int tail;
>> + struct page *page;
>> + raw_spinlock_t lock;
>> +};
>> +
>> +struct la_irde_desc {
>> + struct redirect_table ird_table;
>> + struct redirect_queue inv_queue;
>> +};
> Remove the la_ prefix, and then change the order of these structures as:
> redirect_entry, redirect_gpid, redirect_table, redirect_item,
> redirect_queue, irde_desc, irde_inv_cmd.
OK, I got it
>
>> +
>> +struct smp_invalid_arg {
>> + struct redirect_queue *queue;
>> + struct irde_inv_cmd *cmd;
>> +};
>> +
>> +static struct la_irde_desc irde_descs[MAX_IR_ENGINES];
>> +static phys_addr_t msi_base_addr;
>> +
>> +static inline void redirect_enable(int node)
>> +{
>> + u64 value;
>> +
>> + if (redirect_domain) {
>> + value = iocsr_read64(LOONGARCH_IOCSR_MISC_FUNC);
>> + value |= IOCSR_MISC_FUNC_REDIRECT_EN;
>> + iocsr_write64(value, LOONGARCH_IOCSR_MISC_FUNC);
>> + }
>> +}
> This function can be removed, as said before.
OK, I got it
>
>> +
>> +static inline bool invalid_queue_is_full(int node_id, u32 *tail)
>> +{
>> + u32 head;
>> +
>> + head = iocsr_read32(LOONGARCH_IOCSR_REDIRECT_CQH);
>> + *tail = iocsr_read32(LOONGARCH_IOCSR_REDIRECT_CQT);
>> +
>> + return !!(head == ((*tail + 1) % INVALID_QUEUE_SIZE));
>> +}
>> +
>> +static void invalid_enqueue(struct redirect_queue *rqueue, struct irde_inv_cmd *cmd)
>> +{
>> + struct irde_inv_cmd *inv_addr;
>> + u32 tail;
>> +
>> + guard(raw_spinlock_irqsave)(&rqueue->lock);
>> +
>> + while (invalid_queue_is_full(rqueue->node, &tail))
>> + cpu_relax();
>> +
>> + inv_addr = (struct irde_inv_cmd *)(rqueue->base + tail * sizeof(struct irde_inv_cmd));
>> + memcpy(inv_addr, cmd, sizeof(struct irde_inv_cmd));
>> + tail = (tail + 1) % INVALID_QUEUE_SIZE;
>> +
>> + wmb();
>> +
>> + iocsr_write32(tail, LOONGARCH_IOCSR_REDIRECT_CQT);
>> +}
>> +
>> +static void smp_call_invalid_enqueue(void *arg)
>> +{
>> + struct smp_invalid_arg *s_arg = (struct smp_invalid_arg *)arg;
>> +
>> + invalid_enqueue(s_arg->queue, s_arg->cmd);
>> +}
>> +
>> +static void irde_invlid_entry_node(struct redirect_item *item)
>> +{
>> + struct redirect_queue *rqueue;
>> + struct smp_invalid_arg arg;
>> + struct irde_inv_cmd cmd;
>> + volatile u64 raddr = 0;
>> + int node = item->table->node, cpu;
>> +
>> + rqueue = &(irde_descs[node].inv_queue);
>> + cmd.cmd_info = 0;
>> + cmd.index.type = INVALID_INDEX;
>> + cmd.index.need_notice = 1;
>> + cmd.index.index = item->index;
>> + cmd.notice_addr = (u64)(__pa(&raddr));
>> +
>> + if (cpu_to_node(smp_processor_id()) == node)
>> + invalid_enqueue(rqueue, &cmd);
>> + else {
>> + for_each_cpu(cpu, cpumask_of_node(node)) {
>> + if (cpu_online(cpu))
>> + break;
>> + }
>> + arg.queue = rqueue;
>> + arg.cmd = &cmd;
>> + smp_call_function_single(cpu, smp_call_invalid_enqueue, &arg, 0);
>> + }
>> +
>> + while (!raddr)
>> + cpu_relax();
>> +
>> +}
>> +
>> +static inline struct avecintc_data *irq_data_get_avec_data(struct irq_data *data)
>> +{
>> + return data->parent_data->chip_data;
>> +}
>> +
>> +static int redirect_table_alloc(struct redirect_item *item, struct redirect_table *ird_table)
>> +{
>> + int index;
>> +
>> + guard(raw_spinlock_irqsave)(&ird_table->lock);
>> +
>> + index = find_first_zero_bit(ird_table->bitmap, IRD_ENTRIES);
>> + if (index > IRD_ENTRIES) {
>> + pr_err("No redirect entry to use\n");
>> + return -ENOMEM;
>> + }
>> +
>> + __set_bit(index, ird_table->bitmap);
>> +
>> + item->index = index;
>> + item->entry = &ird_table->table[index];
>> + item->table = ird_table;
>> +
>> + return 0;
>> +}
>> +
>> +static int redirect_table_free(struct redirect_item *item)
>> +{
>> + struct redirect_table *ird_table;
>> + struct redirect_entry *entry;
>> + unsigned long flags;
>> +
>> + ird_table = item->table;
>> +
>> + entry = item->entry;
>> + memset(entry, 0, sizeof(struct redirect_entry));
>> +
>> + raw_spin_lock_irqsave(&ird_table->lock, flags);
>> + bitmap_release_region(ird_table->bitmap, item->index, 0);
>> + raw_spin_unlock_irqrestore(&ird_table->lock, flags);
>> +
>> + kfree(item->gpid);
>> +
>> + irde_invlid_entry_node(item);
>> +
>> + return 0;
>> +}
>> +
>> +static inline void redirect_domain_prepare_entry(struct redirect_item *item, struct avecintc_data *adata)
>> +{
>> + struct redirect_entry *entry = item->entry;
>> +
>> + item->gpid->en = 1;
>> + item->gpid->irqnum = adata->vec;
>> + item->gpid->dst = adata->cpu;
>> +
>> + entry->lo.valid = 1;
>> + entry->lo.gpid = ((long)item->gpid >> GPID_ADDR_SHIFT) & (GPID_ADDR_MASK);
>> + entry->lo.vector = 0xff;
>> + wmb();
>> +}
>> +
>> +static int loongarch_redirect_set_affinity(struct irq_data *data, const struct cpumask *dest, bool force)
> Remove the loongarch_ prefix since it is an internal function.
OK, I got it
>
>> +{
>> + struct redirect_item *item = data->chip_data;
>> + struct avecintc_data *adata;
>> + int ret;
>> +
>> + ret = irq_chip_set_affinity_parent(data, dest, force);
>> + if (ret == IRQ_SET_MASK_OK_DONE)
>> + return IRQ_SET_MASK_OK;
>> + else if (ret) {
>> + pr_err("IRDE:set_affinity error %d\n", ret);
>> + return ret;
>> + }
>> +
>> + adata = irq_data_get_avec_data(data);
>> +
>> + redirect_domain_prepare_entry(item, adata);
>> +
>> + irde_invlid_entry_node(item);
>> +
>> + avecintc_sync(adata);
>> + return IRQ_SET_MASK_OK;
>> +}
>> +
>> +static void loongarch_redirect_compose_msg(struct irq_data *d, struct msi_msg *msg)
> Use redirect_compose_msi_msg() to keep consistency with other
> loongarch-specific drivers.
OK, I got it
>
>> +{
>> + struct redirect_item *item;
>> +
>> + item = irq_data_get_irq_chip_data(d);
>> + msg->address_lo = (msi_base_addr | 1 << 2 | ((item->index & 0xffff) << 4));
>> + msg->address_hi = 0x0;
>> + msg->data = 0x0;
>> +}
>> +
>> +static inline void loongarch_redirect_ack_irq(struct irq_data *d)
>> +{
>> +}
>> +
>> +static inline void loongarch_redirect_unmask_irq(struct irq_data *d)
>> +{
>> +}
>> +
>> +static inline void loongarch_redirect_mask_irq(struct irq_data *d)
>> +{
>> +}
> Remove the loongarch_ prefix since they are internal functions.
OK, I got it
>
>> +
>> +static struct irq_chip loongarch_redirect_chip = {
> Use redirect_irq_controller to keep consistency with other
> loongarch-specific drivers.
>
>> + .name = "redirect",
> Use "REDIRECT" for consistency, too.
OK, I got it
>
>> + .irq_ack = loongarch_redirect_ack_irq,
>> + .irq_mask = loongarch_redirect_mask_irq,
>> + .irq_unmask = loongarch_redirect_unmask_irq,
>> + .irq_set_affinity = loongarch_redirect_set_affinity,
>> + .irq_compose_msi_msg = loongarch_redirect_compose_msg,
>> +};
>> +
>> +static void loongarch_irq_redirect_free_resources(struct irq_domain *domain,
>> + unsigned int virq, unsigned int nr_irqs)
>> +{
>> + struct irq_data *irq_data;
>> + struct redirect_item *item;
>> +
>> + for (int i = 0; i < nr_irqs; i++) {
>> + irq_data = irq_domain_get_irq_data(domain, virq + i);
>> + if (irq_data && irq_data->chip_data) {
>> + item = irq_data->chip_data;
>> + redirect_table_free(item);
>> + kfree(item);
>> + }
>> + }
>> +}
>> +
>> +static int loongarch_irq_redirect_alloc(struct irq_domain *domain,
>> + unsigned int virq, unsigned int nr_irqs,
>> + void *arg)
>> +{
>> + struct redirect_table *ird_table;
>> + struct avecintc_data *avec_data;
>> + struct irq_data *irq_data;
>> + int ret, i, node;
>> +
>> + node = ((msi_alloc_info_t *)arg)->desc->dev->numa_node;
>> + ird_table = &irde_descs[node].ird_table;
>> + ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
>> + if (ret < 0)
>> + return ret;
>> +
>> + for (i = 0; i < nr_irqs; i++) {
>> + struct redirect_item *item;
>> +
>> + item = kzalloc(sizeof(struct redirect_item), GFP_KERNEL);
>> + if (!item) {
>> + pr_err("Alloc redirect descriptor failed\n");
>> + goto out_free_resources;
>> + }
>> +
>> + irq_data = irq_domain_get_irq_data(domain, virq + i);
>> +
>> + avec_data = irq_data_get_avec_data(irq_data);
>> + ret = redirect_table_alloc(item, ird_table);
>> + if (ret) {
>> + pr_err("Alloc redirect table entry failed\n");
>> + goto out_free_resources;
>> + }
>> +
>> + item->gpid = kzalloc_node(sizeof(struct redirect_gpid), GFP_KERNEL, node);
>> + if (!item->gpid) {
>> + pr_err("Alloc redirect GPID failed\n");
>> + goto out_free_resources;
>> + }
>> +
>> + irq_data->chip_data = item;
>> + irq_data->chip = &loongarch_redirect_chip;
>> + redirect_domain_prepare_entry(item, avec_data);
>> + }
>> + return 0;
>> +
>> +out_free_resources:
>> + loongarch_irq_redirect_free_resources(domain, virq, nr_irqs);
>> + irq_domain_free_irqs_common(domain, virq, nr_irqs);
>> +
>> + return -EINVAL;
>> +}
>> +
>> +static void loongarch_irq_redirect_free(struct irq_domain *domain,
>> + unsigned int virq, unsigned int nr_irqs)
>> +{
>> + loongarch_irq_redirect_free_resources(domain, virq, nr_irqs);
>> + return irq_domain_free_irqs_common(domain, virq, nr_irqs);
>> +}
>> +
>> +static const struct irq_domain_ops loongarch_redirect_domain_ops = {
>> + .alloc = loongarch_irq_redirect_alloc,
>> + .free = loongarch_irq_redirect_free,
>> + .select = msi_lib_irq_domain_select,
>> +};
> For consistency, please use:
> static const struct irq_domain_ops redirect_domain_ops = {
> .alloc = redirect_domain_alloc,
> .free = redirect_domain_free,
> .select = msi_lib_irq_domain_select,
> };
OK, I got it, thanks
>
>> +
>> +static int redirect_queue_init(int node)
>> +{
>> + struct redirect_queue *rqueue = &(irde_descs[node].inv_queue);
>> + struct page *pages;
>> +
>> + pages = alloc_pages_node(0, GFP_KERNEL | __GFP_ZERO, INVALID_QUEUE_PAGE_ORDER);
>> + if (!pages) {
>> + pr_err("Node [%d] Invalid Queue alloc pages failed!\n", node);
>> + return -ENOMEM;
>> + }
>> +
>> + rqueue->page = pages;
>> + rqueue->base = (u64)page_address(pages);
>> + rqueue->max_size = INVALID_QUEUE_SIZE;
>> + rqueue->head = 0;
>> + rqueue->tail = 0;
>> + rqueue->node = node;
>> + raw_spin_lock_init(&rqueue->lock);
>> +
>> + iocsr_write32(0, LOONGARCH_IOCSR_REDIRECT_CQH);
>> + iocsr_write32(0, LOONGARCH_IOCSR_REDIRECT_CQT);
>> + iocsr_write64(((rqueue->base & (CQB_ADDR_MASK << CQB_ADDR_SHIFT)) |
>> + (CQB_SIZE_MASK << CQB_SIZE_SHIFT)), LOONGARCH_IOCSR_REDIRECT_CQB);
>> + return 0;
>> +}
>> +
>> +static int redirect_table_init(int node)
>> +{
>> + struct redirect_table *ird_table = &(irde_descs[node].ird_table);
>> + struct page *pages;
>> + unsigned long *bitmap;
>> +
>> + pages = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, IRD_PAGE_ORDER);
>> + if (!pages) {
>> + pr_err("Node [%d] redirect table alloc pages failed!\n", node);
>> + return -ENOMEM;
>> + }
>> + ird_table->page = pages;
>> + ird_table->table = page_address(pages);
>> +
>> + bitmap = bitmap_zalloc(IRD_ENTRIES, GFP_KERNEL);
>> + if (!bitmap) {
>> + pr_err("Node [%d] redirect table bitmap alloc pages failed!\n", node);
>> + return -ENOMEM;
>> + }
>> +
>> + ird_table->bitmap = bitmap;
>> + ird_table->nr_ird = IRD_ENTRIES;
>> + ird_table->node = node;
>> +
>> + raw_spin_lock_init(&ird_table->lock);
>> +
>> + if (redirect_queue_init(node))
>> + return -EINVAL;
>> +
>> + iocsr_write64(CFG_DISABLE_IDLE, LOONGARCH_IOCSR_REDIRECT_CFG);
>> + iocsr_write64(__pa(ird_table->table), LOONGARCH_IOCSR_REDIRECT_TBR);
>> +
>> + return 0;
>> +}
>> +
>> +static void redirect_table_fini(int node)
>> +{
>> + struct redirect_table *ird_table = &(irde_descs[node].ird_table);
>> + struct redirect_queue *rqueue = &(irde_descs[node].inv_queue);
>> +
>> + if (ird_table->page) {
>> + __free_pages(ird_table->page, IRD_PAGE_ORDER);
>> + ird_table->table = NULL;
>> + ird_table->page = NULL;
>> + }
>> +
>> + if (ird_table->page) {
>> + bitmap_free(ird_table->bitmap);
>> + ird_table->bitmap = NULL;
>> + }
>> +
>> + if (rqueue->page) {
>> + __free_pages(rqueue->page, INVALID_QUEUE_PAGE_ORDER);
>> + rqueue->page = NULL;
>> + rqueue->base = 0;
>> + }
>> +
>> + iocsr_write64(0, LOONGARCH_IOCSR_REDIRECT_CQB);
>> + iocsr_write64(0, LOONGARCH_IOCSR_REDIRECT_TBR);
>> +}
>> +
>> +static int redirect_cpu_online(unsigned int cpu)
>> +{
>> + int ret, node = cpu_to_node(cpu);
>> +
>> + if (cpu != cpumask_first(cpumask_of_node(node)))
>> + return 0;
>> +
>> + ret = redirect_table_init(node);
>> + if (ret) {
>> + redirect_table_fini(node);
>> + return -EINVAL;
>> + }
>> +
>> + redirect_enable(node);
>> + return 0;
>> +}
>> +
>> +#if defined(CONFIG_ACPI)
>> +static int __init pch_msi_parse_madt(union acpi_subtable_headers *header,
>> + const unsigned long end)
>> +{
>> + struct acpi_madt_msi_pic *pchmsi_entry = (struct acpi_madt_msi_pic *)header;
>> +
>> + msi_base_addr = pchmsi_entry->msg_address - AVEC_MSG_OFFSET;
>> +
>> + return pch_msi_acpi_init_avec(redirect_domain);
>> +}
>> +
>> +static int __init acpi_cascade_irqdomain_init(void)
>> +{
>> + return acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, pch_msi_parse_madt, 1);
>> +}
>> +
>> +int __init loongarch_irq_redirect_init(struct irq_domain *parent)
> Use redirect_acpi_init() for consistency.
>
>
OK, I got it
>
>
>
>
> Huacai
Thank you
Tianyang
>
>> +{
>> + struct fwnode_handle *fwnode;
>> + struct irq_domain *domain;
>> + int ret;
>> +
>> + fwnode = irq_domain_alloc_named_fwnode("redirect");
>> + if (!fwnode) {
>> + pr_err("Unable to alloc redirect domain handle\n");
>> + goto fail;
>> + }
>> +
>> + domain = irq_domain_create_hierarchy(parent, 0, IRD_ENTRIES, fwnode,
>> + &loongarch_redirect_domain_ops, irde_descs);
>> + if (!domain) {
>> + pr_err("Unable to alloc redirect domain\n");
>> + goto out_free_fwnode;
>> + }
>> +
>> + redirect_domain = domain;
>> +
>> + ret = redirect_table_init(0);
>> + if (ret)
>> + goto out_free_table;
>> +
>> + ret = acpi_cascade_irqdomain_init();
>> + if (ret < 0) {
>> + pr_err("Failed to cascade IRQ domain, ret=%d\n", ret);
>> + goto out_free_table;
>> + }
>> +
>> + cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_REDIRECT_STARTING,
>> + "irqchip/loongarch/redirect:starting",
>> + redirect_cpu_online, NULL);
>> +
>> + pr_info("loongarch irq redirect modules init succeeded\n");
>> + redirect_enable(0);
>> + return 0;
>> +
>> +out_free_table:
>> + redirect_table_fini(0);
>> + irq_domain_remove(redirect_domain);
>> + redirect_domain = NULL;
>> +out_free_fwnode:
>> + irq_domain_free_fwnode(fwnode);
>> +fail:
>> + return -EINVAL;
>> +}
>> +#endif
>> diff --git a/drivers/irqchip/irq-loongson.h b/drivers/irqchip/irq-loongson.h
>> index 11fa138d1f44..f29ab8ec368e 100644
>> --- a/drivers/irqchip/irq-loongson.h
>> +++ b/drivers/irqchip/irq-loongson.h
>> @@ -5,6 +5,15 @@
>>
>> #ifndef _DRIVERS_IRQCHIP_IRQ_LOONGSON_H
>> #define _DRIVERS_IRQCHIP_IRQ_LOONGSON_H
>> +#define AVEC_MSG_OFFSET 0x100000
>> +struct avecintc_data {
>> + struct list_head entry;
>> + unsigned int cpu;
>> + unsigned int vec;
>> + unsigned int prev_cpu;
>> + unsigned int prev_vec;
>> + unsigned int moving;
>> +};
>>
>> int find_pch_pic(u32 gsi);
>>
>> @@ -24,4 +33,7 @@ int pch_msi_acpi_init(struct irq_domain *parent,
>> struct acpi_madt_msi_pic *acpi_pchmsi);
>> int pch_msi_acpi_init_avec(struct irq_domain *parent);
>>
>> +int loongarch_irq_redirect_init(struct irq_domain *parent);
>> +
>> +void avecintc_sync(struct avecintc_data *adata);
>> #endif /* _DRIVERS_IRQCHIP_IRQ_LOONGSON_H */
>> diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
>> index 6cc5e484547c..2fd5531fa378 100644
>> --- a/include/linux/cpuhotplug.h
>> +++ b/include/linux/cpuhotplug.h
>> @@ -146,6 +146,7 @@ enum cpuhp_state {
>> CPUHP_AP_IRQ_MIPS_GIC_STARTING,
>> CPUHP_AP_IRQ_EIOINTC_STARTING,
>> CPUHP_AP_IRQ_AVECINTC_STARTING,
>> + CPUHP_AP_IRQ_REDIRECT_STARTING,
>> CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING,
>> CPUHP_AP_IRQ_THEAD_ACLINT_SSWI_STARTING,
>> CPUHP_AP_IRQ_RISCV_IMSIC_STARTING,
>> --
>> 2.43.0
>>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/2] irq/irq-loongarch-ir:Add Redirect irqchip support
2025-03-03 21:28 ` Thomas Gleixner
@ 2025-03-06 2:07 ` Tianyang Zhang
0 siblings, 0 replies; 13+ messages in thread
From: Tianyang Zhang @ 2025-03-06 2:07 UTC (permalink / raw)
To: Thomas Gleixner, Huacai Chen
Cc: kernel, corbet, alexs, si.yanteng, jiaxun.yang, peterz, wangliupu,
lvjianmin, maobibo, siyanteng, gaosong, yangtiezhu, loongarch,
linux-doc, linux-kernel
Hi, Thomas
在 2025/3/4 上午5:28, Thomas Gleixner 写道:
> On Mon, Mar 03 2025 at 22:34, Huacai Chen wrote:
>> On Mon, Mar 3, 2025 at 6:15 PM Tianyang Zhang <zhangtianyang@loongson.cn> wrote:
>>> +static const struct irq_domain_ops loongarch_redirect_domain_ops = {
>>> + .alloc = loongarch_irq_redirect_alloc,
>>> + .free = loongarch_irq_redirect_free,
>>> + .select = msi_lib_irq_domain_select,
>>> +};
>> For consistency, please use:
>> static const struct irq_domain_ops redirect_domain_ops = {
>> .alloc = redirect_domain_alloc,
>> .free = redirect_domain_free,
>> .select = msi_lib_irq_domain_select,
>> };
> This formatting requirement applies to all structures in this patch. See
>
> https://www.kernel.org/doc/html/latest/process/maintainer-tip.html#coding-style-notes
>
> for further information.
OK, I got it , thanks
> Thanks,
>
> tglx
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 1/2] Docs/LoongArch: Add Advanced Extended-Redirect IRQ model description
2025-03-03 13:36 ` Huacai Chen
@ 2025-03-06 2:09 ` Tianyang Zhang
0 siblings, 0 replies; 13+ messages in thread
From: Tianyang Zhang @ 2025-03-06 2:09 UTC (permalink / raw)
To: Huacai Chen
Cc: kernel, corbet, alexs, si.yanteng, tglx, jiaxun.yang, peterz,
wangliupu, lvjianmin, maobibo, siyanteng, gaosong, yangtiezhu,
loongarch, linux-doc, linux-kernel
Hi, Huacai
在 2025/3/3 下午9:36, Huacai Chen 写道:
> Hi, Tianyang,
>
> On Mon, Mar 3, 2025 at 6:15 PM Tianyang Zhang <zhangtianyang@loongson.cn> wrote:
>> Introduce the Redirect interrupt controllers.When the redirected interrupt
>> controller is enabled, the routing target of MSI interrupts is no longer a
>> specific CPU and vector number, but a specific redirected entry. The actual
>> CPU and vector number used are described by the redirected entry.
> You call it "redirect interrupt controller", then don't call
> "redirected interrupt controller" in other place.
OK, I got it
>
>> Signed-off-by: Tianyang Zhang <zhangtianyang@loongson.cn>
>> ---
>> .../arch/loongarch/irq-chip-model.rst | 38 +++++++++++++++++++
>> .../zh_CN/arch/loongarch/irq-chip-model.rst | 37 ++++++++++++++++++
>> 2 files changed, 75 insertions(+)
>>
>> diff --git a/Documentation/arch/loongarch/irq-chip-model.rst b/Documentation/arch/loongarch/irq-chip-model.rst
>> index a7ecce11e445..45cba22ff181 100644
>> --- a/Documentation/arch/loongarch/irq-chip-model.rst
>> +++ b/Documentation/arch/loongarch/irq-chip-model.rst
>> @@ -181,6 +181,44 @@ go to PCH-PIC/PCH-LPC and gathered by EIOINTC, and then go to CPUINTC directly::
>> | Devices |
>> +---------+
>>
>> +Advanced Extended-Redirect IRQ model
> Call it as "Advanced Extended IRQ model (with redirection)" and
> "高级扩展IRQ模型 (带重定向)"
OK, I got it , thanks
>
> Huacai
>
>> +===============
>> +
>> +In this model, IPI (Inter-Processor Interrupt) and CPU Local Timer interrupt go
>> +to CPUINTC directly, CPU UARTS interrupts go to LIOINTC, PCH-MSI interrupts go
>> +to REDIRECT for remapping it to AVEC, and then go to CPUINTC directly, while all
>> +other devices interrupts go to PCH-PIC/PCH-LPC and gathered by EIOINTC, and then
>> +go to CPUINTC directly::
>> +
>> + +-----+ +-----------------------+ +-------+
>> + | IPI | --> | CPUINTC | <-- | Timer |
>> + +-----+ +-----------------------+ +-------+
>> + ^ ^ ^
>> + | | |
>> + +---------+ +----------+ +---------+ +-------+
>> + | EIOINTC | | AVECINTC | | LIOINTC | <-- | UARTs |
>> + +---------+ +----------+ +---------+ +-------+
>> + ^ ^
>> + | |
>> + | +----------+
>> + | | REDIRECT |
>> + | +----------+
>> + | ^
>> + | |
>> + +---------+ +---------+
>> + | PCH-PIC | | PCH-MSI |
>> + +---------+ +---------+
>> + ^ ^ ^
>> + | | |
>> + +---------+ +---------+ +---------+
>> + | Devices | | PCH-LPC | | Devices |
>> + +---------+ +---------+ +---------+
>> + ^
>> + |
>> + +---------+
>> + | Devices |
>> + +---------+
>> +
>> ACPI-related definitions
>> ========================
>>
>> diff --git a/Documentation/translations/zh_CN/arch/loongarch/irq-chip-model.rst b/Documentation/translations/zh_CN/arch/loongarch/irq-chip-model.rst
>> index d4ff80de47b6..d935da47ce3b 100644
>> --- a/Documentation/translations/zh_CN/arch/loongarch/irq-chip-model.rst
>> +++ b/Documentation/translations/zh_CN/arch/loongarch/irq-chip-model.rst
>> @@ -174,6 +174,43 @@ CPU串口(UARTs)中断发送到LIOINTC,PCH-MSI中断发送到AVECINTC,
>> | Devices |
>> +---------+
>>
>> +高级扩展-重定向IRQ模型
>> +===============
>> +
>> +在这种模型里面,IPI(Inter-Processor Interrupt)和CPU本地时钟中断直接发送到CPUINTC,
>> +CPU串口(UARTs)中断发送到LIOINTC,PCH-MSI中断首先发送到REDIRECT模块,完成重定向后发
>> +送到AVECINTC,而后通过AVECINTC直接送达CPUINTC,而其他所有设备的中断则分别发送到所连
>> +接的PCH-PIC/PCH-LPC,然后由EIOINTC统一收集,再直接到达CPUINTC::
>> +
>> + +-----+ +----------------------Thomas-+ +-------+
>> + | IPI | --> | CPUINTC | <-- | Timer |
>> + +-----+ +-----------------------+ +-------+
>> + ^ ^ ^
>> + | | |
>> + +---------+ +----------+ +---------+ +-------+
>> + | EIOINTC | | AVECINTC | | LIOINTC | <-- | UARTs |
>> + +---------+ +----------+ +---------+ +-------+
>> + ^ ^
>> + | |
>> + | +----------+
>> + | | REDIRECT |
>> + | +----------+
>> + | ^
>> + | |
>> + +---------+ +---------+
>> + | PCH-PIC | | PCH-MSI |
>> + +---------+ +---------+
>> + ^ ^ ^
>> + | | |
>> + +---------+ +---------+ +---------+
>> + | Devices | | PCH-LPC | | Devices |
>> + +---------+ +---------+ +---------+
>> + ^
>> + |
>> + +---------+
>> + | Devices |
>> + +---------+
>> +
>> ACPI相关的定义
>> ==============
>>
>> --
>> 2.43.0
>>
>>
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 2/2] irq/irq-loongarch-ir:Add Redirect irqchip support
2025-03-17 8:22 [PATCH v1 0/2] Loongarch irq-redirect supprot Tianyang Zhang
@ 2025-03-17 8:22 ` Tianyang Zhang
2025-03-19 5:55 ` Tianyang Zhang
0 siblings, 1 reply; 13+ messages in thread
From: Tianyang Zhang @ 2025-03-17 8:22 UTC (permalink / raw)
To: chenhuacai, kernel, corbet, alexs, si.yanteng, tglx, jiaxun.yang,
peterz, wangliupu, lvjianmin, maobibo, siyanteng, gaosong,
yangtiezhu
Cc: loongarch, linux-doc, linux-kernel, Tianyang Zhang
The main function of the Redirected interrupt controller is to manage the
redirected-interrupt table, which consists of many redirected entries.
When MSI interrupts are requested, the driver creates a corresponding
redirected entry that describes the target CPU/vector number and the
operating mode of the interrupt. The redirected interrupt module has an
independent cache, and during the interrupt routing process, it will
prioritize the redirected entries that hit the cache. The driver
invalidates certain entry caches via a command queue.
Co-developed-by: Liupu Wang <wangliupu@loongson.cn>
Signed-off-by: Liupu Wang <wangliupu@loongson.cn>
Signed-off-by: Tianyang Zhang <zhangtianyang@loongson.cn>
---
arch/loongarch/include/asm/cpu-features.h | 1 +
arch/loongarch/include/asm/cpu.h | 2 +
arch/loongarch/include/asm/loongarch.h | 6 +
arch/loongarch/kernel/cpu-probe.c | 3 +
drivers/irqchip/Makefile | 2 +-
drivers/irqchip/irq-loongarch-avec.c | 21 +-
drivers/irqchip/irq-loongarch-ir.c | 561 ++++++++++++++++++++++
drivers/irqchip/irq-loongson.h | 12 +
include/linux/cpuhotplug.h | 1 +
9 files changed, 595 insertions(+), 14 deletions(-)
create mode 100644 drivers/irqchip/irq-loongarch-ir.c
diff --git a/arch/loongarch/include/asm/cpu-features.h b/arch/loongarch/include/asm/cpu-features.h
index fc83bb32f9f0..03f7e93e81e0 100644
--- a/arch/loongarch/include/asm/cpu-features.h
+++ b/arch/loongarch/include/asm/cpu-features.h
@@ -68,5 +68,6 @@
#define cpu_has_ptw cpu_opt(LOONGARCH_CPU_PTW)
#define cpu_has_lspw cpu_opt(LOONGARCH_CPU_LSPW)
#define cpu_has_avecint cpu_opt(LOONGARCH_CPU_AVECINT)
+#define cpu_has_redirectint cpu_opt(LOONGARCH_CPU_REDIRECTINT)
#endif /* __ASM_CPU_FEATURES_H */
diff --git a/arch/loongarch/include/asm/cpu.h b/arch/loongarch/include/asm/cpu.h
index 98cf4d7b4b0a..33cd96e569d8 100644
--- a/arch/loongarch/include/asm/cpu.h
+++ b/arch/loongarch/include/asm/cpu.h
@@ -102,6 +102,7 @@ enum cpu_type_enum {
#define CPU_FEATURE_PTW 27 /* CPU has hardware page table walker */
#define CPU_FEATURE_LSPW 28 /* CPU has LSPW (lddir/ldpte instructions) */
#define CPU_FEATURE_AVECINT 29 /* CPU has AVEC interrupt */
+#define CPU_FEATURE_REDIRECTINT 30 /* CPU has interrupt remmap */
#define LOONGARCH_CPU_CPUCFG BIT_ULL(CPU_FEATURE_CPUCFG)
#define LOONGARCH_CPU_LAM BIT_ULL(CPU_FEATURE_LAM)
@@ -133,5 +134,6 @@ enum cpu_type_enum {
#define LOONGARCH_CPU_PTW BIT_ULL(CPU_FEATURE_PTW)
#define LOONGARCH_CPU_LSPW BIT_ULL(CPU_FEATURE_LSPW)
#define LOONGARCH_CPU_AVECINT BIT_ULL(CPU_FEATURE_AVECINT)
+#define LOONGARCH_CPU_REDIRECTINT BIT_ULL(CPU_FEATURE_REDIRECTINT)
#endif /* _ASM_CPU_H */
diff --git a/arch/loongarch/include/asm/loongarch.h b/arch/loongarch/include/asm/loongarch.h
index 52651aa0e583..95e06cb6831e 100644
--- a/arch/loongarch/include/asm/loongarch.h
+++ b/arch/loongarch/include/asm/loongarch.h
@@ -1130,6 +1130,7 @@
#define IOCSRF_FLATMODE BIT_ULL(10)
#define IOCSRF_VM BIT_ULL(11)
#define IOCSRF_AVEC BIT_ULL(15)
+#define IOCSRF_REDIRECTINT BIT_ULL(16)
#define LOONGARCH_IOCSR_VENDOR 0x10
@@ -1189,6 +1190,11 @@
#define LOONGARCH_IOCSR_EXTIOI_NODEMAP_BASE 0x14a0
#define LOONGARCH_IOCSR_EXTIOI_IPMAP_BASE 0x14c0
+#define LOONGARCH_IOCSR_REDIRECT_CFG 0x15e0
+#define LOONGARCH_IOCSR_REDIRECT_TBR 0x15e8 /* IRT BASE REG*/
+#define LOONGARCH_IOCSR_REDIRECT_CQB 0x15f0 /* IRT CACHE QUEUE BASE */
+#define LOONGARCH_IOCSR_REDIRECT_CQH 0x15f8 /* IRT CACHE QUEUE HEAD, 32bit */
+#define LOONGARCH_IOCSR_REDIRECT_CQT 0x15fc /* IRT CACHE QUEUE TAIL, 32bit */
#define LOONGARCH_IOCSR_EXTIOI_EN_BASE 0x1600
#define LOONGARCH_IOCSR_EXTIOI_BOUNCE_BASE 0x1680
#define LOONGARCH_IOCSR_EXTIOI_ISR_BASE 0x1800
diff --git a/arch/loongarch/kernel/cpu-probe.c b/arch/loongarch/kernel/cpu-probe.c
index fedaa67cde41..89a41c9ba0c0 100644
--- a/arch/loongarch/kernel/cpu-probe.c
+++ b/arch/loongarch/kernel/cpu-probe.c
@@ -289,6 +289,9 @@ static inline void cpu_probe_loongson(struct cpuinfo_loongarch *c, unsigned int
c->options |= LOONGARCH_CPU_EIODECODE;
if (config & IOCSRF_AVEC)
c->options |= LOONGARCH_CPU_AVECINT;
+ if (config & IOCSRF_REDIRECTINT)
+ c->options |= LOONGARCH_CPU_REDIRECTINT;
+ c->options |= LOONGARCH_CPU_REDIRECTINT;
if (config & IOCSRF_VM)
c->options |= LOONGARCH_CPU_HYPERVISOR;
}
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 25e9ad29b8c4..5dd7d6b151d9 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -113,7 +113,7 @@ obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o
obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o
obj-$(CONFIG_TI_SCI_INTA_IRQCHIP) += irq-ti-sci-inta.o
obj-$(CONFIG_TI_PRUSS_INTC) += irq-pruss-intc.o
-obj-$(CONFIG_IRQ_LOONGARCH_CPU) += irq-loongarch-cpu.o irq-loongarch-avec.o
+obj-$(CONFIG_IRQ_LOONGARCH_CPU) += irq-loongarch-cpu.o irq-loongarch-avec.o irq-loongarch-ir.o
obj-$(CONFIG_LOONGSON_LIOINTC) += irq-loongson-liointc.o
obj-$(CONFIG_LOONGSON_EIOINTC) += irq-loongson-eiointc.o
obj-$(CONFIG_LOONGSON_HTPIC) += irq-loongson-htpic.o
diff --git a/drivers/irqchip/irq-loongarch-avec.c b/drivers/irqchip/irq-loongarch-avec.c
index 80e55955a29f..a2a8c819cfb1 100644
--- a/drivers/irqchip/irq-loongarch-avec.c
+++ b/drivers/irqchip/irq-loongarch-avec.c
@@ -24,7 +24,6 @@
#define VECTORS_PER_REG 64
#define IRR_VECTOR_MASK 0xffUL
#define IRR_INVALID_MASK 0x80000000UL
-#define AVEC_MSG_OFFSET 0x100000
#ifdef CONFIG_SMP
struct pending_list {
@@ -47,15 +46,6 @@ struct avecintc_chip {
static struct avecintc_chip loongarch_avec;
-struct avecintc_data {
- struct list_head entry;
- unsigned int cpu;
- unsigned int vec;
- unsigned int prev_cpu;
- unsigned int prev_vec;
- unsigned int moving;
-};
-
static inline void avecintc_enable(void)
{
u64 value;
@@ -85,7 +75,7 @@ static inline void pending_list_init(int cpu)
INIT_LIST_HEAD(&plist->head);
}
-static void avecintc_sync(struct avecintc_data *adata)
+void avecintc_sync(struct avecintc_data *adata)
{
struct pending_list *plist;
@@ -109,7 +99,7 @@ static int avecintc_set_affinity(struct irq_data *data, const struct cpumask *de
return -EBUSY;
if (cpu_online(adata->cpu) && cpumask_test_cpu(adata->cpu, dest))
- return 0;
+ return IRQ_SET_MASK_OK_DONE;
cpumask_and(&intersect_mask, dest, cpu_online_mask);
@@ -121,7 +111,8 @@ static int avecintc_set_affinity(struct irq_data *data, const struct cpumask *de
adata->cpu = cpu;
adata->vec = vector;
per_cpu_ptr(irq_map, adata->cpu)[adata->vec] = irq_data_to_desc(data);
- avecintc_sync(adata);
+ if (!cpu_has_redirectint)
+ avecintc_sync(adata);
}
irq_data_update_effective_affinity(data, cpumask_of(cpu));
@@ -242,6 +233,7 @@ static void avecintc_irq_dispatch(struct irq_desc *desc)
d = this_cpu_read(irq_map[vector]);
if (d) {
generic_handle_irq_desc(d);
+
} else {
spurious_interrupt();
pr_warn("Unexpected IRQ occurs on CPU#%d [vector %ld]\n", smp_processor_id(), vector);
@@ -412,6 +404,9 @@ static int __init pch_msi_parse_madt(union acpi_subtable_headers *header,
static inline int __init acpi_cascade_irqdomain_init(void)
{
+ if (cpu_has_redirectint)
+ return redirect_acpi_init(loongarch_avec.domain);
+
return acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, pch_msi_parse_madt, 1);
}
diff --git a/drivers/irqchip/irq-loongarch-ir.c b/drivers/irqchip/irq-loongarch-ir.c
new file mode 100644
index 000000000000..9e1cbbb72e29
--- /dev/null
+++ b/drivers/irqchip/irq-loongarch-ir.c
@@ -0,0 +1,561 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Loongson Technologies, Inc.
+ */
+
+#include <linux/cpuhotplug.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <linux/spinlock.h>
+#include <linux/msi.h>
+
+#include <asm/irq.h>
+#include <asm/loongarch.h>
+#include <asm/setup.h>
+#include <larchintrin.h>
+
+#include "irq-loongson.h"
+#include "irq-msi-lib.h"
+
+#define IRD_ENTRIES 65536
+
+/* redirect entry size 128bits */
+#define IRD_PAGE_ORDER (20 - PAGE_SHIFT)
+
+/* irt cache invalid queue */
+#define INVALID_QUEUE_SIZE 4096
+
+#define INVALID_QUEUE_PAGE_ORDER (16 - PAGE_SHIFT)
+
+#define GPID_ADDR_MASK 0x3ffffffffffULL
+#define GPID_ADDR_SHIFT 6
+
+#define CQB_SIZE_SHIFT 0
+#define CQB_SIZE_MASK 0xf
+#define CQB_ADDR_SHIFT 12
+#define CQB_ADDR_MASK (0xfffffffffULL)
+
+#define CFG_DISABLE_IDLE 2
+#define INVALID_INDEX 0
+
+#define MAX_IR_ENGINES 16
+
+struct irq_domain *redirect_domain;
+
+struct redirect_entry {
+ struct {
+ __u64 valid : 1,
+ res1 : 5,
+ gpid : 42,
+ res2 : 8,
+ vector : 8;
+ } lo;
+ __u64 hi;
+};
+
+struct redirect_gpid {
+ u64 pir[4]; /* Pending interrupt requested */
+ u8 en : 1, /* doorbell */
+ res0 : 7;
+ u8 irqnum;
+ u16 res1;
+ u32 dst;
+ u32 rsvd[6];
+} __aligned(64);
+
+struct redirect_table {
+ int node;
+ struct redirect_entry *table;
+ unsigned long *bitmap;
+ unsigned int nr_ird;
+ struct page *page;
+ raw_spinlock_t lock;
+};
+
+struct redirect_item {
+ int index;
+ struct redirect_entry *entry;
+ struct redirect_gpid *gpid;
+ struct redirect_table *table;
+};
+
+struct redirect_queue {
+ int node;
+ u64 base;
+ u32 max_size;
+ int head;
+ int tail;
+ struct page *page;
+ raw_spinlock_t lock;
+};
+
+
+struct irde_desc {
+ struct redirect_table ird_table;
+ struct redirect_queue inv_queue;
+};
+
+struct irde_inv_cmd {
+ union {
+ __u64 cmd_info;
+ struct {
+ __u64 res1 : 4,
+ type : 1,
+ need_notice : 1,
+ pad : 2,
+ index : 16,
+ pad2 : 40;
+ } index;
+ };
+ __u64 notice_addr;
+};
+
+struct smp_invalid_arg {
+ struct redirect_queue *queue;
+ struct irde_inv_cmd *cmd;
+};
+
+static struct irde_desc irde_descs[MAX_IR_ENGINES];
+static phys_addr_t msi_base_addr;
+
+static inline bool invalid_queue_is_full(int node_id, u32 *tail)
+{
+ u32 head;
+
+ head = iocsr_read32(LOONGARCH_IOCSR_REDIRECT_CQH);
+ *tail = iocsr_read32(LOONGARCH_IOCSR_REDIRECT_CQT);
+
+ return !!(head == ((*tail + 1) % INVALID_QUEUE_SIZE));
+}
+
+static void invalid_enqueue(struct redirect_queue *rqueue, struct irde_inv_cmd *cmd)
+{
+ struct irde_inv_cmd *inv_addr;
+ u32 tail;
+
+ guard(raw_spinlock_irqsave)(&rqueue->lock);
+
+ while (invalid_queue_is_full(rqueue->node, &tail))
+ cpu_relax();
+
+ inv_addr = (struct irde_inv_cmd *)(rqueue->base + tail * sizeof(struct irde_inv_cmd));
+ memcpy(inv_addr, cmd, sizeof(struct irde_inv_cmd));
+ tail = (tail + 1) % INVALID_QUEUE_SIZE;
+
+ wmb();
+
+ iocsr_write32(tail, LOONGARCH_IOCSR_REDIRECT_CQT);
+}
+
+static void smp_call_invalid_enqueue(void *arg)
+{
+ struct smp_invalid_arg *s_arg = (struct smp_invalid_arg *)arg;
+
+ invalid_enqueue(s_arg->queue, s_arg->cmd);
+}
+
+static void irde_invlid_entry_node(struct redirect_item *item)
+{
+ struct redirect_queue *rqueue;
+ struct smp_invalid_arg arg;
+ struct irde_inv_cmd cmd;
+ volatile u64 raddr = 0;
+ int node = item->table->node, cpu;
+
+ rqueue = &(irde_descs[node].inv_queue);
+ cmd.cmd_info = 0;
+ cmd.index.type = INVALID_INDEX;
+ cmd.index.need_notice = 1;
+ cmd.index.index = item->index;
+ cmd.notice_addr = (u64)(__pa(&raddr));
+
+ if (cpu_to_node(smp_processor_id()) == node)
+ invalid_enqueue(rqueue, &cmd);
+ else {
+ for_each_cpu(cpu, cpumask_of_node(node)) {
+ if (cpu_online(cpu))
+ break;
+ }
+ arg.queue = rqueue;
+ arg.cmd = &cmd;
+ smp_call_function_single(cpu, smp_call_invalid_enqueue, &arg, 0);
+ }
+
+ while (!raddr)
+ cpu_relax();
+
+}
+
+static inline struct avecintc_data *irq_data_get_avec_data(struct irq_data *data)
+{
+ return data->parent_data->chip_data;
+}
+
+static int redirect_table_alloc(struct redirect_item *item, struct redirect_table *ird_table)
+{
+ int index;
+
+ guard(raw_spinlock_irqsave)(&ird_table->lock);
+
+ index = find_first_zero_bit(ird_table->bitmap, IRD_ENTRIES);
+ if (index > IRD_ENTRIES) {
+ pr_err("No redirect entry to use\n");
+ return -ENOMEM;
+ }
+
+ __set_bit(index, ird_table->bitmap);
+
+ item->index = index;
+ item->entry = &ird_table->table[index];
+ item->table = ird_table;
+
+ return 0;
+}
+
+static int redirect_table_free(struct redirect_item *item)
+{
+ struct redirect_table *ird_table;
+ struct redirect_entry *entry;
+ unsigned long flags;
+
+ ird_table = item->table;
+
+ entry = item->entry;
+ memset(entry, 0, sizeof(struct redirect_entry));
+
+ raw_spin_lock_irqsave(&ird_table->lock, flags);
+ bitmap_release_region(ird_table->bitmap, item->index, 0);
+ raw_spin_unlock_irqrestore(&ird_table->lock, flags);
+
+ kfree(item->gpid);
+
+ irde_invlid_entry_node(item);
+
+ return 0;
+}
+
+static inline void redirect_domain_prepare_entry(struct redirect_item *item, struct avecintc_data *adata)
+{
+ struct redirect_entry *entry = item->entry;
+
+ item->gpid->en = 1;
+ item->gpid->irqnum = adata->vec;
+ item->gpid->dst = adata->cpu;
+
+ entry->lo.valid = 1;
+ entry->lo.gpid = ((long)item->gpid >> GPID_ADDR_SHIFT) & (GPID_ADDR_MASK);
+ entry->lo.vector = 0xff;
+ wmb();
+}
+
+static int redirect_set_affinity(struct irq_data *data, const struct cpumask *dest, bool force)
+{
+ struct redirect_item *item = data->chip_data;
+ struct avecintc_data *adata;
+ int ret;
+
+ ret = irq_chip_set_affinity_parent(data, dest, force);
+ if (ret == IRQ_SET_MASK_OK_DONE)
+ return IRQ_SET_MASK_OK;
+ else if (ret) {
+ pr_err("IRDE:set_affinity error %d\n", ret);
+ return ret;
+ }
+
+ adata = irq_data_get_avec_data(data);
+
+ redirect_domain_prepare_entry(item, adata);
+
+ irde_invlid_entry_node(item);
+
+ avecintc_sync(adata);
+ return IRQ_SET_MASK_OK;
+}
+
+static void redirect_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)
+{
+ struct redirect_item *item;
+
+ item = irq_data_get_irq_chip_data(d);
+ msg->address_lo = (msi_base_addr | 1 << 2 | ((item->index & 0xffff) << 4));
+ msg->address_hi = 0x0;
+ msg->data = 0x0;
+}
+
+static inline void redirect_ack_irq(struct irq_data *d)
+{
+}
+
+static inline void redirect_unmask_irq(struct irq_data *d)
+{
+}
+
+static inline void redirect_mask_irq(struct irq_data *d)
+{
+}
+
+static struct irq_chip loongarch_redirect_chip = {
+ .name = "REDIRECT",
+ .irq_ack = redirect_ack_irq,
+ .irq_mask = redirect_mask_irq,
+ .irq_unmask = redirect_unmask_irq,
+ .irq_set_affinity = redirect_set_affinity,
+ .irq_compose_msi_msg = redirect_compose_msi_msg,
+};
+
+static void redirect_free_resources(struct irq_domain *domain,
+ unsigned int virq, unsigned int nr_irqs)
+{
+ struct irq_data *irq_data;
+ struct redirect_item *item;
+
+ for (int i = 0; i < nr_irqs; i++) {
+ irq_data = irq_domain_get_irq_data(domain, virq + i);
+ if (irq_data && irq_data->chip_data) {
+ item = irq_data->chip_data;
+ redirect_table_free(item);
+ kfree(item);
+ }
+ }
+}
+
+static int redirect_alloc(struct irq_domain *domain,
+ unsigned int virq, unsigned int nr_irqs,
+ void *arg)
+{
+ struct redirect_table *ird_table;
+ struct avecintc_data *avec_data;
+ struct irq_data *irq_data;
+ int ret, i, node;
+
+#ifdef CONFIG_NUMA
+ node = ((msi_alloc_info_t *)arg)->desc->dev->numa_node;
+#else
+ node = 0;
+#endif
+ ird_table = &irde_descs[node].ird_table;
+ ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < nr_irqs; i++) {
+ struct redirect_item *item;
+
+ item = kzalloc(sizeof(struct redirect_item), GFP_KERNEL);
+ if (!item) {
+ pr_err("Alloc redirect descriptor failed\n");
+ goto out_free_resources;
+ }
+
+ irq_data = irq_domain_get_irq_data(domain, virq + i);
+
+ avec_data = irq_data_get_avec_data(irq_data);
+ ret = redirect_table_alloc(item, ird_table);
+ if (ret) {
+ pr_err("Alloc redirect table entry failed\n");
+ goto out_free_resources;
+ }
+
+ item->gpid = kzalloc_node(sizeof(struct redirect_gpid), GFP_KERNEL, node);
+ if (!item->gpid) {
+ pr_err("Alloc redirect GPID failed\n");
+ goto out_free_resources;
+ }
+
+ irq_data->chip_data = item;
+ irq_data->chip = &loongarch_redirect_chip;
+ redirect_domain_prepare_entry(item, avec_data);
+ }
+ return 0;
+
+out_free_resources:
+ redirect_free_resources(domain, virq, nr_irqs);
+ irq_domain_free_irqs_common(domain, virq, nr_irqs);
+
+ return -EINVAL;
+}
+
+static void redirect_free(struct irq_domain *domain,
+ unsigned int virq, unsigned int nr_irqs)
+{
+ redirect_free_resources(domain, virq, nr_irqs);
+ return irq_domain_free_irqs_common(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops redirect_domain_ops = {
+ .alloc = redirect_alloc,
+ .free = redirect_free,
+ .select = msi_lib_irq_domain_select,
+};
+
+static int redirect_queue_init(int node)
+{
+ struct redirect_queue *rqueue = &(irde_descs[node].inv_queue);
+ struct page *pages;
+
+ pages = alloc_pages_node(0, GFP_KERNEL | __GFP_ZERO, INVALID_QUEUE_PAGE_ORDER);
+ if (!pages) {
+ pr_err("Node [%d] Invalid Queue alloc pages failed!\n", node);
+ return -ENOMEM;
+ }
+
+ rqueue->page = pages;
+ rqueue->base = (u64)page_address(pages);
+ rqueue->max_size = INVALID_QUEUE_SIZE;
+ rqueue->head = 0;
+ rqueue->tail = 0;
+ rqueue->node = node;
+ raw_spin_lock_init(&rqueue->lock);
+
+ iocsr_write32(0, LOONGARCH_IOCSR_REDIRECT_CQH);
+ iocsr_write32(0, LOONGARCH_IOCSR_REDIRECT_CQT);
+ iocsr_write64(((rqueue->base & (CQB_ADDR_MASK << CQB_ADDR_SHIFT)) |
+ (CQB_SIZE_MASK << CQB_SIZE_SHIFT)), LOONGARCH_IOCSR_REDIRECT_CQB);
+ return 0;
+}
+
+static int redirect_table_init(int node)
+{
+ struct redirect_table *ird_table = &(irde_descs[node].ird_table);
+ struct page *pages;
+ unsigned long *bitmap;
+
+ pages = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, IRD_PAGE_ORDER);
+ if (!pages) {
+ pr_err("Node [%d] redirect table alloc pages failed!\n", node);
+ return -ENOMEM;
+ }
+ ird_table->page = pages;
+ ird_table->table = page_address(pages);
+
+ bitmap = bitmap_zalloc(IRD_ENTRIES, GFP_KERNEL);
+ if (!bitmap) {
+ pr_err("Node [%d] redirect table bitmap alloc pages failed!\n", node);
+ return -ENOMEM;
+ }
+
+ ird_table->bitmap = bitmap;
+ ird_table->nr_ird = IRD_ENTRIES;
+ ird_table->node = node;
+
+ raw_spin_lock_init(&ird_table->lock);
+
+ if (redirect_queue_init(node))
+ return -EINVAL;
+
+ iocsr_write64(CFG_DISABLE_IDLE, LOONGARCH_IOCSR_REDIRECT_CFG);
+ iocsr_write64(__pa(ird_table->table), LOONGARCH_IOCSR_REDIRECT_TBR);
+
+ return 0;
+}
+
+static void redirect_table_fini(int node)
+{
+ struct redirect_table *ird_table = &(irde_descs[node].ird_table);
+ struct redirect_queue *rqueue = &(irde_descs[node].inv_queue);
+
+ if (ird_table->page) {
+ __free_pages(ird_table->page, IRD_PAGE_ORDER);
+ ird_table->table = NULL;
+ ird_table->page = NULL;
+ }
+
+ if (ird_table->page) {
+ bitmap_free(ird_table->bitmap);
+ ird_table->bitmap = NULL;
+ }
+
+ if (rqueue->page) {
+ __free_pages(rqueue->page, INVALID_QUEUE_PAGE_ORDER);
+ rqueue->page = NULL;
+ rqueue->base = 0;
+ }
+
+ iocsr_write64(0, LOONGARCH_IOCSR_REDIRECT_CQB);
+ iocsr_write64(0, LOONGARCH_IOCSR_REDIRECT_TBR);
+}
+
+static int redirect_cpu_online(unsigned int cpu)
+{
+ int ret, node = cpu_to_node(cpu);
+
+ if (cpu != cpumask_first(cpumask_of_node(node)))
+ return 0;
+
+ ret = redirect_table_init(node);
+ if (ret) {
+ redirect_table_fini(node);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#if defined(CONFIG_ACPI)
+static int __init pch_msi_parse_madt(union acpi_subtable_headers *header,
+ const unsigned long end)
+{
+ struct acpi_madt_msi_pic *pchmsi_entry = (struct acpi_madt_msi_pic *)header;
+
+ msi_base_addr = pchmsi_entry->msg_address - AVEC_MSG_OFFSET;
+
+ return pch_msi_acpi_init_avec(redirect_domain);
+}
+
+static int __init acpi_cascade_irqdomain_init(void)
+{
+ return acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, pch_msi_parse_madt, 1);
+}
+
+int __init redirect_acpi_init(struct irq_domain *parent)
+{
+ struct fwnode_handle *fwnode;
+ struct irq_domain *domain;
+ int ret;
+
+ fwnode = irq_domain_alloc_named_fwnode("redirect");
+ if (!fwnode) {
+ pr_err("Unable to alloc redirect domain handle\n");
+ goto fail;
+ }
+
+ domain = irq_domain_create_hierarchy(parent, 0, IRD_ENTRIES, fwnode,
+ &redirect_domain_ops, irde_descs);
+ if (!domain) {
+ pr_err("Unable to alloc redirect domain\n");
+ goto out_free_fwnode;
+ }
+
+ redirect_domain = domain;
+
+ ret = redirect_table_init(0);
+ if (ret)
+ goto out_free_table;
+
+ ret = acpi_cascade_irqdomain_init();
+ if (ret < 0) {
+ pr_err("Failed to cascade IRQ domain, ret=%d\n", ret);
+ goto out_free_table;
+ }
+
+ cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_REDIRECT_STARTING,
+ "irqchip/loongarch/redirect:starting",
+ redirect_cpu_online, NULL);
+
+ pr_info("loongarch irq redirect modules init succeeded\n");
+ return 0;
+
+out_free_table:
+ redirect_table_fini(0);
+ irq_domain_remove(redirect_domain);
+ redirect_domain = NULL;
+out_free_fwnode:
+ irq_domain_free_fwnode(fwnode);
+fail:
+ return -EINVAL;
+}
+#endif
diff --git a/drivers/irqchip/irq-loongson.h b/drivers/irqchip/irq-loongson.h
index 11fa138d1f44..05ad40ffb62b 100644
--- a/drivers/irqchip/irq-loongson.h
+++ b/drivers/irqchip/irq-loongson.h
@@ -5,6 +5,15 @@
#ifndef _DRIVERS_IRQCHIP_IRQ_LOONGSON_H
#define _DRIVERS_IRQCHIP_IRQ_LOONGSON_H
+#define AVEC_MSG_OFFSET 0x100000
+struct avecintc_data {
+ struct list_head entry;
+ unsigned int cpu;
+ unsigned int vec;
+ unsigned int prev_cpu;
+ unsigned int prev_vec;
+ unsigned int moving;
+};
int find_pch_pic(u32 gsi);
@@ -24,4 +33,7 @@ int pch_msi_acpi_init(struct irq_domain *parent,
struct acpi_madt_msi_pic *acpi_pchmsi);
int pch_msi_acpi_init_avec(struct irq_domain *parent);
+int redirect_acpi_init(struct irq_domain *parent);
+
+void avecintc_sync(struct avecintc_data *adata);
#endif /* _DRIVERS_IRQCHIP_IRQ_LOONGSON_H */
diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
index 6cc5e484547c..2fd5531fa378 100644
--- a/include/linux/cpuhotplug.h
+++ b/include/linux/cpuhotplug.h
@@ -146,6 +146,7 @@ enum cpuhp_state {
CPUHP_AP_IRQ_MIPS_GIC_STARTING,
CPUHP_AP_IRQ_EIOINTC_STARTING,
CPUHP_AP_IRQ_AVECINTC_STARTING,
+ CPUHP_AP_IRQ_REDIRECT_STARTING,
CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING,
CPUHP_AP_IRQ_THEAD_ACLINT_SSWI_STARTING,
CPUHP_AP_IRQ_RISCV_IMSIC_STARTING,
--
2.43.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH 2/2] irq/irq-loongarch-ir:Add Redirect irqchip support
2025-03-17 8:22 ` [PATCH 2/2] irq/irq-loongarch-ir:Add Redirect irqchip support Tianyang Zhang
@ 2025-03-19 5:55 ` Tianyang Zhang
0 siblings, 0 replies; 13+ messages in thread
From: Tianyang Zhang @ 2025-03-19 5:55 UTC (permalink / raw)
To: chenhuacai, kernel, corbet, alexs, si.yanteng, tglx, jiaxun.yang,
peterz, wangliupu, lvjianmin, maobibo, siyanteng, gaosong,
yangtiezhu
Cc: loongarch, linux-doc, linux-kernel
Hi, Everyone
We've got feedback on issues from this patch. Please disregard v1. I'll
test and push v2 soon.
在 2025/3/17 下午4:22, Tianyang Zhang 写道:
> The main function of the Redirected interrupt controller is to manage the
> redirected-interrupt table, which consists of many redirected entries.
> When MSI interrupts are requested, the driver creates a corresponding
> redirected entry that describes the target CPU/vector number and the
> operating mode of the interrupt. The redirected interrupt module has an
> independent cache, and during the interrupt routing process, it will
> prioritize the redirected entries that hit the cache. The driver
> invalidates certain entry caches via a command queue.
>
> Co-developed-by: Liupu Wang <wangliupu@loongson.cn>
> Signed-off-by: Liupu Wang <wangliupu@loongson.cn>
> Signed-off-by: Tianyang Zhang <zhangtianyang@loongson.cn>
> ---
> arch/loongarch/include/asm/cpu-features.h | 1 +
> arch/loongarch/include/asm/cpu.h | 2 +
> arch/loongarch/include/asm/loongarch.h | 6 +
> arch/loongarch/kernel/cpu-probe.c | 3 +
> drivers/irqchip/Makefile | 2 +-
> drivers/irqchip/irq-loongarch-avec.c | 21 +-
> drivers/irqchip/irq-loongarch-ir.c | 561 ++++++++++++++++++++++
> drivers/irqchip/irq-loongson.h | 12 +
> include/linux/cpuhotplug.h | 1 +
> 9 files changed, 595 insertions(+), 14 deletions(-)
> create mode 100644 drivers/irqchip/irq-loongarch-ir.c
>
> diff --git a/arch/loongarch/include/asm/cpu-features.h b/arch/loongarch/include/asm/cpu-features.h
> index fc83bb32f9f0..03f7e93e81e0 100644
> --- a/arch/loongarch/include/asm/cpu-features.h
> +++ b/arch/loongarch/include/asm/cpu-features.h
> @@ -68,5 +68,6 @@
> #define cpu_has_ptw cpu_opt(LOONGARCH_CPU_PTW)
> #define cpu_has_lspw cpu_opt(LOONGARCH_CPU_LSPW)
> #define cpu_has_avecint cpu_opt(LOONGARCH_CPU_AVECINT)
> +#define cpu_has_redirectint cpu_opt(LOONGARCH_CPU_REDIRECTINT)
>
> #endif /* __ASM_CPU_FEATURES_H */
> diff --git a/arch/loongarch/include/asm/cpu.h b/arch/loongarch/include/asm/cpu.h
> index 98cf4d7b4b0a..33cd96e569d8 100644
> --- a/arch/loongarch/include/asm/cpu.h
> +++ b/arch/loongarch/include/asm/cpu.h
> @@ -102,6 +102,7 @@ enum cpu_type_enum {
> #define CPU_FEATURE_PTW 27 /* CPU has hardware page table walker */
> #define CPU_FEATURE_LSPW 28 /* CPU has LSPW (lddir/ldpte instructions) */
> #define CPU_FEATURE_AVECINT 29 /* CPU has AVEC interrupt */
> +#define CPU_FEATURE_REDIRECTINT 30 /* CPU has interrupt remmap */
>
> #define LOONGARCH_CPU_CPUCFG BIT_ULL(CPU_FEATURE_CPUCFG)
> #define LOONGARCH_CPU_LAM BIT_ULL(CPU_FEATURE_LAM)
> @@ -133,5 +134,6 @@ enum cpu_type_enum {
> #define LOONGARCH_CPU_PTW BIT_ULL(CPU_FEATURE_PTW)
> #define LOONGARCH_CPU_LSPW BIT_ULL(CPU_FEATURE_LSPW)
> #define LOONGARCH_CPU_AVECINT BIT_ULL(CPU_FEATURE_AVECINT)
> +#define LOONGARCH_CPU_REDIRECTINT BIT_ULL(CPU_FEATURE_REDIRECTINT)
>
> #endif /* _ASM_CPU_H */
> diff --git a/arch/loongarch/include/asm/loongarch.h b/arch/loongarch/include/asm/loongarch.h
> index 52651aa0e583..95e06cb6831e 100644
> --- a/arch/loongarch/include/asm/loongarch.h
> +++ b/arch/loongarch/include/asm/loongarch.h
> @@ -1130,6 +1130,7 @@
> #define IOCSRF_FLATMODE BIT_ULL(10)
> #define IOCSRF_VM BIT_ULL(11)
> #define IOCSRF_AVEC BIT_ULL(15)
> +#define IOCSRF_REDIRECTINT BIT_ULL(16)
>
> #define LOONGARCH_IOCSR_VENDOR 0x10
>
> @@ -1189,6 +1190,11 @@
>
> #define LOONGARCH_IOCSR_EXTIOI_NODEMAP_BASE 0x14a0
> #define LOONGARCH_IOCSR_EXTIOI_IPMAP_BASE 0x14c0
> +#define LOONGARCH_IOCSR_REDIRECT_CFG 0x15e0
> +#define LOONGARCH_IOCSR_REDIRECT_TBR 0x15e8 /* IRT BASE REG*/
> +#define LOONGARCH_IOCSR_REDIRECT_CQB 0x15f0 /* IRT CACHE QUEUE BASE */
> +#define LOONGARCH_IOCSR_REDIRECT_CQH 0x15f8 /* IRT CACHE QUEUE HEAD, 32bit */
> +#define LOONGARCH_IOCSR_REDIRECT_CQT 0x15fc /* IRT CACHE QUEUE TAIL, 32bit */
> #define LOONGARCH_IOCSR_EXTIOI_EN_BASE 0x1600
> #define LOONGARCH_IOCSR_EXTIOI_BOUNCE_BASE 0x1680
> #define LOONGARCH_IOCSR_EXTIOI_ISR_BASE 0x1800
> diff --git a/arch/loongarch/kernel/cpu-probe.c b/arch/loongarch/kernel/cpu-probe.c
> index fedaa67cde41..89a41c9ba0c0 100644
> --- a/arch/loongarch/kernel/cpu-probe.c
> +++ b/arch/loongarch/kernel/cpu-probe.c
> @@ -289,6 +289,9 @@ static inline void cpu_probe_loongson(struct cpuinfo_loongarch *c, unsigned int
> c->options |= LOONGARCH_CPU_EIODECODE;
> if (config & IOCSRF_AVEC)
> c->options |= LOONGARCH_CPU_AVECINT;
> + if (config & IOCSRF_REDIRECTINT)
> + c->options |= LOONGARCH_CPU_REDIRECTINT;
> + c->options |= LOONGARCH_CPU_REDIRECTINT;
> if (config & IOCSRF_VM)
> c->options |= LOONGARCH_CPU_HYPERVISOR;
> }
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index 25e9ad29b8c4..5dd7d6b151d9 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -113,7 +113,7 @@ obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o
> obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o
> obj-$(CONFIG_TI_SCI_INTA_IRQCHIP) += irq-ti-sci-inta.o
> obj-$(CONFIG_TI_PRUSS_INTC) += irq-pruss-intc.o
> -obj-$(CONFIG_IRQ_LOONGARCH_CPU) += irq-loongarch-cpu.o irq-loongarch-avec.o
> +obj-$(CONFIG_IRQ_LOONGARCH_CPU) += irq-loongarch-cpu.o irq-loongarch-avec.o irq-loongarch-ir.o
> obj-$(CONFIG_LOONGSON_LIOINTC) += irq-loongson-liointc.o
> obj-$(CONFIG_LOONGSON_EIOINTC) += irq-loongson-eiointc.o
> obj-$(CONFIG_LOONGSON_HTPIC) += irq-loongson-htpic.o
> diff --git a/drivers/irqchip/irq-loongarch-avec.c b/drivers/irqchip/irq-loongarch-avec.c
> index 80e55955a29f..a2a8c819cfb1 100644
> --- a/drivers/irqchip/irq-loongarch-avec.c
> +++ b/drivers/irqchip/irq-loongarch-avec.c
> @@ -24,7 +24,6 @@
> #define VECTORS_PER_REG 64
> #define IRR_VECTOR_MASK 0xffUL
> #define IRR_INVALID_MASK 0x80000000UL
> -#define AVEC_MSG_OFFSET 0x100000
>
> #ifdef CONFIG_SMP
> struct pending_list {
> @@ -47,15 +46,6 @@ struct avecintc_chip {
>
> static struct avecintc_chip loongarch_avec;
>
> -struct avecintc_data {
> - struct list_head entry;
> - unsigned int cpu;
> - unsigned int vec;
> - unsigned int prev_cpu;
> - unsigned int prev_vec;
> - unsigned int moving;
> -};
> -
> static inline void avecintc_enable(void)
> {
> u64 value;
> @@ -85,7 +75,7 @@ static inline void pending_list_init(int cpu)
> INIT_LIST_HEAD(&plist->head);
> }
>
> -static void avecintc_sync(struct avecintc_data *adata)
> +void avecintc_sync(struct avecintc_data *adata)
> {
> struct pending_list *plist;
>
> @@ -109,7 +99,7 @@ static int avecintc_set_affinity(struct irq_data *data, const struct cpumask *de
> return -EBUSY;
>
> if (cpu_online(adata->cpu) && cpumask_test_cpu(adata->cpu, dest))
> - return 0;
> + return IRQ_SET_MASK_OK_DONE;
>
> cpumask_and(&intersect_mask, dest, cpu_online_mask);
>
> @@ -121,7 +111,8 @@ static int avecintc_set_affinity(struct irq_data *data, const struct cpumask *de
> adata->cpu = cpu;
> adata->vec = vector;
> per_cpu_ptr(irq_map, adata->cpu)[adata->vec] = irq_data_to_desc(data);
> - avecintc_sync(adata);
> + if (!cpu_has_redirectint)
> + avecintc_sync(adata);
> }
>
> irq_data_update_effective_affinity(data, cpumask_of(cpu));
> @@ -242,6 +233,7 @@ static void avecintc_irq_dispatch(struct irq_desc *desc)
> d = this_cpu_read(irq_map[vector]);
> if (d) {
> generic_handle_irq_desc(d);
> +
> } else {
> spurious_interrupt();
> pr_warn("Unexpected IRQ occurs on CPU#%d [vector %ld]\n", smp_processor_id(), vector);
> @@ -412,6 +404,9 @@ static int __init pch_msi_parse_madt(union acpi_subtable_headers *header,
>
> static inline int __init acpi_cascade_irqdomain_init(void)
> {
> + if (cpu_has_redirectint)
> + return redirect_acpi_init(loongarch_avec.domain);
> +
> return acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, pch_msi_parse_madt, 1);
> }
>
> diff --git a/drivers/irqchip/irq-loongarch-ir.c b/drivers/irqchip/irq-loongarch-ir.c
> new file mode 100644
> index 000000000000..9e1cbbb72e29
> --- /dev/null
> +++ b/drivers/irqchip/irq-loongarch-ir.c
> @@ -0,0 +1,561 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2020 Loongson Technologies, Inc.
> + */
> +
> +#include <linux/cpuhotplug.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/irq.h>
> +#include <linux/irqchip.h>
> +#include <linux/irqdomain.h>
> +#include <linux/spinlock.h>
> +#include <linux/msi.h>
> +
> +#include <asm/irq.h>
> +#include <asm/loongarch.h>
> +#include <asm/setup.h>
> +#include <larchintrin.h>
> +
> +#include "irq-loongson.h"
> +#include "irq-msi-lib.h"
> +
> +#define IRD_ENTRIES 65536
> +
> +/* redirect entry size 128bits */
> +#define IRD_PAGE_ORDER (20 - PAGE_SHIFT)
> +
> +/* irt cache invalid queue */
> +#define INVALID_QUEUE_SIZE 4096
> +
> +#define INVALID_QUEUE_PAGE_ORDER (16 - PAGE_SHIFT)
> +
> +#define GPID_ADDR_MASK 0x3ffffffffffULL
> +#define GPID_ADDR_SHIFT 6
> +
> +#define CQB_SIZE_SHIFT 0
> +#define CQB_SIZE_MASK 0xf
> +#define CQB_ADDR_SHIFT 12
> +#define CQB_ADDR_MASK (0xfffffffffULL)
> +
> +#define CFG_DISABLE_IDLE 2
> +#define INVALID_INDEX 0
> +
> +#define MAX_IR_ENGINES 16
> +
> +struct irq_domain *redirect_domain;
> +
> +struct redirect_entry {
> + struct {
> + __u64 valid : 1,
> + res1 : 5,
> + gpid : 42,
> + res2 : 8,
> + vector : 8;
> + } lo;
> + __u64 hi;
> +};
> +
> +struct redirect_gpid {
> + u64 pir[4]; /* Pending interrupt requested */
> + u8 en : 1, /* doorbell */
> + res0 : 7;
> + u8 irqnum;
> + u16 res1;
> + u32 dst;
> + u32 rsvd[6];
> +} __aligned(64);
> +
> +struct redirect_table {
> + int node;
> + struct redirect_entry *table;
> + unsigned long *bitmap;
> + unsigned int nr_ird;
> + struct page *page;
> + raw_spinlock_t lock;
> +};
> +
> +struct redirect_item {
> + int index;
> + struct redirect_entry *entry;
> + struct redirect_gpid *gpid;
> + struct redirect_table *table;
> +};
> +
> +struct redirect_queue {
> + int node;
> + u64 base;
> + u32 max_size;
> + int head;
> + int tail;
> + struct page *page;
> + raw_spinlock_t lock;
> +};
> +
> +
> +struct irde_desc {
> + struct redirect_table ird_table;
> + struct redirect_queue inv_queue;
> +};
> +
> +struct irde_inv_cmd {
> + union {
> + __u64 cmd_info;
> + struct {
> + __u64 res1 : 4,
> + type : 1,
> + need_notice : 1,
> + pad : 2,
> + index : 16,
> + pad2 : 40;
> + } index;
> + };
> + __u64 notice_addr;
> +};
> +
> +struct smp_invalid_arg {
> + struct redirect_queue *queue;
> + struct irde_inv_cmd *cmd;
> +};
> +
> +static struct irde_desc irde_descs[MAX_IR_ENGINES];
> +static phys_addr_t msi_base_addr;
> +
> +static inline bool invalid_queue_is_full(int node_id, u32 *tail)
> +{
> + u32 head;
> +
> + head = iocsr_read32(LOONGARCH_IOCSR_REDIRECT_CQH);
> + *tail = iocsr_read32(LOONGARCH_IOCSR_REDIRECT_CQT);
> +
> + return !!(head == ((*tail + 1) % INVALID_QUEUE_SIZE));
> +}
> +
> +static void invalid_enqueue(struct redirect_queue *rqueue, struct irde_inv_cmd *cmd)
> +{
> + struct irde_inv_cmd *inv_addr;
> + u32 tail;
> +
> + guard(raw_spinlock_irqsave)(&rqueue->lock);
> +
> + while (invalid_queue_is_full(rqueue->node, &tail))
> + cpu_relax();
> +
> + inv_addr = (struct irde_inv_cmd *)(rqueue->base + tail * sizeof(struct irde_inv_cmd));
> + memcpy(inv_addr, cmd, sizeof(struct irde_inv_cmd));
> + tail = (tail + 1) % INVALID_QUEUE_SIZE;
> +
> + wmb();
> +
> + iocsr_write32(tail, LOONGARCH_IOCSR_REDIRECT_CQT);
> +}
> +
> +static void smp_call_invalid_enqueue(void *arg)
> +{
> + struct smp_invalid_arg *s_arg = (struct smp_invalid_arg *)arg;
> +
> + invalid_enqueue(s_arg->queue, s_arg->cmd);
> +}
> +
> +static void irde_invlid_entry_node(struct redirect_item *item)
> +{
> + struct redirect_queue *rqueue;
> + struct smp_invalid_arg arg;
> + struct irde_inv_cmd cmd;
> + volatile u64 raddr = 0;
> + int node = item->table->node, cpu;
> +
> + rqueue = &(irde_descs[node].inv_queue);
> + cmd.cmd_info = 0;
> + cmd.index.type = INVALID_INDEX;
> + cmd.index.need_notice = 1;
> + cmd.index.index = item->index;
> + cmd.notice_addr = (u64)(__pa(&raddr));
> +
> + if (cpu_to_node(smp_processor_id()) == node)
> + invalid_enqueue(rqueue, &cmd);
> + else {
> + for_each_cpu(cpu, cpumask_of_node(node)) {
> + if (cpu_online(cpu))
> + break;
> + }
> + arg.queue = rqueue;
> + arg.cmd = &cmd;
> + smp_call_function_single(cpu, smp_call_invalid_enqueue, &arg, 0);
> + }
> +
> + while (!raddr)
> + cpu_relax();
> +
> +}
> +
> +static inline struct avecintc_data *irq_data_get_avec_data(struct irq_data *data)
> +{
> + return data->parent_data->chip_data;
> +}
> +
> +static int redirect_table_alloc(struct redirect_item *item, struct redirect_table *ird_table)
> +{
> + int index;
> +
> + guard(raw_spinlock_irqsave)(&ird_table->lock);
> +
> + index = find_first_zero_bit(ird_table->bitmap, IRD_ENTRIES);
> + if (index > IRD_ENTRIES) {
> + pr_err("No redirect entry to use\n");
> + return -ENOMEM;
> + }
> +
> + __set_bit(index, ird_table->bitmap);
> +
> + item->index = index;
> + item->entry = &ird_table->table[index];
> + item->table = ird_table;
> +
> + return 0;
> +}
> +
> +static int redirect_table_free(struct redirect_item *item)
> +{
> + struct redirect_table *ird_table;
> + struct redirect_entry *entry;
> + unsigned long flags;
> +
> + ird_table = item->table;
> +
> + entry = item->entry;
> + memset(entry, 0, sizeof(struct redirect_entry));
> +
> + raw_spin_lock_irqsave(&ird_table->lock, flags);
> + bitmap_release_region(ird_table->bitmap, item->index, 0);
> + raw_spin_unlock_irqrestore(&ird_table->lock, flags);
> +
> + kfree(item->gpid);
> +
> + irde_invlid_entry_node(item);
> +
> + return 0;
> +}
> +
> +static inline void redirect_domain_prepare_entry(struct redirect_item *item, struct avecintc_data *adata)
> +{
> + struct redirect_entry *entry = item->entry;
> +
> + item->gpid->en = 1;
> + item->gpid->irqnum = adata->vec;
> + item->gpid->dst = adata->cpu;
> +
> + entry->lo.valid = 1;
> + entry->lo.gpid = ((long)item->gpid >> GPID_ADDR_SHIFT) & (GPID_ADDR_MASK);
> + entry->lo.vector = 0xff;
> + wmb();
> +}
> +
> +static int redirect_set_affinity(struct irq_data *data, const struct cpumask *dest, bool force)
> +{
> + struct redirect_item *item = data->chip_data;
> + struct avecintc_data *adata;
> + int ret;
> +
> + ret = irq_chip_set_affinity_parent(data, dest, force);
> + if (ret == IRQ_SET_MASK_OK_DONE)
> + return IRQ_SET_MASK_OK;
> + else if (ret) {
> + pr_err("IRDE:set_affinity error %d\n", ret);
> + return ret;
> + }
> +
> + adata = irq_data_get_avec_data(data);
> +
> + redirect_domain_prepare_entry(item, adata);
> +
> + irde_invlid_entry_node(item);
> +
> + avecintc_sync(adata);
> + return IRQ_SET_MASK_OK;
> +}
> +
> +static void redirect_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)
> +{
> + struct redirect_item *item;
> +
> + item = irq_data_get_irq_chip_data(d);
> + msg->address_lo = (msi_base_addr | 1 << 2 | ((item->index & 0xffff) << 4));
> + msg->address_hi = 0x0;
> + msg->data = 0x0;
> +}
> +
> +static inline void redirect_ack_irq(struct irq_data *d)
> +{
> +}
> +
> +static inline void redirect_unmask_irq(struct irq_data *d)
> +{
> +}
> +
> +static inline void redirect_mask_irq(struct irq_data *d)
> +{
> +}
> +
> +static struct irq_chip loongarch_redirect_chip = {
> + .name = "REDIRECT",
> + .irq_ack = redirect_ack_irq,
> + .irq_mask = redirect_mask_irq,
> + .irq_unmask = redirect_unmask_irq,
> + .irq_set_affinity = redirect_set_affinity,
> + .irq_compose_msi_msg = redirect_compose_msi_msg,
> +};
> +
> +static void redirect_free_resources(struct irq_domain *domain,
> + unsigned int virq, unsigned int nr_irqs)
> +{
> + struct irq_data *irq_data;
> + struct redirect_item *item;
> +
> + for (int i = 0; i < nr_irqs; i++) {
> + irq_data = irq_domain_get_irq_data(domain, virq + i);
> + if (irq_data && irq_data->chip_data) {
> + item = irq_data->chip_data;
> + redirect_table_free(item);
> + kfree(item);
> + }
> + }
> +}
> +
> +static int redirect_alloc(struct irq_domain *domain,
> + unsigned int virq, unsigned int nr_irqs,
> + void *arg)
> +{
> + struct redirect_table *ird_table;
> + struct avecintc_data *avec_data;
> + struct irq_data *irq_data;
> + int ret, i, node;
> +
> +#ifdef CONFIG_NUMA
> + node = ((msi_alloc_info_t *)arg)->desc->dev->numa_node;
> +#else
> + node = 0;
> +#endif
> + ird_table = &irde_descs[node].ird_table;
> + ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
> + if (ret < 0)
> + return ret;
> +
> + for (i = 0; i < nr_irqs; i++) {
> + struct redirect_item *item;
> +
> + item = kzalloc(sizeof(struct redirect_item), GFP_KERNEL);
> + if (!item) {
> + pr_err("Alloc redirect descriptor failed\n");
> + goto out_free_resources;
> + }
> +
> + irq_data = irq_domain_get_irq_data(domain, virq + i);
> +
> + avec_data = irq_data_get_avec_data(irq_data);
> + ret = redirect_table_alloc(item, ird_table);
> + if (ret) {
> + pr_err("Alloc redirect table entry failed\n");
> + goto out_free_resources;
> + }
> +
> + item->gpid = kzalloc_node(sizeof(struct redirect_gpid), GFP_KERNEL, node);
> + if (!item->gpid) {
> + pr_err("Alloc redirect GPID failed\n");
> + goto out_free_resources;
> + }
> +
> + irq_data->chip_data = item;
> + irq_data->chip = &loongarch_redirect_chip;
> + redirect_domain_prepare_entry(item, avec_data);
> + }
> + return 0;
> +
> +out_free_resources:
> + redirect_free_resources(domain, virq, nr_irqs);
> + irq_domain_free_irqs_common(domain, virq, nr_irqs);
> +
> + return -EINVAL;
> +}
> +
> +static void redirect_free(struct irq_domain *domain,
> + unsigned int virq, unsigned int nr_irqs)
> +{
> + redirect_free_resources(domain, virq, nr_irqs);
> + return irq_domain_free_irqs_common(domain, virq, nr_irqs);
> +}
> +
> +static const struct irq_domain_ops redirect_domain_ops = {
> + .alloc = redirect_alloc,
> + .free = redirect_free,
> + .select = msi_lib_irq_domain_select,
> +};
> +
> +static int redirect_queue_init(int node)
> +{
> + struct redirect_queue *rqueue = &(irde_descs[node].inv_queue);
> + struct page *pages;
> +
> + pages = alloc_pages_node(0, GFP_KERNEL | __GFP_ZERO, INVALID_QUEUE_PAGE_ORDER);
> + if (!pages) {
> + pr_err("Node [%d] Invalid Queue alloc pages failed!\n", node);
> + return -ENOMEM;
> + }
> +
> + rqueue->page = pages;
> + rqueue->base = (u64)page_address(pages);
> + rqueue->max_size = INVALID_QUEUE_SIZE;
> + rqueue->head = 0;
> + rqueue->tail = 0;
> + rqueue->node = node;
> + raw_spin_lock_init(&rqueue->lock);
> +
> + iocsr_write32(0, LOONGARCH_IOCSR_REDIRECT_CQH);
> + iocsr_write32(0, LOONGARCH_IOCSR_REDIRECT_CQT);
> + iocsr_write64(((rqueue->base & (CQB_ADDR_MASK << CQB_ADDR_SHIFT)) |
> + (CQB_SIZE_MASK << CQB_SIZE_SHIFT)), LOONGARCH_IOCSR_REDIRECT_CQB);
> + return 0;
> +}
> +
> +static int redirect_table_init(int node)
> +{
> + struct redirect_table *ird_table = &(irde_descs[node].ird_table);
> + struct page *pages;
> + unsigned long *bitmap;
> +
> + pages = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, IRD_PAGE_ORDER);
> + if (!pages) {
> + pr_err("Node [%d] redirect table alloc pages failed!\n", node);
> + return -ENOMEM;
> + }
> + ird_table->page = pages;
> + ird_table->table = page_address(pages);
> +
> + bitmap = bitmap_zalloc(IRD_ENTRIES, GFP_KERNEL);
> + if (!bitmap) {
> + pr_err("Node [%d] redirect table bitmap alloc pages failed!\n", node);
> + return -ENOMEM;
> + }
> +
> + ird_table->bitmap = bitmap;
> + ird_table->nr_ird = IRD_ENTRIES;
> + ird_table->node = node;
> +
> + raw_spin_lock_init(&ird_table->lock);
> +
> + if (redirect_queue_init(node))
> + return -EINVAL;
> +
> + iocsr_write64(CFG_DISABLE_IDLE, LOONGARCH_IOCSR_REDIRECT_CFG);
> + iocsr_write64(__pa(ird_table->table), LOONGARCH_IOCSR_REDIRECT_TBR);
> +
> + return 0;
> +}
> +
> +static void redirect_table_fini(int node)
> +{
> + struct redirect_table *ird_table = &(irde_descs[node].ird_table);
> + struct redirect_queue *rqueue = &(irde_descs[node].inv_queue);
> +
> + if (ird_table->page) {
> + __free_pages(ird_table->page, IRD_PAGE_ORDER);
> + ird_table->table = NULL;
> + ird_table->page = NULL;
> + }
> +
> + if (ird_table->page) {
> + bitmap_free(ird_table->bitmap);
> + ird_table->bitmap = NULL;
> + }
> +
> + if (rqueue->page) {
> + __free_pages(rqueue->page, INVALID_QUEUE_PAGE_ORDER);
> + rqueue->page = NULL;
> + rqueue->base = 0;
> + }
> +
> + iocsr_write64(0, LOONGARCH_IOCSR_REDIRECT_CQB);
> + iocsr_write64(0, LOONGARCH_IOCSR_REDIRECT_TBR);
> +}
> +
> +static int redirect_cpu_online(unsigned int cpu)
> +{
> + int ret, node = cpu_to_node(cpu);
> +
> + if (cpu != cpumask_first(cpumask_of_node(node)))
> + return 0;
> +
> + ret = redirect_table_init(node);
> + if (ret) {
> + redirect_table_fini(node);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +#if defined(CONFIG_ACPI)
> +static int __init pch_msi_parse_madt(union acpi_subtable_headers *header,
> + const unsigned long end)
> +{
> + struct acpi_madt_msi_pic *pchmsi_entry = (struct acpi_madt_msi_pic *)header;
> +
> + msi_base_addr = pchmsi_entry->msg_address - AVEC_MSG_OFFSET;
> +
> + return pch_msi_acpi_init_avec(redirect_domain);
> +}
> +
> +static int __init acpi_cascade_irqdomain_init(void)
> +{
> + return acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, pch_msi_parse_madt, 1);
> +}
> +
> +int __init redirect_acpi_init(struct irq_domain *parent)
> +{
> + struct fwnode_handle *fwnode;
> + struct irq_domain *domain;
> + int ret;
> +
> + fwnode = irq_domain_alloc_named_fwnode("redirect");
> + if (!fwnode) {
> + pr_err("Unable to alloc redirect domain handle\n");
> + goto fail;
> + }
> +
> + domain = irq_domain_create_hierarchy(parent, 0, IRD_ENTRIES, fwnode,
> + &redirect_domain_ops, irde_descs);
> + if (!domain) {
> + pr_err("Unable to alloc redirect domain\n");
> + goto out_free_fwnode;
> + }
> +
> + redirect_domain = domain;
> +
> + ret = redirect_table_init(0);
> + if (ret)
> + goto out_free_table;
> +
> + ret = acpi_cascade_irqdomain_init();
> + if (ret < 0) {
> + pr_err("Failed to cascade IRQ domain, ret=%d\n", ret);
> + goto out_free_table;
> + }
> +
> + cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_REDIRECT_STARTING,
> + "irqchip/loongarch/redirect:starting",
> + redirect_cpu_online, NULL);
> +
> + pr_info("loongarch irq redirect modules init succeeded\n");
> + return 0;
> +
> +out_free_table:
> + redirect_table_fini(0);
> + irq_domain_remove(redirect_domain);
> + redirect_domain = NULL;
> +out_free_fwnode:
> + irq_domain_free_fwnode(fwnode);
> +fail:
> + return -EINVAL;
> +}
> +#endif
> diff --git a/drivers/irqchip/irq-loongson.h b/drivers/irqchip/irq-loongson.h
> index 11fa138d1f44..05ad40ffb62b 100644
> --- a/drivers/irqchip/irq-loongson.h
> +++ b/drivers/irqchip/irq-loongson.h
> @@ -5,6 +5,15 @@
>
> #ifndef _DRIVERS_IRQCHIP_IRQ_LOONGSON_H
> #define _DRIVERS_IRQCHIP_IRQ_LOONGSON_H
> +#define AVEC_MSG_OFFSET 0x100000
> +struct avecintc_data {
> + struct list_head entry;
> + unsigned int cpu;
> + unsigned int vec;
> + unsigned int prev_cpu;
> + unsigned int prev_vec;
> + unsigned int moving;
> +};
>
> int find_pch_pic(u32 gsi);
>
> @@ -24,4 +33,7 @@ int pch_msi_acpi_init(struct irq_domain *parent,
> struct acpi_madt_msi_pic *acpi_pchmsi);
> int pch_msi_acpi_init_avec(struct irq_domain *parent);
>
> +int redirect_acpi_init(struct irq_domain *parent);
> +
> +void avecintc_sync(struct avecintc_data *adata);
> #endif /* _DRIVERS_IRQCHIP_IRQ_LOONGSON_H */
> diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
> index 6cc5e484547c..2fd5531fa378 100644
> --- a/include/linux/cpuhotplug.h
> +++ b/include/linux/cpuhotplug.h
> @@ -146,6 +146,7 @@ enum cpuhp_state {
> CPUHP_AP_IRQ_MIPS_GIC_STARTING,
> CPUHP_AP_IRQ_EIOINTC_STARTING,
> CPUHP_AP_IRQ_AVECINTC_STARTING,
> + CPUHP_AP_IRQ_REDIRECT_STARTING,
> CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING,
> CPUHP_AP_IRQ_THEAD_ACLINT_SSWI_STARTING,
> CPUHP_AP_IRQ_RISCV_IMSIC_STARTING,
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2025-03-19 5:55 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-03-03 10:15 [PATCH 0/2] Loongarch irq-redirect supprot Tianyang Zhang
2025-03-03 10:15 ` [PATCH 1/2] Docs/LoongArch: Add Advanced Extended-Redirect IRQ model description Tianyang Zhang
2025-03-03 13:36 ` Huacai Chen
2025-03-06 2:09 ` Tianyang Zhang
2025-03-03 10:15 ` [PATCH 2/2] irq/irq-loongarch-ir:Add Redirect irqchip support Tianyang Zhang
2025-03-03 14:34 ` Huacai Chen
2025-03-03 21:28 ` Thomas Gleixner
2025-03-06 2:07 ` Tianyang Zhang
2025-03-06 2:06 ` Tianyang Zhang
2025-03-04 11:24 ` kernel test robot
2025-03-05 14:47 ` kernel test robot
-- strict thread matches above, loose matches on Subject: below --
2025-03-17 8:22 [PATCH v1 0/2] Loongarch irq-redirect supprot Tianyang Zhang
2025-03-17 8:22 ` [PATCH 2/2] irq/irq-loongarch-ir:Add Redirect irqchip support Tianyang Zhang
2025-03-19 5:55 ` Tianyang Zhang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).