diff mbox

[v6,02/11] irqchip: mmp: support irqchip

Message ID 1374833133-21119-3-git-send-email-haojian.zhuang@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Haojian Zhuang July 26, 2013, 10:05 a.m. UTC
Support IRQCHIP on irq-mmp driver.

Signed-off-by: Haojian Zhuang <haojian.zhuang@gmail.com>
---
 arch/arm/mach-mmp/mmp-dt.c  |   8 +-
 arch/arm/mach-mmp/mmp2-dt.c |   8 +-
 drivers/irqchip/irq-mmp.c   | 238 +++++++++++++++++++++++---------------------
 3 files changed, 126 insertions(+), 128 deletions(-)

Comments

Daniel Drake Aug. 12, 2013, 10:53 p.m. UTC | #1
On Fri, Jul 26, 2013 at 4:05 AM, Haojian Zhuang
<haojian.zhuang@gmail.com> wrote:
> Support IRQCHIP on irq-mmp driver.
>
> Signed-off-by: Haojian Zhuang <haojian.zhuang@gmail.com>
> ---
>  arch/arm/mach-mmp/mmp-dt.c  |   8 +-
>  arch/arm/mach-mmp/mmp2-dt.c |   8 +-
>  drivers/irqchip/irq-mmp.c   | 238 +++++++++++++++++++++++---------------------
>  3 files changed, 126 insertions(+), 128 deletions(-)

This patch causes boot to fail, tested on OLPC XO-1.75 (MMP2).

Calibrating delay loop... irq 13, desc: db804740, depth: 1, count: 0,
unhandled: 0
->handle_irq():  c00704f4, handle_bad_irq+0x0/0x214
->irq_data.chip(): c05aa2e8, 0xc05aa2e8
->action():   (null)
   IRQ_NOPROBE set
 IRQ_NOREQUEST set
irq 13, desc: db804740, depth: 1, count: 0, unhandled: 0
->handle_irq():  c00704f4, handle_bad_irq+0x0/0x214
->irq_data.chip(): c05aa2e8, 0xc05aa2e8
->action():   (null)
   IRQ_NOPROBE set
 IRQ_NOREQUEST set
irq 13, desc: db804740, depth: 1, count: 0, unhandled: 0
->handle_irq():  c00704f4, handle_bad_irq+0x0/0x214
->irq_data.chip(): c05aa2e8, 0xc05aa2e8
->action():   (null)
   IRQ_NOPROBE set
 IRQ_NOREQUEST set

continues as infinite loop.

I had a poke, trying to figure out what would have caused this,
without much luck. I tried to go back to irq_domain_add_legacy() and
sprinkled some icu_mask_irq() calls around, nothing changed. Any
ideas?

Thanks
Daniel
Daniel Drake Aug. 13, 2013, 10:53 p.m. UTC | #2
On Mon, Aug 12, 2013 at 4:53 PM, Daniel Drake <dsd@laptop.org> wrote:

> This patch causes boot to fail, tested on OLPC XO-1.75 (MMP2).
>
> Calibrating delay loop... irq 13, desc: db804740, depth: 1, count: 0,
> unhandled: 0
> ->handle_irq():  c00704f4, handle_bad_irq+0x0/0x214
> ->irq_data.chip(): c05aa2e8, 0xc05aa2e8
> ->action():   (null)
>    IRQ_NOPROBE set
>  IRQ_NOREQUEST set
>
> continues as infinite loop.
>
> I had a poke, trying to figure out what would have caused this,
> without much luck. I tried to go back to irq_domain_add_legacy() and
> sprinkled some icu_mask_irq() calls around, nothing changed. Any
> ideas?

In my above experiments I was assuming things were crashing during the
IRQ setup path (e.g mmp_setup_bases). That was wrong - I now realise
that the setup code moved around by this patch exits OK, then the
timer driver loads and requests + unmasks interrupt 13, and these
messages start appearing upon the first interrupt which happens right
after.

It is not happy because no IRQ handler was registered. This is
normally done by mmp_irq_domain_map(), previously called immediately
from irq_domain_add_legacy().
Now that we moved to irq_domain_add_linear() in this patch, the map
function is not called.
I fixed this by calling irq_set_chip_and_handler() and set_irq_flags()
in the loop immediately after where irq_create_mapping() is used, as
is done by irq-bcm2835.c.

