--- a/arch/mips/ath79/gpio.c +++ b/arch/mips/ath79/gpio.c @@ -20,9 +20,14 @@ #include #include #include +#include +#include + +#include #include #include +#include #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; @@ -233,6 +245,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; @@ -269,6 +407,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) @@ -291,14 +433,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 #endif /* __ASM_MACH_ATH79_IRQ_H */