summaryrefslogtreecommitdiff
path: root/arch/arm/mach-at91/gpio.c
diff options
context:
space:
mode:
authorNicolas Ferre <nicolas.ferre@atmel.com>2012-02-14 21:08:14 +0400
committerNicolas Ferre <nicolas.ferre@atmel.com>2012-03-01 16:29:03 +0400
commit8014d6f4dd074d4d248d3de7f63348fa2568476b (patch)
tree7803070e93c4955e7b37eb4f4e81e4d1411aeb98 /arch/arm/mach-at91/gpio.c
parent34bc485ca143c4773fc2afbbeb41e50f86445d0d (diff)
downloadlinux-8014d6f4dd074d4d248d3de7f63348fa2568476b.tar.xz
ARM: at91: AIC and GPIO IRQ device tree initialization
Both AIC and GPIO controllers are now using the standard of_irq_init() function to initialize IRQs in case of DT use. The DT specific initialization functions are now separated from the non-DT case and are now using "linear" irq domains. The .map() irqdomain operation is responsible for positioning the IRQ handlers. In AIC case, the Linux IRQ number is directly programmed in the hardware to avoid an additional reverse mapping operation. The AIC position its irq domain as the "default" irq domain. For DT case, the priority is not yet filled in the SMR. It will be the subject of another patch. Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Diffstat (limited to 'arch/arm/mach-at91/gpio.c')
-rw-r--r--arch/arm/mach-at91/gpio.c122
1 files changed, 94 insertions, 28 deletions
diff --git a/arch/arm/mach-at91/gpio.c b/arch/arm/mach-at91/gpio.c
index ecf6bbb9103a..567df654a2e1 100644
--- a/arch/arm/mach-at91/gpio.c
+++ b/arch/arm/mach-at91/gpio.c
@@ -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>
@@ -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 */
@@ -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;
}
@@ -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);
@@ -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);
@@ -493,23 +492,91 @@ 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)
@@ -518,12 +585,6 @@ static void __init at91_gpio_irqdomain(struct at91_gpio_chip *at91_gpio)
}
/*
- * 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.
*/
void __init at91_gpio_irq_setup(void)
@@ -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);
@@ -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);
}
@@ -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);