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:
Родитель
89d4a1753b
Коммит
e261501d05
|
@ -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 HAVE_CLK
|
||||
select CLKDEV_LOOKUP
|
||||
select IRQ_DOMAIN
|
||||
help
|
||||
This enables support for systems based on the Atmel AT91RM9200,
|
||||
AT91SAM9 processors.
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
ranges;
|
||||
|
||||
aic: interrupt-controller@fffff000 {
|
||||
#interrupt-cells = <1>;
|
||||
#interrupt-cells = <2>;
|
||||
compatible = "atmel,at91rm9200-aic";
|
||||
interrupt-controller;
|
||||
interrupt-parent;
|
||||
|
@ -57,14 +57,14 @@
|
|||
dbgu: serial@fffff200 {
|
||||
compatible = "atmel,at91sam9260-usart";
|
||||
reg = <0xfffff200 0x200>;
|
||||
interrupts = <1>;
|
||||
interrupts = <1 4>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
usart0: serial@fffb0000 {
|
||||
compatible = "atmel,at91sam9260-usart";
|
||||
reg = <0xfffb0000 0x200>;
|
||||
interrupts = <6>;
|
||||
interrupts = <6 4>;
|
||||
atmel,use-dma-rx;
|
||||
atmel,use-dma-tx;
|
||||
status = "disabled";
|
||||
|
@ -73,7 +73,7 @@
|
|||
usart1: serial@fffb4000 {
|
||||
compatible = "atmel,at91sam9260-usart";
|
||||
reg = <0xfffb4000 0x200>;
|
||||
interrupts = <7>;
|
||||
interrupts = <7 4>;
|
||||
atmel,use-dma-rx;
|
||||
atmel,use-dma-tx;
|
||||
status = "disabled";
|
||||
|
@ -82,7 +82,7 @@
|
|||
usart2: serial@fffb8000 {
|
||||
compatible = "atmel,at91sam9260-usart";
|
||||
reg = <0xfffb8000 0x200>;
|
||||
interrupts = <8>;
|
||||
interrupts = <8 4>;
|
||||
atmel,use-dma-rx;
|
||||
atmel,use-dma-tx;
|
||||
status = "disabled";
|
||||
|
@ -91,7 +91,7 @@
|
|||
usart3: serial@fffd0000 {
|
||||
compatible = "atmel,at91sam9260-usart";
|
||||
reg = <0xfffd0000 0x200>;
|
||||
interrupts = <23>;
|
||||
interrupts = <23 4>;
|
||||
atmel,use-dma-rx;
|
||||
atmel,use-dma-tx;
|
||||
status = "disabled";
|
||||
|
@ -100,7 +100,7 @@
|
|||
usart4: serial@fffd4000 {
|
||||
compatible = "atmel,at91sam9260-usart";
|
||||
reg = <0xfffd4000 0x200>;
|
||||
interrupts = <24>;
|
||||
interrupts = <24 4>;
|
||||
atmel,use-dma-rx;
|
||||
atmel,use-dma-tx;
|
||||
status = "disabled";
|
||||
|
@ -109,7 +109,7 @@
|
|||
usart5: serial@fffd8000 {
|
||||
compatible = "atmel,at91sam9260-usart";
|
||||
reg = <0xfffd8000 0x200>;
|
||||
interrupts = <25>;
|
||||
interrupts = <25 4>;
|
||||
atmel,use-dma-rx;
|
||||
atmel,use-dma-tx;
|
||||
status = "disabled";
|
||||
|
@ -118,7 +118,7 @@
|
|||
macb0: ethernet@fffc4000 {
|
||||
compatible = "cdns,at32ap7000-macb", "cdns,macb";
|
||||
reg = <0xfffc4000 0x100>;
|
||||
interrupts = <21>;
|
||||
interrupts = <21 4>;
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
ranges;
|
||||
|
||||
aic: interrupt-controller@fffff000 {
|
||||
#interrupt-cells = <1>;
|
||||
#interrupt-cells = <2>;
|
||||
compatible = "atmel,at91rm9200-aic";
|
||||
interrupt-controller;
|
||||
interrupt-parent;
|
||||
|
@ -56,20 +56,20 @@
|
|||
dma: dma-controller@ffffec00 {
|
||||
compatible = "atmel,at91sam9g45-dma";
|
||||
reg = <0xffffec00 0x200>;
|
||||
interrupts = <21>;
|
||||
interrupts = <21 4>;
|
||||
};
|
||||
|
||||
dbgu: serial@ffffee00 {
|
||||
compatible = "atmel,at91sam9260-usart";
|
||||
reg = <0xffffee00 0x200>;
|
||||
interrupts = <1>;
|
||||
interrupts = <1 4>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
usart0: serial@fff8c000 {
|
||||
compatible = "atmel,at91sam9260-usart";
|
||||
reg = <0xfff8c000 0x200>;
|
||||
interrupts = <7>;
|
||||
interrupts = <7 4>;
|
||||
atmel,use-dma-rx;
|
||||
atmel,use-dma-tx;
|
||||
status = "disabled";
|
||||
|
@ -78,7 +78,7 @@
|
|||
usart1: serial@fff90000 {
|
||||
compatible = "atmel,at91sam9260-usart";
|
||||
reg = <0xfff90000 0x200>;
|
||||
interrupts = <8>;
|
||||
interrupts = <8 4>;
|
||||
atmel,use-dma-rx;
|
||||
atmel,use-dma-tx;
|
||||
status = "disabled";
|
||||
|
@ -87,7 +87,7 @@
|
|||
usart2: serial@fff94000 {
|
||||
compatible = "atmel,at91sam9260-usart";
|
||||
reg = <0xfff94000 0x200>;
|
||||
interrupts = <9>;
|
||||
interrupts = <9 4>;
|
||||
atmel,use-dma-rx;
|
||||
atmel,use-dma-tx;
|
||||
status = "disabled";
|
||||
|
@ -96,7 +96,7 @@
|
|||
usart3: serial@fff98000 {
|
||||
compatible = "atmel,at91sam9260-usart";
|
||||
reg = <0xfff98000 0x200>;
|
||||
interrupts = <10>;
|
||||
interrupts = <10 4>;
|
||||
atmel,use-dma-rx;
|
||||
atmel,use-dma-tx;
|
||||
status = "disabled";
|
||||
|
@ -105,7 +105,7 @@
|
|||
macb0: ethernet@fffbc000 {
|
||||
compatible = "cdns,at32ap7000-macb", "cdns,macb";
|
||||
reg = <0xfffbc000 0x100>;
|
||||
interrupts = <25>;
|
||||
interrupts = <25 4>;
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
|
|
|
@ -24,6 +24,12 @@
|
|||
#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>
|
||||
|
@ -34,22 +40,24 @@
|
|||
#include <asm/mach/map.h>
|
||||
|
||||
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)
|
||||
{
|
||||
/* 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)
|
||||
{
|
||||
/* 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;
|
||||
|
||||
#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 +71,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,8 +86,8 @@ 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;
|
||||
}
|
||||
|
||||
|
@ -90,13 +98,13 @@ static u32 backups;
|
|||
|
||||
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;
|
||||
|
||||
if (value)
|
||||
wakeups |= (1 << d->irq);
|
||||
wakeups |= (1 << d->hwirq);
|
||||
else
|
||||
wakeups &= ~(1 << d->irq);
|
||||
wakeups &= ~(1 << d->hwirq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -127,24 +135,64 @@ static struct irq_chip at91_aic_chip = {
|
|||
.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.
|
||||
*/
|
||||
void __init at91_aic_init(unsigned int priority[NR_AIC_IRQS])
|
||||
{
|
||||
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)
|
||||
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 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: */
|
||||
/* Put hardware 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]);
|
||||
|
|
Загрузка…
Ссылка в новой задаче