--- /dev/null
+++ b/arch/arm/plat-fa/gpio.c
@@ -0,0 +1,283 @@
+/*
+ * Gpiochip and interrupt routines for Faraday FA526 based SoCs
+ *
+ * Copyright (C) 2008-2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
+ * Copyright (C) 2010-2012 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * Based on plat-mxc/gpio.c:
+ *  MXC GPIO supchip. (c) 2008 Daniel Mack <daniel@caiaq.de>
+ *  Copyright 2008 Juergen Beisert, kernel@pengutronix.de
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/spinlock.h>
+
+#include <plat/gpio.h>
+
+#define GPIO_DATA_OUT		0x0
+#define GPIO_DATA_IN		0x4
+#define GPIO_DIR		0x8
+#define GPIO_DATA_SET		0x10
+#define GPIO_DATA_CLR		0x14
+#define GPIO_PULL_EN		0x18
+#define GPIO_PULL_TYPE		0x1C
+#define GPIO_INT_EN		0x20
+#define GPIO_INT_STAT		0x24
+#define GPIO_INT_MASK		0x2C
+#define GPIO_INT_CLR		0x30
+#define GPIO_INT_TYPE		0x34
+#define GPIO_INT_BOTH_EDGE	0x38
+#define GPIO_INT_LEVEL		0x3C
+#define GPIO_DEBOUNCE_EN	0x40
+#define GPIO_DEBOUNCE_PRESCALE	0x44
+
+#define GPIO_REGS_SIZE		0x48
+
+static DEFINE_SPINLOCK(fa_gpio_lock);
+
+static inline struct fa_gpio_chip *to_fgc(struct gpio_chip *chip)
+{
+	return container_of(chip, struct fa_gpio_chip, gpio_chip);
+}
+
+static void _fa_gpio_irq_setenable(struct irq_data *d, int enable)
+{
+	struct fa_gpio_chip *fgc = irq_get_chip_data(d->irq);
+	void __iomem *base = fgc->mem_base;
+	unsigned int gpio = d->irq - fgc->irq_base;
+	unsigned int reg;
+
+	reg = __raw_readl(base + GPIO_INT_EN);
+	reg = (reg & (~(1 << gpio))) | (!!enable << gpio);
+	__raw_writel(reg, base + GPIO_INT_EN);
+}
+
+static void fa_gpio_irq_ack(struct irq_data *d)
+{
+	struct fa_gpio_chip *fgc = irq_get_chip_data(d->irq);
+	unsigned int gpio = d->irq - fgc->irq_base;
+
+	__raw_writel(1 << gpio, fgc->mem_base + GPIO_INT_CLR);
+}
+
+static void fa_gpio_irq_mask(struct irq_data *d)
+{
+	_fa_gpio_irq_setenable(d, 0);
+}
+
+static void fa_gpio_irq_unmask(struct irq_data *d)
+{
+	_fa_gpio_irq_setenable(d, 1);
+}
+
+static int fa_gpio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+	struct fa_gpio_chip *fgc = irq_get_chip_data(d->irq);
+	void __iomem *base = fgc->mem_base;
+	unsigned int gpio = d->irq - fgc->irq_base;
+	unsigned int gpio_mask = 1 << gpio;
+	unsigned int reg_both, reg_level, reg_type;
+
+	reg_type = __raw_readl(base + GPIO_INT_TYPE);
+	reg_level = __raw_readl(base + GPIO_INT_LEVEL);
+	reg_both = __raw_readl(base + GPIO_INT_BOTH_EDGE);
+
+	switch (type) {
+	case IRQ_TYPE_EDGE_BOTH:
+		reg_type &= ~gpio_mask;
+		reg_both |= gpio_mask;
+		break;
+	case IRQ_TYPE_EDGE_RISING:
+		reg_type &= ~gpio_mask;
+		reg_both &= ~gpio_mask;
+		reg_level &= ~gpio_mask;
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		reg_type &= ~gpio_mask;
+		reg_both &= ~gpio_mask;
+		reg_level |= gpio_mask;
+		break;
+	case IRQ_TYPE_LEVEL_HIGH:
+		reg_type |= gpio_mask;
+		reg_level &= ~gpio_mask;
+		break;
+	case IRQ_TYPE_LEVEL_LOW:
+		reg_type |= gpio_mask;
+		reg_level |= gpio_mask;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	__raw_writel(reg_type, base + GPIO_INT_TYPE);
+	__raw_writel(reg_level, base + GPIO_INT_LEVEL);
+	__raw_writel(reg_both, base + GPIO_INT_BOTH_EDGE);
+
+	fa_gpio_irq_ack(d);
+
+	return 0;
+}
+
+static void fa_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+	struct fa_gpio_data *data = irq_get_handler_data(irq);
+	unsigned int chip;
+
+	for (chip = 0; chip < data->nchips; chip++) {
+		struct fa_gpio_chip *fgc = &data->chips[chip];
+		unsigned int status;
+		unsigned int i;
+
+		status = __raw_readl(fgc->mem_base + GPIO_INT_STAT);
+		for (i = fgc->irq_base; status != 0; status >>= 1, i++) {
+			if ((status & 1) == 0)
+				continue;
+
+			BUG_ON(!(irq_desc[i].handle_irq));
+			irq_desc[i].handle_irq(i, &irq_desc[i]);
+		}
+	}
+}
+
+static struct irq_chip fa_gpio_irq_chip = {
+	.name		= "GPIO",
+	.irq_ack	= fa_gpio_irq_ack,
+	.irq_mask	= fa_gpio_irq_mask,
+	.irq_unmask	= fa_gpio_irq_unmask,
+	.irq_set_type	= fa_gpio_irq_set_type,
+};
+
+static void _fa_gpio_set_direction(struct fa_gpio_chip *fgc, unsigned offset,
+				   int is_output)
+{
+	unsigned int reg;
+
+	reg = __raw_readl(fgc->mem_base + GPIO_DIR);
+	if (is_output)
+		reg |= 1 << offset;
+	else
+		reg &= ~(1 << offset);
+	__raw_writel(reg, fgc->mem_base + GPIO_DIR);
+}
+
+static void _fa_gpio_set(struct fa_gpio_chip *fgc, unsigned offset, int value)
+{
+	if (value)
+		__raw_writel(1 << offset, fgc->mem_base + GPIO_DATA_SET);
+	else
+		__raw_writel(1 << offset, fgc->mem_base + GPIO_DATA_CLR);
+}
+
+static void fa_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+	struct fa_gpio_chip *fgc = to_fgc(chip);
+
+	_fa_gpio_set(fgc, offset, value);
+}
+
+static int fa_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+	struct fa_gpio_chip *fgc = to_fgc(chip);
+
+	return (__raw_readl(fgc->mem_base + GPIO_DATA_IN) >> offset) & 1;
+}
+
+static int fa_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	struct fa_gpio_chip *fgc = to_fgc(chip);
+
+	return fgc->irq_base + offset;
+}
+
+static int fa_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+	struct fa_gpio_chip *fgc = to_fgc(chip);
+	unsigned long flags;
+
+	spin_lock_irqsave(&fa_gpio_lock, flags);
+
+	_fa_gpio_set_direction(fgc, offset, 0);
+
+	spin_unlock_irqrestore(&fa_gpio_lock, flags);
+
+	return 0;
+}
+
+static int fa_gpio_direction_output(struct gpio_chip *chip,
+				    unsigned offset,
+				    int value)
+{
+	struct fa_gpio_chip *fgc = to_fgc(chip);
+	unsigned long flags;
+
+	spin_lock_irqsave(&fa_gpio_lock, flags);
+
+	_fa_gpio_set(fgc, offset, value);
+	_fa_gpio_set_direction(fgc, offset, 1);
+
+	spin_unlock_irqrestore(&fa_gpio_lock, flags);
+
+	return 0;
+}
+
+static int fa_gpio_init_chip(struct fa_gpio_chip *fgc)
+{
+	void __iomem *mem_base;
+	unsigned int i;
+	int err;
+
+	mem_base = ioremap(fgc->map_base, GPIO_REGS_SIZE);
+	if (!mem_base)
+		return -ENXIO;
+
+	fgc->mem_base = mem_base;
+
+	fgc->gpio_chip.direction_input = fa_gpio_direction_input;
+	fgc->gpio_chip.direction_output = fa_gpio_direction_output;
+	fgc->gpio_chip.get = fa_gpio_get;
+	fgc->gpio_chip.set = fa_gpio_set;
+	fgc->gpio_chip.to_irq = fa_gpio_to_irq;
+
+	/* disable, unmask and clear all interrupts */
+	__raw_writel(0x0, mem_base + GPIO_INT_EN);
+	__raw_writel(0x0, mem_base + GPIO_INT_MASK);
+	__raw_writel(~0x0, mem_base + GPIO_INT_CLR);
+
+	for (i = fgc->irq_base;
+	     i < fgc->irq_base + fgc->gpio_chip.ngpio; i++) {
+		irq_set_chip(i, &fa_gpio_irq_chip);
+		irq_set_chip_data(i, fgc);
+		irq_set_handler(i, handle_edge_irq);
+		set_irq_flags(i, IRQF_VALID);
+	}
+
+	err = gpiochip_add(&fgc->gpio_chip);
+	if (err)
+		goto unmap;
+
+	return 0;
+
+ unmap:
+	iounmap(fgc->mem_base);
+	return err;
+}
+
+void __init fa_gpio_init(struct fa_gpio_data *data)
+{
+	unsigned int i;
+
+	for (i = 0; i < data->nchips; i++) {
+		int err;
+
+		err = fa_gpio_init_chip(&data->chips[i]);
+		if (WARN(err, "GPIO init failed\n"))
+			return;
+	}
+
+	irq_set_chained_handler(data->irq, fa_gpio_irq_handler);
+	irq_set_handler_data(data->irq, data);
+}
--- /dev/null
+++ b/arch/arm/plat-fa/include/plat/gpio.h
@@ -0,0 +1,33 @@
+/*
+ *  Copyright (c) 2010-2012 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This file is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License, Version 2, as
+ *  published by the Free Software Foundation.
+ */
+
+#ifndef _FA_GPIO_H
+#define _FA_GPIO_H
+
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+struct fa_gpio_chip {
+	struct gpio_chip	gpio_chip;
+	unsigned int		map_base;
+	unsigned int		irq_base;
+
+	void __iomem		*mem_base;
+};
+
+struct fa_gpio_data {
+	struct fa_gpio_chip	*chips;
+	unsigned int		nchips;
+	unsigned int		irq;
+};
+
+void __init fa_gpio_init(struct fa_gpio_data *data);
+
+#endif /* _FA_GPIO_H */
--- a/arch/arm/plat-fa/Kconfig
+++ b/arch/arm/plat-fa/Kconfig
@@ -1,5 +1,8 @@
 if PLAT_FA
 
+config PLAT_FA_GPIO
+	def_bool n
+
 config PLAT_FA_TIME
 	def_bool n
 
--- a/arch/arm/plat-fa/Makefile
+++ b/arch/arm/plat-fa/Makefile
@@ -4,6 +4,7 @@
 
 obj-y :=
 
+obj-$(CONFIG_PLAT_FA_GPIO)	+= gpio.o
 obj-$(CONFIG_PLAT_FA_TIME)	+= time.o
 
 obj-m :=
