linux-mips.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/2] irqchip: Add driver for Loongson-1 interrupt controller
@ 2019-01-22 15:45 Jiaxun Yang
  2019-01-22 15:45 ` [PATCH 2/2] dt-bindings: interrupt-controller: loongson ls1x intc Jiaxun Yang
                   ` (5 more replies)
  0 siblings, 6 replies; 24+ messages in thread
From: Jiaxun Yang @ 2019-01-22 15:45 UTC (permalink / raw)
  To: linux-mips
  Cc: linux-kernel, paul.burton, devicetree, tglx, robh+dt, Jiaxun Yang

This controller appeared on Loongson-1 family MCUs
including Loongson-1B and Loongson-1C.

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---
 drivers/irqchip/Kconfig    |   9 ++
 drivers/irqchip/Makefile   |   1 +
 drivers/irqchip/irq-ls1x.c | 194 +++++++++++++++++++++++++++++++++++++
 3 files changed, 204 insertions(+)
 create mode 100644 drivers/irqchip/irq-ls1x.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 3d1e60779078..5dcb5456cd14 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -406,6 +406,15 @@ config IMX_IRQSTEER
 	help
 	  Support for the i.MX IRQSTEER interrupt multiplexer/remapper.
 
+config LS1X_IRQ
+	bool "Loongson-1 Interrupt Controller"
+	depends on MACH_LOONGSON32
+	default y
+	select IRQ_DOMAIN
+	select GENERIC_IRQ_CHIP
+	help
+	  Support for the Loongson-1 platform Interrupt Controller.
+
 endmenu
 
 config SIFIVE_PLIC
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index c93713d24b86..7acd0e36d0b4 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -94,3 +94,4 @@ obj-$(CONFIG_CSKY_APB_INTC)		+= irq-csky-apb-intc.o
 obj-$(CONFIG_SIFIVE_PLIC)		+= irq-sifive-plic.o
 obj-$(CONFIG_IMX_IRQSTEER)		+= irq-imx-irqsteer.o
 obj-$(CONFIG_MADERA_IRQ)		+= irq-madera.o
+obj-$(CONFIG_LS1X_IRQ)			+= irq-ls1x.o
diff --git a/drivers/irqchip/irq-ls1x.c b/drivers/irqchip/irq-ls1x.c
new file mode 100644
index 000000000000..f8ca738ed25c
--- /dev/null
+++ b/drivers/irqchip/irq-ls1x.c
@@ -0,0 +1,194 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *  Copyright (C) 2019, Jiaxun Yang <jiaxun.yang@flygoat.com>
+ *  Loongson-1 platform IRQ support
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/irqchip.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <asm/io.h>
+
+#include <asm/mach-loongson32/irq.h>
+
+struct ls_intc_data {
+	void __iomem *base;
+	unsigned int chip;
+};
+
+#define MAX_CHIPS	5
+#define LS_REG_INTC_STATUS	0x00
+#define LS_REG_INTC_EN	0x04
+#define LS_REG_INTC_SET	0x08
+#define LS_REG_INTC_CLR	0x0c
+#define LS_REG_INTC_POL 0x10
+#define LS_REG_INTC_EDGE 0x14
+#define CHIP_SIZE		0x18
+
+static irqreturn_t intc_cascade(int irq, void *data)
+{
+	struct ls_intc_data *intc = irq_get_handler_data(irq);
+	uint32_t irq_reg;
+
+	irq_reg = readl(intc->base + (CHIP_SIZE * intc->chip)
+					+ LS_REG_INTC_STATUS);
+	generic_handle_irq(__fls(irq_reg) + (intc->chip * 32) + LS1X_IRQ_BASE);
+
+	return IRQ_HANDLED;
+}
+
+static void ls_intc_set_bit(
+	struct irq_chip_generic *gc, unsigned int offset,
+	u32 mask, bool set)
+{
+	if (set)
+		writel(readl(gc->reg_base + offset) |
+		mask, gc->reg_base + offset);
+	else
+		writel(readl(gc->reg_base + offset) &
+		~mask, gc->reg_base + offset);
+}
+
+static int ls_intc_set_type(struct irq_data *data, unsigned int type)
+{
+	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
+	u32 mask = data->mask;
+
+	switch (type) {
+	case IRQ_TYPE_LEVEL_HIGH:
+		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, false);
+		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, true);
+		break;
+	case IRQ_TYPE_LEVEL_LOW:
+		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, false);
+		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, false);
+		break;
+	case IRQ_TYPE_EDGE_RISING:
+		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, true);
+		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, true);
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, true);
+		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, false);
+		break;
+	case IRQ_TYPE_NONE:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
+static struct irqaction intc_cascade_action = {
+	.handler = intc_cascade,
+	.name = "intc cascade interrupt",
+};
+
+static int __init ls_intc_of_init(struct device_node *node,
+				       unsigned int num_chips)
+{
+	struct ls_intc_data *intc[MAX_CHIPS];
+	struct irq_chip_generic *gc;
+	struct irq_chip_type *ct;
+	struct irq_domain *domain;
+	void __iomem *base;
+	int parent_irq[MAX_CHIPS], err = 0;
+	unsigned int i = 0;
+
+	base = of_iomap(node, 0);
+	if (!base)
+		return -ENODEV;
+
+	for (i = 0; i < num_chips; i++) {
+		/* Mask all irqs */
+		writel(0x0, base + (i * CHIP_SIZE) +
+		       LS_REG_INTC_EN);
+
+		/* Set all irqs to high level triggered */
+		writel(0xffffffff, base + (i * CHIP_SIZE) +
+		       LS_REG_INTC_POL);
+
+		intc[i] = kzalloc(sizeof(*intc[i]), GFP_KERNEL);
+		if (!intc[i]) {
+			err = -ENOMEM;
+			goto out_err;
+		}
+
+		intc[i]->base = base;
+		intc[i]->chip = i;
+
+		parent_irq[i] = irq_of_parse_and_map(node, i);
+		if (!parent_irq[i]) {
+			pr_warn("unable to get parent irq for irqchip %u", i);
+			kfree(intc[i]);
+			intc[i] = NULL;
+			err = -EINVAL;
+			goto out_err;
+		}
+
+		err = irq_set_handler_data(parent_irq[i], intc[i]);
+		if (err)
+			goto out_err;
+
+		gc = irq_alloc_generic_chip("INTC", 1,
+					    LS1X_IRQ_BASE + (i * 32),
+					    base + (i * CHIP_SIZE),
+					    handle_level_irq);
+
+		ct = gc->chip_types;
+		ct->regs.mask = LS_REG_INTC_EN;
+		ct->regs.ack = LS_REG_INTC_CLR;
+		ct->chip.irq_unmask = irq_gc_mask_set_bit;
+		ct->chip.irq_mask = irq_gc_mask_clr_bit;
+		ct->chip.irq_ack = irq_gc_ack_set_bit;
+		ct->chip.irq_set_type = ls_intc_set_type;
+
+		irq_setup_generic_chip(gc, IRQ_MSK(32), 0, 0,
+				       IRQ_NOPROBE | IRQ_LEVEL);
+	}
+
+	domain = irq_domain_add_legacy(node, num_chips * 32, LS1X_IRQ_BASE, 0,
+				       &irq_domain_simple_ops, NULL);
+	if (!domain) {
+		pr_warn("unable to register IRQ domain\n");
+		err = -EINVAL;
+		goto out_err;
+	}
+
+	for (i = 0; i < num_chips; i++)
+		setup_irq(parent_irq[i], &intc_cascade_action);
+
+	return 0;
+
+out_err:
+	for (i = 0; i < MAX_CHIPS; i++) {
+		if (intc[i]) {
+			kfree(intc[i]);
+			irq_dispose_mapping(parent_irq[i]);
+		} else {
+			break;
+		}
+	}
+	return err;
+}
+
+static int __init intc_4chip_of_init(struct device_node *node,
+				     struct device_node *parent)
+{
+	return ls_intc_of_init(node, 4);
+}
+IRQCHIP_DECLARE(ls1b_intc, "loongson,ls1b-intc", intc_4chip_of_init);
+
+static int __init intc_5chip_of_init(struct device_node *node,
+	struct device_node *parent)
+{
+	return ls_intc_of_init(node, 5);
+}
+IRQCHIP_DECLARE(ls1c_intc, "loongson,ls1c-intc", intc_5chip_of_init);
-- 
2.20.1


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

* [PATCH 2/2] dt-bindings: interrupt-controller: loongson ls1x intc
  2019-01-22 15:45 [PATCH 1/2] irqchip: Add driver for Loongson-1 interrupt controller Jiaxun Yang
@ 2019-01-22 15:45 ` Jiaxun Yang
  2019-01-23  6:23 ` irqchip: Add driver for Loongson-1 intc v2 Jiaxun Yang
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 24+ messages in thread
From: Jiaxun Yang @ 2019-01-22 15:45 UTC (permalink / raw)
  To: linux-mips
  Cc: linux-kernel, paul.burton, devicetree, tglx, robh+dt, Jiaxun Yang

Dt-bindings doc about Loongson-1 interrupt controller

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

diff --git a/Documentation/devicetree/bindings/interrupt-controller/loongson,ls1x-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/loongson,ls1x-intc.txt
new file mode 100644
index 000000000000..afa8fec45f88
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/loongson,ls1x-intc.txt
@@ -0,0 +1,28 @@
+Loongson ls1x Interrupt Controller
+
+Required properties:
+
+- compatible : should be "ingenic,<socname>-intc". Valid strings are:
+    loongson,ls1b-intc
+    loongson,ls1c-intc
+
+- reg : Specifies base physical address and size of the registers.
+- interrupt-controller : Identifies the node as an interrupt controller
+- #interrupt-cells : Specifies the number of cells needed to encode an
+  interrupt source. The value shall be 1.
+- interrupts : Specifies the CPU interrupts the controller is connected to,
+    - For ls1b, it must have 4 interrupts.
+    - For ls1c, it must have 5 interrupts.
+
+Example:
+
+intc: interrupt-controller@1fd01040 {
+	compatible = "loongson,ls1c-intc";
+	reg = <0x1fd01040 0x78>;
+
+	interrupt-controller;
+	#interrupt-cells = <1>;
+
+	interrupt-parent = <&cpu_intc>;
+	interrupts = <2>, <3>, <4>, <5>, <6>;
+};
\ No newline at end of file
-- 
2.20.1


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

* irqchip: Add driver for Loongson-1 intc v2
  2019-01-22 15:45 [PATCH 1/2] irqchip: Add driver for Loongson-1 interrupt controller Jiaxun Yang
  2019-01-22 15:45 ` [PATCH 2/2] dt-bindings: interrupt-controller: loongson ls1x intc Jiaxun Yang
@ 2019-01-23  6:23 ` Jiaxun Yang
  2019-01-23  6:23   ` [PATCH v2 2/2] dt-bindings: interrupt-controller: loongson ls1x intc Jiaxun Yang
  2019-01-23  6:23   ` [PATCH v2 1/2] irqchip: Add driver for Loongson-1 interrupt controller Jiaxun Yang
  2019-01-24  3:27 ` irqchip: Add driver for Loongson-1 intc v3 Jiaxun Yang
                   ` (3 subsequent siblings)
  5 siblings, 2 replies; 24+ messages in thread
From: Jiaxun Yang @ 2019-01-23  6:23 UTC (permalink / raw)
  To: linux-mips; +Cc: Thomas Gleixner, Jason Cooper, Marc Zyngier, linux-kernel

v1->v2: Fix SPDX-License-Identifier



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

