linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/6] Loongson PCH IRQ Support
@ 2020-04-22 14:24 Jiaxun Yang
  2020-04-22 14:24 ` [PATCH 1/6] irqchip: Add Loongson HyperTransport Vector support Jiaxun Yang
                   ` (8 more replies)
  0 siblings, 9 replies; 41+ messages in thread
From: Jiaxun Yang @ 2020-04-22 14:24 UTC (permalink / raw)
  To: linux-mips, maz
  Cc: Jiaxun Yang, Thomas Gleixner, Jason Cooper, Rob Herring,
	Huacai Chen, linux-kernel, devicetree

This series mainly added IRQ support for Loongson-7A1000 PCH.
DeviceTree will be added later as PCI support is also pending
for reviewing.

Jiaxun Yang (6):
  irqchip: Add Loongson HyperTransport Vector support
  dt-bindings: interrupt-controller: Add Loongson HTVEC
  irqchip: Add Loongson PCH PIC controller
  dt-bindings: interrupt-controller: Add Loongson PCH PIC
  irqchip: Add Loongson PCH MSI controller
  dt-bindings: interrupt-controller: Add Loongson PCH MSI

 .../interrupt-controller/loongson,htvec.yaml  |  59 ++++
 .../loongson,pch-msi.yaml                     |  56 ++++
 .../loongson,pch-pic.yaml                     |  55 ++++
 drivers/irqchip/Kconfig                       |  26 ++
 drivers/irqchip/Makefile                      |   3 +
 drivers/irqchip/irq-loongson-htvec.c          | 217 ++++++++++++++
 drivers/irqchip/irq-loongson-pch-msi.c        | 265 ++++++++++++++++++
 drivers/irqchip/irq-loongson-pch-pic.c        | 256 +++++++++++++++++
 8 files changed, 937 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/loongson,htvec.yaml
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/loongson,pch-pic.yaml
 create mode 100644 drivers/irqchip/irq-loongson-htvec.c
 create mode 100644 drivers/irqchip/irq-loongson-pch-msi.c
 create mode 100644 drivers/irqchip/irq-loongson-pch-pic.c

-- 
2.26.0.rc2


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

* [PATCH 1/6] irqchip: Add Loongson HyperTransport Vector support
  2020-04-22 14:24 [PATCH 0/6] Loongson PCH IRQ Support Jiaxun Yang
@ 2020-04-22 14:24 ` Jiaxun Yang
  2020-04-23 13:31   ` Marc Zyngier
  2020-04-22 14:24 ` [PATCH 2/6] dt-bindings: interrupt-controller: Add Loongson HTVEC Jiaxun Yang
                   ` (7 subsequent siblings)
  8 siblings, 1 reply; 41+ messages in thread
From: Jiaxun Yang @ 2020-04-22 14:24 UTC (permalink / raw)
  To: linux-mips, maz
  Cc: Jiaxun Yang, Thomas Gleixner, Jason Cooper, Rob Herring,
	Huacai Chen, linux-kernel, devicetree

This controller appears on Loongson-3 chips for receiving interrupt
vectors from PCH's PIC and PCH's PCIe MSI interrupts. It usually act
as the top of irq hierarchy.

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---
 drivers/irqchip/Kconfig              |   8 +
 drivers/irqchip/Makefile             |   1 +
 drivers/irqchip/irq-loongson-htvec.c | 217 +++++++++++++++++++++++++++
 3 files changed, 226 insertions(+)
 create mode 100644 drivers/irqchip/irq-loongson-htvec.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index a85aada04a64..de4564e2ea88 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -532,4 +532,12 @@ config LOONGSON_HTPIC
 	help
 	  Support for the Loongson-3 HyperTransport PIC Controller.
 
+config LOONGSON_HTVEC
+	bool "Loongson3 HyperTransport Interrupt Vector Controller"
+	depends on MACH_LOONGSON64 || COMPILE_TEST
+	default MACH_LOONGSON64
+	select IRQ_DOMAIN_HIERARCHY
+	help
+	  Support for the Loongson3 HyperTransport Interrupt Vector Controller.
+
 endmenu
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 37bbe39bf909..74561879f5a7 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -107,3 +107,4 @@ obj-$(CONFIG_TI_SCI_INTR_IRQCHIP)	+= irq-ti-sci-intr.o
 obj-$(CONFIG_TI_SCI_INTA_IRQCHIP)	+= irq-ti-sci-inta.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-loongson-htvec.c b/drivers/irqchip/irq-loongson-htvec.c
new file mode 100644
index 000000000000..e155ebb99efb
--- /dev/null
+++ b/drivers/irqchip/irq-loongson-htvec.c
@@ -0,0 +1,217 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com>
+ *  Loongson HyperTransport Interrupt Vector support
+ */
+
+#define pr_fmt(fmt) "htvec: " 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>
+
+/* Registers */
+#define HTVEC_EN_OFF		0x20
+#define HTVEC_MAX_PARENT_IRQ 4
+
+#define VEC_COUNT_PER_REG	32
+#define VEC_REG_COUNT		4
+#define VEC_COUNT		(VEC_COUNT_PER_REG * VEC_REG_COUNT)
+#define VEC_REG_IDX(irq_id)	((irq_id) / VEC_COUNT_PER_REG)
+#define VEC_REG_BIT(irq_id)	((irq_id) % VEC_COUNT_PER_REG)
+
+struct htvec {
+	void __iomem *base;
+	struct irq_domain *htvec_domain;
+	raw_spinlock_t htvec_lock;
+};
+
+static void htvec_irq_dispatch(struct irq_desc *desc)
+{
+	struct htvec *priv = irq_desc_get_handler_data(desc);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	uint32_t pending;
+	bool handled = false;
+	int i;
+
+	chained_irq_enter(chip, desc);
+
+	for (i = 0; i < VEC_REG_COUNT; i++) {
+		pending = readl(priv->base + 4 * i);
+		/* Ack all IRQs at once, otherwise IRQ flood might happen */
+		writel(pending, priv->base + 4 * i);
+		while (pending) {
+			int bit = __ffs(pending);
+
+			generic_handle_irq(irq_linear_revmap(priv->htvec_domain,
+						bit + 32 * i));
+			pending &= ~BIT(bit);
+			handled = true;
+		}
+	}
+
+	if (!handled)
+		spurious_interrupt();
+
+	chained_irq_exit(chip, desc);
+}
+
+static void htvec_bitset(void __iomem *addr, int bit)
+{
+	u32 reg;
+
+	addr += VEC_REG_IDX(bit) * 4;
+	reg = readl(addr);
+	reg |= BIT(VEC_REG_BIT(bit));
+	writel(reg, addr);
+}
+
+static void htvec_bitclr(void __iomem *addr, int bit)
+{
+	u32 reg;
+
+	addr += VEC_REG_IDX(bit) * 4;
+	reg = readl(addr);
+	reg &= ~BIT(VEC_REG_BIT(bit));
+	writel(reg, addr);
+}
+
+static void htvec_mask_irq(struct irq_data *d)
+{
+	struct htvec *priv = irq_data_get_irq_chip_data(d);
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&priv->htvec_lock, flags);
+	htvec_bitclr(priv->base + HTVEC_EN_OFF, d->hwirq);
+	raw_spin_unlock_irqrestore(&priv->htvec_lock, flags);
+}
+
+static void htvec_unmask_irq(struct irq_data *d)
+{
+	struct htvec *priv = irq_data_get_irq_chip_data(d);
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&priv->htvec_lock, flags);
+	htvec_bitset(priv->base + HTVEC_EN_OFF, d->hwirq);
+	raw_spin_unlock_irqrestore(&priv->htvec_lock, flags);
+}
+
+static struct irq_chip htvec_irq_chip = {
+	.name			= "LOONGSON_HTVEC",
+	.irq_mask		= htvec_mask_irq,
+	.irq_unmask		= htvec_unmask_irq,
+};
+
+static int htvec_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				  unsigned int nr_irqs, void *arg)
+{
+	struct htvec *priv = domain->host_data;
+	unsigned long hwirq;
+	unsigned int type;
+
+	irq_domain_translate_onecell(domain, arg, &hwirq, &type);
+
+	/* Not much to do, just setup the irqdata */
+	irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
+					&htvec_irq_chip, priv);
+
+	return 0;
+}
+
+static void htvec_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 htvec_domain_ops = {
+	.translate = irq_domain_translate_onecell,
+	.alloc	= htvec_domain_alloc,
+	.free	= htvec_domain_free,
+};
+
+static void htvec_reset(struct htvec *priv)
+{
+	u32 idx;
+
+	/* Clear IRQ cause registers, mask all interrupts */
+	for (idx = 0; idx < VEC_REG_COUNT; idx++) {
+		writel_relaxed(0x0, priv->base + HTVEC_EN_OFF + 4 * idx);
+		writel_relaxed(0xFFFFFFFF, priv->base);
+	}
+}
+
+static int htvec_of_init(struct device_node *node,
+				struct device_node *parent)
+{
+	struct htvec *priv;
+	int err, parent_irq[4], num_parents = 0, i;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	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 4 interrupt line */
+	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++;
+	}
+
+	if (!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),
+						   VEC_COUNT,
+						   &htvec_domain_ops,
+						   priv);
+	if (!priv->htvec_domain) {
+		pr_err("Failed to create IRQ domain\n");
+		err = -ENOMEM;
+		goto iounmap_base;
+	}
+
+	htvec_reset(priv);
+
+	for (i = 0; i < num_parents; i++) {
+		irq_set_chained_handler_and_data(parent_irq[i],
+						htvec_irq_dispatch, priv);
+	}
+
+	return 0;
+
+iounmap_base:
+	iounmap(priv->base);
+free_priv:
+	kfree(priv);
+
+	return err;
+}
+
+IRQCHIP_DECLARE(htvec, "loongson,htvec-1.0", htvec_of_init);
-- 
2.26.0.rc2


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

* [PATCH 2/6] dt-bindings: interrupt-controller: Add Loongson HTVEC
  2020-04-22 14:24 [PATCH 0/6] Loongson PCH IRQ Support Jiaxun Yang
  2020-04-22 14:24 ` [PATCH 1/6] irqchip: Add Loongson HyperTransport Vector support Jiaxun Yang
@ 2020-04-22 14:24 ` Jiaxun Yang
  2020-04-22 14:24 ` [PATCH 3/6] irqchip: Add Loongson PCH PIC controller Jiaxun Yang
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 41+ messages in thread
From: Jiaxun Yang @ 2020-04-22 14:24 UTC (permalink / raw)
  To: linux-mips, maz
  Cc: Jiaxun Yang, Thomas Gleixner, Jason Cooper, Rob Herring,
	Huacai Chen, linux-kernel, devicetree

Add binding for Loongson-3 HyperTransport Interrupt Vector Controller.

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---
 .../interrupt-controller/loongson,htvec.yaml  | 59 +++++++++++++++++++
 1 file changed, 59 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/loongson,htvec.yaml

diff --git a/Documentation/devicetree/bindings/interrupt-controller/loongson,htvec.yaml b/Documentation/devicetree/bindings/interrupt-controller/loongson,htvec.yaml
new file mode 100644
index 000000000000..547a80c89eba
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/loongson,htvec.yaml
@@ -0,0 +1,59 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/interrupt-controller/loongson,htvec.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Loongson-3 HyperTransport Interrupt Vector Controller
+
+maintainers:
+  - Jiaxun Yang <jiaxun.yang@flygoat.com>
+
+allOf:
+  - $ref: /schemas/interrupt-controller.yaml#
+
+description: |
+  This interrupt controller is found in the Loongson-3 family of chips for
+  receiving vectorized interrupts from PCH's interrupt controller.
+
+properties:
+  compatible:
+    const: loongson,htvec-1.0
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    minItems: 1
+    maxItems: 4
+    description: |
+      Four parent interrupts that receive chained interrupts.
+
+  interrupt-controller: true
+
+  '#interrupt-cells':
+    const: 1
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - interrupt-controller
+  - '#interrupt-cells'
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    htvec: interrupt-controller@1fb000080 {
+      compatible = "loongson,htvec-1.0";
+      reg = <0xfb000080 0x40>;
+      interrupt-controller;
+      #interrupt-cells = <1>;
+
+      interrupt-parent = <&liointc>;
+      interrupts = <24 IRQ_TYPE_LEVEL_HIGH>,
+                    <25 IRQ_TYPE_LEVEL_HIGH>,
+                    <26 IRQ_TYPE_LEVEL_HIGH>,
+                    <27 IRQ_TYPE_LEVEL_HIGH>;
+    };
+...
-- 
2.26.0.rc2


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

* [PATCH 3/6] irqchip: Add Loongson PCH PIC controller
  2020-04-22 14:24 [PATCH 0/6] Loongson PCH IRQ Support Jiaxun Yang
  2020-04-22 14:24 ` [PATCH 1/6] irqchip: Add Loongson HyperTransport Vector support Jiaxun Yang
  2020-04-22 14:24 ` [PATCH 2/6] dt-bindings: interrupt-controller: Add Loongson HTVEC Jiaxun Yang
@ 2020-04-22 14:24 ` Jiaxun Yang
  2020-04-23  5:56   ` Huacai Chen
  2020-04-23 14:23   ` Marc Zyngier
  2020-04-22 14:24 ` [PATCH 4/6] dt-bindings: interrupt-controller: Add Loongson PCH PIC Jiaxun Yang
                   ` (5 subsequent siblings)
  8 siblings, 2 replies; 41+ messages in thread
From: Jiaxun Yang @ 2020-04-22 14:24 UTC (permalink / raw)
  To: linux-mips, maz
  Cc: Jiaxun Yang, Thomas Gleixner, Jason Cooper, Rob Herring,
	Huacai Chen, linux-kernel, devicetree

This controller appears on Loongson-7A family of PCH to transform
interrupts from devices into HyperTransport vectorized interrrupts
and send them to procrssor's HT vector controller.

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---
 drivers/irqchip/Kconfig                |   8 +
 drivers/irqchip/Makefile               |   1 +
 drivers/irqchip/irq-loongson-pch-pic.c | 256 +++++++++++++++++++++++++
 3 files changed, 265 insertions(+)
 create mode 100644 drivers/irqchip/irq-loongson-pch-pic.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index de4564e2ea88..4ab7a9b1a5c2 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -540,4 +540,12 @@ config LOONGSON_HTVEC
 	help
 	  Support for the Loongson3 HyperTransport Interrupt Vector Controller.
 
+config LOONGSON_PCH_PIC
+	bool "Loongson PCH PIC Controller"
+	depends on MACH_LOONGSON64 || COMPILE_TEST
+	default MACH_LOONGSON64
+	select IRQ_DOMAIN_HIERARCHY
+	help
+	  Support for the Loongson PCH PIC Controller.
+
 endmenu
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 74561879f5a7..acc72331cec8 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -108,3 +108,4 @@ obj-$(CONFIG_TI_SCI_INTA_IRQCHIP)	+= irq-ti-sci-inta.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
+obj-$(CONFIG_LOONGSON_PCH_PIC)		+= irq-loongson-pch-pic.o
diff --git a/drivers/irqchip/irq-loongson-pch-pic.c b/drivers/irqchip/irq-loongson-pch-pic.c
new file mode 100644
index 000000000000..717ab8335074
--- /dev/null
+++ b/drivers/irqchip/irq-loongson-pch-pic.c
@@ -0,0 +1,256 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com>
+ *  Loongson PCH PIC support
+ */
+
+#define pr_fmt(fmt) "pch-pic: " fmt
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.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>
+
+/* Registers */
+#define PCH_PIC_MASK		0x20
+#define PCH_PIC_HTMSI_EN	0x40
+#define PCH_PIC_EDGE		0x60
+#define PCH_PIC_CLR			0x80
+#define PCH_PIC_AUTO0		0xc0
+#define PCH_PIC_AUTO1		0xe0
+#define PCH_INT_ROUTE(irq)	(0x100 + irq)
+#define PCH_INT_HTVEC(irq)	(0x200 + irq)
+#define PCH_PIC_POL			0x3e0
+
+#define PIC_COUNT_PER_REG	32
+#define PIC_REG_COUNT		2
+#define PIC_COUNT			(PIC_COUNT_PER_REG * PIC_REG_COUNT)
+#define PIC_REG_IDX(irq_id)	((irq_id) / PIC_COUNT_PER_REG)
+#define PIC_REG_BIT(irq_id)	((irq_id) % PIC_COUNT_PER_REG)
+
+struct pch_pic {
+	void __iomem *base;
+	struct irq_domain *pic_domain;
+	int	ht_vec_base;
+	raw_spinlock_t pic_lock;
+};
+
+static void pch_pic_bitset(void __iomem *addr, int bit)
+{
+	u32 reg;
+
+	addr += PIC_REG_IDX(bit) * 4;
+	reg = readl(addr);
+	reg |= BIT(PIC_REG_BIT(bit));
+	writel(reg, addr);
+}
+
+static void pch_pic_bitclr(void __iomem *addr, int bit)
+{
+	u32 reg;
+
+	addr += PIC_REG_IDX(bit) * 4;
+	reg = readl(addr);
+	reg &= ~BIT(PIC_REG_BIT(bit));
+	writel(reg, addr);
+}
+
+static void pch_pic_eoi_irq(struct irq_data *d)
+{
+	struct pch_pic *priv = irq_data_get_irq_chip_data(d);
+	u32 idx = PIC_REG_IDX(d->hwirq);
+
+	writel(BIT(PIC_REG_BIT(d->hwirq)),
+			priv->base + PCH_PIC_CLR + idx * 4);
+}
+
+static void pch_pic_mask_irq(struct irq_data *d)
+{
+	struct pch_pic *priv = irq_data_get_irq_chip_data(d);
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&priv->pic_lock, flags);
+	pch_pic_bitset(priv->base + PCH_PIC_MASK, d->hwirq);
+	raw_spin_unlock_irqrestore(&priv->pic_lock, flags);
+	irq_chip_mask_parent(d);
+}
+
+static void pch_pic_unmask_irq(struct irq_data *d)
+{
+	struct pch_pic *priv = irq_data_get_irq_chip_data(d);
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&priv->pic_lock, flags);
+	pch_pic_bitclr(priv->base + PCH_PIC_MASK, d->hwirq);
+	raw_spin_unlock_irqrestore(&priv->pic_lock, flags);
+	irq_chip_unmask_parent(d);
+}
+
+static int pch_pic_set_type(struct irq_data *d, unsigned int type)
+{
+	struct pch_pic *priv = irq_data_get_irq_chip_data(d);
+	int ret = 0;
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&priv->pic_lock, flags);
+	switch (type) {
+	case IRQ_TYPE_EDGE_RISING:
+		pch_pic_bitset(priv->base + PCH_PIC_EDGE, d->hwirq);
+		pch_pic_bitclr(priv->base + PCH_PIC_POL, d->hwirq);
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		pch_pic_bitset(priv->base + PCH_PIC_EDGE, d->hwirq);
+		pch_pic_bitset(priv->base + PCH_PIC_POL, d->hwirq);
+		break;
+	case IRQ_TYPE_LEVEL_HIGH:
+		pch_pic_bitclr(priv->base + PCH_PIC_EDGE, d->hwirq);
+		pch_pic_bitclr(priv->base + PCH_PIC_POL, d->hwirq);
+		break;
+	case IRQ_TYPE_LEVEL_LOW:
+		pch_pic_bitclr(priv->base + PCH_PIC_EDGE, d->hwirq);
+		pch_pic_bitset(priv->base + PCH_PIC_POL, d->hwirq);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	raw_spin_unlock_irqrestore(&priv->pic_lock, flags);
+
+	return ret;
+}
+
+static void pch_pic_enable_irq(struct irq_data *d)
+{
+	struct pch_pic *priv = irq_data_get_irq_chip_data(d);
+	u8 htvec = d->hwirq + priv->ht_vec_base;
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&priv->pic_lock, flags);
+	writeb(htvec, priv->base + PCH_INT_HTVEC(d->hwirq));
+	/* Hardcode to HT0 Lo */
+	writeb(1, priv->base + PCH_INT_ROUTE(d->hwirq));
+	/* Clear auto bounce, we don't need that */
+	pch_pic_bitclr(priv->base + PCH_PIC_AUTO0, d->hwirq);
+	pch_pic_bitclr(priv->base + PCH_PIC_AUTO1, d->hwirq);
+	/* Enable HTMSI transformer */
+	pch_pic_bitset(priv->base + PCH_PIC_HTMSI_EN, d->hwirq);
+	raw_spin_unlock_irqrestore(&priv->pic_lock, flags);
+	pch_pic_unmask_irq(d);
+}
+
+static struct irq_chip pch_pic_irq_chip = {
+	.name			= "PCH PIC",
+	.irq_eoi		= pch_pic_eoi_irq,
+	.irq_mask		= pch_pic_mask_irq,
+	.irq_unmask		= pch_pic_unmask_irq,
+	.irq_enable		= pch_pic_enable_irq,
+	.irq_disable	= pch_pic_mask_irq,
+	.irq_set_affinity	= irq_chip_set_affinity_parent,
+	.irq_set_type		= pch_pic_set_type,
+};
+
+static int pch_pic_alloc(struct irq_domain *domain, unsigned int virq,
+			      unsigned int nr_irqs, void *arg)
+{
+	struct pch_pic *priv = domain->host_data;
+	struct irq_fwspec fwspec;
+	unsigned long hwirq;
+	unsigned int type;
+	int err;
+
+	irq_domain_translate_twocell(domain, arg, &hwirq, &type);
+
+	fwspec.fwnode = domain->parent->fwnode;
+	fwspec.param_count = 1;
+	fwspec.param[0] = hwirq + priv->ht_vec_base;
+
+	err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
+	if (err)
+		return err;
+
+	irq_domain_set_info(domain, virq, hwirq,
+			    &pch_pic_irq_chip, priv,
+			    handle_fasteoi_irq, NULL, NULL);
+	irq_set_probe(virq);
+
+	return 0;
+}
+
+static const struct irq_domain_ops pch_pic_domain_ops = {
+	.translate = irq_domain_translate_twocell,
+	.alloc	= pch_pic_alloc,
+	.free	= irq_domain_free_irqs_parent,
+};
+
+static void pch_pic_reset(struct pch_pic *priv)
+{
+	u32 idx;
+
+	/* Clear IRQ cause registers, mask all interrupts */
+	for (idx = 0; idx < PIC_REG_COUNT; idx++) {
+		writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_MASK + 4 * idx);
+		writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_CLR + 4 * idx);
+	}
+}
+
+static int pch_pic_of_init(struct device_node *node,
+				struct device_node *parent)
+{
+	struct pch_pic *priv;
+	struct irq_domain *parent_domain;
+	int err;
+	u32 ht_vec_base;
+
+	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;
+		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;
+	}
+
+	if (of_property_read_u32(node, "loongson,pic-base-vec",
+				&ht_vec_base))
+		priv->ht_vec_base = 64;
+	else
+		priv->ht_vec_base = ht_vec_base;
+
+	priv->pic_domain = irq_domain_create_hierarchy(parent_domain, 0,
+						     PIC_COUNT,
+						     of_node_to_fwnode(node),
+						     &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);
+
+	return 0;
+
+iounmap_base:
+	iounmap(priv->base);
+free_priv:
+	kfree(priv);
+
+	return err;
+}
+
+IRQCHIP_DECLARE(pch_pic, "loongson,pch-pic-1.0", pch_pic_of_init);
-- 
2.26.0.rc2


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

* [PATCH 4/6] dt-bindings: interrupt-controller: Add Loongson PCH PIC
  2020-04-22 14:24 [PATCH 0/6] Loongson PCH IRQ Support Jiaxun Yang
                   ` (2 preceding siblings ...)
  2020-04-22 14:24 ` [PATCH 3/6] irqchip: Add Loongson PCH PIC controller Jiaxun Yang
@ 2020-04-22 14:24 ` Jiaxun Yang
  2020-04-23  5:54   ` Huacai Chen
  2020-04-22 14:24 ` [PATCH 5/6] irqchip: Add Loongson PCH MSI controller Jiaxun Yang
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 41+ messages in thread
From: Jiaxun Yang @ 2020-04-22 14:24 UTC (permalink / raw)
  To: linux-mips, maz
  Cc: Jiaxun Yang, Thomas Gleixner, Jason Cooper, Rob Herring,
	Huacai Chen, linux-kernel, devicetree

Add binding for Loongson PCH PIC Controller.

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---
 .../loongson,pch-pic.yaml                     | 55 +++++++++++++++++++
 1 file changed, 55 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/loongson,pch-pic.yaml

diff --git a/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-pic.yaml b/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-pic.yaml
new file mode 100644
index 000000000000..afc0c924e477
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-pic.yaml
@@ -0,0 +1,55 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/interrupt-controller/loongson,pch-pic.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Loongson PCH PIC Controller
+
+maintainers:
+  - Jiaxun Yang <jiaxun.yang@flygoat.com>
+
+allOf:
+  - $ref: /schemas/interrupt-controller.yaml#
+
+description: |
+  This interrupt controller is found in the Loongson-7A family of PCH for
+  transforming interrupts from on-chip devices into HyperTransport vectorized
+  interrupts.
+
+properties:
+  compatible:
+    const: loongson,pch-pic-1.0
+
+  reg:
+    maxItems: 1
+
+  loongson,pic-base-vec:
+    $ref: '/schemas/types.yaml#/definitions/uint32'
+    description: |
+      u32 value of the base of parent HyperTransport vector allocated
+      to PCH PIC.
+
+  interrupt-controller: true
+
+  '#interrupt-cells':
+    const: 2
+
+required:
+  - compatible
+  - reg
+  - interrupt-controller
+  - '#interrupt-cells'
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    pic: interrupt-controller@10000000 {
+      compatible = "loongson,pch-pic-1.0";
+      reg = <0x10000000 0x400>;
+      interrupt-controller;
+      #interrupt-cells = <2>;
+      loongson,pic-base-vec = <64>;
+      interrupt-parent = <&htvec>;
+    };
+...
-- 
2.26.0.rc2


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

* [PATCH 5/6] irqchip: Add Loongson PCH MSI controller
  2020-04-22 14:24 [PATCH 0/6] Loongson PCH IRQ Support Jiaxun Yang
                   ` (3 preceding siblings ...)
  2020-04-22 14:24 ` [PATCH 4/6] dt-bindings: interrupt-controller: Add Loongson PCH PIC Jiaxun Yang
@ 2020-04-22 14:24 ` Jiaxun Yang
  2020-04-23  5:57   ` Huacai Chen
  2020-04-23 14:41   ` Marc Zyngier
  2020-04-22 14:24 ` [PATCH 6/6] dt-bindings: interrupt-controller: Add Loongson PCH MSI Jiaxun Yang
                   ` (3 subsequent siblings)
  8 siblings, 2 replies; 41+ messages in thread
From: Jiaxun Yang @ 2020-04-22 14:24 UTC (permalink / raw)
  To: linux-mips, maz
  Cc: Jiaxun Yang, Thomas Gleixner, Jason Cooper, Rob Herring,
	Huacai Chen, linux-kernel, devicetree

This controller appears on Loongson-7A family of PCH to transform
interrupts from PCI MSI into HyperTransport vectorized interrrupts
and send them to procrssor's HT vector controller.

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---
 drivers/irqchip/Kconfig                |  10 +
 drivers/irqchip/Makefile               |   1 +
 drivers/irqchip/irq-loongson-pch-msi.c | 265 +++++++++++++++++++++++++
 3 files changed, 276 insertions(+)
 create mode 100644 drivers/irqchip/irq-loongson-pch-msi.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 4ab7a9b1a5c2..9f2935418f33 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -548,4 +548,14 @@ config LOONGSON_PCH_PIC
 	help
 	  Support for the Loongson PCH PIC Controller.
 
