Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 296197
b: refs/heads/master
c: 8014d6f
h: refs/heads/master
i:
  296195: 5dae5d5
v: v3
  • Loading branch information
Nicolas Ferre committed Mar 1, 2012
1 parent b9db17a commit 9deec39
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 62 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 34bc485ca143c4773fc2afbbeb41e50f86445d0d
refs/heads/master: 8014d6f4dd074d4d248d3de7f63348fa2568476b
11 changes: 10 additions & 1 deletion trunk/arch/arm/mach-at91/board-dt.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>

#include <mach/hardware.h>
Expand Down Expand Up @@ -80,9 +82,16 @@ static void __init ek_add_device_nand(void)
at91_add_device_nand(&ek_nand_data);
}

static const struct of_device_id irq_of_match[] __initconst = {

{ .compatible = "atmel,at91rm9200-aic", .data = at91_aic_of_init },
{ .compatible = "atmel,at91rm9200-gpio", .data = at91_gpio_of_irq_setup },
{ /*sentinel*/ }
};

static void __init at91_dt_init_irq(void)
{
at91_init_irq_default();
of_irq_init(irq_of_match);
}

static void __init at91_dt_device_init(void)
Expand Down
6 changes: 6 additions & 0 deletions trunk/arch/arm/mach-at91/generic.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/

#include <linux/clkdev.h>
#include <linux/of.h>

/* Map io */
extern void __init at91_map_io(void);
Expand All @@ -25,6 +26,9 @@ extern void __init at91_init_irq_default(void);
extern void __init at91_init_interrupts(unsigned int priority[]);
extern void __init at91x40_init_interrupts(unsigned int priority[]);
extern void __init at91_aic_init(unsigned int priority[]);
extern int __init at91_aic_of_init(struct device_node *node,
struct device_node *parent);


/* Timer */
struct sys_timer;
Expand Down Expand Up @@ -84,5 +88,7 @@ struct at91_gpio_bank {
};
extern void __init at91_gpio_init(struct at91_gpio_bank *, int nr_banks);
extern void __init at91_gpio_irq_setup(void);
extern int __init at91_gpio_of_irq_setup(struct device_node *node,
struct device_node *parent);

extern int at91_extern_irq;
122 changes: 94 additions & 28 deletions trunk/arch/arm/mach-at91/gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <linux/irqdomain.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>

#include <mach/hardware.h>
#include <mach/at91_pio.h>
Expand All @@ -34,6 +35,7 @@ struct at91_gpio_chip {
struct gpio_chip chip;
struct at91_gpio_chip *next; /* Bank sharing same clock */
int pioc_hwirq; /* PIO bank interrupt identifier on AIC */
int pioc_virq; /* PIO bank Linux virtual interrupt */
int pioc_idx; /* PIO bank index */
void __iomem *regbase; /* PIO bank virtual address */
struct clk *clock; /* associated clock */
Expand Down Expand Up @@ -292,7 +294,7 @@ static int gpio_irq_set_wake(struct irq_data *d, unsigned state)
else
wakeups[bank] &= ~mask;

irq_set_irq_wake(gpio_chip[bank].pioc_hwirq, state);
irq_set_irq_wake(at91_gpio->pioc_virq, state);

return 0;
}
Expand Down Expand Up @@ -394,12 +396,12 @@ static struct irq_chip gpio_irqchip = {

static void gpio_irq_handler(unsigned irq, struct irq_desc *desc)
{
unsigned virq;
struct irq_data *idata = irq_desc_get_irq_data(desc);
struct irq_chip *chip = irq_data_get_irq_chip(idata);
struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(idata);
void __iomem *pio = at91_gpio->regbase;
u32 isr;
unsigned long isr;
int n;

/* temporarily mask (level sensitive) parent IRQ */
chip->irq_ack(idata);
Expand All @@ -417,13 +419,10 @@ static void gpio_irq_handler(unsigned irq, struct irq_desc *desc)
continue;
}

virq = gpio_to_irq(at91_gpio->chip.base);

while (isr) {
if (isr & 1)
generic_handle_irq(virq);
virq++;
isr >>= 1;
n = find_first_bit(&isr, BITS_PER_LONG);
while (n < BITS_PER_LONG) {
generic_handle_irq(irq_find_mapping(at91_gpio->domain, n));
n = find_next_bit(&isr, BITS_PER_LONG, n + 1);
}
}
chip->irq_unmask(idata);
Expand Down Expand Up @@ -492,37 +491,99 @@ postcore_initcall(at91_gpio_debugfs_init);

/*--------------------------------------------------------------------------*/

/*
* This lock class tells lockdep that GPIO irqs are in a different
* category than their parents, so it won't report false recursion.
*/
static struct lock_class_key gpio_lock_class;

#if defined(CONFIG_OF)
static int at91_gpio_irq_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
struct at91_gpio_chip *at91_gpio = h->host_data;

irq_set_lockdep_class(virq, &gpio_lock_class);

/*
* Can use the "simple" and not "edge" handler since it's
* shorter, and the AIC handles interrupts sanely.
*/
irq_set_chip_and_handler(virq, &gpio_irqchip,
handle_simple_irq);
set_irq_flags(virq, IRQF_VALID);
irq_set_chip_data(virq, at91_gpio);

return 0;
}