* [PATCH v2 2/2] dt-bindings: interrupt-controller: loongson ls1x intc
  2019-01-23  6:23 ` irqchip: Add driver for Loongson-1 intc v2 Jiaxun Yang
@ 2019-01-23  6:23   ` Jiaxun Yang
  2019-01-23  6:23   ` [PATCH v2 1/2] irqchip: Add driver for Loongson-1 interrupt controller Jiaxun Yang
  1 sibling, 0 replies; 24+ messages in thread
From: Jiaxun Yang @ 2019-01-23  6:23 UTC (permalink / raw)
  To: linux-mips
  Cc: Jiaxun Yang, Thomas Gleixner, Jason Cooper, Marc Zyngier,
	Rob Herring, Mark Rutland, linux-kernel, devicetree

Dt-bindings doc about Loongson-1 interrupt controller

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

diff --git a/Documentation/devicetree/bindings/interrupt-controller/loongson,ls1x-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/loongson,ls1x-intc.txt
new file mode 100644
index 000000000000..afa8fec45f88
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/loongson,ls1x-intc.txt
@@ -0,0 +1,28 @@
+Loongson ls1x Interrupt Controller
+
+Required properties:
+
+- compatible : should be "ingenic,<socname>-intc". Valid strings are:
+    loongson,ls1b-intc
+    loongson,ls1c-intc
+
+- reg : Specifies base physical address and size of the registers.
+- interrupt-controller : Identifies the node as an interrupt controller
+- #interrupt-cells : Specifies the number of cells needed to encode an
+  interrupt source. The value shall be 1.
+- interrupts : Specifies the CPU interrupts the controller is connected to,
+    - For ls1b, it must have 4 interrupts.
+    - For ls1c, it must have 5 interrupts.
+
+Example:
+
+intc: interrupt-controller@1fd01040 {
+	compatible = "loongson,ls1c-intc";
+	reg = <0x1fd01040 0x78>;
+
+	interrupt-controller;
+	#interrupt-cells = <1>;
+
+	interrupt-parent = <&cpu_intc>;
+	interrupts = <2>, <3>, <4>, <5>, <6>;
+};
\ No newline at end of file
-- 
2.20.1


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

* [PATCH v2 1/2] irqchip: Add driver for Loongson-1 interrupt controller
  2019-01-23  6:23 ` irqchip: Add driver for Loongson-1 intc v2 Jiaxun Yang
  2019-01-23  6:23   ` [PATCH v2 2/2] dt-bindings: interrupt-controller: loongson ls1x intc Jiaxun Yang
@ 2019-01-23  6:23   ` Jiaxun Yang
  2019-01-23  8:36     ` Marc Zyngier
  1 sibling, 1 reply; 24+ messages in thread
From: Jiaxun Yang @ 2019-01-23  6:23 UTC (permalink / raw)
  To: linux-mips
  Cc: Jiaxun Yang, Thomas Gleixner, Jason Cooper, Marc Zyngier, linux-kernel

This controller appeared on Loongson-1 family MCUs
including Loongson-1B and Loongson-1C.

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---
 drivers/irqchip/Kconfig    |   9 ++
 drivers/irqchip/Makefile   |   1 +
 drivers/irqchip/irq-ls1x.c | 194 +++++++++++++++++++++++++++++++++++++
 3 files changed, 204 insertions(+)
 create mode 100644 drivers/irqchip/irq-ls1x.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 3d1e60779078..5dcb5456cd14 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -406,6 +406,15 @@ config IMX_IRQSTEER
 	help
 	  Support for the i.MX IRQSTEER interrupt multiplexer/remapper.
 
+config LS1X_IRQ
+	bool "Loongson-1 Interrupt Controller"
+	depends on MACH_LOONGSON32
+	default y
+	select IRQ_DOMAIN
+	select GENERIC_IRQ_CHIP
+	help
+	  Support for the Loongson-1 platform Interrupt Controller.
+
 endmenu
 
 config SIFIVE_PLIC
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index c93713d24b86..7acd0e36d0b4 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -94,3 +94,4 @@ obj-$(CONFIG_CSKY_APB_INTC)		+= irq-csky-apb-intc.o
 obj-$(CONFIG_SIFIVE_PLIC)		+= irq-sifive-plic.o
 obj-$(CONFIG_IMX_IRQSTEER)		+= irq-imx-irqsteer.o
 obj-$(CONFIG_MADERA_IRQ)		+= irq-madera.o
+obj-$(CONFIG_LS1X_IRQ)			+= irq-ls1x.o
diff --git a/drivers/irqchip/irq-ls1x.c b/drivers/irqchip/irq-ls1x.c
new file mode 100644
index 000000000000..b15b01237830
--- /dev/null
+++ b/drivers/irqchip/irq-ls1x.c
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Copyright (C) 2019, Jiaxun Yang <jiaxun.yang@flygoat.com>
+ *  Loongson-1 platform IRQ support
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/irqchip.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/io.h>
+
+#include <asm/mach-loongson32/irq.h>
+
+struct ls_intc_data {
+	void __iomem *base;
+	unsigned int chip;
+};
+
+#define MAX_CHIPS	5
+#define LS_REG_INTC_STATUS	0x00
+#define LS_REG_INTC_EN	0x04
+#define LS_REG_INTC_SET	0x08
+#define LS_REG_INTC_CLR	0x0c
+#define LS_REG_INTC_POL	0x10
+#define LS_REG_INTC_EDGE	0x14
+#define CHIP_SIZE		0x18
+
+static irqreturn_t intc_cascade(int irq, void *data)
+{
+	struct ls_intc_data *intc = irq_get_handler_data(irq);
+	uint32_t irq_reg;
+
+	irq_reg = readl(intc->base + (CHIP_SIZE * intc->chip)
+					+ LS_REG_INTC_STATUS);
+	generic_handle_irq(__fls(irq_reg) + (intc->chip * 32) + LS1X_IRQ_BASE);
+
+	return IRQ_HANDLED;
+}
+
+static void ls_intc_set_bit(
+	struct irq_chip_generic *gc, unsigned int offset,
+	u32 mask, bool set)
+{
+	if (set)
+		writel(readl(gc->reg_base + offset) |
+		mask, gc->reg_base + offset);
+	else
+		writel(readl(gc->reg_base + offset) &
+		~mask, gc->reg_base + offset);
+}
+
+static int ls_intc_set_type(struct irq_data *data, unsigned int type)
+{
+	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
+	u32 mask = data->mask;
+
+	switch (type) {
+	case IRQ_TYPE_LEVEL_HIGH:
+		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, false);
+		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, true);
+		break;
+	case IRQ_TYPE_LEVEL_LOW:
+		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, false);
+		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, false);
+		break;
+	case IRQ_TYPE_EDGE_RISING:
+		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, true);
+		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, true);
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, true);
+		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, false);
+		break;
+	case IRQ_TYPE_NONE:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
+static struct irqaction intc_cascade_action = {
+	.handler = intc_cascade,
+	.name = "intc cascade interrupt",
+};
+
+static int __init ls_intc_of_init(struct device_node *node,
+				       unsigned int num_chips)
+{
+	struct ls_intc_data *intc[MAX_CHIPS];
+	struct irq_chip_generic *gc;
+	struct irq_chip_type *ct;
+	struct irq_domain *domain;
+	void __iomem *base;
+	int parent_irq[MAX_CHIPS], err = 0;
+	unsigned int i = 0;
+
+	base = of_iomap(node, 0);
+	if (!base)
+		return -ENODEV;
+
+	for (i = 0; i < num_chips; i++) {
+		/* Mask all irqs */
+		writel(0x0, base + (i * CHIP_SIZE) +
+		       LS_REG_INTC_EN);
+
+		/* Set all irqs to high level triggered */
+		writel(0xffffffff, base + (i * CHIP_SIZE) +
+		       LS_REG_INTC_POL);
+
+		intc[i] = kzalloc(sizeof(*intc[i]), GFP_KERNEL);
+		if (!intc[i]) {
+			err = -ENOMEM;
+			goto out_err;
+		}
+
+		intc[i]->base = base;
+		intc[i]->chip = i;
+
+		parent_irq[i] = irq_of_parse_and_map(node, i);
+		if (!parent_irq[i]) {
+			pr_warn("unable to get parent irq for irqchip %u", i);
+			kfree(intc[i]);
+			intc[i] = NULL;
+			err = -EINVAL;
+			goto out_err;
+		}
+
+		err = irq_set_handler_data(parent_irq[i], intc[i]);
+		if (err)
+			goto out_err;
+
+		gc = irq_alloc_generic_chip("INTC", 1,
+					    LS1X_IRQ_BASE + (i * 32),
+					    base + (i * CHIP_SIZE),
+					    handle_level_irq);
+
+		ct = gc->chip_types;
+		ct->regs.mask = LS_REG_INTC_EN;
+		ct->regs.ack = LS_REG_INTC_CLR;
+		ct->chip.irq_unmask = irq_gc_mask_set_bit;
+		ct->chip.irq_mask = irq_gc_mask_clr_bit;
+		ct->chip.irq_ack = irq_gc_ack_set_bit;
+		ct->chip.irq_set_type = ls_intc_set_type;
+
+		irq_setup_generic_chip(gc, IRQ_MSK(32), 0, 0,
+				       IRQ_NOPROBE | IRQ_LEVEL);
+	}
+
+	domain = irq_domain_add_legacy(node, num_chips * 32, LS1X_IRQ_BASE, 0,
+				       &irq_domain_simple_ops, NULL);
+	if (!domain) {
+		pr_warn("unable to register IRQ domain\n");
+		err = -EINVAL;
+		goto out_err;
+	}
+
+	for (i = 0; i < num_chips; i++)
+		setup_irq(parent_irq[i], &intc_cascade_action);
+
+	return 0;
+
+out_err:
+	for (i = 0; i < MAX_CHIPS; i++) {
+		if (intc[i]) {
+			kfree(intc[i]);
+			irq_dispose_mapping(parent_irq[i]);
+		} else {
+			break;
+		}
+	}
+	return err;
+}
+
+static int __init intc_4chip_of_init(struct device_node *node,
+				     struct device_node *parent)
+{
+	return ls_intc_of_init(node, 4);
+}
+IRQCHIP_DECLARE(ls1b_intc, "loongson,ls1b-intc", intc_4chip_of_init);
+
+static int __init intc_5chip_of_init(struct device_node *node,
+	struct device_node *parent)
+{
+	return ls_intc_of_init(node, 5);
+}
+IRQCHIP_DECLARE(ls1c_intc, "loongson,ls1c-intc", intc_5chip_of_init);
-- 
2.20.1


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

* Re: [PATCH v2 1/2] irqchip: Add driver for Loongson-1 interrupt controller
  2019-01-23  6:23   ` [PATCH v2 1/2] irqchip: Add driver for Loongson-1 interrupt controller Jiaxun Yang
@ 2019-01-23  8:36     ` Marc Zyngier
  0 siblings, 0 replies; 24+ messages in thread
From: Marc Zyngier @ 2019-01-23  8:36 UTC (permalink / raw)
  To: Jiaxun Yang; +Cc: linux-mips, Thomas Gleixner, Jason Cooper, linux-kernel

Hi Jiaxun,

On Wed, 23 Jan 2019 06:23:36 +0000,
Jiaxun Yang <jiaxun.yang@flygoat.com> wrote:
> 
> This controller appeared on Loongson-1 family MCUs
> including Loongson-1B and Loongson-1C.
> 
> Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
> ---
>  drivers/irqchip/Kconfig    |   9 ++
>  drivers/irqchip/Makefile   |   1 +
>  drivers/irqchip/irq-ls1x.c | 194 +++++++++++++++++++++++++++++++++++++
>  3 files changed, 204 insertions(+)
>  create mode 100644 drivers/irqchip/irq-ls1x.c
> 
> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> index 3d1e60779078..5dcb5456cd14 100644
> --- a/drivers/irqchip/Kconfig
> +++ b/drivers/irqchip/Kconfig
> @@ -406,6 +406,15 @@ config IMX_IRQSTEER
>  	help
>  	  Support for the i.MX IRQSTEER interrupt multiplexer/remapper.
>  
> +config LS1X_IRQ
> +	bool "Loongson-1 Interrupt Controller"
> +	depends on MACH_LOONGSON32
> +	default y
> +	select IRQ_DOMAIN
> +	select GENERIC_IRQ_CHIP
> +	help
> +	  Support for the Loongson-1 platform Interrupt Controller.
> +
>  endmenu
>  
>  config SIFIVE_PLIC
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index c93713d24b86..7acd0e36d0b4 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -94,3 +94,4 @@ obj-$(CONFIG_CSKY_APB_INTC)		+= irq-csky-apb-intc.o
>  obj-$(CONFIG_SIFIVE_PLIC)		+= irq-sifive-plic.o
>  obj-$(CONFIG_IMX_IRQSTEER)		+= irq-imx-irqsteer.o
>  obj-$(CONFIG_MADERA_IRQ)		+= irq-madera.o
> +obj-$(CONFIG_LS1X_IRQ)			+= irq-ls1x.o
> diff --git a/drivers/irqchip/irq-ls1x.c b/drivers/irqchip/irq-ls1x.c
> new file mode 100644
> index 000000000000..b15b01237830
> --- /dev/null
> +++ b/drivers/irqchip/irq-ls1x.c
> @@ -0,0 +1,194 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + *  Copyright (C) 2019, Jiaxun Yang <jiaxun.yang@flygoat.com>
> + *  Loongson-1 platform IRQ support
> + */
> +
> +#include <linux/errno.h>
> +#include <linux/init.h>
> +#include <linux/types.h>
> +#include <linux/interrupt.h>
> +#include <linux/ioport.h>
> +#include <linux/irqchip.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/io.h>
> +
> +#include <asm/mach-loongson32/irq.h>
> +
> +struct ls_intc_data {
> +	void __iomem *base;
> +	unsigned int chip;
> +};
> +
> +#define MAX_CHIPS	5
> +#define LS_REG_INTC_STATUS	0x00
> +#define LS_REG_INTC_EN	0x04
> +#define LS_REG_INTC_SET	0x08
> +#define LS_REG_INTC_CLR	0x0c
> +#define LS_REG_INTC_POL	0x10
> +#define LS_REG_INTC_EDGE	0x14
> +#define CHIP_SIZE		0x18
> +
> +static irqreturn_t intc_cascade(int irq, void *data)
> +{
> +	struct ls_intc_data *intc = irq_get_handler_data(irq);
> +	uint32_t irq_reg;
> +
> +	irq_reg = readl(intc->base + (CHIP_SIZE * intc->chip)
> +					+ LS_REG_INTC_STATUS);
> +	generic_handle_irq(__fls(irq_reg) + (intc->chip * 32) + LS1X_IRQ_BASE);
> +
> +	return IRQ_HANDLED;
> +}

No. A chained interrupt is not a dealt with as a normal interrupt, as
it is a flow handler itself.

> +
> +static void ls_intc_set_bit(
> +	struct irq_chip_generic *gc, unsigned int offset,

Please put the first parameter on the same line as the function name.

> +	u32 mask, bool set)
> +{
> +	if (set)
> +		writel(readl(gc->reg_base + offset) |
> +		mask, gc->reg_base + offset);

Put "mask" on the same line as the rest of the expression.

> +	else
> +		writel(readl(gc->reg_base + offset) &
> +		~mask, gc->reg_base + offset);

Same here.

> +}
> +
> +static int ls_intc_set_type(struct irq_data *data, unsigned int type)
> +{
> +	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
> +	u32 mask = data->mask;
> +
> +	switch (type) {
> +	case IRQ_TYPE_LEVEL_HIGH:
> +		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, false);
> +		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, true);
> +		break;
> +	case IRQ_TYPE_LEVEL_LOW:
> +		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, false);
> +		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, false);
> +		break;
> +	case IRQ_TYPE_EDGE_RISING:
> +		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, true);
> +		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, true);
> +		break;
> +	case IRQ_TYPE_EDGE_FALLING:
> +		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, true);
> +		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, false);
> +		break;
> +	case IRQ_TYPE_NONE:
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +
> +static struct irqaction intc_cascade_action = {
> +	.handler = intc_cascade,
> +	.name = "intc cascade interrupt",
> +};
> +
> +static int __init ls_intc_of_init(struct device_node *node,
> +				       unsigned int num_chips)
> +{
> +	struct ls_intc_data *intc[MAX_CHIPS];
> +	struct irq_chip_generic *gc;
> +	struct irq_chip_type *ct;
> +	struct irq_domain *domain;
> +	void __iomem *base;
> +	int parent_irq[MAX_CHIPS], err = 0;

Initialise both arrays so that you can simplify error handling.

> +	unsigned int i = 0;
> +
> +	base = of_iomap(node, 0);
> +	if (!base)
> +		return -ENODEV;
> +
> +	for (i = 0; i < num_chips; i++) {
> +		/* Mask all irqs */
> +		writel(0x0, base + (i * CHIP_SIZE) +
> +		       LS_REG_INTC_EN);
> +
> +		/* Set all irqs to high level triggered */
> +		writel(0xffffffff, base + (i * CHIP_SIZE) +
> +		       LS_REG_INTC_POL);
> +
> +		intc[i] = kzalloc(sizeof(*intc[i]), GFP_KERNEL);
> +		if (!intc[i]) {
> +			err = -ENOMEM;
> +			goto out_err;
> +		}
> +
> +		intc[i]->base = base;
> +		intc[i]->chip = i;
> +
> +		parent_irq[i] = irq_of_parse_and_map(node, i);
> +		if (!parent_irq[i]) {
> +			pr_warn("unable to get parent irq for irqchip %u", i);
> +			kfree(intc[i]);
> +			intc[i] = NULL;
> +			err = -EINVAL;

You're making life very complicated for yourself. Just set err and
branch to the error handler.

> +			goto out_err;
> +		}
> +
> +		err = irq_set_handler_data(parent_irq[i], intc[i]);
> +		if (err)
> +			goto out_err;
> +
> +		gc = irq_alloc_generic_chip("INTC", 1,
> +					    LS1X_IRQ_BASE + (i * 32),
> +					    base + (i * CHIP_SIZE),
> +					    handle_level_irq);
> +
> +		ct = gc->chip_types;
> +		ct->regs.mask = LS_REG_INTC_EN;
> +		ct->regs.ack = LS_REG_INTC_CLR;
> +		ct->chip.irq_unmask = irq_gc_mask_set_bit;
> +		ct->chip.irq_mask = irq_gc_mask_clr_bit;
> +		ct->chip.irq_ack = irq_gc_ack_set_bit;
> +		ct->chip.irq_set_type = ls_intc_set_type;
> +
> +		irq_setup_generic_chip(gc, IRQ_MSK(32), 0, 0,
> +				       IRQ_NOPROBE | IRQ_LEVEL);
> +	}
> +
> +	domain = irq_domain_add_legacy(node, num_chips * 32, LS1X_IRQ_BASE, 0,
> +				       &irq_domain_simple_ops, NULL);
> +	if (!domain) {
> +		pr_warn("unable to register IRQ domain\n");
> +		err = -EINVAL;
> +		goto out_err;
> +	}
> +
> +	for (i = 0; i < num_chips; i++)
> +		setup_irq(parent_irq[i], &intc_cascade_action);

This is not how we do things anymore (some of this code feels like it
has escaped from a 2.2 kernel). See irq_set_chained_handler, and use
that to properly configure your cascades. There are tons of example in
the tree.

> +
> +	return 0;
> +
> +out_err:
> +	for (i = 0; i < MAX_CHIPS; i++) {
> +		if (intc[i]) {
> +			kfree(intc[i]);
> +			irq_dispose_mapping(parent_irq[i]);
> +		} else {
> +			break;
> +		}
> +	}

	for (i = 0; i < MAX_CHIPS; i++) {
		kfree(intc[i]);
		if (parent_irq[i])
			irq_dispose_mapping(parent_irq[i]);		
	}

> +	return err;
> +}
> +
> +static int __init intc_4chip_of_init(struct device_node *node,
> +				     struct device_node *parent)
> +{
> +	return ls_intc_of_init(node, 4);
> +}
> +IRQCHIP_DECLARE(ls1b_intc, "loongson,ls1b-intc", intc_4chip_of_init);
> +
> +static int __init intc_5chip_of_init(struct device_node *node,
> +	struct device_node *parent)
> +{
> +	return ls_intc_of_init(node, 5);
> +}
> +IRQCHIP_DECLARE(ls1c_intc, "loongson,ls1c-intc", intc_5chip_of_init);

Do you really need this distinction? You could easily find out how
many interrupts you have based on the DT, right? OR do you need this
for anything else?

Thanks,

	M.

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

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

* irqchip: Add driver for Loongson-1 intc v3
  2019-01-22 15:45 [PATCH 1/2] irqchip: Add driver for Loongson-1 interrupt controller Jiaxun Yang
  2019-01-22 15:45 ` [PATCH 2/2] dt-bindings: interrupt-controller: loongson ls1x intc Jiaxun Yang
  2019-01-23  6:23 ` irqchip: Add driver for Loongson-1 intc v2 Jiaxun Yang
@ 2019-01-24  3:27 ` Jiaxun Yang
  2019-01-24  3:27   ` [PATCH v3 1/2] irqchip: Add driver for Loongson-1 interrupt controller Jiaxun Yang
  2019-01-24  3:27   ` [PATCH v3 2/2] dt-bindings: interrupt-controller: loongson ls1x intc Jiaxun Yang
  2019-01-28 14:46 ` irqchip: Add driver for Loongson-1 intc v3 Jiaxun Yang
                   ` (2 subsequent siblings)
  5 siblings, 2 replies; 24+ messages in thread
