All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V5 00/10] irqchip: Add LoongArch-related irqchip drivers
@ 2021-09-16 12:31 Huacai Chen
  2021-09-16 12:31 ` [PATCH V5 01/10] irqchip: Adjust Kconfig for Loongson Huacai Chen
                   ` (9 more replies)
  0 siblings, 10 replies; 15+ messages in thread
From: Huacai Chen @ 2021-09-16 12:31 UTC (permalink / raw)
  To: Thomas Gleixner, Marc Zyngier
  Cc: linux-kernel, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

LoongArch is a new RISC ISA, which is a bit like MIPS or RISC-V.
LoongArch includes a reduced 32-bit version (LA32R), a standard 32-bit
version (LA32S) and a 64-bit version (LA64). LoongArch use ACPI as its
boot protocol LoongArch-specific interrupt controllers (similar to APIC)
are already added in the next revision of ACPI Specification (current
revision is 6.4).

Currently, LoongArch based processors (e.g. Loongson-3A5000) can only
work together with LS7A chipsets. The irq chips in LoongArch computers
include CPUINTC (CPU Core Interrupt Controller), LIOINTC (Legacy I/O
Interrupt Controller), EIOINTC (Extended I/O Interrupt Controller),
HTVECINTC (Hyper-Transport Vector Interrupt Controller), PCH-PIC (Main
Interrupt Controller in LS7A chipset), PCH-LPC (LPC Interrupt Controller
in LS7A chipset) and PCH-MSI (MSI Interrupt Controller).

CPUINTC is a per-core controller (in CPU), LIOINTC/EIOINTC/HTVECINTC are
per-package controllers (in CPU), while PCH-PIC/PCH-LPC/PCH-MSI are all
controllers out of CPU (i.e., in chipsets). These controllers (in other
words, irqchips) are linked in a hierarchy, and there are two models of
hierarchy (legacy model and extended model).

Legacy IRQ model:

In this model, the IPI (Inter-Processor Interrupt) and CPU Local Timer
interrupt go to CPUINTC directly, CPU UARTS interrupts go to LIOINTC,
while all other devices interrupts go to PCH-PIC/PCH-LPC/PCH-MSI and
gathered by HTVECINTC, and then go to LIOINTC, and then CPUINTC.

 +---------------------------------------------+
 |                                             |
 |    +-----+     +---------+     +-------+    |
 |    | IPI | --> | CPUINTC | <-- | Timer |    |
 |    +-----+     +---------+     +-------+    |
 |                     ^                       |
 |                     |                       |
 |                +---------+     +-------+    |
 |                | LIOINTC | <-- | UARTs |    |
 |                +---------+     +-------+    |
 |                     ^                       |
 |                     |                       |
 |               +-----------+                 |
 |               | HTVECINTC |                 |
 |               +-----------+                 |
 |                ^         ^                  |
 |                |         |                  |
 |          +---------+ +---------+            |
 |          | PCH-PIC | | PCH-MSI |            |
 |          +---------+ +---------+            |
 |            ^     ^           ^              |
 |            |     |           |              |
 |    +---------+ +---------+ +---------+      |
 |    | PCH-LPC | | Devices | | Devices |      |
 |    +---------+ +---------+ +---------+      |
 |         ^                                   |
 |         |                                   |
 |    +---------+                              |
 |    | Devices |                              |
 |    +---------+                              |
 |                                             |
 |                                             |
 +---------------------------------------------+

Extended IRQ model:

In this model, the IPI (Inter-Processor Interrupt) and CPU Local Timer
interrupt go to CPUINTC directly, CPU UARTS interrupts go to LIOINTC,
while all other devices interrupts go to PCH-PIC/PCH-LPC/PCH-MSI and
gathered by EIOINTC, and then go to to CPUINTC directly.

 +--------------------------------------------------------+
 |                                                        |
 |         +-----+     +---------+     +-------+          |
 |         | IPI | --> | CPUINTC | <-- | Timer |          |
 |         +-----+     +---------+     +-------+          |
 |                      ^       ^                         |
 |                      |       |                         |
 |               +---------+ +---------+     +-------+    |
 |               | EIOINTC | | LIOINTC | <-- | UARTs |    |
 |               +---------+ +---------+     +-------+    |
 |                ^       ^                               |
 |                |       |                               |
 |         +---------+ +---------+                        |
 |         | PCH-PIC | | PCH-MSI |                        |
 |         +---------+ +---------+                        |
 |           ^     ^           ^                          |
 |           |     |           |                          |
 |   +---------+ +---------+ +---------+                  |
 |   | PCH-LPC | | Devices | | Devices |                  |
 |   +---------+ +---------+ +---------+                  |
 |        ^                                               |
 |        |                                               |
 |   +---------+                                          |
 |   | Devices |                                          |
 |   +---------+                                          |
 |                                                        |
 |                                                        |
 +--------------------------------------------------------+

This patchset adds some irqchip drivers for LoongArch, it is preparing
to add LoongArch support in mainline kernel, we can see a snapshot here:
https://github.com/loongson/linux/tree/loongarch-next

Cross-compile tool chain to build kernel:
https://github.com/loongson/build-tools/releases

Loongson and LoongArch documentations:
https://github.com/loongson/LoongArch-Documentation

LoongArch-specific interrupt controllers:
https://mantis.uefi.org/mantis/view.php?id=2203

V1 -> V2:
1, Remove queued patches;
2, Move common logic of DT/ACPI probing to common functions;
3, Split .suspend()/.resume() functions to separate patches.

V2 -> V3:
1, Fix a bug for loongson-pch-pic probe;
2, Some minor improvements for LPC controller.

V3 -> V4:
1, Rework the CPU interrupt controller driver;
2, Some minor improvements for other controllers.

V4 -> V5:
1, Add a description of LoonArch's IRQ model;
2, Support multiple EIOINTCs in one system;
3, Some minor improvements for other controllers.

Huacai Chen:
 irqchip: Adjust Kconfig for Loongson.
 irqchip/loongson-pch-pic: Add ACPI init support.
 irqchip/loongson-pch-pic: Add suspend/resume support.
 irqchip/loongson-pch-msi: Add ACPI init support.
 irqchip/loongson-htvec: Add ACPI init support.
 irqchip/loongson-htvec: Add suspend/resume support.
 irqchip/loongson-liointc: Add ACPI init support. 
 irqchip: Add LoongArch CPU interrupt controller support.
 irqchip: Add Loongson Extended I/O interrupt controller.
 irqchip: Add Loongson PCH LPC controller support.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
 drivers/irqchip/Kconfig                |  37 +++-
 drivers/irqchip/Makefile               |   3 +
 drivers/irqchip/irq-loongarch-cpu.c    |  89 +++++++++
 drivers/irqchip/irq-loongson-eiointc.c | 331 +++++++++++++++++++++++++++++++++
 drivers/irqchip/irq-loongson-htvec.c   | 142 ++++++++++----
 drivers/irqchip/irq-loongson-liointc.c | 198 ++++++++++++--------
 drivers/irqchip/irq-loongson-pch-lpc.c | 203 ++++++++++++++++++++
 drivers/irqchip/irq-loongson-pch-msi.c | 119 +++++++-----
 drivers/irqchip/irq-loongson-pch-pic.c | 152 ++++++++++++---
 include/linux/cpuhotplug.h             |   1 +
 10 files changed, 1087 insertions(+), 188 deletions(-)
 create mode 100644 drivers/irqchip/irq-loongarch-cpu.c
 create mode 100644 drivers/irqchip/irq-loongson-eiointc.c
 create mode 100644 drivers/irqchip/irq-loongson-pch-lpc.c
--
2.27.0


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH V5 01/10] irqchip: Adjust Kconfig for Loongson
  2021-09-16 12:31 [PATCH V5 00/10] irqchip: Add LoongArch-related irqchip drivers Huacai Chen
@ 2021-09-16 12:31 ` Huacai Chen
  2021-09-16 12:31 ` [PATCH V5 02/10] irqchip/loongson-pch-pic: Add ACPI init support Huacai Chen
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 15+ messages in thread
From: Huacai Chen @ 2021-09-16 12:31 UTC (permalink / raw)
  To: Thomas Gleixner, Marc Zyngier
  Cc: linux-kernel, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

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 <chenhuacai@loongson.cn>
---
 drivers/irqchip/Kconfig | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 4d5924e9f766..084bc4c2eebd 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -539,7 +539,7 @@ config LOONGSON_LIOINTC
 
 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
@@ -547,16 +547,16 @@ config LOONGSON_HTPIC
 	  Support for the Loongson-3 HyperTransport PIC Controller.
 
 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.
 
 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
