[PATCH] [RFC] Using hierarchy irqdomian to implement MTK intpol.

Joe.C srv_yingjoe.chen at mediatek.com
Wed Sep 24 09:04:56 PDT 2014


From: "Joe.C" <yingjoe.chen at mediatek.com>

Here's the first draft of using hierarchy irqdomain to implement MTK intpol
support. I have tested it and intpol works fine. Before continue, I'd like
to get your comments. This is based on Jiang's hierarchy irqdomian [1] and
my mediatek SoC basic support [2].

Simplified block diagram for interrupt:

    ---------      ---------
 ---| CIRQ  |------|ARM GIC|
 ---|       |------|       |
 ---|       |------|       |
 ---|       |------|       |
 ---|       |------|       |
    ---------      ---------

In device tree, interrupt-parent for other devices is cirq, child of gic.
This describe HW better and allow device to specify polarity as it is sent
by the device. I'm using interrupt-parent to specify irq domain parent in
cirq now. Maybe adding another name like 'interrupt-domain-parent' will be
better.

Currently only set_type is implement in CIRQ, but I think this could extended
to cover all function gic_arch_extn provided.

In order to use hierarchy irq domain, gic can't use legacy irq domain anymore.
If arm,hierarchy-irq-domain property is specified, GIC will work in hierarchy
way and all interrupt must be set by device tree.

One thing worth mention is arg in irq_domain_alloc_irqs. On x86, msi is using
struct irq_alloc_info, to pass data between irq domain. In irq_create_of_mapping(),
of_phandle_args is used. All allocs in irq_domain chain will need to use same
interrupt-cells formats.


[1] http://lists.infradead.org/pipermail/linux-arm-kernel/2014-September/286542.html
[2] http://lists.infradead.org/pipermail/linux-arm-kernel/2014-September/284553.html

Signed-off-by: Joe.C <yingjoe.chen at mediatek.com>
---
 arch/arm/boot/dts/mt8135.dtsi     |  14 +++-
 drivers/irqchip/Makefile          |   1 +
 drivers/irqchip/irq-gic.c         |  53 +++++++++++--
 drivers/irqchip/irq-mt65xx-cirq.c | 158 ++++++++++++++++++++++++++++++++++++++
 include/linux/irq.h               |   5 ++
 kernel/irq/chip.c                 |  28 +++++++
 kernel/irq/irqdomain.c            |  14 ++--
 7 files changed, 256 insertions(+), 17 deletions(-)
 create mode 100644 drivers/irqchip/irq-mt65xx-cirq.c