From: Jiaxun Yang @ 2019-01-24  3:27 UTC (permalink / raw)
  To: linux-mips
  Cc: tglx, jason, marc.zyngier, robh+dt, mark.rutland, linux-kernel,
	devicetree

v1->v2: Fix SPDX-License-Identifier
v2->v3: Rework according suggestions from Marc Zyngier, Thanks.



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

* [PATCH v3 1/2] irqchip: Add driver for Loongson-1 interrupt controller
  2019-01-24  3:27 ` irqchip: Add driver for Loongson-1 intc v3 Jiaxun Yang
@ 2019-01-24  3:27   ` Jiaxun Yang
  2019-01-24  9:54     ` Marc Zyngier
  2019-01-24  3:27   ` [PATCH v3 2/2] dt-bindings: interrupt-controller: loongson ls1x intc Jiaxun Yang
  1 sibling, 1 reply; 24+ messages in thread
From: Jiaxun Yang @ 2019-01-24  3:27 UTC (permalink / raw)
  To: linux-mips
  Cc: tglx, jason, marc.zyngier, robh+dt, mark.rutland, linux-kernel,
	devicetree, Jiaxun Yang

This controller appeared on Loongson-1 family MCUs
including Loongson-1B and Loongson-1C.

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---
 drivers/irqchip/Kconfig    |   9 ++
 drivers/irqchip/Makefile   |   1 +
 drivers/irqchip/irq-ls1x.c | 176 +++++++++++++++++++++++++++++++++++++
 3 files changed, 186 insertions(+)
 create mode 100644 drivers/irqchip/irq-ls1x.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 3d1e60779078..5dcb5456cd14 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -406,6 +406,15 @@ config IMX_IRQSTEER
 	help
 	  Support for the i.MX IRQSTEER interrupt multiplexer/remapper.
 
+config LS1X_IRQ
+	bool "Loongson-1 Interrupt Controller"
+	depends on MACH_LOONGSON32
+	default y
+	select IRQ_DOMAIN
+	select GENERIC_IRQ_CHIP
+	help
+	  Support for the Loongson-1 platform Interrupt Controller.
+
 endmenu
 
 config SIFIVE_PLIC
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index c93713d24b86..7acd0e36d0b4 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -94,3 +94,4 @@ obj-$(CONFIG_CSKY_APB_INTC)		+= irq-csky-apb-intc.o
 obj-$(CONFIG_SIFIVE_PLIC)		+= irq-sifive-plic.o
 obj-$(CONFIG_IMX_IRQSTEER)		+= irq-imx-irqsteer.o
 obj-$(CONFIG_MADERA_IRQ)		+= irq-madera.o
+obj-$(CONFIG_LS1X_IRQ)			+= irq-ls1x.o
diff --git a/drivers/irqchip/irq-ls1x.c b/drivers/irqchip/irq-ls1x.c
new file mode 100644
index 000000000000..de92ca04cf9f
--- /dev/null
+++ b/drivers/irqchip/irq-ls1x.c
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Copyright (C) 2019, Jiaxun Yang <jiaxun.yang@flygoat.com>
+ *  Loongson-1 platform IRQ support
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/irqchip.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/io.h>
+#include <linux/irqchip/chained_irq.h>
+
+#include <asm/mach-loongson32/irq.h>
+
+
+#define MAX_CHIPS	5
+#define LS_REG_INTC_STATUS	0x00
+#define LS_REG_INTC_EN	0x04
+#define LS_REG_INTC_SET	0x08
+#define LS_REG_INTC_CLR	0x0c
+#define LS_REG_INTC_POL	0x10
+#define LS_REG_INTC_EDGE	0x14
+#define CHIP_SIZE	0x18
+
+static void ls_chained_handle_irq(struct irq_desc *desc)
+{
+	struct irq_chip_generic *gc = irq_desc_get_handler_data(desc);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	u32 pending;
+
+	chained_irq_enter(chip, desc);
+	pending = readl(gc->reg_base + LS_REG_INTC_STATUS) &
+			readl(gc->reg_base + LS_REG_INTC_EN);
+
+	if (!pending) {
+		spurious_interrupt();
+		chained_irq_exit(chip, desc);
+		return;
+	}
+
+	while (pending) {
+		int bit = __ffs(pending);
+
+		generic_handle_irq(gc->irq_base + bit);
+		pending &= ~BIT(bit);
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static void ls_intc_set_bit(struct irq_chip_generic *gc,
+							unsigned int offset,
+							u32 mask, bool set)
+{
+	if (set)
+		writel(readl(gc->reg_base + offset) | mask,
+		gc->reg_base + offset);
+	else
+		writel(readl(gc->reg_base + offset) & ~mask,
+		gc->reg_base + offset);
+}
+
+static int ls_intc_set_type(struct irq_data *data, unsigned int type)
+{
+	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
+	u32 mask = data->mask;
+
+	switch (type) {
+	case IRQ_TYPE_LEVEL_HIGH:
+		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, false);
+		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, true);
+		break;
+	case IRQ_TYPE_LEVEL_LOW:
+		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, false);
+		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, false);
+		break;
+	case IRQ_TYPE_EDGE_RISING:
+		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, true);
+		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, true);
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, true);
+		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, false);
+		break;
+	case IRQ_TYPE_NONE:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
+static int __init ls_intc_of_init(struct device_node *node,
+				       struct device_node *parent)
+{
+	struct irq_chip_generic *gc[MAX_CHIPS];
+	struct irq_chip_type *ct;
+	struct irq_domain *domain;
+	void __iomem *base;
+	int parent_irq[MAX_CHIPS], err = 0;
+	unsigned int i = 0;
+
+	unsigned int num_chips = of_irq_count(node);
+
+	base = of_iomap(node, 0);
+	if (!base)
+		return -ENODEV;
+
+	for (i = 0; i < num_chips; i++) {
+		/* Mask all irqs */
+		writel(0x0, base + (i * CHIP_SIZE) +
+		       LS_REG_INTC_EN);
+
+		/* Set all irqs to high level triggered */
+		writel(0xffffffff, base + (i * CHIP_SIZE) +
+		       LS_REG_INTC_POL);
+
+		parent_irq[i] = irq_of_parse_and_map(node, i);
+
+		if (!parent_irq[i]) {
+			pr_warn("unable to get parent irq for irqchip %u\n", i);
+			goto out_err;
+		}
+
+		gc[i] = irq_alloc_generic_chip("INTC", 1,
+					    LS1X_IRQ_BASE + (i * 32),
+					    base + (i * CHIP_SIZE),
+					    handle_level_irq);
+
+		ct = gc[i]->chip_types;
+		ct->regs.mask = LS_REG_INTC_EN;
+		ct->regs.ack = LS_REG_INTC_CLR;
+		ct->chip.irq_unmask = irq_gc_mask_set_bit;
+		ct->chip.irq_mask = irq_gc_mask_clr_bit;
+		ct->chip.irq_ack = irq_gc_ack_set_bit;
+		ct->chip.irq_set_type = ls_intc_set_type;
+
+		irq_setup_generic_chip(gc[i], IRQ_MSK(32), 0, 0,
+				       IRQ_NOPROBE | IRQ_LEVEL);
+	}
+
+	domain = irq_domain_add_legacy(node, num_chips * 32, LS1X_IRQ_BASE, 0,
+		&irq_domain_simple_ops, NULL);
+	if (!domain) {
+		pr_warn("unable to register IRQ domain\n");
+		err = -EINVAL;
+		goto out_err;
+	}
+
+	for (i = 0; i < num_chips; i++)
+		irq_set_chained_handler_and_data(parent_irq[i],
+		ls_chained_handle_irq, gc[i]);
+
+	pr_info("ls1x-irq: %u chips registered\n", num_chips);
+
+	return 0;
+
+out_err:
+	for (i = 0; i < MAX_CHIPS; i++) {
+		if (gc[i])
+			irq_destroy_generic_chip(gc[i], IRQ_MSK(32),
+				       IRQ_NOPROBE | IRQ_LEVEL, 0);
+		if (parent_irq[i])
+			irq_dispose_mapping(parent_irq[i]);
+	}
+	return err;
+}
+
+IRQCHIP_DECLARE(ls1x_intc, "loongson,ls1x-intc", ls_intc_of_init);
-- 
2.20.1


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

* [PATCH v3 2/2] dt-bindings: interrupt-controller: loongson ls1x intc
  2019-01-24  3:27 ` irqchip: Add driver for Loongson-1 intc v3 Jiaxun Yang
  2019-01-24  3:27   ` [PATCH v3 1/2] irqchip: Add driver for Loongson-1 interrupt controller Jiaxun Yang
@ 2019-01-24  3:27   ` Jiaxun Yang
  2019-01-30 16:41     ` Rob Herring
  1 sibling, 1 reply; 24+ messages in thread
From: Jiaxun Yang @ 2019-01-24  3:27 UTC (permalink / raw)
  To: linux-mips
  Cc: tglx, jason, marc.zyngier, robh+dt, mark.rutland, linux-kernel,
	devicetree, Jiaxun Yang

Dt-bindings doc about Loongson-1 interrupt controller

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

diff --git a/Documentation/devicetree/bindings/interrupt-controller/loongson,ls1x-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/loongson,ls1x-intc.txt
new file mode 100644
index 000000000000..a4e17c3f5f93
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/loongson,ls1x-intc.txt
@@ -0,0 +1,24 @@
+Loongson ls1x Interrupt Controller
+
+Required properties:
+
+- compatible : should be "loongson,ls1x-intc". Valid strings are:
+
+- reg : Specifies base physical address and size of the registers.
+- interrupt-controller : Identifies the node as an interrupt controller
+- #interrupt-cells : Specifies the number of cells needed to encode an
+  interrupt source. The value shall be 1.
+- interrupts : Specifies the CPU interrupts the controller is connected to.
+
+Example:
+
+intc: interrupt-controller@1fd01040 {
+	compatible = "loongson,ls1x-intc";
+	reg = <0x1fd01040 0x78>;
+
+	interrupt-controller;
+	#interrupt-cells = <1>;
+
+	interrupt-parent = <&cpu_intc>;
+	interrupts = <2>, <3>, <4>, <5>, <6>;
+};
\ No newline at end of file
-- 
2.20.1


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