static struct irq_domain_ops at91_gpio_ops = {
.map = at91_gpio_irq_map,
.xlate = irq_domain_xlate_twocell,
};

int __init at91_gpio_of_irq_setup(struct device_node *node,
struct device_node *parent)
{
struct at91_gpio_chip *prev = NULL;
int alias_idx = of_alias_get_id(node, "gpio");
struct at91_gpio_chip *at91_gpio = &gpio_chip[alias_idx];

/* Disable irqs of this PIO controller */
__raw_writel(~0, at91_gpio->regbase + PIO_IDR);

/* Setup irq domain */
at91_gpio->domain = irq_domain_add_linear(node, at91_gpio->chip.ngpio,
&at91_gpio_ops, at91_gpio);
if (!at91_gpio->domain)
panic("at91_gpio.%d: couldn't allocate irq domain (DT).\n",
at91_gpio->pioc_idx);

/* Setup chained handler */
if (at91_gpio->pioc_idx)
prev = &gpio_chip[at91_gpio->pioc_idx - 1];

/* The toplevel handler handles one bank of GPIOs, except
* on some SoC it can handles up to three...
* We only set up the handler for the first of the list.
*/
if (prev && prev->next == at91_gpio)
return 0;

at91_gpio->pioc_virq = irq_create_mapping(irq_find_host(parent),
at91_gpio->pioc_hwirq);
irq_set_chip_data(at91_gpio->pioc_virq, at91_gpio);
irq_set_chained_handler(at91_gpio->pioc_virq, gpio_irq_handler);

return 0;
}
#else
int __init at91_gpio_of_irq_setup(struct device_node *node,
struct device_node *parent)
{
return -EINVAL;
}
#endif

/*
* irqdomain initialization: pile up irqdomains on top of AIC range
*/
static void __init at91_gpio_irqdomain(struct at91_gpio_chip *at91_gpio)
{
int irq_base;
#if defined(CONFIG_OF)
struct device_node *of_node = at91_gpio->chip.of_node;
#else
struct device_node *of_node = NULL;
#endif

irq_base = irq_alloc_descs(-1, 0, at91_gpio->chip.ngpio, 0);
if (irq_base < 0)
panic("at91_gpio.%d: error %d: couldn't allocate IRQ numbers.\n",
at91_gpio->pioc_idx, irq_base);
at91_gpio->domain = irq_domain_add_legacy(of_node,
at91_gpio->chip.ngpio,
at91_gpio->domain = irq_domain_add_legacy(NULL, at91_gpio->chip.ngpio,
irq_base, 0,
&irq_domain_simple_ops, NULL);
if (!at91_gpio->domain)
panic("at91_gpio.%d: couldn't allocate irq domain.\n",
at91_gpio->pioc_idx);
}

/*
* This lock class tells lockdep that GPIO irqs are in a different
* category than their parents, so it won't report false recursion.
*/
static struct lock_class_key gpio_lock_class;

/*
* Called from the processor-specific init to enable GPIO interrupt support.
*/
Expand All @@ -535,8 +596,7 @@ void __init at91_gpio_irq_setup(void)
for (pioc = 0, this = gpio_chip, prev = NULL;
pioc++ < gpio_banks;
prev = this, this++) {
unsigned pioc_hwirq = this->pioc_hwirq;
int offset;
int offset;

__raw_writel(~0, this->regbase + PIO_IDR);

Expand Down Expand Up @@ -566,8 +626,9 @@ void __init at91_gpio_irq_setup(void)
if (prev && prev->next == this)
continue;

irq_set_chip_data(pioc_hwirq, this);
irq_set_chained_handler(pioc_hwirq, gpio_irq_handler);
this->pioc_virq = irq_create_mapping(NULL, this->pioc_hwirq);
irq_set_chip_data(this->pioc_virq, this);
irq_set_chained_handler(this->pioc_virq, gpio_irq_handler);
}
pr_info("AT91: %d gpio irqs in %d banks\n", gpio_irqnbr, gpio_banks);
}
Expand Down Expand Up @@ -645,7 +706,12 @@ static void at91_gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)
static int at91_gpiolib_to_irq(struct gpio_chip *chip, unsigned offset)
{
struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
int virq = irq_find_mapping(at91_gpio->domain, offset);
int virq;

if (offset < chip->ngpio)
virq = irq_create_mapping(at91_gpio->domain, offset);
else
virq = -ENXIO;

dev_dbg(chip->dev, "%s: request IRQ for GPIO %d, return %d\n",
chip->label, offset + chip->base, virq);
Expand Down
90 changes: 58 additions & 32 deletions trunk/arch/arm/mach-at91/irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -135,27 +135,70 @@ static struct irq_chip at91_aic_chip = {
.irq_set_wake = at91_aic_set_wake,
};

static void __init at91_aic_hw_init(unsigned int spu_vector)
{
int i;

/*
* Perform 8 End Of Interrupt Command to make sure AIC
* will not Lock out nIRQ
*/
for (i = 0; i < 8; i++)
at91_aic_write(AT91_AIC_EOICR, 0);

/*
* Spurious Interrupt ID in Spurious Vector Register.
* When there is no current interrupt, the IRQ Vector Register
* reads the value stored in AIC_SPU
*/
at91_aic_write(AT91_AIC_SPU, spu_vector);

/* No debugging in AIC: Debug (Protect) Control Register */
at91_aic_write(AT91_AIC_DCR, 0);

/* Disable and clear all interrupts initially */
at91_aic_write(AT91_AIC_IDCR, 0xFFFFFFFF);
at91_aic_write(AT91_AIC_ICCR, 0xFFFFFFFF);
}

#if defined(CONFIG_OF)
static int __init __at91_aic_of_init(struct device_node *node,
struct device_node *parent)
static int at91_aic_irq_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
at91_aic_base = of_iomap(node, 0);
at91_aic_np = node;
/* Put virq number in Source Vector Register */
at91_aic_write(AT91_AIC_SVR(hw), virq);

/* Active Low interrupt, without priority */
at91_aic_write(AT91_AIC_SMR(hw), AT91_AIC_SRCTYPE_LOW);

irq_set_chip_and_handler(virq, &at91_aic_chip, handle_level_irq);
set_irq_flags(virq, IRQF_VALID | IRQF_PROBE);

return 0;
}

