--- 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 */