* Re: [PATCH v3 1/2] irqchip: Add driver for Loongson-1 interrupt controller
  2019-01-24  3:27   ` [PATCH v3 1/2] irqchip: Add driver for Loongson-1 interrupt controller Jiaxun Yang
@ 2019-01-24  9:54     ` Marc Zyngier
  2019-01-25 10:56       ` Jiaxun Yang
  0 siblings, 1 reply; 24+ messages in thread
From: Marc Zyngier @ 2019-01-24  9:54 UTC (permalink / raw)
  To: Jiaxun Yang
  Cc: linux-mips, tglx, jason, robh+dt, mark.rutland, linux-kernel, devicetree

On Thu, 24 Jan 2019 03:27:29 +0000,
Jiaxun Yang <jiaxun.yang@flygoat.com> wrote:
> 
> This controller appeared on Loongson-1 family MCUs
> including Loongson-1B and Loongson-1C.
> 
> Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
> ---
>  drivers/irqchip/Kconfig    |   9 ++
>  drivers/irqchip/Makefile   |   1 +
>  drivers/irqchip/irq-ls1x.c | 176 +++++++++++++++++++++++++++++++++++++
>  3 files changed, 186 insertions(+)
>  create mode 100644 drivers/irqchip/irq-ls1x.c
> 
> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> index 3d1e60779078..5dcb5456cd14 100644
> --- a/drivers/irqchip/Kconfig
> +++ b/drivers/irqchip/Kconfig
> @@ -406,6 +406,15 @@ config IMX_IRQSTEER
>  	help
>  	  Support for the i.MX IRQSTEER interrupt multiplexer/remapper.
>  
> +config LS1X_IRQ
> +	bool "Loongson-1 Interrupt Controller"
> +	depends on MACH_LOONGSON32
> +	default y
> +	select IRQ_DOMAIN
> +	select GENERIC_IRQ_CHIP
> +	help
> +	  Support for the Loongson-1 platform Interrupt Controller.
> +
>  endmenu
>  
>  config SIFIVE_PLIC
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index c93713d24b86..7acd0e36d0b4 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -94,3 +94,4 @@ obj-$(CONFIG_CSKY_APB_INTC)		+= irq-csky-apb-intc.o
>  obj-$(CONFIG_SIFIVE_PLIC)		+= irq-sifive-plic.o
>  obj-$(CONFIG_IMX_IRQSTEER)		+= irq-imx-irqsteer.o
>  obj-$(CONFIG_MADERA_IRQ)		+= irq-madera.o
> +obj-$(CONFIG_LS1X_IRQ)			+= irq-ls1x.o
> diff --git a/drivers/irqchip/irq-ls1x.c b/drivers/irqchip/irq-ls1x.c
> new file mode 100644
> index 000000000000..de92ca04cf9f
> --- /dev/null
> +++ b/drivers/irqchip/irq-ls1x.c
> @@ -0,0 +1,176 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + *  Copyright (C) 2019, Jiaxun Yang <jiaxun.yang@flygoat.com>
> + *  Loongson-1 platform IRQ support
> + */
> +
> +#include <linux/errno.h>
> +#include <linux/init.h>
> +#include <linux/types.h>
> +#include <linux/interrupt.h>
> +#include <linux/ioport.h>
> +#include <linux/irqchip.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/io.h>
> +#include <linux/irqchip/chained_irq.h>
> +
> +#include <asm/mach-loongson32/irq.h>
> +
> +
> +#define MAX_CHIPS	5
> +#define LS_REG_INTC_STATUS	0x00
> +#define LS_REG_INTC_EN	0x04
> +#define LS_REG_INTC_SET	0x08
> +#define LS_REG_INTC_CLR	0x0c
> +#define LS_REG_INTC_POL	0x10
> +#define LS_REG_INTC_EDGE	0x14
> +#define CHIP_SIZE	0x18
> +
> +static void ls_chained_handle_irq(struct irq_desc *desc)
> +{
> +	struct irq_chip_generic *gc = irq_desc_get_handler_data(desc);
> +	struct irq_chip *chip = irq_desc_get_chip(desc);
> +	u32 pending;
> +
> +	chained_irq_enter(chip, desc);
> +	pending = readl(gc->reg_base + LS_REG_INTC_STATUS) &
> +			readl(gc->reg_base + LS_REG_INTC_EN);
> +
> +	if (!pending) {
> +		spurious_interrupt();
> +		chained_irq_exit(chip, desc);
> +		return;
> +	}

Given the context, this is the same as writing:

	if (!pending)
		spurious_interrupt();

and let it go through.

> +
> +	while (pending) {
> +		int bit = __ffs(pending);
> +
> +		generic_handle_irq(gc->irq_base + bit);
> +		pending &= ~BIT(bit);
> +	}
> +
> +	chained_irq_exit(chip, desc);
> +}
> +
> +static void ls_intc_set_bit(struct irq_chip_generic *gc,
> +							unsigned int offset,
> +							u32 mask, bool set)
> +{
> +	if (set)
> +		writel(readl(gc->reg_base + offset) | mask,
> +		gc->reg_base + offset);
> +	else
> +		writel(readl(gc->reg_base + offset) & ~mask,
> +		gc->reg_base + offset);
> +}
> +
> +static int ls_intc_set_type(struct irq_data *data, unsigned int type)
> +{
> +	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
> +	u32 mask = data->mask;
> +
> +	switch (type) {
> +	case IRQ_TYPE_LEVEL_HIGH:
> +		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, false);
> +		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, true);
> +		break;
> +	case IRQ_TYPE_LEVEL_LOW:
> +		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, false);
> +		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, false);
> +		break;
> +	case IRQ_TYPE_EDGE_RISING:
> +		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, true);
> +		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, true);
> +		break;
> +	case IRQ_TYPE_EDGE_FALLING:
> +		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, true);
> +		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, false);
> +		break;
> +	case IRQ_TYPE_NONE:
> +		break;
> +	default:
> +		return -EINVAL;
> +	}

I just realised that in your DT binding, you define the interrupt
specifier as having a single cell. Which means that you never describe
the interrupt trigger there. Why?

Does it mean that all the drivers have to configure their interrupt
trigger themselves, and cannot rely on DT to do it? This seems quite
backward.

> +
> +	return 0;
> +}
> +
> +
> +static int __init ls_intc_of_init(struct device_node *node,
> +				       struct device_node *parent)
> +{
> +	struct irq_chip_generic *gc[MAX_CHIPS];
> +	struct irq_chip_type *ct;
> +	struct irq_domain *domain;
> +	void __iomem *base;
> +	int parent_irq[MAX_CHIPS], err = 0;
> +	unsigned int i = 0;
> +
> +	unsigned int num_chips = of_irq_count(node);
> +
> +	base = of_iomap(node, 0);
> +	if (!base)
> +		return -ENODEV;
> +
> +	for (i = 0; i < num_chips; i++) {
> +		/* Mask all irqs */
> +		writel(0x0, base + (i * CHIP_SIZE) +
> +		       LS_REG_INTC_EN);
> +
> +		/* Set all irqs to high level triggered */
> +		writel(0xffffffff, base + (i * CHIP_SIZE) +
> +		       LS_REG_INTC_POL);
> +
> +		parent_irq[i] = irq_of_parse_and_map(node, i);
> +
> +		if (!parent_irq[i]) {
> +			pr_warn("unable to get parent irq for irqchip %u\n", i);
> +			goto out_err;
> +		}
> +
> +		gc[i] = irq_alloc_generic_chip("INTC", 1,
> +					    LS1X_IRQ_BASE + (i * 32),
> +					    base + (i * CHIP_SIZE),
> +					    handle_level_irq);
> +
> +		ct = gc[i]->chip_types;
> +		ct->regs.mask = LS_REG_INTC_EN;
> +		ct->regs.ack = LS_REG_INTC_CLR;
> +		ct->chip.irq_unmask = irq_gc_mask_set_bit;
> +		ct->chip.irq_mask = irq_gc_mask_clr_bit;
> +		ct->chip.irq_ack = irq_gc_ack_set_bit;
> +		ct->chip.irq_set_type = ls_intc_set_type;
> +
> +		irq_setup_generic_chip(gc[i], IRQ_MSK(32), 0, 0,
> +				       IRQ_NOPROBE | IRQ_LEVEL);
> +	}
> +
> +	domain = irq_domain_add_legacy(node, num_chips * 32, LS1X_IRQ_BASE, 0,
> +		&irq_domain_simple_ops, NULL);

Why a legacy domain? This is usually reserved to old drivers that are
converted to a new infrastructure, while needing some form of platform
hacks. I don't see this being the case here.

It is also worrying that although you have up to 5 irqchips, they all
share a single domain. What does this mean? each irqchip is expected
to have its own domain.

> +	if (!domain) {
> +		pr_warn("unable to register IRQ domain\n");
> +		err = -EINVAL;
> +		goto out_err;
> +	}
> +
> +	for (i = 0; i < num_chips; i++)
> +		irq_set_chained_handler_and_data(parent_irq[i],
> +		ls_chained_handle_irq, gc[i]);
> +
> +	pr_info("ls1x-irq: %u chips registered\n", num_chips);
> +
> +	return 0;
> +
> +out_err:
> +	for (i = 0; i < MAX_CHIPS; i++) {
> +		if (gc[i])

But you've never initialised gc[], nor parent_irq[]. So you'll get
whatever the stack had at this point. Not great.

> +			irq_destroy_generic_chip(gc[i], IRQ_MSK(32),
> +				       IRQ_NOPROBE | IRQ_LEVEL, 0);
> +		if (parent_irq[i])
> +			irq_dispose_mapping(parent_irq[i]);
> +	}
> +	return err;
> +}
> +
> +IRQCHIP_DECLARE(ls1x_intc, "loongson,ls1x-intc", ls_intc_of_init);
> -- 
> 2.20.1
> 

Thanks,

	M.

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

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

* Re: [PATCH v3 1/2] irqchip: Add driver for Loongson-1 interrupt controller
  2019-01-24  9:54     ` Marc Zyngier
@ 2019-01-25 10:56       ` Jiaxun Yang
  2019-01-26 12:09         ` Marc Zyngier
  0 siblings, 1 reply; 24+ messages in thread
From: Jiaxun Yang @ 2019-01-25 10:56 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: linux-mips, tglx, jason, robh+dt, mark.rutland, linux-kernel, devicetree

Hi Marc

Thanks for your suggestions, I'm working on v4 and I would like to ask 
if it is better to have a driver for only one irqchip

and create dt nodes for each chip, or just register all the chips in a 
single driver with only one dt node.

在 2019/1/24 下午5:54, Marc Zyngier 写道:
> On Thu, 24 Jan 2019 03:27:29 +0000,
> Jiaxun Yang <jiaxun.yang@flygoat.com> wrote:
>> This controller appeared on Loongson-1 family MCUs
>> including Loongson-1B and Loongson-1C.
>>
>> Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
>> ---
>>   drivers/irqchip/Kconfig    |   9 ++
>>   drivers/irqchip/Makefile   |   1 +
>>   drivers/irqchip/irq-ls1x.c | 176 +++++++++++++++++++++++++++++++++++++
>>   3 files changed, 186 insertions(+)
>>   create mode 100644 drivers/irqchip/irq-ls1x.c
>>
>> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
>> index 3d1e60779078..5dcb5456cd14 100644
>> --- a/drivers/irqchip/Kconfig
>> +++ b/drivers/irqchip/Kconfig
>> @@ -406,6 +406,15 @@ config IMX_IRQSTEER
>>   	help
>>   	  Support for the i.MX IRQSTEER interrupt multiplexer/remapper.
>>   
>> +config LS1X_IRQ
>> +	bool "Loongson-1 Interrupt Controller"
>> +	depends on MACH_LOONGSON32
>> +	default y
>> +	select IRQ_DOMAIN
>> +	select GENERIC_IRQ_CHIP
>> +	help
>> +	  Support for the Loongson-1 platform Interrupt Controller.
>> +
>>   endmenu
>>   
>>   config SIFIVE_PLIC
>> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
>> index c93713d24b86..7acd0e36d0b4 100644
>> --- a/drivers/irqchip/Makefile
>> +++ b/drivers/irqchip/Makefile
>> @@ -94,3 +94,4 @@ obj-$(CONFIG_CSKY_APB_INTC)		+= irq-csky-apb-intc.o
>>   obj-$(CONFIG_SIFIVE_PLIC)		+= irq-sifive-plic.o
>>   obj-$(CONFIG_IMX_IRQSTEER)		+= irq-imx-irqsteer.o
>>   obj-$(CONFIG_MADERA_IRQ)		+= irq-madera.o
>> +obj-$(CONFIG_LS1X_IRQ)			+= irq-ls1x.o
>> diff --git a/drivers/irqchip/irq-ls1x.c b/drivers/irqchip/irq-ls1x.c
>> new file mode 100644
>> index 000000000000..de92ca04cf9f
>> --- /dev/null
>> +++ b/drivers/irqchip/irq-ls1x.c
>> @@ -0,0 +1,176 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + *  Copyright (C) 2019, Jiaxun Yang <jiaxun.yang@flygoat.com>
>> + *  Loongson-1 platform IRQ support
>> + */
>> +
>> +#include <linux/errno.h>
>> +#include <linux/init.h>
>> +#include <linux/types.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/ioport.h>
>> +#include <linux/irqchip.h>
>> +#include <linux/of_address.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/io.h>
>> +#include <linux/irqchip/chained_irq.h>
>> +
>> +#include <asm/mach-loongson32/irq.h>
>> +
>> +
>> +#define MAX_CHIPS	5
>> +#define LS_REG_INTC_STATUS	0x00
>> +#define LS_REG_INTC_EN	0x04
>> +#define LS_REG_INTC_SET	0x08
>> +#define LS_REG_INTC_CLR	0x0c
>> +#define LS_REG_INTC_POL	0x10
>> +#define LS_REG_INTC_EDGE	0x14
>> +#define CHIP_SIZE	0x18
>> +
>> +static void ls_chained_handle_irq(struct irq_desc *desc)
>> +{
>> +	struct irq_chip_generic *gc = irq_desc_get_handler_data(desc);
>> +	struct irq_chip *chip = irq_desc_get_chip(desc);
>> +	u32 pending;
>> +
>> +	chained_irq_enter(chip, desc);
>> +	pending = readl(gc->reg_base + LS_REG_INTC_STATUS) &
>> +			readl(gc->reg_base + LS_REG_INTC_EN);
>> +
>> +	if (!pending) {
>> +		spurious_interrupt();
>> +		chained_irq_exit(chip, desc);
>> +		return;
>> +	}
> Given the context, this is the same as writing:
>
> 	if (!pending)
> 		spurious_interrupt();
>
> and let it go through.
>
>> +
>> +	while (pending) {
>> +		int bit = __ffs(pending);
>> +
>> +		generic_handle_irq(gc->irq_base + bit);
>> +		pending &= ~BIT(bit);
>> +	}
>> +
>> +	chained_irq_exit(chip, desc);
>> +}
>> +
>> +static void ls_intc_set_bit(struct irq_chip_generic *gc,
>> +							unsigned int offset,
>> +							u32 mask, bool set)
>> +{
>> +	if (set)
>> +		writel(readl(gc->reg_base + offset) | mask,
>> +		gc->reg_base + offset);
>> +	else
>> +		writel(readl(gc->reg_base + offset) & ~mask,
>> +		gc->reg_base + offset);
>> +}
>> +
>> +static int ls_intc_set_type(struct irq_data *data, unsigned int type)
>> +{
>> +	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
>> +	u32 mask = data->mask;
>> +
>> +	switch (type) {
>> +	case IRQ_TYPE_LEVEL_HIGH:
>> +		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, false);
>> +		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, true);
>> +		break;
>> +	case IRQ_TYPE_LEVEL_LOW:
>> +		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, false);
>> +		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, false);
>> +		break;
>> +	case IRQ_TYPE_EDGE_RISING:
>> +		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, true);
>> +		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, true);
>> +		break;
>> +	case IRQ_TYPE_EDGE_FALLING:
>> +		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, true);
>> +		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, false);
>> +		break;
>> +	case IRQ_TYPE_NONE:
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
> I just realised that in your DT binding, you define the interrupt
> specifier as having a single cell. Which means that you never describe
> the interrupt trigger there. Why?
>
> Does it mean that all the drivers have to configure their interrupt
> trigger themselves, and cannot rely on DT to do it? This seems quite
> backward.

