--- a/arch/mips/ath79/gpio.c +++ b/arch/mips/ath79/gpio.c @@ -1,6 +1,7 @@ /* * Atheros AR71XX/AR724X/AR913X GPIO API support * + * Copyright (c) 2013 The Linux Foundation. All rights reserved. * Copyright (C) 2010-2011 Jaiganesh Narayanan * Copyright (C) 2008-2011 Gabor Juhos * Copyright (C) 2008 Imre Kaloz @@ -20,9 +21,13 @@ #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; @@ -209,6 +221,143 @@ 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_unmask(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); + /* flush write */ + (void) __raw_readl(base + AR71XX_GPIO_REG_INT_ENABLE); +} + +static void ath79_gpio_irq_mask(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); + /* flush write */ + (void) __raw_readl(base + AR71XX_GPIO_REG_INT_ENABLE); +} + +static struct irq_chip ath79_gpio_irqchip = { + .name = "GPIO", + .irq_mask = ath79_gpio_irq_mask, + .irq_mask_ack = ath79_gpio_irq_mask, + .irq_unmask = ath79_gpio_irq_unmask, + .irq_set_type = ath79_gpio_irq_type, +}; + +static void ath79_gpio_irq(unsigned int irq, struct irq_desc *desc) +{ + void __iomem *base = ath79_gpio_base; + void __iomem *base2 = ath79_reset_base; + unsigned int stat = __raw_readl(base + AR71XX_GPIO_REG_INT_PENDING); + int irq_base = ATH79_GPIO_IRQ_BASE; + + while (stat) { + int bit_num = __ffs(stat); + unsigned long bit = (1<ngpio; irq++) { + irq_set_chip_and_handler(irq, &ath79_gpio_irqchip, handle_level_irq); + } + irq_set_chained_handler(ATH79_MISC_IRQ(2), ath79_gpio_irq); + return 0; +} + + void __init ath79_gpio_init(void) { int err; @@ -241,10 +390,15 @@ void __init ath79_gpio_init(void) ath79_gpio_chip.direction_input = ar934x_gpio_direction_input; ath79_gpio_chip.direction_output = ar934x_gpio_direction_output; } +#ifdef CONFIG_OF_GPIO + ath79_gpio_chip.of_node = of_find_node_by_path("/ath79-gpio"); +#endif err = gpiochip_add(&ath79_gpio_chip); if (err) panic("cannot add AR71xx GPIO chip, error=%d", err); + + ath79_gpio_irq_init(&ath79_gpio_chip); } int gpio_get_value(unsigned gpio) @@ -267,14 +421,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 @@ -1,4 +1,5 @@ /* + * Copyright (c) 2013 The Linux Foundation. All rights reserved. * Copyright (C) 2008-2010 Gabor Juhos * Copyright (C) 2008 Imre Kaloz * @@ -10,7 +11,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 +31,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 */