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:
@ -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]);
|
||||
|
Reference in New Issue
Block a user