My fault, will fix it later.

>
>> +
>> +	return 0;
>> +}
>> +
>> +
>> +static int __init ls_intc_of_init(struct device_node *node,
>> +				       struct device_node *parent)
>> +{
>> +	struct irq_chip_generic *gc[MAX_CHIPS];
>> +	struct irq_chip_type *ct;
>> +	struct irq_domain *domain;
>> +	void __iomem *base;
>> +	int parent_irq[MAX_CHIPS], err = 0;
>> +	unsigned int i = 0;
>> +
>> +	unsigned int num_chips = of_irq_count(node);
>> +
>> +	base = of_iomap(node, 0);
>> +	if (!base)
>> +		return -ENODEV;
>> +
>> +	for (i = 0; i < num_chips; i++) {
>> +		/* Mask all irqs */
>> +		writel(0x0, base + (i * CHIP_SIZE) +
>> +		       LS_REG_INTC_EN);
>> +
>> +		/* Set all irqs to high level triggered */
>> +		writel(0xffffffff, base + (i * CHIP_SIZE) +
>> +		       LS_REG_INTC_POL);
>> +
>> +		parent_irq[i] = irq_of_parse_and_map(node, i);
>> +
>> +		if (!parent_irq[i]) {
>> +			pr_warn("unable to get parent irq for irqchip %u\n", i);
>> +			goto out_err;
>> +		}
>> +
>> +		gc[i] = irq_alloc_generic_chip("INTC", 1,
>> +					    LS1X_IRQ_BASE + (i * 32),
>> +					    base + (i * CHIP_SIZE),
>> +					    handle_level_irq);
>> +
>> +		ct = gc[i]->chip_types;
>> +		ct->regs.mask = LS_REG_INTC_EN;
>> +		ct->regs.ack = LS_REG_INTC_CLR;
>> +		ct->chip.irq_unmask = irq_gc_mask_set_bit;
>> +		ct->chip.irq_mask = irq_gc_mask_clr_bit;
>> +		ct->chip.irq_ack = irq_gc_ack_set_bit;
>> +		ct->chip.irq_set_type = ls_intc_set_type;
>> +
>> +		irq_setup_generic_chip(gc[i], IRQ_MSK(32), 0, 0,
>> +				       IRQ_NOPROBE | IRQ_LEVEL);
>> +	}
>> +
>> +	domain = irq_domain_add_legacy(node, num_chips * 32, LS1X_IRQ_BASE, 0,
>> +		&irq_domain_simple_ops, NULL);
> Why a legacy domain? This is usually reserved to old drivers that are
> converted to a new infrastructure, while needing some form of platform
> hacks. I don't see this being the case here.
>
> It is also worrying that although you have up to 5 irqchips, they all
> share a single domain. What does this mean? each irqchip is expected
> to have its own domain.

Yes, I do like this for backward compatible reason. I'm turning

a legacy platform device mach(arch/mips/loongson32) in to

dt based generic mach and I would like to do it step by step rather than 
one time.

So I use legacy domain in order to keep IRQ same with the

old driver exist on arch/mips/loongson32/common/irq.c

>> +	if (!domain) {
>> +		pr_warn("unable to register IRQ domain\n");
>> +		err = -EINVAL;
>> +		goto out_err;
>> +	}
>> +
>> +	for (i = 0; i < num_chips; i++)
>> +		irq_set_chained_handler_and_data(parent_irq[i],
>> +		ls_chained_handle_irq, gc[i]);
>> +
>> +	pr_info("ls1x-irq: %u chips registered\n", num_chips);
>> +
>> +	return 0;
>> +
>> +out_err:
>> +	for (i = 0; i < MAX_CHIPS; i++) {
>> +		if (gc[i])
> But you've never initialised gc[], nor parent_irq[]. So you'll get
> whatever the stack had at this point. Not great.
>
>> +			irq_destroy_generic_chip(gc[i], IRQ_MSK(32),
>> +				       IRQ_NOPROBE | IRQ_LEVEL, 0);
>> +		if (parent_irq[i])
>> +			irq_dispose_mapping(parent_irq[i]);
>> +	}
>> +	return err;
>> +}
>> +
>> +IRQCHIP_DECLARE(ls1x_intc, "loongson,ls1x-intc", ls_intc_of_init);
>> -- 
>> 2.20.1
>>
> Thanks,
>
> 	M.
>

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

* Re: [PATCH v3 1/2] irqchip: Add driver for Loongson-1 interrupt controller
  2019-01-25 10:56       ` Jiaxun Yang
@ 2019-01-26 12:09         ` Marc Zyngier
  0 siblings, 0 replies; 24+ messages in thread
From: Marc Zyngier @ 2019-01-26 12:09 UTC (permalink / raw)
  To: Jiaxun Yang
  Cc: linux-mips, tglx, jason, robh+dt, mark.rutland, linux-kernel, devicetree

On Fri, 25 Jan 2019 10:56:33 +0000,
Jiaxun Yang <jiaxun.yang@flygoat.com> wrote:
> 
> Hi Marc
> 
> Thanks for your suggestions, I'm working on v4 and I would like to
> ask if it is better to have a driver for only one irqchip and create
> dt nodes for each chip, or just register all the chips in a single
> driver with only one dt node.

It would make more sense to have a node per chip, meaning that you
end-up with one instance per chip as well. It won't make the driver
much more complicated.

[...]

> >> +	domain = irq_domain_add_legacy(node, num_chips * 32, LS1X_IRQ_BASE, 0,
> >> +		&irq_domain_simple_ops, NULL);
> > Why a legacy domain? This is usually reserved to old drivers that are
> > converted to a new infrastructure, while needing some form of platform
> > hacks. I don't see this being the case here.
> > 
> > It is also worrying that although you have up to 5 irqchips, they all
> > share a single domain. What does this mean? each irqchip is expected
> > to have its own domain.
> 
> Yes, I do like this for backward compatible reason. I'm turning
> a legacy platform device mach(arch/mips/loongson32) in to
> dt based generic mach and I would like to do it step by step rather
> than one time.
> 
> So I use legacy domain in order to keep IRQ same with the
> old driver exist on arch/mips/loongson32/common/irq.c

OK, it would have been good to make a note of that in the cover
letter, which is a bit empty at the moment.

Thanks,

	M.

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

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

* irqchip: Add driver for Loongson-1 intc v3
  2019-01-22 15:45 [PATCH 1/2] irqchip: Add driver for Loongson-1 interrupt controller Jiaxun Yang
                   ` (2 preceding siblings ...)
  2019-01-24  3:27 ` irqchip: Add driver for Loongson-1 intc v3 Jiaxun Yang
@ 2019-01-28 14:46 ` Jiaxun Yang
  2019-01-28 14:46   ` [PATCH v4 1/2] irqchip: Add driver for Loongson-1 interrupt controller Jiaxun Yang
  2019-01-28 14:46   ` [PATCH v4 2/2] dt-bindings: interrupt-controller: loongson ls1x intc Jiaxun Yang
  2019-01-29  3:04 ` irqchip: Add driver for Loongson-1 intc v5 Jiaxun Yang
  2019-02-01  6:22 ` irqchip: Add driver for Loongson-1 intc v6 Jiaxun Yang
  5 siblings, 2 replies; 24+ messages in thread
From: Jiaxun Yang @ 2019-01-28 14:46 UTC (permalink / raw)
  To: linux-mips; +Cc: tglx, jason, marc.zyngier, linux-kernel

v1->v2: Fix SPDX-License-Identifier
v2->v3: Rework according suggestions from Marc Zyngier, Thanks.
v3->v4: Rework the driver into a single chip driver.



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

* [PATCH v4 1/2] irqchip: Add driver for Loongson-1 interrupt controller
  2019-01-28 14:46 ` irqchip: Add driver for Loongson-1 intc v3 Jiaxun Yang
@ 2019-01-28 14:46   ` Jiaxun Yang
  2019-01-28 15:20     ` Marc Zyngier
  2019-01-28 14:46   ` [PATCH v4 2/2] dt-bindings: interrupt-controller: loongson ls1x intc Jiaxun Yang
  1 sibling, 1 reply; 24+ messages in thread
From: Jiaxun Yang @ 2019-01-28 14:46 UTC (permalink / raw)
  To: linux-mips; +Cc: tglx, jason, marc.zyngier, linux-kernel, Jiaxun Yang

This controller appeared on Loongson-1 family MCUs
including Loongson-1B and Loongson-1C.

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---
 drivers/irqchip/Kconfig    |   9 ++
 drivers/irqchip/Makefile   |   1 +
 drivers/irqchip/irq-ls1x.c | 196 +++++++++++++++++++++++++++++++++++++
 3 files changed, 206 insertions(+)
 create mode 100644 drivers/irqchip/irq-ls1x.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 3d1e60779078..5dcb5456cd14 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -406,6 +406,15 @@ config IMX_IRQSTEER
 	help
 	  Support for the i.MX IRQSTEER interrupt multiplexer/remapper.
 
+config LS1X_IRQ
+	bool "Loongson-1 Interrupt Controller"
+	depends on MACH_LOONGSON32
+	default y
+	select IRQ_DOMAIN
+	select GENERIC_IRQ_CHIP
+	help
+	  Support for the Loongson-1 platform Interrupt Controller.
+
 endmenu
 
 config SIFIVE_PLIC
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index c93713d24b86..7acd0e36d0b4 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -94,3 +94,4 @@ obj-$(CONFIG_CSKY_APB_INTC)		+= irq-csky-apb-intc.o
 obj-$(CONFIG_SIFIVE_PLIC)		+= irq-sifive-plic.o
 obj-$(CONFIG_IMX_IRQSTEER)		+= irq-imx-irqsteer.o
 obj-$(CONFIG_MADERA_IRQ)		+= irq-madera.o
+obj-$(CONFIG_LS1X_IRQ)			+= irq-ls1x.o
diff --git a/drivers/irqchip/irq-ls1x.c b/drivers/irqchip/irq-ls1x.c
new file mode 100644
index 000000000000..41f856b5d7b3
--- /dev/null
+++ b/drivers/irqchip/irq-ls1x.c
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Copyright (C) 2019, Jiaxun Yang <jiaxun.yang@flygoat.com>
+ *  Loongson-1 platform IRQ support
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/irqchip.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/io.h>
+#include <linux/irqchip/chained_irq.h>
+
+#define LS_REG_INTC_STATUS	0x00
+#define LS_REG_INTC_EN	0x04
+#define LS_REG_INTC_SET	0x08
+#define LS_REG_INTC_CLR	0x0c
+#define LS_REG_INTC_POL	0x10
+#define LS_REG_INTC_EDGE	0x14
+
+/**
+ * struct ls1x_intc_priv - private ls1x-intc data.
+ * @domain:		IRQ domain.
+ * @intc_base:	IO Base of intc registers.
+ */
+
+struct ls1x_intc_priv {
+	struct irq_domain	*domain;
+	void __iomem		*intc_base;
+};
+
+
+static void ls1x_chained_handle_irq(struct irq_desc *desc)
+{
+	struct ls1x_intc_priv *priv = irq_desc_get_handler_data(desc);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	u32 pending;
+
+	chained_irq_enter(chip, desc);
+	pending = readl(priv->intc_base + LS_REG_INTC_STATUS) &
+			readl(priv->intc_base + LS_REG_INTC_EN);
+
+	if (!pending) {
+		spurious_interrupt();
+		chained_irq_exit(chip, desc);
+		return;
+	}
+
+	while (pending) {
+		int bit = __ffs(pending);
+
+		generic_handle_irq(irq_find_mapping(priv->domain, bit));
+		pending &= ~BIT(bit);
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static void ls_intc_set_bit(struct irq_chip_generic *gc,
+							unsigned int offset,
+							u32 mask, bool set)
+{
+	if (set)
+		writel(readl(gc->reg_base + offset) | mask,
+		gc->reg_base + offset);
+	else
+		writel(readl(gc->reg_base + offset) & ~mask,
+		gc->reg_base + offset);
+}
+
+static int ls_intc_set_type(struct irq_data *data, unsigned int type)
+{
+	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
+	u32 mask = data->mask;
+
+	switch (type) {
+	case IRQ_TYPE_LEVEL_HIGH:
+		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, false);
+		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, true);
+		break;
+	case IRQ_TYPE_LEVEL_LOW:
+		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, false);
+		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, false);
+		break;
+	case IRQ_TYPE_EDGE_RISING:
+		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, true);
+		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, true);
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, true);
+		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, false);
+		break;
+	case IRQ_TYPE_NONE:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	irqd_set_trigger_type(data, type);
+	return irq_setup_alt_chip(data, type);
+}
+
+
+static int __init ls1x_intc_of_init(struct device_node *node,
+				       struct device_node *parent)
+{
+	struct irq_chip_generic *gc;
+	struct irq_chip_type *ct;
+	struct ls1x_intc_priv *priv;
+	int parent_irq, err = 0;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->intc_base = of_iomap(node, 0);
+	if (!priv->intc_base) {
+		err = -ENODEV;
+		goto out_free_priv;
+	}
+
+	parent_irq = irq_of_parse_and_map(node, 0);
+	if (!parent_irq) {
+		pr_err("ls1x-irq: unable to get parent irq\n");
+		err =  -ENODEV;
+		goto out_free_priv;
+	}
+
+	/* Set up an IRQ domain */
+	priv->domain = irq_domain_add_linear(node, 32, &irq_generic_chip_ops,
+					     NULL);
+	if (!priv->domain) {
+		pr_err("ls1x-irq: cannot add IRQ domain\n");
+		goto out_free_priv;
+	}
+
+
+	err = irq_alloc_domain_generic_chips(priv->domain, 32, 2,
+		node->full_name, handle_level_irq,
+		IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN, 0,
+		IRQ_GC_INIT_MASK_CACHE);
+	if (err) {
+		pr_err("ls1x-irq: unable to register IRQ domain\n");
+		goto out_free_domain;
+	}
+
+	/* Mask all irqs */
+	writel(0x0, priv->intc_base + LS_REG_INTC_EN);
+
+	/* Ack all irqs */
+	writel(0xffffffff, priv->intc_base + LS_REG_INTC_CLR);
+
+	/* Set all irqs to high level triggered */
+	writel(0xffffffff, priv->intc_base + LS_REG_INTC_POL);
+
+	gc = irq_get_domain_generic_chip(priv->domain, 0);
+
+	gc->reg_base = priv->intc_base;
+
+	ct = gc->chip_types;
+	ct[0].type = IRQ_TYPE_LEVEL_MASK;
+	ct[0].regs.mask = LS_REG_INTC_EN;
+	ct[0].regs.ack = LS_REG_INTC_CLR;
+	ct[0].chip.irq_unmask = irq_gc_mask_set_bit;
+	ct[0].chip.irq_mask = irq_gc_mask_clr_bit;
+	ct[0].chip.irq_ack = irq_gc_ack_set_bit;
+	ct[0].chip.irq_set_type = ls_intc_set_type;
+	ct[0].handler = handle_level_irq;
+
+	ct[1].type = IRQ_TYPE_EDGE_BOTH;
+	ct[1].regs.mask = LS_REG_INTC_EN;
+	ct[1].regs.ack = LS_REG_INTC_CLR;
+	ct[1].chip.irq_unmask = irq_gc_mask_set_bit;
+	ct[1].chip.irq_mask = irq_gc_mask_clr_bit;
+	ct[1].chip.irq_ack = irq_gc_ack_set_bit;
+	ct[1].chip.irq_set_type = ls_intc_set_type;
+	ct[1].handler = handle_edge_irq;
+
+
+	irq_set_chained_handler_and_data(parent_irq,
+		ls1x_chained_handle_irq, priv);
+
+	return 0;
+
+out_free_domain:
+	irq_domain_remove(priv->domain);
+out_free_priv:
+	kfree(priv);
+	return err;
+}
+
+IRQCHIP_DECLARE(ls1x_intc, "loongson,ls1x-intc", ls1x_intc_of_init);
-- 
2.20.1


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

* [PATCH v4 2/2] dt-bindings: interrupt-controller: loongson ls1x intc
  2019-01-28 14:46 ` irqchip: Add driver for Loongson-1 intc v3 Jiaxun Yang
  2019-01-28 14:46   ` [PATCH v4 1/2] irqchip: Add driver for Loongson-1 interrupt controller Jiaxun Yang
@ 2019-01-28 14:46   ` Jiaxun Yang
  1 sibling, 0 replies; 24+ messages in thread
