[RFC PATCH] ARM: at91/aic: add irq domain and device tree support

Nicolas Ferre nicolas.ferre at atmel.com
Tue Feb 7 13:40:43 EST 2012


This is a try of generic irq chip and irqdomain new helper functions.

Signed-off-by: Nicolas Ferre <nicolas.ferre at atmel.com>
---
This is only a RFC and the the documentation will be taken
from previous patch for AIC already sent.

The multi handler mode allows to simplify the reverse mapping operation
before calling the handle_IRQ function.

This patch goes on top of 3.3-rc2 and a bunch of at91 patches + irqdomain/next
form Grant Likely and pl061-domain-v5 from Rob Herring.

This attempt does not work well for the moment. if you have any hint...
On the other hand, I am not sure to having taken the latest irqdomain work form
Grant...

With several debug macros turned on, I have the following log:

[..]

NR_IRQS:192
of_irq_init: init atmel,at91rm9200-aic @ c055a9ec, parent   (null)
irq: Allocated domain of type 2 @0xc78027e0

[..]

of_irq_map_one: dev=/ahb/apb/timer at fffffd30, index=0
 intspec=1 intlen=2
 intsize=2 intlen=2
of_irq_map_raw: par=/ahb/apb/interrupt-controller at fffff000,intspec=[0x00000001
0x00000004...],ointsize=2
of_irq_map_raw: ipar=/ahb/apb/interrupt-controller at fffff000, size=2
 -> addrsize=1
 -> got it !
irq: irq_create_mapping(0xc78027e0, 0x1)
irq: -> using domain @c78027e0
irq: irq 1 on domain /ahb/apb/interrupt-controller at fffff000 mapped to virtual irq 1
Got PIT irq = 1
Now, enable PIT IRQ!
Console: colour dummy device 80x30
Calibrating delay loop... irq 0, desc: c0429104, depth: 1, count: 0, unhandled: 0
->handle_irq():  c0041adc, handle_bad_irq+0x0/0x204
->irq_data.chip(): c042e548, no_irq_chip+0x0/0x5c
->action():   (null)
   IRQ_NOPROBE set
 IRQ_NOREQUEST set
irq 0, desc: c0429104, depth: 1, count: 0, unhandled: 0
->handle_irq():  c0041adc, handle_bad_irq+0x0/0x204
->irq_data.chip(): c042e548, no_irq_chip+0x0/0x5c
->action():   (null)
   IRQ_NOPROBE set
 IRQ_NOREQUEST set

[.. and again ..]

 arch/arm/Kconfig                              |    3 +
 arch/arm/mach-at91/board-dt.c                 |    1 +
 arch/arm/mach-at91/include/mach/at91_aic.h    |    2 +
 arch/arm/mach-at91/include/mach/entry-macro.S |    9 --
 arch/arm/mach-at91/irq.c                      |  151 +++++++++++++++++--------
 5 files changed, 108 insertions(+), 58 deletions(-)

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index c146065..af3431c 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -323,6 +323,9 @@ config ARCH_AT91
 	select ARCH_REQUIRE_GPIOLIB
 	select HAVE_CLK
 	select CLKDEV_LOOKUP
+	select IRQ_DOMAIN
+	select GENERIC_IRQ_CHIP
+	select MULTI_IRQ_HANDLER
 	help
 	  This enables support for systems based on the Atmel AT91RM9200,
 	  AT91SAM9 processors.
diff --git a/arch/arm/mach-at91/board-dt.c b/arch/arm/mach-at91/board-dt.c
index 0579315..735b273 100644
--- a/arch/arm/mach-at91/board-dt.c
+++ b/arch/arm/mach-at91/board-dt.c
@@ -122,4 +122,5 @@ DT_MACHINE_START(at91sam_dt, "Atmel AT91SAM (Device Tree)")
 	.init_irq	= at91_dt_init_irq,
 	.init_machine	= at91_dt_device_init,
 	.dt_compat	= at91_dt_board_compat,
+	.handle_irq	= at91_handle_irq,
 MACHINE_END
diff --git a/arch/arm/mach-at91/include/mach/at91_aic.h b/arch/arm/mach-at91/include/mach/at91_aic.h
index 3045781..0cd3d8d 100644
--- a/arch/arm/mach-at91/include/mach/at91_aic.h
+++ b/arch/arm/mach-at91/include/mach/at91_aic.h
@@ -24,6 +24,8 @@ extern void __iomem *at91_aic_base;
 
 #define at91_aic_write(field, value) \
 	__raw_writel(value, at91_aic_base + field);
+
+void at91_handle_irq(struct pt_regs *regs);
 #else
 .extern at91_aic_base
 #endif
diff --git a/arch/arm/mach-at91/include/mach/entry-macro.S b/arch/arm/mach-at91/include/mach/entry-macro.S
index 423eea0..05f8e26 100644
--- a/arch/arm/mach-at91/include/mach/entry-macro.S
+++ b/arch/arm/mach-at91/include/mach/entry-macro.S
@@ -17,17 +17,8 @@
 	.endm
 
 	.macro  get_irqnr_preamble, base, tmp
