From nobody Mon Jun 15 10:19:04 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8155EC433EF for ; Sat, 30 Apr 2022 08:54:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234786AbiD3I6C (ORCPT ); Sat, 30 Apr 2022 04:58:02 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38420 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229683AbiD3I6B (ORCPT ); Sat, 30 Apr 2022 04:58:01 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BF8B32638 for ; Sat, 30 Apr 2022 01:54:40 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 59E9A60A22 for ; Sat, 30 Apr 2022 08:54:40 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 33F4CC385A7; Sat, 30 Apr 2022 08:54:36 +0000 (UTC) From: Huacai Chen To: Thomas Gleixner , Marc Zyngier Cc: linux-kernel@vger.kernel.org, Xuefeng Li , Huacai Chen , Jiaxun Yang , Huacai Chen Subject: [PATCH V11 01/10] irqchip: Adjust Kconfig for Loongson Date: Sat, 30 Apr 2022 16:53:35 +0800 Message-Id: <20220430085344.3127346-2-chenhuacai@loongson.cn> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20220430085344.3127346-1-chenhuacai@loongson.cn> References: <20220430085344.3127346-1-chenhuacai@loongson.cn> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" We are preparing to add new Loongson (based on LoongArch, not compatible with old MIPS-based Loongson) support. HTVEC will be shared by both old and new Loongson processors, so we adjust its description. HTPIC is only used by MIPS-based Loongson, so we add a MIPS dependency. PCH_PIC and PCH_MSI will have some arch-specific code, so we remove the COMPILE_TEST dependency to avoid build warnings. Signed-off-by: Huacai Chen --- drivers/irqchip/Kconfig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 15edb9a6fcae..39d6be2764a2 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -556,7 +556,7 @@ config LOONGSON_LIOINTC =20 config LOONGSON_HTPIC bool "Loongson3 HyperTransport PIC Controller" - depends on MACH_LOONGSON64 + depends on (MACH_LOONGSON64 && MIPS) default y select IRQ_DOMAIN select GENERIC_IRQ_CHIP @@ -564,16 +564,16 @@ config LOONGSON_HTPIC Support for the Loongson-3 HyperTransport PIC Controller. =20 config LOONGSON_HTVEC - bool "Loongson3 HyperTransport Interrupt Vector Controller" + bool "Loongson HyperTransport Interrupt Vector Controller" depends on MACH_LOONGSON64 default MACH_LOONGSON64 select IRQ_DOMAIN_HIERARCHY help - Support for the Loongson3 HyperTransport Interrupt Vector Controller. + Support for the Loongson HyperTransport Interrupt Vector Controller. =20 config LOONGSON_PCH_PIC bool "Loongson PCH PIC Controller" - depends on MACH_LOONGSON64 || COMPILE_TEST + depends on MACH_LOONGSON64 default MACH_LOONGSON64 select IRQ_DOMAIN_HIERARCHY select IRQ_FASTEOI_HIERARCHY_HANDLERS @@ -582,7 +582,7 @@ config LOONGSON_PCH_PIC =20 config LOONGSON_PCH_MSI bool "Loongson PCH MSI Controller" - depends on MACH_LOONGSON64 || COMPILE_TEST + depends on MACH_LOONGSON64 depends on PCI default MACH_LOONGSON64 select IRQ_DOMAIN_HIERARCHY --=20 2.27.0 From nobody Mon Jun 15 10:19:04 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 999DEC433F5 for ; Sat, 30 Apr 2022 08:56:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235607AbiD3I7S (ORCPT ); Sat, 30 Apr 2022 04:59:18 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40504 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229683AbiD3I7M (ORCPT ); Sat, 30 Apr 2022 04:59:12 -0400 Received: from sin.source.kernel.org (sin.source.kernel.org [145.40.73.55]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 995212F386 for ; Sat, 30 Apr 2022 01:55:50 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by sin.source.kernel.org (Postfix) with ESMTPS id DB35ECE1151 for ; Sat, 30 Apr 2022 08:55:48 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id BF25BC385A7; Sat, 30 Apr 2022 08:55:44 +0000 (UTC) From: Huacai Chen To: Thomas Gleixner , Marc Zyngier Cc: linux-kernel@vger.kernel.org, Xuefeng Li , Huacai Chen , Jiaxun Yang , Huacai Chen Subject: [PATCH V11 02/10] irqchip/loongson-pch-pic: Add ACPI init support Date: Sat, 30 Apr 2022 16:53:36 +0800 Message-Id: <20220430085344.3127346-3-chenhuacai@loongson.cn> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20220430085344.3127346-1-chenhuacai@loongson.cn> References: <20220430085344.3127346-1-chenhuacai@loongson.cn> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" We are preparing to add new Loongson (based on LoongArch, not compatible with old MIPS-based Loongson) support. LoongArch use ACPI other than DT as its boot protocol, so add ACPI init support. PCH-PIC/PCH-MSI stands for "Interrupt Controller" that described in Section 5 of "Loongson 7A1000 Bridge User Manual". For more information please refer Documentation/loongarch/irq-chip-model.rst. Signed-off-by: Huacai Chen --- drivers/irqchip/irq-loongson-pch-pic.c | 108 ++++++++++++++++++------- 1 file changed, 81 insertions(+), 27 deletions(-) diff --git a/drivers/irqchip/irq-loongson-pch-pic.c b/drivers/irqchip/irq-l= oongson-pch-pic.c index a4eb8a2181c7..b0644d595789 100644 --- a/drivers/irqchip/irq-loongson-pch-pic.c +++ b/drivers/irqchip/irq-loongson-pch-pic.c @@ -33,13 +33,18 @@ #define PIC_REG_IDX(irq_id) ((irq_id) / PIC_COUNT_PER_REG) #define PIC_REG_BIT(irq_id) ((irq_id) % PIC_COUNT_PER_REG) =20 +static int nr_pics; + struct pch_pic { void __iomem *base; struct irq_domain *pic_domain; + struct fwnode_handle *domain_handle; u32 ht_vec_base; raw_spinlock_t pic_lock; }; =20 +static struct pch_pic *pch_pic_priv[2]; + static void pch_pic_bitset(struct pch_pic *priv, int offset, int bit) { u32 reg; @@ -180,7 +185,7 @@ static void pch_pic_reset(struct pch_pic *priv) int i; =20 for (i =3D 0; i < PIC_COUNT; i++) { - /* Write vectored ID */ + /* Write vector ID */ writeb(priv->ht_vec_base + i, priv->base + PCH_INT_HTVEC(i)); /* Hardcode route to HT0 Lo */ writeb(1, priv->base + PCH_INT_ROUTE(i)); @@ -198,50 +203,37 @@ static void pch_pic_reset(struct pch_pic *priv) } } =20 -static int pch_pic_of_init(struct device_node *node, - struct device_node *parent) +static int pch_pic_init(phys_addr_t addr, unsigned long size, int vec_base, + struct irq_domain *parent_domain, struct fwnode_handle *domain_handle) { + int vec_count; struct pch_pic *priv; - struct irq_domain *parent_domain; - int err; =20 priv =3D kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; =20 raw_spin_lock_init(&priv->pic_lock); - priv->base =3D of_iomap(node, 0); - if (!priv->base) { - err =3D -ENOMEM; + priv->base =3D ioremap(addr, size); + if (!priv->base) goto free_priv; - } =20 - parent_domain =3D irq_find_host(parent); - if (!parent_domain) { - pr_err("Failed to find the parent domain\n"); - err =3D -ENXIO; - goto iounmap_base; - } + priv->domain_handle =3D domain_handle; =20 - if (of_property_read_u32(node, "loongson,pic-base-vec", - &priv->ht_vec_base)) { - pr_err("Failed to determine pic-base-vec\n"); - err =3D -EINVAL; - goto iounmap_base; - } + priv->ht_vec_base =3D vec_base; + vec_count =3D ((readq(priv->base) >> 48) & 0xff) + 1; =20 priv->pic_domain =3D irq_domain_create_hierarchy(parent_domain, 0, - PIC_COUNT, - of_node_to_fwnode(node), - &pch_pic_domain_ops, - priv); + vec_count, priv->domain_handle, + &pch_pic_domain_ops, priv); + if (!priv->pic_domain) { pr_err("Failed to create IRQ domain\n"); - err =3D -ENOMEM; goto iounmap_base; } =20 pch_pic_reset(priv); + pch_pic_priv[nr_pics++] =3D priv; =20 return 0; =20 @@ -250,7 +242,69 @@ static int pch_pic_of_init(struct device_node *node, free_priv: kfree(priv); =20 - return err; + return -EINVAL; +} + +#ifdef CONFIG_OF + +static int pch_pic_of_init(struct device_node *node, + struct device_node *parent) +{ + int err, vec_base; + struct resource res; + struct irq_domain *parent_domain; + + if (of_address_to_resource(node, 0, &res)) + return -EINVAL; + + parent_domain =3D irq_find_host(parent); + if (!parent_domain) { + pr_err("Failed to find the parent domain\n"); + return -ENXIO; + } + + if (of_property_read_u32(node, "loongson,pic-base-vec", &vec_base)) { + pr_err("Failed to determine pic-base-vec\n"); + return -EINVAL; + } + + err =3D pch_pic_init(res.start, resource_size(&res), vec_base, + parent_domain, of_node_to_fwnode(node)); + if (err < 0) + return err; + + return 0; } =20 IRQCHIP_DECLARE(pch_pic, "loongson,pch-pic-1.0", pch_pic_of_init); + +#endif + +#ifdef CONFIG_ACPI + +struct irq_domain *pch_pic_acpi_init(struct irq_domain *parent, + struct acpi_madt_bio_pic *acpi_pchpic) +{ + int ret, vec_base; + struct fwnode_handle *domain_handle; + + if (!acpi_pchpic) + return NULL; + + vec_base =3D acpi_pchpic->gsi_base - GSI_MIN_PCH_IRQ; + + domain_handle =3D irq_domain_alloc_fwnode((phys_addr_t *)acpi_pchpic); + if (!domain_handle) { + pr_err("Unable to allocate domain handle\n"); + return NULL; + } + + ret =3D pch_pic_init(acpi_pchpic->address, acpi_pchpic->size, + vec_base, parent, domain_handle); + if (ret < 0) + return NULL; + + return irq_find_matching_fwnode(domain_handle, DOMAIN_BUS_ANY); +} + +#endif --=20 2.27.0 From nobody Mon Jun 15 10:19:04 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id A7AD5C433EF for ; Sat, 30 Apr 2022 08:56:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235954AbiD3I75 (ORCPT ); Sat, 30 Apr 2022 04:59:57 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42996 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229683AbiD3I74 (ORCPT ); Sat, 30 Apr 2022 04:59:56 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D89A53FD9C for ; Sat, 30 Apr 2022 01:56:34 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 72317608D5 for ; Sat, 30 Apr 2022 08:56:34 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id A557AC385AA; Sat, 30 Apr 2022 08:56:31 +0000 (UTC) From: Huacai Chen To: Thomas Gleixner , Marc Zyngier Cc: linux-kernel@vger.kernel.org, Xuefeng Li , Huacai Chen , Jiaxun Yang , Huacai Chen Subject: [PATCH V11 03/10] irqchip/loongson-pch-pic: Add suspend/resume support Date: Sat, 30 Apr 2022 16:53:37 +0800 Message-Id: <20220430085344.3127346-4-chenhuacai@loongson.cn> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20220430085344.3127346-1-chenhuacai@loongson.cn> References: <20220430085344.3127346-1-chenhuacai@loongson.cn> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Add suspend/resume support for PCH-PIC irqchip, which is needed for suspend/hibernation. Signed-off-by: Huacai Chen --- drivers/irqchip/irq-loongson-pch-pic.c | 47 ++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/drivers/irqchip/irq-loongson-pch-pic.c b/drivers/irqchip/irq-l= oongson-pch-pic.c index b0644d595789..5047ba96f7d7 100644 --- a/drivers/irqchip/irq-loongson-pch-pic.c +++ b/drivers/irqchip/irq-loongson-pch-pic.c @@ -15,6 +15,7 @@ #include #include #include +#include =20 /* Registers */ #define PCH_PIC_MASK 0x20 @@ -41,6 +42,9 @@ struct pch_pic { struct fwnode_handle *domain_handle; u32 ht_vec_base; raw_spinlock_t pic_lock; + u32 saved_vec_en[PIC_REG_COUNT]; + u32 saved_vec_pol[PIC_REG_COUNT]; + u32 saved_vec_edge[PIC_REG_COUNT]; }; =20 static struct pch_pic *pch_pic_priv[2]; @@ -142,6 +146,7 @@ static struct irq_chip pch_pic_irq_chip =3D { .irq_ack =3D pch_pic_ack_irq, .irq_set_affinity =3D irq_chip_set_affinity_parent, .irq_set_type =3D pch_pic_set_type, + .flags =3D IRQCHIP_SKIP_SET_WAKE, }; =20 static int pch_pic_alloc(struct irq_domain *domain, unsigned int virq, @@ -203,6 +208,46 @@ static void pch_pic_reset(struct pch_pic *priv) } } =20 +static int pch_pic_suspend(void) +{ + int i, j; + + for (i =3D 0; i < nr_pics; i++) { + for (j =3D 0; j < PIC_REG_COUNT; j++) { + pch_pic_priv[i]->saved_vec_pol[j] =3D + readl(pch_pic_priv[i]->base + PCH_PIC_POL + 4 * j); + pch_pic_priv[i]->saved_vec_edge[j] =3D + readl(pch_pic_priv[i]->base + PCH_PIC_EDGE + 4 * j); + pch_pic_priv[i]->saved_vec_en[j] =3D + readl(pch_pic_priv[i]->base + PCH_PIC_MASK + 4 * j); + } + } + + return 0; +} + +static void pch_pic_resume(void) +{ + int i, j; + + for (i =3D 0; i < nr_pics; i++) { + pch_pic_reset(pch_pic_priv[i]); + for (j =3D 0; j < PIC_REG_COUNT; j++) { + writel(pch_pic_priv[i]->saved_vec_pol[j], + pch_pic_priv[i]->base + PCH_PIC_POL + 4 * j); + writel(pch_pic_priv[i]->saved_vec_edge[j], + pch_pic_priv[i]->base + PCH_PIC_EDGE + 4 * j); + writel(pch_pic_priv[i]->saved_vec_en[j], + pch_pic_priv[i]->base + PCH_PIC_MASK + 4 * j); + } + } +} + +static struct syscore_ops pch_pic_syscore_ops =3D { + .suspend =3D pch_pic_suspend, + .resume =3D pch_pic_resume, +}; + static int pch_pic_init(phys_addr_t addr, unsigned long size, int vec_base, struct irq_domain *parent_domain, struct fwnode_handle *domain_handle) { @@ -235,6 +280,8 @@ static int pch_pic_init(phys_addr_t addr, unsigned long= size, int vec_base, pch_pic_reset(priv); pch_pic_priv[nr_pics++] =3D priv; =20 + register_syscore_ops(&pch_pic_syscore_ops); + return 0; =20 iounmap_base: --=20 2.27.0 From nobody Mon Jun 15 10:19:04 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4FD9BC433EF for ; Sat, 30 Apr 2022 08:57:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236145AbiD3JBK (ORCPT ); Sat, 30 Apr 2022 05:01:10 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46032 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229683AbiD3JBH (ORCPT ); Sat, 30 Apr 2022 05:01:07 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 44C8618B27 for ; Sat, 30 Apr 2022 01:57:46 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id D50CD60A22 for ; Sat, 30 Apr 2022 08:57:45 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 1515FC385A7; Sat, 30 Apr 2022 08:57:42 +0000 (UTC) From: Huacai Chen To: Thomas Gleixner , Marc Zyngier Cc: linux-kernel@vger.kernel.org, Xuefeng Li , Huacai Chen , Jiaxun Yang , Huacai Chen Subject: [PATCH V13 04/10] irqchip/loongson-pch-msi: Add ACPI init support Date: Sat, 30 Apr 2022 16:53:38 +0800 Message-Id: <20220430085344.3127346-5-chenhuacai@loongson.cn> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20220430085344.3127346-1-chenhuacai@loongson.cn> References: <20220430085344.3127346-1-chenhuacai@loongson.cn> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" We are preparing to add new Loongson (based on LoongArch, not compatible with old MIPS-based Loongson) support. LoongArch use ACPI other than DT as its boot protocol, so add ACPI init support. PCH-PIC/PCH-MSI stands for "Interrupt Controller" that described in Section 5 of "Loongson 7A1000 Bridge User Manual". For more information please refer Documentation/loongarch/irq-chip-model.rst. Signed-off-by: Huacai Chen --- drivers/irqchip/irq-loongson-pch-msi.c | 128 +++++++++++++++++-------- 1 file changed, 86 insertions(+), 42 deletions(-) diff --git a/drivers/irqchip/irq-loongson-pch-msi.c b/drivers/irqchip/irq-l= oongson-pch-msi.c index e3801c4a77ed..6720212bece1 100644 --- a/drivers/irqchip/irq-loongson-pch-msi.c +++ b/drivers/irqchip/irq-loongson-pch-msi.c @@ -15,14 +15,19 @@ #include #include =20 +static int nr_pics; + struct pch_msi_data { struct mutex msi_map_lock; phys_addr_t doorbell; u32 irq_first; /* The vector number that MSIs starts */ u32 num_irqs; /* The number of vectors for MSIs */ unsigned long *msi_map; + struct fwnode_handle *domain_handle; }; =20 +static struct pch_msi_data *pch_msi_priv[2]; + static void pch_msi_mask_msi_irq(struct irq_data *d) { pci_msi_mask_irq(d); @@ -154,12 +159,14 @@ static const struct irq_domain_ops pch_msi_middle_dom= ain_ops =3D { }; =20 static int pch_msi_init_domains(struct pch_msi_data *priv, - struct device_node *node, - struct irq_domain *parent) + struct irq_domain *parent, + struct fwnode_handle *domain_handle) { struct irq_domain *middle_domain, *msi_domain; =20 - middle_domain =3D irq_domain_create_linear(of_node_to_fwnode(node), + priv->domain_handle =3D domain_handle; + + middle_domain =3D irq_domain_create_linear(priv->domain_handle, priv->num_irqs, &pch_msi_middle_domain_ops, priv); @@ -171,7 +178,7 @@ static int pch_msi_init_domains(struct pch_msi_data *pr= iv, middle_domain->parent =3D parent; irq_domain_update_bus_token(middle_domain, DOMAIN_BUS_NEXUS); =20 - msi_domain =3D pci_msi_create_irq_domain(of_node_to_fwnode(node), + msi_domain =3D pci_msi_create_irq_domain(priv->domain_handle, &pch_msi_domain_info, middle_domain); if (!msi_domain) { @@ -183,19 +190,11 @@ static int pch_msi_init_domains(struct pch_msi_data *= priv, return 0; } =20 -static int pch_msi_init(struct device_node *node, - struct device_node *parent) +static int pch_msi_init(phys_addr_t msg_address, int irq_base, int irq_cou= nt, + struct irq_domain *parent_domain, struct fwnode_handle *domain_handle) { - struct pch_msi_data *priv; - struct irq_domain *parent_domain; - struct resource res; int ret; - - parent_domain =3D irq_find_host(parent); - if (!parent_domain) { - pr_err("Failed to find the parent domain\n"); - return -ENXIO; - } + struct pch_msi_data *priv; =20 priv =3D kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) @@ -203,48 +202,93 @@ static int pch_msi_init(struct device_node *node, =20 mutex_init(&priv->msi_map_lock); =20 - ret =3D of_address_to_resource(node, 0, &res); - if (ret) { - pr_err("Failed to allocate resource\n"); - goto err_priv; - } - - priv->doorbell =3D res.start; - - if (of_property_read_u32(node, "loongson,msi-base-vec", - &priv->irq_first)) { - pr_err("Unable to parse MSI vec base\n"); - ret =3D -EINVAL; - goto err_priv; - } - - if (of_property_read_u32(node, "loongson,msi-num-vecs", - &priv->num_irqs)) { - pr_err("Unable to parse MSI vec number\n"); - ret =3D -EINVAL; - goto err_priv; - } + priv->doorbell =3D msg_address; + priv->irq_first =3D irq_base; + priv->num_irqs =3D irq_count; =20 priv->msi_map =3D bitmap_zalloc(priv->num_irqs, GFP_KERNEL); - if (!priv->msi_map) { - ret =3D -ENOMEM; + if (!priv->msi_map) goto err_priv; - } =20 pr_debug("Registering %d MSIs, starting at %d\n", priv->num_irqs, priv->irq_first); =20 - ret =3D pch_msi_init_domains(priv, node, parent_domain); + ret =3D pch_msi_init_domains(priv, parent_domain, domain_handle); if (ret) goto err_map; =20 + pch_msi_priv[nr_pics++] =3D priv; + return 0; =20 err_map: bitmap_free(priv->msi_map); err_priv: kfree(priv); - return ret; + + return -EINVAL; +} + +#ifdef CONFIG_OF + +static int pch_msi_of_init(struct device_node *node, struct device_node *p= arent) +{ + int err; + int irq_base, irq_count; + struct resource res; + struct irq_domain *parent_domain; + + parent_domain =3D irq_find_host(parent); + if (!parent_domain) { + pr_err("Failed to find the parent domain\n"); + return -ENXIO; + } + + if (of_address_to_resource(node, 0, &res)) { + pr_err("Failed to allocate resource\n"); + return -EINVAL; + } + + if (of_property_read_u32(node, "loongson,msi-base-vec", &irq_base)) { + pr_err("Unable to parse MSI vec base\n"); + return -EINVAL; + } + + if (of_property_read_u32(node, "loongson,msi-num-vecs", &irq_count)) { + pr_err("Unable to parse MSI vec number\n"); + return -EINVAL; + } + + err =3D pch_msi_init(res.start, irq_base, irq_count, parent_domain, of_no= de_to_fwnode(node)); + if (err < 0) + return err; + + return 0; +} + +IRQCHIP_DECLARE(pch_msi, "loongson,pch-msi-1.0", pch_msi_of_init); + +#endif + +#ifdef CONFIG_ACPI + +struct irq_domain *pch_msi_acpi_init(struct irq_domain *parent, + struct acpi_madt_msi_pic *acpi_pchmsi) +{ + int ret; + struct fwnode_handle *domain_handle; + + if (!acpi_pchmsi) + return NULL; + + domain_handle =3D irq_domain_alloc_fwnode((phys_addr_t *)acpi_pchmsi); + + ret =3D pch_msi_init(acpi_pchmsi->msg_address, acpi_pchmsi->start, + acpi_pchmsi->count, parent, domain_handle); + if (ret < 0) + return NULL; + + return irq_find_matching_fwnode(domain_handle, DOMAIN_BUS_PCI_MSI); } =20 -IRQCHIP_DECLARE(pch_msi, "loongson,pch-msi-1.0", pch_msi_init); +#endif --=20 2.27.0 From nobody Mon Jun 15 10:19:04 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D49FAC433EF for ; Sat, 30 Apr 2022 08:58:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236699AbiD3JCA (ORCPT ); Sat, 30 Apr 2022 05:02:00 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46484 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229683AbiD3JB5 (ORCPT ); Sat, 30 Apr 2022 05:01:57 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [IPv6:2604:1380:4641:c500::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A096E19C35 for ; Sat, 30 Apr 2022 01:58:36 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 3F57E60A22 for ; Sat, 30 Apr 2022 08:58:36 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6DA19C385A7; Sat, 30 Apr 2022 08:58:33 +0000 (UTC) From: Huacai Chen To: Thomas Gleixner , Marc Zyngier Cc: linux-kernel@vger.kernel.org, Xuefeng Li , Huacai Chen , Jiaxun Yang , Huacai Chen Subject: [PATCH V11 05/10] irqchip/loongson-htvec: Add ACPI init support Date: Sat, 30 Apr 2022 16:53:39 +0800 Message-Id: <20220430085344.3127346-6-chenhuacai@loongson.cn> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20220430085344.3127346-1-chenhuacai@loongson.cn> References: <20220430085344.3127346-1-chenhuacai@loongson.cn> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" We are preparing to add new Loongson (based on LoongArch, not compatible with old MIPS-based Loongson) support. LoongArch use ACPI other than DT as its boot protocol, so add ACPI init support. HTVECINTC stands for "HyperTransport Interrupts" that described in Section 14.3 of "Loongson 3A5000 Processor Reference Manual". For more information please refer Documentation/loongarch/irq-chip-model.rst. Signed-off-by: Huacai Chen --- drivers/irqchip/irq-loongson-htvec.c | 119 +++++++++++++++++++-------- 1 file changed, 85 insertions(+), 34 deletions(-) diff --git a/drivers/irqchip/irq-loongson-htvec.c b/drivers/irqchip/irq-loo= ngson-htvec.c index 60a335d7e64e..e304374a018c 100644 --- a/drivers/irqchip/irq-loongson-htvec.c +++ b/drivers/irqchip/irq-loongson-htvec.c @@ -20,7 +20,6 @@ /* Registers */ #define HTVEC_EN_OFF 0x20 #define HTVEC_MAX_PARENT_IRQ 8 - #define VEC_COUNT_PER_REG 32 #define VEC_REG_IDX(irq_id) ((irq_id) / VEC_COUNT_PER_REG) #define VEC_REG_BIT(irq_id) ((irq_id) % VEC_COUNT_PER_REG) @@ -30,8 +29,11 @@ struct htvec { void __iomem *base; struct irq_domain *htvec_domain; raw_spinlock_t htvec_lock; + struct fwnode_handle *domain_handle; }; =20 +static struct htvec *htvec_priv; + static void htvec_irq_dispatch(struct irq_desc *desc) { int i; @@ -155,64 +157,113 @@ static void htvec_reset(struct htvec *priv) } } =20 -static int htvec_of_init(struct device_node *node, - struct device_node *parent) +static int htvec_init(phys_addr_t addr, unsigned long size, + int num_parents, int parent_irq[], struct fwnode_handle *domain_handle) { + int i; struct htvec *priv; - int err, parent_irq[8], i; =20 priv =3D kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; =20 + priv->num_parents =3D num_parents; + priv->base =3D ioremap(addr, size); + priv->domain_handle =3D domain_handle; raw_spin_lock_init(&priv->htvec_lock); - priv->base =3D of_iomap(node, 0); - if (!priv->base) { - err =3D -ENOMEM; - goto free_priv; - } - - /* Interrupt may come from any of the 8 interrupt lines */ - for (i =3D 0; i < HTVEC_MAX_PARENT_IRQ; i++) { - parent_irq[i] =3D irq_of_parse_and_map(node, i); - if (parent_irq[i] <=3D 0) - break; - - priv->num_parents++; - } =20 - if (!priv->num_parents) { - pr_err("Failed to get parent irqs\n"); - err =3D -ENODEV; - goto iounmap_base; - } - - priv->htvec_domain =3D irq_domain_create_linear(of_node_to_fwnode(node), + /* Setup IRQ domain */ + priv->htvec_domain =3D irq_domain_create_linear(priv->domain_handle, (VEC_COUNT_PER_REG * priv->num_parents), &htvec_domain_ops, priv); if (!priv->htvec_domain) { - pr_err("Failed to create IRQ domain\n"); - err =3D -ENOMEM; - goto irq_dispose; + pr_err("loongson-htvec: cannot add IRQ domain\n"); + goto iounmap_base; } =20 htvec_reset(priv); =20 - for (i =3D 0; i < priv->num_parents; i++) + for (i =3D 0; i < priv->num_parents; i++) { irq_set_chained_handler_and_data(parent_irq[i], htvec_irq_dispatch, priv); + } + + htvec_priv =3D priv; =20 return 0; =20 -irq_dispose: - for (; i > 0; i--) - irq_dispose_mapping(parent_irq[i - 1]); iounmap_base: iounmap(priv->base); -free_priv: + priv->domain_handle =3D NULL; kfree(priv); =20 - return err; + return -EINVAL; +} + +#ifdef CONFIG_OF + +static int htvec_of_init(struct device_node *node, + struct device_node *parent) +{ + int i, err; + int parent_irq[8]; + int num_parents =3D 0; + struct resource res; + + if (of_address_to_resource(node, 0, &res)) + return -EINVAL; + + /* Interrupt may come from any of the 8 interrupt lines */ + for (i =3D 0; i < HTVEC_MAX_PARENT_IRQ; i++) { + parent_irq[i] =3D irq_of_parse_and_map(node, i); + if (parent_irq[i] <=3D 0) + break; + + num_parents++; + } + + err =3D htvec_init(res.start, resource_size(&res), + num_parents, parent_irq, of_node_to_fwnode(node)); + if (err < 0) + return err; + + return 0; } =20 IRQCHIP_DECLARE(htvec, "loongson,htvec-1.0", htvec_of_init); + +#endif + +#ifdef CONFIG_ACPI + +struct irq_domain *htvec_acpi_init(struct irq_domain *parent, + struct acpi_madt_ht_pic *acpi_htvec) +{ + int i, ret; + int num_parents, parent_irq[8]; + struct fwnode_handle *domain_handle; + + if (!acpi_htvec) + return NULL; + + num_parents =3D HTVEC_MAX_PARENT_IRQ; + + domain_handle =3D irq_domain_alloc_fwnode((phys_addr_t *)acpi_htvec); + if (!domain_handle) { + pr_err("Unable to allocate domain handle\n"); + return NULL; + } + + /* Interrupt may come from any of the 8 interrupt lines */ + for (i =3D 0; i < HTVEC_MAX_PARENT_IRQ; i++) + parent_irq[i] =3D irq_create_mapping(parent, acpi_htvec->cascade[i]); + + ret =3D htvec_init(acpi_htvec->address, acpi_htvec->size, + num_parents, parent_irq, domain_handle); + if (ret < 0) + return NULL; + + return irq_find_matching_fwnode(domain_handle, DOMAIN_BUS_ANY); +} + +#endif --=20 2.27.0 From nobody Mon Jun 15 10:19:04 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 32E0DC433EF for ; Sat, 30 Apr 2022 08:59:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1377992AbiD3JDC (ORCPT ); Sat, 30 Apr 2022 05:03:02 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47574 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1351842AbiD3JDA (ORCPT ); Sat, 30 Apr 2022 05:03:00 -0400 Received: from sin.source.kernel.org (sin.source.kernel.org [145.40.73.55]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6B4491CFDA for ; Sat, 30 Apr 2022 01:59:39 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by sin.source.kernel.org (Postfix) with ESMTPS id C8E5ECE1FC4 for ; Sat, 30 Apr 2022 08:59:37 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id DDA31C385A7; Sat, 30 Apr 2022 08:59:33 +0000 (UTC) From: Huacai Chen To: Thomas Gleixner , Marc Zyngier Cc: linux-kernel@vger.kernel.org, Xuefeng Li , Huacai Chen , Jiaxun Yang , Huacai Chen Subject: [PATCH V11 06/10] irqchip/loongson-htvec: Add suspend/resume support Date: Sat, 30 Apr 2022 16:53:40 +0800 Message-Id: <20220430085344.3127346-7-chenhuacai@loongson.cn> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20220430085344.3127346-1-chenhuacai@loongson.cn> References: <20220430085344.3127346-1-chenhuacai@loongson.cn> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Add suspend/resume support for HTVEC irqchip, which is needed for suspend/hibernation. Signed-off-by: Huacai Chen --- drivers/irqchip/irq-loongson-htvec.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/irqchip/irq-loongson-htvec.c b/drivers/irqchip/irq-loo= ngson-htvec.c index e304374a018c..8cd1c15c1f07 100644 --- a/drivers/irqchip/irq-loongson-htvec.c +++ b/drivers/irqchip/irq-loongson-htvec.c @@ -16,6 +16,7 @@ #include #include #include +#include =20 /* Registers */ #define HTVEC_EN_OFF 0x20 @@ -30,6 +31,7 @@ struct htvec { struct irq_domain *htvec_domain; raw_spinlock_t htvec_lock; struct fwnode_handle *domain_handle; + u32 saved_vec_en[HTVEC_MAX_PARENT_IRQ]; }; =20 static struct htvec *htvec_priv; @@ -157,6 +159,29 @@ static void htvec_reset(struct htvec *priv) } } =20 +static int htvec_suspend(void) +{ + int i; + + for (i =3D 0; i < htvec_priv->num_parents; i++) + htvec_priv->saved_vec_en[i] =3D readl(htvec_priv->base + HTVEC_EN_OFF + = 4 * i); + + return 0; +} + +static void htvec_resume(void) +{ + int i; + + for (i =3D 0; i < htvec_priv->num_parents; i++) + writel(htvec_priv->saved_vec_en[i], htvec_priv->base + HTVEC_EN_OFF + 4 = * i); +} + +static struct syscore_ops htvec_syscore_ops =3D { + .suspend =3D htvec_suspend, + .resume =3D htvec_resume, +}; + static int htvec_init(phys_addr_t addr, unsigned long size, int num_parents, int parent_irq[], struct fwnode_handle *domain_handle) { @@ -190,6 +215,8 @@ static int htvec_init(phys_addr_t addr, unsigned long s= ize, =20 htvec_priv =3D priv; =20 + register_syscore_ops(&htvec_syscore_ops); + return 0; =20 iounmap_base: --=20 2.27.0 From nobody Mon Jun 15 10:19:04 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E1DB9C433EF for ; Sat, 30 Apr 2022 09:00:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1378233AbiD3JDd (ORCPT ); Sat, 30 Apr 2022 05:03:33 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48232 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237624AbiD3JDb (ORCPT ); Sat, 30 Apr 2022 05:03:31 -0400 Received: from ams.source.kernel.org (ams.source.kernel.org [145.40.68.75]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0286F26C4 for ; Sat, 30 Apr 2022 02:00:10 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ams.source.kernel.org (Postfix) with ESMTPS id AA8F9B80FA1 for ; Sat, 30 Apr 2022 09:00:08 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 52416C385A7; Sat, 30 Apr 2022 09:00:05 +0000 (UTC) From: Huacai Chen To: Thomas Gleixner , Marc Zyngier Cc: linux-kernel@vger.kernel.org, Xuefeng Li , Huacai Chen , Jiaxun Yang , Huacai Chen Subject: [PATCH V11 07/10] irqchip/loongson-liointc: Add ACPI init support Date: Sat, 30 Apr 2022 16:53:41 +0800 Message-Id: <20220430085344.3127346-8-chenhuacai@loongson.cn> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20220430085344.3127346-1-chenhuacai@loongson.cn> References: <20220430085344.3127346-1-chenhuacai@loongson.cn> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" We are preparing to add new Loongson (based on LoongArch, not compatible with old MIPS-based Loongson) support. LoongArch use ACPI other than DT as its boot protocol, so add ACPI init support. LIOINTC stands for "Legacy I/O Interrupts" that described in Section 11.1 of "Loongson 3A5000 Processor Reference Manual". For more information please refer Documentation/loongarch/irq-chip-model.rst. Signed-off-by: Huacai Chen --- drivers/irqchip/irq-loongson-liointc.c | 204 +++++++++++++++---------- 1 file changed, 125 insertions(+), 79 deletions(-) diff --git a/drivers/irqchip/irq-loongson-liointc.c b/drivers/irqchip/irq-l= oongson-liointc.c index 649c58391618..ff3cb5b05710 100644 --- a/drivers/irqchip/irq-loongson-liointc.c +++ b/drivers/irqchip/irq-loongson-liointc.c @@ -16,10 +16,14 @@ #include #include =20 +#ifdef CONFIG_MIPS #include +#else +#include +#endif =20 #define LIOINTC_CHIP_IRQ 32 -#define LIOINTC_NUM_PARENT 4 +#define LIOINTC_NUM_PARENT 4 #define LIOINTC_NUM_CORES 4 =20 #define LIOINTC_INTC_CHIP_START 0x20 @@ -41,6 +45,7 @@ struct liointc_handler_data { }; =20 struct liointc_priv { + struct fwnode_handle *domain_handle; struct irq_chip_generic *gc; struct liointc_handler_data handler[LIOINTC_NUM_PARENT]; void __iomem *core_isr[LIOINTC_NUM_CORES]; @@ -53,7 +58,7 @@ static void liointc_chained_handle_irq(struct irq_desc *d= esc) struct liointc_handler_data *handler =3D irq_desc_get_handler_data(desc); struct irq_chip *chip =3D irq_desc_get_chip(desc); struct irq_chip_generic *gc =3D handler->priv->gc; - int core =3D get_ebase_cpunum() % LIOINTC_NUM_CORES; + int core =3D cpu_logical_map(smp_processor_id()) % LIOINTC_NUM_CORES; u32 pending; =20 chained_irq_enter(chip, desc); @@ -143,97 +148,62 @@ static void liointc_resume(struct irq_chip_generic *g= c) irq_gc_unlock_irqrestore(gc, flags); } =20 -static const char * const parent_names[] =3D {"int0", "int1", "int2", "int= 3"}; -static const char * const core_reg_names[] =3D {"isr0", "isr1", "isr2", "i= sr3"}; +static int parent_irq[LIOINTC_NUM_PARENT]; +static u32 parent_int_map[LIOINTC_NUM_PARENT]; +static const char *const parent_names[] =3D {"int0", "int1", "int2", "int3= "}; +static const char *const core_reg_names[] =3D {"isr0", "isr1", "isr2", "is= r3"}; =20 -static void __iomem *liointc_get_reg_byname(struct device_node *node, - const char *name) -{ - int index =3D of_property_match_string(node, "reg-names", name); - - if (index < 0) - return NULL; - - return of_iomap(node, index); -} - -static int __init liointc_of_init(struct device_node *node, - struct device_node *parent) +static int liointc_init(phys_addr_t addr, unsigned long size, int revision, + struct fwnode_handle *domain_handle, struct device_node *node) { + int i, err; + void __iomem *base; + struct irq_chip_type *ct; struct irq_chip_generic *gc; struct irq_domain *domain; - struct irq_chip_type *ct; struct liointc_priv *priv; - void __iomem *base; - u32 of_parent_int_map[LIOINTC_NUM_PARENT]; - int parent_irq[LIOINTC_NUM_PARENT]; - bool have_parent =3D FALSE; - int sz, i, err =3D 0; =20 priv =3D kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; =20 - if (of_device_is_compatible(node, "loongson,liointc-2.0")) { - base =3D liointc_get_reg_byname(node, "main"); - if (!base) { - err =3D -ENODEV; - goto out_free_priv; - } + base =3D ioremap(addr, size); + if (!base) + goto out_free_priv; =20 - for (i =3D 0; i < LIOINTC_NUM_CORES; i++) - priv->core_isr[i] =3D liointc_get_reg_byname(node, core_reg_names[i]); - if (!priv->core_isr[0]) { - err =3D -ENODEV; - goto out_iounmap_base; - } - } else { - base =3D of_iomap(node, 0); - if (!base) { - err =3D -ENODEV; - goto out_free_priv; - } + priv->domain_handle =3D domain_handle; =20 - for (i =3D 0; i < LIOINTC_NUM_CORES; i++) - priv->core_isr[i] =3D base + LIOINTC_REG_INTC_STATUS; - } + for (i =3D 0; i < LIOINTC_NUM_CORES; i++) + priv->core_isr[i] =3D base + LIOINTC_REG_INTC_STATUS; =20 - for (i =3D 0; i < LIOINTC_NUM_PARENT; i++) { - parent_irq[i] =3D of_irq_get_byname(node, parent_names[i]); - if (parent_irq[i] > 0) - have_parent =3D TRUE; - } - if (!have_parent) { - err =3D -ENODEV; - goto out_iounmap_isr; - } + for (i =3D 0; i < LIOINTC_NUM_PARENT; i++) + priv->handler[i].parent_int_map =3D parent_int_map[i]; =20 - sz =3D of_property_read_variable_u32_array(node, - "loongson,parent_int_map", - &of_parent_int_map[0], - LIOINTC_NUM_PARENT, - LIOINTC_NUM_PARENT); - if (sz < 4) { - pr_err("loongson-liointc: No parent_int_map\n"); - err =3D -ENODEV; - goto out_iounmap_isr; - } +#ifdef CONFIG_OF + if (revision > 1) { + for (i =3D 0; i < LIOINTC_NUM_CORES; i++) { + int index =3D of_property_match_string(node, + "reg-names", core_reg_names[i]); =20 - for (i =3D 0; i < LIOINTC_NUM_PARENT; i++) - priv->handler[i].parent_int_map =3D of_parent_int_map[i]; + if (index < 0) + return -EINVAL; + + priv->core_isr[i] =3D of_iomap(node, index); + } + } +#endif =20 /* Setup IRQ domain */ - domain =3D irq_domain_add_linear(node, 32, + domain =3D irq_domain_create_linear(domain_handle, LIOINTC_CHIP_IRQ, &irq_generic_chip_ops, priv); if (!domain) { pr_err("loongson-liointc: cannot add IRQ domain\n"); - err =3D -EINVAL; - goto out_iounmap_isr; + goto out_iounmap; } =20 - err =3D irq_alloc_domain_generic_chips(domain, 32, 1, - node->full_name, handle_level_irq, - IRQ_NOPROBE, 0, 0); + err =3D irq_alloc_domain_generic_chips(domain, LIOINTC_CHIP_IRQ, 1, + (node ? node->full_name : "LIOINTC"), + handle_level_irq, 0, IRQ_NOPROBE, 0); if (err) { pr_err("loongson-liointc: unable to register IRQ domain\n"); goto out_free_domain; @@ -293,20 +263,96 @@ static int __init liointc_of_init(struct device_node = *node, =20 out_free_domain: irq_domain_remove(domain); -out_iounmap_isr: - for (i =3D 0; i < LIOINTC_NUM_CORES; i++) { - if (!priv->core_isr[i]) - continue; - iounmap(priv->core_isr[i]); - } -out_iounmap_base: +out_iounmap: iounmap(base); out_free_priv: kfree(priv); =20 - return err; + return -EINVAL; +} + +#ifdef CONFIG_OF + +static int __init liointc_of_init(struct device_node *node, + struct device_node *parent) +{ + bool have_parent =3D FALSE; + int sz, i, index, revision, err =3D 0; + struct resource res; + + if (!of_device_is_compatible(node, "loongson,liointc-2.0")) { + index =3D 0; + revision =3D 1; + } else { + index =3D of_property_match_string(node, "reg-names", "main"); + revision =3D 2; + } + + if (of_address_to_resource(node, index, &res)) + return -EINVAL; + + for (i =3D 0; i < LIOINTC_NUM_PARENT; i++) { + parent_irq[i] =3D of_irq_get_byname(node, parent_names[i]); + if (parent_irq[i] > 0) + have_parent =3D TRUE; + } + if (!have_parent) + return -ENODEV; + + sz =3D of_property_read_variable_u32_array(node, + "loongson,parent_int_map", + &parent_int_map[0], + LIOINTC_NUM_PARENT, + LIOINTC_NUM_PARENT); + if (sz < 4) { + pr_err("loongson-liointc: No parent_int_map\n"); + return -ENODEV; + } + + err =3D liointc_init(res.start, resource_size(&res), + revision, of_node_to_fwnode(node), node); + if (err < 0) + return err; + + return 0; } =20 IRQCHIP_DECLARE(loongson_liointc_1_0, "loongson,liointc-1.0", liointc_of_i= nit); IRQCHIP_DECLARE(loongson_liointc_1_0a, "loongson,liointc-1.0a", liointc_of= _init); IRQCHIP_DECLARE(loongson_liointc_2_0, "loongson,liointc-2.0", liointc_of_i= nit); + +#endif + +#ifdef CONFIG_ACPI + +struct irq_domain *liointc_acpi_init(struct irq_domain *parent, + struct acpi_madt_lio_pic *acpi_liointc) +{ + int ret; + struct fwnode_handle *domain_handle; + + if (!acpi_liointc) + return NULL; + + parent_int_map[0] =3D acpi_liointc->cascade_map[0]; + parent_int_map[1] =3D acpi_liointc->cascade_map[1]; + + parent_irq[0] =3D irq_create_mapping(parent, acpi_liointc->cascade[0]); + if (!cpu_has_extioi) + parent_irq[1] =3D irq_create_mapping(parent, acpi_liointc->cascade[1]); + + domain_handle =3D irq_domain_alloc_fwnode((phys_addr_t *)acpi_liointc); + if (!domain_handle) { + pr_err("Unable to allocate domain handle\n"); + return NULL; + } + + ret =3D liointc_init(acpi_liointc->address, acpi_liointc->size, + 1, domain_handle, NULL); + if (ret < 0) + return NULL; + + return irq_find_matching_fwnode(domain_handle, DOMAIN_BUS_ANY); +} + +#endif --=20 2.27.0 From nobody Mon Jun 15 10:19:04 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4FB31C433F5 for ; Sat, 30 Apr 2022 09:01:28 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1379780AbiD3JEr (ORCPT ); Sat, 30 Apr 2022 05:04:47 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53234 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238453AbiD3JEo (ORCPT ); Sat, 30 Apr 2022 05:04:44 -0400 Received: from ams.source.kernel.org (ams.source.kernel.org [145.40.68.75]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 203923134B for ; Sat, 30 Apr 2022 02:01:23 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ams.source.kernel.org (Postfix) with ESMTPS id BB45EB81A53 for ; Sat, 30 Apr 2022 09:01:21 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 55964C385A7; Sat, 30 Apr 2022 09:01:18 +0000 (UTC) From: Huacai Chen To: Thomas Gleixner , Marc Zyngier Cc: linux-kernel@vger.kernel.org, Xuefeng Li , Huacai Chen , Jiaxun Yang , Huacai Chen Subject: [PATCH V11 08/10] irqchip: Add LoongArch CPU interrupt controller support Date: Sat, 30 Apr 2022 16:53:42 +0800 Message-Id: <20220430085344.3127346-9-chenhuacai@loongson.cn> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20220430085344.3127346-1-chenhuacai@loongson.cn> References: <20220430085344.3127346-1-chenhuacai@loongson.cn> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" We are preparing to add new Loongson (based on LoongArch, not compatible with old MIPS-based Loongson) support. This patch add the LoongArch CPU interrupt controller support. LoongArch CPUINTC stands for CSR.ECFG/CSR.ESTAT and related interrupt controller that described in Section 7.4 of "LoongArch Reference Manual, Vol 1". For more information please refer Documentation/loongarch/irq- chip-model.rst. LoongArch CPUINTC has 13 interrupt sources: SWI0~1, HWI0~7, IPI, TI (Timer) and PCOV (PMC). IRQ mappings of HWI0~7 are configurable (can be created from DT/ACPI), but IPI, TI (Timer) and PCOV (PMC) are hardcoded bits, so we define get_xxx_irq() for them. Signed-off-by: Huacai Chen --- drivers/irqchip/Kconfig | 10 ++++ drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-loongarch-cpu.c | 92 +++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100644 drivers/irqchip/irq-loongarch-cpu.c diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 39d6be2764a2..a596ee732021 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -545,6 +545,16 @@ config EXYNOS_IRQ_COMBINER Say yes here to add support for the IRQ combiner devices embedded in Samsung Exynos chips. =20 +config IRQ_LOONGARCH_CPU + bool + select GENERIC_IRQ_CHIP + select IRQ_DOMAIN + select GENERIC_IRQ_EFFECTIVE_AFF_MASK + help + Support for the LoongArch CPU Interrupt Controller. For details of + irq chip hierarchy on LoongArch platforms please read the document + Documentation/loongarch/irq-chip-model.rst. + config LOONGSON_LIOINTC bool "Loongson Local I/O Interrupt Controller" depends on MACH_LOONGSON64 diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 160a1d8ceaa9..7c7598454e17 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -105,6 +105,7 @@ obj-$(CONFIG_LS1X_IRQ) +=3D irq-ls1x.o obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) +=3D irq-ti-sci-intr.o obj-$(CONFIG_TI_SCI_INTA_IRQCHIP) +=3D irq-ti-sci-inta.o obj-$(CONFIG_TI_PRUSS_INTC) +=3D irq-pruss-intc.o +obj-$(CONFIG_IRQ_LOONGARCH_CPU) +=3D irq-loongarch-cpu.o obj-$(CONFIG_LOONGSON_LIOINTC) +=3D irq-loongson-liointc.o obj-$(CONFIG_LOONGSON_HTPIC) +=3D irq-loongson-htpic.o obj-$(CONFIG_LOONGSON_HTVEC) +=3D irq-loongson-htvec.o diff --git a/drivers/irqchip/irq-loongarch-cpu.c b/drivers/irqchip/irq-loon= garch-cpu.c new file mode 100644 index 000000000000..6f7e66734af3 --- /dev/null +++ b/drivers/irqchip/irq-loongarch-cpu.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +static struct irq_domain *irq_domain; + +static void mask_loongarch_irq(struct irq_data *d) +{ + clear_csr_ecfg(ECFGF(d->hwirq)); +} + +static void unmask_loongarch_irq(struct irq_data *d) +{ + set_csr_ecfg(ECFGF(d->hwirq)); +} + +static struct irq_chip cpu_irq_controller =3D { + .name =3D "LoongArch", + .irq_mask =3D mask_loongarch_irq, + .irq_unmask =3D unmask_loongarch_irq, +}; + +static void handle_cpu_irq(struct pt_regs *regs) +{ + int hwirq; + unsigned int estat =3D read_csr_estat() & CSR_ESTAT_IS; + + while ((hwirq =3D ffs(estat))) { + estat &=3D ~BIT(hwirq - 1); + generic_handle_domain_irq(irq_domain, hwirq - 1); + } +} + +int get_ipi_irq(void) +{ + return irq_create_mapping(irq_domain, EXCCODE_IPI - EXCCODE_INT_START); +} + +int get_pmc_irq(void) +{ + return irq_create_mapping(irq_domain, EXCCODE_PMC - EXCCODE_INT_START); +} + +int get_timer_irq(void) +{ + return irq_create_mapping(irq_domain, EXCCODE_TIMER - EXCCODE_INT_START); +} + +static int loongarch_cpu_intc_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_noprobe(irq); + irq_set_chip_and_handler(irq, &cpu_irq_controller, handle_percpu_irq); + + return 0; +} + +static const struct irq_domain_ops loongarch_cpu_intc_irq_domain_ops =3D { + .map =3D loongarch_cpu_intc_map, + .xlate =3D irq_domain_xlate_onecell, +}; + +struct irq_domain * __init loongarch_cpu_irq_init(void) +{ + struct fwnode_handle *domain_handle; + + /* Mask interrupts. */ + clear_csr_ecfg(ECFG0_IM); + clear_csr_estat(ESTATF_IP); + + domain_handle =3D irq_domain_alloc_fwnode(NULL); + irq_domain =3D irq_domain_create_linear(domain_handle, EXCCODE_INT_NUM, + &loongarch_cpu_intc_irq_domain_ops, NULL); + + if (!irq_domain) + panic("Failed to add irqdomain for LoongArch CPU"); + + set_handle_irq(&handle_cpu_irq); + + return irq_domain; +} --=20 2.27.0 From nobody Mon Jun 15 10:19:04 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 028D1C433F5 for ; Sat, 30 Apr 2022 09:02:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1378320AbiD3JFa (ORCPT ); Sat, 30 Apr 2022 05:05:30 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55776 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1358940AbiD3JF0 (ORCPT ); Sat, 30 Apr 2022 05:05:26 -0400 Received: from ams.source.kernel.org (ams.source.kernel.org [145.40.68.75]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 44290419AC for ; Sat, 30 Apr 2022 02:02:03 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ams.source.kernel.org (Postfix) with ESMTPS id B9FBDB81A53 for ; Sat, 30 Apr 2022 09:02:01 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id EAC16C385A7; Sat, 30 Apr 2022 09:01:57 +0000 (UTC) From: Huacai Chen To: Thomas Gleixner , Marc Zyngier Cc: linux-kernel@vger.kernel.org, Xuefeng Li , Huacai Chen , Jiaxun Yang , Huacai Chen Subject: [PATCH V11 09/10] irqchip: Add Loongson Extended I/O interrupt controller support Date: Sat, 30 Apr 2022 16:53:43 +0800 Message-Id: <20220430085344.3127346-10-chenhuacai@loongson.cn> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20220430085344.3127346-1-chenhuacai@loongson.cn> References: <20220430085344.3127346-1-chenhuacai@loongson.cn> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" We are preparing to add new Loongson (based on LoongArch, not compatible with old MIPS-based Loongson) support. This patch add Loongson Extended I/O CPU interrupt controller support. EIOINTC stands for "Extended I/O Interrupts" that described in Section 11.2 of "Loongson 3A5000 Processor Reference Manual". For more information please refer Documentation/loongarch/irq-chip-model.rst. Loongson-3A5000 has 4 cores per NUMA node, and each NUMA node has an EIOINTC; while Loongson-3C5000 has 16 cores per NUMA node, and each NUMA node has 4 EIOINTCs. In other words, 16 cores of one NUMA node in Loongson-3C5000 are organized in 4 groups, each group connects to an EIOINTC. We call the "group" here as an EIOINTC node, so each EIOINTC node always includes 4 cores (both in Loongson-3A5000 and Loongson- 3C5000). Signed-off-by: Huacai Chen --- drivers/irqchip/Kconfig | 10 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-loongson-eiointc.c | 379 +++++++++++++++++++++++++ include/linux/cpuhotplug.h | 1 + 4 files changed, 391 insertions(+) create mode 100644 drivers/irqchip/irq-loongson-eiointc.c diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index a596ee732021..eea0c15562cf 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -564,6 +564,16 @@ config LOONGSON_LIOINTC help Support for the Loongson Local I/O Interrupt Controller. =20 +config LOONGSON_EIOINTC + bool "Loongson Extend I/O Interrupt Controller" + depends on LOONGARCH + depends on MACH_LOONGSON64 + default MACH_LOONGSON64 + select IRQ_DOMAIN_HIERARCHY + select GENERIC_IRQ_CHIP + help + Support for the Loongson3 Extend I/O Interrupt Vector Controller. + config LOONGSON_HTPIC bool "Loongson3 HyperTransport PIC Controller" depends on (MACH_LOONGSON64 && MIPS) diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 7c7598454e17..e5e546a8195c 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -107,6 +107,7 @@ obj-$(CONFIG_TI_SCI_INTA_IRQCHIP) +=3D irq-ti-sci-inta.o obj-$(CONFIG_TI_PRUSS_INTC) +=3D irq-pruss-intc.o obj-$(CONFIG_IRQ_LOONGARCH_CPU) +=3D irq-loongarch-cpu.o obj-$(CONFIG_LOONGSON_LIOINTC) +=3D irq-loongson-liointc.o +obj-$(CONFIG_LOONGSON_EIOINTC) +=3D irq-loongson-eiointc.o obj-$(CONFIG_LOONGSON_HTPIC) +=3D irq-loongson-htpic.o obj-$(CONFIG_LOONGSON_HTVEC) +=3D irq-loongson-htvec.o obj-$(CONFIG_LOONGSON_PCH_PIC) +=3D irq-loongson-pch-pic.o diff --git a/drivers/irqchip/irq-loongson-eiointc.c b/drivers/irqchip/irq-l= oongson-eiointc.c new file mode 100644 index 000000000000..ecdd983710b6 --- /dev/null +++ b/drivers/irqchip/irq-loongson-eiointc.c @@ -0,0 +1,379 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Loongson Extend I/O Interrupt Controller support + * + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#define pr_fmt(fmt) "eiointc: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define EIOINTC_REG_NODEMAP 0x14a0 +#define EIOINTC_REG_IPMAP 0x14c0 +#define EIOINTC_REG_ENABLE 0x1600 +#define EIOINTC_REG_BOUNCE 0x1680 +#define EIOINTC_REG_ISR 0x1800 +#define EIOINTC_REG_ROUTE 0x1c00 + +#define VEC_REG_COUNT 4 +#define VEC_COUNT_PER_REG 64 +#define VEC_COUNT (VEC_REG_COUNT * VEC_COUNT_PER_REG) +#define VEC_REG_IDX(irq_id) ((irq_id) / VEC_COUNT_PER_REG) +#define VEC_REG_BIT(irq_id) ((irq_id) % VEC_COUNT_PER_REG) +#define EIOINTC_ALL_ENABLE 0xffffffff + +#define MAX_EIO_NODES (NR_CPUS / CORES_PER_EIO_NODE) + +static int nr_pics; + +struct eiointc_priv { + u32 node; + nodemask_t node_map; + cpumask_t cpuspan_map; + struct fwnode_handle *domain_handle; + struct irq_domain *eiointc_domain; +}; + +static struct eiointc_priv *eiointc_priv[2]; + +int eiointc_get_node(int id) +{ + return eiointc_priv[id]->node; +} + +static int cpu_to_eio_node(int cpu) +{ + return cpu_logical_map(cpu) / CORES_PER_EIO_NODE; +} + +static void eiointc_set_irq_route(int pos, unsigned int cpu, unsigned int = mnode, nodemask_t *node_map) +{ + int i, node, cpu_node, route_node; + unsigned char coremap[MAX_EIO_NODES]; + uint32_t pos_off, data, data_byte, data_mask; + + pos_off =3D pos & ~3; + data_byte =3D pos & 3; + data_mask =3D ~BIT_MASK(data_byte) & 0xf; + + memset(coremap, 0, sizeof(unsigned char) * MAX_EIO_NODES); + + /* Calculate node and coremap of target irq */ + cpu_node =3D cpu_logical_map(cpu) / CORES_PER_EIO_NODE; + coremap[cpu_node] |=3D BIT(cpu_logical_map(cpu) % CORES_PER_EIO_NODE); + + for_each_online_cpu(i) { + node =3D cpu_to_eio_node(i); + if (!node_isset(node, *node_map)) + continue; + + /* EIO node 0 is in charge of inter-node interrupt dispatch */ + route_node =3D (node =3D=3D mnode) ? cpu_node : node; + data =3D ((coremap[node] | (route_node << 4)) << (data_byte * 8)); + csr_any_send(EIOINTC_REG_ROUTE + pos_off, data, data_mask, node * CORES_= PER_EIO_NODE); + } +} + +static DEFINE_RAW_SPINLOCK(affinity_lock); + +static int eiointc_set_irq_affinity(struct irq_data *d, const struct cpuma= sk *affinity, bool force) +{ + unsigned int cpu; + unsigned long flags; + uint32_t vector, regaddr; + struct cpumask intersect_affinity; + struct eiointc_priv *priv =3D d->domain->host_data; + + if (!IS_ENABLED(CONFIG_SMP)) + return -EPERM; + + raw_spin_lock_irqsave(&affinity_lock, flags); + + cpumask_and(&intersect_affinity, affinity, cpu_online_mask); + cpumask_and(&intersect_affinity, &intersect_affinity, &priv->cpuspan_map); + + if (cpumask_empty(&intersect_affinity)) { + raw_spin_unlock_irqrestore(&affinity_lock, flags); + return -EINVAL; + } + cpu =3D cpumask_first(&intersect_affinity); + + if (!d->parent_data) + vector =3D d->hwirq; + else + vector =3D d->parent_data->hwirq; + + regaddr =3D EIOINTC_REG_ENABLE + ((vector >> 5) << 2); + + /* Mask target vector */ + csr_any_send(regaddr, EIOINTC_ALL_ENABLE & (~BIT(vector & 0x1F)), + 0x0, priv->node * CORES_PER_EIO_NODE); + + /* Set route for target vector */ + eiointc_set_irq_route(vector, cpu, priv->node, &priv->node_map); + + /* Unmask target vector */ + csr_any_send(regaddr, EIOINTC_ALL_ENABLE, + 0x0, priv->node * CORES_PER_EIO_NODE); + + irq_data_update_effective_affinity(d, cpumask_of(cpu)); + + raw_spin_unlock_irqrestore(&affinity_lock, flags); + + return IRQ_SET_MASK_OK; +} + +static int eiointc_index(int node) +{ + int i; + + for (i =3D 0; i < nr_pics; i++) { + if (node_isset(node, eiointc_priv[i]->node_map)) + return i; + } + + return -1; +} + +static int eiointc_router_init(unsigned int cpu) +{ + int i, bit; + int node =3D cpu_to_eio_node(cpu); + int index =3D eiointc_index(node); + uint32_t data; + + if (index < 0) { + pr_err("Error: invalid nodemap!\n"); + return -1; + } + + if ((cpu_logical_map(cpu) % CORES_PER_EIO_NODE) =3D=3D 0) { + eiointc_enable(); + + for (i =3D 0; i < VEC_COUNT / 32; i++) { + data =3D (((1 << (i * 2 + 1)) << 16) | (1 << (i * 2))); + iocsr_writel(data, EIOINTC_REG_NODEMAP + i * 4); + } + + for (i =3D 0; i < VEC_COUNT / 32 / 4; i++) { + bit =3D BIT(1 + index); /* Route to IP[1 + index] */ + data =3D bit | (bit << 8) | (bit << 16) | (bit << 24); + iocsr_writel(data, EIOINTC_REG_IPMAP + i * 4); + } + + for (i =3D 0; i < VEC_COUNT / 4; i++) { + /* Route to Node-0 Core-0 */ + if (index =3D=3D 0) + bit =3D BIT(cpu_logical_map(0)); + else + bit =3D (eiointc_priv[index]->node << 4) | 1; + + data =3D bit | (bit << 8) | (bit << 16) | (bit << 24); + iocsr_writel(data, EIOINTC_REG_ROUTE + i * 4); + } + + for (i =3D 0; i < VEC_COUNT / 32; i++) { + data =3D 0xffffffff; + iocsr_writel(data, EIOINTC_REG_ENABLE + i * 4); + iocsr_writel(data, EIOINTC_REG_BOUNCE + i * 4); + } + } + + return 0; +} + +static void eiointc_irq_dispatch(struct irq_desc *desc) +{ + int i; + u64 pending; + bool handled =3D false; + struct irq_chip *chip =3D irq_desc_get_chip(desc); + struct eiointc_priv *priv =3D irq_desc_get_handler_data(desc); + + chained_irq_enter(chip, desc); + + for (i =3D 0; i < VEC_REG_COUNT; i++) { + pending =3D iocsr_readq(EIOINTC_REG_ISR + (i << 3)); + iocsr_writeq(pending, EIOINTC_REG_ISR + (i << 3)); + while (pending) { + int bit =3D __ffs(pending); + int irq =3D bit + VEC_COUNT_PER_REG * i; + + generic_handle_domain_irq(priv->eiointc_domain, irq); + pending &=3D ~BIT(bit); + handled =3D true; + } + } + + if (!handled) + spurious_interrupt(); + + chained_irq_exit(chip, desc); +} + +static void eiointc_ack_irq(struct irq_data *d) +{ + if (d->parent_data) + irq_chip_ack_parent(d); +} + +static void eiointc_mask_irq(struct irq_data *d) +{ + if (d->parent_data) + irq_chip_mask_parent(d); +} + +static void eiointc_unmask_irq(struct irq_data *d) +{ + if (d->parent_data) + irq_chip_unmask_parent(d); +} + +static struct irq_chip eiointc_irq_chip =3D { + .name =3D "EIOINTC", + .irq_ack =3D eiointc_ack_irq, + .irq_mask =3D eiointc_mask_irq, + .irq_unmask =3D eiointc_unmask_irq, + .irq_set_affinity =3D eiointc_set_irq_affinity, +}; + +static int eiointc_domain_alloc(struct irq_domain *domain, unsigned int vi= rq, + unsigned int nr_irqs, void *arg) +{ + int ret; + unsigned int i, type; + unsigned long hwirq =3D 0; + struct eiointc *priv =3D domain->host_data; + + ret =3D irq_domain_translate_onecell(domain, arg, &hwirq, &type); + if (ret < 0) + return -EINVAL; + + if (hwirq >=3D IOCSR_EXTIOI_VECTOR_NUM) + return -EINVAL; + + for (i =3D 0; i < nr_irqs; i++) { + irq_domain_set_info(domain, virq + i, hwirq + i, &eiointc_irq_chip, + priv, handle_edge_irq, NULL, NULL); + } + + return 0; +} + +static void eiointc_domain_free(struct irq_domain *domain, unsigned int vi= rq, + unsigned int nr_irqs) +{ + int i; + + for (i =3D 0; i < nr_irqs; i++) { + struct irq_data *d =3D irq_domain_get_irq_data(domain, virq + i); + + irq_set_handler(virq + i, NULL); + irq_domain_reset_irq_data(d); + } +} + +static const struct irq_domain_ops eiointc_domain_ops =3D { + .translate =3D irq_domain_translate_onecell, + .alloc =3D eiointc_domain_alloc, + .free =3D eiointc_domain_free, +}; + +static int eiointc_suspend(void) +{ + return 0; +} + +static void eiointc_resume(void) +{ + int i, j; + struct irq_desc *desc; + struct irq_data *irq_data; + + eiointc_router_init(0); + + for (i =3D 0; i < nr_pics; i++) { + for (j =3D 0; j < VEC_COUNT; j++) { + desc =3D irq_resolve_mapping(eiointc_priv[i]->eiointc_domain, j); + if (desc && desc->handle_irq && desc->handle_irq !=3D handle_bad_irq) { + irq_data =3D &desc->irq_data; + eiointc_set_irq_affinity(irq_data, irq_data->common->affinity, 0); + } + } + } +} + +static struct syscore_ops eiointc_syscore_ops =3D { + .suspend =3D eiointc_suspend, + .resume =3D eiointc_resume, +}; + +struct irq_domain *eiointc_acpi_init(struct irq_domain *parent, + struct acpi_madt_eio_pic *acpi_eiointc) +{ + int i, parent_irq; + unsigned long node_map; + struct eiointc_priv *priv; + + if (!acpi_eiointc) + return NULL; + + priv =3D kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return NULL; + + priv->domain_handle =3D irq_domain_alloc_fwnode((phys_addr_t *)acpi_eioin= tc); + if (!priv->domain_handle) { + pr_err("Unable to allocate domain handle\n"); + goto out_free_priv; + } + + priv->node =3D acpi_eiointc->node; + node_map =3D 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 =3D irq_domain_create_linear(priv->domain_handle, VE= C_COUNT, + &eiointc_domain_ops, priv); + if (!priv->eiointc_domain) { + pr_err("loongson-eiointc: cannot add IRQ domain\n"); + goto out_free_priv; + } + + eiointc_priv[nr_pics++] =3D priv; + + eiointc_router_init(0); + + parent_irq =3D irq_create_mapping(parent, acpi_eiointc->cascade); + irq_set_chained_handler_and_data(parent_irq, eiointc_irq_dispatch, priv); + + register_syscore_ops(&eiointc_syscore_ops); + cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_LOONGARCH_STARTING, + "irqchip/loongarch/intc:starting", + eiointc_router_init, NULL); + + return irq_find_matching_fwnode(priv->domain_handle, DOMAIN_BUS_ANY); + +out_free_priv: + priv->domain_handle =3D NULL; + kfree(priv); + + return NULL; +} diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h index 82e33137f917..2af7c6587875 100644 --- a/include/linux/cpuhotplug.h +++ b/include/linux/cpuhotplug.h @@ -150,6 +150,7 @@ enum cpuhp_state { CPUHP_AP_IRQ_BCM2836_STARTING, CPUHP_AP_IRQ_MIPS_GIC_STARTING, CPUHP_AP_IRQ_RISCV_STARTING, + CPUHP_AP_IRQ_LOONGARCH_STARTING, CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING, CPUHP_AP_ARM_MVEBU_COHERENCY, CPUHP_AP_MICROCODE_LOADER, --=20 2.27.0 From nobody Mon Jun 15 10:19:04 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9AAD8C433EF for ; Sat, 30 Apr 2022 09:03:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1382391AbiD3JHL (ORCPT ); Sat, 30 Apr 2022 05:07:11 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60050 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236358AbiD3JHJ (ORCPT ); Sat, 30 Apr 2022 05:07:09 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [IPv6:2604:1380:4641:c500::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1C3A76339D for ; Sat, 30 Apr 2022 02:03:48 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id A9C9660DE0 for ; Sat, 30 Apr 2022 09:03:47 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id D0A71C385A7; Sat, 30 Apr 2022 09:03:44 +0000 (UTC) From: Huacai Chen To: Thomas Gleixner , Marc Zyngier Cc: linux-kernel@vger.kernel.org, Xuefeng Li , Huacai Chen , Jiaxun Yang , Huacai Chen Subject: [PATCH V11 10/10] irqchip: Add Loongson PCH LPC controller support Date: Sat, 30 Apr 2022 16:53:44 +0800 Message-Id: <20220430085344.3127346-11-chenhuacai@loongson.cn> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20220430085344.3127346-1-chenhuacai@loongson.cn> References: <20220430085344.3127346-1-chenhuacai@loongson.cn> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" We are preparing to add new Loongson (based on LoongArch, not compatible with old MIPS-based Loongson) support. This patch add Loongson PCH LPC interrupt controller support. PCH-LPC stands for "LPC Interrupts" that described in Section 24.3 of "Loongson 7A1000 Bridge User Manual". For more information please refer Documentation/loongarch/irq-chip-model.rst. Signed-off-by: Huacai Chen --- drivers/irqchip/Kconfig | 8 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-loongson-pch-lpc.c | 225 +++++++++++++++++++++++++ 3 files changed, 234 insertions(+) create mode 100644 drivers/irqchip/irq-loongson-pch-lpc.c diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index eea0c15562cf..c56a8dbb51cc 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -610,6 +610,14 @@ config LOONGSON_PCH_MSI help Support for the Loongson PCH MSI Controller. =20 +config LOONGSON_PCH_LPC + bool "Loongson PCH LPC Controller" + depends on MACH_LOONGSON64 + default (MACH_LOONGSON64 && LOONGARCH) + select IRQ_DOMAIN_HIERARCHY + help + Support for the Loongson PCH LPC Controller. + config MST_IRQ bool "MStar Interrupt Controller" depends on ARCH_MEDIATEK || ARCH_MSTARV7 || COMPILE_TEST diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index e5e546a8195c..71d9bc551d5b 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -112,6 +112,7 @@ obj-$(CONFIG_LOONGSON_HTPIC) +=3D irq-loongson-htpic.o obj-$(CONFIG_LOONGSON_HTVEC) +=3D irq-loongson-htvec.o obj-$(CONFIG_LOONGSON_PCH_PIC) +=3D irq-loongson-pch-pic.o obj-$(CONFIG_LOONGSON_PCH_MSI) +=3D irq-loongson-pch-msi.o +obj-$(CONFIG_LOONGSON_PCH_LPC) +=3D irq-loongson-pch-lpc.o obj-$(CONFIG_MST_IRQ) +=3D irq-mst-intc.o obj-$(CONFIG_SL28CPLD_INTC) +=3D irq-sl28cpld.o obj-$(CONFIG_MACH_REALTEK_RTL) +=3D irq-realtek-rtl.o diff --git a/drivers/irqchip/irq-loongson-pch-lpc.c b/drivers/irqchip/irq-l= oongson-pch-lpc.c new file mode 100644 index 000000000000..55ef72bfdfea --- /dev/null +++ b/drivers/irqchip/irq-loongson-pch-lpc.c @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Loongson LPC Interrupt Controller support + * + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#define pr_fmt(fmt) "lpc: " fmt + +#include +#include +#include +#include +#include +#include +#include + +/* Registers */ +#define LPC_INT_CTL 0x00 +#define LPC_INT_ENA 0x04 +#define LPC_INT_STS 0x08 +#define LPC_INT_CLR 0x0c +#define LPC_INT_POL 0x10 +#define LPC_COUNT 16 + +struct pch_lpc { + void __iomem *base; + struct irq_domain *lpc_domain; + struct fwnode_handle *domain_handle; + raw_spinlock_t lpc_lock; + u32 saved_reg_ctl; + u32 saved_reg_ena; + u32 saved_reg_pol; +}; + +static struct pch_lpc *pch_lpc_priv; + +static void ack_lpc_irq(struct irq_data *d) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&pch_lpc_priv->lpc_lock, flags); + writel(0x1 << d->irq, pch_lpc_priv->base + LPC_INT_CLR); + raw_spin_unlock_irqrestore(&pch_lpc_priv->lpc_lock, flags); +} +static void mask_lpc_irq(struct irq_data *d) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&pch_lpc_priv->lpc_lock, flags); + writel(readl(pch_lpc_priv->base + LPC_INT_ENA) & (~(0x1 << (d->irq))), + pch_lpc_priv->base + LPC_INT_ENA); + raw_spin_unlock_irqrestore(&pch_lpc_priv->lpc_lock, flags); +} + +static void mask_ack_lpc_irq(struct irq_data *d) +{ +} + +static void unmask_lpc_irq(struct irq_data *d) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&pch_lpc_priv->lpc_lock, flags); + writel(readl(pch_lpc_priv->base + LPC_INT_ENA) | (0x1 << (d->irq)), + pch_lpc_priv->base + LPC_INT_ENA); + raw_spin_unlock_irqrestore(&pch_lpc_priv->lpc_lock, flags); +} + +static int lpc_set_type(struct irq_data *d, unsigned int type) +{ + u32 val; + u32 mask =3D 0x1 << (d->hwirq); + + if (!(type & IRQ_TYPE_LEVEL_MASK)) + return 0; + + val =3D readl(pch_lpc_priv->base + LPC_INT_POL); + + if (type =3D=3D IRQ_TYPE_LEVEL_HIGH) + val |=3D mask; + else + val &=3D ~mask; + + writel(val, pch_lpc_priv->base + LPC_INT_POL); + + return 0; +} + +static struct irq_chip pch_lpc_irq_chip =3D { + .name =3D "PCH LPC", + .irq_mask =3D mask_lpc_irq, + .irq_unmask =3D unmask_lpc_irq, + .irq_ack =3D ack_lpc_irq, + .irq_mask_ack =3D mask_ack_lpc_irq, + .irq_eoi =3D unmask_lpc_irq, + .irq_set_type =3D lpc_set_type, + .flags =3D IRQCHIP_SKIP_SET_WAKE, +}; + +static void lpc_irq_dispatch(struct irq_desc *desc) +{ + struct irq_chip *chip =3D irq_desc_get_chip(desc); + u32 pending; + + chained_irq_enter(chip, desc); + + pending =3D readl(pch_lpc_priv->base + LPC_INT_ENA); + pending &=3D readl(pch_lpc_priv->base + LPC_INT_STS); + if (!pending) + spurious_interrupt(); + + while (pending) { + int bit =3D __ffs(pending); + + generic_handle_irq(bit); + pending &=3D ~BIT(bit); + } + chained_irq_exit(chip, desc); +} + +static int pch_lpc_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hw) +{ + irq_set_chip_and_handler(irq, &pch_lpc_irq_chip, handle_level_irq); + return 0; +} + +static const struct irq_domain_ops pch_lpc_domain_ops =3D { + .map =3D pch_lpc_map, + .translate =3D irq_domain_translate_twocell, +}; + +static void pch_lpc_reset(struct pch_lpc *priv) +{ + /* Enable the LPC interrupt, bit31: en bit30: edge */ + writel(0x80000000, priv->base + LPC_INT_CTL); + writel(0, priv->base + LPC_INT_ENA); + /* Clear all 18-bit interrpt bit */ + writel(0x3ffff, priv->base + LPC_INT_CLR); +} + +static int pch_lpc_disabled(struct pch_lpc *priv) +{ + return (readl(priv->base + LPC_INT_ENA) =3D=3D 0xffffffff) && + (readl(priv->base + LPC_INT_STS) =3D=3D 0xffffffff); +} + +static int pch_lpc_suspend(void) +{ + pch_lpc_priv->saved_reg_ctl =3D readl(pch_lpc_priv->base + LPC_INT_CTL); + pch_lpc_priv->saved_reg_ena =3D readl(pch_lpc_priv->base + LPC_INT_ENA); + pch_lpc_priv->saved_reg_pol =3D readl(pch_lpc_priv->base + LPC_INT_POL); + return 0; +} + +static void pch_lpc_resume(void) +{ + writel(pch_lpc_priv->saved_reg_ctl, pch_lpc_priv->base + LPC_INT_CTL); + writel(pch_lpc_priv->saved_reg_ena, pch_lpc_priv->base + LPC_INT_ENA); + writel(pch_lpc_priv->saved_reg_pol, pch_lpc_priv->base + LPC_INT_POL); +} + +static struct syscore_ops pch_lpc_syscore_ops =3D { + .suspend =3D pch_lpc_suspend, + .resume =3D pch_lpc_resume, +}; + +struct irq_domain *pch_lpc_acpi_init(struct irq_domain *parent, + struct acpi_madt_lpc_pic *acpi_pchlpc) +{ + int parent_irq; + struct pch_lpc *priv; + struct irq_fwspec fwspec; + + if (!acpi_pchlpc) + return NULL; + + priv =3D kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return NULL; + + raw_spin_lock_init(&priv->lpc_lock); + + priv->base =3D ioremap(acpi_pchlpc->address, acpi_pchlpc->size); + if (!priv->base) + goto free_priv; + + if (pch_lpc_disabled(priv)) { + pr_err("Failed to get LPC status\n"); + goto iounmap_base; + } + + priv->domain_handle =3D irq_domain_alloc_fwnode((phys_addr_t *)acpi_pchlp= c); + if (!priv->domain_handle) { + pr_err("Unable to allocate domain handle\n"); + goto iounmap_base; + } + priv->lpc_domain =3D irq_domain_add_legacy(NULL, LPC_COUNT, 0, 0, + &pch_lpc_domain_ops, priv); + if (!priv->lpc_domain) { + pr_err("Failed to create IRQ domain\n"); + goto iounmap_base; + } + pch_lpc_reset(priv); + + fwspec.fwnode =3D parent->fwnode; + fwspec.param[0] =3D acpi_pchlpc->cascade; + fwspec.param[1] =3D IRQ_TYPE_LEVEL_HIGH; + fwspec.param_count =3D 2; + parent_irq =3D irq_create_fwspec_mapping(&fwspec); + irq_set_chained_handler_and_data(parent_irq, lpc_irq_dispatch, priv); + pch_lpc_priv =3D priv; + + register_syscore_ops(&pch_lpc_syscore_ops); + + return irq_find_matching_fwnode(priv->domain_handle, DOMAIN_BUS_ANY); + +iounmap_base: + iounmap(priv->base); +free_priv: + kfree(priv); + + return NULL; +} --=20 2.27.0