From: Jiaxun Yang @ 2019-01-28 14:46 UTC (permalink / raw)
  To: linux-mips; +Cc: tglx, jason, marc.zyngier, linux-kernel, Jiaxun Yang

Dt-bindings doc about Loongson-1 interrupt controller.

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

diff --git a/Documentation/devicetree/bindings/interrupt-controller/loongson,ls1x-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/loongson,ls1x-intc.txt
new file mode 100644
index 000000000000..a63ed9fcb535
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/loongson,ls1x-intc.txt
@@ -0,0 +1,24 @@
+Loongson ls1x Interrupt Controller
+
+Required properties:
+
+- compatible : should be "loongson,ls1x-intc". Valid strings are:
+
+- reg : Specifies base physical address and size of the registers.
+- interrupt-controller : Identifies the node as an interrupt controller
+- #interrupt-cells : Specifies the number of cells needed to encode an
+  interrupt source. The value shall be 2.
+- interrupts : Specifies the CPU interrupt the controller is connected to.
+
+Example:
+
+intc: interrupt-controller@1fd01040 {
+	compatible = "loongson,ls1x-intc";
+	reg = <0x1fd01040 0x18>;
+
+	interrupt-controller;
+	#interrupt-cells = <2>;
+
+	interrupt-parent = <&cpu_intc>;
+	interrupts = <2>;
+};
-- 
2.20.1


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

* Re: [PATCH v4 1/2] irqchip: Add driver for Loongson-1 interrupt controller
  2019-01-28 14:46   ` [PATCH v4 1/2] irqchip: Add driver for Loongson-1 interrupt controller Jiaxun Yang
@ 2019-01-28 15:20     ` Marc Zyngier
  0 siblings, 0 replies; 24+ messages in thread
From: Marc Zyngier @ 2019-01-28 15:20 UTC (permalink / raw)
  To: Jiaxun Yang, linux-mips; +Cc: tglx, jason, linux-kernel

On 28/01/2019 14:46, Jiaxun Yang wrote:
> This controller appeared on Loongson-1 family MCUs
> including Loongson-1B and Loongson-1C.
> 
> Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
> ---
>  drivers/irqchip/Kconfig    |   9 ++
>  drivers/irqchip/Makefile   |   1 +
>  drivers/irqchip/irq-ls1x.c | 196 +++++++++++++++++++++++++++++++++++++
>  3 files changed, 206 insertions(+)
>  create mode 100644 drivers/irqchip/irq-ls1x.c
> 
> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> index 3d1e60779078..5dcb5456cd14 100644
> --- a/drivers/irqchip/Kconfig
> +++ b/drivers/irqchip/Kconfig
> @@ -406,6 +406,15 @@ config IMX_IRQSTEER
>  	help
>  	  Support for the i.MX IRQSTEER interrupt multiplexer/remapper.
>  
> +config LS1X_IRQ
> +	bool "Loongson-1 Interrupt Controller"
> +	depends on MACH_LOONGSON32
> +	default y
> +	select IRQ_DOMAIN
> +	select GENERIC_IRQ_CHIP
> +	help
> +	  Support for the Loongson-1 platform Interrupt Controller.
> +
>  endmenu
>  
>  config SIFIVE_PLIC
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index c93713d24b86..7acd0e36d0b4 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -94,3 +94,4 @@ obj-$(CONFIG_CSKY_APB_INTC)		+= irq-csky-apb-intc.o
>  obj-$(CONFIG_SIFIVE_PLIC)		+= irq-sifive-plic.o
>  obj-$(CONFIG_IMX_IRQSTEER)		+= irq-imx-irqsteer.o
>  obj-$(CONFIG_MADERA_IRQ)		+= irq-madera.o
> +obj-$(CONFIG_LS1X_IRQ)			+= irq-ls1x.o
> diff --git a/drivers/irqchip/irq-ls1x.c b/drivers/irqchip/irq-ls1x.c
> new file mode 100644
> index 000000000000..41f856b5d7b3
> --- /dev/null
> +++ b/drivers/irqchip/irq-ls1x.c
> @@ -0,0 +1,196 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + *  Copyright (C) 2019, Jiaxun Yang <jiaxun.yang@flygoat.com>
> + *  Loongson-1 platform IRQ support
> + */
> +
> +#include <linux/errno.h>
> +#include <linux/init.h>
> +#include <linux/types.h>
> +#include <linux/interrupt.h>
> +#include <linux/ioport.h>
> +#include <linux/irqchip.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/io.h>
> +#include <linux/irqchip/chained_irq.h>
> +
> +#define LS_REG_INTC_STATUS	0x00
> +#define LS_REG_INTC_EN	0x04
> +#define LS_REG_INTC_SET	0x08
> +#define LS_REG_INTC_CLR	0x0c
> +#define LS_REG_INTC_POL	0x10
> +#define LS_REG_INTC_EDGE	0x14
> +
> +/**
> + * struct ls1x_intc_priv - private ls1x-intc data.
> + * @domain:		IRQ domain.
> + * @intc_base:	IO Base of intc registers.
> + */
> +
> +struct ls1x_intc_priv {
> +	struct irq_domain	*domain;
> +	void __iomem		*intc_base;
> +};
> +
> +
> +static void ls1x_chained_handle_irq(struct irq_desc *desc)
> +{
> +	struct ls1x_intc_priv *priv = irq_desc_get_handler_data(desc);
> +	struct irq_chip *chip = irq_desc_get_chip(desc);
> +	u32 pending;
> +
> +	chained_irq_enter(chip, desc);
> +	pending = readl(priv->intc_base + LS_REG_INTC_STATUS) &
> +			readl(priv->intc_base + LS_REG_INTC_EN);
> +
> +	if (!pending) {
> +		spurious_interrupt();
> +		chained_irq_exit(chip, desc);
> +		return;
> +	}

As I wrote previously, this can be simplified.

> +
> +	while (pending) {
> +		int bit = __ffs(pending);
> +
> +		generic_handle_irq(irq_find_mapping(priv->domain, bit));
> +		pending &= ~BIT(bit);
> +	}
> +
> +	chained_irq_exit(chip, desc);
> +}
> +
> +static void ls_intc_set_bit(struct irq_chip_generic *gc,
> +							unsigned int offset,
> +							u32 mask, bool set)
> +{
> +	if (set)
> +		writel(readl(gc->reg_base + offset) | mask,
> +		gc->reg_base + offset);
> +	else
> +		writel(readl(gc->reg_base + offset) & ~mask,
> +		gc->reg_base + offset);
> +}
> +
> +static int ls_intc_set_type(struct irq_data *data, unsigned int type)
> +{
> +	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
> +	u32 mask = data->mask;
> +
> +	switch (type) {
> +	case IRQ_TYPE_LEVEL_HIGH:
> +		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, false);
> +		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, true);
> +		break;
> +	case IRQ_TYPE_LEVEL_LOW:
> +		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, false);
> +		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, false);
> +		break;
> +	case IRQ_TYPE_EDGE_RISING:
> +		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, true);
> +		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, true);
> +		break;
> +	case IRQ_TYPE_EDGE_FALLING:
> +		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, true);
> +		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, false);
> +		break;
> +	case IRQ_TYPE_NONE:
> +		break;

What does this mean? I can't see how that can lean to anything valid.

> +	default:
> +		return -EINVAL;
> +	}
> +
> +	irqd_set_trigger_type(data, type);
> +	return irq_setup_alt_chip(data, type);
> +}
> +
> +
> +static int __init ls1x_intc_of_init(struct device_node *node,
> +				       struct device_node *parent)
> +{
> +	struct irq_chip_generic *gc;
> +	struct irq_chip_type *ct;
> +	struct ls1x_intc_priv *priv;
> +	int parent_irq, err = 0;
> +
> +	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->intc_base = of_iomap(node, 0);
> +	if (!priv->intc_base) {
> +		err = -ENODEV;
> +		goto out_free_priv;
> +	}
> +
> +	parent_irq = irq_of_parse_and_map(node, 0);
> +	if (!parent_irq) {
> +		pr_err("ls1x-irq: unable to get parent irq\n");
> +		err =  -ENODEV;
> +		goto out_free_priv;

This is leaking the MMIO mapping.

> +	}
> +
> +	/* Set up an IRQ domain */
> +	priv->domain = irq_domain_add_linear(node, 32, &irq_generic_chip_ops,
> +					     NULL);
> +	if (!priv->domain) {
> +		pr_err("ls1x-irq: cannot add IRQ domain\n");
> +		goto out_free_priv;
> +	}
> +
> +

Spurious empty line.

> +	err = irq_alloc_domain_generic_chips(priv->domain, 32, 2,
> +		node->full_name, handle_level_irq,
> +		IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN, 0,
> +		IRQ_GC_INIT_MASK_CACHE);
> +	if (err) {
> +		pr_err("ls1x-irq: unable to register IRQ domain\n");
> +		goto out_free_domain;
> +	}
> +
> +	/* Mask all irqs */
> +	writel(0x0, priv->intc_base + LS_REG_INTC_EN);
> +
> +	/* Ack all irqs */
> +	writel(0xffffffff, priv->intc_base + LS_REG_INTC_CLR);
> +
> +	/* Set all irqs to high level triggered */
> +	writel(0xffffffff, priv->intc_base + LS_REG_INTC_POL);
> +
> +	gc = irq_get_domain_generic_chip(priv->domain, 0);
> +
> +	gc->reg_base = priv->intc_base;
> +
> +	ct = gc->chip_types;
> +	ct[0].type = IRQ_TYPE_LEVEL_MASK;
> +	ct[0].regs.mask = LS_REG_INTC_EN;
> +	ct[0].regs.ack = LS_REG_INTC_CLR;
> +	ct[0].chip.irq_unmask = irq_gc_mask_set_bit;
> +	ct[0].chip.irq_mask = irq_gc_mask_clr_bit;
> +	ct[0].chip.irq_ack = irq_gc_ack_set_bit;
> +	ct[0].chip.irq_set_type = ls_intc_set_type;
> +	ct[0].handler = handle_level_irq;
> +
> +	ct[1].type = IRQ_TYPE_EDGE_BOTH;
> +	ct[1].regs.mask = LS_REG_INTC_EN;
> +	ct[1].regs.ack = LS_REG_INTC_CLR;
> +	ct[1].chip.irq_unmask = irq_gc_mask_set_bit;
> +	ct[1].chip.irq_mask = irq_gc_mask_clr_bit;
> +	ct[1].chip.irq_ack = irq_gc_ack_set_bit;
> +	ct[1].chip.irq_set_type = ls_intc_set_type;
> +	ct[1].handler = handle_edge_irq;
> +
> +
> +	irq_set_chained_handler_and_data(parent_irq,
> +		ls1x_chained_handle_irq, priv);
> +
> +	return 0;
> +
> +out_free_domain:
> +	irq_domain_remove(priv->domain);
> +out_free_priv:
> +	kfree(priv);
> +	return err;
> +}
> +
> +IRQCHIP_DECLARE(ls1x_intc, "loongson,ls1x-intc", ls1x_intc_of_init);
> 

Thanks,

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

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