@@ -565,7 +565,7 @@ config LOONGSON_PCH_PIC
 
 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
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH V5 02/10] irqchip/loongson-pch-pic: Add ACPI init support
  2021-09-16 12:31 [PATCH V5 00/10] irqchip: Add LoongArch-related irqchip drivers Huacai Chen
  2021-09-16 12:31 ` [PATCH V5 01/10] irqchip: Adjust Kconfig for Loongson Huacai Chen
@ 2021-09-16 12:31 ` Huacai Chen
  2021-09-16 12:31 ` [PATCH V5 03/10] irqchip/loongson-pch-pic: Add suspend/resume support Huacai Chen
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 15+ messages in thread
From: Huacai Chen @ 2021-09-16 12:31 UTC (permalink / raw)
  To: Thomas Gleixner, Marc Zyngier
  Cc: linux-kernel, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

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.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
 drivers/irqchip/irq-loongson-pch-pic.c | 105 ++++++++++++++++++-------
 1 file changed, 78 insertions(+), 27 deletions(-)

diff --git a/drivers/irqchip/irq-loongson-pch-pic.c b/drivers/irqchip/irq-loongson-pch-pic.c
index a4eb8a2181c7..6fd0c983e02a 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)
 
+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;
 };
 
+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;
 
 	for (i = 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)
 	}
 }
 
-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;
 
 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 	if (!priv)
 		return -ENOMEM;
 
 	raw_spin_lock_init(&priv->pic_lock);
-	priv->base = of_iomap(node, 0);
-	if (!priv->base) {
-		err = -ENOMEM;
+	priv->base = ioremap(addr, size);
+	if (!priv->base)
 		goto free_priv;
-	}
 
-	parent_domain = irq_find_host(parent);
-	if (!parent_domain) {
-		pr_err("Failed to find the parent domain\n");
-		err = -ENXIO;
-		goto iounmap_base;
-	}
+	priv->domain_handle = domain_handle;
 
-	if (of_property_read_u32(node, "loongson,pic-base-vec",
-				&priv->ht_vec_base)) {
-		pr_err("Failed to determine pic-base-vec\n");
-		err = -EINVAL;
-		goto iounmap_base;
-	}
+	priv->ht_vec_base = vec_base;
+	vec_count = ((readq(priv->base) >> 48) & 0xff) + 1;
 
 	priv->pic_domain = 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 = -ENOMEM;
 		goto iounmap_base;
 	}
 
 	pch_pic_reset(priv);
+	pch_pic_priv[nr_pics++] = priv;
 
 	return 0;
 
@@ -250,7 +242,66 @@ static int pch_pic_of_init(struct device_node *node,
 free_priv:
 	kfree(priv);
 
-	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 = 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 = pch_pic_init(res.start, resource_size(&res), vec_base,
+				parent_domain, of_node_to_fwnode(node));
+	if (err < 0)
+		return err;
+
+	return 0;
 }
 
 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;
+
+	vec_base = acpi_pchpic->gsi_base - GSI_MIN_PCH_IRQ;
+
+	domain_handle = irq_domain_alloc_fwnode((phys_addr_t *)acpi_pchpic);
+	if (!domain_handle) {
+		pr_err("Unable to allocate domain handle\n");
+		return NULL;
+	}
+
+	ret = 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
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH V5 03/10] irqchip/loongson-pch-pic: Add suspend/resume support
  2021-09-16 12:31 [PATCH V5 00/10] irqchip: Add LoongArch-related irqchip drivers Huacai Chen
  2021-09-16 12:31 ` [PATCH V5 01/10] irqchip: Adjust Kconfig for Loongson Huacai Chen
  2021-09-16 12:31 ` [PATCH V5 02/10] irqchip/loongson-pch-pic: Add ACPI init support Huacai Chen
@ 2021-09-16 12:31 ` Huacai Chen
  2021-09-16 12:31 ` [PATCH V5 04/10] irqchip/loongson-pch-msi: Add ACPI init support Huacai Chen
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 15+ messages in thread
From: Huacai Chen @ 2021-09-16 12:31 UTC (permalink / raw)
  To: Thomas Gleixner, Marc Zyngier
  Cc: linux-kernel, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

Add suspend/resume support for PCH-PIC irqchip, which is needed for
suspend/hibernation.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
 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-loongson-pch-pic.c
index 6fd0c983e02a..dadc908d3a61 100644
--- a/drivers/irqchip/irq-loongson-pch-pic.c
+++ b/drivers/irqchip/irq-loongson-pch-pic.c
@@ -15,6 +15,7 @@
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
 #include <linux/of_platform.h>
+#include <linux/syscore_ops.h>
 
 /* 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];
 };
 
 struct pch_pic *pch_pic_priv[2];
@@ -142,6 +146,7 @@ static struct irq_chip pch_pic_irq_chip = {
 	.irq_ack		= pch_pic_ack_irq,
 	.irq_set_affinity	= irq_chip_set_affinity_parent,
 	.irq_set_type		= pch_pic_set_type,
+	.flags			= IRQCHIP_SKIP_SET_WAKE,
 };
 
 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)
 	}
 }
 
+static int pch_pic_suspend(void)
+{
+	int i, j;
+
+	for (i = 0; i < nr_pics; i++) {
+		for (j = 0; j < PIC_REG_COUNT; j++) {
+			pch_pic_priv[i]->saved_vec_pol[j] =
+				readl(pch_pic_priv[i]->base + PCH_PIC_POL + 4 * j);
+			pch_pic_priv[i]->saved_vec_edge[j] =
+				readl(pch_pic_priv[i]->base + PCH_PIC_EDGE + 4 * j);
+			pch_pic_priv[i]->saved_vec_en[j] =
+				readl(pch_pic_priv[i]->base + PCH_PIC_MASK + 4 * j);
+		}
+	}
+
+	return 0;
+}
+
+static void pch_pic_resume(void)
+{
+	int i, j;
+
+	for (i = 0; i < nr_pics; i++) {
+		pch_pic_reset(pch_pic_priv[i]);
+		for (j = 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 = {
+	.suspend =  pch_pic_suspend,
+	.resume =  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++] = priv;
 
+	register_syscore_ops(&pch_pic_syscore_ops);
+
 	return 0;
 
 iounmap_base:
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH V5 04/10] irqchip/loongson-pch-msi: Add ACPI init support
  2021-09-16 12:31 [PATCH V5 00/10] irqchip: Add LoongArch-related irqchip drivers Huacai Chen
                   ` (2 preceding siblings ...)
  2021-09-16 12:31 ` [PATCH V5 03/10] irqchip/loongson-pch-pic: Add suspend/resume support Huacai Chen
@ 2021-09-16 12:31 ` Huacai Chen
  2021-09-16 12:31 ` [PATCH V5 05/10] irqchip/loongson-htvec: " Huacai Chen
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 15+ messages in thread
From: Huacai Chen @ 2021-09-16 12:31 UTC (permalink / raw)
  To: Thomas Gleixner, Marc Zyngier
  Cc: linux-kernel, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

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.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
 drivers/irqchip/irq-loongson-pch-msi.c | 125 ++++++++++++++++---------
 1 file changed, 83 insertions(+), 42 deletions(-)

diff --git a/drivers/irqchip/irq-loongson-pch-msi.c b/drivers/irqchip/irq-loongson-pch-msi.c
index 32562b7e681b..8d875dbe587f 100644
--- a/drivers/irqchip/irq-loongson-pch-msi.c
+++ b/drivers/irqchip/irq-loongson-pch-msi.c
@@ -15,14 +15,19 @@
 #include <linux/pci.h>
 #include <linux/slab.h>
 
+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;
 };
 
+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_domain_ops = {
 };
 
 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;
 
-	middle_domain = irq_domain_create_linear(of_node_to_fwnode(node),
+	priv->domain_handle = domain_handle;
+
+	middle_domain = 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 *priv,
 	middle_domain->parent = parent;
 	irq_domain_update_bus_token(middle_domain, DOMAIN_BUS_NEXUS);
 
-	msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(node),
+	msi_domain = 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;
 }
 
-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_count,
+			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 = irq_find_host(parent);
-	if (!parent_domain) {
-		pr_err("Failed to find the parent domain\n");
-		return -ENXIO;
-	}
+	struct pch_msi_data *priv;
 
 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 	if (!priv)
@@ -203,48 +202,90 @@ static int pch_msi_init(struct device_node *node,
 
 	mutex_init(&priv->msi_map_lock);
 
-	ret = of_address_to_resource(node, 0, &res);
-	if (ret) {
-		pr_err("Failed to allocate resource\n");
-		goto err_priv;
-	}
-
-	priv->doorbell = 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 = -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 = -EINVAL;
-		goto err_priv;
-	}
+	priv->doorbell = msg_address;
+	priv->irq_first = irq_base;
+	priv->num_irqs = irq_count;
 
 	priv->msi_map = bitmap_zalloc(priv->num_irqs, GFP_KERNEL);
-	if (!priv->msi_map) {
-		ret = -ENOMEM;
+	if (!priv->msi_map)
 		goto err_priv;
-	}
 
 	pr_debug("Registering %d MSIs, starting at %d\n",
 		 priv->num_irqs, priv->irq_first);
 
-	ret = pch_msi_init_domains(priv, node, parent_domain);
+	ret = pch_msi_init_domains(priv, parent_domain, domain_handle);
 	if (ret)
 		goto err_map;
 
+	pch_msi_priv[nr_pics++] = priv;
+
 	return 0;
 
 err_map:
 	kfree(priv->msi_map);
 err_priv:
 	kfree(priv);
-	return ret;
+
+	return -EINVAL;
+}
+
+#ifdef CONFIG_OF
+
+int pch_msi_of_init(struct device_node *node, struct device_node *parent)
+{
+	int err;
+	int irq_base, irq_count;
+	struct resource res;
+	struct irq_domain *parent_domain;
+
+	parent_domain = 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 = pch_msi_init(res.start, irq_base, irq_count, parent_domain, of_node_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;
+
+	domain_handle = irq_domain_alloc_fwnode((phys_addr_t *)acpi_pchmsi);
+
+	ret = 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_ANY);
 }
 
-IRQCHIP_DECLARE(pch_msi, "loongson,pch-msi-1.0", pch_msi_init);
+#endif
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH V5 05/10] irqchip/loongson-htvec: Add ACPI init support
  2021-09-16 12:31 [PATCH V5 00/10] irqchip: Add LoongArch-related irqchip drivers Huacai Chen
                   ` (3 preceding siblings ...)
  2021-09-16 12:31 ` [PATCH V5 04/10] irqchip/loongson-pch-msi: Add ACPI init support Huacai Chen
@ 2021-09-16 12:31 ` Huacai Chen
  2021-09-16 12:31 ` [PATCH V5 06/10] irqchip/loongson-htvec: Add suspend/resume support Huacai Chen
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 15+ messages in thread
From: Huacai Chen @ 2021-09-16 12:31 UTC (permalink / raw)
  To: Thomas Gleixner, Marc Zyngier
  Cc: linux-kernel, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

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.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
 drivers/irqchip/irq-loongson-htvec.c | 115 +++++++++++++++++++--------
 1 file changed, 81 insertions(+), 34 deletions(-)

diff --git a/drivers/irqchip/irq-loongson-htvec.c b/drivers/irqchip/irq-loongson-htvec.c
index 60a335d7e64e..f54c9ccbd69e 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;
 };
 
+struct htvec *htvec_priv;
+
 static void htvec_irq_dispatch(struct irq_desc *desc)
 {
 	int i;
@@ -155,64 +157,109 @@ static void htvec_reset(struct htvec *priv)
 	}
 }
 
-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;
 
 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 	if (!priv)
 		return -ENOMEM;
 
+	priv->num_parents = num_parents;
+	priv->base = ioremap(addr, size);
+	priv->domain_handle = domain_handle;
 	raw_spin_lock_init(&priv->htvec_lock);
-	priv->base = of_iomap(node, 0);
-	if (!priv->base) {
-		err = -ENOMEM;
-		goto free_priv;
-	}
-
-	/* Interrupt may come from any of the 8 interrupt lines */
-	for (i = 0; i < HTVEC_MAX_PARENT_IRQ; i++) {
-		parent_irq[i] = irq_of_parse_and_map(node, i);
-		if (parent_irq[i] <= 0)
-			break;
-
-		priv->num_parents++;
-	}
-
-	if (!priv->num_parents) {
-		pr_err("Failed to get parent irqs\n");
-		err = -ENODEV;
-		goto iounmap_base;
-	}
 
-	priv->htvec_domain = irq_domain_create_linear(of_node_to_fwnode(node),
+	/* Setup IRQ domain */
+	priv->htvec_domain = 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 = -ENOMEM;
-		goto irq_dispose;
+		pr_err("loongson-htvec: cannot add IRQ domain\n");
+		goto iounmap_base;
 	}
 
 	htvec_reset(priv);
 
-	for (i = 0; i < priv->num_parents; i++)
+	for (i = 0; i < priv->num_parents; i++) {
 		irq_set_chained_handler_and_data(parent_irq[i],
 						 htvec_irq_dispatch, priv);
+	}
+
+	htvec_priv = priv;
 
 	return 0;
 
-irq_dispose:
-	for (; i > 0; i--)
-		irq_dispose_mapping(parent_irq[i - 1]);
 iounmap_base:
 	iounmap(priv->base);
-free_priv:
+	priv->domain_handle = NULL;
 	kfree(priv);
 
-	return err;
+	return -EINVAL;
+}
+
+#ifdef CONFIG_OF
+
+static int htvec_of_init(struct device_node *node,
+				struct device_node *parent)
+{
+	int i, err;
+	int num_parents, parent_irq[8];
+	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 = 0; i < HTVEC_MAX_PARENT_IRQ; i++) {
+		parent_irq[i] = irq_of_parse_and_map(node, i);
+		if (parent_irq[i] <= 0)
+			break;
+
+		num_parents++;
+	}
+
+	err = htvec_init(res.start, resource_size(&res),
+			num_parents, parent_irq, of_node_to_fwnode(node));
+	if (err < 0)
+		return err;
+
+	return 0;
 }
 
 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;
+
+	num_parents = HTVEC_MAX_PARENT_IRQ;
+
+	domain_handle = 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 = 0; i < HTVEC_MAX_PARENT_IRQ; i++)
+		parent_irq[i] = irq_create_mapping(parent, acpi_htvec->cascade[i]);
+
+	ret = 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
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH V5 06/10] irqchip/loongson-htvec: Add suspend/resume support
  2021-09-16 12:31 [PATCH V5 00/10] irqchip: Add LoongArch-related irqchip drivers Huacai Chen
                   ` (4 preceding siblings ...)
  2021-09-16 12:31 ` [PATCH V5 05/10] irqchip/loongson-htvec: " Huacai Chen
@ 2021-09-16 12:31 ` Huacai Chen
  2021-09-16 12:31 ` [PATCH V5 07/10] irqchip/loongson-liointc: Add ACPI init support Huacai Chen
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 15+ messages in thread
From: Huacai Chen @ 2021-09-16 12:31 UTC (permalink / raw)
  To: Thomas Gleixner, Marc Zyngier
  Cc: linux-kernel, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

Add suspend/resume support for HTVEC irqchip, which is needed for
suspend/hibernation.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
 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-loongson-htvec.c
index f54c9ccbd69e..82adb2615013 100644
--- a/drivers/irqchip/irq-loongson-htvec.c
+++ b/drivers/irqchip/irq-loongson-htvec.c
@@ -16,6 +16,7 @@
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
 #include <linux/of_platform.h>
+#include <linux/syscore_ops.h>
 
 /* 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];
 };
 
 struct htvec *htvec_priv;
@@ -157,6 +159,29 @@ static void htvec_reset(struct htvec *priv)
 	}
 }
 
+static int htvec_suspend(void)
+{
+	int i;
+
+	for (i = 0; i < htvec_priv->num_parents; i++)
+		htvec_priv->saved_vec_en[i] = readl(htvec_priv->base + HTVEC_EN_OFF + 4 * i);
+
+	return 0;
+}
+
+static void htvec_resume(void)
+{
+	int i;
+
+	for (i = 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 = {
+	.suspend = htvec_suspend,
+	.resume = 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 size,
 
 	htvec_priv = priv;
 
+	register_syscore_ops(&htvec_syscore_ops);
+
 	return 0;
 
 iounmap_base:
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH V5 07/10] irqchip/loongson-liointc: Add ACPI init support
  2021-09-16 12:31 [PATCH V5 00/10] irqchip: Add LoongArch-related irqchip drivers Huacai Chen
                   ` (5 preceding siblings ...)
  2021-09-16 12:31 ` [PATCH V5 06/10] irqchip/loongson-htvec: Add suspend/resume support Huacai Chen
@ 2021-09-16 12:31 ` Huacai Chen
  2021-09-16 12:31 ` [PATCH V5 08/10] irqchip: Add LoongArch CPU interrupt controller support Huacai Chen
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 15+ messages in thread
From: Huacai Chen @ 2021-09-16 12:31 UTC (permalink / raw)
  To: Thomas Gleixner, Marc Zyngier
  Cc: linux-kernel, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

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.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
 drivers/irqchip/irq-loongson-liointc.c | 200 +++++++++++++++----------
 1 file changed, 121 insertions(+), 79 deletions(-)

diff --git a/drivers/irqchip/irq-loongson-liointc.c b/drivers/irqchip/irq-loongson-liointc.c
index 649c58391618..c034b85d5246 100644
--- a/drivers/irqchip/irq-loongson-liointc.c
+++ b/drivers/irqchip/irq-loongson-liointc.c
@@ -16,10 +16,14 @@
 #include <linux/smp.h>
 #include <linux/irqchip/chained_irq.h>
 
+#ifdef CONFIG_MIPS
 #include <loongson.h>
+#else
+#include <asm/loongson.h>
+#endif
 
 #define LIOINTC_CHIP_IRQ	32
-#define LIOINTC_NUM_PARENT 4
+#define LIOINTC_NUM_PARENT	4
 #define LIOINTC_NUM_CORES	4
 
 #define LIOINTC_INTC_CHIP_START	0x20
@@ -41,6 +45,7 @@ struct liointc_handler_data {
 };
 
 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 *desc)
 	struct liointc_handler_data *handler = irq_desc_get_handler_data(desc);
 	struct irq_chip *chip = irq_desc_get_chip(desc);
 	struct irq_chip_generic *gc = handler->priv->gc;
-	int core = get_ebase_cpunum() % LIOINTC_NUM_CORES;
+	int core = cpu_logical_map(smp_processor_id()) % LIOINTC_NUM_CORES;
 	u32 pending;
 
 	chained_irq_enter(chip, desc);
@@ -143,97 +148,61 @@ static void liointc_resume(struct irq_chip_generic *gc)
 	irq_gc_unlock_irqrestore(gc, flags);
 }
 
-static const char * const parent_names[] = {"int0", "int1", "int2", "int3"};
-static const char * const core_reg_names[] = {"isr0", "isr1", "isr2", "isr3"};
+static int parent_irq[LIOINTC_NUM_PARENT];
+static u32 parent_int_map[LIOINTC_NUM_PARENT];
+static const char *const parent_names[] = {"int0", "int1", "int2", "int3"};
+static const char *const core_reg_names[] = {"isr0", "isr1", "isr2", "isr3"};
 
-static void __iomem *liointc_get_reg_byname(struct device_node *node,
-						const char *name)
-{
-	int index = 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, index, 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 = FALSE;
-	int sz, i, err = 0;
 
 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 	if (!priv)
 		return -ENOMEM;
 
-	if (of_device_is_compatible(node, "loongson,liointc-2.0")) {
-		base = liointc_get_reg_byname(node, "main");
-		if (!base) {
-			err = -ENODEV;
-			goto out_free_priv;
-		}
+	base = ioremap(addr, size);
+	if (!base)
+		goto out_free_priv;
 
-		for (i = 0; i < LIOINTC_NUM_CORES; i++)
-			priv->core_isr[i] = liointc_get_reg_byname(node, core_reg_names[i]);
-		if (!priv->core_isr[0]) {
-			err = -ENODEV;
-			goto out_iounmap_base;
-		}
-	} else {
-		base = of_iomap(node, 0);
-		if (!base) {
-			err = -ENODEV;
-			goto out_free_priv;
-		}
+	priv->domain_handle = domain_handle;
 
-		for (i = 0; i < LIOINTC_NUM_CORES; i++)
-			priv->core_isr[i] = base + LIOINTC_REG_INTC_STATUS;
-	}
+	for (i = 0; i < LIOINTC_NUM_CORES; i++)
+		priv->core_isr[i] = base + LIOINTC_REG_INTC_STATUS;
 
-	for (i = 0; i < LIOINTC_NUM_PARENT; i++) {
-		parent_irq[i] = of_irq_get_byname(node, parent_names[i]);
-		if (parent_irq[i] > 0)
-			have_parent = TRUE;
-	}
-	if (!have_parent) {
-		err = -ENODEV;
-		goto out_iounmap_isr;
-	}
+	for (i = 0; i < LIOINTC_NUM_PARENT; i++)
+		priv->handler[i].parent_int_map = parent_int_map[i];
 
-	sz = 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 = -ENODEV;
-		goto out_iounmap_isr;
-	}
+#ifdef CONFIG_OF
+	if (revision > 1) {
+		for (i = 0; i < LIOINTC_NUM_CORES; i++) {
+			index = of_property_match_string(node, "reg-names", core_reg_names[i]);
 
-	for (i = 0; i < LIOINTC_NUM_PARENT; i++)
-		priv->handler[i].parent_int_map = of_parent_int_map[i];
+			if (index < 0)
+				return -EINVAL;
+
+			priv->core_isr[i] = of_iomap(node, index);
+		}
+	}
+#endif
 
 	/* Setup IRQ domain */
-	domain = irq_domain_add_linear(node, 32,
+	domain = 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 = -EINVAL;
-		goto out_iounmap_isr;
+		goto out_iounmap;
 	}
 
-	err = irq_alloc_domain_generic_chips(domain, 32, 1,
-					node->full_name, handle_level_irq,
-					IRQ_NOPROBE, 0, 0);
+	err = 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 +262,93 @@ static int __init liointc_of_init(struct device_node *node,
 
 out_free_domain:
 	irq_domain_remove(domain);
-out_iounmap_isr:
-	for (i = 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);
 
-	return err;
+	return -EINVAL;
+}
+
+#ifdef CONFIG_OF
+
+static int __init liointc_of_init(struct device_node *node,
+				  struct device_node *parent)
+{
+	bool have_parent = FALSE;
+	int sz, i, index, revision, err = 0;
+	struct resource res;
+
+	if (!of_device_is_compatible(node, "loongson,liointc-2.0")) {
+		index = 0;
+		revision = 1;
+	} else {
+		index = of_property_match_string(node, "reg-names", "main");
+		revision = 2;
+	}
+
+	if (of_address_to_resource(node, index, &res))
+		return -EINVAL;
+
+	for (i = 0; i < LIOINTC_NUM_PARENT; i++) {
+		parent_irq[i] = of_irq_get_byname(node, parent_names[i]);
+		if (parent_irq[i] > 0)
+			have_parent = TRUE;
+	}
+	if (!have_parent)
+		return -ENODEV;
+
+	sz = 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 = liointc_init(res.start, resource_size(&res),
+			revision, of_node_to_fwnode(node), node);
+	if (err < 0)
+		return err;
+
+	return 0;
 }
 
 IRQCHIP_DECLARE(loongson_liointc_1_0, "loongson,liointc-1.0", liointc_of_init);
 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_init);
+
+#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;
+
+	parent_int_map[0] = acpi_liointc->cascade_map[0];
+	parent_int_map[1] = acpi_liointc->cascade_map[1];
+
+	parent_irq[0] = irq_create_mapping(parent, acpi_liointc->cascade[0]);
+	if (!cpu_has_extioi)
+		parent_irq[1] = irq_create_mapping(parent, acpi_liointc->cascade[1]);
+
+	domain_handle = irq_domain_alloc_fwnode((phys_addr_t *)acpi_liointc);
+	if (!domain_handle) {
+		pr_err("Unable to allocate domain handle\n");
+		return NULL;
+	}
+
+	ret = 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
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH V5 08/10] irqchip: Add LoongArch CPU interrupt controller support
  2021-09-16 12:31 [PATCH V5 00/10] irqchip: Add LoongArch-related irqchip drivers Huacai Chen
                   ` (6 preceding siblings ...)
  2021-09-16 12:31 ` [PATCH V5 07/10] irqchip/loongson-liointc: Add ACPI init support Huacai Chen
@ 2021-09-16 12:31 ` Huacai Chen
  2021-09-30 14:24   ` Marc Zyngier
  2021-09-16 12:31 ` [PATCH V5 09/10] irqchip: Add Loongson Extended I/O " Huacai Chen
  2021-09-16 12:31 ` [PATCH V5 10/10] irqchip: Add Loongson PCH LPC " Huacai Chen
  9 siblings, 1 reply; 15+ messages in thread
From: Huacai Chen @ 2021-09-16 12:31 UTC (permalink / raw)
  To: Thomas Gleixner, Marc Zyngier
  Cc: linux-kernel, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

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.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
 drivers/irqchip/Kconfig             | 10 ++++
 drivers/irqchip/Makefile            |  1 +
 drivers/irqchip/irq-loongarch-cpu.c | 89 +++++++++++++++++++++++++++++
 3 files changed, 100 insertions(+)
 create mode 100644 drivers/irqchip/irq-loongarch-cpu.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 084bc4c2eebd..443c3a7a0cc1 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -528,6 +528,16 @@ config EXYNOS_IRQ_COMBINER
 	  Say yes here to add support for the IRQ combiner devices embedded
 	  in Samsung Exynos chips.
 
+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 f88cbf36a9d2..4e34eebe180b 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -105,6 +105,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
 obj-$(CONFIG_LOONGSON_LIOINTC)		+= irq-loongson-liointc.o
 obj-$(CONFIG_LOONGSON_HTPIC)		+= irq-loongson-htpic.o
 obj-$(CONFIG_LOONGSON_HTVEC)		+= irq-loongson-htvec.o
diff --git a/drivers/irqchip/irq-loongarch-cpu.c b/drivers/irqchip/irq-loongarch-cpu.c
new file mode 100644
index 000000000000..bc15e3cefbd8
--- /dev/null
+++ b/drivers/irqchip/irq-loongarch-cpu.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+
+#include <asm/loongarch.h>
+#include <asm/setup.h>
+
+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 = {
+	.name		= "LoongArch",
+	.irq_mask	= mask_loongarch_irq,
+	.irq_unmask	= unmask_loongarch_irq,
+};
+
+static void handle_cpu_irq(struct pt_regs *regs)
+{
+	int hwirq;
+	unsigned int estat = read_csr_estat() & CSR_ESTAT_IS;
+
+	while ((hwirq = ffs(estat))) {
+		estat &= ~BIT(hwirq - 1);
+		handle_domain_irq(irq_domain, hwirq - 1, regs);
+	}
+}
+
+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 = {
+	.map = loongarch_cpu_intc_map,
+	.xlate = irq_domain_xlate_onecell,
+};
+
+struct irq_domain * __init loongarch_cpu_irq_init(void)
+{
+	/* Mask interrupts. */
+	clear_csr_ecfg(ECFG0_IM);
+	clear_csr_estat(ESTATF_IP);
+
+	irq_domain = irq_domain_add_simple(NULL, EXCCODE_INT_NUM,
+		     0, &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;
+}
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH V5 09/10] irqchip: Add Loongson Extended I/O interrupt controller support
  2021-09-16 12:31 [PATCH V5 00/10] irqchip: Add LoongArch-related irqchip drivers Huacai Chen
                   ` (7 preceding siblings ...)
  2021-09-16 12:31 ` [PATCH V5 08/10] irqchip: Add LoongArch CPU interrupt controller support Huacai Chen
@ 2021-09-16 12:31 ` Huacai Chen
  2021-09-30 16:21   ` Marc Zyngier
  2021-09-16 12:31 ` [PATCH V5 10/10] irqchip: Add Loongson PCH LPC " Huacai Chen
  9 siblings, 1 reply; 15+ messages in thread