+config LOONGSON_PCH_MSI
+	bool "Loongson PCH PIC Controller"
+	depends on MACH_LOONGSON64 || COMPILE_TEST
+	depends on PCI
+	default MACH_LOONGSON64
+	select IRQ_DOMAIN_HIERARCHY
+	select PCI_MSI
+	help
+	  Support for the Loongson PCH MSI Controller.
+
 endmenu
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index acc72331cec8..3a4ce283189a 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -109,3 +109,4 @@ obj-$(CONFIG_LOONGSON_LIOINTC)		+= irq-loongson-liointc.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
+obj-$(CONFIG_LOONGSON_PCH_MSI)		+= irq-loongson-pch-msi.o
diff --git a/drivers/irqchip/irq-loongson-pch-msi.c b/drivers/irqchip/irq-loongson-pch-msi.c
new file mode 100644
index 000000000000..4bb00f2ce86a
--- /dev/null
+++ b/drivers/irqchip/irq-loongson-pch-msi.c
@@ -0,0 +1,265 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com>
+ *  Loongson PCH MSI support
+ */
+
+#define pr_fmt(fmt) "pch-msi: " fmt
+
+#include <linux/irqchip.h>
+#include <linux/msi.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_pci.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+struct pch_msi_data {
+	spinlock_t msi_map_lock;
+	phys_addr_t addr;
+	u32 irq_first;		/* The vectoe number that MSIs start */
+	u32 num_irqs;		/* The number of vector for MSIs */
+	unsigned long *msi_map;
+};
+
+static void pch_msi_mask_msi_irq(struct irq_data *d)
+{
+	pci_msi_mask_irq(d);
+	irq_chip_mask_parent(d);
+}
+
+static void pch_msi_unmask_msi_irq(struct irq_data *d)
+{
+	pci_msi_unmask_irq(d);
+	irq_chip_unmask_parent(d);
+}
+
+static struct irq_chip pch_msi_irq_chip = {
+	.name			= "PCH MSI",
+	.irq_mask		= pch_msi_mask_msi_irq,
+	.irq_unmask		= pch_msi_unmask_msi_irq,
+	.irq_set_affinity	= irq_chip_set_affinity_parent,
+};
+
+static int pch_msi_allocate_hwirq(struct pch_msi_data *priv, int num_req)
+{
+	int first;
+
+	spin_lock(&priv->msi_map_lock);
+
+	first = bitmap_find_next_zero_area(priv->msi_map, priv->num_irqs, 0,
+					   num_req, 0);
+	if (first >= priv->num_irqs) {
+		spin_unlock(&priv->msi_map_lock);
+		return -ENOSPC;
+	}
+
+	bitmap_set(priv->msi_map, first, num_req);
+
+	spin_unlock(&priv->msi_map_lock);
+
+	return priv->irq_first + first;
+}
+
+static void pch_msi_free_hwirq(struct pch_msi_data *priv,
+				int hwirq, int num_req)
+{
+	int first = hwirq - priv->irq_first;
+
+	spin_lock(&priv->msi_map_lock);
+
+	bitmap_clear(priv->msi_map, first, num_req);
+
+	spin_unlock(&priv->msi_map_lock);
+}
+
+static void pch_msi_compose_msi_msg(struct irq_data *data,
+					struct msi_msg *msg)
+{
+	struct pch_msi_data *priv = irq_data_get_irq_chip_data(data);
+	phys_addr_t msg_addr = priv->addr;
+
+	msg->address_hi = upper_32_bits(msg_addr);
+	msg->address_lo = lower_32_bits(msg_addr);
+	msg->data = data->hwirq;
+}
+
+static struct msi_domain_info pch_msi_domain_info = {
+	.flags		= MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+			  MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX,
+	.chip	= &pch_msi_irq_chip,
+};
+
+static struct irq_chip middle_irq_chip = {
+	.name			= "PCH MSI Middle",
+	.irq_mask		= irq_chip_mask_parent,
+	.irq_unmask		= irq_chip_unmask_parent,
+	.irq_set_affinity	= irq_chip_set_affinity_parent,
+	.irq_compose_msi_msg	= pch_msi_compose_msi_msg,
+};
+
+static int pch_msi_parent_domain_alloc(struct irq_domain *domain,
+					unsigned int virq, int hwirq)
+{
+	struct irq_fwspec fwspec;
+	int ret;
+
+	if (!is_of_node(domain->parent->fwnode))
+		return -EINVAL;
+
+	fwspec.fwnode = domain->parent->fwnode;
+	fwspec.param_count = 1;
+	fwspec.param[0] = hwirq;
+
+	ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
+	if (ret)
+		return ret;
+
+	irq_domain_set_info(domain, virq, hwirq,
+			    &middle_irq_chip, NULL,
+			    handle_simple_irq, NULL, NULL);
+	irq_set_probe(virq);
+
+	return 0;
+}
+
+static int pch_msi_middle_domain_alloc(struct irq_domain *domain,
+					   unsigned int virq,
+					   unsigned int nr_irqs, void *args)
+{
+	struct pch_msi_data *priv = domain->host_data;
+	int hwirq, err, i;
+
+	hwirq = pch_msi_allocate_hwirq(priv, nr_irqs);
+	if (hwirq < 0)
+		return hwirq;
+
+	for (i = 0; i < nr_irqs; i++) {
+		err = pch_msi_parent_domain_alloc(domain, virq + i, hwirq + i);
+		if (err)
+			goto err_hwirq;
+
+		irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
+					      &middle_irq_chip, priv);
+	}
+
+	return 0;
+err_hwirq:
+	while (--i >= 0)
+		irq_domain_free_irqs_parent(domain, virq, i);
+
+	pch_msi_free_hwirq(priv, hwirq, nr_irqs);
+	return err;
+}
+
+static void pch_msi_middle_domain_free(struct irq_domain *domain,
+					   unsigned int virq,
+					   unsigned int nr_irqs)
+{
+	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+	struct pch_msi_data *priv = irq_data_get_irq_chip_data(d);
+
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+	pch_msi_free_hwirq(priv, d->hwirq, nr_irqs);
+}
+
+static const struct irq_domain_ops pch_msi_middle_domain_ops = {
+	.alloc	= pch_msi_middle_domain_alloc,
+	.free	= pch_msi_middle_domain_free,
+};
+
+static int pch_msi_init_domains(struct pch_msi_data *priv,
+				struct device_node *node,
+				struct device_node *parent)
+{
+	struct irq_domain *middle_domain, *msi_domain, *parent_domain;
+
+	parent_domain = irq_find_host(parent);
+	if (!parent_domain) {
+		pr_err("Failed to find the parent domain\n");
+		return -ENXIO;
+	}
+
+	middle_domain = irq_domain_add_tree(NULL,
+					    &pch_msi_middle_domain_ops,
+					    priv);
+	if (!middle_domain) {
+		pr_err("Failed to create the MSI middle domain\n");
+		return -ENOMEM;
+	}
+
+	middle_domain->parent = parent_domain;
+
+	msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(node),
+					       &pch_msi_domain_info,
+					       middle_domain);
+	if (!msi_domain) {
+		pr_err("Failed to create MSI domain\n");
+		irq_domain_remove(middle_domain);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static int pch_msi_init(struct device_node *node,
+			    struct device_node *parent)
+{
+	struct pch_msi_data *priv;
+	struct resource res;
+	int ret;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	spin_lock_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->addr = 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->msi_map = kcalloc(BITS_TO_LONGS(priv->num_irqs),
+				sizeof(*priv->msi_map),
+				GFP_KERNEL);
+	if (!priv->msi_map) {
+		ret = -ENOMEM;
+		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);
+	if (ret)
+		goto err_map;
+
+	return 0;
+
+err_map:
+	kfree(priv->msi_map);
+err_priv:
+	kfree(priv);
+	return ret;
+}
+
+IRQCHIP_DECLARE(pch_msi, "loongson,pch-msi-1.0", pch_msi_init);
-- 
2.26.0.rc2


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

* [PATCH 6/6] dt-bindings: interrupt-controller: Add Loongson PCH MSI
  2020-04-22 14:24 [PATCH 0/6] Loongson PCH IRQ Support Jiaxun Yang
                   ` (4 preceding siblings ...)
  2020-04-22 14:24 ` [PATCH 5/6] irqchip: Add Loongson PCH MSI controller Jiaxun Yang
@ 2020-04-22 14:24 ` Jiaxun Yang
  2020-04-23  5:55   ` Huacai Chen
  2020-04-23  5:50 ` [PATCH 0/6] Loongson PCH IRQ Support Huacai Chen
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 41+ messages in thread
From: Jiaxun Yang @ 2020-04-22 14:24 UTC (permalink / raw)
  To: linux-mips, maz
  Cc: Jiaxun Yang, Thomas Gleixner, Jason Cooper, Rob Herring,
	Huacai Chen, linux-kernel, devicetree

Add binding for Loongson PCH MSI controller.

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---
 .../loongson,pch-msi.yaml                     | 56 +++++++++++++++++++
 1 file changed, 56 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml

diff --git a/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml b/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml
new file mode 100644
index 000000000000..dfb9cecacba0
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml
@@ -0,0 +1,56 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/interrupt-controller/loongson,pch-msi.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Loongson PCH MSI Controller
+
+maintainers:
+  - Jiaxun Yang <jiaxun.yang@flygoat.com>
+
+description: |
+  This interrupt controller is found in the Loongson-7A family of PCH for
+  transforming interrupts from PCIe MSI into HyperTransport vectorized
+  interrupts.
+
+properties:
+  compatible:
+    const: loongson,pch-msi-1.0
+
+  reg:
+    maxItems: 1
+
+  loongson,msi-base-vec:
+    $ref: '/schemas/types.yaml#/definitions/uint32'
+    description: |
+      u32 value of the base of parent HyperTransport vector allocated
+      to PCH MSI.
+
+  loongson,msi-num-vecs:
+    $ref: '/schemas/types.yaml#/definitions/uint32'
+    description: |
+      u32 value of the number of parent HyperTransport vectors allocated
+      to PCH MSI.
+
+  msi-controller: true
+
+required:
+  - compatible
+  - reg
+  - msi-controller
+  - loongson,msi-base-vec
+  - loongson,msi-num-vecs
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    msi: msi-controller@2ff00000 {
+      compatible = "loongson,pch-msi-1.0";
+      reg = <0x2ff00000 0x4>;
+      msi-controller;
+      loongson,msi-base-vec = <64>;
+      loongson,msi-num-vecs = <64>;
+      interrupt-parent = <&htvec>;
+    };
+...
-- 
2.26.0.rc2


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

* Re: [PATCH 0/6] Loongson PCH IRQ Support
  2020-04-22 14:24 [PATCH 0/6] Loongson PCH IRQ Support Jiaxun Yang
                   ` (5 preceding siblings ...)
  2020-04-22 14:24 ` [PATCH 6/6] dt-bindings: interrupt-controller: Add Loongson PCH MSI Jiaxun Yang
@ 2020-04-23  5:50 ` Huacai Chen
  2020-04-28  6:32 ` [PATCH v2 1/6] irqchip: Add Loongson HyperTransport Vector support Jiaxun Yang
  2020-05-01  9:21 ` [PATCH v3 " Jiaxun Yang
  8 siblings, 0 replies; 41+ messages in thread
From: Huacai Chen @ 2020-04-23  5:50 UTC (permalink / raw)
  To: Jiaxun Yang
  Cc: open list:MIPS, Marc Zyngier, Thomas Gleixner, Jason Cooper,
	Rob Herring, LKML, devicetree

Hi, Jiaxun,

We have established a rule before, so please don't call the PCH bridge
as Loongson-7A or Loongson-7A1000. You can call it LS7A, or Loongson's
LS7A if needed.

Huacai

On Wed, Apr 22, 2020 at 10:26 PM Jiaxun Yang <jiaxun.yang@flygoat.com> wrote:
>
> This series mainly added IRQ support for Loongson-7A1000 PCH.
> DeviceTree will be added later as PCI support is also pending
> for reviewing.
>
> Jiaxun Yang (6):
>   irqchip: Add Loongson HyperTransport Vector support
>   dt-bindings: interrupt-controller: Add Loongson HTVEC
>   irqchip: Add Loongson PCH PIC controller
>   dt-bindings: interrupt-controller: Add Loongson PCH PIC
>   irqchip: Add Loongson PCH MSI controller
>   dt-bindings: interrupt-controller: Add Loongson PCH MSI
>
>  .../interrupt-controller/loongson,htvec.yaml  |  59 ++++
>  .../loongson,pch-msi.yaml                     |  56 ++++
>  .../loongson,pch-pic.yaml                     |  55 ++++
>  drivers/irqchip/Kconfig                       |  26 ++
>  drivers/irqchip/Makefile                      |   3 +
>  drivers/irqchip/irq-loongson-htvec.c          | 217 ++++++++++++++
>  drivers/irqchip/irq-loongson-pch-msi.c        | 265 ++++++++++++++++++
>  drivers/irqchip/irq-loongson-pch-pic.c        | 256 +++++++++++++++++
>  8 files changed, 937 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/loongson,htvec.yaml
>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml
>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/loongson,pch-pic.yaml
>  create mode 100644 drivers/irqchip/irq-loongson-htvec.c
>  create mode 100644 drivers/irqchip/irq-loongson-pch-msi.c
>  create mode 100644 drivers/irqchip/irq-loongson-pch-pic.c
>
> --
> 2.26.0.rc2
>

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

* Re: [PATCH 4/6] dt-bindings: interrupt-controller: Add Loongson PCH PIC
  2020-04-22 14:24 ` [PATCH 4/6] dt-bindings: interrupt-controller: Add Loongson PCH PIC Jiaxun Yang
@ 2020-04-23  5:54   ` Huacai Chen
  0 siblings, 0 replies; 41+ messages in thread
From: Huacai Chen @ 2020-04-23  5:54 UTC (permalink / raw)
  To: Jiaxun Yang
  Cc: open list:MIPS, Marc Zyngier, Thomas Gleixner, Jason Cooper,
	Rob Herring, LKML, devicetree

Hi, Jiaxun,

On Wed, Apr 22, 2020 at 10:27 PM Jiaxun Yang <jiaxun.yang@flygoat.com> wrote:
>
> Add binding for Loongson PCH PIC Controller.
>
> Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
> ---
>  .../loongson,pch-pic.yaml                     | 55 +++++++++++++++++++
>  1 file changed, 55 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/loongson,pch-pic.yaml
>
> diff --git a/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-pic.yaml b/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-pic.yaml
> new file mode 100644
> index 000000000000..afc0c924e477
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-pic.yaml
> @@ -0,0 +1,55 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: "http://devicetree.org/schemas/interrupt-controller/loongson,pch-pic.yaml#"
> +$schema: "http://devicetree.org/meta-schemas/core.yaml#"
> +
> +title: Loongson PCH PIC Controller
> +
> +maintainers:
> +  - Jiaxun Yang <jiaxun.yang@flygoat.com>
> +
> +allOf:
> +  - $ref: /schemas/interrupt-controller.yaml#
> +
> +description: |
> +  This interrupt controller is found in the Loongson-7A family of PCH for
Please use "Loongson's LS7A family" here.

> +  transforming interrupts from on-chip devices into HyperTransport vectorized
> +  interrupts.
> +
> +properties:
> +  compatible:
> +    const: loongson,pch-pic-1.0
> +
> +  reg:
> +    maxItems: 1
> +
> +  loongson,pic-base-vec:
> +    $ref: '/schemas/types.yaml#/definitions/uint32'
> +    description: |
> +      u32 value of the base of parent HyperTransport vector allocated
> +      to PCH PIC.
> +
> +  interrupt-controller: true
> +
> +  '#interrupt-cells':
> +    const: 2
> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupt-controller
> +  - '#interrupt-cells'
> +
> +examples:
> +  - |
> +    #include <dt-bindings/interrupt-controller/irq.h>
> +    pic: interrupt-controller@10000000 {
> +      compatible = "loongson,pch-pic-1.0";
> +      reg = <0x10000000 0x400>;
> +      interrupt-controller;
> +      #interrupt-cells = <2>;
> +      loongson,pic-base-vec = <64>;
> +      interrupt-parent = <&htvec>;
> +    };
> +...
> --
> 2.26.0.rc2
>

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

* Re: [PATCH 6/6] dt-bindings: interrupt-controller: Add Loongson PCH MSI
  2020-04-22 14:24 ` [PATCH 6/6] dt-bindings: interrupt-controller: Add Loongson PCH MSI Jiaxun Yang
@ 2020-04-23  5:55   ` Huacai Chen
  2020-04-23 12:43     ` Marc Zyngier
  0 siblings, 1 reply; 41+ messages in thread
From: Huacai Chen @ 2020-04-23  5:55 UTC (permalink / raw)
  To: Jiaxun Yang
  Cc: open list:MIPS, Marc Zyngier, Thomas Gleixner, Jason Cooper,
	Rob Herring, LKML, devicetree

Hi, Jiaxun,

On Wed, Apr 22, 2020 at 10:28 PM Jiaxun Yang <jiaxun.yang@flygoat.com> wrote:
>
> Add binding for Loongson PCH MSI controller.
>
> Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
> ---
>  .../loongson,pch-msi.yaml                     | 56 +++++++++++++++++++
>  1 file changed, 56 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml
>
> diff --git a/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml b/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml
> new file mode 100644
> index 000000000000..dfb9cecacba0
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml
> @@ -0,0 +1,56 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: "http://devicetree.org/schemas/interrupt-controller/loongson,pch-msi.yaml#"
> +$schema: "http://devicetree.org/meta-schemas/core.yaml#"
> +
> +title: Loongson PCH MSI Controller
> +
> +maintainers:
> +  - Jiaxun Yang <jiaxun.yang@flygoat.com>
> +
> +description: |
> +  This interrupt controller is found in the Loongson-7A family of PCH for
Please use "Loongson's LS7A family" here.

> +  transforming interrupts from PCIe MSI into HyperTransport vectorized
> +  interrupts.
> +
> +properties:
> +  compatible:
> +    const: loongson,pch-msi-1.0
> +
> +  reg:
> +    maxItems: 1
> +
> +  loongson,msi-base-vec:
> +    $ref: '/schemas/types.yaml#/definitions/uint32'
> +    description: |
> +      u32 value of the base of parent HyperTransport vector allocated
> +      to PCH MSI.
> +
> +  loongson,msi-num-vecs:
> +    $ref: '/schemas/types.yaml#/definitions/uint32'
> +    description: |
> +      u32 value of the number of parent HyperTransport vectors allocated
> +      to PCH MSI.
> +
> +  msi-controller: true
> +
> +required:
> +  - compatible
> +  - reg
> +  - msi-controller
> +  - loongson,msi-base-vec
> +  - loongson,msi-num-vecs
> +
> +examples:
> +  - |
> +    #include <dt-bindings/interrupt-controller/irq.h>
> +    msi: msi-controller@2ff00000 {
> +      compatible = "loongson,pch-msi-1.0";
> +      reg = <0x2ff00000 0x4>;
> +      msi-controller;
> +      loongson,msi-base-vec = <64>;
> +      loongson,msi-num-vecs = <64>;
> +      interrupt-parent = <&htvec>;
> +    };
> +...
> --
> 2.26.0.rc2
>

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

* Re: [PATCH 3/6] irqchip: Add Loongson PCH PIC controller
  2020-04-22 14:24 ` [PATCH 3/6] irqchip: Add Loongson PCH PIC controller Jiaxun Yang
@ 2020-04-23  5:56   ` Huacai Chen
  2020-04-23 14:23   ` Marc Zyngier
  1 sibling, 0 replies; 41+ messages in thread
From: Huacai Chen @ 2020-04-23  5:56 UTC (permalink / raw)
  To: Jiaxun Yang
  Cc: open list:MIPS, Marc Zyngier, Thomas Gleixner, Jason Cooper,
	Rob Herring, LKML, devicetree

Hi, Jiaxun,

On Wed, Apr 22, 2020 at 10:26 PM Jiaxun Yang <jiaxun.yang@flygoat.com> wrote:
>
> This controller appears on Loongson-7A family of PCH to transform
Please use "Loongson's LS7A family" here.

> interrupts from devices into HyperTransport vectorized interrrupts
> and send them to procrssor's HT vector controller.
>
> Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
> ---
>  drivers/irqchip/Kconfig                |   8 +
>  drivers/irqchip/Makefile               |   1 +
>  drivers/irqchip/irq-loongson-pch-pic.c | 256 +++++++++++++++++++++++++
>  3 files changed, 265 insertions(+)
>  create mode 100644 drivers/irqchip/irq-loongson-pch-pic.c
>
> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> index de4564e2ea88..4ab7a9b1a5c2 100644
> --- a/drivers/irqchip/Kconfig
> +++ b/drivers/irqchip/Kconfig
> @@ -540,4 +540,12 @@ config LOONGSON_HTVEC
>         help
>           Support for the Loongson3 HyperTransport Interrupt Vector Controller.
>
> +config LOONGSON_PCH_PIC
> +       bool "Loongson PCH PIC Controller"
> +       depends on MACH_LOONGSON64 || COMPILE_TEST
> +       default MACH_LOONGSON64
> +       select IRQ_DOMAIN_HIERARCHY
> +       help
> +         Support for the Loongson PCH PIC Controller.
> +
>  endmenu
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index 74561879f5a7..acc72331cec8 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -108,3 +108,4 @@ obj-$(CONFIG_TI_SCI_INTA_IRQCHIP)   += irq-ti-sci-inta.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
> +obj-$(CONFIG_LOONGSON_PCH_PIC)         += irq-loongson-pch-pic.o
> diff --git a/drivers/irqchip/irq-loongson-pch-pic.c b/drivers/irqchip/irq-loongson-pch-pic.c
> new file mode 100644
> index 000000000000..717ab8335074
> --- /dev/null
> +++ b/drivers/irqchip/irq-loongson-pch-pic.c
> @@ -0,0 +1,256 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + *  Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com>
> + *  Loongson PCH PIC support
> + */
> +
> +#define pr_fmt(fmt) "pch-pic: " fmt
> +
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/irqchip.h>
> +#include <linux/irqdomain.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>
> +
> +/* Registers */
> +#define PCH_PIC_MASK           0x20
> +#define PCH_PIC_HTMSI_EN       0x40
> +#define PCH_PIC_EDGE           0x60
> +#define PCH_PIC_CLR                    0x80
> +#define PCH_PIC_AUTO0          0xc0
> +#define PCH_PIC_AUTO1          0xe0
> +#define PCH_INT_ROUTE(irq)     (0x100 + irq)
> +#define PCH_INT_HTVEC(irq)     (0x200 + irq)
> +#define PCH_PIC_POL                    0x3e0
> +
> +#define PIC_COUNT_PER_REG      32
> +#define PIC_REG_COUNT          2
> +#define PIC_COUNT                      (PIC_COUNT_PER_REG * PIC_REG_COUNT)
> +#define PIC_REG_IDX(irq_id)    ((irq_id) / PIC_COUNT_PER_REG)
> +#define PIC_REG_BIT(irq_id)    ((irq_id) % PIC_COUNT_PER_REG)
> +
> +struct pch_pic {
> +       void __iomem *base;
> +       struct irq_domain *pic_domain;
> +       int     ht_vec_base;
> +       raw_spinlock_t pic_lock;
> +};
> +
> +static void pch_pic_bitset(void __iomem *addr, int bit)
> +{
> +       u32 reg;
> +
> +       addr += PIC_REG_IDX(bit) * 4;
> +       reg = readl(addr);
> +       reg |= BIT(PIC_REG_BIT(bit));
> +       writel(reg, addr);
> +}
> +
> +static void pch_pic_bitclr(void __iomem *addr, int bit)
> +{
> +       u32 reg;
> +
> +       addr += PIC_REG_IDX(bit) * 4;
> +       reg = readl(addr);
> +       reg &= ~BIT(PIC_REG_BIT(bit));
> +       writel(reg, addr);
> +}
> +
> +static void pch_pic_eoi_irq(struct irq_data *d)
> +{
> +       struct pch_pic *priv = irq_data_get_irq_chip_data(d);
> +       u32 idx = PIC_REG_IDX(d->hwirq);
> +
> +       writel(BIT(PIC_REG_BIT(d->hwirq)),
> +                       priv->base + PCH_PIC_CLR + idx * 4);
> +}
> +
> +static void pch_pic_mask_irq(struct irq_data *d)
> +{
> +       struct pch_pic *priv = irq_data_get_irq_chip_data(d);
> +       unsigned long flags;
> +
> +       raw_spin_lock_irqsave(&priv->pic_lock, flags);
> +       pch_pic_bitset(priv->base + PCH_PIC_MASK, d->hwirq);
> +       raw_spin_unlock_irqrestore(&priv->pic_lock, flags);
> +       irq_chip_mask_parent(d);
> +}
> +
> +static void pch_pic_unmask_irq(struct irq_data *d)
> +{
> +       struct pch_pic *priv = irq_data_get_irq_chip_data(d);
> +       unsigned long flags;
> +
> +       raw_spin_lock_irqsave(&priv->pic_lock, flags);
> +       pch_pic_bitclr(priv->base + PCH_PIC_MASK, d->hwirq);
> +       raw_spin_unlock_irqrestore(&priv->pic_lock, flags);
> +       irq_chip_unmask_parent(d);
> +}
> +
> +static int pch_pic_set_type(struct irq_data *d, unsigned int type)
> +{
> +       struct pch_pic *priv = irq_data_get_irq_chip_data(d);
> +       int ret = 0;
> +       unsigned long flags;
> +
> +       raw_spin_lock_irqsave(&priv->pic_lock, flags);
> +       switch (type) {
> +       case IRQ_TYPE_EDGE_RISING:
> +               pch_pic_bitset(priv->base + PCH_PIC_EDGE, d->hwirq);
> +               pch_pic_bitclr(priv->base + PCH_PIC_POL, d->hwirq);
> +               break;
> +       case IRQ_TYPE_EDGE_FALLING:
> +               pch_pic_bitset(priv->base + PCH_PIC_EDGE, d->hwirq);
> +               pch_pic_bitset(priv->base + PCH_PIC_POL, d->hwirq);
> +               break;
> +       case IRQ_TYPE_LEVEL_HIGH:
> +               pch_pic_bitclr(priv->base + PCH_PIC_EDGE, d->hwirq);
> +               pch_pic_bitclr(priv->base + PCH_PIC_POL, d->hwirq);
> +               break;
> +       case IRQ_TYPE_LEVEL_LOW:
> +               pch_pic_bitclr(priv->base + PCH_PIC_EDGE, d->hwirq);
> +               pch_pic_bitset(priv->base + PCH_PIC_POL, d->hwirq);
> +               break;
> +       default:
> +               ret = -EINVAL;
> +               break;
> +       }
> +       raw_spin_unlock_irqrestore(&priv->pic_lock, flags);
> +
> +       return ret;
> +}
> +
> +static void pch_pic_enable_irq(struct irq_data *d)
> +{
> +       struct pch_pic *priv = irq_data_get_irq_chip_data(d);
> +       u8 htvec = d->hwirq + priv->ht_vec_base;
> +       unsigned long flags;
> +
> +       raw_spin_lock_irqsave(&priv->pic_lock, flags);
> +       writeb(htvec, priv->base + PCH_INT_HTVEC(d->hwirq));
> +       /* Hardcode to HT0 Lo */
> +       writeb(1, priv->base + PCH_INT_ROUTE(d->hwirq));
> +       /* Clear auto bounce, we don't need that */
> +       pch_pic_bitclr(priv->base + PCH_PIC_AUTO0, d->hwirq);
> +       pch_pic_bitclr(priv->base + PCH_PIC_AUTO1, d->hwirq);
> +       /* Enable HTMSI transformer */
> +       pch_pic_bitset(priv->base + PCH_PIC_HTMSI_EN, d->hwirq);
> +       raw_spin_unlock_irqrestore(&priv->pic_lock, flags);
> +       pch_pic_unmask_irq(d);
> +}
> +
> +static struct irq_chip pch_pic_irq_chip = {
> +       .name                   = "PCH PIC",
> +       .irq_eoi                = pch_pic_eoi_irq,
> +       .irq_mask               = pch_pic_mask_irq,
> +       .irq_unmask             = pch_pic_unmask_irq,
> +       .irq_enable             = pch_pic_enable_irq,
> +       .irq_disable    = pch_pic_mask_irq,
> +       .irq_set_affinity       = irq_chip_set_affinity_parent,
> +       .irq_set_type           = pch_pic_set_type,
> +};
> +
> +static int pch_pic_alloc(struct irq_domain *domain, unsigned int virq,
> +                             unsigned int nr_irqs, void *arg)
> +{
> +       struct pch_pic *priv = domain->host_data;
> +       struct irq_fwspec fwspec;
> +       unsigned long hwirq;
> +       unsigned int type;
> +       int err;
> +
> +       irq_domain_translate_twocell(domain, arg, &hwirq, &type);
> +
> +       fwspec.fwnode = domain->parent->fwnode;
> +       fwspec.param_count = 1;
> +       fwspec.param[0] = hwirq + priv->ht_vec_base;
> +
> +       err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
> +       if (err)
> +               return err;
> +
> +       irq_domain_set_info(domain, virq, hwirq,
> +                           &pch_pic_irq_chip, priv,
> +                           handle_fasteoi_irq, NULL, NULL);
> +       irq_set_probe(virq);
> +
> +       return 0;
> +}
> +
> +static const struct irq_domain_ops pch_pic_domain_ops = {
> +       .translate = irq_domain_translate_twocell,
> +       .alloc  = pch_pic_alloc,
> +       .free   = irq_domain_free_irqs_parent,
> +};
> +
> +static void pch_pic_reset(struct pch_pic *priv)
> +{
> +       u32 idx;
> +
> +       /* Clear IRQ cause registers, mask all interrupts */
> +       for (idx = 0; idx < PIC_REG_COUNT; idx++) {
> +               writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_MASK + 4 * idx);
> +               writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_CLR + 4 * idx);
> +       }
> +}
> +
> +static int pch_pic_of_init(struct device_node *node,
> +                               struct device_node *parent)
> +{
> +       struct pch_pic *priv;
> +       struct irq_domain *parent_domain;
> +       int err;
> +       u32 ht_vec_base;
> +
> +       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;
> +               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;
> +       }
> +
> +       if (of_property_read_u32(node, "loongson,pic-base-vec",
> +                               &ht_vec_base))
> +               priv->ht_vec_base = 64;
> +       else
> +               priv->ht_vec_base = ht_vec_base;
> +
> +       priv->pic_domain = irq_domain_create_hierarchy(parent_domain, 0,
> +                                                    PIC_COUNT,
> +                                                    of_node_to_fwnode(node),
> +                                                    &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);
> +
> +       return 0;
> +
> +iounmap_base:
> +       iounmap(priv->base);
> +free_priv:
> +       kfree(priv);
> +
> +       return err;
> +}
> +
> +IRQCHIP_DECLARE(pch_pic, "loongson,pch-pic-1.0", pch_pic_of_init);
> --
> 2.26.0.rc2
>

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