* irqchip: Add driver for Loongson-1 intc v5
  2019-01-22 15:45 [PATCH 1/2] irqchip: Add driver for Loongson-1 interrupt controller Jiaxun Yang
                   ` (3 preceding siblings ...)
  2019-01-28 14:46 ` irqchip: Add driver for Loongson-1 intc v3 Jiaxun Yang
@ 2019-01-29  3:04 ` Jiaxun Yang
  2019-01-29  3:05   ` [PATCH v5 1/2] irqchip: Add driver for Loongson-1 interrupt controller Jiaxun Yang
  2019-01-29  3:05   ` [PATCH v5 2/2] dt-bindings: interrupt-controller: loongson ls1x intc Jiaxun Yang
  2019-02-01  6:22 ` irqchip: Add driver for Loongson-1 intc v6 Jiaxun Yang
  5 siblings, 2 replies; 24+ messages in thread
From: Jiaxun Yang @ 2019-01-29  3:04 UTC (permalink / raw)
  To: linux-mips; +Cc: tglx, jason, marc.zyngier, linux-kernel

v1->v2: Fix SPDX-License-Identifier
v2->v3: Rework according suggestions from Marc Zyngier, Thanks.
v3->v4: Rework the driver into a single chip driver.
v4->v5: Fix minor issues.



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

* [PATCH v5 1/2] irqchip: Add driver for Loongson-1 interrupt controller
  2019-01-29  3:04 ` irqchip: Add driver for Loongson-1 intc v5 Jiaxun Yang
@ 2019-01-29  3:05   ` Jiaxun Yang
  2019-01-29  3:05   ` [PATCH v5 2/2] dt-bindings: interrupt-controller: loongson ls1x intc Jiaxun Yang
  1 sibling, 0 replies; 24+ messages in thread
From: Jiaxun Yang @ 2019-01-29  3:05 UTC (permalink / raw)
  To: linux-mips; +Cc: tglx, jason, marc.zyngier, linux-kernel, Jiaxun Yang

This controller appeared on Loongson-1 family MCUs
including Loongson-1B and Loongson-1C.

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---
 drivers/irqchip/Kconfig    |   9 ++
 drivers/irqchip/Makefile   |   1 +
 drivers/irqchip/irq-ls1x.c | 192 +++++++++++++++++++++++++++++++++++++
 3 files changed, 202 insertions(+)
 create mode 100644 drivers/irqchip/irq-ls1x.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 3d1e60779078..5dcb5456cd14 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -406,6 +406,15 @@ config IMX_IRQSTEER
 	help
 	  Support for the i.MX IRQSTEER interrupt multiplexer/remapper.
 
+config LS1X_IRQ
+	bool "Loongson-1 Interrupt Controller"
+	depends on MACH_LOONGSON32
+	default y
+	select IRQ_DOMAIN
+	select GENERIC_IRQ_CHIP
+	help
+	  Support for the Loongson-1 platform Interrupt Controller.
+
 endmenu
 
 config SIFIVE_PLIC
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index c93713d24b86..7acd0e36d0b4 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -94,3 +94,4 @@ obj-$(CONFIG_CSKY_APB_INTC)		+= irq-csky-apb-intc.o
 obj-$(CONFIG_SIFIVE_PLIC)		+= irq-sifive-plic.o
 obj-$(CONFIG_IMX_IRQSTEER)		+= irq-imx-irqsteer.o
 obj-$(CONFIG_MADERA_IRQ)		+= irq-madera.o
+obj-$(CONFIG_LS1X_IRQ)			+= irq-ls1x.o
diff --git a/drivers/irqchip/irq-ls1x.c b/drivers/irqchip/irq-ls1x.c
new file mode 100644
index 000000000000..79967d8df53e
--- /dev/null
+++ b/drivers/irqchip/irq-ls1x.c
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Copyright (C) 2019, Jiaxun Yang <jiaxun.yang@flygoat.com>
+ *  Loongson-1 platform IRQ support
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/irqchip.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/io.h>
+#include <linux/irqchip/chained_irq.h>
+
+#define LS_REG_INTC_STATUS	0x00
+#define LS_REG_INTC_EN	0x04
+#define LS_REG_INTC_SET	0x08
+#define LS_REG_INTC_CLR	0x0c
+#define LS_REG_INTC_POL	0x10
+#define LS_REG_INTC_EDGE	0x14
+
+/**
+ * struct ls1x_intc_priv - private ls1x-intc data.
+ * @domain:		IRQ domain.
+ * @intc_base:	IO Base of intc registers.
+ */
+
+struct ls1x_intc_priv {
+	struct irq_domain	*domain;
+	void __iomem		*intc_base;
+};
+
+
+static void ls1x_chained_handle_irq(struct irq_desc *desc)
+{
+	struct ls1x_intc_priv *priv = irq_desc_get_handler_data(desc);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	u32 pending;
+
+	chained_irq_enter(chip, desc);
+	pending = readl(priv->intc_base + LS_REG_INTC_STATUS) &
+			readl(priv->intc_base + LS_REG_INTC_EN);
+
+	if (!pending)
+		spurious_interrupt();
+
+	while (pending) {
+		int bit = __ffs(pending);
+
+		generic_handle_irq(irq_find_mapping(priv->domain, bit));
+		pending &= ~BIT(bit);
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static void ls_intc_set_bit(struct irq_chip_generic *gc,
+							unsigned int offset,
+							u32 mask, bool set)
+{
+	if (set)
+		writel(readl(gc->reg_base + offset) | mask,
+		gc->reg_base + offset);
+	else
+		writel(readl(gc->reg_base + offset) & ~mask,
+		gc->reg_base + offset);
+}
+
+static int ls_intc_set_type(struct irq_data *data, unsigned int type)
+{
+	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
+	u32 mask = data->mask;
+
+	switch (type) {
+	case IRQ_TYPE_LEVEL_HIGH:
+		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, false);
+		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, true);
+		break;
+	case IRQ_TYPE_LEVEL_LOW:
+		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, false);
+		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, false);
+		break;
+	case IRQ_TYPE_EDGE_RISING:
+		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, true);
+		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, true);
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, true);
+		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, false);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	irqd_set_trigger_type(data, type);
+	return irq_setup_alt_chip(data, type);
+}
+
+
+static int __init ls1x_intc_of_init(struct device_node *node,
+				       struct device_node *parent)
+{
+	struct irq_chip_generic *gc;
+	struct irq_chip_type *ct;
+	struct ls1x_intc_priv *priv;
+	int parent_irq, err = 0;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->intc_base = of_iomap(node, 0);
+	if (!priv->intc_base) {
+		err = -ENODEV;
+		goto out_free_priv;
+	}
+
+	parent_irq = irq_of_parse_and_map(node, 0);
+	if (!parent_irq) {
+		pr_err("ls1x-irq: unable to get parent irq\n");
+		err =  -ENODEV;
+		goto out_iounmap;
+	}
+
+	/* Set up an IRQ domain */
+	priv->domain = irq_domain_add_linear(node, 32, &irq_generic_chip_ops,
+					     NULL);
+	if (!priv->domain) {
+		pr_err("ls1x-irq: cannot add IRQ domain\n");
+		goto out_iounmap;
+	}
+
+	err = irq_alloc_domain_generic_chips(priv->domain, 32, 2,
+		node->full_name, handle_level_irq,
+		IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN, 0,
+		IRQ_GC_INIT_MASK_CACHE);
+	if (err) {
+		pr_err("ls1x-irq: unable to register IRQ domain\n");
+		goto out_free_domain;
+	}
+
+	/* Mask all irqs */
+	writel(0x0, priv->intc_base + LS_REG_INTC_EN);
+
+	/* Ack all irqs */
+	writel(0xffffffff, priv->intc_base + LS_REG_INTC_CLR);
+
+	/* Set all irqs to high level triggered */
+	writel(0xffffffff, priv->intc_base + LS_REG_INTC_POL);
+
+	gc = irq_get_domain_generic_chip(priv->domain, 0);
+
+	gc->reg_base = priv->intc_base;
+
+	ct = gc->chip_types;
+	ct[0].type = IRQ_TYPE_LEVEL_MASK;
+	ct[0].regs.mask = LS_REG_INTC_EN;
+	ct[0].regs.ack = LS_REG_INTC_CLR;
+	ct[0].chip.irq_unmask = irq_gc_mask_set_bit;
+	ct[0].chip.irq_mask = irq_gc_mask_clr_bit;
+	ct[0].chip.irq_ack = irq_gc_ack_set_bit;
+	ct[0].chip.irq_set_type = ls_intc_set_type;
+	ct[0].handler = handle_level_irq;
+
+	ct[1].type = IRQ_TYPE_EDGE_BOTH;
+	ct[1].regs.mask = LS_REG_INTC_EN;
+	ct[1].regs.ack = LS_REG_INTC_CLR;
+	ct[1].chip.irq_unmask = irq_gc_mask_set_bit;
+	ct[1].chip.irq_mask = irq_gc_mask_clr_bit;
+	ct[1].chip.irq_ack = irq_gc_ack_set_bit;
+	ct[1].chip.irq_set_type = ls_intc_set_type;
+	ct[1].handler = handle_edge_irq;
+
+	irq_set_chained_handler_and_data(parent_irq,
+		ls1x_chained_handle_irq, priv);
+
+	return 0;
+
+out_free_domain:
+	irq_domain_remove(priv->domain);
+out_iounmap:
+	iounmap(priv->intc_base);
+out_free_priv:
+	kfree(priv);
+	
+	return err;
+}
+
+IRQCHIP_DECLARE(ls1x_intc, "loongson,ls1x-intc", ls1x_intc_of_init);
-- 
2.20.1


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

* [PATCH v5 2/2] dt-bindings: interrupt-controller: loongson ls1x intc
  2019-01-29  3:04 ` irqchip: Add driver for Loongson-1 intc v5 Jiaxun Yang
  2019-01-29  3:05   ` [PATCH v5 1/2] irqchip: Add driver for Loongson-1 interrupt controller Jiaxun Yang
@ 2019-01-29  3:05   ` Jiaxun Yang
  1 sibling, 0 replies; 24+ messages in thread
From: Jiaxun Yang @ 2019-01-29  3:05 UTC (permalink / raw)
  To: linux-mips; +Cc: tglx, jason, marc.zyngier, linux-kernel, Jiaxun Yang

Dt-bindings doc about Loongson-1 interrupt controller.

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

diff --git a/Documentation/devicetree/bindings/interrupt-controller/loongson,ls1x-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/loongson,ls1x-intc.txt
new file mode 100644
index 000000000000..a63ed9fcb535
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/loongson,ls1x-intc.txt
@@ -0,0 +1,24 @@
+Loongson ls1x Interrupt Controller
+
+Required properties:
+
+- compatible : should be "loongson,ls1x-intc". Valid strings are:
+
+- reg : Specifies base physical address and size of the registers.
+- interrupt-controller : Identifies the node as an interrupt controller
+- #interrupt-cells : Specifies the number of cells needed to encode an
+  interrupt source. The value shall be 2.
+- interrupts : Specifies the CPU interrupt the controller is connected to.
+
+Example:
+
+intc: interrupt-controller@1fd01040 {
+	compatible = "loongson,ls1x-intc";
+	reg = <0x1fd01040 0x18>;
+
+	interrupt-controller;
+	#interrupt-cells = <2>;
+
+	interrupt-parent = <&cpu_intc>;
+	interrupts = <2>;
+};
-- 
2.20.1


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

* Re: [PATCH v3 2/2] dt-bindings: interrupt-controller: loongson ls1x intc
  2019-01-24  3:27   ` [PATCH v3 2/2] dt-bindings: interrupt-controller: loongson ls1x intc Jiaxun Yang
@ 2019-01-30 16:41     ` Rob Herring
  0 siblings, 0 replies; 24+ messages in thread
From: Rob Herring @ 2019-01-30 16:41 UTC (permalink / raw)
  To: Jiaxun Yang
  Cc: linux-mips, tglx, jason, marc.zyngier, mark.rutland,
	linux-kernel, devicetree

On Thu, Jan 24, 2019 at 11:27:30AM +0800, Jiaxun Yang wrote:
> Dt-bindings doc about Loongson-1 interrupt controller
> 
> Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
> ---
>  .../loongson,ls1x-intc.txt                    | 24 +++++++++++++++++++
>  1 file changed, 24 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/loongson,ls1x-intc.txt
> 
> diff --git a/Documentation/devicetree/bindings/interrupt-controller/loongson,ls1x-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/loongson,ls1x-intc.txt
> new file mode 100644
> index 000000000000..a4e17c3f5f93
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/interrupt-controller/loongson,ls1x-intc.txt
> @@ -0,0 +1,24 @@
> +Loongson ls1x Interrupt Controller
> +
> +Required properties:
> +
> +- compatible : should be "loongson,ls1x-intc". Valid strings are:
> +
> +- reg : Specifies base physical address and size of the registers.
> +- interrupt-controller : Identifies the node as an interrupt controller
> +- #interrupt-cells : Specifies the number of cells needed to encode an
> +  interrupt source. The value shall be 1.
> +- interrupts : Specifies the CPU interrupts the controller is connected to.
> +
> +Example:
> +
> +intc: interrupt-controller@1fd01040 {
> +	compatible = "loongson,ls1x-intc";
> +	reg = <0x1fd01040 0x78>;
> +
> +	interrupt-controller;
> +	#interrupt-cells = <1>;
> +
> +	interrupt-parent = <&cpu_intc>;
> +	interrupts = <2>, <3>, <4>, <5>, <6>;
> +};
> \ No newline at end of file

Please fix.

Otherwise,

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

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

* irqchip: Add driver for Loongson-1 intc v6
  2019-01-22 15:45 [PATCH 1/2] irqchip: Add driver for Loongson-1 interrupt controller Jiaxun Yang
                   ` (4 preceding siblings ...)
  2019-01-29  3:04 ` irqchip: Add driver for Loongson-1 intc v5 Jiaxun Yang
@ 2019-02-01  6:22 ` Jiaxun Yang
  2019-02-01  6:22   ` [PATCH v6 1/2] irqchip: Add driver for Loongson-1 interrupt controller Jiaxun Yang
                     ` (2 more replies)
  5 siblings, 3 replies; 24+ messages in thread
From: Jiaxun Yang @ 2019-02-01  6:22 UTC (permalink / raw)
  To: linux-mips; +Cc: tglx, jason, marc.zyngier, linux-kernel, robh+dt, devicetree

v1->v2: Fix SPDX-License-Identifier
v2->v3: Rework according suggestions from Marc Zyngier, Thanks.
v3->v4: Rework the driver into a single chip driver.
v4->v5: Fix minor issues.
v5->v6: Fix doc and collect Rob's review tag.



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

* [PATCH v6 1/2] irqchip: Add driver for Loongson-1 interrupt controller
  2019-02-01  6:22 ` irqchip: Add driver for Loongson-1 intc v6 Jiaxun Yang