static const struct of_device_id aic_ids[] __initconst = {
{ .compatible = "atmel,at91rm9200-aic", .data = __at91_aic_of_init },
{ /*sentinel*/ }
static struct irq_domain_ops at91_aic_irq_ops = {
.map = at91_aic_irq_map,
.xlate = irq_domain_xlate_twocell,
};

static void __init at91_aic_of_init(void)
int __init at91_aic_of_init(struct device_node *node,
struct device_node *parent)
{
of_irq_init(aic_ids);
at91_aic_base = of_iomap(node, 0);
at91_aic_np = node;

at91_aic_domain = irq_domain_add_linear(at91_aic_np, NR_AIC_IRQS,
&at91_aic_irq_ops, NULL);
if (!at91_aic_domain)
panic("Unable to add AIC irq domain (DT)\n");

irq_set_default_host(at91_aic_domain);

at91_aic_hw_init(NR_AIC_IRQS);

return 0;
}
#else
static void __init at91_aic_of_init(void) {}
#endif

/*
Expand All @@ -166,11 +209,7 @@ void __init at91_aic_init(unsigned int priority[NR_AIC_IRQS])
unsigned int i;
int irq_base;

if (of_have_populated_dt())
at91_aic_of_init();
else
at91_aic_base = ioremap(AT91_AIC, 512);

at91_aic_base = ioremap(AT91_AIC, 512);
if (!at91_aic_base)
panic("Unable to ioremap AIC registers\n");

Expand All @@ -187,6 +226,8 @@ void __init at91_aic_init(unsigned int priority[NR_AIC_IRQS])
if (!at91_aic_domain)
panic("Unable to add AIC irq domain\n");

irq_set_default_host(at91_aic_domain);

/*
* 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.
Expand All @@ -199,22 +240,7 @@ void __init at91_aic_init(unsigned int priority[NR_AIC_IRQS])

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);
}

/*
* Spurious Interrupt ID in Spurious Vector Register is NR_AIC_IRQS
* When there is no current interrupt, the IRQ Vector Register reads the value stored in AIC_SPU
*/
at91_aic_write(AT91_AIC_SPU, NR_AIC_IRQS);

/* No debugging in AIC: Debug (Protect) Control Register */
at91_aic_write(AT91_AIC_DCR, 0);

/* Disable and clear all interrupts initially */
at91_aic_write(AT91_AIC_IDCR, 0xFFFFFFFF);
at91_aic_write(AT91_AIC_ICCR, 0xFFFFFFFF);
at91_aic_hw_init(NR_AIC_IRQS);
}

0 comments on commit 9deec39

Please sign in to comment.