-	ldr	\base, =at91_aic_base		@ base virtual address of AIC peripheral
-	ldr	\base, [\base]
-	.endm
-
-	.macro  arch_ret_to_user, tmp1, tmp2
 	.endm
 
 	.macro	get_irqnr_and_base, irqnr, irqstat, base, tmp
-	ldr	\irqnr, [\base, #AT91_AIC_IVR]		@ read IRQ vector register: de-asserts nIRQ to processor (and clears interrupt)
-	ldr	\irqstat, [\base, #AT91_AIC_ISR]	@ read interrupt source number
-	teq	\irqstat, #0				@ ISR is 0 when no current interrupt, or spurious interrupt
-	streq	\tmp, [\base, #AT91_AIC_EOICR]		@ not going to be handled further, then ACK it now.
 	.endm
 
diff --git a/arch/arm/mach-at91/irq.c b/arch/arm/mach-at91/irq.c
index be6b639..5a50e86 100644
--- a/arch/arm/mach-at91/irq.c
+++ b/arch/arm/mach-at91/irq.c
@@ -24,32 +24,29 @@
 #include <linux/module.h>
 #include <linux/mm.h>
 #include <linux/types.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/err.h>
 
 #include <mach/hardware.h>
 #include <asm/irq.h>
 #include <asm/setup.h>
+#include <asm/exception.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/irq.h>
 #include <asm/mach/map.h>
 
-void __iomem *at91_aic_base;
-
-static void at91_aic_mask_irq(struct irq_data *d)
-{
-	/* Disable interrupt on AIC */
-	at91_aic_write(AT91_AIC_IDCR, 1 << d->irq);
-}
-
-static void at91_aic_unmask_irq(struct irq_data *d)
-{
-	/* Enable interrupt on AIC */
-	at91_aic_write(AT91_AIC_IECR, 1 << d->irq);
-}
+void __iomem	*at91_aic_base;
+static struct irq_chip_generic *at91_aic_gc;
+struct device_node *at91_aic_np;
 
 unsigned int at91_extern_irq;
 
-#define is_extern_irq(irq) ((1 << (irq)) & at91_extern_irq)
+#define is_extern_irq(hwirq) ((1 << (hwirq)) & at91_extern_irq)
 
 static int at91_aic_set_type(struct irq_data *d, unsigned type)
 {
@@ -63,13 +60,13 @@ static int at91_aic_set_type(struct irq_data *d, unsigned type)
 		srctype = AT91_AIC_SRCTYPE_RISING;
 		break;
 	case IRQ_TYPE_LEVEL_LOW:
-		if ((d->irq == AT91_ID_FIQ) || is_extern_irq(d->irq))		/* only supported on external interrupts */
+		if ((d->hwirq == AT91_ID_FIQ) || is_extern_irq(d->hwirq))		/* only supported on external interrupts */
 			srctype = AT91_AIC_SRCTYPE_LOW;
 		else
 			return -EINVAL;
 		break;
 	case IRQ_TYPE_EDGE_FALLING:
-		if ((d->irq == AT91_ID_FIQ) || is_extern_irq(d->irq))		/* only supported on external interrupts */
+		if ((d->hwirq == AT91_ID_FIQ) || is_extern_irq(d->hwirq))		/* only supported on external interrupts */
 			srctype = AT91_AIC_SRCTYPE_FALLING;
 		else
 			return -EINVAL;
@@ -78,39 +75,34 @@ static int at91_aic_set_type(struct irq_data *d, unsigned type)
 		return -EINVAL;
 	}
 
-	smr = at91_aic_read(AT91_AIC_SMR(d->irq)) & ~AT91_AIC_SRCTYPE;
-	at91_aic_write(AT91_AIC_SMR(d->irq), smr | srctype);
+	smr = at91_aic_read(AT91_AIC_SMR(d->hwirq)) & ~AT91_AIC_SRCTYPE;
+	at91_aic_write(AT91_AIC_SMR(d->hwirq), smr | srctype);
 	return 0;
 }
 
 #ifdef CONFIG_PM
 
-static u32 wakeups;
 static u32 backups;
 
-static int at91_aic_set_wake(struct irq_data *d, unsigned value)
+void at91_irq_suspend(struct irq_data *d)
 {
-	if (unlikely(d->irq >= 32))
-		return -EINVAL;
-
-	if (value)
-		wakeups |= (1 << d->irq);
-	else
-		wakeups &= ~(1 << d->irq);
-
-	return 0;
-}
+	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
 
-void at91_irq_suspend(void)
-{
 	backups = at91_aic_read(AT91_AIC_IMR);
 	at91_aic_write(AT91_AIC_IDCR, backups);
-	at91_aic_write(AT91_AIC_IECR, wakeups);
+
+	irq_gc_lock(gc);
+	at91_aic_write(AT91_AIC_IECR, gc->wake_active);
+	irq_gc_unlock(gc);
 }
 
-void at91_irq_resume(void)
+void at91_irq_resume(struct irq_data *d)
 {
-	at91_aic_write(AT91_AIC_IDCR, wakeups);
+	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+
+	irq_gc_lock(gc);
+	at91_aic_write(AT91_AIC_IDCR, gc->wake_active);
+	irq_gc_unlock(gc);
 	at91_aic_write(AT91_AIC_IECR, backups);
 }
 
@@ -118,40 +110,86 @@ void at91_irq_resume(void)
 #define at91_aic_set_wake	NULL
 #endif
 
-static struct irq_chip at91_aic_chip = {
-	.name		= "AIC",
-	.irq_ack	= at91_aic_mask_irq,
-	.irq_mask	= at91_aic_mask_irq,
-	.irq_unmask	= at91_aic_unmask_irq,
-	.irq_set_type	= at91_aic_set_type,
-	.irq_set_wake	= at91_aic_set_wake,
+static void __init at91_aic_init_gc(struct irq_chip_generic *gc)
+
+{
+	struct irq_chip_type *ct = gc->chip_types;
+
+	at91_aic_gc = gc;
+	gc->wake_enabled = IRQ_MSK(NR_AIC_IRQS);
+	ct->chip.irq_ack = irq_gc_mask_set_bit;
+	ct->chip.irq_mask = irq_gc_mask_set_bit;
+	ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
+	ct->chip.irq_set_type = at91_aic_set_type;
+	ct->chip.irq_set_wake = irq_gc_set_wake;
+	ct->regs.mask = AT91_AIC_IDCR;
+	ct->regs.enable = AT91_AIC_IECR;
+}
+
+#if defined(CONFIG_OF)
+static int __init __at91_aic_of_init(struct device_node *node,
+				     struct device_node *parent)
+{
+	at91_aic_np = node;
+	at91_aic_base = of_iomap(at91_aic_np, 0);
+	return 0;
+}
+
+static const struct of_device_id aic_ids[] __initconst = {
+	{ .compatible = "atmel,at91rm9200-aic", .data = __at91_aic_of_init },
+	{ /*sentinel*/ }
 };
 
+static void __init at91_aic_of_init(void)
+{
+	of_irq_init(aic_ids);
+}
+#else
+static void __init at91_aic_of_init(void) {}
+#endif
+
 /*
  * Initialize the AIC interrupt controller.
  */
 void __init at91_aic_init(unsigned int priority[NR_AIC_IRQS])
 {
-	unsigned int i;
-
-	at91_aic_base = ioremap(AT91_AIC, 512);
+	unsigned int	i;
+	int		irq_base;
+	int		ret;
+
+	if(of_have_populated_dt()) {
+		at91_aic_of_init();
+		irq_base = -1;
+	} else {
+		at91_aic_base = ioremap(AT91_AIC, 512);
+		irq_base = AIC_BASE_IRQ;
+	}
 
 	if (!at91_aic_base)
-		panic("Impossible to ioremap AT91_AIC\n");
+		panic("AIC: Unable to ioremap AIC registers\n");
+
+	ret = irq_setup_generic_chip_domain("AIC", of_node_get(at91_aic_np),
+				      1, irq_base, at91_aic_base,
+				      handle_level_irq,
+				      NR_AIC_IRQS,
+				      IRQ_GC_INIT_MASK_CACHE,
+				      IRQ_NOREQUEST | IRQ_NOPROBE, 0,
+				      at91_aic_init_gc, NULL);
+
+	if (ret)
+		printk(KERN_CRIT "Unable to get generic chip domain\n");
 
 	/*
 	 * The IVR is used by macro get_irqnr_and_base to read and verify.
 	 * The irq number is NR_AIC_IRQS when a spurious interrupt has occurred.
 	 */
 	for (i = 0; i < NR_AIC_IRQS; i++) {
+
 		/* Put irq number in Source Vector Register: */
 		at91_aic_write(AT91_AIC_SVR(i), i);
 		/* Active Low interrupt, with the specified priority */
 		at91_aic_write(AT91_AIC_SMR(i), AT91_AIC_SRCTYPE_LOW | priority[i]);
 
-		irq_set_chip_and_handler(i, &at91_aic_chip, handle_level_irq);
-		set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
-
 		/* Perform 8 End Of Interrupt Command to make sure AIC will not Lock out nIRQ */
 		if (i < 8)
 			at91_aic_write(AT91_AIC_EOICR, 0);
@@ -170,3 +208,18 @@ void __init at91_aic_init(unsigned int priority[NR_AIC_IRQS])
 	at91_aic_write(AT91_AIC_IDCR, 0xFFFFFFFF);
 	at91_aic_write(AT91_AIC_ICCR, 0xFFFFFFFF);
 }
+
+asmlinkage void __exception_irq_entry at91_handle_irq(struct pt_regs *regs)
+{
+        u32 stat, irq;
+
+	irq = at91_aic_read(AT91_AIC_IVR);
+	stat = at91_aic_read(AT91_AIC_ISR);
+
+	if (stat == 0) {
+		/* No interrupt or spurious interrupt: ACK it now */
+		at91_aic_write(AT91_AIC_EOICR, 0);
+	}
+
+	handle_IRQ(irq_find_mapping(at91_aic_gc->domain, irq), regs);
+}
-- 
1.7.5.4




More information about the linux-arm-kernel mailing list