From: Huacai Chen @ 2021-09-16 12:31 UTC (permalink / raw)
  To: Thomas Gleixner, Marc Zyngier
  Cc: linux-kernel, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

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.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
 drivers/irqchip/Kconfig                |  10 +
 drivers/irqchip/Makefile               |   1 +
 drivers/irqchip/irq-loongson-eiointc.c | 373 +++++++++++++++++++++++++
 include/linux/cpuhotplug.h             |   1 +
 4 files changed, 385 insertions(+)
 create mode 100644 drivers/irqchip/irq-loongson-eiointc.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 443c3a7a0cc1..aff08ad824c9 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -547,6 +547,16 @@ config LOONGSON_LIOINTC
 	help
 	  Support for the Loongson Local I/O Interrupt Controller.
 
+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 4e34eebe180b..eb3fdc6fe808 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -107,6 +107,7 @@ 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
 obj-$(CONFIG_LOONGSON_LIOINTC)		+= irq-loongson-liointc.o
+obj-$(CONFIG_LOONGSON_EIOINTC)		+= irq-loongson-eiointc.o
 obj-$(CONFIG_LOONGSON_HTPIC)		+= irq-loongson-htpic.o
 obj-$(CONFIG_LOONGSON_HTVEC)		+= irq-loongson-htvec.o
 obj-$(CONFIG_LOONGSON_PCH_PIC)		+= irq-loongson-pch-pic.o