* Re: [PATCH 5/6] irqchip: Add Loongson PCH MSI controller
  2020-04-22 14:24 ` [PATCH 5/6] irqchip: Add Loongson PCH MSI controller Jiaxun Yang
@ 2020-04-23  5:57   ` Huacai Chen
  2020-04-23 14:41   ` Marc Zyngier
  1 sibling, 0 replies; 41+ messages in thread
From: Huacai Chen @ 2020-04-23  5:57 UTC (permalink / raw)
  To: Jiaxun Yang
  Cc: open list:MIPS, Marc Zyngier, Thomas Gleixner, Jason Cooper,
	Rob Herring, LKML, devicetree

Hi, Jiaxun

On Wed, Apr 22, 2020 at 10:27 PM Jiaxun Yang <jiaxun.yang@flygoat.com> wrote:
>
> This controller appears on Loongson-7A family of PCH to transform
Please use "Loongson's LS7A family" here.

> interrupts from PCI MSI into HyperTransport vectorized interrrupts
> and send them to procrssor's HT vector controller.
>
> Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
> ---
>  drivers/irqchip/Kconfig                |  10 +
>  drivers/irqchip/Makefile               |   1 +
>  drivers/irqchip/irq-loongson-pch-msi.c | 265 +++++++++++++++++++++++++
>  3 files changed, 276 insertions(+)
>  create mode 100644 drivers/irqchip/irq-loongson-pch-msi.c
>
> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> index 4ab7a9b1a5c2..9f2935418f33 100644
> --- a/drivers/irqchip/Kconfig
> +++ b/drivers/irqchip/Kconfig
> @@ -548,4 +548,14 @@ config LOONGSON_PCH_PIC
>         help
>           Support for the Loongson PCH PIC Controller.
>
> +config LOONGSON_PCH_MSI
> +       bool "Loongson PCH PIC Controller"
> +       depends on MACH_LOONGSON64 || COMPILE_TEST
> +       depends on PCI
> +       default MACH_LOONGSON64
> +       select IRQ_DOMAIN_HIERARCHY
> +       select PCI_MSI
> +       help
> +         Support for the Loongson PCH MSI Controller.
> +
>  endmenu
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index acc72331cec8..3a4ce283189a 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -109,3 +109,4 @@ obj-$(CONFIG_LOONGSON_LIOINTC)              += irq-loongson-liointc.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
> +obj-$(CONFIG_LOONGSON_PCH_MSI)         += irq-loongson-pch-msi.o
> diff --git a/drivers/irqchip/irq-loongson-pch-msi.c b/drivers/irqchip/irq-loongson-pch-msi.c
> new file mode 100644
> index 000000000000..4bb00f2ce86a
> --- /dev/null
> +++ b/drivers/irqchip/irq-loongson-pch-msi.c
> @@ -0,0 +1,265 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + *  Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com>
> + *  Loongson PCH MSI support
> + */
> +
> +#define pr_fmt(fmt) "pch-msi: " fmt
> +
> +#include <linux/irqchip.h>
> +#include <linux/msi.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_pci.h>
> +#include <linux/pci.h>
> +#include <linux/slab.h>
> +
> +struct pch_msi_data {
> +       spinlock_t msi_map_lock;
> +       phys_addr_t addr;
> +       u32 irq_first;          /* The vectoe number that MSIs start */
> +       u32 num_irqs;           /* The number of vector for MSIs */
> +       unsigned long *msi_map;
> +};
> +
> +static void pch_msi_mask_msi_irq(struct irq_data *d)
> +{
> +       pci_msi_mask_irq(d);
> +       irq_chip_mask_parent(d);
> +}
> +
> +static void pch_msi_unmask_msi_irq(struct irq_data *d)
> +{
> +       pci_msi_unmask_irq(d);
> +       irq_chip_unmask_parent(d);
> +}
> +
> +static struct irq_chip pch_msi_irq_chip = {
> +       .name                   = "PCH MSI",
> +       .irq_mask               = pch_msi_mask_msi_irq,
> +       .irq_unmask             = pch_msi_unmask_msi_irq,
> +       .irq_set_affinity       = irq_chip_set_affinity_parent,
> +};
> +
> +static int pch_msi_allocate_hwirq(struct pch_msi_data *priv, int num_req)
> +{
> +       int first;
> +
> +       spin_lock(&priv->msi_map_lock);
> +
> +       first = bitmap_find_next_zero_area(priv->msi_map, priv->num_irqs, 0,
> +                                          num_req, 0);
> +       if (first >= priv->num_irqs) {
> +               spin_unlock(&priv->msi_map_lock);
> +               return -ENOSPC;
> +       }
> +
> +       bitmap_set(priv->msi_map, first, num_req);
> +
> +       spin_unlock(&priv->msi_map_lock);
> +
> +       return priv->irq_first + first;
> +}
> +
> +static void pch_msi_free_hwirq(struct pch_msi_data *priv,
> +                               int hwirq, int num_req)
> +{
> +       int first = hwirq - priv->irq_first;
> +
> +       spin_lock(&priv->msi_map_lock);
> +
> +       bitmap_clear(priv->msi_map, first, num_req);
> +
> +       spin_unlock(&priv->msi_map_lock);
> +}
> +
> +static void pch_msi_compose_msi_msg(struct irq_data *data,
> +                                       struct msi_msg *msg)
> +{
> +       struct pch_msi_data *priv = irq_data_get_irq_chip_data(data);
> +       phys_addr_t msg_addr = priv->addr;
> +
> +       msg->address_hi = upper_32_bits(msg_addr);
> +       msg->address_lo = lower_32_bits(msg_addr);
> +       msg->data = data->hwirq;
> +}
> +
> +static struct msi_domain_info pch_msi_domain_info = {
> +       .flags          = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +                         MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX,
> +       .chip   = &pch_msi_irq_chip,
> +};
> +
> +static struct irq_chip middle_irq_chip = {
> +       .name                   = "PCH MSI Middle",
> +       .irq_mask               = irq_chip_mask_parent,
> +       .irq_unmask             = irq_chip_unmask_parent,
> +       .irq_set_affinity       = irq_chip_set_affinity_parent,
> +       .irq_compose_msi_msg    = pch_msi_compose_msi_msg,
> +};
> +
> +static int pch_msi_parent_domain_alloc(struct irq_domain *domain,
> +                                       unsigned int virq, int hwirq)
> +{
> +       struct irq_fwspec fwspec;
> +       int ret;
> +
> +       if (!is_of_node(domain->parent->fwnode))
> +               return -EINVAL;
> +
> +       fwspec.fwnode = domain->parent->fwnode;
> +       fwspec.param_count = 1;
> +       fwspec.param[0] = hwirq;
> +
> +       ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
> +       if (ret)
> +               return ret;
> +
> +       irq_domain_set_info(domain, virq, hwirq,
> +                           &middle_irq_chip, NULL,
> +                           handle_simple_irq, NULL, NULL);
> +       irq_set_probe(virq);
> +
> +       return 0;
> +}
> +
> +static int pch_msi_middle_domain_alloc(struct irq_domain *domain,
> +                                          unsigned int virq,
> +                                          unsigned int nr_irqs, void *args)
> +{
> +       struct pch_msi_data *priv = domain->host_data;
> +       int hwirq, err, i;
> +
> +       hwirq = pch_msi_allocate_hwirq(priv, nr_irqs);
> +       if (hwirq < 0)
> +               return hwirq;
> +
> +       for (i = 0; i < nr_irqs; i++) {
> +               err = pch_msi_parent_domain_alloc(domain, virq + i, hwirq + i);
> +               if (err)
> +                       goto err_hwirq;
> +
> +               irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
> +                                             &middle_irq_chip, priv);
> +       }
> +
> +       return 0;
> +err_hwirq:
> +       while (--i >= 0)
> +               irq_domain_free_irqs_parent(domain, virq, i);
> +
> +       pch_msi_free_hwirq(priv, hwirq, nr_irqs);
> +       return err;
> +}
> +
> +static void pch_msi_middle_domain_free(struct irq_domain *domain,
> +                                          unsigned int virq,
> +                                          unsigned int nr_irqs)
> +{
> +       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
> +       struct pch_msi_data *priv = irq_data_get_irq_chip_data(d);
> +
> +       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> +       pch_msi_free_hwirq(priv, d->hwirq, nr_irqs);
> +}
> +
> +static const struct irq_domain_ops pch_msi_middle_domain_ops = {
> +       .alloc  = pch_msi_middle_domain_alloc,
> +       .free   = pch_msi_middle_domain_free,
> +};
> +
> +static int pch_msi_init_domains(struct pch_msi_data *priv,
> +                               struct device_node *node,
> +                               struct device_node *parent)
> +{
> +       struct irq_domain *middle_domain, *msi_domain, *parent_domain;
> +
> +       parent_domain = irq_find_host(parent);
> +       if (!parent_domain) {
> +               pr_err("Failed to find the parent domain\n");
> +               return -ENXIO;
> +       }
> +
> +       middle_domain = irq_domain_add_tree(NULL,
> +                                           &pch_msi_middle_domain_ops,
> +                                           priv);
> +       if (!middle_domain) {
> +               pr_err("Failed to create the MSI middle domain\n");
> +               return -ENOMEM;
> +       }
> +
> +       middle_domain->parent = parent_domain;
> +
> +       msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(node),
> +                                              &pch_msi_domain_info,
> +                                              middle_domain);
> +       if (!msi_domain) {
> +               pr_err("Failed to create MSI domain\n");
> +               irq_domain_remove(middle_domain);
> +               return -ENOMEM;
> +       }
> +
> +       return 0;
> +}
> +
> +static int pch_msi_init(struct device_node *node,
> +                           struct device_node *parent)
> +{
> +       struct pch_msi_data *priv;
> +       struct resource res;
> +       int ret;
> +
> +       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> +       if (!priv)
> +               return -ENOMEM;
> +
> +       spin_lock_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->addr = 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->msi_map = kcalloc(BITS_TO_LONGS(priv->num_irqs),
> +                               sizeof(*priv->msi_map),
> +                               GFP_KERNEL);
> +       if (!priv->msi_map) {
> +               ret = -ENOMEM;
> +               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);
> +       if (ret)
> +               goto err_map;
> +
> +       return 0;
> +
> +err_map:
> +       kfree(priv->msi_map);
> +err_priv:
> +       kfree(priv);
> +       return ret;
> +}
> +
> +IRQCHIP_DECLARE(pch_msi, "loongson,pch-msi-1.0", pch_msi_init);
> --
> 2.26.0.rc2
>

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

* Re: [PATCH 6/6] dt-bindings: interrupt-controller: Add Loongson PCH MSI
  2020-04-23  5:55   ` Huacai Chen
@ 2020-04-23 12:43     ` Marc Zyngier
  2020-04-24  1:27       ` Huacai Chen
  0 siblings, 1 reply; 41+ messages in thread
From: Marc Zyngier @ 2020-04-23 12:43 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Jiaxun Yang, open list:MIPS, Thomas Gleixner, Jason Cooper,
	Rob Herring, LKML, devicetree

Huacai,

On 2020-04-23 06:55, Huacai Chen wrote:
> Hi, Jiaxun,
> 
> On Wed, Apr 22, 2020 at 10:28 PM Jiaxun Yang <jiaxun.yang@flygoat.com> 
> wrote:
>> 
>> Add binding for Loongson PCH MSI controller.
>> 
>> Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
>> ---
>>  .../loongson,pch-msi.yaml                     | 56 
>> +++++++++++++++++++
>>  1 file changed, 56 insertions(+)
>>  create mode 100644 
>> Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml
>> 
>> diff --git 
>> a/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml 
>> b/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml
>> new file mode 100644
>> index 000000000000..dfb9cecacba0
>> --- /dev/null
>> +++ 
>> b/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml
>> @@ -0,0 +1,56 @@
>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: 
>> "http://devicetree.org/schemas/interrupt-controller/loongson,pch-msi.yaml#"
>> +$schema: "http://devicetree.org/meta-schemas/core.yaml#"
>> +
>> +title: Loongson PCH MSI Controller
>> +
>> +maintainers:
>> +  - Jiaxun Yang <jiaxun.yang@flygoat.com>
>> +
>> +description: |
>> +  This interrupt controller is found in the Loongson-7A family of PCH 
>> for
> Please use "Loongson's LS7A family" here.

It's the fourth email you send on the same subject. I think the author
has got the message already. Frankly, it is only a name, and if they
want to call it Bob, so be it.

Thanks,

         M.
-- 
Jazz is not dead. It just smells funny...

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

* Re: [PATCH 1/6] irqchip: Add Loongson HyperTransport Vector support
  2020-04-22 14:24 ` [PATCH 1/6] irqchip: Add Loongson HyperTransport Vector support Jiaxun Yang
@ 2020-04-23 13:31   ` Marc Zyngier
  0 siblings, 0 replies; 41+ messages in thread
From: Marc Zyngier @ 2020-04-23 13:31 UTC (permalink / raw)
  To: Jiaxun Yang
  Cc: linux-mips, Thomas Gleixner, Jason Cooper, Rob Herring,
	Huacai Chen, linux-kernel, devicetree

On Wed, 22 Apr 2020 15:24:21 +0100,
Jiaxun Yang <jiaxun.yang@flygoat.com> wrote:
> 
> This controller appears on Loongson-3 chips for receiving interrupt
> vectors from PCH's PIC and PCH's PCIe MSI interrupts. It usually act
> as the top of irq hierarchy.
> 
> Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
> ---
>  drivers/irqchip/Kconfig              |   8 +
>  drivers/irqchip/Makefile             |   1 +
>  drivers/irqchip/irq-loongson-htvec.c | 217 +++++++++++++++++++++++++++
>  3 files changed, 226 insertions(+)
>  create mode 100644 drivers/irqchip/irq-loongson-htvec.c
> 
> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> index a85aada04a64..de4564e2ea88 100644
> --- a/drivers/irqchip/Kconfig
> +++ b/drivers/irqchip/Kconfig
> @@ -532,4 +532,12 @@ config LOONGSON_HTPIC
>  	help
>  	  Support for the Loongson-3 HyperTransport PIC Controller.
>  
> +config LOONGSON_HTVEC
> +	bool "Loongson3 HyperTransport Interrupt Vector Controller"
> +	depends on MACH_LOONGSON64 || COMPILE_TEST
> +	default MACH_LOONGSON64
> +	select IRQ_DOMAIN_HIERARCHY
> +	help
> +	  Support for the Loongson3 HyperTransport Interrupt Vector Controller.
> +
>  endmenu
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index 37bbe39bf909..74561879f5a7 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -107,3 +107,4 @@ obj-$(CONFIG_TI_SCI_INTR_IRQCHIP)	+= irq-ti-sci-intr.o
>  obj-$(CONFIG_TI_SCI_INTA_IRQCHIP)	+= irq-ti-sci-inta.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-loongson-htvec.c b/drivers/irqchip/irq-loongson-htvec.c
> new file mode 100644
> index 000000000000..e155ebb99efb
> --- /dev/null
> +++ b/drivers/irqchip/irq-loongson-htvec.c
> @@ -0,0 +1,217 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + *  Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com>
> + *  Loongson HyperTransport Interrupt Vector support
> + */
> +
> +#define pr_fmt(fmt) "htvec: " 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>
> +
> +/* Registers */
> +#define HTVEC_EN_OFF		0x20
> +#define HTVEC_MAX_PARENT_IRQ 4
> +
> +#define VEC_COUNT_PER_REG	32
> +#define VEC_REG_COUNT		4
> +#define VEC_COUNT		(VEC_COUNT_PER_REG * VEC_REG_COUNT)
> +#define VEC_REG_IDX(irq_id)	((irq_id) / VEC_COUNT_PER_REG)
> +#define VEC_REG_BIT(irq_id)	((irq_id) % VEC_COUNT_PER_REG)
> +
> +struct htvec {
> +	void __iomem *base;
> +	struct irq_domain *htvec_domain;
> +	raw_spinlock_t htvec_lock;

nit: please align member of the structure vertically:

struct htvec {		  
	void __iomem		*base;
	struct irq_domain	*htvec_d
	raw_spinlock_t		htvec_lock;
};

> +};
> +
> +static void htvec_irq_dispatch(struct irq_desc *desc)
> +{
> +	struct htvec *priv = irq_desc_get_handler_data(desc);
> +	struct irq_chip *chip = irq_desc_get_chip(desc);
> +	uint32_t pending;

In the kernel, please use u32. uint32_t is reserved for uapi code.

> +	bool handled = false;
> +	int i;
> +
> +	chained_irq_enter(chip, desc);

If this is a chained interrupt controller, it isn't the top-level
interrupt controller, as it obviously feed into another one.

> +
> +	for (i = 0; i < VEC_REG_COUNT; i++) {
> +		pending = readl(priv->base + 4 * i);
> +		/* Ack all IRQs at once, otherwise IRQ flood might happen */
> +		writel(pending, priv->base + 4 * i);
> +		while (pending) {
> +			int bit = __ffs(pending);
> +
> +			generic_handle_irq(irq_linear_revmap(priv->htvec_domain,
> +						bit + 32 * i));

Isn't this 32 actually VEC_COUNT_PER_REG?

> +			pending &= ~BIT(bit);
> +			handled = true;
> +		}
> +	}
> +
> +	if (!handled)
> +		spurious_interrupt();
> +
> +	chained_irq_exit(chip, desc);
> +}
> +
> +static void htvec_bitset(void __iomem *addr, int bit)
> +{
> +	u32 reg;
> +
> +	addr += VEC_REG_IDX(bit) * 4;
> +	reg = readl(addr);
> +	reg |= BIT(VEC_REG_BIT(bit));
> +	writel(reg, addr);
> +}
> +
> +static void htvec_bitclr(void __iomem *addr, int bit)
> +{
> +	u32 reg;
> +
> +	addr += VEC_REG_IDX(bit) * 4;
> +	reg = readl(addr);
> +	reg &= ~BIT(VEC_REG_BIT(bit));
> +	writel(reg, addr);
> +}

Given that these two functions have only a single call site, please
move them into their respective caller. At least we won't have to
worry about the locking.

> +
> +static void htvec_mask_irq(struct irq_data *d)
> +{
> +	struct htvec *priv = irq_data_get_irq_chip_data(d);
> +	unsigned long flags;
> +
> +	raw_spin_lock_irqsave(&priv->htvec_lock, flags);
> +	htvec_bitclr(priv->base + HTVEC_EN_OFF, d->hwirq);
> +	raw_spin_unlock_irqrestore(&priv->htvec_lock, flags);
> +}
> +
> +static void htvec_unmask_irq(struct irq_data *d)
> +{
> +	struct htvec *priv = irq_data_get_irq_chip_data(d);
> +	unsigned long flags;
> +
> +	raw_spin_lock_irqsave(&priv->htvec_lock, flags);
> +	htvec_bitset(priv->base + HTVEC_EN_OFF, d->hwirq);
> +	raw_spin_unlock_irqrestore(&priv->htvec_lock, flags);
> +}
> +
> +static struct irq_chip htvec_irq_chip = {
> +	.name			= "LOONGSON_HTVEC",
> +	.irq_mask		= htvec_mask_irq,
> +	.irq_unmask		= htvec_unmask_irq,
> +};
> +
> +static int htvec_domain_alloc(struct irq_domain *domain, unsigned int virq,
> +				  unsigned int nr_irqs, void *arg)
> +{
> +	struct htvec *priv = domain->host_data;
> +	unsigned long hwirq;
> +	unsigned int type;
> +
> +	irq_domain_translate_onecell(domain, arg, &hwirq, &type);
> +
> +	/* Not much to do, just setup the irqdata */
> +	irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
> +					&htvec_irq_chip, priv);

What sets the flow handler?

Another thing is that you ignore the "nr_irqs" parameter, while you
are handling it in the free callback. You have to be consistent.

> +
> +	return 0;
> +}
> +
> +static void htvec_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 htvec_domain_ops = {
> +	.translate = irq_domain_translate_onecell,
> +	.alloc	= htvec_domain_alloc,
> +	.free	= htvec_domain_free,
> +};
> +
> +static void htvec_reset(struct htvec *priv)
> +{
> +	u32 idx;
> +
> +	/* Clear IRQ cause registers, mask all interrupts */
> +	for (idx = 0; idx < VEC_REG_COUNT; idx++) {
> +		writel_relaxed(0x0, priv->base + HTVEC_EN_OFF + 4 * idx);
> +		writel_relaxed(0xFFFFFFFF, priv->base);
> +	}
> +}
> +
> +static int htvec_of_init(struct device_node *node,
> +				struct device_node *parent)
> +{
> +	struct htvec *priv;
> +	int err, parent_irq[4], num_parents = 0, i;
> +
> +	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	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 4 interrupt line */
> +	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++;
> +	}
> +
> +	if (!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),
> +						   VEC_COUNT,
> +						   &htvec_domain_ops,
> +						   priv);
> +	if (!priv->htvec_domain) {
> +		pr_err("Failed to create IRQ domain\n");
> +		err = -ENOMEM;
> +		goto iounmap_base;
> +	}
> +
> +	htvec_reset(priv);
> +
> +	for (i = 0; i < num_parents; i++) {
> +		irq_set_chained_handler_and_data(parent_irq[i],
> +						htvec_irq_dispatch, priv);
> +	}

Useless braces.

> +
> +	return 0;
> +
> +iounmap_base:
> +	iounmap(priv->base);
> +free_priv:
> +	kfree(priv);
> +
> +	return err;
> +}
> +
> +IRQCHIP_DECLARE(htvec, "loongson,htvec-1.0", htvec_of_init);

Thanks,

	M.

-- 
Jazz is not dead, it just smells funny.

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

* Re: [PATCH 3/6] irqchip: Add Loongson PCH PIC controller
  2020-04-22 14:24 ` [PATCH 3/6] irqchip: Add Loongson PCH PIC controller Jiaxun Yang
  2020-04-23  5:56   ` Huacai Chen