@ 2019-02-01  6:22   ` Jiaxun Yang
  2019-02-01  6:22   ` [PATCH v6 2/2] dt-bindings: interrupt-controller: loongson ls1x intc Jiaxun Yang
  2019-02-14 10:36   ` irqchip: Add driver for Loongson-1 intc v6 Marc Zyngier
  2 siblings, 0 replies; 24+ messages in thread
From: Jiaxun Yang @ 2019-02-01  6:22 UTC (permalink / raw)
  To: linux-mips
  Cc: tglx, jason, marc.zyngier, linux-kernel, robh+dt, devicetree,
	Jiaxun Yang

This controller appeared on Loongson-1 family MCUs
including Loongson-1B and Loongson-1C.

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---
 drivers/irqchip/Kconfig    |   9 ++
 drivers/irqchip/Makefile   |   1 +
 drivers/irqchip/irq-ls1x.c | 192 +++++++++++++++++++++++++++++++++++++
 3 files changed, 202 insertions(+)
 create mode 100644 drivers/irqchip/irq-ls1x.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 3d1e60779078..5dcb5456cd14 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -406,6 +406,15 @@ config IMX_IRQSTEER
 	help
 	  Support for the i.MX IRQSTEER interrupt multiplexer/remapper.
 
+config LS1X_IRQ
+	bool "Loongson-1 Interrupt Controller"
+	depends on MACH_LOONGSON32
+	default y
+	select IRQ_DOMAIN
+	select GENERIC_IRQ_CHIP
+	help
+	  Support for the Loongson-1 platform Interrupt Controller.
+
 endmenu
 
 config SIFIVE_PLIC
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index c93713d24b86..7acd0e36d0b4 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -94,3 +94,4 @@ obj-$(CONFIG_CSKY_APB_INTC)		+= irq-csky-apb-intc.o
 obj-$(CONFIG_SIFIVE_PLIC)		+= irq-sifive-plic.o
 obj-$(CONFIG_IMX_IRQSTEER)		+= irq-imx-irqsteer.o
 obj-$(CONFIG_MADERA_IRQ)		+= irq-madera.o
+obj-$(CONFIG_LS1X_IRQ)			+= irq-ls1x.o
diff --git a/drivers/irqchip/irq-ls1x.c b/drivers/irqchip/irq-ls1x.c
new file mode 100644
index 000000000000..79967d8df53e
--- /dev/null
+++ b/drivers/irqchip/irq-ls1x.c
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Copyright (C) 2019, Jiaxun Yang <jiaxun.yang@flygoat.com>
+ *  Loongson-1 platform IRQ support
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/irqchip.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/io.h>
+#include <linux/irqchip/chained_irq.h>
+
+#define LS_REG_INTC_STATUS	0x00
+#define LS_REG_INTC_EN	0x04
+#define LS_REG_INTC_SET	0x08
+#define LS_REG_INTC_CLR	0x0c
+#define LS_REG_INTC_POL	0x10
+#define LS_REG_INTC_EDGE	0x14
+
+/**
+ * struct ls1x_intc_priv - private ls1x-intc data.
+ * @domain:		IRQ domain.
+ * @intc_base:	IO Base of intc registers.
+ */
+
+struct ls1x_intc_priv {
+	struct irq_domain	*domain;
+	void __iomem		*intc_base;
+};
+
+
+static void ls1x_chained_handle_irq(struct irq_desc *desc)
+{
+	struct ls1x_intc_priv *priv = irq_desc_get_handler_data(desc);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	u32 pending;
+
+	chained_irq_enter(chip, desc);
+	pending = readl(priv->intc_base + LS_REG_INTC_STATUS) &
+			readl(priv->intc_base + LS_REG_INTC_EN);
+
+	if (!pending)
+		spurious_interrupt();
+
+	while (pending) {
+		int bit = __ffs(pending);
+
+		generic_handle_irq(irq_find_mapping(priv->domain, bit));
+		pending &= ~BIT(bit);
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static void ls_intc_set_bit(struct irq_chip_generic *gc,
+							unsigned int offset,
+							u32 mask, bool set)
+{
+	if (set)
+		writel(readl(gc->reg_base + offset) | mask,
+		gc->reg_base + offset);
+	else
+		writel(readl(gc->reg_base + offset) & ~mask,
+		gc->reg_base + offset);
+}
+
+static int ls_intc_set_type(struct irq_data *data, unsigned int type)
+{
+	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
+	u32 mask = data->mask;
+
+	switch (type) {
+	case IRQ_TYPE_LEVEL_HIGH:
+		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, false);
+		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, true);
+		break;
+	case IRQ_TYPE_LEVEL_LOW:
+		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, false);
+		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, false);
+		break;
+	case IRQ_TYPE_EDGE_RISING:
+		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, true);
+		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, true);
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, true);
+		ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, false);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	irqd_set_trigger_type(data, type);
+	return irq_setup_alt_chip(data, type);
+}
+
+
+static int __init ls1x_intc_of_init(struct device_node *node,
+				       struct device_node *parent)
+{
+	struct irq_chip_generic *gc;
+	struct irq_chip_type *ct;
+	struct ls1x_intc_priv *priv;
+	int parent_irq, err = 0;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->intc_base = of_iomap(node, 0);
+	if (!priv->intc_base) {
+		err = -ENODEV;
+		goto out_free_priv;
+	}
+
+	parent_irq = irq_of_parse_and_map(node, 0);
+	if (!parent_irq) {
+		pr_err("ls1x-irq: unable to get parent irq\n");
+		err =  -ENODEV;
+		goto out_iounmap;
+	}
+
+	/* Set up an IRQ domain */
+	priv->domain = irq_domain_add_linear(node, 32, &irq_generic_chip_ops,
+					     NULL);
+	if (!priv->domain) {
+		pr_err("ls1x-irq: cannot add IRQ domain\n");
+		goto out_iounmap;
+	}
+
+	err = irq_alloc_domain_generic_chips(priv->domain, 32, 2,
+		node->full_name, handle_level_irq,
+		IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN, 0,
+		IRQ_GC_INIT_MASK_CACHE);
+	if (err) {
+		pr_err("ls1x-irq: unable to register IRQ domain\n");
+		goto out_free_domain;
+	}
+
+	/* Mask all irqs */
+	writel(0x0, priv->intc_base + LS_REG_INTC_EN);
+
+	/* Ack all irqs */
+	writel(0xffffffff, priv->intc_base + LS_REG_INTC_CLR);
+
+	/* Set all irqs to high level triggered */
+	writel(0xffffffff, priv->intc_base + LS_REG_INTC_POL);
+
+	gc = irq_get_domain_generic_chip(priv->domain, 0);
+
+	gc->reg_base = priv->intc_base;
+
+	ct = gc->chip_types;
+	ct[0].type = IRQ_TYPE_LEVEL_MASK;
+	ct[0].regs.mask = LS_REG_INTC_EN;
+	ct[0].regs.ack = LS_REG_INTC_CLR;
+	ct[0].chip.irq_unmask = irq_gc_mask_set_bit;
+	ct[0].chip.irq_mask = irq_gc_mask_clr_bit;
+	ct[0].chip.irq_ack = irq_gc_ack_set_bit;
+	ct[0].chip.irq_set_type = ls_intc_set_type;
+	ct[0].handler = handle_level_irq;
+
+	ct[1].type = IRQ_TYPE_EDGE_BOTH;
+	ct[1].regs.mask = LS_REG_INTC_EN;
+	ct[1].regs.ack = LS_REG_INTC_CLR;
+	ct[1].chip.irq_unmask = irq_gc_mask_set_bit;
+	ct[1].chip.irq_mask = irq_gc_mask_clr_bit;
+	ct[1].chip.irq_ack = irq_gc_ack_set_bit;
+	ct[1].chip.irq_set_type = ls_intc_set_type;
+	ct[1].handler = handle_edge_irq;
+
+	irq_set_chained_handler_and_data(parent_irq,
+		ls1x_chained_handle_irq, priv);
+
+	return 0;
+
+out_free_domain:
+	irq_domain_remove(priv->domain);
+out_iounmap:
+	iounmap(priv->intc_base);
+out_free_priv:
+	kfree(priv);
+	
+	return err;
+}
+
+IRQCHIP_DECLARE(ls1x_intc, "loongson,ls1x-intc", ls1x_intc_of_init);
-- 
2.20.1


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

* [PATCH v6 2/2] dt-bindings: interrupt-controller: loongson ls1x intc
  2019-02-01  6:22 ` irqchip: Add driver for Loongson-1 intc v6 Jiaxun Yang
  2019-02-01  6:22   ` [PATCH v6 1/2] irqchip: Add driver for Loongson-1 interrupt controller Jiaxun Yang
@ 2019-02-01  6:22   ` Jiaxun Yang
  2019-02-14 10:36   ` irqchip: Add driver for Loongson-1 intc v6 Marc Zyngier
  2 siblings, 0 replies; 24+ messages in thread
From: Jiaxun Yang @ 2019-02-01  6:22 UTC (permalink / raw)
  To: linux-mips
  Cc: tglx, jason, marc.zyngier, linux-kernel, robh+dt, devicetree,
	Jiaxun Yang, Rob Herring

Dt-bindings doc about Loongson-1 interrupt controller.

Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---
 .../loongson,ls1x-intc.txt                    | 25 +++++++++++++++++++
 1 file changed, 25 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/loongson,ls1x-intc.txt

diff --git a/Documentation/devicetree/bindings/interrupt-controller/loongson,ls1x-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/loongson,ls1x-intc.txt
new file mode 100644
index 000000000000..9385f8b55109
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/loongson,ls1x-intc.txt
@@ -0,0 +1,25 @@
+Loongson ls1x Interrupt Controller
+
+Required properties:
+
+- compatible : should be "loongson,ls1x-intc". Valid strings are:
+
+- reg : Specifies base physical address and size of the registers.
+- interrupt-controller : Identifies the node as an interrupt controller
+- #interrupt-cells : Specifies the number of cells needed to encode an
+  interrupt source. The value shall be 2.
+- interrupts : Specifies the CPU interrupt the controller is connected to.
+
+Example:
+
+intc: interrupt-controller@1fd01040 {
+	compatible = "loongson,ls1x-intc";
+	reg = <0x1fd01040 0x18>;
+
+	interrupt-controller;
+	#interrupt-cells = <2>;
+
+	interrupt-parent = <&cpu_intc>;
+	interrupts = <2>;
+};
+
-- 
2.20.1


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

* Re: irqchip: Add driver for Loongson-1 intc v6
  2019-02-01  6:22 ` irqchip: Add driver for Loongson-1 intc v6 Jiaxun Yang
  2019-02-01  6:22   ` [PATCH v6 1/2] irqchip: Add driver for Loongson-1 interrupt controller Jiaxun Yang
  2019-02-01  6:22   ` [PATCH v6 2/2] dt-bindings: interrupt-controller: loongson ls1x intc Jiaxun Yang
@ 2019-02-14 10:36   ` Marc Zyngier
  2 siblings, 0 replies; 24+ messages in thread
From: Marc Zyngier @ 2019-02-14 10:36 UTC (permalink / raw)
  To: Jiaxun Yang; +Cc: linux-mips, tglx, jason, linux-kernel, robh+dt, devicetree

On Fri, 01 Feb 2019 06:22:34 +0000,
Jiaxun Yang <jiaxun.yang@flygoat.com> wrote:
> 
> v1->v2: Fix SPDX-License-Identifier
> v2->v3: Rework according suggestions from Marc Zyngier, Thanks.
> v3->v4: Rework the driver into a single chip driver.
> v4->v5: Fix minor issues.
> v5->v6: Fix doc and collect Rob's review tag.

Now applied to irqchip-next

Thanks,

	M.

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

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

end of thread, other threads:[~2019-02-14 10:36 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-01-22 15:45 [PATCH 1/2] irqchip: Add driver for Loongson-1 interrupt controller Jiaxun Yang
2019-01-22 15:45 ` [PATCH 2/2] dt-bindings: interrupt-controller: loongson ls1x intc Jiaxun Yang
2019-01-23  6:23 ` irqchip: Add driver for Loongson-1 intc v2 Jiaxun Yang
2019-01-23  6:23   ` [PATCH v2 2/2] dt-bindings: interrupt-controller: loongson ls1x intc Jiaxun Yang
2019-01-23  6:23   ` [PATCH v2 1/2] irqchip: Add driver for Loongson-1 interrupt controller Jiaxun Yang
2019-01-23  8:36     ` Marc Zyngier
2019-01-24  3:27 ` irqchip: Add driver for Loongson-1 intc v3 Jiaxun Yang
2019-01-24  3:27   ` [PATCH v3 1/2] irqchip: Add driver for Loongson-1 interrupt controller Jiaxun Yang
2019-01-24  9:54     ` Marc Zyngier
2019-01-25 10:56       ` Jiaxun Yang
2019-01-26 12:09         ` Marc Zyngier
2019-01-24  3:27   ` [PATCH v3 2/2] dt-bindings: interrupt-controller: loongson ls1x intc Jiaxun Yang
2019-01-30 16:41     ` Rob Herring
2019-01-28 14:46 ` irqchip: Add driver for Loongson-1 intc v3 Jiaxun Yang
2019-01-28 14:46   ` [PATCH v4 1/2] irqchip: Add driver for Loongson-1 interrupt controller Jiaxun Yang
2019-01-28 15:20     ` Marc Zyngier
2019-01-28 14:46   ` [PATCH v4 2/2] dt-bindings: interrupt-controller: loongson ls1x intc Jiaxun Yang
2019-01-29  3:04 ` irqchip: Add driver for Loongson-1 intc v5 Jiaxun Yang
2019-01-29  3:05   ` [PATCH v5 1/2] irqchip: Add driver for Loongson-1 interrupt controller Jiaxun Yang
2019-01-29  3:05   ` [PATCH v5 2/2] dt-bindings: interrupt-controller: loongson ls1x intc Jiaxun Yang
2019-02-01  6:22 ` irqchip: Add driver for Loongson-1 intc v6 Jiaxun Yang
2019-02-01  6:22   ` [PATCH v6 1/2] irqchip: Add driver for Loongson-1 interrupt controller Jiaxun Yang
2019-02-01  6:22   ` [PATCH v6 2/2] dt-bindings: interrupt-controller: loongson ls1x intc Jiaxun Yang
2019-02-14 10:36   ` irqchip: Add driver for Loongson-1 intc v6 Marc Zyngier

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).