Now I get a new crash, still handling the first interrupt. In
icu_mask_ack_irq(), d->domain is NULL. We dereference it, causing a
crash. The domain field should have been set by irq_create_mapping().

What actually happens is that early_irq_init (kernel/irqdesc.c)
allocates 16 interrupt descriptors even before we've looked at the
device tree. Are those "legacy" interrupts? I can't figure out what
that is supposed to mean. Then the interrupts registered by irq-mmp.c
start at 16 (e.g. hwirq 0 becomes virq 16).

When the hardware interrupt 13 comes in, the call chain is:
handle_IRQ()
generic_handle_irq()
handle_level_irq()
icu_mask_ack_irq()

generic_handle_irq() above looks up the descriptor for IRQ 13, but it
does it without considering the mapping. So it picks up the descriptor
for "legacy" interrupt 13 and passes that to icu_mask_ack_irq() -
rather than passing the descriptor for the hwirq 13 that irq-mmp.c
registered and was expecting (i.e. no translation was performed).

How is this supposed to work?

Thanks
Daniel
Daniel Drake Aug. 14, 2013, 5:47 p.m. UTC | #3
On Tue, Aug 13, 2013 at 4:53 PM, Daniel Drake <dsd@laptop.org> wrote:
> generic_handle_irq() above looks up the descriptor for IRQ 13, but it
> does it without considering the mapping. So it picks up the descriptor
> for "legacy" interrupt 13 and passes that to icu_mask_ack_irq() -
> rather than passing the descriptor for the hwirq 13 that irq-mmp.c
> registered and was expecting (i.e. no translation was performed).
>
> How is this supposed to work?

All the users of irq_domain_add_linear() that I have looked at supply
a custom IRQ handler routine (via set_handle_irq) that does the
translation.

So this patch needs to be merged with patch 3 in the series (which
does just that), or the conversion from legacy to linear needs to
happen in patch 3, not this one. Personally I would just merge the 2
patches.

With patch 3 also applied, there is no more crash.

Thanks,
Daniel
diff mbox

Patch

diff --git a/arch/arm/mach-mmp/mmp-dt.c b/arch/arm/mach-mmp/mmp-dt.c
index b37915d..cca529c 100644
--- a/arch/arm/mach-mmp/mmp-dt.c
+++ b/arch/arm/mach-mmp/mmp-dt.c
@@ -9,17 +9,13 @@ 
  *  publishhed by the Free Software Foundation.
  */
 
-#include <linux/irq.h>
-#include <linux/irqdomain.h>
-#include <linux/of_irq.h>
+#include <linux/irqchip.h>
 #include <linux/of_platform.h>
 #include <asm/mach/arch.h>
 #include <asm/mach/time.h>
-#include <mach/irqs.h>
 
 #include "common.h"
 