@ 2020-04-23 14:23   ` Marc Zyngier
  1 sibling, 0 replies; 41+ messages in thread
From: Marc Zyngier @ 2020-04-23 14:23 UTC (permalink / raw)
  To: Jiaxun Yang
  Cc: linux-mips, Thomas Gleixner, Jason Cooper, Rob Herring,
	Huacai Chen, linux-kernel, devicetree

On Wed, 22 Apr 2020 22:24:23 +0800
Jiaxun Yang <jiaxun.yang@flygoat.com> wrote:

> This controller appears on Loongson-7A family of PCH to transform
> interrupts from devices into HyperTransport vectorized interrrupts
> and send them to procrssor's HT vector controller.
> 
> Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
> ---
>  drivers/irqchip/Kconfig                |   8 +
>  drivers/irqchip/Makefile               |   1 +
>  drivers/irqchip/irq-loongson-pch-pic.c | 256 +++++++++++++++++++++++++
>  3 files changed, 265 insertions(+)
>  create mode 100644 drivers/irqchip/irq-loongson-pch-pic.c
> 
> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> index de4564e2ea88..4ab7a9b1a5c2 100644
> --- a/drivers/irqchip/Kconfig
> +++ b/drivers/irqchip/Kconfig
> @@ -540,4 +540,12 @@ config LOONGSON_HTVEC
>  	help
>  	  Support for the Loongson3 HyperTransport Interrupt Vector Controller.
>  
> +config LOONGSON_PCH_PIC
> +	bool "Loongson PCH PIC Controller"
> +	depends on MACH_LOONGSON64 || COMPILE_TEST
> +	default MACH_LOONGSON64
> +	select IRQ_DOMAIN_HIERARCHY
> +	help
> +	  Support for the Loongson PCH PIC Controller.
> +
>  endmenu
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index 74561879f5a7..acc72331cec8 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -108,3 +108,4 @@ obj-$(CONFIG_TI_SCI_INTA_IRQCHIP)	+= irq-ti-sci-inta.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
> +obj-$(CONFIG_LOONGSON_PCH_PIC)		+= irq-loongson-pch-pic.o
> diff --git a/drivers/irqchip/irq-loongson-pch-pic.c b/drivers/irqchip/irq-loongson-pch-pic.c
> new file mode 100644
> index 000000000000..717ab8335074
> --- /dev/null
> +++ b/drivers/irqchip/irq-loongson-pch-pic.c
> @@ -0,0 +1,256 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + *  Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com>
> + *  Loongson PCH PIC support
> + */
> +
> +#define pr_fmt(fmt) "pch-pic: " fmt
> +
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/irqchip.h>
> +#include <linux/irqdomain.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>
> +
> +/* Registers */
> +#define PCH_PIC_MASK		0x20
> +#define PCH_PIC_HTMSI_EN	0x40
> +#define PCH_PIC_EDGE		0x60
> +#define PCH_PIC_CLR			0x80
> +#define PCH_PIC_AUTO0		0xc0
> +#define PCH_PIC_AUTO1		0xe0
> +#define PCH_INT_ROUTE(irq)	(0x100 + irq)
> +#define PCH_INT_HTVEC(irq)	(0x200 + irq)
> +#define PCH_PIC_POL			0x3e0
> +
> +#define PIC_COUNT_PER_REG	32
> +#define PIC_REG_COUNT		2
> +#define PIC_COUNT			(PIC_COUNT_PER_REG * PIC_REG_COUNT)
> +#define PIC_REG_IDX(irq_id)	((irq_id) / PIC_COUNT_PER_REG)
> +#define PIC_REG_BIT(irq_id)	((irq_id) % PIC_COUNT_PER_REG)
> +
> +struct pch_pic {
> +	void __iomem *base;
> +	struct irq_domain *pic_domain;
> +	int	ht_vec_base;
> +	raw_spinlock_t pic_lock;
> +};

Same comment about the layout of the data structure.

> +
> +static void pch_pic_bitset(void __iomem *addr, int bit)
> +{
> +	u32 reg;
> +
> +	addr += PIC_REG_IDX(bit) * 4;
> +	reg = readl(addr);
> +	reg |= BIT(PIC_REG_BIT(bit));
> +	writel(reg, addr);
> +}
> +
> +static void pch_pic_bitclr(void __iomem *addr, int bit)
> +{
> +	u32 reg;
> +
> +	addr += PIC_REG_IDX(bit) * 4;
> +	reg = readl(addr);
> +	reg &= ~BIT(PIC_REG_BIT(bit));
> +	writel(reg, addr);
> +}
> +
> +static void pch_pic_eoi_irq(struct irq_data *d)
> +{
> +	struct pch_pic *priv = irq_data_get_irq_chip_data(d);
> +	u32 idx = PIC_REG_IDX(d->hwirq);
> +
> +	writel(BIT(PIC_REG_BIT(d->hwirq)),
> +			priv->base + PCH_PIC_CLR + idx * 4);
> +}
> +
> +static void pch_pic_mask_irq(struct irq_data *d)
> +{
> +	struct pch_pic *priv = irq_data_get_irq_chip_data(d);
> +	unsigned long flags;
> +
> +	raw_spin_lock_irqsave(&priv->pic_lock, flags);
> +	pch_pic_bitset(priv->base + PCH_PIC_MASK, d->hwirq);
> +	raw_spin_unlock_irqrestore(&priv->pic_lock, flags);
> +	irq_chip_mask_parent(d);
> +}
> +
> +static void pch_pic_unmask_irq(struct irq_data *d)
> +{
> +	struct pch_pic *priv = irq_data_get_irq_chip_data(d);
> +	unsigned long flags;
> +
> +	raw_spin_lock_irqsave(&priv->pic_lock, flags);
> +	pch_pic_bitclr(priv->base + PCH_PIC_MASK, d->hwirq);
> +	raw_spin_unlock_irqrestore(&priv->pic_lock, flags);
> +	irq_chip_unmask_parent(d);
> +}
> +
> +static int pch_pic_set_type(struct irq_data *d, unsigned int type)
> +{
> +	struct pch_pic *priv = irq_data_get_irq_chip_data(d);
> +	int ret = 0;
> +	unsigned long flags;
> +
> +	raw_spin_lock_irqsave(&priv->pic_lock, flags);
> +	switch (type) {
> +	case IRQ_TYPE_EDGE_RISING:
> +		pch_pic_bitset(priv->base + PCH_PIC_EDGE, d->hwirq);
> +		pch_pic_bitclr(priv->base + PCH_PIC_POL, d->hwirq);
> +		break;
> +	case IRQ_TYPE_EDGE_FALLING:
> +		pch_pic_bitset(priv->base + PCH_PIC_EDGE, d->hwirq);
> +		pch_pic_bitset(priv->base + PCH_PIC_POL, d->hwirq);
> +		break;
> +	case IRQ_TYPE_LEVEL_HIGH:
> +		pch_pic_bitclr(priv->base + PCH_PIC_EDGE, d->hwirq);
> +		pch_pic_bitclr(priv->base + PCH_PIC_POL, d->hwirq);
> +		break;
> +	case IRQ_TYPE_LEVEL_LOW:
> +		pch_pic_bitclr(priv->base + PCH_PIC_EDGE, d->hwirq);
> +		pch_pic_bitset(priv->base + PCH_PIC_POL, d->hwirq);
> +		break;
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +	raw_spin_unlock_irqrestore(&priv->pic_lock, flags);

It would make a lot more sense if the locking was done in the
bitclr/bitset functions, unless there is a reason for wanting larger
critical sections (but I can't see it at the moment).

> +
> +	return ret;
> +}
> +
> +static void pch_pic_enable_irq(struct irq_data *d)
> +{
> +	struct pch_pic *priv = irq_data_get_irq_chip_data(d);
> +	u8 htvec = d->hwirq + priv->ht_vec_base;
> +	unsigned long flags;
> +
> +	raw_spin_lock_irqsave(&priv->pic_lock, flags);
> +	writeb(htvec, priv->base + PCH_INT_HTVEC(d->hwirq));
> +	/* Hardcode to HT0 Lo */
> +	writeb(1, priv->base + PCH_INT_ROUTE(d->hwirq));
> +	/* Clear auto bounce, we don't need that */
> +	pch_pic_bitclr(priv->base + PCH_PIC_AUTO0, d->hwirq);
> +	pch_pic_bitclr(priv->base + PCH_PIC_AUTO1, d->hwirq);
> +	/* Enable HTMSI transformer */
> +	pch_pic_bitset(priv->base + PCH_PIC_HTMSI_EN, d->hwirq);
> +	raw_spin_unlock_irqrestore(&priv->pic_lock, flags);
> +	pch_pic_unmask_irq(d);

This looks wrong. enable/disable should not call unmask/mask. It is
also odd that you don't have a proper disable callback that does the
opposite of enable (you call the mask callback, which is also wrong).

I suggest you kill enable/disable altogether, and move this code to the
the probe (or reset) function.

> +}
> +
> +static struct irq_chip pch_pic_irq_chip = {
> +	.name			= "PCH PIC",
> +	.irq_eoi		= pch_pic_eoi_irq,
> +	.irq_mask		= pch_pic_mask_irq,
> +	.irq_unmask		= pch_pic_unmask_irq,
> +	.irq_enable		= pch_pic_enable_irq,
> +	.irq_disable	= pch_pic_mask_irq,
> +	.irq_set_affinity	= irq_chip_set_affinity_parent,

Given that this plugs into the HTVEC irqchip, which doesn't implement
an affinity method (nor that it could, given that it is itself a
chained irqchip), I don't see the point...

> +	.irq_set_type		= pch_pic_set_type,
> +};
> +
> +static int pch_pic_alloc(struct irq_domain *domain, unsigned int virq,
> +			      unsigned int nr_irqs, void *arg)
> +{
> +	struct pch_pic *priv = domain->host_data;
> +	struct irq_fwspec fwspec;
> +	unsigned long hwirq;
> +	unsigned int type;
> +	int err;
> +
> +	irq_domain_translate_twocell(domain, arg, &hwirq, &type);
> +
> +	fwspec.fwnode = domain->parent->fwnode;
> +	fwspec.param_count = 1;
> +	fwspec.param[0] = hwirq + priv->ht_vec_base;
> +
> +	err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
> +	if (err)
> +		return err;
> +
> +	irq_domain_set_info(domain, virq, hwirq,
> +			    &pch_pic_irq_chip, priv,
> +			    handle_fasteoi_irq, NULL, NULL);
> +	irq_set_probe(virq);
> +
> +	return 0;
> +}
> +
> +static const struct irq_domain_ops pch_pic_domain_ops = {
> +	.translate = irq_domain_translate_twocell,
> +	.alloc	= pch_pic_alloc,
> +	.free	= irq_domain_free_irqs_parent,
> +};
> +
> +static void pch_pic_reset(struct pch_pic *priv)
> +{
> +	u32 idx;
> +
> +	/* Clear IRQ cause registers, mask all interrupts */
> +	for (idx = 0; idx < PIC_REG_COUNT; idx++) {
> +		writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_MASK + 4 * idx);
> +		writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_CLR + 4 * idx);
> +	}
> +}
> +
> +static int pch_pic_of_init(struct device_node *node,
> +				struct device_node *parent)
> +{
> +	struct pch_pic *priv;
> +	struct irq_domain *parent_domain;
> +	int err;
> +	u32 ht_vec_base;
> +
> +	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;
> +		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;
> +	}
> +
> +	if (of_property_read_u32(node, "loongson,pic-base-vec",
> +				&ht_vec_base))
> +		priv->ht_vec_base = 64;

The binding doesn't describe the property as being optional.

> +	else
> +		priv->ht_vec_base = ht_vec_base;
> +
> +	priv->pic_domain = irq_domain_create_hierarchy(parent_domain, 0,
> +						     PIC_COUNT,
> +						     of_node_to_fwnode(node),
> +						     &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);
> +
> +	return 0;
> +
> +iounmap_base:
> +	iounmap(priv->base);
> +free_priv:
> +	kfree(priv);
> +
> +	return err;
> +}
> +
> +IRQCHIP_DECLARE(pch_pic, "loongson,pch-pic-1.0", pch_pic_of_init);

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

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

* Re: [PATCH 5/6] irqchip: Add Loongson PCH MSI controller
  2020-04-22 14:24 ` [PATCH 5/6] irqchip: Add Loongson PCH MSI controller Jiaxun Yang
  2020-04-23  5:57   ` Huacai Chen
@ 2020-04-23 14:41   ` Marc Zyngier
  2020-04-24  1:33     ` Jiaxun Yang
  1 sibling, 1 reply; 41+ messages in thread
From: Marc Zyngier @ 2020-04-23 14:41 UTC (permalink / raw)
  To: Jiaxun Yang
  Cc: linux-mips, Thomas Gleixner, Jason Cooper, Rob Herring,
	Huacai Chen, linux-kernel, devicetree

On Wed, 22 Apr 2020 22:24:25 +0800
Jiaxun Yang <jiaxun.yang@flygoat.com> wrote:

> This controller appears on Loongson-7A family of PCH to transform
> interrupts from PCI MSI into HyperTransport vectorized interrrupts
> and send them to procrssor's HT vector controller.
> 
> Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
> ---
>  drivers/irqchip/Kconfig                |  10 +
>  drivers/irqchip/Makefile               |   1 +
>  drivers/irqchip/irq-loongson-pch-msi.c | 265 +++++++++++++++++++++++++
>  3 files changed, 276 insertions(+)
>  create mode 100644 drivers/irqchip/irq-loongson-pch-msi.c
> 
> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> index 4ab7a9b1a5c2..9f2935418f33 100644
> --- a/drivers/irqchip/Kconfig
> +++ b/drivers/irqchip/Kconfig
> @@ -548,4 +548,14 @@ config LOONGSON_PCH_PIC
>  	help
>  	  Support for the Loongson PCH PIC Controller.
>  
> +config LOONGSON_PCH_MSI
> +	bool "Loongson PCH PIC Controller"
> +	depends on MACH_LOONGSON64 || COMPILE_TEST
> +	depends on PCI
> +	default MACH_LOONGSON64
> +	select IRQ_DOMAIN_HIERARCHY
> +	select PCI_MSI
> +	help
> +	  Support for the Loongson PCH MSI Controller.
> +
>  endmenu
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index acc72331cec8..3a4ce283189a 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -109,3 +109,4 @@ obj-$(CONFIG_LOONGSON_LIOINTC)		+= irq-loongson-liointc.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
> +obj-$(CONFIG_LOONGSON_PCH_MSI)		+= irq-loongson-pch-msi.o
> diff --git a/drivers/irqchip/irq-loongson-pch-msi.c b/drivers/irqchip/irq-loongson-pch-msi.c
> new file mode 100644
> index 000000000000..4bb00f2ce86a
> --- /dev/null
> +++ b/drivers/irqchip/irq-loongson-pch-msi.c
> @@ -0,0 +1,265 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + *  Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com>
> + *  Loongson PCH MSI support
> + */
> +
> +#define pr_fmt(fmt) "pch-msi: " fmt
> +
> +#include <linux/irqchip.h>
> +#include <linux/msi.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_pci.h>
> +#include <linux/pci.h>
> +#include <linux/slab.h>
> +
> +struct pch_msi_data {
> +	spinlock_t msi_map_lock;
> +	phys_addr_t addr;

addr isn't very descriptive. use something like "doorbell", or anything
that shows that this is where the endpoint write to to generate a MSI.

> +	u32 irq_first;		/* The vectoe number that MSIs start */
> +	u32 num_irqs;		/* The number of vector for MSIs */
> +	unsigned long *msi_map;
> +};
> +
> +static void pch_msi_mask_msi_irq(struct irq_data *d)
> +{
> +	pci_msi_mask_irq(d);
> +	irq_chip_mask_parent(d);
> +}
> +
> +static void pch_msi_unmask_msi_irq(struct irq_data *d)
> +{
> +	pci_msi_unmask_irq(d);
> +	irq_chip_unmask_parent(d);
> +}
> +
> +static struct irq_chip pch_msi_irq_chip = {
> +	.name			= "PCH MSI",
> +	.irq_mask		= pch_msi_mask_msi_irq,
> +	.irq_unmask		= pch_msi_unmask_msi_irq,
> +	.irq_set_affinity	= irq_chip_set_affinity_parent,

Same remark as the previous driver.

> +};
> +
> +static int pch_msi_allocate_hwirq(struct pch_msi_data *priv, int num_req)
> +{
> +	int first;
> +
> +	spin_lock(&priv->msi_map_lock);
> +
> +	first = bitmap_find_next_zero_area(priv->msi_map, priv->num_irqs, 0,
> +					   num_req, 0);

This doesn't work for Multi-MSI, where the base of the MSI vectors must
be naturally aligned to the number of vectors. See
bitmap_find_free_region() and co.

> +	if (first >= priv->num_irqs) {
> +		spin_unlock(&priv->msi_map_lock);
> +		return -ENOSPC;
> +	}
> +
> +	bitmap_set(priv->msi_map, first, num_req);
> +
> +	spin_unlock(&priv->msi_map_lock);
> +
> +	return priv->irq_first + first;
> +}
> +
> +static void pch_msi_free_hwirq(struct pch_msi_data *priv,
> +				int hwirq, int num_req)
> +{
> +	int first = hwirq - priv->irq_first;
> +
> +	spin_lock(&priv->msi_map_lock);
> +
> +	bitmap_clear(priv->msi_map, first, num_req);

See bitmap_release_region() in relation to the above.

> +
> +	spin_unlock(&priv->msi_map_lock);
> +}
> +
> +static void pch_msi_compose_msi_msg(struct irq_data *data,
> +					struct msi_msg *msg)
> +{
> +	struct pch_msi_data *priv = irq_data_get_irq_chip_data(data);
> +	phys_addr_t msg_addr = priv->addr;
> +
> +	msg->address_hi = upper_32_bits(msg_addr);
> +	msg->address_lo = lower_32_bits(msg_addr);
> +	msg->data = data->hwirq;
> +}
> +
> +static struct msi_domain_info pch_msi_domain_info = {
> +	.flags		= MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +			  MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX,
> +	.chip	= &pch_msi_irq_chip,
> +};
> +
> +static struct irq_chip middle_irq_chip = {
> +	.name			= "PCH MSI Middle",
> +	.irq_mask		= irq_chip_mask_parent,
> +	.irq_unmask		= irq_chip_unmask_parent,
> +	.irq_set_affinity	= irq_chip_set_affinity_parent,

again.

> +	.irq_compose_msi_msg	= pch_msi_compose_msi_msg,
> +};
> +
> +static int pch_msi_parent_domain_alloc(struct irq_domain *domain,
> +					unsigned int virq, int hwirq)
> +{
> +	struct irq_fwspec fwspec;
> +	int ret;
> +
> +	if (!is_of_node(domain->parent->fwnode))
> +		return -EINVAL;

How can that happen?

> +
> +	fwspec.fwnode = domain->parent->fwnode;
> +	fwspec.param_count = 1;
> +	fwspec.param[0] = hwirq;
> +
> +	ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
> +	if (ret)
> +		return ret;
> +
> +	irq_domain_set_info(domain, virq, hwirq,
> +			    &middle_irq_chip, NULL,
> +			    handle_simple_irq, NULL, NULL);

No, this should at least be handle_edge_irq. More importantly, you
should use the flow set by the underlying irqchip, and not provide your
own.

> +	irq_set_probe(virq);

Probe? what does it mean for MSIs? I also don't see how you tell the
underlying irqchip that the MSI is edge triggered.

> +
> +	return 0;
> +}
> +
> +static int pch_msi_middle_domain_alloc(struct irq_domain *domain,
> +					   unsigned int virq,
> +					   unsigned int nr_irqs, void *args)
> +{
> +	struct pch_msi_data *priv = domain->host_data;
> +	int hwirq, err, i;
> +
> +	hwirq = pch_msi_allocate_hwirq(priv, nr_irqs);
> +	if (hwirq < 0)
> +		return hwirq;
> +
> +	for (i = 0; i < nr_irqs; i++) {
> +		err = pch_msi_parent_domain_alloc(domain, virq + i, hwirq + i);
> +		if (err)
> +			goto err_hwirq;
> +
> +		irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
> +					      &middle_irq_chip, priv);
> +	}
> +
> +	return 0;
> +err_hwirq:
> +	while (--i >= 0)
> +		irq_domain_free_irqs_parent(domain, virq, i);
> +
> +	pch_msi_free_hwirq(priv, hwirq, nr_irqs);
> +	return err;
> +}
> +
> +static void pch_msi_middle_domain_free(struct irq_domain *domain,
> +					   unsigned int virq,
> +					   unsigned int nr_irqs)
> +{
> +	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
> +	struct pch_msi_data *priv = irq_data_get_irq_chip_data(d);
> +
> +	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> +	pch_msi_free_hwirq(priv, d->hwirq, nr_irqs);
> +}
> +
> +static const struct irq_domain_ops pch_msi_middle_domain_ops = {
> +	.alloc	= pch_msi_middle_domain_alloc,
> +	.free	= pch_msi_middle_domain_free,
> +};
> +
> +static int pch_msi_init_domains(struct pch_msi_data *priv,
> +				struct device_node *node,
> +				struct device_node *parent)
> +{
> +	struct irq_domain *middle_domain, *msi_domain, *parent_domain;
> +
> +	parent_domain = irq_find_host(parent);
> +	if (!parent_domain) {
> +		pr_err("Failed to find the parent domain\n");
> +		return -ENXIO;
> +	}
> +
> +	middle_domain = irq_domain_add_tree(NULL,
> +					    &pch_msi_middle_domain_ops,
> +					    priv);

You don't really need a tree, unless your interrupt space is huge and
very sparse. Given that the DT example says 64, I would go with a
linear domain if that number is realistic.

> +	if (!middle_domain) {
> +		pr_err("Failed to create the MSI middle domain\n");
> +		return -ENOMEM;
> +	}
> +
> +	middle_domain->parent = parent_domain;
> +
> +	msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(node),
> +					       &pch_msi_domain_info,
> +					       middle_domain);
> +	if (!msi_domain) {
> +		pr_err("Failed to create MSI domain\n");
> +		irq_domain_remove(middle_domain);
> +		return -ENOMEM;
> +	}
> +
> +	return 0;
> +}
> +
> +static int pch_msi_init(struct device_node *node,
> +			    struct device_node *parent)
> +{
> +	struct pch_msi_data *priv;
> +	struct resource res;
> +	int ret;
> +
> +	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	spin_lock_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->addr = 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->msi_map = kcalloc(BITS_TO_LONGS(priv->num_irqs),
> +				sizeof(*priv->msi_map),
> +				GFP_KERNEL);
> +	if (!priv->msi_map) {
> +		ret = -ENOMEM;
> +		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);
> +	if (ret)
> +		goto err_map;
> +
> +	return 0;
> +
> +err_map:
> +	kfree(priv->msi_map);
> +err_priv:
> +	kfree(priv);
> +	return ret;
> +}
> +
> +IRQCHIP_DECLARE(pch_msi, "loongson,pch-msi-1.0", pch_msi_init);

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

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

* Re: [PATCH 6/6] dt-bindings: interrupt-controller: Add Loongson PCH MSI
  2020-04-23 12:43     ` Marc Zyngier
@ 2020-04-24  1:27       ` Huacai Chen
  0 siblings, 0 replies; 41+ messages in thread
From: Huacai Chen @ 2020-04-24  1:27 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Jiaxun Yang, open list:MIPS, Thomas Gleixner, Jason Cooper,
	Rob Herring, LKML, devicetree

Hi, Marc,

On Thu, Apr 23, 2020 at 8:46 PM Marc Zyngier <maz@kernel.org> wrote:
>
> Huacai,
>
> On 2020-04-23 06:55, Huacai Chen wrote:
> > Hi, Jiaxun,
> >
> > On Wed, Apr 22, 2020 at 10:28 PM Jiaxun Yang <jiaxun.yang@flygoat.com>
> > wrote:
> >>
> >> Add binding for Loongson PCH MSI controller.
> >>
> >> Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
> >> ---
> >>  .../loongson,pch-msi.yaml                     | 56
> >> +++++++++++++++++++
> >>  1 file changed, 56 insertions(+)
> >>  create mode 100644
> >> Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml
> >>
> >> diff --git
> >> a/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml
> >> b/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml
> >> new file mode 100644
> >> index 000000000000..dfb9cecacba0
> >> --- /dev/null
> >> +++
> >> b/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml
> >> @@ -0,0 +1,56 @@
> >> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> >> +%YAML 1.2
> >> +---
> >> +$id:
> >> "http://devicetree.org/schemas/interrupt-controller/loongson,pch-msi.yaml#"
> >> +$schema: "http://devicetree.org/meta-schemas/core.yaml#"
> >> +
> >> +title: Loongson PCH MSI Controller
> >> +
> >> +maintainers:
> >> +  - Jiaxun Yang <jiaxun.yang@flygoat.com>
> >> +
> >> +description: |
> >> +  This interrupt controller is found in the Loongson-7A family of PCH
> >> for
> > Please use "Loongson's LS7A family" here.
>
> It's the fourth email you send on the same subject. I think the author
> has got the message already. Frankly, it is only a name, and if they
> want to call it Bob, so be it.
I'm sorry that make so much noise, and I will not do this again. Yes,
a name is just a name, but we can do something to avoid confusing as
much as possible.

>
> Thanks,
>
>          M.
> --
> Jazz is not dead. It just smells funny...

Thanks,
Huacai

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

* Re: [PATCH 5/6] irqchip: Add Loongson PCH MSI controller
  2020-04-23 14:41   ` Marc Zyngier
@ 2020-04-24  1:33     ` Jiaxun Yang
  2020-04-24  8:28       ` Marc Zyngier
  0 siblings, 1 reply; 41+ messages in thread
From: Jiaxun Yang @ 2020-04-24  1:33 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: linux-mips, Thomas Gleixner, Jason Cooper, Rob Herring,
	Huacai Chen, linux-kernel, devicetree

On Thu, 23 Apr 2020 15:41:35 +0100
Marc Zyngier <maz@kernel.org> wrote:

> On Wed, 22 Apr 2020 22:24:25 +0800
> Jiaxun Yang <jiaxun.yang@flygoat.com> wrote:
> 
> > This controller appears on Loongson-7A family of PCH to transform
> > interrupts from PCI MSI into HyperTransport vectorized interrrupts
> > and send them to procrssor's HT vector controller.
> > 
> > Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
> > ---
[...]
> > +	ret = irq_domain_alloc_irqs_parent(domain, virq, 1,
> > &fwspec);
> > +	if (ret)
> > +		return ret;
> > +
> > +	irq_domain_set_info(domain, virq, hwirq,
> > +			    &middle_irq_chip, NULL,
> > +			    handle_simple_irq, NULL, NULL);  
> 
> No, this should at least be handle_edge_irq. More importantly, you
> should use the flow set by the underlying irqchip, and not provide
> your own.

Hi Marc,
Thanks for your review.

The underlying irqchip (HTVEC) follows a simple_irq flow as it only
has mask/unmask callback, and it doesn't have tyoe configuration. so I
followed simple_irq flow.

How can I use the flow set by the underlying irqchip? Just use
irq_domain_set_hwirq_and_chip here and set_handler in HTVEC driver?


> 
> > +	irq_set_probe(virq);  
> 
> Probe? what does it mean for MSIs? I also don't see how you tell the
> underlying irqchip that the MSI is edge triggered.
> 
> > +
> > +	return 0;
> > +}
> > +
> > +static int pch_msi_middle_domain_alloc(struct irq_domain *domain,
> > +					   unsigned int virq,
> > +					   unsigned int nr_irqs,
> > void *args) +{
> > +	struct pch_msi_data *priv = domain->host_data;
> > +	int hwirq, err, i;
> > +
> > +	hwirq = pch_msi_allocate_hwirq(priv, nr_irqs);
> > +	if (hwirq < 0)
> > +		return hwirq;
> > +
> > +	for (i = 0; i < nr_irqs; i++) {
> > +		err = pch_msi_parent_domain_alloc(domain, virq +
> > i, hwirq + i);
> > +		if (err)
> > +			goto err_hwirq;
> > +
> > +		irq_domain_set_hwirq_and_chip(domain, virq + i,
> > hwirq + i,
> > +					      &middle_irq_chip,
> > priv);
> > +	}
> > +
> > +	return 0;
> > +err_hwirq:
> > +	while (--i >= 0)
> > +		irq_domain_free_irqs_parent(domain, virq, i);
> > +
> > +	pch_msi_free_hwirq(priv, hwirq, nr_irqs);
> > +	return err;
> > +}
> > +
> > +static void pch_msi_middle_domain_free(struct irq_domain *domain,
> > +					   unsigned int virq,
> > +					   unsigned int nr_irqs)
> > +{
> > +	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
> > +	struct pch_msi_data *priv = irq_data_get_irq_chip_data(d);
> > +
> > +	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> > +	pch_msi_free_hwirq(priv, d->hwirq, nr_irqs);
> > +}
> > +
> > +static const struct irq_domain_ops pch_msi_middle_domain_ops = {
> > +	.alloc	= pch_msi_middle_domain_alloc,
> > +	.free	= pch_msi_middle_domain_free,
> > +};
> > +
> > +static int pch_msi_init_domains(struct pch_msi_data *priv,
> > +				struct device_node *node,
> > +				struct device_node *parent)
> > +{
> > +	struct irq_domain *middle_domain, *msi_domain,
> > *parent_domain; +
> > +	parent_domain = irq_find_host(parent);
> > +	if (!parent_domain) {
> > +		pr_err("Failed to find the parent domain\n");
> > +		return -ENXIO;
> > +	}
> > +
> > +	middle_domain = irq_domain_add_tree(NULL,
> > +
> > &pch_msi_middle_domain_ops,
> > +					    priv);  
> 
> You don't really need a tree, unless your interrupt space is huge and
> very sparse. Given that the DT example says 64, I would go with a
> linear domain if that number is realistic.
> 
It can up to 192 in latest generation of chip, is it still suitable?

In the latest generation, we have a enhanced version of HTVEC which has
another delivery system that will be able to configure affinity. That's
why I placed set_affinity call back here and in PCH PIC driver.

Thanks. 

[...]
> > +}
> > +
> > +IRQCHIP_DECLARE(pch_msi, "loongson,pch-msi-1.0", pch_msi_init);  
> 
> Thanks,
> 
> 	M.

--
Jiaxun Yang

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

* Re: [PATCH 5/6] irqchip: Add Loongson PCH MSI controller
  2020-04-24  1:33     ` Jiaxun Yang
@ 2020-04-24  8:28       ` Marc Zyngier
  0 siblings, 0 replies; 41+ messages in thread
From: Marc Zyngier @ 2020-04-24  8:28 UTC (permalink / raw)
  To: Jiaxun Yang
  Cc: linux-mips, Thomas Gleixner, Jason Cooper, Rob Herring,
	Huacai Chen, linux-kernel, devicetree

