summaryrefslogtreecommitdiff
path: root/target
diff options
context:
space:
mode:
Diffstat (limited to 'target')
-rw-r--r--target/linux/ar71xx/patches-3.18/739-MIPS-ath79-add-gpio-irq-support.patch224
1 files changed, 224 insertions, 0 deletions
diff --git a/target/linux/ar71xx/patches-3.18/739-MIPS-ath79-add-gpio-irq-support.patch b/target/linux/ar71xx/patches-3.18/739-MIPS-ath79-add-gpio-irq-support.patch
new file mode 100644
index 0000000..90bc963
--- /dev/null
+++ b/target/linux/ar71xx/patches-3.18/739-MIPS-ath79-add-gpio-irq-support.patch
@@ -0,0 +1,224 @@
+--- a/arch/mips/ath79/gpio.c
++++ b/arch/mips/ath79/gpio.c
+@@ -20,9 +20,14 @@
+ #include <linux/io.h>
+ #include <linux/ioport.h>
+ #include <linux/gpio.h>
++#include <linux/irq.h>
++#include <linux/interrupt.h>
++
++#include <linux/of.h>
+
+ #include <asm/mach-ath79/ar71xx_regs.h>
+ #include <asm/mach-ath79/ath79.h>
++#include <asm/mach-ath79/irq.h>
+ #include "common.h"
+
+ void __iomem *ath79_gpio_base;
+@@ -31,6 +36,13 @@ EXPORT_SYMBOL_GPL(ath79_gpio_base);
+ static unsigned long ath79_gpio_count;
+ static DEFINE_SPINLOCK(ath79_gpio_lock);
+
++/*
++ * gpio_both_edge is a bitmask of which gpio pins need to have
++ * the detect priority flipped from the interrupt handler to
++ * emulate IRQ_TYPE_EDGE_BOTH.
++ */
++static unsigned long gpio_both_edge = 0;
++
+ static void __ath79_gpio_set_value(unsigned gpio, int value)
+ {
+ void __iomem *base = ath79_gpio_base;
+@@ -209,6 +221,132 @@ void __init ath79_gpio_output_select(uns
+ spin_unlock_irqrestore(&ath79_gpio_lock, flags);
+ }
+
++static int ath79_gpio_irq_type(struct irq_data *d, unsigned type)
++{
++ int offset = d->irq - ATH79_GPIO_IRQ_BASE;
++ void __iomem *base = ath79_gpio_base;
++ unsigned long flags;
++ unsigned long int_type;
++ unsigned long int_polarity;
++ unsigned long bit = (1 << offset);
++
++ spin_lock_irqsave(&ath79_gpio_lock, flags);
++
++ int_type = __raw_readl(base + AR71XX_GPIO_REG_INT_TYPE);
++ int_polarity = __raw_readl(base + AR71XX_GPIO_REG_INT_POLARITY);
++
++ gpio_both_edge &= ~bit;
++
++ switch (type) {
++ case IRQ_TYPE_EDGE_RISING:
++ int_type &= ~bit;
++ int_polarity |= bit;
++ break;
++
++ case IRQ_TYPE_EDGE_FALLING:
++ int_type &= ~bit;
++ int_polarity &= ~bit;
++ break;
++
++ case IRQ_TYPE_LEVEL_HIGH:
++ int_type |= bit;
++ int_polarity |= bit;
++ break;
++
++ case IRQ_TYPE_LEVEL_LOW:
++ int_type |= bit;
++ int_polarity &= ~bit;
++ break;
++
++ case IRQ_TYPE_EDGE_BOTH:
++ int_type |= bit;
++ /* set polarity based on current value */
++ if (gpio_get_value(offset)) {
++ int_polarity &= ~bit;
++ } else {
++ int_polarity |= bit;
++ }
++ /* flip this gpio in the interrupt handler */
++ gpio_both_edge |= bit;
++ break;
++
++ default:
++ spin_unlock_irqrestore(&ath79_gpio_lock, flags);
++ return -EINVAL;
++ }
++
++ __raw_writel(int_type, base + AR71XX_GPIO_REG_INT_TYPE);
++ __raw_writel(int_polarity, base + AR71XX_GPIO_REG_INT_POLARITY);
++
++ __raw_writel(__raw_readl(base + AR71XX_GPIO_REG_INT_MODE) | (1 << offset),
++ base + AR71XX_GPIO_REG_INT_MODE);
++
++ __raw_writel(__raw_readl(base + AR71XX_GPIO_REG_INT_ENABLE) & ~(1 << offset),
++ base + AR71XX_GPIO_REG_INT_ENABLE);
++
++ spin_unlock_irqrestore(&ath79_gpio_lock, flags);
++ return 0;
++}
++
++static void ath79_gpio_irq_enable(struct irq_data *d)
++{
++ int offset = d->irq - ATH79_GPIO_IRQ_BASE;
++ void __iomem *base = ath79_gpio_base;
++
++ __raw_writel(__raw_readl(base + AR71XX_GPIO_REG_INT_ENABLE) | (1 << offset),
++ base + AR71XX_GPIO_REG_INT_ENABLE);
++}
++
++static void ath79_gpio_irq_disable(struct irq_data *d)
++{
++ int offset = d->irq - ATH79_GPIO_IRQ_BASE;
++ void __iomem *base = ath79_gpio_base;
++
++ __raw_writel(__raw_readl(base + AR71XX_GPIO_REG_INT_ENABLE) & ~(1 << offset),
++ base + AR71XX_GPIO_REG_INT_ENABLE);
++}
++
++static struct irq_chip ath79_gpio_irqchip = {
++ .name = "GPIO",
++ .irq_enable = ath79_gpio_irq_enable,
++ .irq_disable = ath79_gpio_irq_disable,
++ .irq_set_type = ath79_gpio_irq_type,
++};
++
++static irqreturn_t ath79_gpio_irq(int irq, void *dev)
++{
++ void __iomem *base = ath79_gpio_base;
++ unsigned long stat = __raw_readl(base + AR71XX_GPIO_REG_INT_PENDING);
++ int bit_num;
++
++ for_each_set_bit(bit_num, &stat, sizeof(stat) * BITS_PER_BYTE) {
++ unsigned long bit = BIT(bit_num);
++
++ if (bit & gpio_both_edge) {
++ __raw_writel(__raw_readl(base + AR71XX_GPIO_REG_INT_POLARITY) ^ bit,
++ base + AR71XX_GPIO_REG_INT_POLARITY);
++ }
++
++ generic_handle_irq(ATH79_GPIO_IRQ(bit_num));
++ }
++
++ return IRQ_HANDLED;
++}
++
++static int __init ath79_gpio_irq_init(struct gpio_chip *chip)
++{
++ int irq;
++ int irq_base = ATH79_GPIO_IRQ_BASE;
++
++ for (irq = irq_base; irq < irq_base + chip->ngpio; irq++) {
++ irq_set_chip_data(irq, chip);
++ irq_set_chip_and_handler(irq, &ath79_gpio_irqchip, handle_simple_irq);
++ irq_set_noprobe(irq);
++ }
++
++ return 0;
++}
++
+ void __init ath79_gpio_init(void)
+ {
+ int err;
+@@ -245,6 +383,10 @@ void __init ath79_gpio_init(void)
+ err = gpiochip_add(&ath79_gpio_chip);
+ if (err)
+ panic("cannot add AR71xx GPIO chip, error=%d", err);
++
++ ath79_gpio_irq_init(&ath79_gpio_chip);
++
++ request_irq(ATH79_MISC_IRQ(2), ath79_gpio_irq, 0, "ath79-gpio", NULL);
+ }
+
+ int gpio_get_value(unsigned gpio)
+@@ -267,14 +409,22 @@ EXPORT_SYMBOL(gpio_set_value);
+
+ int gpio_to_irq(unsigned gpio)
+ {
+- /* FIXME */
+- return -EINVAL;
++ if (gpio > ath79_gpio_count) {
++ return -EINVAL;
++ }
++
++ return ATH79_GPIO_IRQ_BASE + gpio;
+ }
+ EXPORT_SYMBOL(gpio_to_irq);
+
+ int irq_to_gpio(unsigned irq)
+ {
+- /* FIXME */
+- return -EINVAL;
++ unsigned gpio = irq - ATH79_GPIO_IRQ_BASE;
++
++ if (gpio > ath79_gpio_count) {
++ return -EINVAL;
++ }
++
++ return gpio;
+ }
+ EXPORT_SYMBOL(irq_to_gpio);
+--- a/arch/mips/include/asm/mach-ath79/irq.h
++++ b/arch/mips/include/asm/mach-ath79/irq.h
+@@ -10,7 +10,7 @@
+ #define __ASM_MACH_ATH79_IRQ_H
+
+ #define MIPS_CPU_IRQ_BASE 0
+-#define NR_IRQS 51
++#define NR_IRQS 83
+
+ #define ATH79_CPU_IRQ(_x) (MIPS_CPU_IRQ_BASE + (_x))
+
+@@ -30,6 +30,10 @@
+ #define ATH79_IP3_IRQ_COUNT 3
+ #define ATH79_IP3_IRQ(_x) (ATH79_IP3_IRQ_BASE + (_x))
+
++#define ATH79_GPIO_IRQ_BASE (ATH79_IP3_IRQ_BASE + ATH79_IP3_IRQ_COUNT)
++#define ATH79_GPIO_IRQ_COUNT 32
++#define ATH79_GPIO_IRQ(_x) (ATH79_GPIO_IRQ_BASE + (_x))
++
+ #include_next <irq.h>
+
+ #endif /* __ASM_MACH_ATH79_IRQ_H */