diff --git a/drivers/irqchip/irq-loongson-eiointc.c b/drivers/irqchip/irq-loongson-eiointc.c
new file mode 100644
index 000000000000..353c91ea5ad2
--- /dev/null
+++ b/drivers/irqchip/irq-loongson-eiointc.c
@@ -0,0 +1,373 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Loongson Extend I/O Interrupt Controller support
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#define pr_fmt(fmt) "eiointc: " fmt
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/syscore_ops.h>
+
+#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_DEF_ENABLE	0xffffffff
+
+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;
+};
+
+struct eiointc_priv *eiointc_priv[2];
+
+int eiointc_get_node(int id)
+{
+	return eiointc_priv[id]->node;
+}
+
+static void eiointc_set_irq_route(int pos, unsigned int cpu, unsigned int mnode, nodemask_t *node_map)
+{
+	int node, cpu_node, route_node;
+	unsigned char coremap[MAX_NUMNODES];
+	uint32_t pos_off, data, data_byte, data_mask;
+
+	pos_off = pos & ~3;
+	data_byte = pos & 3;
+	data_mask = ~BIT_MASK(data_byte) & 0xf;
+
+	memset(coremap, 0, sizeof(unsigned char) * MAX_NUMNODES);
+
+	/* Calculate node and coremap of target irq */
+	cpu_node = cpu_to_node(cpu);
+	coremap[cpu_node] |= (1 << (topology_core_id(cpu)));
+
+	for_each_online_node(node) {
+		if (!node_isset(node, *node_map))
+			continue;
+
+		/* Node 0 is in charge of inter-node interrupt dispatch */
+		route_node = (node == mnode) ? cpu_node : node;
+		data = ((coremap[node] | (route_node << 4)) << (data_byte * 8));
+		csr_any_send(EIOINTC_REG_ROUTE + pos_off, data, data_mask, node);
+	}
+}
+
+static DEFINE_SPINLOCK(affinity_lock);
+
+static int eiointc_set_irq_affinity(struct irq_data *d, const struct cpumask *affinity, bool force)
+{
+	unsigned int cpu;
+	unsigned long flags;
+	uint32_t vector, pos_off;
+	struct cpumask intersect_affinity;
+	struct eiointc_priv *priv = (struct eiointc_priv *)d->domain->host_data;
+
+	if (!IS_ENABLED(CONFIG_SMP))
+		return -EPERM;
+
+	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)) {
+		spin_unlock_irqrestore(&affinity_lock, flags);
+		return -EINVAL;
+	}
+	cpu = cpumask_first(&intersect_affinity);
+
+	/*
+	 * Control interrupt enable or disalbe through cpu 0
+	 * which is reponsible for dispatching interrupts.
+	 */
+	if (!d->parent_data)
+		vector = d->hwirq;
+	else
+		vector = d->parent_data->hwirq;
+
+	pos_off = vector >> 5;
+
+	csr_any_send(EIOINTC_REG_ENABLE + (pos_off << 2),
+		     EIOINTC_DEF_ENABLE & (~((1 << (vector & 0x1F)))), 0x0, 0);
+	eiointc_set_irq_route(vector, cpu, priv->node, &priv->node_map);
+	csr_any_send(EIOINTC_REG_ENABLE + (pos_off << 2), EIOINTC_DEF_ENABLE, 0x0, 0);
+
+	irq_data_update_effective_affinity(d, cpumask_of(cpu));
+
+	spin_unlock_irqrestore(&affinity_lock, flags);
+
+	return IRQ_SET_MASK_OK;
+}
+
+static int eiointc_index(int node)
+{
+	int i;
+
+	for (i = 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;
+	uint32_t data;
+	uint32_t node = cpu_to_node(cpu);
+	uint32_t index = eiointc_index(node);
+
+	if (index < 0) {
+		pr_err("Error: invalid nodemap!\n");
+		return -1;
+	}
+
+	if (cpu == cpumask_first(cpumask_of_node(node))) {
+		eiointc_enable();
+
+		for (i = 0; i < VEC_COUNT / 32; i++) {
+			data = (((1 << (i * 2 + 1)) << 16) | (1 << (i * 2)));
+			iocsr_writel(data, EIOINTC_REG_NODEMAP + i * 4);
+		}
+
+		for (i = 0; i < VEC_COUNT / 32 / 4; i++) {
+			bit = BIT(1 + index); /* Route to IP[1 + index] */
+			data = bit | (bit << 8) | (bit << 16) | (bit << 24);
+			iocsr_writel(data, EIOINTC_REG_IPMAP + i * 4);
+		}
+
+		for (i = 0; i < VEC_COUNT / 4; i++) {
+			/* Route to Node-0 Core-0 */
+			if (index == 0)
+				bit = BIT(cpu_logical_map(0));
+			else
+				bit = (eiointc_priv[index]->node << 4) | 1;
+
+			data = bit | (bit << 8) | (bit << 16) | (bit << 24);
+			iocsr_writel(data, EIOINTC_REG_ROUTE + i * 4);
+		}
+
+		for (i = 0; i < VEC_COUNT / 32; i++) {
+			data = 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 = false;
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	struct eiointc_priv *priv = irq_desc_get_handler_data(desc);
+
+	chained_irq_enter(chip, desc);
+
+	for (i = 0; i < VEC_REG_COUNT; i++) {
+		pending = iocsr_readq(EIOINTC_REG_ISR + (i << 3));
+		iocsr_writeq(pending, EIOINTC_REG_ISR + (i << 3));
+		while (pending) {
+			int bit = __ffs(pending);
+			int irq = bit + VEC_COUNT_PER_REG * i;
+
+			generic_handle_domain_irq(priv->eiointc_domain, irq);
+			pending &= ~BIT(bit);
+			handled = true;
+		}
+	}
+
+	if (!handled)
+		spurious_interrupt();
+
+	chained_irq_exit(chip, desc);
+}
+
+static void eiointc_ack_irq(struct irq_data *d)
+{
+}
+
+static void eiointc_mask_irq(struct irq_data *d)
+{
+}
+
+static void eiointc_unmask_irq(struct irq_data *d)
+{
+}
+
+static struct irq_chip eiointc_irq_chip = {
+	.name			= "EIOINTC",
+	.irq_ack		= eiointc_ack_irq,
+	.irq_mask		= eiointc_mask_irq,
+	.irq_unmask		= eiointc_unmask_irq,
+	.irq_set_affinity	= eiointc_set_irq_affinity,
+};
+
+static int eiointc_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				unsigned int nr_irqs, void *arg)
+{
+	int ret;
+	unsigned int i, type;
+	unsigned long hwirq = 0;
+	struct eiointc *priv = domain->host_data;
+
+	ret = irq_domain_translate_onecell(domain, arg, &hwirq, &type);
+	if (ret)
+		return ret;
+
+	for (i = 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 virq,
+				unsigned int nr_irqs)
+{
+	int i;
+
+	for (i = 0; i < nr_irqs; i++) {
+		struct irq_data *d = 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 = {
+	.translate	= irq_domain_translate_onecell,
+	.alloc		= eiointc_domain_alloc,
+	.free		= eiointc_domain_free,
+};
+
+static int eiointc_suspend(void)
+{
+	return 0;
+}
+
+static bool is_eiointc_irq(struct irq_data *irq_data)
+{
+	int i;
+	struct irq_domain *parent;
+
+	for (parent = irq_data->domain; parent; parent = parent->parent) {
+		for (i = 0; i < nr_pics; i++) {
+			if (parent == eiointc_priv[i]->eiointc_domain)
+				return true;
+		}
+	}
+
+	return false;
+}
+
+static void eiointc_resume(void)
+{
+	int i;
+	struct irq_desc *desc;
+	struct irq_data *irq_data;
+
+	eiointc_router_init(0);
+
+	for (i = 0; i < NR_IRQS; i++) {
+		desc = irq_to_desc(i);
+		if (desc && desc->handle_irq && desc->handle_irq != handle_bad_irq) {
+			irq_data = &desc->irq_data;
+			if (!is_eiointc_irq(irq_data))
+				continue;
+
+			eiointc_set_irq_affinity(irq_data, irq_data->common->affinity, 0);
+		}
+	}
+}
+
+static struct syscore_ops eiointc_syscore_ops = {
+	.suspend = eiointc_suspend,
+	.resume = 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;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return NULL;
+
+	priv->domain_handle = irq_domain_alloc_fwnode((phys_addr_t *)acpi_eiointc);
+	if (!priv->domain_handle) {
+		pr_err("Unable to allocate domain handle\n");
+		goto out_free_priv;
+	}
+
+	priv->node = acpi_eiointc->node;
+	node_map = acpi_eiointc->node_map ? : -1ULL;
+
+	for_each_node(i) {
+		if (node_map & (1ULL << i)) {
+			node_set(i, priv->node_map);
+			cpumask_or(&priv->cpuspan_map, &priv->cpuspan_map, cpumask_of_node(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_priv;
+	}
+
+	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);
+
+	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 = NULL;
+	kfree(priv);
+
+	return NULL;
+}
diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
index 832d8a74fa59..541d02f05352 100644
--- a/include/linux/cpuhotplug.h
+++ b/include/linux/cpuhotplug.h
@@ -146,6 +146,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,
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH V5 10/10] irqchip: Add Loongson PCH LPC controller support
  2021-09-16 12:31 [PATCH V5 00/10] irqchip: Add LoongArch-related irqchip drivers Huacai Chen
                   ` (8 preceding siblings ...)
  2021-09-16 12:31 ` [PATCH V5 09/10] irqchip: Add Loongson Extended I/O " Huacai Chen
@ 2021-09-16 12:31 ` Huacai Chen
  9 siblings, 0 replies; 15+ messages in thread
From: Huacai Chen @ 2021-09-16 12:31 UTC (permalink / raw)
  To: Thomas Gleixner, Marc Zyngier
  Cc: linux-kernel, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

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.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
 drivers/irqchip/Kconfig                |   8 +
 drivers/irqchip/Makefile               |   1 +
 drivers/irqchip/irq-loongson-pch-lpc.c | 203 +++++++++++++++++++++++++
 3 files changed, 212 insertions(+)
 create mode 100644 drivers/irqchip/irq-loongson-pch-lpc.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index aff08ad824c9..29cd8107690d 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -593,6 +593,14 @@ config LOONGSON_PCH_MSI
 	help
 	  Support for the Loongson PCH MSI Controller.
 
+config LOONGSON_PCH_LPC
+	bool "Loongson PCH LPC Controller"
+	depends on MACH_LOONGSON64
+	default MACH_LOONGSON64
+	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 eb3fdc6fe808..6fd07980fa47 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -112,6 +112,7 @@ obj-$(CONFIG_LOONGSON_HTPIC)		+= irq-loongson-htpic.o
 obj-$(CONFIG_LOONGSON_HTVEC)		+= irq-loongson-htvec.o
 obj-$(CONFIG_LOONGSON_PCH_PIC)		+= irq-loongson-pch-pic.o
 obj-$(CONFIG_LOONGSON_PCH_MSI)		+= irq-loongson-pch-msi.o
+obj-$(CONFIG_LOONGSON_PCH_LPC)		+= irq-loongson-pch-lpc.o
 obj-$(CONFIG_MST_IRQ)			+= irq-mst-intc.o
 obj-$(CONFIG_SL28CPLD_INTC)		+= irq-sl28cpld.o
 obj-$(CONFIG_MACH_REALTEK_RTL)		+= irq-realtek-rtl.o
diff --git a/drivers/irqchip/irq-loongson-pch-lpc.c b/drivers/irqchip/irq-loongson-pch-lpc.c
new file mode 100644
index 000000000000..06053f377af8
--- /dev/null
+++ b/drivers/irqchip/irq-loongson-pch-lpc.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Loongson LPC Interrupt Controller support
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#define pr_fmt(fmt) "lpc: " fmt
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/syscore_ops.h>
+
+/* 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;
+};
+
+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 struct irq_chip pch_lpc_irq_chip = {
+	.name			= "PCH LPC",
+	.irq_mask		= mask_lpc_irq,
+	.irq_unmask		= unmask_lpc_irq,
+	.irq_ack		= ack_lpc_irq,
+	.irq_mask_ack		= mask_ack_lpc_irq,
+	.irq_eoi		= unmask_lpc_irq,
+	.flags			= IRQCHIP_SKIP_SET_WAKE,
+};
+
+static void lpc_irq_dispatch(struct irq_desc *desc)
+{
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	u32 pending;
+
+	chained_irq_enter(chip, desc);
+
+	pending = readl(pch_lpc_priv->base + LPC_INT_ENA);
+	pending &= readl(pch_lpc_priv->base + LPC_INT_STS);
+	if (!pending)
+		spurious_interrupt();
+
+	while (pending) {
+		int bit = __ffs(pending);
+
+		generic_handle_irq(bit);
+		pending &= ~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 = {
+	.map = pch_lpc_map,
+	.xlate = irq_domain_xlate_onecell,
+};
+
+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) == 0xffffffff) &&
+			(readl(priv->base + LPC_INT_STS) == 0xffffffff);
+}
+
+static int pch_lpc_suspend(void)
+{
+	pch_lpc_priv->saved_reg_ctl = readl(pch_lpc_priv->base + LPC_INT_CTL);
+	pch_lpc_priv->saved_reg_ena = readl(pch_lpc_priv->base + LPC_INT_ENA);
+	pch_lpc_priv->saved_reg_pol = 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 = {
+	.suspend = pch_lpc_suspend,
+	.resume = 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 = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return NULL;
+
+	raw_spin_lock_init(&priv->lpc_lock);
+
+	priv->base = 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 = irq_domain_alloc_fwnode((phys_addr_t *)acpi_pchlpc);
+	if (!priv->domain_handle) {
+		pr_err("Unable to allocate domain handle\n");
+		goto iounmap_base;
+	}
+	priv->lpc_domain = 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.param_count = 2;
+	fwspec.param[0] = acpi_pchlpc->cascade;
+	fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH;
+	parent_irq = irq_domain_alloc_irqs(parent, 1, NUMA_NO_NODE, &fwspec);
+	irq_set_chained_handler_and_data(parent_irq, lpc_irq_dispatch, priv);
+	pch_lpc_priv = 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;
+}
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH V5 08/10] irqchip: Add LoongArch CPU interrupt controller support
  2021-09-16 12:31 ` [PATCH V5 08/10] irqchip: Add LoongArch CPU interrupt controller support Huacai Chen
@ 2021-09-30 14:24   ` Marc Zyngier
  2021-10-02 12:33     ` Huacai Chen
  0 siblings, 1 reply; 15+ messages in thread
From: Marc Zyngier @ 2021-09-30 14:24 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Thomas Gleixner, linux-kernel, Xuefeng Li, Huacai Chen, Jiaxun Yang

On Thu, 16 Sep 2021 13:31:36 +0100,
Huacai Chen <chenhuacai@loongson.cn> wrote:
> 
> 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.
> 
> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
> ---
>  drivers/irqchip/Kconfig             | 10 ++++
>  drivers/irqchip/Makefile            |  1 +
>  drivers/irqchip/irq-loongarch-cpu.c | 89 +++++++++++++++++++++++++++++
>  3 files changed, 100 insertions(+)
>  create mode 100644 drivers/irqchip/irq-loongarch-cpu.c
> 
> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> index 084bc4c2eebd..443c3a7a0cc1 100644
> --- a/drivers/irqchip/Kconfig
> +++ b/drivers/irqchip/Kconfig
> @@ -528,6 +528,16 @@ config EXYNOS_IRQ_COMBINER
>  	  Say yes here to add support for the IRQ combiner devices embedded
>  	  in Samsung Exynos chips.
>  
> +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 f88cbf36a9d2..4e34eebe180b 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -105,6 +105,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
>  obj-$(CONFIG_LOONGSON_LIOINTC)		+= irq-loongson-liointc.o
>  obj-$(CONFIG_LOONGSON_HTPIC)		+= irq-loongson-htpic.o
>  obj-$(CONFIG_LOONGSON_HTVEC)		+= irq-loongson-htvec.o
> diff --git a/drivers/irqchip/irq-loongarch-cpu.c b/drivers/irqchip/irq-loongarch-cpu.c
> new file mode 100644
> index 000000000000..bc15e3cefbd8
> --- /dev/null
> +++ b/drivers/irqchip/irq-loongarch-cpu.c
> @@ -0,0 +1,89 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/irqchip.h>
> +#include <linux/irqdomain.h>
> +
> +#include <asm/loongarch.h>
> +#include <asm/setup.h>
> +
> +static struct irq_domain *irq_domain;
> +
> +static void mask_loongarch_irq(struct irq_data *d)
> +{
> +	clear_csr_ecfg(ECFGF(d->hwirq));

Where is this function defined?

> +}
> +
> +static void unmask_loongarch_irq(struct irq_data *d)
> +{
> +	set_csr_ecfg(ECFGF(d->hwirq));
> +}
> +
> +static struct irq_chip cpu_irq_controller = {
> +	.name		= "LoongArch",
> +	.irq_mask	= mask_loongarch_irq,
> +	.irq_unmask	= unmask_loongarch_irq,
> +};
> +
> +static void handle_cpu_irq(struct pt_regs *regs)
> +{
> +	int hwirq;
> +	unsigned int estat = read_csr_estat() & CSR_ESTAT_IS;
> +
> +	while ((hwirq = ffs(estat))) {
> +		estat &= ~BIT(hwirq - 1);
> +		handle_domain_irq(irq_domain, hwirq - 1, regs);
> +	}
> +}
> +
> +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);
> +}

How are these functions used? Why aren't the mappings created using
the normal DT or ACPI flows? Where are these macros?

> +
> +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 = {
> +	.map = loongarch_cpu_intc_map,
> +	.xlate = irq_domain_xlate_onecell,
> +};
> +
> +struct irq_domain * __init loongarch_cpu_irq_init(void)
> +{
> +	/* Mask interrupts. */
> +	clear_csr_ecfg(ECFG0_IM);
> +	clear_csr_estat(ESTATF_IP);
> +
> +	irq_domain = irq_domain_add_simple(NULL, EXCCODE_INT_NUM,

This needs to be attached to a firmware node of some sort.

> +		     0, &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;

Who needs to know anything about this irq domain?

> +}

Given that this refers to all sort of symbols that are not included
here and that there is no reference to another patch series containing
the missing code, I'm not sure where you want to take this.

	M.

-- 
Without deviation from the norm, progress is not possible.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH V5 09/10] irqchip: Add Loongson Extended I/O interrupt controller support
  2021-09-16 12:31 ` [PATCH V5 09/10] irqchip: Add Loongson Extended I/O " Huacai Chen
@ 2021-09-30 16:21   ` Marc Zyngier
  2021-10-02 12:41     ` Huacai Chen
  0 siblings, 1 reply; 15+ messages in thread
From: Marc Zyngier @ 2021-09-30 16:21 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Thomas Gleixner, linux-kernel, Xuefeng Li, Huacai Chen, Jiaxun Yang

On Thu, 16 Sep 2021 13:31:37 +0100,
Huacai Chen <chenhuacai@loongson.cn> wrote:
> 
> 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.
> 
> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
> ---
>  drivers/irqchip/Kconfig                |  10 +
>  drivers/irqchip/Makefile               |   1 +
>  drivers/irqchip/irq-loongson-eiointc.c | 373 +++++++++++++++++++++++++
>  include/linux/cpuhotplug.h             |   1 +
>  4 files changed, 385 insertions(+)
>  create mode 100644 drivers/irqchip/irq-loongson-eiointc.c
> 
> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> index 443c3a7a0cc1..aff08ad824c9 100644
> --- a/drivers/irqchip/Kconfig
> +++ b/drivers/irqchip/Kconfig
> @@ -547,6 +547,16 @@ config LOONGSON_LIOINTC
>  	help
>  	  Support for the Loongson Local I/O Interrupt Controller.
>  
> +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 4e34eebe180b..eb3fdc6fe808 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -107,6 +107,7 @@ 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
>  obj-$(CONFIG_LOONGSON_LIOINTC)		+= irq-loongson-liointc.o
> +obj-$(CONFIG_LOONGSON_EIOINTC)		+= irq-loongson-eiointc.o
>  obj-$(CONFIG_LOONGSON_HTPIC)		+= irq-loongson-htpic.o
>  obj-$(CONFIG_LOONGSON_HTVEC)		+= irq-loongson-htvec.o
>  obj-$(CONFIG_LOONGSON_PCH_PIC)		+= irq-loongson-pch-pic.o
> diff --git a/drivers/irqchip/irq-loongson-eiointc.c b/drivers/irqchip/irq-loongson-eiointc.c
> new file mode 100644
> index 000000000000..353c91ea5ad2
> --- /dev/null
> +++ b/drivers/irqchip/irq-loongson-eiointc.c
> @@ -0,0 +1,373 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Loongson Extend I/O Interrupt Controller support
> + *
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +
> +#define pr_fmt(fmt) "eiointc: " fmt
> +
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/irqchip.h>
> +#include <linux/irqdomain.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/kernel.h>
> +#include <linux/platform_device.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +#include <linux/syscore_ops.h>
> +
> +#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_DEF_ENABLE	0xffffffff
> +
> +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;
> +};
> +
> +struct eiointc_priv *eiointc_priv[2];
> +
> +int eiointc_get_node(int id)
> +{
> +	return eiointc_priv[id]->node;
> +}

Why aren't these static?

> +
> +static void eiointc_set_irq_route(int pos, unsigned int cpu, unsigned int mnode, nodemask_t *node_map)
> +{
> +	int node, cpu_node, route_node;
> +	unsigned char coremap[MAX_NUMNODES];
> +	uint32_t pos_off, data, data_byte, data_mask;
> +
> +	pos_off = pos & ~3;
> +	data_byte = pos & 3;
> +	data_mask = ~BIT_MASK(data_byte) & 0xf;
> +
> +	memset(coremap, 0, sizeof(unsigned char) * MAX_NUMNODES);
> +
> +	/* Calculate node and coremap of target irq */
> +	cpu_node = cpu_to_node(cpu);
> +	coremap[cpu_node] |= (1 << (topology_core_id(cpu)));

BIT()?

> +
> +	for_each_online_node(node) {
> +		if (!node_isset(node, *node_map))
> +			continue;
> +
> +		/* Node 0 is in charge of inter-node interrupt dispatch */
> +		route_node = (node == mnode) ? cpu_node : node;
> +		data = ((coremap[node] | (route_node << 4)) << (data_byte * 8));
> +		csr_any_send(EIOINTC_REG_ROUTE + pos_off, data, data_mask, node);
> +	}
> +}
> +
> +static DEFINE_SPINLOCK(affinity_lock);
> +
> +static int eiointc_set_irq_affinity(struct irq_data *d, const struct cpumask *affinity, bool force)
> +{
> +	unsigned int cpu;
> +	unsigned long flags;
> +	uint32_t vector, pos_off;
> +	struct cpumask intersect_affinity;
> +	struct eiointc_priv *priv = (struct eiointc_priv *)d->domain->host_data;

Drop the cast.

> +
> +	if (!IS_ENABLED(CONFIG_SMP))
> +		return -EPERM;
> +
> +	spin_lock_irqsave(&affinity_lock, flags);

This must be a raw spinlock.

> +
> +	cpumask_and(&intersect_affinity, affinity, cpu_online_mask);
> +	cpumask_and(&intersect_affinity, &intersect_affinity, &priv->cpuspan_map);
> +
> +	if (cpumask_empty(&intersect_affinity)) {
> +		spin_unlock_irqrestore(&affinity_lock, flags);
> +		return -EINVAL;
> +	}
> +	cpu = cpumask_first(&intersect_affinity);
> +
> +	/*
> +	 * Control interrupt enable or disalbe through cpu 0
> +	 * which is reponsible for dispatching interrupts.
> +	 */
> +	if (!d->parent_data)
> +		vector = d->hwirq;
> +	else
> +		vector = d->parent_data->hwirq;
> +
> +	pos_off = vector >> 5;
> +
> +	csr_any_send(EIOINTC_REG_ENABLE + (pos_off << 2),
> +		     EIOINTC_DEF_ENABLE & (~((1 << (vector & 0x1F)))), 0x0, 0);
> +	eiointc_set_irq_route(vector, cpu, priv->node, &priv->node_map);
> +	csr_any_send(EIOINTC_REG_ENABLE + (pos_off << 2), EIOINTC_DEF_ENABLE, 0x0, 0);

These bit shifts are undecipherable. At the very least, explain what
this is doing.

> +
> +	irq_data_update_effective_affinity(d, cpumask_of(cpu));
> +
> +	spin_unlock_irqrestore(&affinity_lock, flags);
> +
> +	return IRQ_SET_MASK_OK;
> +}
> +
> +static int eiointc_index(int node)
> +{
> +	int i;
> +
> +	for (i = 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;
> +	uint32_t data;
> +	uint32_t node = cpu_to_node(cpu);
> +	uint32_t index = eiointc_index(node);
> +
> +	if (index < 0) {
> +		pr_err("Error: invalid nodemap!\n");
> +		return -1;
> +	}
> +
> +	if (cpu == cpumask_first(cpumask_of_node(node))) {
> +		eiointc_enable();
> +
> +		for (i = 0; i < VEC_COUNT / 32; i++) {
> +			data = (((1 << (i * 2 + 1)) << 16) | (1 << (i * 2)));
> +			iocsr_writel(data, EIOINTC_REG_NODEMAP + i * 4);
> +		}
> +
> +		for (i = 0; i < VEC_COUNT / 32 / 4; i++) {
> +			bit = BIT(1 + index); /* Route to IP[1 + index] */
> +			data = bit | (bit << 8) | (bit << 16) | (bit << 24);
> +			iocsr_writel(data, EIOINTC_REG_IPMAP + i * 4);
> +		}
> +
> +		for (i = 0; i < VEC_COUNT / 4; i++) {
> +			/* Route to Node-0 Core-0 */
> +			if (index == 0)
> +				bit = BIT(cpu_logical_map(0));
> +			else
> +				bit = (eiointc_priv[index]->node << 4) | 1;
> +
> +			data = bit | (bit << 8) | (bit << 16) | (bit << 24);
> +			iocsr_writel(data, EIOINTC_REG_ROUTE + i * 4);
> +		}
> +
> +		for (i = 0; i < VEC_COUNT / 32; i++) {
> +			data = 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 = false;
> +	struct irq_chip *chip = irq_desc_get_chip(desc);
> +	struct eiointc_priv *priv = irq_desc_get_handler_data(desc);
> +
> +	chained_irq_enter(chip, desc);
> +
> +	for (i = 0; i < VEC_REG_COUNT; i++) {
> +		pending = iocsr_readq(EIOINTC_REG_ISR + (i << 3));
> +		iocsr_writeq(pending, EIOINTC_REG_ISR + (i << 3));
> +		while (pending) {
> +			int bit = __ffs(pending);
> +			int irq = bit + VEC_COUNT_PER_REG * i;
> +
> +			generic_handle_domain_irq(priv->eiointc_domain, irq);
> +			pending &= ~BIT(bit);
> +			handled = true;
> +		}
> +	}
> +
> +	if (!handled)
> +		spurious_interrupt();
> +
> +	chained_irq_exit(chip, desc);
> +}
> +
> +static void eiointc_ack_irq(struct irq_data *d)
> +{
> +}
> +
> +static void eiointc_mask_irq(struct irq_data *d)
> +{
> +}
> +
> +static void eiointc_unmask_irq(struct irq_data *d)
> +{
> +}
> +
> +static struct irq_chip eiointc_irq_chip = {
> +	.name			= "EIOINTC",
> +	.irq_ack		= eiointc_ack_irq,
> +	.irq_mask		= eiointc_mask_irq,
> +	.irq_unmask		= eiointc_unmask_irq,
> +	.irq_set_affinity	= eiointc_set_irq_affinity,

If this is only routing interrupts, why isn't this a hierarchical
interrupt controller that passes all the callbacks directly to the
parent?

> +};
> +
> +static int eiointc_domain_alloc(struct irq_domain *domain, unsigned int virq,
> +				unsigned int nr_irqs, void *arg)
> +{
> +	int ret;
> +	unsigned int i, type;
> +	unsigned long hwirq = 0;
> +	struct eiointc *priv = domain->host_data;
> +
> +	ret = irq_domain_translate_onecell(domain, arg, &hwirq, &type);
> +	if (ret)
> +		return ret;
> +
> +	for (i = 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 virq,
> +				unsigned int nr_irqs)
> +{
> +	int i;
> +
> +	for (i = 0; i < nr_irqs; i++) {
> +		struct irq_data *d = 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 = {
> +	.translate	= irq_domain_translate_onecell,
> +	.alloc		= eiointc_domain_alloc,
> +	.free		= eiointc_domain_free,
> +};
> +
> +static int eiointc_suspend(void)
> +{
> +	return 0;
> +}
> +
> +static bool is_eiointc_irq(struct irq_data *irq_data)
> +{
> +	int i;
> +	struct irq_domain *parent;
> +
> +	for (parent = irq_data->domain; parent; parent = parent->parent) {
> +		for (i = 0; i < nr_pics; i++) {
> +			if (parent == eiointc_priv[i]->eiointc_domain)
> +				return true;
> +		}
> +	}
> +
> +	return false;
> +}
> +
> +static void eiointc_resume(void)
> +{
> +	int i;
> +	struct irq_desc *desc;
> +	struct irq_data *irq_data;
> +
> +	eiointc_router_init(0);
> +
> +	for (i = 0; i < NR_IRQS; i++) {

No. Never.

> +		desc = irq_to_desc(i);
> +		if (desc && desc->handle_irq && desc->handle_irq != handle_bad_irq) {
> +			irq_data = &desc->irq_data;
> +			if (!is_eiointc_irq(irq_data))
> +				continue;
> +
> +			eiointc_set_irq_affinity(irq_data, irq_data->common->affinity, 0);
> +		}

If you need to restore some state, track the interrupts that actually
matter. But this is... just not on.

	M.

-- 
Without deviation from the norm, progress is not possible.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH V5 08/10] irqchip: Add LoongArch CPU interrupt controller support
  2021-09-30 14:24   ` Marc Zyngier
@ 2021-10-02 12:33     ` Huacai Chen
  0 siblings, 0 replies; 15+ messages in thread
From: Huacai Chen @ 2021-10-02 12:33 UTC (permalink / raw)
  To: Marc Zyngier; +Cc: Huacai Chen, Thomas Gleixner, LKML, Xuefeng Li, Jiaxun Yang

Hi, Marc,

On Thu, Sep 30, 2021 at 10:24 PM Marc Zyngier <maz@kernel.org> wrote:
>
> On Thu, 16 Sep 2021 13:31:36 +0100,
> Huacai Chen <chenhuacai@loongson.cn> wrote:
> >
> > 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.
> >
> > Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
> > ---
> >  drivers/irqchip/Kconfig             | 10 ++++
> >  drivers/irqchip/Makefile            |  1 +
> >  drivers/irqchip/irq-loongarch-cpu.c | 89 +++++++++++++++++++++++++++++
> >  3 files changed, 100 insertions(+)
> >  create mode 100644 drivers/irqchip/irq-loongarch-cpu.c
> >
> > diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> > index 084bc4c2eebd..443c3a7a0cc1 100644
> > --- a/drivers/irqchip/Kconfig
> > +++ b/drivers/irqchip/Kconfig
> > @@ -528,6 +528,16 @@ config EXYNOS_IRQ_COMBINER
> >         Say yes here to add support for the IRQ combiner devices embedded
> >         in Samsung Exynos chips.
> >
> > +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 f88cbf36a9d2..4e34eebe180b 100644
> > --- a/drivers/irqchip/Makefile
> > +++ b/drivers/irqchip/Makefile
> > @@ -105,6 +105,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
> >  obj-$(CONFIG_LOONGSON_LIOINTC)               += irq-loongson-liointc.o
> >  obj-$(CONFIG_LOONGSON_HTPIC)         += irq-loongson-htpic.o
> >  obj-$(CONFIG_LOONGSON_HTVEC)         += irq-loongson-htvec.o
> > diff --git a/drivers/irqchip/irq-loongarch-cpu.c b/drivers/irqchip/irq-loongarch-cpu.c
> > new file mode 100644
> > index 000000000000..bc15e3cefbd8
> > --- /dev/null
> > +++ b/drivers/irqchip/irq-loongarch-cpu.c
> > @@ -0,0 +1,89 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> > + */
> > +
> > +#include <linux/init.h>
> > +#include <linux/kernel.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/irq.h>
> > +#include <linux/irqchip.h>
> > +#include <linux/irqdomain.h>
> > +
> > +#include <asm/loongarch.h>
> > +#include <asm/setup.h>
> > +
> > +static struct irq_domain *irq_domain;
> > +
> > +static void mask_loongarch_irq(struct irq_data *d)
> > +{
> > +     clear_csr_ecfg(ECFGF(d->hwirq));
>
> Where is this function defined?
It is defined in the arch code.

>
> > +}
> > +
> > +static void unmask_loongarch_irq(struct irq_data *d)
> > +{
> > +     set_csr_ecfg(ECFGF(d->hwirq));
> > +}
> > +
> > +static struct irq_chip cpu_irq_controller = {
> > +     .name           = "LoongArch",
> > +     .irq_mask       = mask_loongarch_irq,
> > +     .irq_unmask     = unmask_loongarch_irq,
> > +};
> > +
> > +static void handle_cpu_irq(struct pt_regs *regs)
> > +{
> > +     int hwirq;
> > +     unsigned int estat = read_csr_estat() & CSR_ESTAT_IS;
> > +
> > +     while ((hwirq = ffs(estat))) {
> > +             estat &= ~BIT(hwirq - 1);
> > +             handle_domain_irq(irq_domain, hwirq - 1, regs);
> > +     }
> > +}
> > +
> > +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);
> > +}
>
> How are these functions used? Why aren't the mappings created using
> the normal DT or ACPI flows? Where are these macros?
Please see the ESTAT register:
https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#exception-status
HWI0~HWI7 are configuable (can be created by DT/ACPI), but IPI, TI
(Timer), PCOV (PMC) are hardcoded bits, so we define get_xxx_irq() for
them.

>
> > +
> > +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 = {
> > +     .map = loongarch_cpu_intc_map,
> > +     .xlate = irq_domain_xlate_onecell,
> > +};
> > +
> > +struct irq_domain * __init loongarch_cpu_irq_init(void)
> > +{
> > +     /* Mask interrupts. */
> > +     clear_csr_ecfg(ECFG0_IM);
> > +     clear_csr_estat(ESTATF_IP);
> > +
> > +     irq_domain = irq_domain_add_simple(NULL, EXCCODE_INT_NUM,
>
> This needs to be attached to a firmware node of some sort.
OK, this will be improved.

>
> > +                  0, &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;
>
> Who needs to know anything about this irq domain?
LoongArch use ACPI, but ACPI table cannot describe the hierarchy of
irqchips, so we initilize the irqchip subsystem in this way (from arch
code):

+ cpu_domain = loongarch_cpu_irq_init();
+ liointc_domain = liointc_acpi_init(cpu_domain, acpi_liointc);
+ parent_domain = eiointc_acpi_init(cpu_domain, acpi_eiointc[i]);
+ pch_pic_domain[i] = pch_pic_acpi_init(parent_domain, acpi_pchpic[i]);
+ pch_msi_domain[i] = pch_msi_acpi_init(parent_domain, acpi_pchmsi[i]);

Upstream irqchip init function return an irqdomain, and this domain
will be used by downstream irqchips as its parent.

>
> > +}
>
> Given that this refers to all sort of symbols that are not included
> here and that there is no reference to another patch series containing
> the missing code, I'm not sure where you want to take this.

I will add references in the next version. For this version,
arch-specific irq code comes from here:
https://lore.kernel.org/linux-arch/20210927064300.624279-11-chenhuacai@loongson.cn/T/#u

Huacai

>
>         M.
>
> --
> Without deviation from the norm, progress is not possible.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH V5 09/10] irqchip: Add Loongson Extended I/O interrupt controller support
  2021-09-30 16:21   ` Marc Zyngier
@ 2021-10-02 12:41     ` Huacai Chen
  0 siblings, 0 replies; 15+ messages in thread
From: Huacai Chen @ 2021-10-02 12:41 UTC (permalink / raw)
  To: Marc Zyngier; +Cc: Huacai Chen, Thomas Gleixner, LKML, Xuefeng Li, Jiaxun Yang

Hi, Marc,

On Fri, Oct 1, 2021 at 12:21 AM Marc Zyngier <maz@kernel.org> wrote:
>
> On Thu, 16 Sep 2021 13:31:37 +0100,
> Huacai Chen <chenhuacai@loongson.cn> wrote:
> >
> > 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.
> >
> > Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
> > ---
> >  drivers/irqchip/Kconfig                |  10 +
> >  drivers/irqchip/Makefile               |   1 +
> >  drivers/irqchip/irq-loongson-eiointc.c | 373 +++++++++++++++++++++++++
> >  include/linux/cpuhotplug.h             |   1 +
> >  4 files changed, 385 insertions(+)
> >  create mode 100644 drivers/irqchip/irq-loongson-eiointc.c
> >
> > diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> > index 443c3a7a0cc1..aff08ad824c9 100644
> > --- a/drivers/irqchip/Kconfig
> > +++ b/drivers/irqchip/Kconfig
> > @@ -547,6 +547,16 @@ config LOONGSON_LIOINTC
> >       help
> >         Support for the Loongson Local I/O Interrupt Controller.
> >
> > +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 4e34eebe180b..eb3fdc6fe808 100644
> > --- a/drivers/irqchip/Makefile
> > +++ b/drivers/irqchip/Makefile
> > @@ -107,6 +107,7 @@ 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
> >  obj-$(CONFIG_LOONGSON_LIOINTC)               += irq-loongson-liointc.o
> > +obj-$(CONFIG_LOONGSON_EIOINTC)               += irq-loongson-eiointc.o
> >  obj-$(CONFIG_LOONGSON_HTPIC)         += irq-loongson-htpic.o
> >  obj-$(CONFIG_LOONGSON_HTVEC)         += irq-loongson-htvec.o
> >  obj-$(CONFIG_LOONGSON_PCH_PIC)               += irq-loongson-pch-pic.o
> > diff --git a/drivers/irqchip/irq-loongson-eiointc.c b/drivers/irqchip/irq-loongson-eiointc.c
> > new file mode 100644
> > index 000000000000..353c91ea5ad2
> > --- /dev/null
> > +++ b/drivers/irqchip/irq-loongson-eiointc.c
> > @@ -0,0 +1,373 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Loongson Extend I/O Interrupt Controller support
> > + *
> > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> > + */
> > +
> > +#define pr_fmt(fmt) "eiointc: " fmt
> > +
> > +#include <linux/interrupt.h>
> > +#include <linux/irq.h>
> > +#include <linux/irqchip.h>
> > +#include <linux/irqdomain.h>
> > +#include <linux/irqchip/chained_irq.h>
> > +#include <linux/kernel.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/syscore_ops.h>
> > +
> > +#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_DEF_ENABLE   0xffffffff
> > +
> > +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;
> > +};
> > +
> > +struct eiointc_priv *eiointc_priv[2];
> > +
> > +int eiointc_get_node(int id)
> > +{
> > +     return eiointc_priv[id]->node;
> > +}
>
> Why aren't these static?
Emm, eiointc_priv should be static, but eiointc_get_node() is used by arch code.

>
> > +
> > +static void eiointc_set_irq_route(int pos, unsigned int cpu, unsigned int mnode, nodemask_t *node_map)
> > +{
> > +     int node, cpu_node, route_node;
> > +     unsigned char coremap[MAX_NUMNODES];
> > +     uint32_t pos_off, data, data_byte, data_mask;
> > +
> > +     pos_off = pos & ~3;
> > +     data_byte = pos & 3;
> > +     data_mask = ~BIT_MASK(data_byte) & 0xf;
> > +
> > +     memset(coremap, 0, sizeof(unsigned char) * MAX_NUMNODES);
> > +
> > +     /* Calculate node and coremap of target irq */
> > +     cpu_node = cpu_to_node(cpu);
> > +     coremap[cpu_node] |= (1 << (topology_core_id(cpu)));
>
> BIT()?
OK, thanks.

>
> > +
> > +     for_each_online_node(node) {
> > +             if (!node_isset(node, *node_map))
> > +                     continue;
> > +
> > +             /* Node 0 is in charge of inter-node interrupt dispatch */
> > +             route_node = (node == mnode) ? cpu_node : node;
> > +             data = ((coremap[node] | (route_node << 4)) << (data_byte * 8));
> > +             csr_any_send(EIOINTC_REG_ROUTE + pos_off, data, data_mask, node);
> > +     }
> > +}
> > +
> > +static DEFINE_SPINLOCK(affinity_lock);
> > +
> > +static int eiointc_set_irq_affinity(struct irq_data *d, const struct cpumask *affinity, bool force)
> > +{
> > +     unsigned int cpu;
> > +     unsigned long flags;
> > +     uint32_t vector, pos_off;
> > +     struct cpumask intersect_affinity;
> > +     struct eiointc_priv *priv = (struct eiointc_priv *)d->domain->host_data;
>
> Drop the cast.
OK, thanks.

>
> > +
> > +     if (!IS_ENABLED(CONFIG_SMP))
> > +             return -EPERM;
> > +
> > +     spin_lock_irqsave(&affinity_lock, flags);
>
> This must be a raw spinlock.
OK, thanks.

>
> > +
> > +     cpumask_and(&intersect_affinity, affinity, cpu_online_mask);
> > +     cpumask_and(&intersect_affinity, &intersect_affinity, &priv->cpuspan_map);
> > +
> > +     if (cpumask_empty(&intersect_affinity)) {
> > +             spin_unlock_irqrestore(&affinity_lock, flags);
> > +             return -EINVAL;
> > +     }
> > +     cpu = cpumask_first(&intersect_affinity);
> > +
> > +     /*
> > +      * Control interrupt enable or disalbe through cpu 0
> > +      * which is reponsible for dispatching interrupts.
> > +      */
> > +     if (!d->parent_data)
> > +             vector = d->hwirq;
> > +     else
> > +             vector = d->parent_data->hwirq;
> > +
> > +     pos_off = vector >> 5;
> > +
> > +     csr_any_send(EIOINTC_REG_ENABLE + (pos_off << 2),
> > +                  EIOINTC_DEF_ENABLE & (~((1 << (vector & 0x1F)))), 0x0, 0);
> > +     eiointc_set_irq_route(vector, cpu, priv->node, &priv->node_map);
> > +     csr_any_send(EIOINTC_REG_ENABLE + (pos_off << 2), EIOINTC_DEF_ENABLE, 0x0, 0);
>
> These bit shifts are undecipherable. At the very least, explain what
> this is doing.
OK, this will be improved.

>
> > +
> > +     irq_data_update_effective_affinity(d, cpumask_of(cpu));
> > +
> > +     spin_unlock_irqrestore(&affinity_lock, flags);
> > +
> > +     return IRQ_SET_MASK_OK;
> > +}
> > +
> > +static int eiointc_index(int node)
> > +{
> > +     int i;
> > +
> > +     for (i = 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;
> > +     uint32_t data;
> > +     uint32_t node = cpu_to_node(cpu);
> > +     uint32_t index = eiointc_index(node);
> > +
> > +     if (index < 0) {
> > +             pr_err("Error: invalid nodemap!\n");
> > +             return -1;
> > +     }
> > +
> > +     if (cpu == cpumask_first(cpumask_of_node(node))) {
> > +             eiointc_enable();
> > +
> > +             for (i = 0; i < VEC_COUNT / 32; i++) {
> > +                     data = (((1 << (i * 2 + 1)) << 16) | (1 << (i * 2)));
> > +                     iocsr_writel(data, EIOINTC_REG_NODEMAP + i * 4);
> > +             }
> > +
> > +             for (i = 0; i < VEC_COUNT / 32 / 4; i++) {
> > +                     bit = BIT(1 + index); /* Route to IP[1 + index] */
> > +                     data = bit | (bit << 8) | (bit << 16) | (bit << 24);
> > +                     iocsr_writel(data, EIOINTC_REG_IPMAP + i * 4);
> > +             }
> > +
> > +             for (i = 0; i < VEC_COUNT / 4; i++) {
> > +                     /* Route to Node-0 Core-0 */
> > +                     if (index == 0)
> > +                             bit = BIT(cpu_logical_map(0));
> > +                     else
> > +                             bit = (eiointc_priv[index]->node << 4) | 1;
> > +
> > +                     data = bit | (bit << 8) | (bit << 16) | (bit << 24);
> > +                     iocsr_writel(data, EIOINTC_REG_ROUTE + i * 4);
> > +             }
> > +
> > +             for (i = 0; i < VEC_COUNT / 32; i++) {
> > +                     data = 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 = false;
> > +     struct irq_chip *chip = irq_desc_get_chip(desc);
> > +     struct eiointc_priv *priv = irq_desc_get_handler_data(desc);
> > +
> > +     chained_irq_enter(chip, desc);
> > +
> > +     for (i = 0; i < VEC_REG_COUNT; i++) {
> > +             pending = iocsr_readq(EIOINTC_REG_ISR + (i << 3));
> > +             iocsr_writeq(pending, EIOINTC_REG_ISR + (i << 3));
> > +             while (pending) {
> > +                     int bit = __ffs(pending);
> > +                     int irq = bit + VEC_COUNT_PER_REG * i;
> > +
> > +                     generic_handle_domain_irq(priv->eiointc_domain, irq);
> > +                     pending &= ~BIT(bit);
> > +                     handled = true;
> > +             }
> > +     }
> > +
> > +     if (!handled)
> > +             spurious_interrupt();
> > +
> > +     chained_irq_exit(chip, desc);
> > +}
> > +
> > +static void eiointc_ack_irq(struct irq_data *d)
> > +{
> > +}
> > +
> > +static void eiointc_mask_irq(struct irq_data *d)
> > +{
> > +}
> > +
> > +static void eiointc_unmask_irq(struct irq_data *d)
> > +{
> > +}
> > +
> > +static struct irq_chip eiointc_irq_chip = {
> > +     .name                   = "EIOINTC",
> > +     .irq_ack                = eiointc_ack_irq,
> > +     .irq_mask               = eiointc_mask_irq,
> > +     .irq_unmask             = eiointc_unmask_irq,
> > +     .irq_set_affinity       = eiointc_set_irq_affinity,
>
> If this is only routing interrupts, why isn't this a hierarchical
> interrupt controller that passes all the callbacks directly to the
> parent?
OK, this will be improved.

>
> > +};
> > +
> > +static int eiointc_domain_alloc(struct irq_domain *domain, unsigned int virq,
> > +                             unsigned int nr_irqs, void *arg)
> > +{
> > +     int ret;
> > +     unsigned int i, type;
> > +     unsigned long hwirq = 0;
> > +     struct eiointc *priv = domain->host_data;
> > +
> > +     ret = irq_domain_translate_onecell(domain, arg, &hwirq, &type);
> > +     if (ret)
> > +             return ret;
> > +
> > +     for (i = 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 virq,
> > +                             unsigned int nr_irqs)
> > +{
> > +     int i;
> > +
> > +     for (i = 0; i < nr_irqs; i++) {
> > +             struct irq_data *d = 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 = {
> > +     .translate      = irq_domain_translate_onecell,
> > +     .alloc          = eiointc_domain_alloc,
> > +     .free           = eiointc_domain_free,
> > +};
> > +
> > +static int eiointc_suspend(void)
> > +{
> > +     return 0;
> > +}
> > +
> > +static bool is_eiointc_irq(struct irq_data *irq_data)
> > +{
> > +     int i;
> > +     struct irq_domain *parent;
> > +
> > +     for (parent = irq_data->domain; parent; parent = parent->parent) {
> > +             for (i = 0; i < nr_pics; i++) {
> > +                     if (parent == eiointc_priv[i]->eiointc_domain)
> > +                             return true;
> > +             }
> > +     }
> > +
> > +     return false;
> > +}
> > +
> > +static void eiointc_resume(void)
> > +{
> > +     int i;
> > +     struct irq_desc *desc;
> > +     struct irq_data *irq_data;
> > +
> > +     eiointc_router_init(0);
> > +
> > +     for (i = 0; i < NR_IRQS; i++) {
>
> No. Never.
>
> > +             desc = irq_to_desc(i);
> > +             if (desc && desc->handle_irq && desc->handle_irq != handle_bad_irq) {
> > +                     irq_data = &desc->irq_data;
> > +                     if (!is_eiointc_irq(irq_data))
> > +                             continue;
> > +
> > +                     eiointc_set_irq_affinity(irq_data, irq_data->common->affinity, 0);
> > +             }
>
> If you need to restore some state, track the interrupts that actually
> matter. But this is... just not on.
OK, let me try to improve.

Huacai
>
>         M.
>
> --
> Without deviation from the norm, progress is not possible.

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2021-10-02 12:43 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-09-16 12:31 [PATCH V5 00/10] irqchip: Add LoongArch-related irqchip drivers Huacai Chen
2021-09-16 12:31 ` [PATCH V5 01/10] irqchip: Adjust Kconfig for Loongson Huacai Chen
2021-09-16 12:31 ` [PATCH V5 02/10] irqchip/loongson-pch-pic: Add ACPI init support Huacai Chen
2021-09-16 12:31 ` [PATCH V5 03/10] irqchip/loongson-pch-pic: Add suspend/resume support Huacai Chen
2021-09-16 12:31 ` [PATCH V5 04/10] irqchip/loongson-pch-msi: Add ACPI init support Huacai Chen
2021-09-16 12:31 ` [PATCH V5 05/10] irqchip/loongson-htvec: " Huacai Chen
2021-09-16 12:31 ` [PATCH V5 06/10] irqchip/loongson-htvec: Add suspend/resume support Huacai Chen
2021-09-16 12:31 ` [PATCH V5 07/10] irqchip/loongson-liointc: Add ACPI init support Huacai Chen
2021-09-16 12:31 ` [PATCH V5 08/10] irqchip: Add LoongArch CPU interrupt controller support Huacai Chen
2021-09-30 14:24   ` Marc Zyngier
2021-10-02 12:33     ` Huacai Chen
2021-09-16 12:31 ` [PATCH V5 09/10] irqchip: Add Loongson Extended I/O " Huacai Chen
2021-09-30 16:21   ` Marc Zyngier
2021-10-02 12:41     ` Huacai Chen
2021-09-16 12:31 ` [PATCH V5 10/10] irqchip: Add Loongson PCH LPC " Huacai Chen

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.