On 2020-04-24 02:33, Jiaxun Yang wrote:
> On Thu, 23 Apr 2020 15:41:35 +0100
> Marc Zyngier <maz@kernel.org> wrote:
> 
>> On Wed, 22 Apr 2020 22:24:25 +0800
>> Jiaxun Yang <jiaxun.yang@flygoat.com> wrote:
>> 
>> > This controller appears on Loongson-7A family of PCH to transform
>> > interrupts from PCI MSI into HyperTransport vectorized interrrupts
>> > and send them to procrssor's HT vector controller.
>> >
>> > Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
>> > ---
> [...]
>> > +	ret = irq_domain_alloc_irqs_parent(domain, virq, 1,
>> > &fwspec);
>> > +	if (ret)
>> > +		return ret;
>> > +
>> > +	irq_domain_set_info(domain, virq, hwirq,
>> > +			    &middle_irq_chip, NULL,
>> > +			    handle_simple_irq, NULL, NULL);
>> 
>> No, this should at least be handle_edge_irq. More importantly, you
>> should use the flow set by the underlying irqchip, and not provide
>> your own.
> 
> Hi Marc,
> Thanks for your review.
> 
> The underlying irqchip (HTVEC) follows a simple_irq flow as it only
> has mask/unmask callback, and it doesn't have tyoe configuration. so I
> followed simple_irq flow.

Not having a type doesn't mean that it can stick to simple_irq, which is
the wrong things to use in 99% of the HW cases.

> How can I use the flow set by the underlying irqchip? Just use
> irq_domain_set_hwirq_and_chip here and set_handler in HTVEC driver?

You need to find out how the HTVEC behaves. My gut feeling is that it
is itself edge triggered, but you would need to look in the 
documentation
to find out.

> 
> 
>> 
>> > +	irq_set_probe(virq);
>> 
>> Probe? what does it mean for MSIs? I also don't see how you tell the
>> underlying irqchip that the MSI is edge triggered.
>> 
>> > +
>> > +	return 0;
>> > +}
>> > +
>> > +static int pch_msi_middle_domain_alloc(struct irq_domain *domain,
>> > +					   unsigned int virq,
>> > +					   unsigned int nr_irqs,
>> > void *args) +{
>> > +	struct pch_msi_data *priv = domain->host_data;
>> > +	int hwirq, err, i;
>> > +
>> > +	hwirq = pch_msi_allocate_hwirq(priv, nr_irqs);
>> > +	if (hwirq < 0)
>> > +		return hwirq;
>> > +
>> > +	for (i = 0; i < nr_irqs; i++) {
>> > +		err = pch_msi_parent_domain_alloc(domain, virq +
>> > i, hwirq + i);
>> > +		if (err)
>> > +			goto err_hwirq;
>> > +
>> > +		irq_domain_set_hwirq_and_chip(domain, virq + i,
>> > hwirq + i,
>> > +					      &middle_irq_chip,
>> > priv);
>> > +	}
>> > +
>> > +	return 0;
>> > +err_hwirq:
>> > +	while (--i >= 0)
>> > +		irq_domain_free_irqs_parent(domain, virq, i);
>> > +
>> > +	pch_msi_free_hwirq(priv, hwirq, nr_irqs);
>> > +	return err;
>> > +}
>> > +
>> > +static void pch_msi_middle_domain_free(struct irq_domain *domain,
>> > +					   unsigned int virq,
>> > +					   unsigned int nr_irqs)
>> > +{
>> > +	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
>> > +	struct pch_msi_data *priv = irq_data_get_irq_chip_data(d);
>> > +
>> > +	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
>> > +	pch_msi_free_hwirq(priv, d->hwirq, nr_irqs);
>> > +}
>> > +
>> > +static const struct irq_domain_ops pch_msi_middle_domain_ops = {
>> > +	.alloc	= pch_msi_middle_domain_alloc,
>> > +	.free	= pch_msi_middle_domain_free,
>> > +};
>> > +
>> > +static int pch_msi_init_domains(struct pch_msi_data *priv,
>> > +				struct device_node *node,
>> > +				struct device_node *parent)
>> > +{
>> > +	struct irq_domain *middle_domain, *msi_domain,
>> > *parent_domain; +
>> > +	parent_domain = irq_find_host(parent);
>> > +	if (!parent_domain) {
>> > +		pr_err("Failed to find the parent domain\n");
>> > +		return -ENXIO;
>> > +	}
>> > +
>> > +	middle_domain = irq_domain_add_tree(NULL,
>> > +
>> > &pch_msi_middle_domain_ops,
>> > +					    priv);
>> 
>> You don't really need a tree, unless your interrupt space is huge and
>> very sparse. Given that the DT example says 64, I would go with a
>> linear domain if that number is realistic.
>> 
> It can up to 192 in latest generation of chip, is it still suitable?

That's a pretty small number, really. Just stick to a linear domain.

> In the latest generation, we have a enhanced version of HTVEC which has
> another delivery system that will be able to configure affinity. That's
> why I placed set_affinity call back here and in PCH PIC driver.

Once you add support for this new version, this will make sense. at the
moment, this is pretty pointless.

Thanks,

         M.
-- 
Jazz is not dead. It just smells funny...

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

* [PATCH v2 1/6] irqchip: Add Loongson HyperTransport Vector support
  2020-04-22 14:24 [PATCH 0/6] Loongson PCH IRQ Support Jiaxun Yang
                   ` (6 preceding siblings ...)
  2020-04-23  5:50 ` [PATCH 0/6] Loongson PCH IRQ Support Huacai Chen
@ 2020-04-28  6:32 ` Jiaxun Yang
  2020-04-28  6:32   ` [PATCH v2 2/6] dt-bindings: interrupt-controller: Add Loongson HTVEC Jiaxun Yang
                     ` (5 more replies)
  2020-05-01  9:21 ` [PATCH v3 " Jiaxun Yang
  8 siblings, 6 replies; 41+ messages in thread
From: Jiaxun Yang @ 2020-04-28  6:32 UTC (permalink / raw)
  To: maz
  Cc: Jiaxun Yang, Thomas Gleixner, Jason Cooper, Rob Herring,
	Huacai Chen, linux-kernel, devicetree, linux-mips

This controller appears on Loongson-3 chips for receiving interrupt
vectors from PCH's PIC and PCH's PCIe MSI interrupts.

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---
v2:
	- Style cleanup
	- Set ack callback and set correct edge_irq handler
---
 drivers/irqchip/Kconfig              |   8 +
 drivers/irqchip/Makefile             |   1 +
 drivers/irqchip/irq-loongson-htvec.c | 214 +++++++++++++++++++++++++++
 3 files changed, 223 insertions(+)
 create mode 100644 drivers/irqchip/irq-loongson-htvec.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index a85aada04a64..de4564e2ea88 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -532,4 +532,12 @@ config LOONGSON_HTPIC
 	help
 	  Support for the Loongson-3 HyperTransport PIC Controller.
 
+config LOONGSON_HTVEC
+	bool "Loongson3 HyperTransport Interrupt Vector Controller"
+	depends on MACH_LOONGSON64 || COMPILE_TEST
+	default MACH_LOONGSON64
+	select IRQ_DOMAIN_HIERARCHY
+	help
+	  Support for the Loongson3 HyperTransport Interrupt Vector Controller.
+
 endmenu
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 37bbe39bf909..74561879f5a7 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -107,3 +107,4 @@ obj-$(CONFIG_TI_SCI_INTR_IRQCHIP)	+= irq-ti-sci-intr.o
 obj-$(CONFIG_TI_SCI_INTA_IRQCHIP)	+= irq-ti-sci-inta.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-loongson-htvec.c b/drivers/irqchip/irq-loongson-htvec.c
new file mode 100644
index 000000000000..3b6032e3bb13
--- /dev/null
+++ b/drivers/irqchip/irq-loongson-htvec.c
@@ -0,0 +1,214 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com>
+ *  Loongson HyperTransport Interrupt Vector support
+ */
+
+#define pr_fmt(fmt) "htvec: " 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>
+
+/* Registers */
+#define HTVEC_EN_OFF		0x20
+#define HTVEC_MAX_PARENT_IRQ	4
+
+#define VEC_COUNT_PER_REG	32
+#define VEC_REG_COUNT		4
+#define VEC_COUNT		(VEC_COUNT_PER_REG * VEC_REG_COUNT)
+#define VEC_REG_IDX(irq_id)	((irq_id) / VEC_COUNT_PER_REG)
+#define VEC_REG_BIT(irq_id)	((irq_id) % VEC_COUNT_PER_REG)
+
+struct htvec {
+	void __iomem		*base;
+	struct irq_domain	*htvec_domain;
+	raw_spinlock_t		htvec_lock;
+};
+
+static void htvec_irq_dispatch(struct irq_desc *desc)
+{
+	struct htvec *priv = irq_desc_get_handler_data(desc);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	u32 pending;
+	bool handled = false;
+	int i;
+
+	chained_irq_enter(chip, desc);
+
+	for (i = 0; i < VEC_REG_COUNT; i++) {
+		pending = readl(priv->base + 4 * i);
+		writel(pending, priv->base + 4 * i);
+		while (pending) {
+			int bit = __ffs(pending);
+
+			generic_handle_irq(irq_linear_revmap(priv->htvec_domain,
+						bit + VEC_COUNT_PER_REG * i));
+			pending &= ~BIT(bit);
+			handled = true;
+		}
+	}
+
+	if (!handled)
+		spurious_interrupt();
+
+	chained_irq_exit(chip, desc);
+}
+
+static void htvec_ack_irq(struct irq_data *d)
+{
+	struct htvec *priv = irq_data_get_irq_chip_data(d);
+	void __iomem *addr = priv->base;
+
+	writel(VEC_REG_BIT(d->hwirq), priv->base + VEC_REG_IDX(d->hwirq) * 4);
+}
+
+static void htvec_mask_irq(struct irq_data *d)
+{
+	struct htvec *priv = irq_data_get_irq_chip_data(d);
+	void __iomem *addr = priv->base + HTVEC_EN_OFF;
+	unsigned long flags;
+	u32 reg;
+
+	raw_spin_lock_irqsave(&priv->htvec_lock, flags);
+	addr += VEC_REG_IDX(d->hwirq) * 4;
+	reg = readl(addr);
+	reg &= ~BIT(VEC_REG_BIT(d->hwirq));
+	writel(reg, addr);
+	raw_spin_unlock_irqrestore(&priv->htvec_lock, flags);
+}
+
+static void htvec_unmask_irq(struct irq_data *d)
+{
+	struct htvec *priv = irq_data_get_irq_chip_data(d);
+	void __iomem *addr = priv->base + HTVEC_EN_OFF;
+	unsigned long flags;
+	u32 reg;
+
+	raw_spin_lock_irqsave(&priv->htvec_lock, flags);
+	addr += VEC_REG_IDX(d->hwirq) * 4;
+	reg = readl(addr);
+	reg |= BIT(VEC_REG_BIT(d->hwirq));
+	writel(reg, addr);
+	raw_spin_unlock_irqrestore(&priv->htvec_lock, flags);
+}
+
+static struct irq_chip htvec_irq_chip = {
+	.name			= "LOONGSON_HTVEC",
+	.irq_mask		= htvec_mask_irq,
+	.irq_unmask		= htvec_unmask_irq,
+	.irq_ack		= htvec_ack_irq,
+};
+
+static int htvec_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				  unsigned int nr_irqs, void *arg)
+{
+	struct htvec *priv = domain->host_data;
+	unsigned long hwirq;
+	unsigned int type, i;
+
+	irq_domain_translate_onecell(domain, arg, &hwirq, &type);
+
+	for (i = 0; i < nr_irqs; i++)
+		irq_domain_set_info(domain, virq + i, hwirq + i, &htvec_irq_chip,
+					priv, handle_edge_irq, NULL, NULL);
+
+	return 0;
+}
+
+static void htvec_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 htvec_domain_ops = {
+	.translate	= irq_domain_translate_onecell,
+	.alloc		= htvec_domain_alloc,
+	.free		= htvec_domain_free,
+};
+
+static void htvec_reset(struct htvec *priv)
+{
+	u32 idx;
+
+	/* Clear IRQ cause registers, mask all interrupts */
+	for (idx = 0; idx < VEC_REG_COUNT; idx++) {
+		writel_relaxed(0x0, priv->base + HTVEC_EN_OFF + 4 * idx);
+		writel_relaxed(0xFFFFFFFF, priv->base);
+	}
+}
+
+static int htvec_of_init(struct device_node *node,
+				struct device_node *parent)
+{
+	struct htvec *priv;
+	int err, parent_irq[4], num_parents = 0, i;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	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 4 interrupt line */
+	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++;
+	}
+
+	if (!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),
+						   VEC_COUNT,
+						   &htvec_domain_ops,
+						   priv);
+	if (!priv->htvec_domain) {
+		pr_err("Failed to create IRQ domain\n");
+		err = -ENOMEM;
+		goto iounmap_base;
+	}
+
+	htvec_reset(priv);
+
+	for (i = 0; i < num_parents; i++)
+		irq_set_chained_handler_and_data(parent_irq[i],
+						htvec_irq_dispatch, priv);
+
+	return 0;
+
+iounmap_base:
+	iounmap(priv->base);
+free_priv:
+	kfree(priv);
+
+	return err;
+}
+
+IRQCHIP_DECLARE(htvec, "loongson,htvec-1.0", htvec_of_init);
-- 
2.26.0.rc2


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

* [PATCH v2 2/6] dt-bindings: interrupt-controller: Add Loongson HTVEC
  2020-04-28  6:32 ` [PATCH v2 1/6] irqchip: Add Loongson HyperTransport Vector support Jiaxun Yang
@ 2020-04-28  6:32   ` Jiaxun Yang
  2020-04-28  6:32   ` [PATCH v2 3/6] irqchip: Add Loongson PCH PIC controller Jiaxun Yang
                     ` (4 subsequent siblings)
  5 siblings, 0 replies; 41+ messages in thread
From: Jiaxun Yang @ 2020-04-28  6:32 UTC (permalink / raw)
  To: maz
  Cc: Jiaxun Yang, Thomas Gleixner, Jason Cooper, Rob Herring,
	Huacai Chen, linux-kernel, devicetree, linux-mips

Add binding for Loongson-3 HyperTransport Interrupt Vector Controller.

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---
 .../interrupt-controller/loongson,htvec.yaml  | 59 +++++++++++++++++++
 1 file changed, 59 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/loongson,htvec.yaml

diff --git a/Documentation/devicetree/bindings/interrupt-controller/loongson,htvec.yaml b/Documentation/devicetree/bindings/interrupt-controller/loongson,htvec.yaml
new file mode 100644
index 000000000000..547a80c89eba
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/loongson,htvec.yaml
@@ -0,0 +1,59 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/interrupt-controller/loongson,htvec.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Loongson-3 HyperTransport Interrupt Vector Controller
+
+maintainers:
+  - Jiaxun Yang <jiaxun.yang@flygoat.com>
+
+allOf:
+  - $ref: /schemas/interrupt-controller.yaml#
+
+description: |
+  This interrupt controller is found in the Loongson-3 family of chips for
+  receiving vectorized interrupts from PCH's interrupt controller.
+
+properties:
+  compatible:
+    const: loongson,htvec-1.0
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    minItems: 1
+    maxItems: 4
+    description: |
+      Four parent interrupts that receive chained interrupts.
+
+  interrupt-controller: true
+
+  '#interrupt-cells':
+    const: 1
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - interrupt-controller
+  - '#interrupt-cells'
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    htvec: interrupt-controller@1fb000080 {
+      compatible = "loongson,htvec-1.0";
+      reg = <0xfb000080 0x40>;
+      interrupt-controller;
+      #interrupt-cells = <1>;
+
+      interrupt-parent = <&liointc>;
+      interrupts = <24 IRQ_TYPE_LEVEL_HIGH>,
+                    <25 IRQ_TYPE_LEVEL_HIGH>,
+                    <26 IRQ_TYPE_LEVEL_HIGH>,
+                    <27 IRQ_TYPE_LEVEL_HIGH>;
+    };
+...
-- 
2.26.0.rc2


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

* [PATCH v2 3/6] irqchip: Add Loongson PCH PIC controller
  2020-04-28  6:32 ` [PATCH v2 1/6] irqchip: Add Loongson HyperTransport Vector support Jiaxun Yang
  2020-04-28  6:32   ` [PATCH v2 2/6] dt-bindings: interrupt-controller: Add Loongson HTVEC Jiaxun Yang
@ 2020-04-28  6:32   ` Jiaxun Yang
  2020-05-13 12:09     ` Thomas Gleixner
  2020-04-28  6:32   ` [PATCH v2 4/6] dt-bindings: interrupt-controller: Add Loongson PCH PIC Jiaxun Yang
                     ` (3 subsequent siblings)
  5 siblings, 1 reply; 41+ messages in thread
From: Jiaxun Yang @ 2020-04-28  6:32 UTC (permalink / raw)
  To: maz
  Cc: Jiaxun Yang, Thomas Gleixner, Jason Cooper, Rob Herring,
	Huacai Chen, linux-kernel, devicetree, linux-mips

This controller appears on Loongson LS7A family of PCH to transform
interrupts from devices into HyperTransport vectorized interrrupts
and send them to procrssor's HT vector controller.

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---
v2:
	- Style clean-ups
	- Use IRQ_FASTEOI_HIERARCHY_HANDLERS
	- Move lock into bitclr & bitset
	- Make loongson,pic-base-vec as required property
---
 drivers/irqchip/Kconfig                |   9 +
 drivers/irqchip/Makefile               |   1 +
 drivers/irqchip/irq-loongson-pch-pic.c | 245 +++++++++++++++++++++++++
 3 files changed, 255 insertions(+)
 create mode 100644 drivers/irqchip/irq-loongson-pch-pic.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index de4564e2ea88..5524a621638c 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -540,4 +540,13 @@ config LOONGSON_HTVEC
 	help
 	  Support for the Loongson3 HyperTransport Interrupt Vector Controller.
 
+config LOONGSON_PCH_PIC
+	bool "Loongson PCH PIC Controller"
+	depends on MACH_LOONGSON64 || COMPILE_TEST
+	default MACH_LOONGSON64
+	select IRQ_DOMAIN_HIERARCHY
+	select IRQ_FASTEOI_HIERARCHY_HANDLERS
+	help
+	  Support for the Loongson PCH PIC Controller.
+
 endmenu
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 74561879f5a7..acc72331cec8 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -108,3 +108,4 @@ obj-$(CONFIG_TI_SCI_INTA_IRQCHIP)	+= irq-ti-sci-inta.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
+obj-$(CONFIG_LOONGSON_PCH_PIC)		+= irq-loongson-pch-pic.o
diff --git a/drivers/irqchip/irq-loongson-pch-pic.c b/drivers/irqchip/irq-loongson-pch-pic.c
new file mode 100644
index 000000000000..9b4605873b2a
--- /dev/null
+++ b/drivers/irqchip/irq-loongson-pch-pic.c
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com>
+ *  Loongson PCH PIC support
+ */
+
+#define pr_fmt(fmt) "pch-pic: " fmt
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.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>
+
+/* Registers */
+#define PCH_PIC_MASK		0x20
+#define PCH_PIC_HTMSI_EN	0x40
+#define PCH_PIC_EDGE		0x60
+#define PCH_PIC_CLR		0x80
+#define PCH_PIC_AUTO0		0xc0
+#define PCH_PIC_AUTO1		0xe0
+#define PCH_INT_ROUTE(irq)	(0x100 + irq)
+#define PCH_INT_HTVEC(irq)	(0x200 + irq)
+#define PCH_PIC_POL		0x3e0
+
+#define PIC_COUNT_PER_REG	32
+#define PIC_REG_COUNT		2
+#define PIC_COUNT		(PIC_COUNT_PER_REG * PIC_REG_COUNT)
+#define PIC_REG_IDX(irq_id)	((irq_id) / PIC_COUNT_PER_REG)
+#define PIC_REG_BIT(irq_id)	((irq_id) % PIC_COUNT_PER_REG)
+
+struct pch_pic {
+	void __iomem		*base;
+	struct irq_domain	*pic_domain;
+	u32			ht_vec_base;
+	raw_spinlock_t		pic_lock;
+};
+
+static void pch_pic_bitset(struct pch_pic *priv, int offset, int bit)
+{
+	void __iomem *addr = priv->base + offset + PIC_REG_IDX(bit) * 4;
+	unsigned long flags;
+	u32 reg;
+
+	raw_spin_lock_irqsave(&priv->pic_lock, flags);
+	reg = readl(addr);
+	reg |= BIT(PIC_REG_BIT(bit));
+	writel(reg, addr);
+	raw_spin_unlock_irqrestore(&priv->pic_lock, flags);
+}
+
+static void pch_pic_bitclr(struct pch_pic *priv, int offset, int bit)
+{
+	void __iomem *addr = priv->base + offset + PIC_REG_IDX(bit) * 4;
+	unsigned long flags;
+	u32 reg;
+
+	raw_spin_lock_irqsave(&priv->pic_lock, flags);
+	reg = readl(addr);
+	reg &= ~BIT(PIC_REG_BIT(bit));
+	writel(reg, addr);
+	raw_spin_unlock_irqrestore(&priv->pic_lock, flags);
+}
+
+static void pch_pic_eoi_irq(struct irq_data *d)
+{
+	struct pch_pic *priv = irq_data_get_irq_chip_data(d);
+	u32 idx = PIC_REG_IDX(d->hwirq);
+
+	writel(BIT(PIC_REG_BIT(d->hwirq)),
+			priv->base + PCH_PIC_CLR + idx * 4);
+}
+
+static void pch_pic_mask_irq(struct irq_data *d)
+{
+	struct pch_pic *priv = irq_data_get_irq_chip_data(d);
+
+	pch_pic_bitset(priv, PCH_PIC_MASK, d->hwirq);
+	irq_chip_mask_parent(d);
+}
+
+static void pch_pic_unmask_irq(struct irq_data *d)
+{
+	struct pch_pic *priv = irq_data_get_irq_chip_data(d);
+
+	pch_pic_bitclr(priv, PCH_PIC_MASK, d->hwirq);
+	irq_chip_unmask_parent(d);
+}
+
+static int pch_pic_set_type(struct irq_data *d, unsigned int type)
+{
+	struct pch_pic *priv = irq_data_get_irq_chip_data(d);
+	int ret = 0;
+
+	switch (type) {
+	case IRQ_TYPE_EDGE_RISING:
+		pch_pic_bitset(priv, PCH_PIC_EDGE, d->hwirq);
+		pch_pic_bitclr(priv, PCH_PIC_POL, d->hwirq);
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		pch_pic_bitset(priv, PCH_PIC_EDGE, d->hwirq);
+		pch_pic_bitset(priv, PCH_PIC_POL, d->hwirq);
+		break;
+	case IRQ_TYPE_LEVEL_HIGH:
+		pch_pic_bitclr(priv, PCH_PIC_EDGE, d->hwirq);
+		pch_pic_bitclr(priv, PCH_PIC_POL, d->hwirq);
+		break;
+	case IRQ_TYPE_LEVEL_LOW:
+		pch_pic_bitclr(priv, PCH_PIC_EDGE, d->hwirq);
+		pch_pic_bitset(priv, PCH_PIC_POL, d->hwirq);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static struct irq_chip pch_pic_irq_chip = {
+	.name			= "PCH PIC",
+	.irq_mask		= pch_pic_mask_irq,
+	.irq_unmask		= pch_pic_unmask_irq,
+	.irq_ack		= irq_chip_ack_parent,
+	.irq_eoi		= pch_pic_eoi_irq,
+	.irq_set_affinity	= irq_chip_set_affinity_parent,
+	.irq_set_type		= pch_pic_set_type,
+};
+
+static int pch_pic_alloc(struct irq_domain *domain, unsigned int virq,
+			      unsigned int nr_irqs, void *arg)
+{
+	struct pch_pic *priv = domain->host_data;
+	struct irq_fwspec fwspec;
+	unsigned long hwirq;
+	unsigned int type;
+	int err;
+
+	irq_domain_translate_twocell(domain, arg, &hwirq, &type);
+
+	fwspec.fwnode = domain->parent->fwnode;
+	fwspec.param_count = 1;
+	fwspec.param[0] = hwirq + priv->ht_vec_base;
+
+	err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
+	if (err)
+		return err;
+
+	irq_domain_set_info(domain, virq, hwirq,
+			    &pch_pic_irq_chip, priv,
+			    handle_fasteoi_ack_irq, NULL, NULL);
+	irq_set_probe(virq);
+
+	return 0;
+}
+
+static const struct irq_domain_ops pch_pic_domain_ops = {
+	.translate	= irq_domain_translate_twocell,
+	.alloc		= pch_pic_alloc,
+	.free		= irq_domain_free_irqs_parent,
+};
+
+static void pch_pic_reset(struct pch_pic *priv)
+{
+	int i;
+
+	for (i = 0; i < PIC_COUNT; i++) {
+		/* Write vectore 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));
+	}
+
+	for (i = 0; i < PIC_REG_COUNT; i++) {
+		/* Clear IRQ cause registers, mask all interrupts */
+		writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_MASK + 4 * i);
+		writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_CLR + 4 * i);
+		/* Clear auto bounce, we don't need that */
+		writel_relaxed(0, priv->base + PCH_PIC_AUTO0 + 4 * i);
+		writel_relaxed(0, priv->base + PCH_PIC_AUTO1 + 4 * i);
+		/* Enable HTMSI transformer */
+		writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_HTMSI_EN + 4 * i);
+	}
+}
+
+static int pch_pic_of_init(struct device_node *node,
+				struct device_node *parent)
+{
+	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;
+		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;
+	}
+
+	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->pic_domain = irq_domain_create_hierarchy(parent_domain, 0,
+						     PIC_COUNT,
+						     of_node_to_fwnode(node),
+						     &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);
+
+	return 0;
+
+iounmap_base:
+	iounmap(priv->base);
+free_priv:
+	kfree(priv);
+
+	return err;
+}
+
+IRQCHIP_DECLARE(pch_pic, "loongson,pch-pic-1.0", pch_pic_of_init);
-- 
2.26.0.rc2


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

* [PATCH v2 4/6] dt-bindings: interrupt-controller: Add Loongson PCH PIC
  2020-04-28  6:32 ` [PATCH v2 1/6] irqchip: Add Loongson HyperTransport Vector support Jiaxun Yang
  2020-04-28  6:32   ` [PATCH v2 2/6] dt-bindings: interrupt-controller: Add Loongson HTVEC Jiaxun Yang
  2020-04-28  6:32   ` [PATCH v2 3/6] irqchip: Add Loongson PCH PIC controller Jiaxun Yang
@ 2020-04-28  6:32   ` Jiaxun Yang
  2020-04-28  6:32   ` [PATCH v2 5/6] irqchip: Add Loongson PCH MSI controller Jiaxun Yang
                     ` (2 subsequent siblings)
  5 siblings, 0 replies; 41+ messages in thread
From: Jiaxun Yang @ 2020-04-28  6:32 UTC (permalink / raw)
  To: maz
  Cc: Jiaxun Yang, Thomas Gleixner, Jason Cooper, Rob Herring,
	Huacai Chen, linux-kernel, devicetree, linux-mips

Add binding for Loongson PCH PIC Controller.

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
--
v2:
	- Fix naming
	- Mark loongson,pic-base-vec as required
---
 .../loongson,pch-pic.yaml                     | 56 +++++++++++++++++++
 1 file changed, 56 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/loongson,pch-pic.yaml

