mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-05 00:20:32 +00:00
ARM: at91/aic: add irq domain and device tree support
Add an irqdomain for the AIC interrupt controller. The device tree support is mapping the registers and is using the irq_domain_add_legacy() to manage hwirq translation. The documentation is describing the meaning of the two cells required for using this "interrupt-controller" in a device tree node. Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com> Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
This commit is contained in:
parent
89d4a1753b
commit
e261501d05
5 changed files with 117 additions and 30 deletions
38
Documentation/devicetree/bindings/arm/atmel-aic.txt
Normal file
38
Documentation/devicetree/bindings/arm/atmel-aic.txt
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
* Advanced Interrupt Controller (AIC)
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: Should be "atmel,<chip>-aic"
|
||||||
|
- interrupt-controller: Identifies the node as an interrupt controller.
|
||||||
|
- interrupt-parent: For single AIC system, it is an empty property.
|
||||||
|
- #interrupt-cells: The number of cells to define the interrupts. It sould be 2.
|
||||||
|
The first cell is the IRQ number (aka "Peripheral IDentifier" on datasheet).
|
||||||
|
The second cell is used to specify flags:
|
||||||
|
bits[3:0] trigger type and level flags:
|
||||||
|
1 = low-to-high edge triggered.
|
||||||
|
2 = high-to-low edge triggered.
|
||||||
|
4 = active high level-sensitive.
|
||||||
|
8 = active low level-sensitive.
|
||||||
|
Valid combinations are 1, 2, 3, 4, 8.
|
||||||
|
Default flag for internal sources should be set to 4 (active high).
|
||||||
|
- reg: Should contain AIC registers location and length
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
/*
|
||||||
|
* AIC
|
||||||
|
*/
|
||||||
|
aic: interrupt-controller@fffff000 {
|
||||||
|
compatible = "atmel,at91rm9200-aic";
|
||||||
|
interrupt-controller;
|
||||||
|
interrupt-parent;
|
||||||
|
#interrupt-cells = <2>;
|
||||||
|
reg = <0xfffff000 0x200>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* An interrupt generating device that is wired to an AIC.
|
||||||
|
*/
|
||||||
|
dma: dma-controller@ffffec00 {
|
||||||
|
compatible = "atmel,at91sam9g45-dma";
|
||||||
|
reg = <0xffffec00 0x200>;
|
||||||
|
interrupts = <21 4>;
|
||||||
|
};
|
|
@ -322,6 +322,7 @@ config ARCH_AT91
|
||||||
select ARCH_REQUIRE_GPIOLIB
|
select ARCH_REQUIRE_GPIOLIB
|
||||||
select HAVE_CLK
|
select HAVE_CLK
|
||||||
select CLKDEV_LOOKUP
|
select CLKDEV_LOOKUP
|
||||||
|
select IRQ_DOMAIN
|
||||||
help
|
help
|
||||||
This enables support for systems based on the Atmel AT91RM9200,
|
This enables support for systems based on the Atmel AT91RM9200,
|
||||||
AT91SAM9 processors.
|
AT91SAM9 processors.
|
||||||
|
|
|
@ -47,7 +47,7 @@ apb {
|
||||||
ranges;
|
ranges;
|
||||||
|
|
||||||
aic: interrupt-controller@fffff000 {
|
aic: interrupt-controller@fffff000 {
|
||||||
#interrupt-cells = <1>;
|
#interrupt-cells = <2>;
|
||||||
compatible = "atmel,at91rm9200-aic";
|
compatible = "atmel,at91rm9200-aic";
|
||||||
interrupt-controller;
|
interrupt-controller;
|
||||||
interrupt-parent;
|
interrupt-parent;
|
||||||
|
@ -57,14 +57,14 @@ aic: interrupt-controller@fffff000 {
|
||||||
dbgu: serial@fffff200 {
|
dbgu: serial@fffff200 {
|
||||||
compatible = "atmel,at91sam9260-usart";
|
compatible = "atmel,at91sam9260-usart";
|
||||||
reg = <0xfffff200 0x200>;
|
reg = <0xfffff200 0x200>;
|
||||||
interrupts = <1>;
|
interrupts = <1 4>;
|
||||||
status = "disabled";
|
status = "disabled";
|
||||||
};
|
};
|
||||||
|
|
||||||
usart0: serial@fffb0000 {
|
usart0: serial@fffb0000 {
|
||||||
compatible = "atmel,at91sam9260-usart";
|
compatible = "atmel,at91sam9260-usart";
|
||||||
reg = <0xfffb0000 0x200>;
|
reg = <0xfffb0000 0x200>;
|
||||||
interrupts = <6>;
|
interrupts = <6 4>;
|
||||||
atmel,use-dma-rx;
|
atmel,use-dma-rx;
|
||||||
atmel,use-dma-tx;
|
atmel,use-dma-tx;
|
||||||
status = "disabled";
|
status = "disabled";
|
||||||
|
@ -73,7 +73,7 @@ usart0: serial@fffb0000 {
|
||||||
usart1: serial@fffb4000 {
|
usart1: serial@fffb4000 {
|
||||||
compatible = "atmel,at91sam9260-usart";
|
compatible = "atmel,at91sam9260-usart";
|
||||||
reg = <0xfffb4000 0x200>;
|
reg = <0xfffb4000 0x200>;
|
||||||
interrupts = <7>;
|
interrupts = <7 4>;
|
||||||
atmel,use-dma-rx;
|
atmel,use-dma-rx;
|
||||||
atmel,use-dma-tx;
|
atmel,use-dma-tx;
|
||||||
status = "disabled";
|
status = "disabled";
|
||||||
|
@ -82,7 +82,7 @@ usart1: serial@fffb4000 {
|
||||||
usart2: serial@fffb8000 {
|
usart2: serial@fffb8000 {
|
||||||
compatible = "atmel,at91sam9260-usart";
|
compatible = "atmel,at91sam9260-usart";
|
||||||
reg = <0xfffb8000 0x200>;
|
reg = <0xfffb8000 0x200>;
|
||||||
interrupts = <8>;
|
interrupts = <8 4>;
|
||||||
atmel,use-dma-rx;
|
atmel,use-dma-rx;
|
||||||
atmel,use-dma-tx;
|
atmel,use-dma-tx;
|
||||||
status = "disabled";
|
status = "disabled";
|
||||||
|
@ -91,7 +91,7 @@ usart2: serial@fffb8000 {
|
||||||
usart3: serial@fffd0000 {
|
usart3: serial@fffd0000 {
|
||||||
compatible = "atmel,at91sam9260-usart";
|
compatible = "atmel,at91sam9260-usart";
|
||||||
reg = <0xfffd0000 0x200>;
|
reg = <0xfffd0000 0x200>;
|
||||||
interrupts = <23>;
|
interrupts = <23 4>;
|
||||||
atmel,use-dma-rx;
|
atmel,use-dma-rx;
|
||||||
atmel,use-dma-tx;
|
atmel,use-dma-tx;
|
||||||
status = "disabled";
|
status = "disabled";
|
||||||
|
@ -100,7 +100,7 @@ usart3: serial@fffd0000 {
|
||||||
usart4: serial@fffd4000 {
|
usart4: serial@fffd4000 {
|
||||||
compatible = "atmel,at91sam9260-usart";
|
compatible = "atmel,at91sam9260-usart";
|
||||||
reg = <0xfffd4000 0x200>;
|
reg = <0xfffd4000 0x200>;
|
||||||
interrupts = <24>;
|
interrupts = <24 4>;
|
||||||
atmel,use-dma-rx;
|
atmel,use-dma-rx;
|
||||||
atmel,use-dma-tx;
|
atmel,use-dma-tx;
|
||||||
status = "disabled";
|
status = "disabled";
|
||||||
|
@ -109,7 +109,7 @@ usart4: serial@fffd4000 {
|
||||||
usart5: serial@fffd8000 {
|
usart5: serial@fffd8000 {
|
||||||
compatible = "atmel,at91sam9260-usart";
|
compatible = "atmel,at91sam9260-usart";
|
||||||
reg = <0xfffd8000 0x200>;
|
reg = <0xfffd8000 0x200>;
|
||||||
interrupts = <25>;
|
interrupts = <25 4>;
|
||||||
atmel,use-dma-rx;
|
atmel,use-dma-rx;
|
||||||
atmel,use-dma-tx;
|
atmel,use-dma-tx;
|
||||||
status = "disabled";
|
status = "disabled";
|
||||||
|
@ -118,7 +118,7 @@ usart5: serial@fffd8000 {
|
||||||
macb0: ethernet@fffc4000 {
|
macb0: ethernet@fffc4000 {
|
||||||
compatible = "cdns,at32ap7000-macb", "cdns,macb";
|
compatible = "cdns,at32ap7000-macb", "cdns,macb";
|
||||||
reg = <0xfffc4000 0x100>;
|
reg = <0xfffc4000 0x100>;
|
||||||
interrupts = <21>;
|
interrupts = <21 4>;
|
||||||
status = "disabled";
|
status = "disabled";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -46,7 +46,7 @@ apb {
|
||||||
ranges;
|
ranges;
|
||||||
|
|
||||||
aic: interrupt-controller@fffff000 {
|
aic: interrupt-controller@fffff000 {
|
||||||
#interrupt-cells = <1>;
|
#interrupt-cells = <2>;
|
||||||
compatible = "atmel,at91rm9200-aic";
|
compatible = "atmel,at91rm9200-aic";
|
||||||
interrupt-controller;
|
interrupt-controller;
|
||||||
interrupt-parent;
|
interrupt-parent;
|
||||||
|
@ -56,20 +56,20 @@ aic: interrupt-controller@fffff000 {
|
||||||
dma: dma-controller@ffffec00 {
|
dma: dma-controller@ffffec00 {
|
||||||
compatible = "atmel,at91sam9g45-dma";
|
compatible = "atmel,at91sam9g45-dma";
|
||||||
reg = <0xffffec00 0x200>;
|
reg = <0xffffec00 0x200>;
|
||||||
interrupts = <21>;
|
interrupts = <21 4>;
|
||||||
};
|
};
|
||||||
|
|
||||||
dbgu: serial@ffffee00 {
|
dbgu: serial@ffffee00 {
|
||||||
compatible = "atmel,at91sam9260-usart";
|
compatible = "atmel,at91sam9260-usart";
|
||||||
reg = <0xffffee00 0x200>;
|
reg = <0xffffee00 0x200>;
|
||||||
interrupts = <1>;
|
interrupts = <1 4>;
|
||||||
status = "disabled";
|
status = "disabled";
|
||||||
};
|
};
|
||||||
|
|
||||||
usart0: serial@fff8c000 {
|
usart0: serial@fff8c000 {
|
||||||
compatible = "atmel,at91sam9260-usart";
|
compatible = "atmel,at91sam9260-usart";
|
||||||
reg = <0xfff8c000 0x200>;
|
reg = <0xfff8c000 0x200>;
|
||||||
interrupts = <7>;
|
interrupts = <7 4>;
|
||||||
atmel,use-dma-rx;
|
atmel,use-dma-rx;
|
||||||
atmel,use-dma-tx;
|
atmel,use-dma-tx;
|
||||||
status = "disabled";
|
status = "disabled";
|
||||||
|
@ -78,7 +78,7 @@ usart0: serial@fff8c000 {
|
||||||
usart1: serial@fff90000 {
|
usart1: serial@fff90000 {
|
||||||
compatible = "atmel,at91sam9260-usart";
|
compatible = "atmel,at91sam9260-usart";
|
||||||
reg = <0xfff90000 0x200>;
|
reg = <0xfff90000 0x200>;
|
||||||
interrupts = <8>;
|
interrupts = <8 4>;
|
||||||
atmel,use-dma-rx;
|
atmel,use-dma-rx;
|
||||||
atmel,use-dma-tx;
|
atmel,use-dma-tx;
|
||||||
status = "disabled";
|
status = "disabled";
|
||||||
|
@ -87,7 +87,7 @@ usart1: serial@fff90000 {
|
||||||
usart2: serial@fff94000 {
|
usart2: serial@fff94000 {
|
||||||
compatible = "atmel,at91sam9260-usart";
|
compatible = "atmel,at91sam9260-usart";
|
||||||
reg = <0xfff94000 0x200>;
|
reg = <0xfff94000 0x200>;
|
||||||
interrupts = <9>;
|
interrupts = <9 4>;
|
||||||
atmel,use-dma-rx;
|
atmel,use-dma-rx;
|
||||||
atmel,use-dma-tx;
|
atmel,use-dma-tx;
|
||||||
status = "disabled";
|
status = "disabled";
|
||||||
|
@ -96,7 +96,7 @@ usart2: serial@fff94000 {
|
||||||
usart3: serial@fff98000 {
|
usart3: serial@fff98000 {
|
||||||
compatible = "atmel,at91sam9260-usart";
|
compatible = "atmel,at91sam9260-usart";
|
||||||
reg = <0xfff98000 0x200>;
|
reg = <0xfff98000 0x200>;
|
||||||
interrupts = <10>;
|
interrupts = <10 4>;
|
||||||
atmel,use-dma-rx;
|
atmel,use-dma-rx;
|
||||||
atmel,use-dma-tx;
|
atmel,use-dma-tx;
|
||||||
status = "disabled";
|
status = "disabled";
|
||||||
|
@ -105,7 +105,7 @@ usart3: serial@fff98000 {
|
||||||
macb0: ethernet@fffbc000 {
|
macb0: ethernet@fffbc000 {
|
||||||
compatible = "cdns,at32ap7000-macb", "cdns,macb";
|
compatible = "cdns,at32ap7000-macb", "cdns,macb";
|
||||||
reg = <0xfffbc000 0x100>;
|
reg = <0xfffbc000 0x100>;
|
||||||
interrupts = <25>;
|
interrupts = <25 4>;
|
||||||
status = "disabled";
|
status = "disabled";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,6 +24,12 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/mm.h>
|
#include <linux/mm.h>
|
||||||
#include <linux/types.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 <mach/hardware.h>
|
||||||
#include <asm/irq.h>
|
#include <asm/irq.h>
|
||||||
|
@ -34,22 +40,24 @@
|
||||||
#include <asm/mach/map.h>
|
#include <asm/mach/map.h>
|
||||||
|
|
||||||
void __iomem *at91_aic_base;
|
void __iomem *at91_aic_base;
|
||||||
|
static struct irq_domain *at91_aic_domain;
|
||||||
|
static struct device_node *at91_aic_np;
|
||||||
|
|
||||||
static void at91_aic_mask_irq(struct irq_data *d)
|
static void at91_aic_mask_irq(struct irq_data *d)
|
||||||
{
|
{
|
||||||
/* Disable interrupt on AIC */
|
/* Disable interrupt on AIC */
|
||||||
at91_aic_write(AT91_AIC_IDCR, 1 << d->irq);
|
at91_aic_write(AT91_AIC_IDCR, 1 << d->hwirq);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void at91_aic_unmask_irq(struct irq_data *d)
|
static void at91_aic_unmask_irq(struct irq_data *d)
|
||||||
{
|
{
|
||||||
/* Enable interrupt on AIC */
|
/* Enable interrupt on AIC */
|
||||||
at91_aic_write(AT91_AIC_IECR, 1 << d->irq);
|
at91_aic_write(AT91_AIC_IECR, 1 << d->hwirq);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int at91_extern_irq;
|
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)
|
static int at91_aic_set_type(struct irq_data *d, unsigned type)
|
||||||
{
|
{
|
||||||
|
@ -63,13 +71,13 @@ static int at91_aic_set_type(struct irq_data *d, unsigned type)
|
||||||
srctype = AT91_AIC_SRCTYPE_RISING;
|
srctype = AT91_AIC_SRCTYPE_RISING;
|
||||||
break;
|
break;
|
||||||
case IRQ_TYPE_LEVEL_LOW:
|
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;
|
srctype = AT91_AIC_SRCTYPE_LOW;
|
||||||
else
|
else
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
break;
|
break;
|
||||||
case IRQ_TYPE_EDGE_FALLING:
|
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;
|
srctype = AT91_AIC_SRCTYPE_FALLING;
|
||||||
else
|
else
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -78,8 +86,8 @@ static int at91_aic_set_type(struct irq_data *d, unsigned type)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
smr = at91_aic_read(AT91_AIC_SMR(d->irq)) & ~AT91_AIC_SRCTYPE;
|
smr = at91_aic_read(AT91_AIC_SMR(d->hwirq)) & ~AT91_AIC_SRCTYPE;
|
||||||
at91_aic_write(AT91_AIC_SMR(d->irq), smr | srctype);
|
at91_aic_write(AT91_AIC_SMR(d->hwirq), smr | srctype);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,13 +98,13 @@ static u32 backups;
|
||||||
|
|
||||||
static int at91_aic_set_wake(struct irq_data *d, unsigned value)
|
static int at91_aic_set_wake(struct irq_data *d, unsigned value)
|
||||||
{
|
{
|
||||||
if (unlikely(d->irq >= 32))
|
if (unlikely(d->hwirq >= NR_AIC_IRQS))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (value)
|
if (value)
|
||||||
wakeups |= (1 << d->irq);
|
wakeups |= (1 << d->hwirq);
|
||||||
else
|
else
|
||||||
wakeups &= ~(1 << d->irq);
|
wakeups &= ~(1 << d->hwirq);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -127,24 +135,64 @@ static struct irq_chip at91_aic_chip = {
|
||||||
.irq_set_wake = at91_aic_set_wake,
|
.irq_set_wake = at91_aic_set_wake,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if defined(CONFIG_OF)
|
||||||
|
static int __init __at91_aic_of_init(struct device_node *node,
|
||||||
|
struct device_node *parent)
|
||||||
|
{
|
||||||
|
at91_aic_base = of_iomap(node, 0);
|
||||||
|
at91_aic_np = node;
|
||||||
|
|
||||||
|
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.
|
* Initialize the AIC interrupt controller.
|
||||||
*/
|
*/
|
||||||
void __init at91_aic_init(unsigned int priority[NR_AIC_IRQS])
|
void __init at91_aic_init(unsigned int priority[NR_AIC_IRQS])
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
int irq_base;
|
||||||
|
|
||||||
at91_aic_base = ioremap(AT91_AIC, 512);
|
if (of_have_populated_dt())
|
||||||
|
at91_aic_of_init();
|
||||||
|
else
|
||||||
|
at91_aic_base = ioremap(AT91_AIC, 512);
|
||||||
|
|
||||||
if (!at91_aic_base)
|
if (!at91_aic_base)
|
||||||
panic("Impossible to ioremap AT91_AIC\n");
|
panic("Unable to ioremap AIC registers\n");
|
||||||
|
|
||||||
|
/* Add irq domain for AIC */
|
||||||
|
irq_base = irq_alloc_descs(-1, 0, NR_AIC_IRQS, 0);
|
||||||
|
if (irq_base < 0) {
|
||||||
|
WARN(1, "Cannot allocate irq_descs, assuming pre-allocated\n");
|
||||||
|
irq_base = 0;
|
||||||
|
}
|
||||||
|
at91_aic_domain = irq_domain_add_legacy(at91_aic_np, NR_AIC_IRQS,
|
||||||
|
irq_base, 0,
|
||||||
|
&irq_domain_simple_ops, NULL);
|
||||||
|
|
||||||
|
if (!at91_aic_domain)
|
||||||
|
panic("Unable to add AIC irq domain\n");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The IVR is used by macro get_irqnr_and_base to read and verify.
|
* 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.
|
* The irq number is NR_AIC_IRQS when a spurious interrupt has occurred.
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < NR_AIC_IRQS; i++) {
|
for (i = 0; i < NR_AIC_IRQS; i++) {
|
||||||
/* Put irq number in Source Vector Register: */
|
/* Put hardware irq number in Source Vector Register: */
|
||||||
at91_aic_write(AT91_AIC_SVR(i), i);
|
at91_aic_write(AT91_AIC_SVR(i), i);
|
||||||
/* Active Low interrupt, with the specified priority */
|
/* Active Low interrupt, with the specified priority */
|
||||||
at91_aic_write(AT91_AIC_SMR(i), AT91_AIC_SRCTYPE_LOW | priority[i]);
|
at91_aic_write(AT91_AIC_SMR(i), AT91_AIC_SRCTYPE_LOW | priority[i]);
|
||||||
|
|
Loading…
Reference in a new issue