diff --git a/arch/arm/boot/dts/mt8135.dtsi b/arch/arm/boot/dts/mt8135.dtsi
index 90a56ad..e19ab1a 100644
--- a/arch/arm/boot/dts/mt8135.dtsi
+++ b/arch/arm/boot/dts/mt8135.dtsi
@@ -18,7 +18,7 @@
 
 / {
 	compatible = "mediatek,mt8135";
-	interrupt-parent = <&gic>;
+	interrupt-parent = <&cirq>;
 
 	cpu-map {
 		cluster0 {
@@ -97,15 +97,25 @@
 		timer: timer at 10008000 {
 			compatible = "mediatek,mt8135-timer", "mediatek,mt6577-timer";
 			reg = <0 0x10008000 0 0x80>;
-			interrupts = <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>;
+			interrupts = <GIC_SPI 113 IRQ_TYPE_LEVEL_LOW>;
 			clocks = <&system_clk>, <&rtc_clk>;
 			clock-names = "system-clk", "rtc-clk";
 		};
 
+		cirq: interrupt-controller at 10200030 {
+			compatible = "mediatek,mt8135-cirq", "mediatek,mt6577-cirq";
+			interrupt-controller;
+			#interrupt-cells = <3>;
+			interrupt-parent = <&gic>;
+			reg = <0 0x10200030 0 0x1c>;
+		};
+
 		gic: interrupt-controller at 10211000 {
 			compatible = "arm,cortex-a15-gic";
 			interrupt-controller;
 			#interrupt-cells = <3>;
+			arm,hierarchy-irq-domain;
+			interrupt-parent = <&gic>;
 			reg = <0 0x10211000 0 0x1000>,
 			      <0 0x10212000 0 0x1000>,
 			      <0 0x10214000 0 0x2000>,
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 73052ba..0b34675 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -34,3 +34,4 @@ obj-$(CONFIG_XTENSA)			+= irq-xtensa-pic.o
 obj-$(CONFIG_XTENSA_MX)			+= irq-xtensa-mx.o
 obj-$(CONFIG_IRQ_CROSSBAR)		+= irq-crossbar.o
 obj-$(CONFIG_BRCMSTB_L2_IRQ)		+= irq-brcmstb-l2.o
+obj-$(CONFIG_ARCH_MEDIATEK)		+= irq-mt65xx-cirq.o
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 4b959e6..d66d707 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -767,22 +767,50 @@ void __init gic_init_physaddr(struct device_node *node)
 static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
 				irq_hw_number_t hw)
 {
+	irq_domain_set_hwirq_and_chip(d, irq, hw, &gic_chip, d->host_data);
 	if (hw < 32) {
 		irq_set_percpu_devid(irq);
-		irq_set_chip_and_handler(irq, &gic_chip,
-					 handle_percpu_devid_irq);
+		irq_set_handler(irq, handle_percpu_devid_irq);
 		set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);
 	} else {
-		irq_set_chip_and_handler(irq, &gic_chip,
-					 handle_fasteoi_irq);
+		irq_set_handler(irq, handle_fasteoi_irq);
 		set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
 
 		gic_routable_irq_domain_ops->map(d, irq, hw);
 	}
-	irq_set_chip_data(irq, d->host_data);
 	return 0;
 }
 
+static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				unsigned int nr_irqs, void *arg)
+{
+	int i, ret;
+	irq_hw_number_t hwirq;
+	unsigned int type = IRQ_TYPE_NONE;
+	struct of_phandle_args *irq_data = arg;
+
+	ret = domain->ops->xlate(domain, irq_data->np, irq_data->args,
+				 irq_data->args_count, &hwirq, &type);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < nr_irqs; i++)
+		gic_irq_domain_map(domain, virq+i, hwirq+i);
+
+	return 0;
+}
+
+static void gic_irq_domain_free(struct irq_domain *domain, unsigned int virq,
+				unsigned int nr_irqs)
+{
+	int i;
+
+	for (i = 0; i < nr_irqs; i++) {
+		irq_set_handler(virq + i, NULL);
+		irq_domain_set_hwirq_and_chip(domain, virq + i, 0, NULL, NULL);
+	}
+}
+
 static void gic_irq_domain_unmap(struct irq_domain *d, unsigned int irq)
 {
 	gic_routable_irq_domain_ops->unmap(d, irq);
@@ -795,8 +823,6 @@ static int gic_irq_domain_xlate(struct irq_domain *d,
 {
 	unsigned long ret = 0;
 
-	if (d->of_node != controller)
-		return -EINVAL;
 	if (intsize < 3)
 		return -EINVAL;
 
@@ -839,6 +865,14 @@ static struct notifier_block gic_cpu_notifier = {
 };
 #endif
 
+static const struct irq_domain_ops gic_irq_domain_hierarchy_ops = {
+	.alloc = gic_irq_domain_alloc,
+	.free = gic_irq_domain_free,
+	.map = gic_irq_domain_map,
+	.unmap = gic_irq_domain_unmap,
+	.xlate = gic_irq_domain_xlate,
+};
+
 static const struct irq_domain_ops gic_irq_domain_ops = {
 	.map = gic_irq_domain_map,
 	.unmap = gic_irq_domain_unmap,
@@ -952,7 +986,10 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
 
 	gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */
 
-	if (of_property_read_u32(node, "arm,routable-irqs",
+	if (of_find_property(node, "arm,hierarchy-irq-domain", NULL))
+		gic->domain = irq_domain_add_linear(node, gic_irqs,
+					&gic_irq_domain_hierarchy_ops, gic);
+	else if (of_property_read_u32(node, "arm,routable-irqs",
 				 &nr_routable_irqs)) {
 		irq_base = irq_alloc_descs(irq_start, 16, gic_irqs,
 					   numa_node_id());
diff --git a/drivers/irqchip/irq-mt65xx-cirq.c b/drivers/irqchip/irq-mt65xx-cirq.c
new file mode 100644
index 0000000..1243a7f
--- /dev/null
+++ b/drivers/irqchip/irq-mt65xx-cirq.c
@@ -0,0 +1,158 @@
+
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "irqchip.h"
+
+#define MT6577_SYS_CIRQ_NUM	(168)
+
+struct mt_cirq_chip_data {
+	spinlock_t lock;
+	void __iomem *intpol_base;
+};
+
+
+static int mt_cirq_set_type(struct irq_data *data, unsigned int type)
+{
+	irq_hw_number_t hwirq = data->hwirq;
+	struct mt_cirq_chip_data *chip_data = data->chip_data;
+	u32 offset, reg_index, value;
+	unsigned long flags;
+	int ret;
+
+	offset = hwirq & 0x1f;
+	reg_index = hwirq >> 5;
+
+	spin_lock_irqsave(&chip_data->lock, flags);
+	value = readl_relaxed(chip_data->intpol_base + reg_index * 4);
+	if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_EDGE_FALLING) {
+		type = (type == IRQ_TYPE_LEVEL_LOW) ?
+			IRQ_TYPE_LEVEL_HIGH : IRQ_TYPE_EDGE_RISING;
+		value |= (1 << offset);
+	} else
+		value &= ~(1 << offset);
+	writel(value, chip_data->intpol_base + reg_index * 4);
+
+	data = data->parent_data;
+	ret = data->chip->irq_set_type(data, type);
+	spin_unlock_irqrestore(&chip_data->lock, flags);
+	return ret;
+}
+
+static struct irq_chip mt_cirq_chip = {
+	.name			= "MT_CIRQ",
+	.irq_mask		= irq_chip_mask_parent,
+	.irq_unmask		= irq_chip_unmask_parent,
+	.irq_eoi		= irq_chip_eoi_parent,
+	.irq_set_type		= mt_cirq_set_type,
+	.irq_retrigger		= irq_chip_retrigger_hierarchy,
+	.irq_set_affinity	= irq_chip_set_affinity_parent,
+};
+
+static int mt_cirq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				unsigned int nr_irqs, void *arg)
+{
+	int i, ret;
+	irq_hw_number_t hwirq;
+	struct of_phandle_args *irq_data = arg;
+
+	if (irq_data->args_count != 3)
+		return -EINVAL;
+
+	hwirq = irq_data->args[1];
+	if (irq_find_mapping(domain, hwirq) > 0)
+		return -EEXIST;
+
+	for (i = 0; i < nr_irqs; i++)
+		irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
+					      &mt_cirq_chip, domain->host_data);
+
+	ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static void mt_cirq_domain_free(struct irq_domain *domain, unsigned int virq,
+				unsigned int nr_irqs)
+{
+	int i;
+
+	for (i = 0; i < nr_irqs; i++) {
+		irq_set_handler(virq + i, NULL);
+		irq_domain_set_hwirq_and_chip(domain, virq + i, 0, NULL, NULL);
+	}
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static int mt_cirq_domain_xlate(struct irq_domain *d,
+				struct device_node *controller,
+				const u32 *intspec, unsigned int intsize,
+				unsigned long *out_hwirq,
+				unsigned int *out_type)
+{
+	return d->parent->ops->xlate(d->parent, controller, intspec, intsize,
+					out_hwirq, out_type);
+}
+
+static struct irq_domain_ops cirq_domain_ops = {
+	.alloc = mt_cirq_domain_alloc,
+	.free = mt_cirq_domain_free,
+	.xlate = mt_cirq_domain_xlate,
+};
+
+static int __init mtk_cirq_of_init(struct device_node *node,
+				   struct device_node *parent)
+{
+	struct device_node *parent_node;
+	struct irq_domain *domain, *domain_parent = NULL;
+	struct mt_cirq_chip_data *chip_data;
+	int ret = 0;
+
+	parent_node = of_irq_find_parent(node);
+	if (parent_node) {
+		domain_parent = irq_find_host(parent_node);
+		of_node_put(parent_node);
+	}
+
+	if (!domain_parent) {
+		pr_err("mtk_cirq: interrupt-parent not found\n");
+		return -EINVAL;
+	}
+
+	chip_data = kzalloc(sizeof(*chip_data), GFP_KERNEL);
+	if (!chip_data)
+		return -ENOMEM;
+
+	chip_data->intpol_base = of_io_request_and_map(node, 0, "intpol");
+	if (!chip_data->intpol_base) {
+		pr_err("mtk_cirq: unable to map cirq register\n");
+		ret = -ENOMEM;
+		goto out_free;
+	}
+
+	domain = irq_domain_add_linear(node, MT6577_SYS_CIRQ_NUM,
+				&cirq_domain_ops, chip_data);
+	if (!domain) {
+		ret = -ENOMEM;
+		goto out_unmap;
+	}
+	domain->parent = domain_parent;
+	spin_lock_init(&chip_data->lock);
+
+	return 0;
+
+out_unmap:
+	iounmap(chip_data->intpol_base);
+out_free:
+	kfree(chip_data);
+	return ret;
+}
+IRQCHIP_DECLARE(mtk_cirq, "mediatek,mt6577-cirq", mtk_cirq_of_init);
diff --git a/include/linux/irq.h b/include/linux/irq.h
index bfa027f..a877b88 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -435,6 +435,11 @@ extern void handle_nested_irq(unsigned int irq);
 
 #ifdef	CONFIG_IRQ_DOMAIN_HIERARCHY
 extern void irq_chip_ack_parent(struct irq_data *data);
+extern void irq_chip_mask_parent(struct irq_data *data);
+extern void irq_chip_unmask_parent(struct irq_data *data);
+extern void irq_chip_eoi_parent(struct irq_data *data);
+extern int irq_chip_set_affinity_parent(struct irq_data *data,
+				const struct cpumask *dest, bool force);
 extern int irq_chip_retrigger_hierarchy(struct irq_data *data);
 #endif
 
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index b8ee27e..da3042b 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -830,6 +830,34 @@ void irq_chip_ack_parent(struct irq_data *data)
 		data->chip->irq_ack(data);
 }
 
+void irq_chip_mask_parent(struct irq_data *data)
+{
+	data = data->parent_data;
+	data->chip->irq_mask(data);
+}
+
+void irq_chip_unmask_parent(struct irq_data *data)
+{
+	data = data->parent_data;
+	data->chip->irq_unmask(data);
+}
+
+void irq_chip_eoi_parent(struct irq_data *data)
+{
+	data = data->parent_data;
+	data->chip->irq_eoi(data);
+}
+
+int irq_chip_set_affinity_parent(struct irq_data *data,
+				 const struct cpumask *dest, bool force)
+{
+	data = data->parent_data;
+	if (data->chip->irq_set_affinity)
+		return data->chip->irq_set_affinity(data, dest, force);
+
+	return -ENOSYS;
+}
+
 int irq_chip_retrigger_hierarchy(struct irq_data *data)
 {
 	for (data = data->parent_data; data; data = data->parent_data)
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index e285f3a..01e852b 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -467,7 +467,7 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
 	struct irq_domain *domain;
 	irq_hw_number_t hwirq;
 	unsigned int type = IRQ_TYPE_NONE;
-	unsigned int virq;
+	int virq;
 
 	domain = irq_data->np ? irq_find_host(irq_data->np) : irq_default_domain;
 	if (!domain) {
@@ -493,8 +493,8 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
 	else
 #endif
 		virq = irq_create_mapping(domain, hwirq);
-	if (!virq)
-		return virq;
+	if (virq <= 0)
+		return 0;
 
 	/* Set type if specified and different than the current one */
 	if (type != IRQ_TYPE_NONE &&
@@ -716,20 +716,20 @@ const struct irq_domain_ops irq_domain_simple_ops = {
 };
 EXPORT_SYMBOL_GPL(irq_domain_simple_ops);
 
-static int irq_domain_alloc_descs(int virq, unsigned int nr_irqs,
+static int irq_domain_alloc_descs(int virq, unsigned int cnt,
 				  irq_hw_number_t hwirq, int node)
 {
 	unsigned int hint;
 
 	if (virq >= 0) {
-		virq = irq_alloc_descs(virq, virq, nr_irqs, node);
+		virq = irq_alloc_descs(virq, virq, cnt, node);
 	} else {
 		hint = hwirq % nr_irqs;
 		if (hint == 0)
 			hint++;
-		virq = irq_alloc_descs_from(hint, nr_irqs, node);
+		virq = irq_alloc_descs_from(hint, cnt, node);
 		if (virq <= 0 && hint > 1)
-			virq = irq_alloc_descs_from(1, nr_irqs, node);
+			virq = irq_alloc_descs_from(1, cnt, node);
 	}
 
 	return virq;
-- 
1.8.1.1.dirty




More information about the linux-arm-kernel mailing list