diff --git a/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-pic.yaml b/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-pic.yaml
new file mode 100644
index 000000000000..92a8cd0ce23b
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-pic.yaml
@@ -0,0 +1,56 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/interrupt-controller/loongson,pch-pic.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Loongson PCH PIC Controller
+
+maintainers:
+  - Jiaxun Yang <jiaxun.yang@flygoat.com>
+
+allOf:
+  - $ref: /schemas/interrupt-controller.yaml#
+
+description: |
+  This interrupt controller is found in the Loongson LS7A family of PCH for
+  transforming interrupts from on-chip devices into HyperTransport vectorized
+  interrupts.
+
+properties:
+  compatible:
+    const: loongson,pch-pic-1.0
+
+  reg:
+    maxItems: 1
+
+  loongson,pic-base-vec:
+    $ref: '/schemas/types.yaml#/definitions/uint32'
+    description: |
+      u32 value of the base of parent HyperTransport vector allocated
+      to PCH PIC.
+
+  interrupt-controller: true
+
+  '#interrupt-cells':
+    const: 2
+
+required:
+  - compatible
+  - reg
+  - loongson,pic-base-vec
+  - interrupt-controller
+  - '#interrupt-cells'
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    pic: interrupt-controller@10000000 {
+      compatible = "loongson,pch-pic-1.0";
+      reg = <0x10000000 0x400>;
+      interrupt-controller;
+      #interrupt-cells = <2>;
+      loongson,pic-base-vec = <64>;
+      interrupt-parent = <&htvec>;
+    };
+...
-- 
2.26.0.rc2


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

* [PATCH v2 5/6] irqchip: Add Loongson PCH MSI controller
  2020-04-28  6:32 ` [PATCH v2 1/6] irqchip: Add Loongson HyperTransport Vector support Jiaxun Yang
                     ` (2 preceding siblings ...)
  2020-04-28  6:32   ` [PATCH v2 4/6] dt-bindings: interrupt-controller: Add Loongson PCH PIC Jiaxun Yang
@ 2020-04-28  6:32   ` Jiaxun Yang
  2020-04-28 13:07     ` Marc Zyngier
  2020-05-13 12:13     ` Thomas Gleixner
  2020-04-28  6:32   ` [PATCH v2 6/6] dt-bindings: interrupt-controller: Add Loongson PCH MSI Jiaxun Yang
  2020-04-28 16:59   ` [PATCH v2 1/6] irqchip: Add Loongson HyperTransport Vector support Marc Zyngier
  5 siblings, 2 replies; 41+ messages in thread
From: Jiaxun Yang @ 2020-04-28  6:32 UTC (permalink / raw)
  To: maz
  Cc: Jiaxun Yang, Thomas Gleixner, Jason Cooper, Rob Herring,
	Huacai Chen, linux-kernel, devicetree, linux-mips

This controller appears on Loongson LS7A family of PCH to transform
interrupts from PCI MSI into HyperTransport vectorized interrrupts
and send them to procrssor's HT vector controller.

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
--
v2:
	- Style clean-ups
	- Add ack callback
	- Use bitmap_find_free_region
---
 drivers/irqchip/Kconfig                |  10 +
 drivers/irqchip/Makefile               |   1 +
 drivers/irqchip/irq-loongson-pch-msi.c | 259 +++++++++++++++++++++++++
 3 files changed, 270 insertions(+)
 create mode 100644 drivers/irqchip/irq-loongson-pch-msi.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 5524a621638c..0b6b826dd843 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -549,4 +549,14 @@ config LOONGSON_PCH_PIC
 	help
 	  Support for the Loongson PCH PIC Controller.
 
+config LOONGSON_PCH_MSI
+	bool "Loongson PCH PIC Controller"
+	depends on MACH_LOONGSON64 || COMPILE_TEST
+	depends on PCI
+	default MACH_LOONGSON64
+	select IRQ_DOMAIN_HIERARCHY
+	select PCI_MSI
+	help
+	  Support for the Loongson PCH MSI Controller.
+
 endmenu
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index acc72331cec8..3a4ce283189a 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -109,3 +109,4 @@ obj-$(CONFIG_LOONGSON_LIOINTC)		+= irq-loongson-liointc.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
+obj-$(CONFIG_LOONGSON_PCH_MSI)		+= irq-loongson-pch-msi.o
diff --git a/drivers/irqchip/irq-loongson-pch-msi.c b/drivers/irqchip/irq-loongson-pch-msi.c
new file mode 100644
index 000000000000..5b4d607a899e
--- /dev/null
+++ b/drivers/irqchip/irq-loongson-pch-msi.c
@@ -0,0 +1,259 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com>
+ *  Loongson PCH MSI support
+ */
+
+#define pr_fmt(fmt) "pch-msi: " fmt
+
+#include <linux/irqchip.h>
+#include <linux/msi.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_pci.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+struct pch_msi_data {
+	spinlock_t		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;
+};
+
+static void pch_msi_mask_msi_irq(struct irq_data *d)
+{
+	pci_msi_mask_irq(d);
+	irq_chip_mask_parent(d);
+}
+
+static void pch_msi_unmask_msi_irq(struct irq_data *d)
+{
+	pci_msi_unmask_irq(d);
+	irq_chip_unmask_parent(d);
+}
+
+static struct irq_chip pch_msi_irq_chip = {
+	.name			= "PCH MSI",
+	.irq_mask		= pch_msi_mask_msi_irq,
+	.irq_unmask		= pch_msi_unmask_msi_irq,
+	.irq_ack		= irq_chip_ack_parent,
+	.irq_set_affinity	= irq_chip_set_affinity_parent,
+};
+
+static int pch_msi_allocate_hwirq(struct pch_msi_data *priv, int num_req)
+{
+	int first;
+
+	spin_lock(&priv->msi_map_lock);
+
+	first = bitmap_find_free_region(priv->msi_map, priv->num_irqs,
+					get_count_order(num_req));
+	if (first < 0) {
+		spin_unlock(&priv->msi_map_lock);
+		return -ENOSPC;
+	}
+
+	bitmap_set(priv->msi_map, first, num_req);
+	spin_unlock(&priv->msi_map_lock);
+
+	return priv->irq_first + first;
+}
+
+static void pch_msi_free_hwirq(struct pch_msi_data *priv,
+				int hwirq, int num_req)
+{
+	int first = hwirq - priv->irq_first;
+
+	spin_lock(&priv->msi_map_lock);
+	bitmap_clear(priv->msi_map, first, num_req);
+	spin_unlock(&priv->msi_map_lock);
+}
+
+static void pch_msi_compose_msi_msg(struct irq_data *data,
+					struct msi_msg *msg)
+{
+	struct pch_msi_data *priv = irq_data_get_irq_chip_data(data);
+
+	msg->address_hi = upper_32_bits(priv->doorbell);
+	msg->address_lo = lower_32_bits(priv->doorbell);
+	msg->data = data->hwirq;
+}
+
+static struct msi_domain_info pch_msi_domain_info = {
+	.flags		= MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+			  MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX,
+	.chip	= &pch_msi_irq_chip,
+};
+
+static struct irq_chip middle_irq_chip = {
+	.name			= "PCH MSI Middle",
+	.irq_mask		= irq_chip_mask_parent,
+	.irq_unmask		= irq_chip_unmask_parent,
+	.irq_ack		= irq_chip_ack_parent,
+	.irq_set_affinity	= irq_chip_set_affinity_parent,
+	.irq_compose_msi_msg	= pch_msi_compose_msi_msg,
+};
+
+static int pch_msi_parent_domain_alloc(struct irq_domain *domain,
+					unsigned int virq, int hwirq)
+{
+	struct irq_fwspec fwspec;
+	int ret;
+
+	fwspec.fwnode = domain->parent->fwnode;
+	fwspec.param_count = 1;
+	fwspec.param[0] = hwirq;
+
+	ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
+	if (ret)
+		return ret;
+
+	irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
+					&middle_irq_chip, NULL);
+
+	return 0;
+}
+
+static int pch_msi_middle_domain_alloc(struct irq_domain *domain,
+					   unsigned int virq,
+					   unsigned int nr_irqs, void *args)
+{
+	struct pch_msi_data *priv = domain->host_data;
+	int hwirq, err, i;
+
+	hwirq = pch_msi_allocate_hwirq(priv, nr_irqs);
+	if (hwirq < 0)
+		return hwirq;
+
+	for (i = 0; i < nr_irqs; i++) {
+		err = pch_msi_parent_domain_alloc(domain, virq + i, hwirq + i);
+		if (err)
+			goto err_hwirq;
+
+		irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
+					      &middle_irq_chip, priv);
+	}
+
+	return 0;
+
+err_hwirq:
+	while (--i >= 0)
+		irq_domain_free_irqs_parent(domain, virq, i);
+
+	pch_msi_free_hwirq(priv, hwirq, nr_irqs);
+	return err;
+}
+
+static void pch_msi_middle_domain_free(struct irq_domain *domain,
+					   unsigned int virq,
+					   unsigned int nr_irqs)
+{
+	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+	struct pch_msi_data *priv = irq_data_get_irq_chip_data(d);
+
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+	pch_msi_free_hwirq(priv, d->hwirq, nr_irqs);
+}
+
+static const struct irq_domain_ops pch_msi_middle_domain_ops = {
+	.alloc	= pch_msi_middle_domain_alloc,
+	.free	= pch_msi_middle_domain_free,
+};
+
+static int pch_msi_init_domains(struct pch_msi_data *priv,
+				struct device_node *node,
+				struct device_node *parent)
+{
+	struct irq_domain *middle_domain, *msi_domain, *parent_domain;
+
+	parent_domain = irq_find_host(parent);
+	if (!parent_domain) {
+		pr_err("Failed to find the parent domain\n");
+		return -ENXIO;
+	}
+
+	middle_domain = irq_domain_add_linear(NULL, priv->num_irqs,
+						&pch_msi_middle_domain_ops,
+						priv);
+	if (!middle_domain) {
+		pr_err("Failed to create the MSI middle domain\n");
+		return -ENOMEM;
+	}
+
+	middle_domain->parent = parent_domain;
+
+	msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(node),
+					       &pch_msi_domain_info,
+					       middle_domain);
+	if (!msi_domain) {
+		pr_err("Failed to create MSI domain\n");
+		irq_domain_remove(middle_domain);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static int pch_msi_init(struct device_node *node,
+			    struct device_node *parent)
+{
+	struct pch_msi_data *priv;
+	struct resource res;
+	int ret;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	spin_lock_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->msi_map = kcalloc(BITS_TO_LONGS(priv->num_irqs),
+				sizeof(*priv->msi_map),
+				GFP_KERNEL);
+	if (!priv->msi_map) {
+		ret = -ENOMEM;
+		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);
+	if (ret)
+		goto err_map;
+
+	return 0;
+
+err_map:
+	kfree(priv->msi_map);
+err_priv:
+	kfree(priv);
+	return ret;
+}
+
+IRQCHIP_DECLARE(pch_msi, "loongson,pch-msi-1.0", pch_msi_init);
-- 
2.26.0.rc2


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

* [PATCH v2 6/6] dt-bindings: interrupt-controller: Add Loongson PCH MSI
  2020-04-28  6:32 ` [PATCH v2 1/6] irqchip: Add Loongson HyperTransport Vector support Jiaxun Yang
                     ` (3 preceding siblings ...)
  2020-04-28  6:32   ` [PATCH v2 5/6] irqchip: Add Loongson PCH MSI controller Jiaxun Yang
@ 2020-04-28  6:32   ` Jiaxun Yang
  2020-04-28 16:59   ` [PATCH v2 1/6] irqchip: Add Loongson HyperTransport Vector support Marc Zyngier
  5 siblings, 0 replies; 41+ messages in thread
From: Jiaxun Yang @ 2020-04-28  6:32 UTC (permalink / raw)
  To: maz
  Cc: Jiaxun Yang, Thomas Gleixner, Jason Cooper, Rob Herring,
	Huacai Chen, linux-kernel, devicetree, linux-mips

Add binding for Loongson PCH MSI controller.

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---
 .../loongson,pch-msi.yaml                     | 56 +++++++++++++++++++
 1 file changed, 56 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml

diff --git a/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml b/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml
new file mode 100644
index 000000000000..513ed1933035
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml
@@ -0,0 +1,56 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/interrupt-controller/loongson,pch-msi.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Loongson PCH MSI Controller
+
+maintainers:
+  - Jiaxun Yang <jiaxun.yang@flygoat.com>
+
+description: |
+  This interrupt controller is found in the Loongson LS7A family of PCH for
+  transforming interrupts from PCIe MSI into HyperTransport vectorized
+  interrupts.
+
+properties:
+  compatible:
+    const: loongson,pch-msi-1.0
+
+  reg:
+    maxItems: 1
+
+  loongson,msi-base-vec:
+    $ref: '/schemas/types.yaml#/definitions/uint32'
+    description: |
+      u32 value of the base of parent HyperTransport vector allocated
+      to PCH MSI.
+
+  loongson,msi-num-vecs:
+    $ref: '/schemas/types.yaml#/definitions/uint32'
+    description: |
+      u32 value of the number of parent HyperTransport vectors allocated
+      to PCH MSI.
+
+  msi-controller: true
+
+required:
+  - compatible
+  - reg
+  - msi-controller
+  - loongson,msi-base-vec
+  - loongson,msi-num-vecs
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    msi: msi-controller@2ff00000 {
+      compatible = "loongson,pch-msi-1.0";
+      reg = <0x2ff00000 0x4>;
+      msi-controller;
+      loongson,msi-base-vec = <64>;
+      loongson,msi-num-vecs = <64>;
+      interrupt-parent = <&htvec>;
+    };
+...
-- 
2.26.0.rc2


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

* Re: [PATCH v2 5/6] irqchip: Add Loongson PCH MSI controller
  2020-04-28  6:32   ` [PATCH v2 5/6] irqchip: Add Loongson PCH MSI controller Jiaxun Yang
@ 2020-04-28 13:07     ` Marc Zyngier
  2020-05-13 12:13     ` Thomas Gleixner
  1 sibling, 0 replies; 41+ messages in thread
From: Marc Zyngier @ 2020-04-28 13:07 UTC (permalink / raw)
  To: Jiaxun Yang
  Cc: Thomas Gleixner, Jason Cooper, Rob Herring, Huacai Chen,
	linux-kernel, devicetree, linux-mips

On Tue, 28 Apr 2020 14:32:44 +0800
Jiaxun Yang <jiaxun.yang@flygoat.com> wrote:

> This controller appears on Loongson LS7A family of PCH to transform
> interrupts from PCI MSI into HyperTransport vectorized interrrupts
> and send them to procrssor's HT vector controller.
> 
> Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
> --
> v2:
> 	- Style clean-ups
> 	- Add ack callback
> 	- Use bitmap_find_free_region
> ---
>  drivers/irqchip/Kconfig                |  10 +
>  drivers/irqchip/Makefile               |   1 +
>  drivers/irqchip/irq-loongson-pch-msi.c | 259 +++++++++++++++++++++++++
>  3 files changed, 270 insertions(+)
>  create mode 100644 drivers/irqchip/irq-loongson-pch-msi.c
> 
> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> index 5524a621638c..0b6b826dd843 100644
> --- a/drivers/irqchip/Kconfig
> +++ b/drivers/irqchip/Kconfig
> @@ -549,4 +549,14 @@ config LOONGSON_PCH_PIC
>  	help
>  	  Support for the Loongson PCH PIC Controller.
>  
> +config LOONGSON_PCH_MSI
> +	bool "Loongson PCH PIC Controller"
> +	depends on MACH_LOONGSON64 || COMPILE_TEST
> +	depends on PCI
> +	default MACH_LOONGSON64
> +	select IRQ_DOMAIN_HIERARCHY
> +	select PCI_MSI
> +	help
> +	  Support for the Loongson PCH MSI Controller.
> +
>  endmenu
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index acc72331cec8..3a4ce283189a 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -109,3 +109,4 @@ obj-$(CONFIG_LOONGSON_LIOINTC)		+= irq-loongson-liointc.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
> +obj-$(CONFIG_LOONGSON_PCH_MSI)		+= irq-loongson-pch-msi.o
> diff --git a/drivers/irqchip/irq-loongson-pch-msi.c b/drivers/irqchip/irq-loongson-pch-msi.c
> new file mode 100644
> index 000000000000..5b4d607a899e
> --- /dev/null
> +++ b/drivers/irqchip/irq-loongson-pch-msi.c
> @@ -0,0 +1,259 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + *  Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com>
> + *  Loongson PCH MSI support
> + */
> +
> +#define pr_fmt(fmt) "pch-msi: " fmt
> +
> +#include <linux/irqchip.h>
> +#include <linux/msi.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_pci.h>
> +#include <linux/pci.h>
> +#include <linux/slab.h>
> +
> +struct pch_msi_data {
> +	spinlock_t		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;
> +};
> +
> +static void pch_msi_mask_msi_irq(struct irq_data *d)
> +{
> +	pci_msi_mask_irq(d);
> +	irq_chip_mask_parent(d);
> +}
> +
> +static void pch_msi_unmask_msi_irq(struct irq_data *d)
> +{
> +	pci_msi_unmask_irq(d);
> +	irq_chip_unmask_parent(d);
> +}
> +
> +static struct irq_chip pch_msi_irq_chip = {
> +	.name			= "PCH MSI",
> +	.irq_mask		= pch_msi_mask_msi_irq,
> +	.irq_unmask		= pch_msi_unmask_msi_irq,
> +	.irq_ack		= irq_chip_ack_parent,
> +	.irq_set_affinity	= irq_chip_set_affinity_parent,
> +};
> +
> +static int pch_msi_allocate_hwirq(struct pch_msi_data *priv, int num_req)
> +{
> +	int first;
> +
> +	spin_lock(&priv->msi_map_lock);

Why does it have to be a spinlock? As far as I can tell, we never
allocate MSIs from non-preemptible contexts.

> +
> +	first = bitmap_find_free_region(priv->msi_map, priv->num_irqs,
> +					get_count_order(num_req));
> +	if (first < 0) {
> +		spin_unlock(&priv->msi_map_lock);
> +		return -ENOSPC;
> +	}
> +
> +	bitmap_set(priv->msi_map, first, num_req);

What is that for? bitmap_find_free_region has already done the work for
you.

> +	spin_unlock(&priv->msi_map_lock);
> +
> +	return priv->irq_first + first;
> +}
> +
> +static void pch_msi_free_hwirq(struct pch_msi_data *priv,
> +				int hwirq, int num_req)
> +{
> +	int first = hwirq - priv->irq_first;
> +
> +	spin_lock(&priv->msi_map_lock);
> +	bitmap_clear(priv->msi_map, first, num_req);

bitmap_clear doesn't reverse the effects of bitmap_find_free_region.

> +	spin_unlock(&priv->msi_map_lock);
> +}
> +
> +static void pch_msi_compose_msi_msg(struct irq_data *data,
> +					struct msi_msg *msg)
> +{
> +	struct pch_msi_data *priv = irq_data_get_irq_chip_data(data);
> +
> +	msg->address_hi = upper_32_bits(priv->doorbell);
> +	msg->address_lo = lower_32_bits(priv->doorbell);
> +	msg->data = data->hwirq;
> +}
> +
> +static struct msi_domain_info pch_msi_domain_info = {
> +	.flags		= MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +			  MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX,
> +	.chip	= &pch_msi_irq_chip,
> +};
> +
> +static struct irq_chip middle_irq_chip = {
> +	.name			= "PCH MSI Middle",

This "middle" thing doesn't mean anything. What it really implements is
a generic MSI irqchip. What you call PCH MSI seems to be PCI-MSI. Pretty
confusing.

> +	.irq_mask		= irq_chip_mask_parent,
> +	.irq_unmask		= irq_chip_unmask_parent,
> +	.irq_ack		= irq_chip_ack_parent,
> +	.irq_set_affinity	= irq_chip_set_affinity_parent,
> +	.irq_compose_msi_msg	= pch_msi_compose_msi_msg,
> +};
> +
> +static int pch_msi_parent_domain_alloc(struct irq_domain *domain,
> +					unsigned int virq, int hwirq)
> +{
> +	struct irq_fwspec fwspec;
> +	int ret;
> +
> +	fwspec.fwnode = domain->parent->fwnode;
> +	fwspec.param_count = 1;
> +	fwspec.param[0] = hwirq;
> +
> +	ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
> +	if (ret)
> +		return ret;
> +
> +	irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
> +					&middle_irq_chip, NULL);
> +
> +	return 0;
> +}
> +
> +static int pch_msi_middle_domain_alloc(struct irq_domain *domain,
> +					   unsigned int virq,
> +					   unsigned int nr_irqs, void *args)
> +{
> +	struct pch_msi_data *priv = domain->host_data;
> +	int hwirq, err, i;
> +
> +	hwirq = pch_msi_allocate_hwirq(priv, nr_irqs);
> +	if (hwirq < 0)
> +		return hwirq;
> +
> +	for (i = 0; i < nr_irqs; i++) {
> +		err = pch_msi_parent_domain_alloc(domain, virq + i, hwirq + i);
> +		if (err)
> +			goto err_hwirq;
> +
> +		irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
> +					      &middle_irq_chip, priv);

So you're doing that twice per MSI. I think once is enough.

> +	}
> +
> +	return 0;
> +
> +err_hwirq:
> +	while (--i >= 0)
> +		irq_domain_free_irqs_parent(domain, virq, i);

This looks very wrong. Why isn't it just:

	irq_domain_free_irqs_parent(domain, virq, i - 1);

?

> +
> +	pch_msi_free_hwirq(priv, hwirq, nr_irqs);

And when you look at the whole error handling (once fixed), it is
exactly pch_msi_middle_domain_free(). So why not calling this directly?

> +	return err;
> +}
> +
> +static void pch_msi_middle_domain_free(struct irq_domain *domain,
> +					   unsigned int virq,
> +					   unsigned int nr_irqs)
> +{
> +	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
> +	struct pch_msi_data *priv = irq_data_get_irq_chip_data(d);
> +
> +	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> +	pch_msi_free_hwirq(priv, d->hwirq, nr_irqs);
> +}
> +
> +static const struct irq_domain_ops pch_msi_middle_domain_ops = {
> +	.alloc	= pch_msi_middle_domain_alloc,
> +	.free	= pch_msi_middle_domain_free,
> +};
> +
> +static int pch_msi_init_domains(struct pch_msi_data *priv,
> +				struct device_node *node,
> +				struct device_node *parent)
> +{
> +	struct irq_domain *middle_domain, *msi_domain,
> *parent_domain; +
> +	parent_domain = irq_find_host(parent);
> +	if (!parent_domain) {
> +		pr_err("Failed to find the parent domain\n");
> +		return -ENXIO;
> +	}

Can't you check this early in the probe routine and bail out?

> +
> +	middle_domain = irq_domain_add_linear(NULL, priv->num_irqs,
> +						&pch_msi_middle_domain_ops,
> +						priv);

NULL isn't an acceptable parameter. This irqdomain really belongs to
the node. See irq_domain_update_bus_token() to specialize the domain so
that its allocation doesn't clash with the PCI domain that you allocate
below (DOMAIN_BUS_NEXUS is used by quite a few drivers).

Also, please use irq_domain_create_linear instead, in order
to be consistent with the rest of the API usage in this file.

> +	if (!middle_domain) {
> +		pr_err("Failed to create the MSI middle domain\n");
> +		return -ENOMEM;
> +	}
> +
> +	middle_domain->parent = parent_domain;
> +
> +	msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(node),
> +					       &pch_msi_domain_info,
> +					       middle_domain);
> +	if (!msi_domain) {
> +		pr_err("Failed to create MSI domain\n");
> +		irq_domain_remove(middle_domain);
> +		return -ENOMEM;
> +	}
> +
> +	return 0;
> +}
> +
> +static int pch_msi_init(struct device_node *node,
> +			    struct device_node *parent)
> +{
> +	struct pch_msi_data *priv;
> +	struct resource res;
> +	int ret;
> +
> +	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	spin_lock_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->msi_map = kcalloc(BITS_TO_LONGS(priv->num_irqs),
> +				sizeof(*priv->msi_map),
> +				GFP_KERNEL);

We have bitmap_alloc() that should already do the right thing.

> +	if (!priv->msi_map) {
> +		ret = -ENOMEM;
> +		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);
> +	if (ret)
> +		goto err_map;
> +
> +	return 0;
> +
> +err_map:
> +	kfree(priv->msi_map);
> +err_priv:
> +	kfree(priv);
> +	return ret;
> +}
> +
> +IRQCHIP_DECLARE(pch_msi, "loongson,pch-msi-1.0", pch_msi_init);


Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

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

* Re: [PATCH v2 1/6] irqchip: Add Loongson HyperTransport Vector support
  2020-04-28  6:32 ` [PATCH v2 1/6] irqchip: Add Loongson HyperTransport Vector support Jiaxun Yang
                     ` (4 preceding siblings ...)
  2020-04-28  6:32   ` [PATCH v2 6/6] dt-bindings: interrupt-controller: Add Loongson PCH MSI Jiaxun Yang
@ 2020-04-28 16:59   ` Marc Zyngier
  5 siblings, 0 replies; 41+ messages in thread
From: Marc Zyngier @ 2020-04-28 16:59 UTC (permalink / raw)
  To: Jiaxun Yang
  Cc: Thomas Gleixner, Jason Cooper, Rob Herring, Huacai Chen,
	linux-kernel, devicetree, linux-mips

On Tue, 28 Apr 2020 14:32:40 +0800
Jiaxun Yang <jiaxun.yang@flygoat.com> wrote:

