mirror of https://github.com/hak5/openwrt-owl.git
225 lines
5.5 KiB
Diff
225 lines
5.5 KiB
Diff
|
--- 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 */
|