From f679616565f1cf1a4acb245dbc0032dafcd40637 Mon Sep 17 00:00:00 2001 From: Jianmin Lv Date: Wed, 14 Jun 2023 19:59:32 +0800 Subject: irqchip/loongson-pch-pic: Fix initialization of HT vector register In an ACPI-based dual-bridge system, IRQ of each bridge's PCH PIC sent to CPU is always a zero-based number, which means that the IRQ on PCH PIC of each bridge is mapped into vector range from 0 to 63 of upstream irqchip(e.g. EIOINTC). EIOINTC N: [0 ... 63 | 64 ... 255] -------- ---------- ^ ^ | | PCH PIC N | PCH MSI N For example, the IRQ vector number of sata controller on PCH PIC of each bridge is 16, which is sent to upstream irqchip of EIOINTC when an interrupt occurs, which will set bit 16 of EIOINTC. Since hwirq of 16 on EIOINTC has been mapped to a irq_desc for sata controller during hierarchy irq allocation, the related mapped IRQ will be found through irq_resolve_mapping() in the IRQ domain of EIOINTC. So, the IRQ number set in HT vector register should be fixed to be a zero-based number. Cc: stable@vger.kernel.org Reviewed-by: Huacai Chen Co-developed-by: liuyun Signed-off-by: liuyun Signed-off-by: Jianmin Lv Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20230614115936.5950-2-lvjianmin@loongson.cn --- drivers/irqchip/irq-loongson-pch-pic.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-loongson-pch-pic.c b/drivers/irqchip/irq-loongson-pch-pic.c index e5fe4d50be05..921c5c0190d1 100644 --- a/drivers/irqchip/irq-loongson-pch-pic.c +++ b/drivers/irqchip/irq-loongson-pch-pic.c @@ -401,14 +401,12 @@ static int __init acpi_cascade_irqdomain_init(void) int __init pch_pic_acpi_init(struct irq_domain *parent, struct acpi_madt_bio_pic *acpi_pchpic) { - int ret, vec_base; + int ret; struct fwnode_handle *domain_handle; if (find_pch_pic(acpi_pchpic->gsi_base) >= 0) return 0; - vec_base = acpi_pchpic->gsi_base - GSI_MIN_PCH_IRQ; - domain_handle = irq_domain_alloc_fwnode(&acpi_pchpic->address); if (!domain_handle) { pr_err("Unable to allocate domain handle\n"); @@ -416,7 +414,7 @@ int __init pch_pic_acpi_init(struct irq_domain *parent, } ret = pch_pic_init(acpi_pchpic->address, acpi_pchpic->size, - vec_base, parent, domain_handle, acpi_pchpic->gsi_base); + 0, parent, domain_handle, acpi_pchpic->gsi_base); if (ret < 0) { irq_domain_free_fwnode(domain_handle); -- cgit v1.2.3 From 783422e704ca0fa41cb2fe9ed79e46b6fe7eae29 Mon Sep 17 00:00:00 2001 From: Liu Peibao Date: Wed, 14 Jun 2023 19:59:33 +0800 Subject: irqchip/loongson-pch-pic: Fix potential incorrect hwirq assignment In DeviceTree path, when ht_vec_base is not zero, the hwirq of PCH PIC will be assigned incorrectly. Because when pch_pic_domain_translate() adds the ht_vec_base to hwirq, the hwirq does not have the ht_vec_base subtracted when calling irq_domain_set_info(). The ht_vec_base is designed for the parent irq chip/domain of the PCH PIC. It seems not proper to deal this in callbacks of the PCH PIC domain and let's put this back like the initial commit ef8c01eb64ca ("irqchip: Add Loongson PCH PIC controller"). Fixes: bcdd75c596c8 ("irqchip/loongson-pch-pic: Add ACPI init support") Cc: stable@vger.kernel.org Reviewed-by: Huacai Chen Signed-off-by: Liu Peibao Signed-off-by: Jianmin Lv Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20230614115936.5950-3-lvjianmin@loongson.cn --- drivers/irqchip/irq-loongson-pch-pic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-loongson-pch-pic.c b/drivers/irqchip/irq-loongson-pch-pic.c index 921c5c0190d1..93a71f66efeb 100644 --- a/drivers/irqchip/irq-loongson-pch-pic.c +++ b/drivers/irqchip/irq-loongson-pch-pic.c @@ -164,7 +164,7 @@ static int pch_pic_domain_translate(struct irq_domain *d, if (fwspec->param_count < 2) return -EINVAL; - *hwirq = fwspec->param[0] + priv->ht_vec_base; + *hwirq = fwspec->param[0]; *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; } else { if (fwspec->param_count < 1) @@ -196,7 +196,7 @@ static int pch_pic_alloc(struct irq_domain *domain, unsigned int virq, parent_fwspec.fwnode = domain->parent->fwnode; parent_fwspec.param_count = 1; - parent_fwspec.param[0] = hwirq; + parent_fwspec.param[0] = hwirq + priv->ht_vec_base; err = irq_domain_alloc_irqs_parent(domain, virq, 1, &parent_fwspec); if (err) -- cgit v1.2.3 From 1d7471b4e0ebba5a4bf9db4ade43619e8f2d333d Mon Sep 17 00:00:00 2001 From: Jianmin Lv Date: Wed, 14 Jun 2023 19:59:34 +0800 Subject: irqchip/loongson-liointc: Fix IRQ trigger polarity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For the INT_POLARITY register of Loongson-2K series IRQ controller, '0' indicates high level or rising edge triggered, '1' indicates low level or falling edge triggered, and we can find out the information from the Loongson 2K1000LA User Manual v1.0, Table 9-2, Section 9.3 (中断寄存器描述 / Description of the Interrupt Registers). For Loongson-3 CPU series, setting INT_POLARITY register is not supported and writting it has no effect. So trigger polarity setting shouled be fixed for Loongson-2K CPU series. Fixes: 17343d0b4039 ("irqchip/loongson-liointc: Support to set IRQ type for ACPI path") Cc: stable@vger.kernel.org Reviewed-by: Huacai Chen Co-developed-by: Chong Qiao Signed-off-by: Chong Qiao Signed-off-by: Jianmin Lv Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20230614115936.5950-4-lvjianmin@loongson.cn --- drivers/irqchip/irq-loongson-liointc.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-loongson-liointc.c b/drivers/irqchip/irq-loongson-liointc.c index 8d00a9ad5b00..5dd9db8f8fa8 100644 --- a/drivers/irqchip/irq-loongson-liointc.c +++ b/drivers/irqchip/irq-loongson-liointc.c @@ -32,6 +32,10 @@ #define LIOINTC_REG_INTC_EN_STATUS (LIOINTC_INTC_CHIP_START + 0x04) #define LIOINTC_REG_INTC_ENABLE (LIOINTC_INTC_CHIP_START + 0x08) #define LIOINTC_REG_INTC_DISABLE (LIOINTC_INTC_CHIP_START + 0x0c) +/* + * LIOINTC_REG_INTC_POL register is only valid for Loongson-2K series, and + * Loongson-3 series behave as noops. + */ #define LIOINTC_REG_INTC_POL (LIOINTC_INTC_CHIP_START + 0x10) #define LIOINTC_REG_INTC_EDGE (LIOINTC_INTC_CHIP_START + 0x14) @@ -116,19 +120,19 @@ static int liointc_set_type(struct irq_data *data, unsigned int type) switch (type) { case IRQ_TYPE_LEVEL_HIGH: liointc_set_bit(gc, LIOINTC_REG_INTC_EDGE, mask, false); - liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, true); + liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, false); break; case IRQ_TYPE_LEVEL_LOW: liointc_set_bit(gc, LIOINTC_REG_INTC_EDGE, mask, false); - liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, false); + liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, true); break; case IRQ_TYPE_EDGE_RISING: liointc_set_bit(gc, LIOINTC_REG_INTC_EDGE, mask, true); - liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, true); + liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, false); break; case IRQ_TYPE_EDGE_FALLING: liointc_set_bit(gc, LIOINTC_REG_INTC_EDGE, mask, true); - liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, false); + liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, true); break; default: irq_gc_unlock_irqrestore(gc, flags); -- cgit v1.2.3 From e01f9882f6fdbe0fa8ae39fe7691db2964e9fda6 Mon Sep 17 00:00:00 2001 From: Yinbo Zhu Date: Wed, 14 Jun 2023 19:59:35 +0800 Subject: irqchip/loongson-liointc: Add IRQCHIP_SKIP_SET_WAKE flag LIOINTC doesn't require specific logic to work with wakeup IRQs, and no irq_set_wake callback is needed. To allow registered IRQs from LIOINTC to be used as a wakeup-source, and ensure irq_set_irq_wake() works well, the flag IRQCHIP_SKIP_SET_WAKE should be added. Reviewed-by: Huacai Chen Signed-off-by: Yinbo Zhu Signed-off-by: Jianmin Lv Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20230614115936.5950-5-lvjianmin@loongson.cn --- drivers/irqchip/irq-loongson-liointc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-loongson-liointc.c b/drivers/irqchip/irq-loongson-liointc.c index 5dd9db8f8fa8..e4b33aed1c97 100644 --- a/drivers/irqchip/irq-loongson-liointc.c +++ b/drivers/irqchip/irq-loongson-liointc.c @@ -295,6 +295,7 @@ static int liointc_init(phys_addr_t addr, unsigned long size, int revision, ct->chip.irq_mask = irq_gc_mask_disable_reg; ct->chip.irq_mask_ack = irq_gc_mask_disable_reg; ct->chip.irq_set_type = liointc_set_type; + ct->chip.flags = IRQCHIP_SKIP_SET_WAKE; gc->mask_cache = 0; priv->gc = gc; -- cgit v1.2.3 From fb07b8f83441febeb0daf199b5f18c6de9bbab03 Mon Sep 17 00:00:00 2001 From: Jianmin Lv Date: Wed, 14 Jun 2023 19:59:36 +0800 Subject: irqchip/loongson-eiointc: Fix irq affinity setting during resume The hierarchy of PCH PIC, PCH PCI MSI and EIONTC is as following: PCH PIC ------->| |---->EIOINTC PCH PCI MSI --->| so the irq_data list of irq_desc for IRQs on PCH PIC and PCH PCI MSI is like this: irq_desc->irq_data(domain: PCH PIC)->parent_data(domain: EIOINTC) irq_desc->irq_data(domain: PCH PCI MSI)->parent_data(domain: EIOINTC) In eiointc_resume(), the irq_data passed into eiointc_set_irq_affinity() should be matched to EIOINTC domain instead of PCH PIC or PCH PCI MSI domain, so fix it. Fixes: a90335c2dfb4 ("irqchip/loongson-eiointc: Add suspend/resume support") Reported-by: yangqiming Signed-off-by: Jianmin Lv Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20230614115936.5950-6-lvjianmin@loongson.cn --- drivers/irqchip/irq-loongson-eiointc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-loongson-eiointc.c b/drivers/irqchip/irq-loongson-eiointc.c index 71ef19f77a5a..a7fcde3e3ecc 100644 --- a/drivers/irqchip/irq-loongson-eiointc.c +++ b/drivers/irqchip/irq-loongson-eiointc.c @@ -314,7 +314,7 @@ static void eiointc_resume(void) desc = irq_resolve_mapping(eiointc_priv[i]->eiointc_domain, j); if (desc && desc->handle_irq && desc->handle_irq != handle_bad_irq) { raw_spin_lock(&desc->lock); - irq_data = &desc->irq_data; + irq_data = irq_domain_get_irq_data(eiointc_priv[i]->eiointc_domain, irq_desc_get_irq(desc)); eiointc_set_irq_affinity(irq_data, irq_data->common->affinity, 0); raw_spin_unlock(&desc->lock); } -- cgit v1.2.3 From a3f1132c4c6be3ab5af1a2ae1885deda81539277 Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Fri, 5 May 2023 17:46:49 +0800 Subject: irqchip/loongson-eiointc: Add DT init support Add EIOINTC irqchip DT support, which is needed for Loongson chips based on DT and supporting EIOINTC, such as the Loongson-2K0500 SOC. Signed-off-by: Binbin Zhou Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/764e02d924094580ac0f1d15535f4b98308705c6.1683279769.git.zhoubinbin@loongson.cn --- drivers/irqchip/irq-loongson-eiointc.c | 133 ++++++++++++++++++++++++--------- 1 file changed, 98 insertions(+), 35 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-loongson-eiointc.c b/drivers/irqchip/irq-loongson-eiointc.c index a7fcde3e3ecc..92d8aa28bdf5 100644 --- a/drivers/irqchip/irq-loongson-eiointc.c +++ b/drivers/irqchip/irq-loongson-eiointc.c @@ -36,6 +36,7 @@ static int nr_pics; struct eiointc_priv { u32 node; + u32 vec_count; nodemask_t node_map; cpumask_t cpuspan_map; struct fwnode_handle *domain_handle; @@ -153,18 +154,18 @@ static int eiointc_router_init(unsigned int cpu) if ((cpu_logical_map(cpu) % CORES_PER_EIO_NODE) == 0) { eiointc_enable(); - for (i = 0; i < VEC_COUNT / 32; i++) { + for (i = 0; i < eiointc_priv[0]->vec_count / 32; i++) { data = (((1 << (i * 2 + 1)) << 16) | (1 << (i * 2))); iocsr_write32(data, EIOINTC_REG_NODEMAP + i * 4); } - for (i = 0; i < VEC_COUNT / 32 / 4; i++) { + for (i = 0; i < eiointc_priv[0]->vec_count / 32 / 4; i++) { bit = BIT(1 + index); /* Route to IP[1 + index] */ data = bit | (bit << 8) | (bit << 16) | (bit << 24); iocsr_write32(data, EIOINTC_REG_IPMAP + i * 4); } - for (i = 0; i < VEC_COUNT / 4; i++) { + for (i = 0; i < eiointc_priv[0]->vec_count / 4; i++) { /* Route to Node-0 Core-0 */ if (index == 0) bit = BIT(cpu_logical_map(0)); @@ -175,7 +176,7 @@ static int eiointc_router_init(unsigned int cpu) iocsr_write32(data, EIOINTC_REG_ROUTE + i * 4); } - for (i = 0; i < VEC_COUNT / 32; i++) { + for (i = 0; i < eiointc_priv[0]->vec_count / 32; i++) { data = 0xffffffff; iocsr_write32(data, EIOINTC_REG_ENABLE + i * 4); iocsr_write32(data, EIOINTC_REG_BOUNCE + i * 4); @@ -195,7 +196,7 @@ static void eiointc_irq_dispatch(struct irq_desc *desc) chained_irq_enter(chip, desc); - for (i = 0; i < VEC_REG_COUNT; i++) { + for (i = 0; i < eiointc_priv[0]->vec_count / VEC_COUNT_PER_REG; i++) { pending = iocsr_read64(EIOINTC_REG_ISR + (i << 3)); iocsr_write64(pending, EIOINTC_REG_ISR + (i << 3)); while (pending) { @@ -310,7 +311,7 @@ static void eiointc_resume(void) eiointc_router_init(0); for (i = 0; i < nr_pics; i++) { - for (j = 0; j < VEC_COUNT; j++) { + for (j = 0; j < eiointc_priv[0]->vec_count; j++) { desc = irq_resolve_mapping(eiointc_priv[i]->eiointc_domain, j); if (desc && desc->handle_irq && desc->handle_irq != handle_bad_irq) { raw_spin_lock(&desc->lock); @@ -375,11 +376,47 @@ static int __init acpi_cascade_irqdomain_init(void) return 0; } +static int __init eiointc_init(struct eiointc_priv *priv, int parent_irq, + u64 node_map) +{ + int i; + + node_map = node_map ? node_map : -1ULL; + for_each_possible_cpu(i) { + if (node_map & (1ULL << (cpu_to_eio_node(i)))) { + node_set(cpu_to_eio_node(i), priv->node_map); + cpumask_or(&priv->cpuspan_map, &priv->cpuspan_map, + cpumask_of(i)); + } + } + + priv->eiointc_domain = irq_domain_create_linear(priv->domain_handle, + priv->vec_count, + &eiointc_domain_ops, + priv); + if (!priv->eiointc_domain) { + pr_err("loongson-extioi: cannot add IRQ domain\n"); + return -ENOMEM; + } + + eiointc_priv[nr_pics++] = priv; + eiointc_router_init(0); + irq_set_chained_handler_and_data(parent_irq, eiointc_irq_dispatch, priv); + + if (nr_pics == 1) { + register_syscore_ops(&eiointc_syscore_ops); + cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_LOONGARCH_STARTING, + "irqchip/loongarch/intc:starting", + eiointc_router_init, NULL); + } + + return 0; +} + int __init eiointc_acpi_init(struct irq_domain *parent, struct acpi_madt_eio_pic *acpi_eiointc) { - int i, ret, parent_irq; - unsigned long node_map; + int parent_irq, ret; struct eiointc_priv *priv; int node; @@ -394,37 +431,14 @@ int __init eiointc_acpi_init(struct irq_domain *parent, goto out_free_priv; } + priv->vec_count = VEC_COUNT; priv->node = acpi_eiointc->node; - node_map = acpi_eiointc->node_map ? : -1ULL; - - for_each_possible_cpu(i) { - if (node_map & (1ULL << cpu_to_eio_node(i))) { - node_set(cpu_to_eio_node(i), priv->node_map); - cpumask_or(&priv->cpuspan_map, &priv->cpuspan_map, cpumask_of(i)); - } - } - - /* Setup IRQ domain */ - priv->eiointc_domain = irq_domain_create_linear(priv->domain_handle, VEC_COUNT, - &eiointc_domain_ops, priv); - if (!priv->eiointc_domain) { - pr_err("loongson-eiointc: cannot add IRQ domain\n"); - goto out_free_handle; - } - - eiointc_priv[nr_pics++] = priv; - - eiointc_router_init(0); parent_irq = irq_create_mapping(parent, acpi_eiointc->cascade); - irq_set_chained_handler_and_data(parent_irq, eiointc_irq_dispatch, priv); - if (nr_pics == 1) { - register_syscore_ops(&eiointc_syscore_ops); - cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_LOONGARCH_STARTING, - "irqchip/loongarch/intc:starting", - eiointc_router_init, NULL); - } + ret = eiointc_init(priv, parent_irq, acpi_eiointc->node_map); + if (ret < 0) + goto out_free_handle; if (cpu_has_flatmode) node = cpu_to_node(acpi_eiointc->node * CORES_PER_EIO_NODE); @@ -432,7 +446,10 @@ int __init eiointc_acpi_init(struct irq_domain *parent, node = acpi_eiointc->node; acpi_set_vec_parent(node, priv->eiointc_domain, pch_group); acpi_set_vec_parent(node, priv->eiointc_domain, msi_group); + ret = acpi_cascade_irqdomain_init(); + if (ret < 0) + goto out_free_handle; return ret; @@ -444,3 +461,49 @@ out_free_priv: return -ENOMEM; } + +static int __init eiointc_of_init(struct device_node *of_node, + struct device_node *parent) +{ + int parent_irq, ret; + struct eiointc_priv *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + parent_irq = irq_of_parse_and_map(of_node, 0); + if (parent_irq <= 0) { + ret = -ENODEV; + goto out_free_priv; + } + + ret = irq_set_handler_data(parent_irq, priv); + if (ret < 0) + goto out_free_priv; + + /* + * In particular, the number of devices supported by the LS2K0500 + * extended I/O interrupt vector is 128. + */ + if (of_device_is_compatible(of_node, "loongson,ls2k0500-eiointc")) + priv->vec_count = 128; + else + priv->vec_count = VEC_COUNT; + + priv->node = 0; + priv->domain_handle = of_node_to_fwnode(of_node); + + ret = eiointc_init(priv, parent_irq, 0); + if (ret < 0) + goto out_free_priv; + + return 0; + +out_free_priv: + kfree(priv); + return ret; +} + +IRQCHIP_DECLARE(loongson_ls2k0500_eiointc, "loongson,ls2k0500-eiointc", eiointc_of_init); +IRQCHIP_DECLARE(loongson_ls2k2000_eiointc, "loongson,ls2k2000-eiointc", eiointc_of_init); -- cgit v1.2.3