> This controller appears on Loongson-3 chips for receiving interrupt
> vectors from PCH's PIC and PCH's PCIe MSI interrupts.
> 
> Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
> ---
> v2:
> 	- Style cleanup
> 	- Set ack callback and set correct edge_irq handler
> ---
>  drivers/irqchip/Kconfig              |   8 +
>  drivers/irqchip/Makefile             |   1 +
>  drivers/irqchip/irq-loongson-htvec.c | 214 +++++++++++++++++++++++++++
>  3 files changed, 223 insertions(+)
>  create mode 100644 drivers/irqchip/irq-loongson-htvec.c
> 
> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> index a85aada04a64..de4564e2ea88 100644
> --- a/drivers/irqchip/Kconfig
> +++ b/drivers/irqchip/Kconfig
> @@ -532,4 +532,12 @@ config LOONGSON_HTPIC
>  	help
>  	  Support for the Loongson-3 HyperTransport PIC Controller.
>  
> +config LOONGSON_HTVEC
> +	bool "Loongson3 HyperTransport Interrupt Vector Controller"
> +	depends on MACH_LOONGSON64 || COMPILE_TEST
> +	default MACH_LOONGSON64
> +	select IRQ_DOMAIN_HIERARCHY
> +	help
> +	  Support for the Loongson3 HyperTransport Interrupt Vector Controller.
> +
>  endmenu
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index 37bbe39bf909..74561879f5a7 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -107,3 +107,4 @@ obj-$(CONFIG_TI_SCI_INTR_IRQCHIP)	+= irq-ti-sci-intr.o
>  obj-$(CONFIG_TI_SCI_INTA_IRQCHIP)	+= irq-ti-sci-inta.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-loongson-htvec.c b/drivers/irqchip/irq-loongson-htvec.c
> new file mode 100644
> index 000000000000..3b6032e3bb13
> --- /dev/null
> +++ b/drivers/irqchip/irq-loongson-htvec.c
> @@ -0,0 +1,214 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + *  Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com>
> + *  Loongson HyperTransport Interrupt Vector support
> + */
> +
> +#define pr_fmt(fmt) "htvec: " 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>
> +
> +/* Registers */
> +#define HTVEC_EN_OFF		0x20
> +#define HTVEC_MAX_PARENT_IRQ	4
> +
> +#define VEC_COUNT_PER_REG	32
> +#define VEC_REG_COUNT		4
> +#define VEC_COUNT		(VEC_COUNT_PER_REG * VEC_REG_COUNT)
> +#define VEC_REG_IDX(irq_id)	((irq_id) / VEC_COUNT_PER_REG)
> +#define VEC_REG_BIT(irq_id)	((irq_id) % VEC_COUNT_PER_REG)
> +
> +struct htvec {
> +	void __iomem		*base;
> +	struct irq_domain	*htvec_domain;
> +	raw_spinlock_t		htvec_lock;
> +};
> +
> +static void htvec_irq_dispatch(struct irq_desc *desc)
> +{
> +	struct htvec *priv = irq_desc_get_handler_data(desc);
> +	struct irq_chip *chip = irq_desc_get_chip(desc);
> +	u32 pending;
> +	bool handled = false;
> +	int i;
> +
> +	chained_irq_enter(chip, desc);
> +
> +	for (i = 0; i < VEC_REG_COUNT; i++) {
> +		pending = readl(priv->base + 4 * i);
> +		writel(pending, priv->base + 4 * i);
> +		while (pending) {
> +			int bit = __ffs(pending);
> +
> +			generic_handle_irq(irq_linear_revmap(priv->htvec_domain,
> +						bit + VEC_COUNT_PER_REG * i));
> +			pending &= ~BIT(bit);
> +			handled = true;
> +		}
> +	}
> +
> +	if (!handled)
> +		spurious_interrupt();
> +
> +	chained_irq_exit(chip, desc);
> +}
> +
> +static void htvec_ack_irq(struct irq_data *d)
> +{
> +	struct htvec *priv = irq_data_get_irq_chip_data(d);
> +	void __iomem *addr = priv->base;
> +
> +	writel(VEC_REG_BIT(d->hwirq), priv->base + VEC_REG_IDX(d->hwirq) * 4);

Are you really sure about this? All the other operations seems to be
based on a hot-bit pattern, while these particular registers seems to
be taking hwirq mod 32. I'm willing to bet this is wrong.

	M.
-- 
Jazz is not dead. It just smells funny...

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

* [PATCH v3 1/6] irqchip: Add Loongson HyperTransport Vector support
  2020-04-22 14:24 [PATCH 0/6] Loongson PCH IRQ Support Jiaxun Yang
                   ` (7 preceding siblings ...)
  2020-04-28  6:32 ` [PATCH v2 1/6] irqchip: Add Loongson HyperTransport Vector support Jiaxun Yang
@ 2020-05-01  9:21 ` Jiaxun Yang
  2020-05-01  9:21   ` [PATCH v3 2/6] dt-bindings: interrupt-controller: Add Loongson HTVEC Jiaxun Yang
                     ` (6 more replies)
  8 siblings, 7 replies; 41+ messages in thread
From: Jiaxun Yang @ 2020-05-01  9:21 UTC (permalink / raw)
  To: maz
  Cc: Jiaxun Yang, Thomas Gleixner, Jason Cooper, Rob Herring,
	Huacai Chen, linux-kernel, devicetree, linux-mips

This controller appears on Loongson-3 chips for receiving interrupt
vectors from PCH's PIC and PCH's PCIe MSI interrupts.

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---
v2:
	- Style cleanup
	- Set ack callback and set correct edge_irq handler

v3:
	- Correct bitops in ACK callback
---
 drivers/irqchip/Kconfig              |   8 +
 drivers/irqchip/Makefile             |   1 +
 drivers/irqchip/irq-loongson-htvec.c | 213 +++++++++++++++++++++++++++
 3 files changed, 222 insertions(+)
 create mode 100644 drivers/irqchip/irq-loongson-htvec.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index a85aada04a64..de4564e2ea88 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -532,4 +532,12 @@ config LOONGSON_HTPIC
 	help
 	  Support for the Loongson-3 HyperTransport PIC Controller.
 
+config LOONGSON_HTVEC
+	bool "Loongson3 HyperTransport Interrupt Vector Controller"
+	depends on MACH_LOONGSON64 || COMPILE_TEST
+	default MACH_LOONGSON64
+	select IRQ_DOMAIN_HIERARCHY
+	help
+	  Support for the Loongson3 HyperTransport Interrupt Vector Controller.
+
 endmenu
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 37bbe39bf909..74561879f5a7 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -107,3 +107,4 @@ obj-$(CONFIG_TI_SCI_INTR_IRQCHIP)	+= irq-ti-sci-intr.o
 obj-$(CONFIG_TI_SCI_INTA_IRQCHIP)	+= irq-ti-sci-inta.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-loongson-htvec.c b/drivers/irqchip/irq-loongson-htvec.c
new file mode 100644
index 000000000000..b3ad9931d08b
--- /dev/null
+++ b/drivers/irqchip/irq-loongson-htvec.c
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com>
+ *  Loongson HyperTransport Interrupt Vector support
+ */
+
+#define pr_fmt(fmt) "htvec: " 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>
+
+/* Registers */
+#define HTVEC_EN_OFF		0x20
+#define HTVEC_MAX_PARENT_IRQ	4
+
+#define VEC_COUNT_PER_REG	32
+#define VEC_REG_COUNT		4
+#define VEC_COUNT		(VEC_COUNT_PER_REG * VEC_REG_COUNT)
+#define VEC_REG_IDX(irq_id)	((irq_id) / VEC_COUNT_PER_REG)
+#define VEC_REG_BIT(irq_id)	((irq_id) % VEC_COUNT_PER_REG)
+
+struct htvec {
+	void __iomem		*base;
+	struct irq_domain	*htvec_domain;
+	raw_spinlock_t		htvec_lock;
+};
+
+static void htvec_irq_dispatch(struct irq_desc *desc)
+{
+	struct htvec *priv = irq_desc_get_handler_data(desc);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	u32 pending;
+	bool handled = false;
+	int i;
+
+	chained_irq_enter(chip, desc);
+
+	for (i = 0; i < VEC_REG_COUNT; i++) {
+		pending = readl(priv->base + 4 * i);
+		while (pending) {
+			int bit = __ffs(pending);
+
+			generic_handle_irq(irq_linear_revmap(priv->htvec_domain,
+						bit + VEC_COUNT_PER_REG * i));
+			pending &= ~BIT(bit);
+			handled = true;
+		}
+	}
+
+	if (!handled)
+		spurious_interrupt();
+
+	chained_irq_exit(chip, desc);
+}
+
+static void htvec_ack_irq(struct irq_data *d)
+{
+	struct htvec *priv = irq_data_get_irq_chip_data(d);
+
+	writel(BIT(VEC_REG_BIT(d->hwirq)),
+		priv->base + VEC_REG_IDX(d->hwirq) * 4);
+}
+
+static void htvec_mask_irq(struct irq_data *d)
+{
+	struct htvec *priv = irq_data_get_irq_chip_data(d);
+	void __iomem *addr = priv->base + HTVEC_EN_OFF;
+	unsigned long flags;
+	u32 reg;
+
+	raw_spin_lock_irqsave(&priv->htvec_lock, flags);
+	addr += VEC_REG_IDX(d->hwirq) * 4;
+	reg = readl(addr);
+	reg &= ~BIT(VEC_REG_BIT(d->hwirq));
+	writel(reg, addr);
+	raw_spin_unlock_irqrestore(&priv->htvec_lock, flags);
+}
+
+static void htvec_unmask_irq(struct irq_data *d)
+{
+	struct htvec *priv = irq_data_get_irq_chip_data(d);
+	void __iomem *addr = priv->base + HTVEC_EN_OFF;
+	unsigned long flags;
+	u32 reg;
+
+	raw_spin_lock_irqsave(&priv->htvec_lock, flags);
+	addr += VEC_REG_IDX(d->hwirq) * 4;
+	reg = readl(addr);
+	reg |= BIT(VEC_REG_BIT(d->hwirq));
+	writel(reg, addr);
+	raw_spin_unlock_irqrestore(&priv->htvec_lock, flags);
+}
+
+static struct irq_chip htvec_irq_chip = {
+	.name			= "LOONGSON_HTVEC",
+	.irq_mask		= htvec_mask_irq,
+	.irq_unmask		= htvec_unmask_irq,
+	.irq_ack		= htvec_ack_irq,
+};
+
+static int htvec_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				  unsigned int nr_irqs, void *arg)
+{
+	struct htvec *priv = domain->host_data;
+	unsigned long hwirq;
+	unsigned int type, i;
+
+	irq_domain_translate_onecell(domain, arg, &hwirq, &type);
+
+	for (i = 0; i < nr_irqs; i++)
+		irq_domain_set_info(domain, virq + i, hwirq + i, &htvec_irq_chip,
+					priv, handle_edge_irq, NULL, NULL);
+
+	return 0;
+}
+
+static void htvec_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 htvec_domain_ops = {
+	.translate	= irq_domain_translate_onecell,
+	.alloc		= htvec_domain_alloc,
+	.free		= htvec_domain_free,
+};
+
+static void htvec_reset(struct htvec *priv)
+{
+	u32 idx;
+
+	/* Clear IRQ cause registers, mask all interrupts */
+	for (idx = 0; idx < VEC_REG_COUNT; idx++) {
+		writel_relaxed(0x0, priv->base + HTVEC_EN_OFF + 4 * idx);
+		writel_relaxed(0xFFFFFFFF, priv->base);
+	}
+}
+
+static int htvec_of_init(struct device_node *node,
+				struct device_node *parent)
+{
+	struct htvec *priv;
+	int err, parent_irq[4], num_parents = 0, i;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	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 4 interrupt line */
+	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++;
+	}
+
+	if (!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),
+						   VEC_COUNT,
+						   &htvec_domain_ops,
+						   priv);
+	if (!priv->htvec_domain) {
+		pr_err("Failed to create IRQ domain\n");
+		err = -ENOMEM;
+		goto iounmap_base;
+	}
+
+	htvec_reset(priv);
+
+	for (i = 0; i < num_parents; i++)
+		irq_set_chained_handler_and_data(parent_irq[i],
+						htvec_irq_dispatch, priv);
+
+	return 0;
+
+iounmap_base:
+	iounmap(priv->base);
+free_priv:
+	kfree(priv);
+
+	return err;
+}
+
+IRQCHIP_DECLARE(htvec, "loongson,htvec-1.0", htvec_of_init);
-- 
2.26.0.rc2


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

* [PATCH v3 2/6] dt-bindings: interrupt-controller: Add Loongson HTVEC
  2020-05-01  9:21 ` [PATCH v3 " Jiaxun Yang
@ 2020-05-01  9:21   ` Jiaxun Yang
  2020-05-12 16:42     ` Rob Herring
  2020-05-01  9:21   ` [PATCH v3 3/6] irqchip: Add Loongson PCH PIC controller Jiaxun Yang
                     ` (5 subsequent siblings)
  6 siblings, 1 reply; 41+ messages in thread
From: Jiaxun Yang @ 2020-05-01  9:21 UTC (permalink / raw)
  To: maz
  Cc: Jiaxun Yang, Thomas Gleixner, Jason Cooper, Rob Herring,
	Huacai Chen, linux-kernel, devicetree, linux-mips

Add binding for Loongson-3 HyperTransport Interrupt Vector Controller.

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---
 .../interrupt-controller/loongson,htvec.yaml  | 59 +++++++++++++++++++
 1 file changed, 59 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/loongson,htvec.yaml

diff --git a/Documentation/devicetree/bindings/interrupt-controller/loongson,htvec.yaml b/Documentation/devicetree/bindings/interrupt-controller/loongson,htvec.yaml
new file mode 100644
index 000000000000..547a80c89eba
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/loongson,htvec.yaml
@@ -0,0 +1,59 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/interrupt-controller/loongson,htvec.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Loongson-3 HyperTransport Interrupt Vector Controller
+
+maintainers:
+  - Jiaxun Yang <jiaxun.yang@flygoat.com>
+
+allOf:
+  - $ref: /schemas/interrupt-controller.yaml#
+
+description: |
+  This interrupt controller is found in the Loongson-3 family of chips for
+  receiving vectorized interrupts from PCH's interrupt controller.
+
+properties:
+  compatible:
+    const: loongson,htvec-1.0
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    minItems: 1
+    maxItems: 4
+    description: |
+      Four parent interrupts that receive chained interrupts.
+
+  interrupt-controller: true
+
+  '#interrupt-cells':
+    const: 1
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - interrupt-controller
+  - '#interrupt-cells'
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    htvec: interrupt-controller@1fb000080 {
+      compatible = "loongson,htvec-1.0";
+      reg = <0xfb000080 0x40>;
+      interrupt-controller;
+      #interrupt-cells = <1>;
+
+      interrupt-parent = <&liointc>;
+      interrupts = <24 IRQ_TYPE_LEVEL_HIGH>,
+                    <25 IRQ_TYPE_LEVEL_HIGH>,
+                    <26 IRQ_TYPE_LEVEL_HIGH>,
+                    <27 IRQ_TYPE_LEVEL_HIGH>;
+    };
+...
-- 
2.26.0.rc2


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

* [PATCH v3 3/6] irqchip: Add Loongson PCH PIC controller
  2020-05-01  9:21 ` [PATCH v3 " Jiaxun Yang
  2020-05-01  9:21   ` [PATCH v3 2/6] dt-bindings: interrupt-controller: Add Loongson HTVEC Jiaxun Yang
@ 2020-05-01  9:21   ` Jiaxun Yang
  2020-05-01  9:21   ` [PATCH v3 4/6] dt-bindings: interrupt-controller: Add Loongson PCH PIC Jiaxun Yang
                     ` (4 subsequent siblings)
  6 siblings, 0 replies; 41+ messages in thread
From: Jiaxun Yang @ 2020-05-01  9:21 UTC (permalink / raw)
  To: maz
  Cc: Jiaxun Yang, Thomas Gleixner, Jason Cooper, Rob Herring,
	Huacai Chen, linux-kernel, devicetree, linux-mips

This controller appears on Loongson LS7A family of PCH to transform
interrupts from devices into HyperTransport vectorized interrrupts
and send them to procrssor's HT vector controller.

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---
v2:
	- Style clean-ups
	- Use IRQ_FASTEOI_HIERARCHY_HANDLERS
	- Move lock into bitclr & bitset
	- Make loongson,pic-base-vec as required property
---
 drivers/irqchip/Kconfig                |   9 +
 drivers/irqchip/Makefile               |   1 +
 drivers/irqchip/irq-loongson-pch-pic.c | 245 +++++++++++++++++++++++++
 3 files changed, 255 insertions(+)
 create mode 100644 drivers/irqchip/irq-loongson-pch-pic.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index de4564e2ea88..5524a621638c 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -540,4 +540,13 @@ config LOONGSON_HTVEC
 	help
 	  Support for the Loongson3 HyperTransport Interrupt Vector Controller.
 
+config LOONGSON_PCH_PIC
+	bool "Loongson PCH PIC Controller"
+	depends on MACH_LOONGSON64 || COMPILE_TEST
+	default MACH_LOONGSON64
+	select IRQ_DOMAIN_HIERARCHY
+	select IRQ_FASTEOI_HIERARCHY_HANDLERS
+	help
+	  Support for the Loongson PCH PIC Controller.
+
 endmenu
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 74561879f5a7..acc72331cec8 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -108,3 +108,4 @@ obj-$(CONFIG_TI_SCI_INTA_IRQCHIP)	+= irq-ti-sci-inta.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
+obj-$(CONFIG_LOONGSON_PCH_PIC)		+= irq-loongson-pch-pic.o
diff --git a/drivers/irqchip/irq-loongson-pch-pic.c b/drivers/irqchip/irq-loongson-pch-pic.c
new file mode 100644
index 000000000000..9b4605873b2a
--- /dev/null
+++ b/drivers/irqchip/irq-loongson-pch-pic.c
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com>
+ *  Loongson PCH PIC support
+ */
+
+#define pr_fmt(fmt) "pch-pic: " fmt
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.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>
+
+/* Registers */
+#define PCH_PIC_MASK		0x20
+#define PCH_PIC_HTMSI_EN	0x40
+#define PCH_PIC_EDGE		0x60
+#define PCH_PIC_CLR		0x80
+#define PCH_PIC_AUTO0		0xc0
+#define PCH_PIC_AUTO1		0xe0
+#define PCH_INT_ROUTE(irq)	(0x100 + irq)
+#define PCH_INT_HTVEC(irq)	(0x200 + irq)
+#define PCH_PIC_POL		0x3e0
+
+#define PIC_COUNT_PER_REG	32
+#define PIC_REG_COUNT		2
+#define PIC_COUNT		(PIC_COUNT_PER_REG * PIC_REG_COUNT)
+#define PIC_REG_IDX(irq_id)	((irq_id) / PIC_COUNT_PER_REG)
+#define PIC_REG_BIT(irq_id)	((irq_id) % PIC_COUNT_PER_REG)
+
+struct pch_pic {
+	void __iomem		*base;
+	struct irq_domain	*pic_domain;
+	u32			ht_vec_base;
+	raw_spinlock_t		pic_lock;
+};
+
+static void pch_pic_bitset(struct pch_pic *priv, int offset, int bit)
+{
+	void __iomem *addr = priv->base + offset + PIC_REG_IDX(bit) * 4;
+	unsigned long flags;
+	u32 reg;
+
+	raw_spin_lock_irqsave(&priv->pic_lock, flags);
+	reg = readl(addr);
+	reg |= BIT(PIC_REG_BIT(bit));
+	writel(reg, addr);
+	raw_spin_unlock_irqrestore(&priv->pic_lock, flags);
+}
+
+static void pch_pic_bitclr(struct pch_pic *priv, int offset, int bit)
+{
+	void __iomem *addr = priv->base + offset + PIC_REG_IDX(bit) * 4;
+	unsigned long flags;
+	u32 reg;
+
+	raw_spin_lock_irqsave(&priv->pic_lock, flags);
+	reg = readl(addr);
+	reg &= ~BIT(PIC_REG_BIT(bit));
+	writel(reg, addr);
+	raw_spin_unlock_irqrestore(&priv->pic_lock, flags);
+}
+
+static void pch_pic_eoi_irq(struct irq_data *d)
+{
+	struct pch_pic *priv = irq_data_get_irq_chip_data(d);
+	u32 idx = PIC_REG_IDX(d->hwirq);
+
+	writel(BIT(PIC_REG_BIT(d->hwirq)),
+			priv->base + PCH_PIC_CLR + idx * 4);
+}
+
+static void pch_pic_mask_irq(struct irq_data *d)
+{
+	struct pch_pic *priv = irq_data_get_irq_chip_data(d);
+
+	pch_pic_bitset(priv, PCH_PIC_MASK, d->hwirq);
+	irq_chip_mask_parent(d);
+}
+
+static void pch_pic_unmask_irq(struct irq_data *d)
+{
+	struct pch_pic *priv = irq_data_get_irq_chip_data(d);
+
+	pch_pic_bitclr(priv, PCH_PIC_MASK, d->hwirq);
+	irq_chip_unmask_parent(d);
+}
+
+static int pch_pic_set_type(struct irq_data *d, unsigned int type)
+{
+	struct pch_pic *priv = irq_data_get_irq_chip_data(d);
+	int ret = 0;
+
+	switch (type) {
+	case IRQ_TYPE_EDGE_RISING:
+		pch_pic_bitset(priv, PCH_PIC_EDGE, d->hwirq);
+		pch_pic_bitclr(priv, PCH_PIC_POL, d->hwirq);
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		pch_pic_bitset(priv, PCH_PIC_EDGE, d->hwirq);
+		pch_pic_bitset(priv, PCH_PIC_POL, d->hwirq);
+		break;
+	case IRQ_TYPE_LEVEL_HIGH:
+		pch_pic_bitclr(priv, PCH_PIC_EDGE, d->hwirq);
+		pch_pic_bitclr(priv, PCH_PIC_POL, d->hwirq);
+		break;
+	case IRQ_TYPE_LEVEL_LOW:
+		pch_pic_bitclr(priv, PCH_PIC_EDGE, d->hwirq);
+		pch_pic_bitset(priv, PCH_PIC_POL, d->hwirq);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static struct irq_chip pch_pic_irq_chip = {
+	.name			= "PCH PIC",
+	.irq_mask		= pch_pic_mask_irq,
+	.irq_unmask		= pch_pic_unmask_irq,
+	.irq_ack		= irq_chip_ack_parent,
+	.irq_eoi		= pch_pic_eoi_irq,
+	.irq_set_affinity	= irq_chip_set_affinity_parent,
+	.irq_set_type		= pch_pic_set_type,
+};
+
+static int pch_pic_alloc(struct irq_domain *domain, unsigned int virq,
+			      unsigned int nr_irqs, void *arg)
+{
+	struct pch_pic *priv = domain->host_data;
+	struct irq_fwspec fwspec;
+	unsigned long hwirq;
+	unsigned int type;
+	int err;
+
+	irq_domain_translate_twocell(domain, arg, &hwirq, &type);
+
+	fwspec.fwnode = domain->parent->fwnode;
+	fwspec.param_count = 1;
+	fwspec.param[0] = hwirq + priv->ht_vec_base;
+
+	err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
+	if (err)
+		return err;
+
+	irq_domain_set_info(domain, virq, hwirq,
+			    &pch_pic_irq_chip, priv,
+			    handle_fasteoi_ack_irq, NULL, NULL);
+	irq_set_probe(virq);
+
+	return 0;
+}
+
+static const struct irq_domain_ops pch_pic_domain_ops = {
+	.translate	= irq_domain_translate_twocell,
+	.alloc		= pch_pic_alloc,
+	.free		= irq_domain_free_irqs_parent,
+};
+
+static void pch_pic_reset(struct pch_pic *priv)
+{
+	int i;
+
+	for (i = 0; i < PIC_COUNT; i++) {
+		/* Write vectore 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));
+	}
+
+	for (i = 0; i < PIC_REG_COUNT; i++) {
+		/* Clear IRQ cause registers, mask all interrupts */
+		writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_MASK + 4 * i);
+		writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_CLR + 4 * i);
+		/* Clear auto bounce, we don't need that */
+		writel_relaxed(0, priv->base + PCH_PIC_AUTO0 + 4 * i);
+		writel_relaxed(0, priv->base + PCH_PIC_AUTO1 + 4 * i);
+		/* Enable HTMSI transformer */
+		writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_HTMSI_EN + 4 * i);
+	}
+}
+
+static int pch_pic_of_init(struct device_node *node,
+				struct device_node *parent)
+{
+	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;
+		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;
+	}
+
+	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->pic_domain = irq_domain_create_hierarchy(parent_domain, 0,
+						     PIC_COUNT,
+						     of_node_to_fwnode(node),
+						     &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);
+
+	return 0;
+
+iounmap_base:
+	iounmap(priv->base);
+free_priv:
+	kfree(priv);
+
+	return err;
+}
+
+IRQCHIP_DECLARE(pch_pic, "loongson,pch-pic-1.0", pch_pic_of_init);
-- 
2.26.0.rc2


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

* [PATCH v3 4/6] dt-bindings: interrupt-controller: Add Loongson PCH PIC
  2020-05-01  9:21 ` [PATCH v3 " Jiaxun Yang
  2020-05-01  9:21   ` [PATCH v3 2/6] dt-bindings: interrupt-controller: Add Loongson HTVEC Jiaxun Yang
  2020-05-01  9:21   ` [PATCH v3 3/6] irqchip: Add Loongson PCH PIC controller Jiaxun Yang
@ 2020-05-01  9:21   ` Jiaxun Yang
  2020-05-01  9:21   ` [PATCH v3 5/6] irqchip: Add Loongson PCH MSI controller Jiaxun Yang
                     ` (3 subsequent siblings)
  6 siblings, 0 replies; 41+ messages in thread
From: Jiaxun Yang @ 2020-05-01  9:21 UTC (permalink / raw)
  To: maz
  Cc: Jiaxun Yang, Thomas Gleixner, Jason Cooper, Rob Herring,
	Huacai Chen, linux-kernel, devicetree, linux-mips

Add binding for Loongson PCH PIC Controller.

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
--
v2:
	- Fix naming
	- Mark loongson,pic-base-vec as required
---
 .../loongson,pch-pic.yaml                     | 56 +++++++++++++++++++
 1 file changed, 56 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/loongson,pch-pic.yaml

diff --git a/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-pic.yaml b/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-pic.yaml
new file mode 100644
index 000000000000..92a8cd0ce23b
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-pic.yaml
@@ -0,0 +1,56 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/interrupt-controller/loongson,pch-pic.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Loongson PCH PIC Controller
+
+maintainers:
+  - Jiaxun Yang <jiaxun.yang@flygoat.com>
+
+allOf:
+  - $ref: /schemas/interrupt-controller.yaml#
+
+description: |
+  This interrupt controller is found in the Loongson LS7A family of PCH for
+  transforming interrupts from on-chip devices into HyperTransport vectorized
+  interrupts.
+
+properties:
+  compatible:
+    const: loongson,pch-pic-1.0
+
+  reg:
+    maxItems: 1
+
+  loongson,pic-base-vec:
+    $ref: '/schemas/types.yaml#/definitions/uint32'
+    description: |
+      u32 value of the base of parent HyperTransport vector allocated
+      to PCH PIC.
+
+  interrupt-controller: true
+
+  '#interrupt-cells':
+    const: 2
+
+required:
+  - compatible
+  - reg
+  - loongson,pic-base-vec
+  - interrupt-controller
+  - '#interrupt-cells'
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    pic: interrupt-controller@10000000 {
+      compatible = "loongson,pch-pic-1.0";
+      reg = <0x10000000 0x400>;
+      interrupt-controller;
+      #interrupt-cells = <2>;
+      loongson,pic-base-vec = <64>;
+      interrupt-parent = <&htvec>;
+    };
+...
-- 
2.26.0.rc2


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

* [PATCH v3 5/6] irqchip: Add Loongson PCH MSI controller
  2020-05-01  9:21 ` [PATCH v3 " Jiaxun Yang
                     ` (2 preceding siblings ...)
  2020-05-01  9:21   ` [PATCH v3 4/6] dt-bindings: interrupt-controller: Add Loongson PCH PIC Jiaxun Yang
@ 2020-05-01  9:21   ` Jiaxun Yang
  2020-05-01  9:21   ` [PATCH v3 6/6] dt-bindings: interrupt-controller: Add Loongson PCH MSI Jiaxun Yang
                     ` (2 subsequent siblings)
  6 siblings, 0 replies; 41+ messages in thread
From: Jiaxun Yang @ 2020-05-01  9:21 UTC (permalink / raw)
  To: maz
  Cc: Jiaxun Yang, Thomas Gleixner, Jason Cooper, Rob Herring,
	Huacai Chen, linux-kernel, devicetree, linux-mips

This controller appears on Loongson LS7A family of PCH to transform
interrupts from PCI MSI into HyperTransport vectorized interrrupts
and send them to procrssor's HT vector controller.

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
--
v2:
	- Style clean-ups
	- Add ack callback
	- Use bitmap_find_free_region
v3:
	- Style clean-ups
	- mutex lock instead of spin lock
	- correct bitmap usage
---
 drivers/irqchip/Kconfig                |  10 +
 drivers/irqchip/Makefile               |   1 +
 drivers/irqchip/irq-loongson-pch-msi.c | 255 +++++++++++++++++++++++++
 3 files changed, 266 insertions(+)
 create mode 100644 drivers/irqchip/irq-loongson-pch-msi.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 5524a621638c..0b6b826dd843 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -549,4 +549,14 @@ config LOONGSON_PCH_PIC
 	help
 	  Support for the Loongson PCH PIC Controller.
 
+config LOONGSON_PCH_MSI
+	bool "Loongson PCH PIC Controller"
+	depends on MACH_LOONGSON64 || COMPILE_TEST
+	depends on PCI
+	default MACH_LOONGSON64
+	select IRQ_DOMAIN_HIERARCHY
+	select PCI_MSI
+	help
+	  Support for the Loongson PCH MSI Controller.
+
 endmenu
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index acc72331cec8..3a4ce283189a 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -109,3 +109,4 @@ obj-$(CONFIG_LOONGSON_LIOINTC)		+= irq-loongson-liointc.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
+obj-$(CONFIG_LOONGSON_PCH_MSI)		+= irq-loongson-pch-msi.o
diff --git a/drivers/irqchip/irq-loongson-pch-msi.c b/drivers/irqchip/irq-loongson-pch-msi.c
new file mode 100644
index 000000000000..73e6124a95ac
--- /dev/null
+++ b/drivers/irqchip/irq-loongson-pch-msi.c
@@ -0,0 +1,255 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com>
+ *  Loongson PCH MSI support
+ */
+
+#define pr_fmt(fmt) "pch-msi: " fmt
+
+#include <linux/irqchip.h>
+#include <linux/msi.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_pci.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+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;
+};
+
+static void pch_msi_mask_msi_irq(struct irq_data *d)
+{
+	pci_msi_mask_irq(d);
+	irq_chip_mask_parent(d);
+}
+
+static void pch_msi_unmask_msi_irq(struct irq_data *d)
+{
+	pci_msi_unmask_irq(d);
+	irq_chip_unmask_parent(d);
+}
+
+static struct irq_chip pch_msi_irq_chip = {
+	.name			= "PCH PCI MSI",
+	.irq_mask		= pch_msi_mask_msi_irq,
+	.irq_unmask		= pch_msi_unmask_msi_irq,
+	.irq_ack		= irq_chip_ack_parent,
+	.irq_set_affinity	= irq_chip_set_affinity_parent,
+};
+
+static int pch_msi_allocate_hwirq(struct pch_msi_data *priv, int num_req)
+{
+	int first;
+
+	mutex_lock(&priv->msi_map_lock);
+
+	first = bitmap_find_free_region(priv->msi_map, priv->num_irqs,
+					get_count_order(num_req));
+	if (first < 0) {
+		mutex_unlock(&priv->msi_map_lock);
+		return -ENOSPC;
+	}
+
+	mutex_unlock(&priv->msi_map_lock);
+
+	return priv->irq_first + first;
+}
+
+static void pch_msi_free_hwirq(struct pch_msi_data *priv,
+				int hwirq, int num_req)
+{
+	int first = hwirq - priv->irq_first;
+
+	mutex_lock(&priv->msi_map_lock);
+	bitmap_release_region(priv->msi_map, first, get_count_order(num_req));
+	mutex_unlock(&priv->msi_map_lock);
+}
+
+static void pch_msi_compose_msi_msg(struct irq_data *data,
+					struct msi_msg *msg)
+{
+	struct pch_msi_data *priv = irq_data_get_irq_chip_data(data);
+
+	msg->address_hi = upper_32_bits(priv->doorbell);
+	msg->address_lo = lower_32_bits(priv->doorbell);
+	msg->data = data->hwirq;
+}
+
+static struct msi_domain_info pch_msi_domain_info = {
+	.flags		= MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+			  MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX,
+	.chip	= &pch_msi_irq_chip,
+};
+
+static struct irq_chip middle_irq_chip = {
+	.name			= "PCH MSI",
+	.irq_mask		= irq_chip_mask_parent,
+	.irq_unmask		= irq_chip_unmask_parent,
+	.irq_ack		= irq_chip_ack_parent,
+	.irq_set_affinity	= irq_chip_set_affinity_parent,
+	.irq_compose_msi_msg	= pch_msi_compose_msi_msg,
+};
+
+static int pch_msi_parent_domain_alloc(struct irq_domain *domain,
+					unsigned int virq, int hwirq)
+{
+	struct irq_fwspec fwspec;
+	int ret;
+
+	fwspec.fwnode = domain->parent->fwnode;
+	fwspec.param_count = 1;
+	fwspec.param[0] = hwirq;
+
+	ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int pch_msi_middle_domain_alloc(struct irq_domain *domain,
+					   unsigned int virq,
+					   unsigned int nr_irqs, void *args)
+{
+	struct pch_msi_data *priv = domain->host_data;
+	int hwirq, err, i;
+
+	hwirq = pch_msi_allocate_hwirq(priv, nr_irqs);
+	if (hwirq < 0)
+		return hwirq;
+
+	for (i = 0; i < nr_irqs; i++) {
+		err = pch_msi_parent_domain_alloc(domain, virq + i, hwirq + i);
+		if (err)
+			goto err_hwirq;
+
+		irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
+					      &middle_irq_chip, priv);
+	}
+
+	return 0;
+
+err_hwirq:
+	pch_msi_free_hwirq(priv, hwirq, nr_irqs);
+	irq_domain_free_irqs_parent(domain, virq, i - 1);
+
+	return err;
+}
+
+static void pch_msi_middle_domain_free(struct irq_domain *domain,
+					   unsigned int virq,
+					   unsigned int nr_irqs)
+{
+	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+	struct pch_msi_data *priv = irq_data_get_irq_chip_data(d);
+
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+	pch_msi_free_hwirq(priv, d->hwirq, nr_irqs);
+}
+
+static const struct irq_domain_ops pch_msi_middle_domain_ops = {
+	.alloc	= pch_msi_middle_domain_alloc,
+	.free	= pch_msi_middle_domain_free,
+};
+
+static int pch_msi_init_domains(struct pch_msi_data *priv,
+				struct device_node *node,
+				struct irq_domain *parent)
+{
+	struct irq_domain *middle_domain, *msi_domain;
+
+	middle_domain = irq_domain_create_linear(of_node_to_fwnode(node),
+						priv->num_irqs,
+						&pch_msi_middle_domain_ops,
+						priv);
+	if (!middle_domain) {
+		pr_err("Failed to create the MSI middle domain\n");
+		return -ENOMEM;
+	}
+
+	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),
+					       &pch_msi_domain_info,
+					       middle_domain);
+	if (!msi_domain) {
+		pr_err("Failed to create PCI MSI domain\n");
+		irq_domain_remove(middle_domain);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static int pch_msi_init(struct device_node *node,
+			    struct device_node *parent)
+{
+	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;
+	}
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	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->msi_map = bitmap_alloc(priv->num_irqs, GFP_KERNEL);
+	if (!priv->msi_map) {
+		ret = -ENOMEM;
+		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);
+	if (ret)
+		goto err_map;
+
+	return 0;
+
+err_map:
+	kfree(priv->msi_map);
+err_priv:
+	kfree(priv);
+	return ret;
+}
+
+IRQCHIP_DECLARE(pch_msi, "loongson,pch-msi-1.0", pch_msi_init);
-- 
2.26.0.rc2


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

* [PATCH v3 6/6] dt-bindings: interrupt-controller: Add Loongson PCH MSI
  2020-05-01  9:21 ` [PATCH v3 " Jiaxun Yang
                     ` (3 preceding siblings ...)
  2020-05-01  9:21   ` [PATCH v3 5/6] irqchip: Add Loongson PCH MSI controller Jiaxun Yang
@ 2020-05-01  9:21   ` Jiaxun Yang
  2020-05-12 20:57     ` Rob Herring
  2020-05-12  7:45   ` [PATCH v3 1/6] irqchip: Add Loongson HyperTransport Vector support Jiaxun Yang
  2020-05-13 12:06   ` Thomas Gleixner
  6 siblings, 1 reply; 41+ messages in thread
From: Jiaxun Yang @ 2020-05-01  9:21 UTC (permalink / raw)
  To: maz
  Cc: Jiaxun Yang, Thomas Gleixner, Jason Cooper, Rob Herring,
	Huacai Chen, linux-kernel, devicetree, linux-mips

Add binding for Loongson PCH MSI controller.

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---
 .../loongson,pch-msi.yaml                     | 56 +++++++++++++++++++
 1 file changed, 56 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml

diff --git a/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml b/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml
new file mode 100644
index 000000000000..513ed1933035
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml
@@ -0,0 +1,56 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/interrupt-controller/loongson,pch-msi.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Loongson PCH MSI Controller
+
+maintainers:
+  - Jiaxun Yang <jiaxun.yang@flygoat.com>
+
+description: |
+  This interrupt controller is found in the Loongson LS7A family of PCH for
+  transforming interrupts from PCIe MSI into HyperTransport vectorized
+  interrupts.
+
+properties:
+  compatible:
+    const: loongson,pch-msi-1.0
+
+  reg:
+    maxItems: 1
+
+  loongson,msi-base-vec:
+    $ref: '/schemas/types.yaml#/definitions/uint32'
+    description: |
+      u32 value of the base of parent HyperTransport vector allocated
+      to PCH MSI.
+
+  loongson,msi-num-vecs:
+    $ref: '/schemas/types.yaml#/definitions/uint32'
+    description: |
+      u32 value of the number of parent HyperTransport vectors allocated
+      to PCH MSI.
+
+  msi-controller: true
+
+required:
+  - compatible
+  - reg
+  - msi-controller
+  - loongson,msi-base-vec
+  - loongson,msi-num-vecs
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    msi: msi-controller@2ff00000 {
+      compatible = "loongson,pch-msi-1.0";
+      reg = <0x2ff00000 0x4>;
+      msi-controller;
+      loongson,msi-base-vec = <64>;
+      loongson,msi-num-vecs = <64>;
+      interrupt-parent = <&htvec>;
+    };
+...
-- 
2.26.0.rc2


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

* Re: [PATCH v3 1/6] irqchip: Add Loongson HyperTransport Vector support
  2020-05-01  9:21 ` [PATCH v3 " Jiaxun Yang
                     ` (4 preceding siblings ...)
  2020-05-01  9:21   ` [PATCH v3 6/6] dt-bindings: interrupt-controller: Add Loongson PCH MSI Jiaxun Yang
@ 2020-05-12  7:45   ` Jiaxun Yang
  2020-05-13 12:06   ` Thomas Gleixner
  6 siblings, 0 replies; 41+ messages in thread
From: Jiaxun Yang @ 2020-05-12  7:45 UTC (permalink / raw)
  To: maz
  Cc: Thomas Gleixner, Jason Cooper, Rob Herring, Huacai Chen,
	linux-kernel, devicetree, linux-mips

On Fri,  1 May 2020 17:21:32 +0800
Jiaxun Yang <jiaxun.yang@flygoat.com> wrote:

> This controller appears on Loongson-3 chips for receiving interrupt
> vectors from PCH's PIC and PCH's PCIe MSI interrupts.
> 
> Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
> ---
> v2:
> 	- Style cleanup
> 	- Set ack callback and set correct edge_irq handler
> 
> v3:
> 	- Correct bitops in ACK callback

Any update about v3?

Thanks.

[...]
--
Jiaxun Yang

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

* Re: [PATCH v3 2/6] dt-bindings: interrupt-controller: Add Loongson HTVEC
  2020-05-01  9:21   ` [PATCH v3 2/6] dt-bindings: interrupt-controller: Add Loongson HTVEC Jiaxun Yang
@ 2020-05-12 16:42     ` Rob Herring
  0 siblings, 0 replies; 41+ messages in thread
From: Rob Herring @ 2020-05-12 16:42 UTC (permalink / raw)
  To: Jiaxun Yang
  Cc: maz, Thomas Gleixner, Jason Cooper, Huacai Chen, linux-kernel,
	devicetree, linux-mips

On Fri, May 01, 2020 at 05:21:33PM +0800, Jiaxun Yang wrote:
> Add binding for Loongson-3 HyperTransport Interrupt Vector Controller.
> 
> Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
> ---
>  .../interrupt-controller/loongson,htvec.yaml  | 59 +++++++++++++++++++
>  1 file changed, 59 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/loongson,htvec.yaml
> 
> diff --git a/Documentation/devicetree/bindings/interrupt-controller/loongson,htvec.yaml b/Documentation/devicetree/bindings/interrupt-controller/loongson,htvec.yaml
> new file mode 100644
> index 000000000000..547a80c89eba
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/interrupt-controller/loongson,htvec.yaml
> @@ -0,0 +1,59 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: "http://devicetree.org/schemas/interrupt-controller/loongson,htvec.yaml#"
> +$schema: "http://devicetree.org/meta-schemas/core.yaml#"
> +
> +title: Loongson-3 HyperTransport Interrupt Vector Controller
> +
> +maintainers:
> +  - Jiaxun Yang <jiaxun.yang@flygoat.com>
> +
> +allOf:
> +  - $ref: /schemas/interrupt-controller.yaml#

Don't need this. It's already applied to any node named 
'interrupt-controller'.

> +
> +description: |

Can drop '|' if you don't need formatting.

> +  This interrupt controller is found in the Loongson-3 family of chips for
> +  receiving vectorized interrupts from PCH's interrupt controller.
> +
> +properties:
> +  compatible:
> +    const: loongson,htvec-1.0
> +
> +  reg:
> +    maxItems: 1
> +
> +  interrupts:
> +    minItems: 1
> +    maxItems: 4
> +    description: |
> +      Four parent interrupts that receive chained interrupts.
> +
> +  interrupt-controller: true
> +
> +  '#interrupt-cells':
> +    const: 1
> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupts
> +  - interrupt-controller
> +  - '#interrupt-cells'

Add:

additionalProperties: false

> +
> +examples:
> +  - |
> +    #include <dt-bindings/interrupt-controller/irq.h>
> +    htvec: interrupt-controller@1fb000080 {

Unit-address doesn't match reg.

> +      compatible = "loongson,htvec-1.0";
> +      reg = <0xfb000080 0x40>;
> +      interrupt-controller;
> +      #interrupt-cells = <1>;
> +
> +      interrupt-parent = <&liointc>;
> +      interrupts = <24 IRQ_TYPE_LEVEL_HIGH>,
> +                    <25 IRQ_TYPE_LEVEL_HIGH>,
> +                    <26 IRQ_TYPE_LEVEL_HIGH>,
> +                    <27 IRQ_TYPE_LEVEL_HIGH>;
> +    };
> +...
> -- 
> 2.26.0.rc2
> 

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

* Re: [PATCH v3 6/6] dt-bindings: interrupt-controller: Add Loongson PCH MSI
  2020-05-01  9:21   ` [PATCH v3 6/6] dt-bindings: interrupt-controller: Add Loongson PCH MSI Jiaxun Yang
@ 2020-05-12 20:57     ` Rob Herring
  0 siblings, 0 replies; 41+ messages in thread
From: Rob Herring @ 2020-05-12 20:57 UTC (permalink / raw)
  To: Jiaxun Yang
  Cc: Jason Cooper, linux-mips, maz, devicetree, Thomas Gleixner,
	linux-kernel, Huacai Chen, Rob Herring

On Fri,  1 May 2020 17:21:37 +0800, Jiaxun Yang wrote:
> Add binding for Loongson PCH MSI controller.
> 
> Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
> ---
>  .../loongson,pch-msi.yaml                     | 56 +++++++++++++++++++
>  1 file changed, 56 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml
> 

Reviewed-by: Rob Herring <robh@kernel.org>

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

* Re: [PATCH v3 1/6] irqchip: Add Loongson HyperTransport Vector support
  2020-05-01  9:21 ` [PATCH v3 " Jiaxun Yang
                     ` (5 preceding siblings ...)
  2020-05-12  7:45   ` [PATCH v3 1/6] irqchip: Add Loongson HyperTransport Vector support Jiaxun Yang
@ 2020-05-13 12:06   ` Thomas Gleixner
  6 siblings, 0 replies; 41+ messages in thread
From: Thomas Gleixner @ 2020-05-13 12:06 UTC (permalink / raw)
  To: Jiaxun Yang, maz
  Cc: Jiaxun Yang, Jason Cooper, Rob Herring, Huacai Chen,
	linux-kernel, devicetree, linux-mips

Jiaxun Yang <jiaxun.yang@flygoat.com> writes:
> +static void htvec_mask_irq(struct irq_data *d)
> +{
> +	struct htvec *priv = irq_data_get_irq_chip_data(d);
> +	void __iomem *addr = priv->base + HTVEC_EN_OFF;
> +	unsigned long flags;
> +	u32 reg;
> +
> +	raw_spin_lock_irqsave(&priv->htvec_lock, flags);

No need for irqsave() these functions are called with interrupts disabled.

> +	addr += VEC_REG_IDX(d->hwirq) * 4;
> +	reg = readl(addr);
> +	reg &= ~BIT(VEC_REG_BIT(d->hwirq));
> +	writel(reg, addr);
> +	raw_spin_unlock_irqrestore(&priv->htvec_lock, flags);
> +}

> +static int htvec_domain_alloc(struct irq_domain *domain, unsigned int virq,
> +				  unsigned int nr_irqs, void *arg)
> +{
> +	struct htvec *priv = domain->host_data;
> +	unsigned long hwirq;
> +	unsigned int type, i;
> +
> +	irq_domain_translate_onecell(domain, arg, &hwirq, &type);
> +
> +	for (i = 0; i < nr_irqs; i++)
> +		irq_domain_set_info(domain, virq + i, hwirq + i, &htvec_irq_chip,
> +					priv, handle_edge_irq, NULL, NULL);

This wants curly brackets and the second line of arguments wants to be
aligned with the first argument:

	for (i = 0; i < nr_irqs; i++) {
		irq_domain_set_info(domain, virq + i, hwirq + i, &htvec_irq_chip,
				    priv, handle_edge_irq, NULL, NULL);
	}

See https://lore.kernel.org/lkml/alpine.DEB.2.20.1701171956290.3645@nanos/

The alignment of arguments wants to be fixed all over the place.

> +static int htvec_of_init(struct device_node *node,
> +				struct device_node *parent)
> +{
> +	struct htvec *priv;
> +	int err, parent_irq[4], num_parents = 0, i;

Please order the variable declaration in reverse fir tree length order:

	int err, parent_irq[4], num_parents = 0, i;
	struct htvec *priv;

That's way better readable than the above. All over the place please.

> +	priv->htvec_domain = irq_domain_create_linear(of_node_to_fwnode(node),
> +						   VEC_COUNT,
> +						   &htvec_domain_ops,
> +						   priv);
> +	if (!priv->htvec_domain) {
> +		pr_err("Failed to create IRQ domain\n");
> +		err = -ENOMEM;
> +		goto iounmap_base;
> +	}
> +
> +	htvec_reset(priv);
> +
> +	for (i = 0; i < num_parents; i++)
> +		irq_set_chained_handler_and_data(parent_irq[i],
> +						htvec_irq_dispatch, priv);

See above.

Thanks,

        tglx

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

* Re: [PATCH v2 3/6] irqchip: Add Loongson PCH PIC controller
  2020-04-28  6:32   ` [PATCH v2 3/6] irqchip: Add Loongson PCH PIC controller Jiaxun Yang
@ 2020-05-13 12:09     ` Thomas Gleixner
  0 siblings, 0 replies; 41+ messages in thread
From: Thomas Gleixner @ 2020-05-13 12:09 UTC (permalink / raw)
  To: Jiaxun Yang, maz
  Cc: Jiaxun Yang, Jason Cooper, Rob Herring, Huacai Chen,
	linux-kernel, devicetree, linux-mips

Jiaxun Yang <jiaxun.yang@flygoat.com> writes:
> +static void pch_pic_bitset(struct pch_pic *priv, int offset, int bit)
> +{
> +	void __iomem *addr = priv->base + offset + PIC_REG_IDX(bit) * 4;
> +	unsigned long flags;
> +	u32 reg;
> +
> +	raw_spin_lock_irqsave(&priv->pic_lock, flags);

See other reply.

> +	reg = readl(addr);
> +	reg |= BIT(PIC_REG_BIT(bit));
> +	writel(reg, addr);
> +	raw_spin_unlock_irqrestore(&priv->pic_lock, flags);
> +}
> +static int pch_pic_of_init(struct device_node *node,
> +				struct device_node *parent)
> +{
> +	struct pch_pic *priv;
> +	struct irq_domain *parent_domain;
> +	int err;

ordering

Thanks,

        tglx

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

* Re: [PATCH v2 5/6] irqchip: Add Loongson PCH MSI controller
  2020-04-28  6:32   ` [PATCH v2 5/6] irqchip: Add Loongson PCH MSI controller Jiaxun Yang
  2020-04-28 13:07     ` Marc Zyngier
@ 2020-05-13 12:13     ` Thomas Gleixner
  2020-05-13 12:15       ` Thomas Gleixner
  1 sibling, 1 reply; 41+ messages in thread
From: Thomas Gleixner @ 2020-05-13 12:13 UTC (permalink / raw)
  To: Jiaxun Yang, maz
  Cc: Jiaxun Yang, Jason Cooper, Rob Herring, Huacai Chen,
	linux-kernel, devicetree, linux-mips

Jiaxun Yang <jiaxun.yang@flygoat.com> writes:
> +
> +struct pch_msi_data {
> +	spinlock_t		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;
> +};
> +
> +static void pch_msi_mask_msi_irq(struct irq_data *d)
> +{
> +	pci_msi_mask_irq(d);
> +	irq_chip_mask_parent(d);
> +}
> +
> +static void pch_msi_unmask_msi_irq(struct irq_data *d)
> +{
> +	pci_msi_unmask_irq(d);
> +	irq_chip_unmask_parent(d);

The ordering of mask and unmask is assymetric. That does not make sense.

> +static struct msi_domain_info pch_msi_domain_info = {
> +	.flags		= MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
> +			  MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX,
> +	.chip	= &pch_msi_irq_chip,

Please maintain tabular layout.

Thanks,

        tglx

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

* Re: [PATCH v2 5/6] irqchip: Add Loongson PCH MSI controller
  2020-05-13 12:13     ` Thomas Gleixner
@ 2020-05-13 12:15       ` Thomas Gleixner
  2020-05-20 11:51         ` Jiaxun Yang
  0 siblings, 1 reply; 41+ messages in thread
From: Thomas Gleixner @ 2020-05-13 12:15 UTC (permalink / raw)
  To: Jiaxun Yang, maz
  Cc: Jiaxun Yang, Jason Cooper, Rob Herring, Huacai Chen,
	linux-kernel, devicetree, linux-mips

Thomas Gleixner <tglx@linutronix.de> writes:
> Jiaxun Yang <jiaxun.yang@flygoat.com> writes:
>> +
>> +struct pch_msi_data {
>> +	spinlock_t		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;
>> +};
>> +
>> +static void pch_msi_mask_msi_irq(struct irq_data *d)
>> +{
>> +	pci_msi_mask_irq(d);
>> +	irq_chip_mask_parent(d);
>> +}
>> +
>> +static void pch_msi_unmask_msi_irq(struct irq_data *d)
>> +{
>> +	pci_msi_unmask_irq(d);
>> +	irq_chip_unmask_parent(d);
>
> The ordering of mask and unmask is assymetric. That does not make sense.
>
>> +static struct msi_domain_info pch_msi_domain_info = {
>> +	.flags		= MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
>> +			  MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX,
>> +	.chip	= &pch_msi_irq_chip,
>
> Please maintain tabular layout.

Ooops. Wanted to reply to V3, but the comments are valid for V3 as well.

Thanks,

        tglx

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

* Re: [PATCH v2 5/6] irqchip: Add Loongson PCH MSI controller
  2020-05-13 12:15       ` Thomas Gleixner
@ 2020-05-20 11:51         ` Jiaxun Yang
  0 siblings, 0 replies; 41+ messages in thread
From: Jiaxun Yang @ 2020-05-20 11:51 UTC (permalink / raw)
  To: Thomas Gleixner, maz
  Cc: Jason Cooper, Rob Herring, Huacai Chen, linux-kernel, devicetree,
	linux-mips



于 2020年5月13日 GMT+08:00 下午8:15:40, Thomas Gleixner <tglx@linutronix.de> 写到:
>Thomas Gleixner <tglx@linutronix.de> writes:
>> Jiaxun Yang <jiaxun.yang@flygoat.com> writes:
>>> +
>>> +struct pch_msi_data {
>>> +	spinlock_t		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;
>>> +};
>>> +
>>> +static void pch_msi_mask_msi_irq(struct irq_data *d)
>>> +{
>>> +	pci_msi_mask_irq(d);
>>> +	irq_chip_mask_parent(d);
>>> +}
>>> +
>>> +static void pch_msi_unmask_msi_irq(struct irq_data *d)
>>> +{
>>> +	pci_msi_unmask_irq(d);
>>> +	irq_chip_unmask_parent(d);
>>
>> The ordering of mask and unmask is assymetric. That does not make sense.
>>
>>> +static struct msi_domain_info pch_msi_domain_info = {
>>> +	.flags		= MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
>>> +			  MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX,
>>> +	.chip	= &pch_msi_irq_chip,
>>
>> Please maintain tabular layout.
>
>Ooops. Wanted to reply to V3, but the comments are valid for V3 as well.

All fixed in v4.

Please review.

Thanks!


>
>Thanks,
>
>        tglx

-- 
Jiaxun Yang

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

end of thread, other threads:[~2020-05-20 11:52 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-04-22 14:24 [PATCH 0/6] Loongson PCH IRQ Support Jiaxun Yang
2020-04-22 14:24 ` [PATCH 1/6] irqchip: Add Loongson HyperTransport Vector support Jiaxun Yang
2020-04-23 13:31   ` Marc Zyngier
2020-04-22 14:24 ` [PATCH 2/6] dt-bindings: interrupt-controller: Add Loongson HTVEC Jiaxun Yang
2020-04-22 14:24 ` [PATCH 3/6] irqchip: Add Loongson PCH PIC controller Jiaxun Yang
2020-04-23  5:56   ` Huacai Chen
2020-04-23 14:23   ` Marc Zyngier
2020-04-22 14:24 ` [PATCH 4/6] dt-bindings: interrupt-controller: Add Loongson PCH PIC Jiaxun Yang
2020-04-23  5:54   ` Huacai Chen
2020-04-22 14:24 ` [PATCH 5/6] irqchip: Add Loongson PCH MSI controller Jiaxun Yang
2020-04-23  5:57   ` Huacai Chen
2020-04-23 14:41   ` Marc Zyngier
2020-04-24  1:33     ` Jiaxun Yang
2020-04-24  8:28       ` Marc Zyngier
2020-04-22 14:24 ` [PATCH 6/6] dt-bindings: interrupt-controller: Add Loongson PCH MSI Jiaxun Yang
2020-04-23  5:55   ` Huacai Chen
2020-04-23 12:43     ` Marc Zyngier
2020-04-24  1:27       ` Huacai Chen
2020-04-23  5:50 ` [PATCH 0/6] Loongson PCH IRQ Support Huacai Chen
2020-04-28  6:32 ` [PATCH v2 1/6] irqchip: Add Loongson HyperTransport Vector support Jiaxun Yang
2020-04-28  6:32   ` [PATCH v2 2/6] dt-bindings: interrupt-controller: Add Loongson HTVEC Jiaxun Yang
2020-04-28  6:32   ` [PATCH v2 3/6] irqchip: Add Loongson PCH PIC controller Jiaxun Yang
2020-05-13 12:09     ` Thomas Gleixner
2020-04-28  6:32   ` [PATCH v2 4/6] dt-bindings: interrupt-controller: Add Loongson PCH PIC Jiaxun Yang
2020-04-28  6:32   ` [PATCH v2 5/6] irqchip: Add Loongson PCH MSI controller Jiaxun Yang
2020-04-28 13:07     ` Marc Zyngier
2020-05-13 12:13     ` Thomas Gleixner
2020-05-13 12:15       ` Thomas Gleixner
2020-05-20 11:51         ` Jiaxun Yang
2020-04-28  6:32   ` [PATCH v2 6/6] dt-bindings: interrupt-controller: Add Loongson PCH MSI Jiaxun Yang
2020-04-28 16:59   ` [PATCH v2 1/6] irqchip: Add Loongson HyperTransport Vector support Marc Zyngier
2020-05-01  9:21 ` [PATCH v3 " Jiaxun Yang
2020-05-01  9:21   ` [PATCH v3 2/6] dt-bindings: interrupt-controller: Add Loongson HTVEC Jiaxun Yang
2020-05-12 16:42     ` Rob Herring
2020-05-01  9:21   ` [PATCH v3 3/6] irqchip: Add Loongson PCH PIC controller Jiaxun Yang
2020-05-01  9:21   ` [PATCH v3 4/6] dt-bindings: interrupt-controller: Add Loongson PCH PIC Jiaxun Yang
2020-05-01  9:21   ` [PATCH v3 5/6] irqchip: Add Loongson PCH MSI controller Jiaxun Yang
2020-05-01  9:21   ` [PATCH v3 6/6] dt-bindings: interrupt-controller: Add Loongson PCH MSI Jiaxun Yang
2020-05-12 20:57     ` Rob Herring
2020-05-12  7:45   ` [PATCH v3 1/6] irqchip: Add Loongson HyperTransport Vector support Jiaxun Yang
2020-05-13 12:06   ` Thomas Gleixner

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).