-extern void __init mmp_dt_irq_init(void);
 extern void __init mmp_dt_init_timer(void);
 
 static const struct of_dev_auxdata pxa168_auxdata_lookup[] __initconst = {
@@ -64,7 +60,6 @@  static const char *mmp_dt_board_compat[] __initdata = {
 
 DT_MACHINE_START(PXA168_DT, "Marvell PXA168 (Device Tree Support)")
 	.map_io		= mmp_map_io,
-	.init_irq	= mmp_dt_irq_init,
 	.init_time	= mmp_dt_init_timer,
 	.init_machine	= pxa168_dt_init,
 	.dt_compat	= mmp_dt_board_compat,
@@ -72,7 +67,6 @@  MACHINE_END
 
 DT_MACHINE_START(PXA910_DT, "Marvell PXA910 (Device Tree Support)")
 	.map_io		= mmp_map_io,
-	.init_irq	= mmp_dt_irq_init,
 	.init_time	= mmp_dt_init_timer,
 	.init_machine	= pxa910_dt_init,
 	.dt_compat	= mmp_dt_board_compat,
diff --git a/arch/arm/mach-mmp/mmp2-dt.c b/arch/arm/mach-mmp/mmp2-dt.c
index 4ac2567..023cb45 100644
--- a/arch/arm/mach-mmp/mmp2-dt.c
+++ b/arch/arm/mach-mmp/mmp2-dt.c
@@ -10,18 +10,13 @@ 
  */
 
 #include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/irqdomain.h>
-#include <linux/of_irq.h>
+#include <linux/irqchip.h>
 #include <linux/of_platform.h>
 #include <asm/mach/arch.h>
 #include <asm/mach/time.h>
-#include <mach/irqs.h>
-#include <mach/regs-apbc.h>
 
 #include "common.h"
 
-extern void __init mmp_dt_irq_init(void);
 extern void __init mmp_dt_init_timer(void);
 
 static const struct of_dev_auxdata mmp2_auxdata_lookup[] __initconst = {
@@ -49,7 +44,6 @@  static const char *mmp2_dt_board_compat[] __initdata = {
 
 DT_MACHINE_START(MMP2_DT, "Marvell MMP2 (Device Tree Support)")
 	.map_io		= mmp_map_io,
-	.init_irq	= mmp_dt_irq_init,
 	.init_time	= mmp_dt_init_timer,
 	.init_machine	= mmp2_dt_init,
 	.dt_compat	= mmp2_dt_board_compat,
diff --git a/drivers/irqchip/irq-mmp.c b/drivers/irqchip/irq-mmp.c
index dab6def..275709b 100644
--- a/drivers/irqchip/irq-mmp.c
+++ b/drivers/irqchip/irq-mmp.c
@@ -30,6 +30,8 @@ 
 #include <mach/pm-pxa910.h>
 #endif
 
+#include "irqchip.h"
+
 #define MAX_ICU_NR		16
 
 struct icu_chip_data {
@@ -324,138 +326,146 @@  void __init mmp2_init_icu(void)
 }
 
 #ifdef CONFIG_OF
-static const struct of_device_id intc_ids[] __initconst = {
-	{ .compatible = "mrvl,mmp-intc", .data = &mmp_conf },
-	{ .compatible = "mrvl,mmp2-intc", .data = &mmp2_conf },
-	{}
-};
-
-static const struct of_device_id mmp_mux_irq_match[] __initconst = {
-	{ .compatible = "mrvl,mmp2-mux-intc" },
-	{}
-};
-
-int __init mmp2_mux_init(struct device_node *parent)
+static int __init mmp_init_bases(struct device_node *node)
 {
-	struct device_node *node;
-	const struct of_device_id *of_id;
-	struct resource res;
-	int i, irq_base, ret, irq;
-	u32 nr_irqs, mfp_irq;
+	int ret, nr_irqs, irq, i = 0;
 
-	node = parent;
-	max_icu_nr = 1;
-	for (i = 1; i < MAX_ICU_NR; i++) {
-		node = of_find_matching_node(node, mmp_mux_irq_match);
-		if (!node)
-			break;
-		of_id = of_match_node(&mmp_mux_irq_match[0], node);
-		ret = of_property_read_u32(node, "mrvl,intc-nr-irqs",
-					   &nr_irqs);
-		if (ret) {
-			pr_err("Not found mrvl,intc-nr-irqs property\n");
-			ret = -EINVAL;
-			goto err;
-		}
-		ret = of_address_to_resource(node, 0, &res);
-		if (ret < 0) {
-			pr_err("Not found reg property\n");
-			ret = -EINVAL;
-			goto err;
-		}
-		icu_data[i].reg_status = mmp_icu_base + res.start;
-		ret = of_address_to_resource(node, 1, &res);
-		if (ret < 0) {
-			pr_err("Not found reg property\n");
-			ret = -EINVAL;
-			goto err;
-		}
-		icu_data[i].reg_mask = mmp_icu_base + res.start;
-		icu_data[i].cascade_irq = irq_of_parse_and_map(node, 0);
-		if (!icu_data[i].cascade_irq) {
-			ret = -EINVAL;
-			goto err;
-		}
+	ret = of_property_read_u32(node, "mrvl,intc-nr-irqs", &nr_irqs);
+	if (ret) {
+		pr_err("Not found mrvl,intc-nr-irqs property\n");
+		return ret;
+	}
 
-		irq_base = irq_alloc_descs(-1, 0, nr_irqs, 0);
-		if (irq_base < 0) {
-			pr_err("Failed to allocate IRQ numbers for mux intc\n");
-			ret = irq_base;
+	mmp_icu_base = of_iomap(node, 0);
+	if (!mmp_icu_base) {
+		pr_err("Failed to get interrupt controller register\n");
+		return -ENOMEM;
+	}
+
+	icu_data[0].virq_base = 0;
+	icu_data[0].domain = irq_domain_add_linear(node, nr_irqs,
+						   &mmp_irq_domain_ops,
+						   &icu_data[0]);
+	for (irq = 0; irq < nr_irqs; irq++) {
+		ret = irq_create_mapping(icu_data[0].domain, irq);
+		if (!ret) {
+			pr_err("Failed to mapping hwirq\n");
 			goto err;
 		}
-		if (!of_property_read_u32(node, "mrvl,clr-mfp-irq",
-					  &mfp_irq)) {
-			icu_data[i].clr_mfp_irq_base = irq_base;
-			icu_data[i].clr_mfp_hwirq = mfp_irq;
-		}
-		irq_set_chained_handler(icu_data[i].cascade_irq,
-					icu_mux_irq_demux);
-		icu_data[i].nr_irqs = nr_irqs;
-		icu_data[i].virq_base = irq_base;
-		icu_data[i].domain = irq_domain_add_legacy(node, nr_irqs,
-							   irq_base, 0,
-							   &mmp_irq_domain_ops,
-							   &icu_data[i]);
-		for (irq = irq_base; irq < irq_base + nr_irqs; irq++)
-			icu_mask_irq(irq_get_irq_data(irq));
+		if (!irq)
+			icu_data[0].virq_base = ret;
 	}
-	max_icu_nr = i;
+	icu_data[0].nr_irqs = nr_irqs;
 	return 0;
 err:
-	of_node_put(node);
-	max_icu_nr = i;
-	return ret;
+	if (icu_data[0].virq_base) {
+		for (i = 0; i < irq; i++)
+			irq_dispose_mapping(icu_data[0].virq_base + i);
+	}
+	irq_domain_remove(icu_data[0].domain);
+	iounmap(mmp_icu_base);
+	return -EINVAL;
 }
 
-void __init mmp_dt_irq_init(void)
+static int __init mmp_of_init(struct device_node *node,
+			      struct device_node *parent)
 {
-	struct device_node *node;
-	const struct of_device_id *of_id;
-	struct mmp_intc_conf *conf;
-	int nr_irqs, irq_base, ret, irq;
-
-	node = of_find_matching_node(NULL, intc_ids);
-	if (!node) {
-		pr_err("Failed to find interrupt controller in arch-mmp\n");
-		return;
-	}
-	of_id = of_match_node(intc_ids, node);
-	conf = of_id->data;
+	int ret;
 
-	ret = of_property_read_u32(node, "mrvl,intc-nr-irqs", &nr_irqs);
+	ret = mmp_init_bases(node);
+	if (ret < 0)
+		return ret;
+
+	icu_data[0].conf_enable = mmp_conf.conf_enable;
+	icu_data[0].conf_disable = mmp_conf.conf_disable;
+	icu_data[0].conf_mask = mmp_conf.conf_mask;
+	irq_set_default_host(icu_data[0].domain);
+	max_icu_nr = 1;
+	return 0;
+}
+IRQCHIP_DECLARE(mmp_intc, "mrvl,mmp-intc", mmp_of_init);
+
+static int __init mmp2_of_init(struct device_node *node,
+			       struct device_node *parent)
+{
+	int ret;
+
+	ret = mmp_init_bases(node);
+	if (ret < 0)
+		return ret;
+
+	icu_data[0].conf_enable = mmp2_conf.conf_enable;
+	icu_data[0].conf_disable = mmp2_conf.conf_disable;
+	icu_data[0].conf_mask = mmp2_conf.conf_mask;
+	irq_set_default_host(icu_data[0].domain);
+	max_icu_nr = 1;
+	return 0;
+}
+IRQCHIP_DECLARE(mmp2_intc, "mrvl,mmp2-intc", mmp2_of_init);
+
+static int __init mmp2_mux_of_init(struct device_node *node,
+				   struct device_node *parent)
+{
+	struct resource res;
+	int i, ret, irq, j = 0;
+	u32 nr_irqs, mfp_irq;
+
+	if (!parent)
+		return -ENODEV;
+
+	i = max_icu_nr;
+	ret = of_property_read_u32(node, "mrvl,intc-nr-irqs",
+				   &nr_irqs);
 	if (ret) {
 		pr_err("Not found mrvl,intc-nr-irqs property\n");
-		return;
+		return -EINVAL;
 	}
-
-	mmp_icu_base = of_iomap(node, 0);
-	if (!mmp_icu_base) {
-		pr_err("Failed to get interrupt controller register\n");
-		return;
+	ret = of_address_to_resource(node, 0, &res);
+	if (ret < 0) {
+		pr_err("Not found reg property\n");
+		return -EINVAL;
 	}
-
-	irq_base = irq_alloc_descs(-1, 0, nr_irqs - NR_IRQS_LEGACY, 0);
-	if (irq_base < 0) {
-		pr_err("Failed to allocate IRQ numbers\n");
-		goto err;
-	} else if (irq_base != NR_IRQS_LEGACY) {
-		pr_err("ICU's irqbase should be started from 0\n");
-		goto err;
+	icu_data[i].reg_status = mmp_icu_base + res.start;
+	ret = of_address_to_resource(node, 1, &res);
+	if (ret < 0) {
+		pr_err("Not found reg property\n");
+		return -EINVAL;
 	}
-	icu_data[0].conf_enable = conf->conf_enable;
-	icu_data[0].conf_disable = conf->conf_disable;
-	icu_data[0].conf_mask = conf->conf_mask;
-	icu_data[0].nr_irqs = nr_irqs;
-	icu_data[0].virq_base = 0;
-	icu_data[0].domain = irq_domain_add_legacy(node, nr_irqs, 0, 0,
+	icu_data[i].reg_mask = mmp_icu_base + res.start;
+	icu_data[i].cascade_irq = irq_of_parse_and_map(node, 0);
+	if (!icu_data[i].cascade_irq)
+		return -EINVAL;
+
+	icu_data[i].virq_base = 0;
+	icu_data[i].domain = irq_domain_add_linear(node, nr_irqs,
 						   &mmp_irq_domain_ops,
-						   &icu_data[0]);
-	irq_set_default_host(icu_data[0].domain);
-	for (irq = 0; irq < nr_irqs; irq++)
-		icu_mask_irq(irq_get_irq_data(irq));
-	mmp2_mux_init(node);
-	return;
+						   &icu_data[i]);
+	for (irq = 0; irq < nr_irqs; irq++) {
+		ret = irq_create_mapping(icu_data[i].domain, irq);
+		if (!ret) {
+			pr_err("Failed to mapping hwirq\n");
+			goto err;
+		}
+		if (!irq)
+			icu_data[i].virq_base = ret;
+	}
+	icu_data[i].nr_irqs = nr_irqs;
+	if (!of_property_read_u32(node, "mrvl,clr-mfp-irq",
+				  &mfp_irq)) {
+		icu_data[i].clr_mfp_irq_base = icu_data[i].virq_base;
+		icu_data[i].clr_mfp_hwirq = mfp_irq;
+	}
+	irq_set_chained_handler(icu_data[i].cascade_irq,
+				icu_mux_irq_demux);
+	max_icu_nr++;
+	return 0;
 err:
-	iounmap(mmp_icu_base);
+	if (icu_data[i].virq_base) {
+		for (j = 0; j < irq; j++)
+			irq_dispose_mapping(icu_data[i].virq_base + j);
+	}
+	irq_domain_remove(icu_data[i].domain);
+	return -EINVAL;
 }
+IRQCHIP_DECLARE(mmp2_mux_intc, "mrvl,mmp2-mux-intc", mmp2_mux_of_init);
 #endif