Merge commit 'AU_LINUX_ANDROID_ICS.04.00.04.00.126' into msm-3.4

AU_LINUX_ANDROID_ICS.04.00.04.00.126 from msm-3.0.
First parent is from google/android-3.4.

* commit 'AU_LINUX_ANDROID_ICS.04.00.04.00.126': (8712 commits)
  PRNG: Device tree entry for qrng device.
  vidc:1080p: Set video core timeout value for Thumbnail mode
  msm: sps: improve the debugging support in SPS driver
  board-8064 msm: Overlap secure and non secure video firmware heaps.
  msm: clock: Add handoff ops for 7x30 and copper XO clocks
  msm_fb: display: Wait for external vsync before DTV IOMMU unmap
  msm: Fix ciruclar dependency in debug UART settings
  msm: gdsc: Add GDSC regulator driver for msm-copper
  defconfig: Enable Mobicore Driver.
  mobicore: Add mobicore driver.
  mobicore: rename variable to lower case.
  mobicore: rename folder.
  mobicore: add makefiles
  mobicore: initial import of kernel driver
  ASoC: msm: Add SLIMBUS_2_RX CPU DAI
  board-8064-gpio: Update FUNC for EPM SPI CS
  msm_fb: display: Remove chicken bit config during video playback
  mmc: msm_sdcc: enable the sanitize capability
  msm-fb: display: lm2 writeback support on mpq platfroms
  msm_fb: display: Disable LVDS phy & pll during panel off
  ...

Signed-off-by: Steve Muckle <smuckle@codeaurora.org>
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index e03653d..ef077a5 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -124,6 +124,14 @@
 	  Qualcomm MSM chips.  Most of the pins on the MSM can be
 	  selected for GPIO, and are controlled by this driver.
 
+config GPIO_FSM9XXX
+	tristate "Qualcomm FSM GPIO"
+	depends on GPIOLIB && ARCH_MSM
+	help
+	  Say yes here to support the GPIO interface on Qualcomm FSM chips.
+	  Most of the pins on the MSM can be selected for GPIO, and are
+	  controlled by this driver.
+
 config GPIO_MXC
 	def_bool y
 	depends on ARCH_MXC
@@ -514,4 +522,44 @@
 	help
 	  Select this option to enable GPIO driver for the TPS65910
 	  chip family.
+
+config GPIO_PM8XXX
+	tristate "Qualcomm PM8xxx GPIO support"
+	depends on MFD_PM8XXX
+	default y if MFD_PM8XXX
+	help
+	  This option enables support for on-chip GPIO found on Qualcomm PM8xxx
+	  PMICs.
+
+config GPIO_PM8XXX_MPP
+	tristate "Support for Qualcomm PM8xxx MPP features"
+	depends on MFD_PM8XXX
+	default y if MFD_PM8XXX
+	help
+	  This is the multi-purpose pin (MPP) driver for Qualcomm PM 8xxx PMIC
+	  chips.
+
+config GPIO_PM8XXX_RPC
+	tristate "Qualcomm PM8xxx RPC based GPIO support"
+	depends on MSM_SMD
+	help
+	  This option enables support for on-chip GPIO found on Qualcomm PM8xxx
+	  PMICs through RPC.
+
+config GPIO_QPNP
+	depends on ARCH_MSMCOPPER
+	depends on OF_SPMI
+	depends on MSM_QPNP_INT
+	tristate "Qualcomm QPNP GPIO support"
+	help
+	  Say 'y' here to include support for the Qualcomm QPNP gpio
+	  support. QPNP is a SPMI based PMIC implementation.
+
+config GPIO_QPNP_DEBUG
+	depends on GPIO_QPNP
+	depends on DEBUG_FS
+	bool "Qualcomm QPNP GPIO debug support"
+	help
+	  Say 'y' here to include debug support for the Qualcomm
+	  QPNP gpio support
 endif
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 007f54b..babd44d 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -34,6 +34,7 @@
 obj-$(CONFIG_GPIO_MPC8XXX)	+= gpio-mpc8xxx.o
 obj-$(CONFIG_GPIO_MSM_V1)	+= gpio-msm-v1.o
 obj-$(CONFIG_GPIO_MSM_V2)	+= gpio-msm-v2.o
+obj-$(CONFIG_GPIO_FSM9XXX)	+= gpio-fsm9xxx.o
 obj-$(CONFIG_GPIO_MXC)		+= gpio-mxc.o
 obj-$(CONFIG_GPIO_MXS)		+= gpio-mxs.o
 obj-$(CONFIG_PLAT_NOMADIK)	+= gpio-nomadik.o
@@ -42,7 +43,11 @@
 obj-$(CONFIG_GPIO_PCF857X)	+= gpio-pcf857x.o
 obj-$(CONFIG_GPIO_PCH)		+= gpio-pch.o
 obj-$(CONFIG_GPIO_PL061)	+= gpio-pl061.o
+obj-$(CONFIG_GPIO_PM8XXX)	+= pm8xxx-gpio.o
+obj-$(CONFIG_GPIO_PM8XXX_MPP) 	+= pm8xxx-mpp.o
+obj-$(CONFIG_GPIO_PM8XXX_RPC)	+= gpio-pm8xxx-rpc.o
 obj-$(CONFIG_GPIO_PXA)		+= gpio-pxa.o
+obj-$(CONFIG_GPIO_QPNP)		+= qpnp-gpio.o
 obj-$(CONFIG_GPIO_RDC321X)	+= gpio-rdc321x.o
 obj-$(CONFIG_PLAT_SAMSUNG)	+= gpio-samsung.o
 obj-$(CONFIG_ARCH_SA1100)	+= gpio-sa1100.o
diff --git a/drivers/gpio/gpio-fsm9xxx.c b/drivers/gpio/gpio-fsm9xxx.c
new file mode 100644
index 0000000..ad7e1fd
--- /dev/null
+++ b/drivers/gpio/gpio-fsm9xxx.c
@@ -0,0 +1,335 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/bitops.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <mach/gpiomux.h>
+#include <mach/msm_iomap.h>
+
+/* see 80-VA736-2 Rev C pp 695-751
+**
+** These are actually the *shadow* gpio registers, since the
+** real ones (which allow full access) are only available to the
+** ARM9 side of the world.
+**
+** Since the _BASE need to be page-aligned when we're mapping them
+** to virtual addresses, adjust for the additional offset in these
+** macros.
+*/
+
+#if defined(CONFIG_ARCH_FSM9XXX)
+#define MSM_GPIO1_REG(off) (MSM_TLMM_BASE + (off))
+#endif
+
+#if defined(CONFIG_ARCH_FSM9XXX)
+
+/* output value */
+#define MSM_GPIO_OUT_G(group)  MSM_GPIO1_REG(0x00 + (group) * 4)
+#define MSM_GPIO_OUT_N(gpio)   MSM_GPIO_OUT_G((gpio) / 32)
+#define MSM_GPIO_OUT_0         MSM_GPIO_OUT_G(0)   /* gpio  31-0   */
+#define MSM_GPIO_OUT_1         MSM_GPIO_OUT_G(1)   /* gpio  63-32  */
+#define MSM_GPIO_OUT_2         MSM_GPIO_OUT_G(2)   /* gpio  95-64  */
+#define MSM_GPIO_OUT_3         MSM_GPIO_OUT_G(3)   /* gpio 127-96  */
+#define MSM_GPIO_OUT_4         MSM_GPIO_OUT_G(4)   /* gpio 159-128 */
+#define MSM_GPIO_OUT_5         MSM_GPIO_OUT_G(5)   /* gpio 167-160 */
+
+/* same pin map as above, output enable */
+#define MSM_GPIO_OE_G(group)   MSM_GPIO1_REG(0x20 + (group) * 4)
+#define MSM_GPIO_OE_N(gpio)    MSM_GPIO_OE_G((gpio) / 32)
+#define MSM_GPIO_OE_0          MSM_GPIO_OE_G(0)
+#define MSM_GPIO_OE_1          MSM_GPIO_OE_G(1)
+#define MSM_GPIO_OE_2          MSM_GPIO_OE_G(2)
+#define MSM_GPIO_OE_3          MSM_GPIO_OE_G(3)
+#define MSM_GPIO_OE_4          MSM_GPIO_OE_G(4)
+#define MSM_GPIO_OE_5          MSM_GPIO_OE_G(5)
+
+/* same pin map as above, input read */
+#define MSM_GPIO_IN_G(group)   MSM_GPIO1_REG(0x48 + (group) * 4)
+#define MSM_GPIO_IN_N(gpio)    MSM_GPIO_IN_G((gpio) / 32)
+#define MSM_GPIO_IN_0          MSM_GPIO_IN_G(0)
+#define MSM_GPIO_IN_1          MSM_GPIO_IN_G(1)
+#define MSM_GPIO_IN_2          MSM_GPIO_IN_G(2)
+#define MSM_GPIO_IN_3          MSM_GPIO_IN_G(3)
+#define MSM_GPIO_IN_4          MSM_GPIO_IN_G(4)
+#define MSM_GPIO_IN_5          MSM_GPIO_IN_G(5)
+
+/* configuration */
+#define MSM_GPIO_PAGE          MSM_GPIO1_REG(0x40)
+#define MSM_GPIO_CONFIG        MSM_GPIO1_REG(0x44)
+
+#endif /* CONFIG_ARCH_FSM9XXX */
+
+#define MSM_GPIO_BANK(bank, first, last)				\
+	{								\
+		.regs = {						\
+			.out =         MSM_GPIO_OUT_##bank,		\
+			.in =          MSM_GPIO_IN_##bank,		\
+			.oe =          MSM_GPIO_OE_##bank,		\
+		},							\
+		.chip = {						\
+			.base = (first),				\
+			.ngpio = (last) - (first) + 1,			\
+			.get = msm_gpio_get,				\
+			.set = msm_gpio_set,				\
+			.direction_input = msm_gpio_direction_input,	\
+			.direction_output = msm_gpio_direction_output,	\
+			.request = msm_gpio_request,			\
+			.free = msm_gpio_free,				\
+		}							\
+	}
+
+struct msm_gpio_regs {
+	void __iomem *out;
+	void __iomem *in;
+	void __iomem *oe;
+};
+
+struct msm_gpio_chip {
+	spinlock_t		lock;
+	struct gpio_chip	chip;
+	struct msm_gpio_regs	regs;
+};
+
+static int msm_gpio_write(struct msm_gpio_chip *msm_chip,
+			  unsigned offset, unsigned on)
+{
+	unsigned mask = BIT(offset);
+	unsigned val;
+
+	val = __raw_readl(msm_chip->regs.out);
+	if (on)
+		__raw_writel(val | mask, msm_chip->regs.out);
+	else
+		__raw_writel(val & ~mask, msm_chip->regs.out);
+	return 0;
+}
+
+static int msm_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+	struct msm_gpio_chip *msm_chip;
+	unsigned long irq_flags;
+
+	msm_chip = container_of(chip, struct msm_gpio_chip, chip);
+	spin_lock_irqsave(&msm_chip->lock, irq_flags);
+	__raw_writel(__raw_readl(msm_chip->regs.oe) & ~BIT(offset),
+		msm_chip->regs.oe);
+	spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
+	return 0;
+}
+
+static int
+msm_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value)
+{
+	struct msm_gpio_chip *msm_chip;
+	unsigned long irq_flags;
+
+	msm_chip = container_of(chip, struct msm_gpio_chip, chip);
+	spin_lock_irqsave(&msm_chip->lock, irq_flags);
+	msm_gpio_write(msm_chip, offset, value);
+	__raw_writel(__raw_readl(msm_chip->regs.oe) | BIT(offset),
+		msm_chip->regs.oe);
+	spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
+	return 0;
+}
+
+static int msm_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+	struct msm_gpio_chip *msm_chip;
+
+	msm_chip = container_of(chip, struct msm_gpio_chip, chip);
+	return (__raw_readl(msm_chip->regs.in) & (1U << offset)) ? 1 : 0;
+}
+
+static void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+	struct msm_gpio_chip *msm_chip;
+	unsigned long irq_flags;
+
+	msm_chip = container_of(chip, struct msm_gpio_chip, chip);
+	spin_lock_irqsave(&msm_chip->lock, irq_flags);
+	msm_gpio_write(msm_chip, offset, value);
+	spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
+}
+
+#ifdef CONFIG_MSM_GPIOMUX
+static int msm_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+	return msm_gpiomux_get(chip->base + offset);
+}
+
+static void msm_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+	msm_gpiomux_put(chip->base + offset);
+}
+#else
+#define msm_gpio_request NULL
+#define msm_gpio_free NULL
+#endif
+
+struct msm_gpio_chip msm_gpio_chips[] = {
+	MSM_GPIO_BANK(0,   0,  31),
+	MSM_GPIO_BANK(1,  32,  63),
+	MSM_GPIO_BANK(2,  64,  95),
+	MSM_GPIO_BANK(3,  96, 127),
+	MSM_GPIO_BANK(4, 128, 159),
+	MSM_GPIO_BANK(5, 160, 167),
+};
+
+void msm_gpio_enter_sleep(int from_idle)
+{
+	return;
+}
+
+void msm_gpio_exit_sleep(void)
+{
+	return;
+}
+
+static int __init msm_init_gpio(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) {
+		spin_lock_init(&msm_gpio_chips[i].lock);
+		gpiochip_add(&msm_gpio_chips[i].chip);
+	}
+
+	return 0;
+}
+
+postcore_initcall(msm_init_gpio);
+
+int gpio_tlmm_config(unsigned config, unsigned disable)
+{
+	uint32_t flags;
+	unsigned gpio = GPIO_PIN(config);
+
+	if (gpio > NR_MSM_GPIOS)
+		return -EINVAL;
+	flags = ((GPIO_DRVSTR(config) << 6) & (0x7 << 6)) |
+		((GPIO_FUNC(config) << 2) & (0xf << 2)) |
+		((GPIO_PULL(config) & 0x3));
+	dsb();
+	__raw_writel(gpio, MSM_GPIO_PAGE);
+	dsb();
+	__raw_writel(flags, MSM_GPIO_CONFIG);
+
+	return 0;
+}
+EXPORT_SYMBOL(gpio_tlmm_config);
+
+int msm_gpios_request_enable(const struct msm_gpio *table, int size)
+{
+	int rc = msm_gpios_request(table, size);
+	if (rc)
+		return rc;
+	rc = msm_gpios_enable(table, size);
+	if (rc)
+		msm_gpios_free(table, size);
+	return rc;
+}
+EXPORT_SYMBOL(msm_gpios_request_enable);
+
+void msm_gpios_disable_free(const struct msm_gpio *table, int size)
+{
+	msm_gpios_disable(table, size);
+	msm_gpios_free(table, size);
+}
+EXPORT_SYMBOL(msm_gpios_disable_free);
+
+int msm_gpios_request(const struct msm_gpio *table, int size)
+{
+	int rc;
+	int i;
+	const struct msm_gpio *g;
+	for (i = 0; i < size; i++) {
+		g = table + i;
+		rc = gpio_request(GPIO_PIN(g->gpio_cfg), g->label);
+		if (rc) {
+			pr_err("gpio_request(%d) <%s> failed: %d\n",
+			       GPIO_PIN(g->gpio_cfg), g->label ?: "?", rc);
+			goto err;
+		}
+	}
+	return 0;
+err:
+	msm_gpios_free(table, i);
+	return rc;
+}
+EXPORT_SYMBOL(msm_gpios_request);
+
+void msm_gpios_free(const struct msm_gpio *table, int size)
+{
+	int i;
+	const struct msm_gpio *g;
+	for (i = size-1; i >= 0; i--) {
+		g = table + i;
+		gpio_free(GPIO_PIN(g->gpio_cfg));
+	}
+}
+EXPORT_SYMBOL(msm_gpios_free);
+
+int msm_gpios_enable(const struct msm_gpio *table, int size)
+{
+	int rc;
+	int i;
+	const struct msm_gpio *g;
+	for (i = 0; i < size; i++) {
+		g = table + i;
+		rc = gpio_tlmm_config(g->gpio_cfg, GPIO_CFG_ENABLE);
+		if (rc) {
+			pr_err("gpio_tlmm_config(0x%08x, GPIO_CFG_ENABLE)"
+			       " <%s> failed: %d\n",
+			       g->gpio_cfg, g->label ?: "?", rc);
+			pr_err("pin %d func %d dir %d pull %d drvstr %d\n",
+			       GPIO_PIN(g->gpio_cfg), GPIO_FUNC(g->gpio_cfg),
+			       GPIO_DIR(g->gpio_cfg), GPIO_PULL(g->gpio_cfg),
+			       GPIO_DRVSTR(g->gpio_cfg));
+			goto err;
+		}
+	}
+	return 0;
+err:
+	msm_gpios_disable(table, i);
+	return rc;
+}
+EXPORT_SYMBOL(msm_gpios_enable);
+
+int msm_gpios_disable(const struct msm_gpio *table, int size)
+{
+	int rc = 0;
+	int i;
+	const struct msm_gpio *g;
+	for (i = size-1; i >= 0; i--) {
+		int tmp;
+		g = table + i;
+		tmp = gpio_tlmm_config(g->gpio_cfg, GPIO_CFG_DISABLE);
+		if (tmp) {
+			pr_err("gpio_tlmm_config(0x%08x, GPIO_CFG_DISABLE)"
+			       " <%s> failed: %d\n",
+			       g->gpio_cfg, g->label ?: "?", rc);
+			pr_err("pin %d func %d dir %d pull %d drvstr %d\n",
+			       GPIO_PIN(g->gpio_cfg), GPIO_FUNC(g->gpio_cfg),
+			       GPIO_DIR(g->gpio_cfg), GPIO_PULL(g->gpio_cfg),
+			       GPIO_DRVSTR(g->gpio_cfg));
+			if (!rc)
+				rc = tmp;
+		}
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(msm_gpios_disable);
+
diff --git a/drivers/gpio/gpio-msm-v1.c b/drivers/gpio/gpio-msm-v1.c
index 52a4d42..8718c9a 100644
--- a/drivers/gpio/gpio-msm-v1.c
+++ b/drivers/gpio/gpio-msm-v1.c
@@ -1,6 +1,7 @@
-/*
+/* linux/arch/arm/mach-msm/gpio.c
+ *
  * Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -19,9 +20,13 @@
 #include <linux/io.h>
 #include <linux/irq.h>
 #include <linux/module.h>
-#include <mach/cpu.h>
-#include <mach/msm_gpiomux.h>
+#include <linux/platform_device.h>
+#include <asm/mach/irq.h>
+#include <mach/gpiomux.h>
 #include <mach/msm_iomap.h>
+#include <mach/msm_smsm.h>
+#include <mach/proc_comm.h>
+
 
 /* see 80-VA736-2 Rev C pp 695-751
 **
@@ -34,257 +39,187 @@
 ** macros.
 */
 
+#if defined(CONFIG_ARCH_MSM7X30)
 #define MSM_GPIO1_REG(off) (MSM_GPIO1_BASE + (off))
 #define MSM_GPIO2_REG(off) (MSM_GPIO2_BASE + 0x400 + (off))
-#define MSM_GPIO1_SHADOW_REG(off) (MSM_GPIO1_BASE + 0x800 + (off))
-#define MSM_GPIO2_SHADOW_REG(off) (MSM_GPIO2_BASE + 0xC00 + (off))
+#else
+#define MSM_GPIO1_REG(off) (MSM_GPIO1_BASE + 0x800 + (off))
+#define MSM_GPIO2_REG(off) (MSM_GPIO2_BASE + 0xC00 + (off))
+#endif
 
-/*
- * MSM7X00 registers
- */
+#if defined(CONFIG_ARCH_MSM7X00A) || defined(CONFIG_ARCH_MSM7X25) ||\
+    defined(CONFIG_ARCH_MSM7X27)
+
 /* output value */
-#define MSM7X00_GPIO_OUT_0	MSM_GPIO1_SHADOW_REG(0x00)  /* gpio  15-0  */
-#define MSM7X00_GPIO_OUT_1	MSM_GPIO2_SHADOW_REG(0x00)  /* gpio  42-16 */
-#define MSM7X00_GPIO_OUT_2	MSM_GPIO1_SHADOW_REG(0x04)  /* gpio  67-43 */
-#define MSM7X00_GPIO_OUT_3	MSM_GPIO1_SHADOW_REG(0x08)  /* gpio  94-68 */
-#define MSM7X00_GPIO_OUT_4	MSM_GPIO1_SHADOW_REG(0x0C)  /* gpio 106-95 */
-#define MSM7X00_GPIO_OUT_5	MSM_GPIO1_SHADOW_REG(0x50)  /* gpio 107-121 */
+#define MSM_GPIO_OUT_0         MSM_GPIO1_REG(0x00)  /* gpio  15-0  */
+#define MSM_GPIO_OUT_1         MSM_GPIO2_REG(0x00)  /* gpio  42-16 */
+#define MSM_GPIO_OUT_2         MSM_GPIO1_REG(0x04)  /* gpio  67-43 */
+#define MSM_GPIO_OUT_3         MSM_GPIO1_REG(0x08)  /* gpio  94-68 */
+#define MSM_GPIO_OUT_4         MSM_GPIO1_REG(0x0C)  /* gpio 106-95 */
+#define MSM_GPIO_OUT_5         MSM_GPIO1_REG(0x50)  /* gpio 107-121 */
 
 /* same pin map as above, output enable */
-#define MSM7X00_GPIO_OE_0	MSM_GPIO1_SHADOW_REG(0x10)
-#define MSM7X00_GPIO_OE_1	MSM_GPIO2_SHADOW_REG(0x08)
-#define MSM7X00_GPIO_OE_2	MSM_GPIO1_SHADOW_REG(0x14)
-#define MSM7X00_GPIO_OE_3	MSM_GPIO1_SHADOW_REG(0x18)
-#define MSM7X00_GPIO_OE_4	MSM_GPIO1_SHADOW_REG(0x1C)
-#define MSM7X00_GPIO_OE_5	MSM_GPIO1_SHADOW_REG(0x54)
+#define MSM_GPIO_OE_0          MSM_GPIO1_REG(0x10)
+#define MSM_GPIO_OE_1          MSM_GPIO2_REG(0x08)
+#define MSM_GPIO_OE_2          MSM_GPIO1_REG(0x14)
+#define MSM_GPIO_OE_3          MSM_GPIO1_REG(0x18)
+#define MSM_GPIO_OE_4          MSM_GPIO1_REG(0x1C)
+#define MSM_GPIO_OE_5          MSM_GPIO1_REG(0x54)
 
 /* same pin map as above, input read */
-#define MSM7X00_GPIO_IN_0	MSM_GPIO1_SHADOW_REG(0x34)
-#define MSM7X00_GPIO_IN_1	MSM_GPIO2_SHADOW_REG(0x20)
-#define MSM7X00_GPIO_IN_2	MSM_GPIO1_SHADOW_REG(0x38)
-#define MSM7X00_GPIO_IN_3	MSM_GPIO1_SHADOW_REG(0x3C)
-#define MSM7X00_GPIO_IN_4	MSM_GPIO1_SHADOW_REG(0x40)
-#define MSM7X00_GPIO_IN_5	MSM_GPIO1_SHADOW_REG(0x44)
+#define MSM_GPIO_IN_0          MSM_GPIO1_REG(0x34)
+#define MSM_GPIO_IN_1          MSM_GPIO2_REG(0x20)
+#define MSM_GPIO_IN_2          MSM_GPIO1_REG(0x38)
+#define MSM_GPIO_IN_3          MSM_GPIO1_REG(0x3C)
+#define MSM_GPIO_IN_4          MSM_GPIO1_REG(0x40)
+#define MSM_GPIO_IN_5          MSM_GPIO1_REG(0x44)
 
 /* same pin map as above, 1=edge 0=level interrup */
-#define MSM7X00_GPIO_INT_EDGE_0	MSM_GPIO1_SHADOW_REG(0x60)
-#define MSM7X00_GPIO_INT_EDGE_1	MSM_GPIO2_SHADOW_REG(0x50)
-#define MSM7X00_GPIO_INT_EDGE_2	MSM_GPIO1_SHADOW_REG(0x64)
-#define MSM7X00_GPIO_INT_EDGE_3	MSM_GPIO1_SHADOW_REG(0x68)
-#define MSM7X00_GPIO_INT_EDGE_4	MSM_GPIO1_SHADOW_REG(0x6C)
-#define MSM7X00_GPIO_INT_EDGE_5	MSM_GPIO1_SHADOW_REG(0xC0)
+#define MSM_GPIO_INT_EDGE_0    MSM_GPIO1_REG(0x60)
+#define MSM_GPIO_INT_EDGE_1    MSM_GPIO2_REG(0x50)
+#define MSM_GPIO_INT_EDGE_2    MSM_GPIO1_REG(0x64)
+#define MSM_GPIO_INT_EDGE_3    MSM_GPIO1_REG(0x68)
+#define MSM_GPIO_INT_EDGE_4    MSM_GPIO1_REG(0x6C)
+#define MSM_GPIO_INT_EDGE_5    MSM_GPIO1_REG(0xC0)
 
 /* same pin map as above, 1=positive 0=negative */
-#define MSM7X00_GPIO_INT_POS_0	MSM_GPIO1_SHADOW_REG(0x70)
-#define MSM7X00_GPIO_INT_POS_1	MSM_GPIO2_SHADOW_REG(0x58)
-#define MSM7X00_GPIO_INT_POS_2	MSM_GPIO1_SHADOW_REG(0x74)
-#define MSM7X00_GPIO_INT_POS_3	MSM_GPIO1_SHADOW_REG(0x78)
-#define MSM7X00_GPIO_INT_POS_4	MSM_GPIO1_SHADOW_REG(0x7C)
-#define MSM7X00_GPIO_INT_POS_5	MSM_GPIO1_SHADOW_REG(0xBC)
+#define MSM_GPIO_INT_POS_0     MSM_GPIO1_REG(0x70)
+#define MSM_GPIO_INT_POS_1     MSM_GPIO2_REG(0x58)
+#define MSM_GPIO_INT_POS_2     MSM_GPIO1_REG(0x74)
+#define MSM_GPIO_INT_POS_3     MSM_GPIO1_REG(0x78)
+#define MSM_GPIO_INT_POS_4     MSM_GPIO1_REG(0x7C)
+#define MSM_GPIO_INT_POS_5     MSM_GPIO1_REG(0xBC)
 
 /* same pin map as above, interrupt enable */
-#define MSM7X00_GPIO_INT_EN_0	MSM_GPIO1_SHADOW_REG(0x80)
-#define MSM7X00_GPIO_INT_EN_1	MSM_GPIO2_SHADOW_REG(0x60)
-#define MSM7X00_GPIO_INT_EN_2	MSM_GPIO1_SHADOW_REG(0x84)
-#define MSM7X00_GPIO_INT_EN_3	MSM_GPIO1_SHADOW_REG(0x88)
-#define MSM7X00_GPIO_INT_EN_4	MSM_GPIO1_SHADOW_REG(0x8C)
-#define MSM7X00_GPIO_INT_EN_5	MSM_GPIO1_SHADOW_REG(0xB8)
+#define MSM_GPIO_INT_EN_0      MSM_GPIO1_REG(0x80)
+#define MSM_GPIO_INT_EN_1      MSM_GPIO2_REG(0x60)
+#define MSM_GPIO_INT_EN_2      MSM_GPIO1_REG(0x84)
+#define MSM_GPIO_INT_EN_3      MSM_GPIO1_REG(0x88)
+#define MSM_GPIO_INT_EN_4      MSM_GPIO1_REG(0x8C)
+#define MSM_GPIO_INT_EN_5      MSM_GPIO1_REG(0xB8)
 
 /* same pin map as above, write 1 to clear interrupt */
-#define MSM7X00_GPIO_INT_CLEAR_0	MSM_GPIO1_SHADOW_REG(0x90)
-#define MSM7X00_GPIO_INT_CLEAR_1	MSM_GPIO2_SHADOW_REG(0x68)
-#define MSM7X00_GPIO_INT_CLEAR_2	MSM_GPIO1_SHADOW_REG(0x94)
-#define MSM7X00_GPIO_INT_CLEAR_3	MSM_GPIO1_SHADOW_REG(0x98)
-#define MSM7X00_GPIO_INT_CLEAR_4	MSM_GPIO1_SHADOW_REG(0x9C)
-#define MSM7X00_GPIO_INT_CLEAR_5	MSM_GPIO1_SHADOW_REG(0xB4)
+#define MSM_GPIO_INT_CLEAR_0   MSM_GPIO1_REG(0x90)
+#define MSM_GPIO_INT_CLEAR_1   MSM_GPIO2_REG(0x68)
+#define MSM_GPIO_INT_CLEAR_2   MSM_GPIO1_REG(0x94)
+#define MSM_GPIO_INT_CLEAR_3   MSM_GPIO1_REG(0x98)
+#define MSM_GPIO_INT_CLEAR_4   MSM_GPIO1_REG(0x9C)
+#define MSM_GPIO_INT_CLEAR_5   MSM_GPIO1_REG(0xB4)
 
 /* same pin map as above, 1=interrupt pending */
-#define MSM7X00_GPIO_INT_STATUS_0	MSM_GPIO1_SHADOW_REG(0xA0)
-#define MSM7X00_GPIO_INT_STATUS_1	MSM_GPIO2_SHADOW_REG(0x70)
-#define MSM7X00_GPIO_INT_STATUS_2	MSM_GPIO1_SHADOW_REG(0xA4)
-#define MSM7X00_GPIO_INT_STATUS_3	MSM_GPIO1_SHADOW_REG(0xA8)
-#define MSM7X00_GPIO_INT_STATUS_4	MSM_GPIO1_SHADOW_REG(0xAC)
-#define MSM7X00_GPIO_INT_STATUS_5	MSM_GPIO1_SHADOW_REG(0xB0)
+#define MSM_GPIO_INT_STATUS_0  MSM_GPIO1_REG(0xA0)
+#define MSM_GPIO_INT_STATUS_1  MSM_GPIO2_REG(0x70)
+#define MSM_GPIO_INT_STATUS_2  MSM_GPIO1_REG(0xA4)
+#define MSM_GPIO_INT_STATUS_3  MSM_GPIO1_REG(0xA8)
+#define MSM_GPIO_INT_STATUS_4  MSM_GPIO1_REG(0xAC)
+#define MSM_GPIO_INT_STATUS_5  MSM_GPIO1_REG(0xB0)
 
-/*
- * QSD8X50 registers
- */
+#endif
+
+#if defined(CONFIG_ARCH_MSM7X30)
+
 /* output value */
-#define QSD8X50_GPIO_OUT_0	MSM_GPIO1_SHADOW_REG(0x00)  /* gpio  15-0   */
-#define QSD8X50_GPIO_OUT_1	MSM_GPIO2_SHADOW_REG(0x00)  /* gpio  42-16  */
-#define QSD8X50_GPIO_OUT_2	MSM_GPIO1_SHADOW_REG(0x04)  /* gpio  67-43  */
-#define QSD8X50_GPIO_OUT_3	MSM_GPIO1_SHADOW_REG(0x08)  /* gpio  94-68  */
-#define QSD8X50_GPIO_OUT_4	MSM_GPIO1_SHADOW_REG(0x0C)  /* gpio 103-95  */
-#define QSD8X50_GPIO_OUT_5	MSM_GPIO1_SHADOW_REG(0x10)  /* gpio 121-104 */
-#define QSD8X50_GPIO_OUT_6	MSM_GPIO1_SHADOW_REG(0x14)  /* gpio 152-122 */
-#define QSD8X50_GPIO_OUT_7	MSM_GPIO1_SHADOW_REG(0x18)  /* gpio 164-153 */
+#define MSM_GPIO_OUT_0         MSM_GPIO1_REG(0x00)   /* gpio  15-0   */
+#define MSM_GPIO_OUT_1         MSM_GPIO2_REG(0x00)   /* gpio  43-16  */
+#define MSM_GPIO_OUT_2         MSM_GPIO1_REG(0x04)   /* gpio  67-44  */
+#define MSM_GPIO_OUT_3         MSM_GPIO1_REG(0x08)   /* gpio  94-68  */
+#define MSM_GPIO_OUT_4         MSM_GPIO1_REG(0x0C)   /* gpio 106-95  */
+#define MSM_GPIO_OUT_5         MSM_GPIO1_REG(0x50)   /* gpio 133-107 */
+#define MSM_GPIO_OUT_6         MSM_GPIO1_REG(0xC4)   /* gpio 150-134 */
+#define MSM_GPIO_OUT_7         MSM_GPIO1_REG(0x214)  /* gpio 181-151 */
 
 /* same pin map as above, output enable */
-#define QSD8X50_GPIO_OE_0	MSM_GPIO1_SHADOW_REG(0x20)
-#define QSD8X50_GPIO_OE_1	MSM_GPIO2_SHADOW_REG(0x08)
-#define QSD8X50_GPIO_OE_2	MSM_GPIO1_SHADOW_REG(0x24)
-#define QSD8X50_GPIO_OE_3	MSM_GPIO1_SHADOW_REG(0x28)
-#define QSD8X50_GPIO_OE_4	MSM_GPIO1_SHADOW_REG(0x2C)
-#define QSD8X50_GPIO_OE_5	MSM_GPIO1_SHADOW_REG(0x30)
-#define QSD8X50_GPIO_OE_6	MSM_GPIO1_SHADOW_REG(0x34)
-#define QSD8X50_GPIO_OE_7	MSM_GPIO1_SHADOW_REG(0x38)
+#define MSM_GPIO_OE_0          MSM_GPIO1_REG(0x10)
+#define MSM_GPIO_OE_1          MSM_GPIO2_REG(0x08)
+#define MSM_GPIO_OE_2          MSM_GPIO1_REG(0x14)
+#define MSM_GPIO_OE_3          MSM_GPIO1_REG(0x18)
+#define MSM_GPIO_OE_4          MSM_GPIO1_REG(0x1C)
+#define MSM_GPIO_OE_5          MSM_GPIO1_REG(0x54)
+#define MSM_GPIO_OE_6          MSM_GPIO1_REG(0xC8)
+#define MSM_GPIO_OE_7          MSM_GPIO1_REG(0x218)
 
 /* same pin map as above, input read */
-#define QSD8X50_GPIO_IN_0	MSM_GPIO1_SHADOW_REG(0x50)
-#define QSD8X50_GPIO_IN_1	MSM_GPIO2_SHADOW_REG(0x20)
-#define QSD8X50_GPIO_IN_2	MSM_GPIO1_SHADOW_REG(0x54)
-#define QSD8X50_GPIO_IN_3	MSM_GPIO1_SHADOW_REG(0x58)
-#define QSD8X50_GPIO_IN_4	MSM_GPIO1_SHADOW_REG(0x5C)
-#define QSD8X50_GPIO_IN_5	MSM_GPIO1_SHADOW_REG(0x60)
-#define QSD8X50_GPIO_IN_6	MSM_GPIO1_SHADOW_REG(0x64)
-#define QSD8X50_GPIO_IN_7	MSM_GPIO1_SHADOW_REG(0x68)
+#define MSM_GPIO_IN_0          MSM_GPIO1_REG(0x34)
+#define MSM_GPIO_IN_1          MSM_GPIO2_REG(0x20)
+#define MSM_GPIO_IN_2          MSM_GPIO1_REG(0x38)
+#define MSM_GPIO_IN_3          MSM_GPIO1_REG(0x3C)
+#define MSM_GPIO_IN_4          MSM_GPIO1_REG(0x40)
+#define MSM_GPIO_IN_5          MSM_GPIO1_REG(0x44)
+#define MSM_GPIO_IN_6          MSM_GPIO1_REG(0xCC)
+#define MSM_GPIO_IN_7          MSM_GPIO1_REG(0x21C)
 
 /* same pin map as above, 1=edge 0=level interrup */
-#define QSD8X50_GPIO_INT_EDGE_0	MSM_GPIO1_SHADOW_REG(0x70)
-#define QSD8X50_GPIO_INT_EDGE_1	MSM_GPIO2_SHADOW_REG(0x50)
-#define QSD8X50_GPIO_INT_EDGE_2	MSM_GPIO1_SHADOW_REG(0x74)
-#define QSD8X50_GPIO_INT_EDGE_3	MSM_GPIO1_SHADOW_REG(0x78)
-#define QSD8X50_GPIO_INT_EDGE_4	MSM_GPIO1_SHADOW_REG(0x7C)
-#define QSD8X50_GPIO_INT_EDGE_5	MSM_GPIO1_SHADOW_REG(0x80)
-#define QSD8X50_GPIO_INT_EDGE_6	MSM_GPIO1_SHADOW_REG(0x84)
-#define QSD8X50_GPIO_INT_EDGE_7	MSM_GPIO1_SHADOW_REG(0x88)
+#define MSM_GPIO_INT_EDGE_0    MSM_GPIO1_REG(0x60)
+#define MSM_GPIO_INT_EDGE_1    MSM_GPIO2_REG(0x50)
+#define MSM_GPIO_INT_EDGE_2    MSM_GPIO1_REG(0x64)
+#define MSM_GPIO_INT_EDGE_3    MSM_GPIO1_REG(0x68)
+#define MSM_GPIO_INT_EDGE_4    MSM_GPIO1_REG(0x6C)
+#define MSM_GPIO_INT_EDGE_5    MSM_GPIO1_REG(0xC0)
+#define MSM_GPIO_INT_EDGE_6    MSM_GPIO1_REG(0xD0)
+#define MSM_GPIO_INT_EDGE_7    MSM_GPIO1_REG(0x240)
 
 /* same pin map as above, 1=positive 0=negative */
-#define QSD8X50_GPIO_INT_POS_0	MSM_GPIO1_SHADOW_REG(0x90)
-#define QSD8X50_GPIO_INT_POS_1	MSM_GPIO2_SHADOW_REG(0x58)
-#define QSD8X50_GPIO_INT_POS_2	MSM_GPIO1_SHADOW_REG(0x94)
-#define QSD8X50_GPIO_INT_POS_3	MSM_GPIO1_SHADOW_REG(0x98)
-#define QSD8X50_GPIO_INT_POS_4	MSM_GPIO1_SHADOW_REG(0x9C)
-#define QSD8X50_GPIO_INT_POS_5	MSM_GPIO1_SHADOW_REG(0xA0)
-#define QSD8X50_GPIO_INT_POS_6	MSM_GPIO1_SHADOW_REG(0xA4)
-#define QSD8X50_GPIO_INT_POS_7	MSM_GPIO1_SHADOW_REG(0xA8)
+#define MSM_GPIO_INT_POS_0     MSM_GPIO1_REG(0x70)
+#define MSM_GPIO_INT_POS_1     MSM_GPIO2_REG(0x58)
+#define MSM_GPIO_INT_POS_2     MSM_GPIO1_REG(0x74)
+#define MSM_GPIO_INT_POS_3     MSM_GPIO1_REG(0x78)
+#define MSM_GPIO_INT_POS_4     MSM_GPIO1_REG(0x7C)
+#define MSM_GPIO_INT_POS_5     MSM_GPIO1_REG(0xBC)
+#define MSM_GPIO_INT_POS_6     MSM_GPIO1_REG(0xD4)
+#define MSM_GPIO_INT_POS_7     MSM_GPIO1_REG(0x228)
 
 /* same pin map as above, interrupt enable */
-#define QSD8X50_GPIO_INT_EN_0	MSM_GPIO1_SHADOW_REG(0xB0)
-#define QSD8X50_GPIO_INT_EN_1	MSM_GPIO2_SHADOW_REG(0x60)
-#define QSD8X50_GPIO_INT_EN_2	MSM_GPIO1_SHADOW_REG(0xB4)
-#define QSD8X50_GPIO_INT_EN_3	MSM_GPIO1_SHADOW_REG(0xB8)
-#define QSD8X50_GPIO_INT_EN_4	MSM_GPIO1_SHADOW_REG(0xBC)
-#define QSD8X50_GPIO_INT_EN_5	MSM_GPIO1_SHADOW_REG(0xC0)
-#define QSD8X50_GPIO_INT_EN_6	MSM_GPIO1_SHADOW_REG(0xC4)
-#define QSD8X50_GPIO_INT_EN_7	MSM_GPIO1_SHADOW_REG(0xC8)
+#define MSM_GPIO_INT_EN_0      MSM_GPIO1_REG(0x80)
+#define MSM_GPIO_INT_EN_1      MSM_GPIO2_REG(0x60)
+#define MSM_GPIO_INT_EN_2      MSM_GPIO1_REG(0x84)
+#define MSM_GPIO_INT_EN_3      MSM_GPIO1_REG(0x88)
+#define MSM_GPIO_INT_EN_4      MSM_GPIO1_REG(0x8C)
+#define MSM_GPIO_INT_EN_5      MSM_GPIO1_REG(0xB8)
+#define MSM_GPIO_INT_EN_6      MSM_GPIO1_REG(0xD8)
+#define MSM_GPIO_INT_EN_7      MSM_GPIO1_REG(0x22C)
 
 /* same pin map as above, write 1 to clear interrupt */
-#define QSD8X50_GPIO_INT_CLEAR_0	MSM_GPIO1_SHADOW_REG(0xD0)
-#define QSD8X50_GPIO_INT_CLEAR_1	MSM_GPIO2_SHADOW_REG(0x68)
-#define QSD8X50_GPIO_INT_CLEAR_2	MSM_GPIO1_SHADOW_REG(0xD4)
-#define QSD8X50_GPIO_INT_CLEAR_3	MSM_GPIO1_SHADOW_REG(0xD8)
-#define QSD8X50_GPIO_INT_CLEAR_4	MSM_GPIO1_SHADOW_REG(0xDC)
-#define QSD8X50_GPIO_INT_CLEAR_5	MSM_GPIO1_SHADOW_REG(0xE0)
-#define QSD8X50_GPIO_INT_CLEAR_6	MSM_GPIO1_SHADOW_REG(0xE4)
-#define QSD8X50_GPIO_INT_CLEAR_7	MSM_GPIO1_SHADOW_REG(0xE8)
+#define MSM_GPIO_INT_CLEAR_0   MSM_GPIO1_REG(0x90)
+#define MSM_GPIO_INT_CLEAR_1   MSM_GPIO2_REG(0x68)
+#define MSM_GPIO_INT_CLEAR_2   MSM_GPIO1_REG(0x94)
+#define MSM_GPIO_INT_CLEAR_3   MSM_GPIO1_REG(0x98)
+#define MSM_GPIO_INT_CLEAR_4   MSM_GPIO1_REG(0x9C)
+#define MSM_GPIO_INT_CLEAR_5   MSM_GPIO1_REG(0xB4)
+#define MSM_GPIO_INT_CLEAR_6   MSM_GPIO1_REG(0xDC)
+#define MSM_GPIO_INT_CLEAR_7   MSM_GPIO1_REG(0x230)
 
 /* same pin map as above, 1=interrupt pending */
-#define QSD8X50_GPIO_INT_STATUS_0	MSM_GPIO1_SHADOW_REG(0xF0)
-#define QSD8X50_GPIO_INT_STATUS_1	MSM_GPIO2_SHADOW_REG(0x70)
-#define QSD8X50_GPIO_INT_STATUS_2	MSM_GPIO1_SHADOW_REG(0xF4)
-#define QSD8X50_GPIO_INT_STATUS_3	MSM_GPIO1_SHADOW_REG(0xF8)
-#define QSD8X50_GPIO_INT_STATUS_4	MSM_GPIO1_SHADOW_REG(0xFC)
-#define QSD8X50_GPIO_INT_STATUS_5	MSM_GPIO1_SHADOW_REG(0x100)
-#define QSD8X50_GPIO_INT_STATUS_6	MSM_GPIO1_SHADOW_REG(0x104)
-#define QSD8X50_GPIO_INT_STATUS_7	MSM_GPIO1_SHADOW_REG(0x108)
+#define MSM_GPIO_INT_STATUS_0  MSM_GPIO1_REG(0xA0)
+#define MSM_GPIO_INT_STATUS_1  MSM_GPIO2_REG(0x70)
+#define MSM_GPIO_INT_STATUS_2  MSM_GPIO1_REG(0xA4)
+#define MSM_GPIO_INT_STATUS_3  MSM_GPIO1_REG(0xA8)
+#define MSM_GPIO_INT_STATUS_4  MSM_GPIO1_REG(0xAC)
+#define MSM_GPIO_INT_STATUS_5  MSM_GPIO1_REG(0xB0)
+#define MSM_GPIO_INT_STATUS_6  MSM_GPIO1_REG(0xE0)
+#define MSM_GPIO_INT_STATUS_7  MSM_GPIO1_REG(0x234)
 
-/*
- * MSM7X30 registers
- */
-/* output value */
-#define MSM7X30_GPIO_OUT_0	MSM_GPIO1_REG(0x00)   /* gpio  15-0   */
-#define MSM7X30_GPIO_OUT_1	MSM_GPIO2_REG(0x00)   /* gpio  43-16  */
-#define MSM7X30_GPIO_OUT_2	MSM_GPIO1_REG(0x04)   /* gpio  67-44  */
-#define MSM7X30_GPIO_OUT_3	MSM_GPIO1_REG(0x08)   /* gpio  94-68  */
-#define MSM7X30_GPIO_OUT_4	MSM_GPIO1_REG(0x0C)   /* gpio 106-95  */
-#define MSM7X30_GPIO_OUT_5	MSM_GPIO1_REG(0x50)   /* gpio 133-107 */
-#define MSM7X30_GPIO_OUT_6	MSM_GPIO1_REG(0xC4)   /* gpio 150-134 */
-#define MSM7X30_GPIO_OUT_7	MSM_GPIO1_REG(0x214)  /* gpio 181-151 */
+#endif
 
-/* same pin map as above, output enable */
-#define MSM7X30_GPIO_OE_0	MSM_GPIO1_REG(0x10)
-#define MSM7X30_GPIO_OE_1	MSM_GPIO2_REG(0x08)
-#define MSM7X30_GPIO_OE_2	MSM_GPIO1_REG(0x14)
-#define MSM7X30_GPIO_OE_3	MSM_GPIO1_REG(0x18)
-#define MSM7X30_GPIO_OE_4	MSM_GPIO1_REG(0x1C)
-#define MSM7X30_GPIO_OE_5	MSM_GPIO1_REG(0x54)
-#define MSM7X30_GPIO_OE_6	MSM_GPIO1_REG(0xC8)
-#define MSM7X30_GPIO_OE_7	MSM_GPIO1_REG(0x218)
-
-/* same pin map as above, input read */
-#define MSM7X30_GPIO_IN_0	MSM_GPIO1_REG(0x34)
-#define MSM7X30_GPIO_IN_1	MSM_GPIO2_REG(0x20)
-#define MSM7X30_GPIO_IN_2	MSM_GPIO1_REG(0x38)
-#define MSM7X30_GPIO_IN_3	MSM_GPIO1_REG(0x3C)
-#define MSM7X30_GPIO_IN_4	MSM_GPIO1_REG(0x40)
-#define MSM7X30_GPIO_IN_5	MSM_GPIO1_REG(0x44)
-#define MSM7X30_GPIO_IN_6	MSM_GPIO1_REG(0xCC)
-#define MSM7X30_GPIO_IN_7	MSM_GPIO1_REG(0x21C)
-
-/* same pin map as above, 1=edge 0=level interrup */
-#define MSM7X30_GPIO_INT_EDGE_0	MSM_GPIO1_REG(0x60)
-#define MSM7X30_GPIO_INT_EDGE_1	MSM_GPIO2_REG(0x50)
-#define MSM7X30_GPIO_INT_EDGE_2	MSM_GPIO1_REG(0x64)
-#define MSM7X30_GPIO_INT_EDGE_3	MSM_GPIO1_REG(0x68)
-#define MSM7X30_GPIO_INT_EDGE_4	MSM_GPIO1_REG(0x6C)
-#define MSM7X30_GPIO_INT_EDGE_5	MSM_GPIO1_REG(0xC0)
-#define MSM7X30_GPIO_INT_EDGE_6	MSM_GPIO1_REG(0xD0)
-#define MSM7X30_GPIO_INT_EDGE_7	MSM_GPIO1_REG(0x240)
-
-/* same pin map as above, 1=positive 0=negative */
-#define MSM7X30_GPIO_INT_POS_0	MSM_GPIO1_REG(0x70)
-#define MSM7X30_GPIO_INT_POS_1	MSM_GPIO2_REG(0x58)
-#define MSM7X30_GPIO_INT_POS_2	MSM_GPIO1_REG(0x74)
-#define MSM7X30_GPIO_INT_POS_3	MSM_GPIO1_REG(0x78)
-#define MSM7X30_GPIO_INT_POS_4	MSM_GPIO1_REG(0x7C)
-#define MSM7X30_GPIO_INT_POS_5	MSM_GPIO1_REG(0xBC)
-#define MSM7X30_GPIO_INT_POS_6	MSM_GPIO1_REG(0xD4)
-#define MSM7X30_GPIO_INT_POS_7	MSM_GPIO1_REG(0x228)
-
-/* same pin map as above, interrupt enable */
-#define MSM7X30_GPIO_INT_EN_0	MSM_GPIO1_REG(0x80)
-#define MSM7X30_GPIO_INT_EN_1	MSM_GPIO2_REG(0x60)
-#define MSM7X30_GPIO_INT_EN_2	MSM_GPIO1_REG(0x84)
-#define MSM7X30_GPIO_INT_EN_3	MSM_GPIO1_REG(0x88)
-#define MSM7X30_GPIO_INT_EN_4	MSM_GPIO1_REG(0x8C)
-#define MSM7X30_GPIO_INT_EN_5	MSM_GPIO1_REG(0xB8)
-#define MSM7X30_GPIO_INT_EN_6	MSM_GPIO1_REG(0xD8)
-#define MSM7X30_GPIO_INT_EN_7	MSM_GPIO1_REG(0x22C)
-
-/* same pin map as above, write 1 to clear interrupt */
-#define MSM7X30_GPIO_INT_CLEAR_0	MSM_GPIO1_REG(0x90)
-#define MSM7X30_GPIO_INT_CLEAR_1	MSM_GPIO2_REG(0x68)
-#define MSM7X30_GPIO_INT_CLEAR_2	MSM_GPIO1_REG(0x94)
-#define MSM7X30_GPIO_INT_CLEAR_3	MSM_GPIO1_REG(0x98)
-#define MSM7X30_GPIO_INT_CLEAR_4	MSM_GPIO1_REG(0x9C)
-#define MSM7X30_GPIO_INT_CLEAR_5	MSM_GPIO1_REG(0xB4)
-#define MSM7X30_GPIO_INT_CLEAR_6	MSM_GPIO1_REG(0xDC)
-#define MSM7X30_GPIO_INT_CLEAR_7	MSM_GPIO1_REG(0x230)
-
-/* same pin map as above, 1=interrupt pending */
-#define MSM7X30_GPIO_INT_STATUS_0	MSM_GPIO1_REG(0xA0)
-#define MSM7X30_GPIO_INT_STATUS_1	MSM_GPIO2_REG(0x70)
-#define MSM7X30_GPIO_INT_STATUS_2	MSM_GPIO1_REG(0xA4)
-#define MSM7X30_GPIO_INT_STATUS_3	MSM_GPIO1_REG(0xA8)
-#define MSM7X30_GPIO_INT_STATUS_4	MSM_GPIO1_REG(0xAC)
-#define MSM7X30_GPIO_INT_STATUS_5	MSM_GPIO1_REG(0xB0)
-#define MSM7X30_GPIO_INT_STATUS_6	MSM_GPIO1_REG(0xE0)
-#define MSM7X30_GPIO_INT_STATUS_7	MSM_GPIO1_REG(0x234)
+enum {
+	GPIO_DEBUG_SLEEP = 1U << 0,
+};
+static int msm_gpio_debug_mask;
+module_param_named(debug_mask, msm_gpio_debug_mask, int,
+		   S_IRUGO | S_IWUSR | S_IWGRP);
 
 #define FIRST_GPIO_IRQ MSM_GPIO_TO_INT(0)
 
-#define MSM_GPIO_BANK(soc, bank, first, last)				\
+#define MSM_GPIO_BANK(bank, first, last)				\
 	{								\
 		.regs = {						\
-			.out =         soc##_GPIO_OUT_##bank,		\
-			.in =          soc##_GPIO_IN_##bank,		\
-			.int_status =  soc##_GPIO_INT_STATUS_##bank,	\
-			.int_clear =   soc##_GPIO_INT_CLEAR_##bank,	\
-			.int_en =      soc##_GPIO_INT_EN_##bank,	\
-			.int_edge =    soc##_GPIO_INT_EDGE_##bank,	\
-			.int_pos =     soc##_GPIO_INT_POS_##bank,	\
-			.oe =          soc##_GPIO_OE_##bank,		\
+			.out =         MSM_GPIO_OUT_##bank,		\
+			.in =          MSM_GPIO_IN_##bank,		\
+			.int_status =  MSM_GPIO_INT_STATUS_##bank,	\
+			.int_clear =   MSM_GPIO_INT_CLEAR_##bank,	\
+			.int_en =      MSM_GPIO_INT_EN_##bank,		\
+			.int_edge =    MSM_GPIO_INT_EDGE_##bank,	\
+			.int_pos =     MSM_GPIO_INT_POS_##bank,		\
+			.oe =          MSM_GPIO_OE_##bank,		\
 		},							\
 		.chip = {						\
 			.base = (first),				\
@@ -329,11 +264,11 @@
 	unsigned mask = BIT(offset);
 	unsigned val;
 
-	val = readl(msm_chip->regs.out);
+	val = __raw_readl(msm_chip->regs.out);
 	if (on)
-		writel(val | mask, msm_chip->regs.out);
+		__raw_writel(val | mask, msm_chip->regs.out);
 	else
-		writel(val & ~mask, msm_chip->regs.out);
+		__raw_writel(val & ~mask, msm_chip->regs.out);
 	return 0;
 }
 
@@ -342,13 +277,13 @@
 	int loop_limit = 100;
 	unsigned pol, val, val2, intstat;
 	do {
-		val = readl(msm_chip->regs.in);
-		pol = readl(msm_chip->regs.int_pos);
+		val = __raw_readl(msm_chip->regs.in);
+		pol = __raw_readl(msm_chip->regs.int_pos);
 		pol = (pol & ~msm_chip->both_edge_detect) |
 		      (~val & msm_chip->both_edge_detect);
-		writel(pol, msm_chip->regs.int_pos);
-		intstat = readl(msm_chip->regs.int_status);
-		val2 = readl(msm_chip->regs.in);
+		__raw_writel(pol, msm_chip->regs.int_pos);
+		intstat = __raw_readl(msm_chip->regs.int_status);
+		val2 = __raw_readl(msm_chip->regs.in);
 		if (((val ^ val2) & msm_chip->both_edge_detect & ~intstat) == 0)
 			return;
 	} while (loop_limit-- > 0);
@@ -365,10 +300,10 @@
 	/* Save interrupts that already triggered before we loose them. */
 	/* Any interrupt that triggers between the read of int_status */
 	/* and the write to int_clear will still be lost though. */
-	msm_chip->int_status_copy |= readl(msm_chip->regs.int_status);
+	msm_chip->int_status_copy |= __raw_readl(msm_chip->regs.int_status);
 	msm_chip->int_status_copy &= ~bit;
 #endif
-	writel(bit, msm_chip->regs.int_clear);
+	__raw_writel(bit, msm_chip->regs.int_clear);
 	msm_gpio_update_both_edge_detect(msm_chip);
 	return 0;
 }
@@ -380,7 +315,9 @@
 
 	msm_chip = container_of(chip, struct msm_gpio_chip, chip);
 	spin_lock_irqsave(&msm_chip->lock, irq_flags);
-	writel(readl(msm_chip->regs.oe) & ~BIT(offset), msm_chip->regs.oe);
+	__raw_writel(__raw_readl(msm_chip->regs.oe) & ~BIT(offset),
+			msm_chip->regs.oe);
+	mb();
 	spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
 	return 0;
 }
@@ -394,7 +331,9 @@
 	msm_chip = container_of(chip, struct msm_gpio_chip, chip);
 	spin_lock_irqsave(&msm_chip->lock, irq_flags);
 	msm_gpio_write(msm_chip, offset, value);
-	writel(readl(msm_chip->regs.oe) | BIT(offset), msm_chip->regs.oe);
+	__raw_writel(__raw_readl(msm_chip->regs.oe) | BIT(offset),
+			msm_chip->regs.oe);
+	mb();
 	spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
 	return 0;
 }
@@ -402,9 +341,12 @@
 static int msm_gpio_get(struct gpio_chip *chip, unsigned offset)
 {
 	struct msm_gpio_chip *msm_chip;
+	int rc;
 
 	msm_chip = container_of(chip, struct msm_gpio_chip, chip);
-	return (readl(msm_chip->regs.in) & (1U << offset)) ? 1 : 0;
+	rc = (__raw_readl(msm_chip->regs.in) & (1U << offset)) ? 1 : 0;
+	mb();
+	return rc;
 }
 
 static void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
@@ -415,6 +357,7 @@
 	msm_chip = container_of(chip, struct msm_gpio_chip, chip);
 	spin_lock_irqsave(&msm_chip->lock, irq_flags);
 	msm_gpio_write(msm_chip, offset, value);
+	mb();
 	spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
 }
 
@@ -438,84 +381,88 @@
 #define msm_gpio_free NULL
 #endif
 
-static struct msm_gpio_chip *msm_gpio_chips;
-static int msm_gpio_count;
-
-static struct msm_gpio_chip msm_gpio_chips_msm7x01[] = {
-	MSM_GPIO_BANK(MSM7X00, 0,   0,  15),
-	MSM_GPIO_BANK(MSM7X00, 1,  16,  42),
-	MSM_GPIO_BANK(MSM7X00, 2,  43,  67),
-	MSM_GPIO_BANK(MSM7X00, 3,  68,  94),
-	MSM_GPIO_BANK(MSM7X00, 4,  95, 106),
-	MSM_GPIO_BANK(MSM7X00, 5, 107, 121),
-};
-
-static struct msm_gpio_chip msm_gpio_chips_msm7x30[] = {
-	MSM_GPIO_BANK(MSM7X30, 0,   0,  15),
-	MSM_GPIO_BANK(MSM7X30, 1,  16,  43),
-	MSM_GPIO_BANK(MSM7X30, 2,  44,  67),
-	MSM_GPIO_BANK(MSM7X30, 3,  68,  94),
-	MSM_GPIO_BANK(MSM7X30, 4,  95, 106),
-	MSM_GPIO_BANK(MSM7X30, 5, 107, 133),
-	MSM_GPIO_BANK(MSM7X30, 6, 134, 150),
-	MSM_GPIO_BANK(MSM7X30, 7, 151, 181),
-};
-
-static struct msm_gpio_chip msm_gpio_chips_qsd8x50[] = {
-	MSM_GPIO_BANK(QSD8X50, 0,   0,  15),
-	MSM_GPIO_BANK(QSD8X50, 1,  16,  42),
-	MSM_GPIO_BANK(QSD8X50, 2,  43,  67),
-	MSM_GPIO_BANK(QSD8X50, 3,  68,  94),
-	MSM_GPIO_BANK(QSD8X50, 4,  95, 103),
-	MSM_GPIO_BANK(QSD8X50, 5, 104, 121),
-	MSM_GPIO_BANK(QSD8X50, 6, 122, 152),
-	MSM_GPIO_BANK(QSD8X50, 7, 153, 164),
+struct msm_gpio_chip msm_gpio_chips[] = {
+#if defined(CONFIG_ARCH_MSM7X00A)
+	MSM_GPIO_BANK(0,   0,  15),
+	MSM_GPIO_BANK(1,  16,  42),
+	MSM_GPIO_BANK(2,  43,  67),
+	MSM_GPIO_BANK(3,  68,  94),
+	MSM_GPIO_BANK(4,  95, 106),
+	MSM_GPIO_BANK(5, 107, 121),
+#elif defined(CONFIG_ARCH_MSM7X25) || defined(CONFIG_ARCH_MSM7X27)
+	MSM_GPIO_BANK(0,   0,  15),
+	MSM_GPIO_BANK(1,  16,  42),
+	MSM_GPIO_BANK(2,  43,  67),
+	MSM_GPIO_BANK(3,  68,  94),
+	MSM_GPIO_BANK(4,  95, 106),
+	MSM_GPIO_BANK(5, 107, 132),
+#elif defined(CONFIG_ARCH_MSM7X30)
+	MSM_GPIO_BANK(0,   0,  15),
+	MSM_GPIO_BANK(1,  16,  43),
+	MSM_GPIO_BANK(2,  44,  67),
+	MSM_GPIO_BANK(3,  68,  94),
+	MSM_GPIO_BANK(4,  95, 106),
+	MSM_GPIO_BANK(5, 107, 133),
+	MSM_GPIO_BANK(6, 134, 150),
+	MSM_GPIO_BANK(7, 151, 181),
+#elif defined(CONFIG_ARCH_QSD8X50)
+	MSM_GPIO_BANK(0,   0,  15),
+	MSM_GPIO_BANK(1,  16,  42),
+	MSM_GPIO_BANK(2,  43,  67),
+	MSM_GPIO_BANK(3,  68,  94),
+	MSM_GPIO_BANK(4,  95, 103),
+	MSM_GPIO_BANK(5, 104, 121),
+	MSM_GPIO_BANK(6, 122, 152),
+	MSM_GPIO_BANK(7, 153, 164),
+#endif
 };
 
 static void msm_gpio_irq_ack(struct irq_data *d)
 {
 	unsigned long irq_flags;
-	struct msm_gpio_chip *msm_chip = irq_data_get_irq_chip_data(d);
+	struct msm_gpio_chip *msm_chip = irq_get_chip_data(d->irq);
 	spin_lock_irqsave(&msm_chip->lock, irq_flags);
 	msm_gpio_clear_detect_status(msm_chip,
-				     d->irq - gpio_to_irq(msm_chip->chip.base));
+			     d->irq - gpio_to_irq(msm_chip->chip.base));
 	spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
 }
 
 static void msm_gpio_irq_mask(struct irq_data *d)
 {
 	unsigned long irq_flags;
-	struct msm_gpio_chip *msm_chip = irq_data_get_irq_chip_data(d);
+	struct msm_gpio_chip *msm_chip = irq_get_chip_data(d->irq);
 	unsigned offset = d->irq - gpio_to_irq(msm_chip->chip.base);
 
 	spin_lock_irqsave(&msm_chip->lock, irq_flags);
 	/* level triggered interrupts are also latched */
-	if (!(readl(msm_chip->regs.int_edge) & BIT(offset)))
+	if (!(__raw_readl(msm_chip->regs.int_edge) & BIT(offset)))
 		msm_gpio_clear_detect_status(msm_chip, offset);
 	msm_chip->int_enable[0] &= ~BIT(offset);
-	writel(msm_chip->int_enable[0], msm_chip->regs.int_en);
+	__raw_writel(msm_chip->int_enable[0], msm_chip->regs.int_en);
+	mb();
 	spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
 }
 
 static void msm_gpio_irq_unmask(struct irq_data *d)
 {
 	unsigned long irq_flags;
-	struct msm_gpio_chip *msm_chip = irq_data_get_irq_chip_data(d);
+	struct msm_gpio_chip *msm_chip = irq_get_chip_data(d->irq);
 	unsigned offset = d->irq - gpio_to_irq(msm_chip->chip.base);
 
 	spin_lock_irqsave(&msm_chip->lock, irq_flags);
 	/* level triggered interrupts are also latched */
-	if (!(readl(msm_chip->regs.int_edge) & BIT(offset)))
+	if (!(__raw_readl(msm_chip->regs.int_edge) & BIT(offset)))
 		msm_gpio_clear_detect_status(msm_chip, offset);
 	msm_chip->int_enable[0] |= BIT(offset);
-	writel(msm_chip->int_enable[0], msm_chip->regs.int_en);
+	__raw_writel(msm_chip->int_enable[0], msm_chip->regs.int_en);
+	mb();
 	spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
 }
 
 static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
 {
 	unsigned long irq_flags;
-	struct msm_gpio_chip *msm_chip = irq_data_get_irq_chip_data(d);
+	struct msm_gpio_chip *msm_chip = irq_get_chip_data(d->irq);
 	unsigned offset = d->irq - gpio_to_irq(msm_chip->chip.base);
 
 	spin_lock_irqsave(&msm_chip->lock, irq_flags);
@@ -532,17 +479,17 @@
 static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type)
 {
 	unsigned long irq_flags;
-	struct msm_gpio_chip *msm_chip = irq_data_get_irq_chip_data(d);
+	struct msm_gpio_chip *msm_chip = irq_get_chip_data(d->irq);
 	unsigned offset = d->irq - gpio_to_irq(msm_chip->chip.base);
 	unsigned val, mask = BIT(offset);
 
 	spin_lock_irqsave(&msm_chip->lock, irq_flags);
-	val = readl(msm_chip->regs.int_edge);
+	val = __raw_readl(msm_chip->regs.int_edge);
 	if (flow_type & IRQ_TYPE_EDGE_BOTH) {
-		writel(val | mask, msm_chip->regs.int_edge);
+		__raw_writel(val | mask, msm_chip->regs.int_edge);
 		__irq_set_handler_locked(d->irq, handle_edge_irq);
 	} else {
-		writel(val & ~mask, msm_chip->regs.int_edge);
+		__raw_writel(val & ~mask, msm_chip->regs.int_edge);
 		__irq_set_handler_locked(d->irq, handle_level_irq);
 	}
 	if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) {
@@ -550,12 +497,13 @@
 		msm_gpio_update_both_edge_detect(msm_chip);
 	} else {
 		msm_chip->both_edge_detect &= ~mask;
-		val = readl(msm_chip->regs.int_pos);
+		val = __raw_readl(msm_chip->regs.int_pos);
 		if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_HIGH))
-			writel(val | mask, msm_chip->regs.int_pos);
+			__raw_writel(val | mask, msm_chip->regs.int_pos);
 		else
-			writel(val & ~mask, msm_chip->regs.int_pos);
+			__raw_writel(val & ~mask, msm_chip->regs.int_pos);
 	}
+	mb();
 	spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
 	return 0;
 }
@@ -564,10 +512,13 @@
 {
 	int i, j, mask;
 	unsigned val;
+	struct irq_chip *chip = irq_desc_get_chip(desc);
 
-	for (i = 0; i < msm_gpio_count; i++) {
+	chained_irq_enter(chip, desc);
+
+	for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) {
 		struct msm_gpio_chip *msm_chip = &msm_gpio_chips[i];
-		val = readl(msm_chip->regs.int_status);
+		val = __raw_readl(msm_chip->regs.int_status);
 		val &= msm_chip->int_enable[0];
 		while (val) {
 			mask = val & -val;
@@ -580,34 +531,266 @@
 					   msm_chip->chip.base + j);
 		}
 	}
-	desc->irq_data.chip->irq_ack(&desc->irq_data);
+
+	chained_irq_exit(chip, desc);
 }
 
 static struct irq_chip msm_gpio_irq_chip = {
-	.name          = "msmgpio",
-	.irq_ack       = msm_gpio_irq_ack,
-	.irq_mask      = msm_gpio_irq_mask,
-	.irq_unmask    = msm_gpio_irq_unmask,
-	.irq_set_wake  = msm_gpio_irq_set_wake,
-	.irq_set_type  = msm_gpio_irq_set_type,
+	.name      = "msmgpio",
+	.irq_ack	= msm_gpio_irq_ack,
+	.irq_mask	= msm_gpio_irq_mask,
+	.irq_unmask	= msm_gpio_irq_unmask,
+	.irq_set_wake	= msm_gpio_irq_set_wake,
+	.irq_set_type	= msm_gpio_irq_set_type,
 };
 
-static int __init msm_init_gpio(void)
+#define NUM_GPIO_SMEM_BANKS 6
+#define GPIO_SMEM_NUM_GROUPS 2
+#define GPIO_SMEM_MAX_PC_INTERRUPTS 8
+struct tramp_gpio_smem {
+	uint16_t num_fired[GPIO_SMEM_NUM_GROUPS];
+	uint16_t fired[GPIO_SMEM_NUM_GROUPS][GPIO_SMEM_MAX_PC_INTERRUPTS];
+	uint32_t enabled[NUM_GPIO_SMEM_BANKS];
+	uint32_t detection[NUM_GPIO_SMEM_BANKS];
+	uint32_t polarity[NUM_GPIO_SMEM_BANKS];
+};
+
+static void msm_gpio_sleep_int(unsigned long arg)
+{
+	int i, j;
+	struct tramp_gpio_smem *smem_gpio;
+
+	BUILD_BUG_ON(NR_GPIO_IRQS > NUM_GPIO_SMEM_BANKS * 32);
+
+	smem_gpio = smem_alloc(SMEM_GPIO_INT, sizeof(*smem_gpio));
+	if (smem_gpio == NULL)
+		return;
+
+	local_irq_disable();
+	for (i = 0; i < GPIO_SMEM_NUM_GROUPS; i++) {
+		int count = smem_gpio->num_fired[i];
+		for (j = 0; j < count; j++) {
+			/* TODO: Check mask */
+			generic_handle_irq(
+				MSM_GPIO_TO_INT(smem_gpio->fired[i][j]));
+		}
+	}
+	local_irq_enable();
+}
+
+static DECLARE_TASKLET(msm_gpio_sleep_int_tasklet, msm_gpio_sleep_int, 0);
+
+void msm_gpio_enter_sleep(int from_idle)
+{
+	int i;
+	struct tramp_gpio_smem *smem_gpio;
+
+	smem_gpio = smem_alloc(SMEM_GPIO_INT, sizeof(*smem_gpio));
+
+	if (smem_gpio) {
+		for (i = 0; i < ARRAY_SIZE(smem_gpio->enabled); i++) {
+			smem_gpio->enabled[i] = 0;
+			smem_gpio->detection[i] = 0;
+			smem_gpio->polarity[i] = 0;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) {
+		__raw_writel(msm_gpio_chips[i].int_enable[!from_idle],
+		       msm_gpio_chips[i].regs.int_en);
+		if (smem_gpio) {
+			uint32_t tmp;
+			int start, index, shiftl, shiftr;
+			start = msm_gpio_chips[i].chip.base;
+			index = start / 32;
+			shiftl = start % 32;
+			shiftr = 32 - shiftl;
+			tmp = msm_gpio_chips[i].int_enable[!from_idle];
+			smem_gpio->enabled[index] |= tmp << shiftl;
+			smem_gpio->enabled[index+1] |= tmp >> shiftr;
+			smem_gpio->detection[index] |=
+				__raw_readl(msm_gpio_chips[i].regs.int_edge) <<
+				shiftl;
+			smem_gpio->detection[index+1] |=
+				__raw_readl(msm_gpio_chips[i].regs.int_edge) >>
+				shiftr;
+			smem_gpio->polarity[index] |=
+				__raw_readl(msm_gpio_chips[i].regs.int_pos) <<
+				shiftl;
+			smem_gpio->polarity[index+1] |=
+				__raw_readl(msm_gpio_chips[i].regs.int_pos) >>
+				shiftr;
+		}
+	}
+	mb();
+
+	if (smem_gpio) {
+		if (msm_gpio_debug_mask & GPIO_DEBUG_SLEEP)
+			for (i = 0; i < ARRAY_SIZE(smem_gpio->enabled); i++) {
+				printk("msm_gpio_enter_sleep gpio %d-%d: enable"
+				       " %08x, edge %08x, polarity %08x\n",
+				       i * 32, i * 32 + 31,
+				       smem_gpio->enabled[i],
+				       smem_gpio->detection[i],
+				       smem_gpio->polarity[i]);
+			}
+		for (i = 0; i < GPIO_SMEM_NUM_GROUPS; i++)
+			smem_gpio->num_fired[i] = 0;
+	}
+}
+
+void msm_gpio_exit_sleep(void)
+{
+	int i;
+	struct tramp_gpio_smem *smem_gpio;
+
+	smem_gpio = smem_alloc(SMEM_GPIO_INT, sizeof(*smem_gpio));
+
+	for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) {
+		__raw_writel(msm_gpio_chips[i].int_enable[0],
+		       msm_gpio_chips[i].regs.int_en);
+	}
+	mb();
+
+	if (smem_gpio && (smem_gpio->num_fired[0] || smem_gpio->num_fired[1])) {
+		if (msm_gpio_debug_mask & GPIO_DEBUG_SLEEP)
+			printk(KERN_INFO "gpio: fired %x %x\n",
+			      smem_gpio->num_fired[0], smem_gpio->num_fired[1]);
+		tasklet_schedule(&msm_gpio_sleep_int_tasklet);
+	}
+}
+
+
+int gpio_tlmm_config(unsigned config, unsigned disable)
+{
+	return msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &config, &disable);
+}
+EXPORT_SYMBOL(gpio_tlmm_config);
+
+int msm_gpios_request_enable(const struct msm_gpio *table, int size)
+{
+	int rc = msm_gpios_request(table, size);
+	if (rc)
+		return rc;
+	rc = msm_gpios_enable(table, size);
+	if (rc)
+		msm_gpios_free(table, size);
+	return rc;
+}
+EXPORT_SYMBOL(msm_gpios_request_enable);
+
+void msm_gpios_disable_free(const struct msm_gpio *table, int size)
+{
+	msm_gpios_disable(table, size);
+	msm_gpios_free(table, size);
+}
+EXPORT_SYMBOL(msm_gpios_disable_free);
+
+int msm_gpios_request(const struct msm_gpio *table, int size)
+{
+	int rc;
+	int i;
+	const struct msm_gpio *g;
+	for (i = 0; i < size; i++) {
+		g = table + i;
+		rc = gpio_request(GPIO_PIN(g->gpio_cfg), g->label);
+		if (rc) {
+			pr_err("gpio_request(%d) <%s> failed: %d\n",
+			       GPIO_PIN(g->gpio_cfg), g->label ?: "?", rc);
+			goto err;
+		}
+	}
+	return 0;
+err:
+	msm_gpios_free(table, i);
+	return rc;
+}
+EXPORT_SYMBOL(msm_gpios_request);
+
+void msm_gpios_free(const struct msm_gpio *table, int size)
+{
+	int i;
+	const struct msm_gpio *g;
+	for (i = size-1; i >= 0; i--) {
+		g = table + i;
+		gpio_free(GPIO_PIN(g->gpio_cfg));
+	}
+}
+EXPORT_SYMBOL(msm_gpios_free);
+
+int msm_gpios_enable(const struct msm_gpio *table, int size)
+{
+	int rc;
+	int i;
+	const struct msm_gpio *g;
+	for (i = 0; i < size; i++) {
+		g = table + i;
+		rc = gpio_tlmm_config(g->gpio_cfg, GPIO_CFG_ENABLE);
+		if (rc) {
+			pr_err("gpio_tlmm_config(0x%08x, GPIO_CFG_ENABLE)"
+			       " <%s> failed: %d\n",
+			       g->gpio_cfg, g->label ?: "?", rc);
+			pr_err("pin %d func %d dir %d pull %d drvstr %d\n",
+			       GPIO_PIN(g->gpio_cfg), GPIO_FUNC(g->gpio_cfg),
+			       GPIO_DIR(g->gpio_cfg), GPIO_PULL(g->gpio_cfg),
+			       GPIO_DRVSTR(g->gpio_cfg));
+			goto err;
+		}
+	}
+	return 0;
+err:
+	msm_gpios_disable(table, i);
+	return rc;
+}
+EXPORT_SYMBOL(msm_gpios_enable);
+
+int msm_gpios_disable(const struct msm_gpio *table, int size)
+{
+	int rc = 0;
+	int i;
+	const struct msm_gpio *g;
+	for (i = size-1; i >= 0; i--) {
+		int tmp;
+		g = table + i;
+		tmp = gpio_tlmm_config(g->gpio_cfg, GPIO_CFG_DISABLE);
+		if (tmp) {
+			pr_err("gpio_tlmm_config(0x%08x, GPIO_CFG_DISABLE)"
+			       " <%s> failed: %d\n",
+			       g->gpio_cfg, g->label ?: "?", rc);
+			pr_err("pin %d func %d dir %d pull %d drvstr %d\n",
+			       GPIO_PIN(g->gpio_cfg), GPIO_FUNC(g->gpio_cfg),
+			       GPIO_DIR(g->gpio_cfg), GPIO_PULL(g->gpio_cfg),
+			       GPIO_DRVSTR(g->gpio_cfg));
+			if (!rc)
+				rc = tmp;
+		}
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(msm_gpios_disable);
+
+/* Locate the GPIO_OUT register for the given GPIO and return its address
+ * and the bit position of the gpio's bit within the register.
+ *
+ * This function is used by gpiomux-v1 in order to support output transitions.
+ */
+void msm_gpio_find_out(const unsigned gpio, void __iomem **out,
+	unsigned *offset)
+{
+	struct msm_gpio_chip *msm_chip = msm_gpio_chips;
+
+	while (gpio >= msm_chip->chip.base + msm_chip->chip.ngpio)
+		++msm_chip;
+
+	*out = msm_chip->regs.out;
+	*offset = gpio - msm_chip->chip.base;
+}
+
+static int __devinit msm_gpio_probe(struct platform_device *dev)
 {
 	int i, j = 0;
-
-	if (cpu_is_msm7x01()) {
-		msm_gpio_chips = msm_gpio_chips_msm7x01;
-		msm_gpio_count = ARRAY_SIZE(msm_gpio_chips_msm7x01);
-	} else if (cpu_is_msm7x30()) {
-		msm_gpio_chips = msm_gpio_chips_msm7x30;
-		msm_gpio_count = ARRAY_SIZE(msm_gpio_chips_msm7x30);
-	} else if (cpu_is_qsd8x50()) {
-		msm_gpio_chips = msm_gpio_chips_qsd8x50;
-		msm_gpio_count = ARRAY_SIZE(msm_gpio_chips_qsd8x50);
-	} else {
-		return 0;
-	}
+	int grp_irq;
 
 	for (i = FIRST_GPIO_IRQ; i < FIRST_GPIO_IRQ + NR_GPIO_IRQS; i++) {
 		if (i - FIRST_GPIO_IRQ >=
@@ -620,17 +803,35 @@
 		set_irq_flags(i, IRQF_VALID);
 	}
 
-	for (i = 0; i < msm_gpio_count; i++) {
+	for (i = 0; i < dev->num_resources; i++) {
+		grp_irq = platform_get_irq(dev, i);
+		if (grp_irq < 0)
+			return -ENXIO;
+
+		irq_set_chained_handler(grp_irq, msm_gpio_irq_handler);
+		irq_set_irq_wake(grp_irq, (i + 1));
+	}
+
+	for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) {
 		spin_lock_init(&msm_gpio_chips[i].lock);
-		writel(0, msm_gpio_chips[i].regs.int_en);
+		__raw_writel(0, msm_gpio_chips[i].regs.int_en);
 		gpiochip_add(&msm_gpio_chips[i].chip);
 	}
 
-	irq_set_chained_handler(INT_GPIO_GROUP1, msm_gpio_irq_handler);
-	irq_set_chained_handler(INT_GPIO_GROUP2, msm_gpio_irq_handler);
-	irq_set_irq_wake(INT_GPIO_GROUP1, 1);
-	irq_set_irq_wake(INT_GPIO_GROUP2, 2);
+	mb();
 	return 0;
 }
 
-postcore_initcall(msm_init_gpio);
+static struct platform_driver msm_gpio_driver = {
+	.probe = msm_gpio_probe,
+	.driver = {
+		.name = "msmgpio",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init msm_gpio_init(void)
+{
+	return platform_driver_register(&msm_gpio_driver);
+}
+postcore_initcall(msm_gpio_init);
diff --git a/drivers/gpio/gpio-msm-v2.c b/drivers/gpio/gpio-msm-v2.c
index 5cb1227..ad436e0 100644
--- a/drivers/gpio/gpio-msm-v2.c
+++ b/drivers/gpio/gpio-msm-v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -9,50 +9,121 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
  */
-#define pr_fmt(fmt) "%s: " fmt, __func__
-
 #include <linux/bitmap.h>
 #include <linux/bitops.h>
 #include <linux/gpio.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
-#include <linux/io.h>
 #include <linux/irq.h>
+#include <linux/io.h>
 #include <linux/module.h>
-#include <linux/platform_device.h>
 #include <linux/spinlock.h>
+#include <linux/syscore_ops.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
+#include <linux/err.h>
 
 #include <asm/mach/irq.h>
 
-#include <mach/msm_gpiomux.h>
 #include <mach/msm_iomap.h>
+#include <mach/gpiomux.h>
+#include <mach/mpm.h>
 
 /* Bits of interest in the GPIO_IN_OUT register.
  */
 enum {
-	GPIO_IN  = 0,
-	GPIO_OUT = 1
+	GPIO_IN_BIT  = 0,
+	GPIO_OUT_BIT = 1
 };
 
 /* Bits of interest in the GPIO_INTR_STATUS register.
  */
 enum {
-	INTR_STATUS = 0,
+	INTR_STATUS_BIT = 0,
 };
 
 /* Bits of interest in the GPIO_CFG register.
  */
 enum {
-	GPIO_OE = 9,
+	GPIO_OE_BIT = 9,
 };
 
 /* Bits of interest in the GPIO_INTR_CFG register.
+ */
+enum {
+	INTR_ENABLE_BIT        = 0,
+	INTR_POL_CTL_BIT       = 1,
+	INTR_DECT_CTL_BIT      = 2,
+	INTR_RAW_STATUS_EN_BIT = 3,
+};
+
+/* Codes of interest in GPIO_INTR_CFG_SU.
+ */
+enum {
+	TARGET_PROC_SCORPION = 4,
+	TARGET_PROC_NONE     = 7,
+};
+
+/*
+ * There is no 'DC_POLARITY_LO' because the GIC is incapable
+ * of asserting on falling edge or level-low conditions.  Even though
+ * the registers allow for low-polarity inputs, the case can never arise.
+ */
+enum {
+	DC_POLARITY_HI	= BIT(11),
+	DC_IRQ_ENABLE	= BIT(3),
+};
+
+enum msm_tlmm_register {
+	SDC4_HDRV_PULL_CTL = 0x20a0,
+	SDC3_HDRV_PULL_CTL = 0x20a4,
+	SDC1_HDRV_PULL_CTL = 0x20a0,
+};
+
+struct tlmm_field_cfg {
+	enum msm_tlmm_register reg;
+	u8                     off;
+};
+
+static const struct tlmm_field_cfg tlmm_hdrv_cfgs[] = {
+	{SDC4_HDRV_PULL_CTL, 6}, /* TLMM_HDRV_SDC4_CLK  */
+	{SDC4_HDRV_PULL_CTL, 3}, /* TLMM_HDRV_SDC4_CMD  */
+	{SDC4_HDRV_PULL_CTL, 0}, /* TLMM_HDRV_SDC4_DATA */
+	{SDC3_HDRV_PULL_CTL, 6}, /* TLMM_HDRV_SDC3_CLK  */
+	{SDC3_HDRV_PULL_CTL, 3}, /* TLMM_HDRV_SDC3_CMD  */
+	{SDC3_HDRV_PULL_CTL, 0}, /* TLMM_HDRV_SDC3_DATA */
+	{SDC1_HDRV_PULL_CTL, 6}, /* TLMM_HDRV_SDC1_CLK  */
+	{SDC1_HDRV_PULL_CTL, 3}, /* TLMM_HDRV_SDC1_CMD  */
+	{SDC1_HDRV_PULL_CTL, 0}, /* TLMM_HDRV_SDC1_DATA */
+};
+
+static const struct tlmm_field_cfg tlmm_pull_cfgs[] = {
+	{SDC4_HDRV_PULL_CTL, 11}, /* TLMM_PULL_SDC4_CMD  */
+	{SDC4_HDRV_PULL_CTL, 9},  /* TLMM_PULL_SDC4_DATA */
+	{SDC3_HDRV_PULL_CTL, 14}, /* TLMM_PULL_SDC3_CLK  */
+	{SDC3_HDRV_PULL_CTL, 11}, /* TLMM_PULL_SDC3_CMD  */
+	{SDC3_HDRV_PULL_CTL, 9},  /* TLMM_PULL_SDC3_DATA */
+	{SDC1_HDRV_PULL_CTL, 13}, /* TLMM_PULL_SDC1_CLK  */
+	{SDC1_HDRV_PULL_CTL, 11}, /* TLMM_PULL_SDC1_CMD  */
+	{SDC1_HDRV_PULL_CTL, 9},  /* TLMM_PULL_SDC1_DATA */
+};
+
+/*
+ * Supported arch specific irq extension.
+ * Default make them NULL.
+ */
+struct irq_chip msm_gpio_irq_extn = {
+	.irq_eoi	= NULL,
+	.irq_mask	= NULL,
+	.irq_unmask	= NULL,
+	.irq_retrigger	= NULL,
+	.irq_set_type	= NULL,
+	.irq_set_wake	= NULL,
+	.irq_disable	= NULL,
+};
+
+/*
  * When a GPIO triggers, two separate decisions are made, controlled
  * by two separate flags.
  *
@@ -64,22 +135,13 @@
  * If INTR_ENABLE is set and INTR_RAW_STATUS_EN is NOT set, an interrupt
  * can be triggered but the status register will not reflect it.
  */
-enum {
-	INTR_ENABLE        = 0,
-	INTR_POL_CTL       = 1,
-	INTR_DECT_CTL      = 2,
-	INTR_RAW_STATUS_EN = 3,
-};
-
-/* Codes of interest in GPIO_INTR_CFG_SU.
- */
-enum {
-	TARGET_PROC_SCORPION = 4,
-	TARGET_PROC_NONE     = 7,
-};
-
+#define INTR_RAW_STATUS_EN BIT(INTR_RAW_STATUS_EN_BIT)
+#define INTR_ENABLE        BIT(INTR_ENABLE_BIT)
+#define INTR_DECT_CTL_EDGE BIT(INTR_DECT_CTL_BIT)
+#define INTR_POL_CTL_HI    BIT(INTR_POL_CTL_BIT)
 
 #define GPIO_INTR_CFG_SU(gpio)    (MSM_TLMM_BASE + 0x0400 + (0x04 * (gpio)))
+#define DIR_CONN_INTR_CFG_SU(irq) (MSM_TLMM_BASE + 0x0700 + (0x04 * (irq)))
 #define GPIO_CONFIG(gpio)         (MSM_TLMM_BASE + 0x1000 + (0x10 * (gpio)))
 #define GPIO_IN_OUT(gpio)         (MSM_TLMM_BASE + 0x1004 + (0x10 * (gpio)))
 #define GPIO_INTR_CFG(gpio)       (MSM_TLMM_BASE + 0x1008 + (0x10 * (gpio)))
@@ -90,7 +152,7 @@
  *
  * @enabled_irqs: a bitmap used to optimize the summary-irq handler.  By
  * keeping track of which gpios are unmasked as irq sources, we avoid
- * having to do readl calls on hundreds of iomapped registers each time
+ * having to do __raw_readl calls on hundreds of iomapped registers each time
  * the summary interrupt fires in order to locate the active interrupts.
  *
  * @wake_irqs: a bitmap for tracking which interrupt lines are enabled
@@ -103,9 +165,10 @@
  */
 struct msm_gpio_dev {
 	struct gpio_chip gpio_chip;
-	DECLARE_BITMAP(enabled_irqs, NR_GPIO_IRQS);
-	DECLARE_BITMAP(wake_irqs, NR_GPIO_IRQS);
-	DECLARE_BITMAP(dual_edge_irqs, NR_GPIO_IRQS);
+	DECLARE_BITMAP(enabled_irqs, NR_MSM_GPIOS);
+	DECLARE_BITMAP(wake_irqs, NR_MSM_GPIOS);
+	DECLARE_BITMAP(dual_edge_irqs, NR_MSM_GPIOS);
+	struct irq_domain domain;
 };
 
 static DEFINE_SPINLOCK(tlmm_lock);
@@ -117,22 +180,26 @@
 
 static inline void set_gpio_bits(unsigned n, void __iomem *reg)
 {
-	writel(readl(reg) | n, reg);
+	__raw_writel(__raw_readl(reg) | n, reg);
 }
 
-static inline void clear_gpio_bits(unsigned n, void __iomem *reg)
+static inline void clr_gpio_bits(unsigned n, void __iomem *reg)
 {
-	writel(readl(reg) & ~n, reg);
+	__raw_writel(__raw_readl(reg) & ~n, reg);
 }
 
 static int msm_gpio_get(struct gpio_chip *chip, unsigned offset)
 {
-	return readl(GPIO_IN_OUT(offset)) & BIT(GPIO_IN);
+	int rc;
+	rc = __raw_readl(GPIO_IN_OUT(offset)) & BIT(GPIO_IN_BIT);
+	mb();
+	return rc;
 }
 
 static void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
 {
-	writel(val ? BIT(GPIO_OUT) : 0, GPIO_IN_OUT(offset));
+	__raw_writel(val ? BIT(GPIO_OUT_BIT) : 0, GPIO_IN_OUT(offset));
+	mb();
 }
 
 static int msm_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
@@ -140,7 +207,8 @@
 	unsigned long irq_flags;
 
 	spin_lock_irqsave(&tlmm_lock, irq_flags);
-	clear_gpio_bits(BIT(GPIO_OE), GPIO_CONFIG(offset));
+	clr_gpio_bits(BIT(GPIO_OE_BIT), GPIO_CONFIG(offset));
+	mb();
 	spin_unlock_irqrestore(&tlmm_lock, irq_flags);
 	return 0;
 }
@@ -153,11 +221,38 @@
 
 	spin_lock_irqsave(&tlmm_lock, irq_flags);
 	msm_gpio_set(chip, offset, val);
-	set_gpio_bits(BIT(GPIO_OE), GPIO_CONFIG(offset));
+	set_gpio_bits(BIT(GPIO_OE_BIT), GPIO_CONFIG(offset));
+	mb();
 	spin_unlock_irqrestore(&tlmm_lock, irq_flags);
 	return 0;
 }
 
+#ifdef CONFIG_OF
+static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	struct msm_gpio_dev *g_dev = to_msm_gpio_dev(chip);
+	struct irq_domain *domain = &g_dev->domain;
+	return domain->irq_base + (offset - chip->base);
+}
+
+static inline int msm_irq_to_gpio(struct gpio_chip *chip, unsigned irq)
+{
+	struct msm_gpio_dev *g_dev = to_msm_gpio_dev(chip);
+	struct irq_domain *domain = &g_dev->domain;
+	return irq - domain->irq_base;
+}
+#else
+static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	return MSM_GPIO_TO_INT(offset - chip->base);
+}
+
+static inline int msm_irq_to_gpio(struct gpio_chip *chip, unsigned irq)
+{
+	return irq - MSM_GPIO_TO_INT(chip->base);
+}
+#endif
+
 static int msm_gpio_request(struct gpio_chip *chip, unsigned offset)
 {
 	return msm_gpiomux_get(chip->base + offset);
@@ -168,20 +263,11 @@
 	msm_gpiomux_put(chip->base + offset);
 }
 
-static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
-{
-	return MSM_GPIO_TO_INT(chip->base + offset);
-}
-
-static inline int msm_irq_to_gpio(struct gpio_chip *chip, unsigned irq)
-{
-	return irq - MSM_GPIO_TO_INT(chip->base);
-}
-
 static struct msm_gpio_dev msm_gpio = {
 	.gpio_chip = {
+		.label		  = "msmgpio",
 		.base             = 0,
-		.ngpio            = NR_GPIO_IRQS,
+		.ngpio            = NR_MSM_GPIOS,
 		.direction_input  = msm_gpio_direction_input,
 		.direction_output = msm_gpio_direction_output,
 		.get              = msm_gpio_get,
@@ -192,6 +278,18 @@
 	},
 };
 
+static void switch_mpm_config(struct irq_data *d, unsigned val)
+{
+	/* switch the configuration in the mpm as well */
+	if (!msm_gpio_irq_extn.irq_set_type)
+		return;
+
+	if (val)
+		msm_gpio_irq_extn.irq_set_type(d, IRQF_TRIGGER_FALLING);
+	else
+		msm_gpio_irq_extn.irq_set_type(d, IRQF_TRIGGER_RISING);
+}
+
 /* For dual-edge interrupts in software, since the hardware has no
  * such support:
  *
@@ -212,34 +310,44 @@
  *
  * Algorithm comes from Google's msmgpio driver, see mach-msm/gpio.c.
  */
-static void msm_gpio_update_dual_edge_pos(unsigned gpio)
+static void msm_gpio_update_dual_edge_pos(struct irq_data *d, unsigned gpio)
 {
 	int loop_limit = 100;
 	unsigned val, val2, intstat;
 
 	do {
-		val = readl(GPIO_IN_OUT(gpio)) & BIT(GPIO_IN);
+		val = __raw_readl(GPIO_IN_OUT(gpio)) & BIT(GPIO_IN_BIT);
 		if (val)
-			clear_gpio_bits(BIT(INTR_POL_CTL), GPIO_INTR_CFG(gpio));
+			clr_gpio_bits(INTR_POL_CTL_HI, GPIO_INTR_CFG(gpio));
 		else
-			set_gpio_bits(BIT(INTR_POL_CTL), GPIO_INTR_CFG(gpio));
-		val2 = readl(GPIO_IN_OUT(gpio)) & BIT(GPIO_IN);
-		intstat = readl(GPIO_INTR_STATUS(gpio)) & BIT(INTR_STATUS);
-		if (intstat || val == val2)
+			set_gpio_bits(INTR_POL_CTL_HI, GPIO_INTR_CFG(gpio));
+		val2 = __raw_readl(GPIO_IN_OUT(gpio)) & BIT(GPIO_IN_BIT);
+		intstat = __raw_readl(GPIO_INTR_STATUS(gpio)) &
+					BIT(INTR_STATUS_BIT);
+		if (intstat || val == val2) {
+			switch_mpm_config(d, val);
 			return;
+		}
 	} while (loop_limit-- > 0);
-	pr_err("dual-edge irq failed to stabilize, "
+	pr_err("%s: dual-edge irq failed to stabilize, "
 	       "interrupts dropped. %#08x != %#08x\n",
-	       val, val2);
+	       __func__, val, val2);
 }
 
 static void msm_gpio_irq_ack(struct irq_data *d)
 {
 	int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
 
-	writel(BIT(INTR_STATUS), GPIO_INTR_STATUS(gpio));
+	__raw_writel(BIT(INTR_STATUS_BIT), GPIO_INTR_STATUS(gpio));
 	if (test_bit(gpio, msm_gpio.dual_edge_irqs))
-		msm_gpio_update_dual_edge_pos(gpio);
+		msm_gpio_update_dual_edge_pos(d, gpio);
+	mb();
+}
+
+static void __msm_gpio_irq_mask(unsigned int gpio)
+{
+	__raw_writel(TARGET_PROC_NONE, GPIO_INTR_CFG_SU(gpio));
+	clr_gpio_bits(INTR_RAW_STATUS_EN | INTR_ENABLE, GPIO_INTR_CFG(gpio));
 }
 
 static void msm_gpio_irq_mask(struct irq_data *d)
@@ -248,10 +356,20 @@
 	unsigned long irq_flags;
 
 	spin_lock_irqsave(&tlmm_lock, irq_flags);
-	writel(TARGET_PROC_NONE, GPIO_INTR_CFG_SU(gpio));
-	clear_gpio_bits(INTR_RAW_STATUS_EN | INTR_ENABLE, GPIO_INTR_CFG(gpio));
+	__msm_gpio_irq_mask(gpio);
 	__clear_bit(gpio, msm_gpio.enabled_irqs);
+	mb();
 	spin_unlock_irqrestore(&tlmm_lock, irq_flags);
+
+	if (msm_gpio_irq_extn.irq_mask)
+		msm_gpio_irq_extn.irq_mask(d);
+
+}
+
+static void __msm_gpio_irq_unmask(unsigned int gpio)
+{
+	set_gpio_bits(INTR_RAW_STATUS_EN | INTR_ENABLE, GPIO_INTR_CFG(gpio));
+	__raw_writel(TARGET_PROC_SCORPION, GPIO_INTR_CFG_SU(gpio));
 }
 
 static void msm_gpio_irq_unmask(struct irq_data *d)
@@ -261,9 +379,18 @@
 
 	spin_lock_irqsave(&tlmm_lock, irq_flags);
 	__set_bit(gpio, msm_gpio.enabled_irqs);
-	set_gpio_bits(INTR_RAW_STATUS_EN | INTR_ENABLE, GPIO_INTR_CFG(gpio));
-	writel(TARGET_PROC_SCORPION, GPIO_INTR_CFG_SU(gpio));
+	__msm_gpio_irq_unmask(gpio);
+	mb();
 	spin_unlock_irqrestore(&tlmm_lock, irq_flags);
+
+	if (msm_gpio_irq_extn.irq_mask)
+		msm_gpio_irq_extn.irq_unmask(d);
+}
+
+static void msm_gpio_irq_disable(struct irq_data *d)
+{
+	if (msm_gpio_irq_extn.irq_disable)
+		msm_gpio_irq_extn.irq_disable(d);
 }
 
 static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type)
@@ -274,33 +401,37 @@
 
 	spin_lock_irqsave(&tlmm_lock, irq_flags);
 
-	bits = readl(GPIO_INTR_CFG(gpio));
+	bits = __raw_readl(GPIO_INTR_CFG(gpio));
 
 	if (flow_type & IRQ_TYPE_EDGE_BOTH) {
-		bits |= BIT(INTR_DECT_CTL);
+		bits |= INTR_DECT_CTL_EDGE;
 		__irq_set_handler_locked(d->irq, handle_edge_irq);
 		if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
 			__set_bit(gpio, msm_gpio.dual_edge_irqs);
 		else
 			__clear_bit(gpio, msm_gpio.dual_edge_irqs);
 	} else {
-		bits &= ~BIT(INTR_DECT_CTL);
+		bits &= ~INTR_DECT_CTL_EDGE;
 		__irq_set_handler_locked(d->irq, handle_level_irq);
 		__clear_bit(gpio, msm_gpio.dual_edge_irqs);
 	}
 
 	if (flow_type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_HIGH))
-		bits |= BIT(INTR_POL_CTL);
+		bits |= INTR_POL_CTL_HI;
 	else
-		bits &= ~BIT(INTR_POL_CTL);
+		bits &= ~INTR_POL_CTL_HI;
 
-	writel(bits, GPIO_INTR_CFG(gpio));
+	__raw_writel(bits, GPIO_INTR_CFG(gpio));
 
 	if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
-		msm_gpio_update_dual_edge_pos(gpio);
+		msm_gpio_update_dual_edge_pos(d, gpio);
 
+	mb();
 	spin_unlock_irqrestore(&tlmm_lock, irq_flags);
 
+	if (msm_gpio_irq_extn.irq_set_type)
+		msm_gpio_irq_extn.irq_set_type(d, flow_type);
+
 	return 0;
 }
 
@@ -310,22 +441,24 @@
  * which have been set as summary IRQ lines and which are triggered,
  * and to call their interrupt handlers.
  */
-static void msm_summary_irq_handler(unsigned int irq, struct irq_desc *desc)
+static irqreturn_t msm_summary_irq_handler(int irq, void *data)
 {
 	unsigned long i;
+	struct irq_desc *desc = irq_to_desc(irq);
 	struct irq_chip *chip = irq_desc_get_chip(desc);
 
 	chained_irq_enter(chip, desc);
 
-	for (i = find_first_bit(msm_gpio.enabled_irqs, NR_GPIO_IRQS);
-	     i < NR_GPIO_IRQS;
-	     i = find_next_bit(msm_gpio.enabled_irqs, NR_GPIO_IRQS, i + 1)) {
-		if (readl(GPIO_INTR_STATUS(i)) & BIT(INTR_STATUS))
+	for (i = find_first_bit(msm_gpio.enabled_irqs, NR_MSM_GPIOS);
+	     i < NR_MSM_GPIOS;
+	     i = find_next_bit(msm_gpio.enabled_irqs, NR_MSM_GPIOS, i + 1)) {
+		if (__raw_readl(GPIO_INTR_STATUS(i)) & BIT(INTR_STATUS_BIT))
 			generic_handle_irq(msm_gpio_to_irq(&msm_gpio.gpio_chip,
 							   i));
 	}
 
 	chained_irq_exit(chip, desc);
+	return IRQ_HANDLED;
 }
 
 static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
@@ -333,15 +466,18 @@
 	int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
 
 	if (on) {
-		if (bitmap_empty(msm_gpio.wake_irqs, NR_GPIO_IRQS))
-			irq_set_irq_wake(TLMM_SCSS_SUMMARY_IRQ, 1);
+		if (bitmap_empty(msm_gpio.wake_irqs, NR_MSM_GPIOS))
+			irq_set_irq_wake(TLMM_MSM_SUMMARY_IRQ, 1);
 		set_bit(gpio, msm_gpio.wake_irqs);
 	} else {
 		clear_bit(gpio, msm_gpio.wake_irqs);
-		if (bitmap_empty(msm_gpio.wake_irqs, NR_GPIO_IRQS))
-			irq_set_irq_wake(TLMM_SCSS_SUMMARY_IRQ, 0);
+		if (bitmap_empty(msm_gpio.wake_irqs, NR_MSM_GPIOS))
+			irq_set_irq_wake(TLMM_MSM_SUMMARY_IRQ, 0);
 	}
 
+	if (msm_gpio_irq_extn.irq_set_wake)
+		msm_gpio_irq_extn.irq_set_wake(d, on);
+
 	return 0;
 }
 
@@ -352,82 +488,270 @@
 	.irq_ack	= msm_gpio_irq_ack,
 	.irq_set_type	= msm_gpio_irq_set_type,
 	.irq_set_wake	= msm_gpio_irq_set_wake,
+	.irq_disable	= msm_gpio_irq_disable,
 };
 
-static int __devinit msm_gpio_probe(struct platform_device *dev)
+/*
+ * This lock class tells lockdep that GPIO irqs are in a different
+ * category than their parent, so it won't report false recursion.
+ */
+static struct lock_class_key msm_gpio_lock_class;
+
+static int __devinit msm_gpio_probe(void)
 {
 	int i, irq, ret;
 
-	bitmap_zero(msm_gpio.enabled_irqs, NR_GPIO_IRQS);
-	bitmap_zero(msm_gpio.wake_irqs, NR_GPIO_IRQS);
-	bitmap_zero(msm_gpio.dual_edge_irqs, NR_GPIO_IRQS);
-	msm_gpio.gpio_chip.label = dev->name;
+	spin_lock_init(&tlmm_lock);
+	bitmap_zero(msm_gpio.enabled_irqs, NR_MSM_GPIOS);
+	bitmap_zero(msm_gpio.wake_irqs, NR_MSM_GPIOS);
+	bitmap_zero(msm_gpio.dual_edge_irqs, NR_MSM_GPIOS);
 	ret = gpiochip_add(&msm_gpio.gpio_chip);
 	if (ret < 0)
 		return ret;
 
 	for (i = 0; i < msm_gpio.gpio_chip.ngpio; ++i) {
 		irq = msm_gpio_to_irq(&msm_gpio.gpio_chip, i);
+		irq_set_lockdep_class(irq, &msm_gpio_lock_class);
 		irq_set_chip_and_handler(irq, &msm_gpio_irq_chip,
 					 handle_level_irq);
 		set_irq_flags(irq, IRQF_VALID);
 	}
 
-	irq_set_chained_handler(TLMM_SCSS_SUMMARY_IRQ,
-				msm_summary_irq_handler);
+	ret = request_irq(TLMM_MSM_SUMMARY_IRQ, msm_summary_irq_handler,
+			IRQF_TRIGGER_HIGH, "msmgpio", NULL);
+	if (ret) {
+		pr_err("Request_irq failed for TLMM_MSM_SUMMARY_IRQ - %d\n",
+				ret);
+		return ret;
+	}
 	return 0;
 }
 
-static int __devexit msm_gpio_remove(struct platform_device *dev)
+static int __devexit msm_gpio_remove(void)
 {
 	int ret = gpiochip_remove(&msm_gpio.gpio_chip);
 
 	if (ret < 0)
 		return ret;
 
-	irq_set_handler(TLMM_SCSS_SUMMARY_IRQ, NULL);
+	irq_set_handler(TLMM_MSM_SUMMARY_IRQ, NULL);
 
 	return 0;
 }
 
-static struct platform_driver msm_gpio_driver = {
-	.probe = msm_gpio_probe,
-	.remove = __devexit_p(msm_gpio_remove),
-	.driver = {
-		.name = "msmgpio",
-		.owner = THIS_MODULE,
-	},
-};
+#ifdef CONFIG_PM
+static int msm_gpio_suspend(void)
+{
+	unsigned long irq_flags;
+	unsigned long i;
 
-static struct platform_device msm_device_gpio = {
-	.name = "msmgpio",
-	.id   = -1,
+	spin_lock_irqsave(&tlmm_lock, irq_flags);
+	for_each_set_bit(i, msm_gpio.enabled_irqs, NR_MSM_GPIOS)
+			__msm_gpio_irq_mask(i);
+
+	for_each_set_bit(i, msm_gpio.wake_irqs, NR_MSM_GPIOS)
+		__msm_gpio_irq_unmask(i);
+	mb();
+	spin_unlock_irqrestore(&tlmm_lock, irq_flags);
+	return 0;
+}
+
+extern int msm_show_resume_irq_mask;
+
+void msm_gpio_show_resume_irq(void)
+{
+	unsigned long irq_flags;
+	int i, irq, intstat;
+
+	if (!msm_show_resume_irq_mask)
+		return;
+
+	spin_lock_irqsave(&tlmm_lock, irq_flags);
+	for_each_set_bit(i, msm_gpio.wake_irqs, NR_MSM_GPIOS) {
+		intstat = __raw_readl(GPIO_INTR_STATUS(i)) &
+					BIT(INTR_STATUS_BIT);
+		if (intstat) {
+			irq = msm_gpio_to_irq(&msm_gpio.gpio_chip, i);
+			pr_warning("%s: %d triggered\n",
+				__func__, irq);
+		}
+	}
+	spin_unlock_irqrestore(&tlmm_lock, irq_flags);
+}
+
+static void msm_gpio_resume(void)
+{
+	unsigned long irq_flags;
+	unsigned long i;
+
+	msm_gpio_show_resume_irq();
+
+	spin_lock_irqsave(&tlmm_lock, irq_flags);
+	for_each_set_bit(i, msm_gpio.wake_irqs, NR_MSM_GPIOS)
+		__msm_gpio_irq_mask(i);
+
+	for_each_set_bit(i, msm_gpio.enabled_irqs, NR_MSM_GPIOS)
+		__msm_gpio_irq_unmask(i);
+	mb();
+	spin_unlock_irqrestore(&tlmm_lock, irq_flags);
+}
+#else
+#define msm_gpio_suspend NULL
+#define msm_gpio_resume NULL
+#endif
+
+static struct syscore_ops msm_gpio_syscore_ops = {
+	.suspend = msm_gpio_suspend,
+	.resume = msm_gpio_resume,
 };
 
 static int __init msm_gpio_init(void)
 {
-	int rc;
-
-	rc = platform_driver_register(&msm_gpio_driver);
-	if (!rc) {
-		rc = platform_device_register(&msm_device_gpio);
-		if (rc)
-			platform_driver_unregister(&msm_gpio_driver);
-	}
-
-	return rc;
+	msm_gpio_probe();
+	register_syscore_ops(&msm_gpio_syscore_ops);
+	return 0;
 }
 
 static void __exit msm_gpio_exit(void)
 {
-	platform_device_unregister(&msm_device_gpio);
-	platform_driver_unregister(&msm_gpio_driver);
+	unregister_syscore_ops(&msm_gpio_syscore_ops);
+	msm_gpio_remove();
 }
 
 postcore_initcall(msm_gpio_init);
 module_exit(msm_gpio_exit);
 
+static void msm_tlmm_set_field(const struct tlmm_field_cfg *configs,
+			       unsigned id, unsigned width, unsigned val)
+{
+	unsigned long irqflags;
+	u32 mask = (1 << width) - 1;
+	u32 __iomem *reg = MSM_TLMM_BASE + configs[id].reg;
+	u32 reg_val;
+
+	spin_lock_irqsave(&tlmm_lock, irqflags);
+	reg_val = __raw_readl(reg);
+	reg_val &= ~(mask << configs[id].off);
+	reg_val |= (val & mask) << configs[id].off;
+	__raw_writel(reg_val, reg);
+	mb();
+	spin_unlock_irqrestore(&tlmm_lock, irqflags);
+}
+
+void msm_tlmm_set_hdrive(enum msm_tlmm_hdrive_tgt tgt, int drv_str)
+{
+	msm_tlmm_set_field(tlmm_hdrv_cfgs, tgt, 3, drv_str);
+}
+EXPORT_SYMBOL(msm_tlmm_set_hdrive);
+
+void msm_tlmm_set_pull(enum msm_tlmm_pull_tgt tgt, int pull)
+{
+	msm_tlmm_set_field(tlmm_pull_cfgs, tgt, 2, pull);
+}
+EXPORT_SYMBOL(msm_tlmm_set_pull);
+
+int gpio_tlmm_config(unsigned config, unsigned disable)
+{
+	uint32_t flags;
+	unsigned gpio = GPIO_PIN(config);
+
+	if (gpio > NR_MSM_GPIOS)
+		return -EINVAL;
+
+	flags = ((GPIO_DIR(config) << 9) & (0x1 << 9)) |
+		((GPIO_DRVSTR(config) << 6) & (0x7 << 6)) |
+		((GPIO_FUNC(config) << 2) & (0xf << 2)) |
+		((GPIO_PULL(config) & 0x3));
+	__raw_writel(flags, GPIO_CONFIG(gpio));
+	mb();
+
+	return 0;
+}
+EXPORT_SYMBOL(gpio_tlmm_config);
+
+int msm_gpio_install_direct_irq(unsigned gpio, unsigned irq,
+					unsigned int input_polarity)
+{
+	unsigned long irq_flags;
+	uint32_t bits;
+
+	if (gpio >= NR_MSM_GPIOS || irq >= NR_TLMM_MSM_DIR_CONN_IRQ)
+		return -EINVAL;
+
+	spin_lock_irqsave(&tlmm_lock, irq_flags);
+
+	__raw_writel(__raw_readl(GPIO_CONFIG(gpio)) | BIT(GPIO_OE_BIT),
+		GPIO_CONFIG(gpio));
+	__raw_writel(__raw_readl(GPIO_INTR_CFG(gpio)) &
+		~(INTR_RAW_STATUS_EN | INTR_ENABLE),
+		GPIO_INTR_CFG(gpio));
+	__raw_writel(DC_IRQ_ENABLE | TARGET_PROC_NONE,
+		GPIO_INTR_CFG_SU(gpio));
+
+	bits = TARGET_PROC_SCORPION | (gpio << 3);
+	if (input_polarity)
+		bits |= DC_POLARITY_HI;
+	__raw_writel(bits, DIR_CONN_INTR_CFG_SU(irq));
+
+	mb();
+	spin_unlock_irqrestore(&tlmm_lock, irq_flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(msm_gpio_install_direct_irq);
+
+#ifdef CONFIG_OF
+static int msm_gpio_domain_dt_translate(struct irq_domain *d,
+					struct device_node *controller,
+					const u32 *intspec,
+					unsigned int intsize,
+					unsigned long *out_hwirq,
+					unsigned int *out_type)
+{
+	if (d->of_node != controller)
+		return -EINVAL;
+	if (intsize != 2)
+		return -EINVAL;
+
+	/* hwirq value */
+	*out_hwirq = intspec[0];
+
+	/* irq flags */
+	*out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
+	return 0;
+}
+
+static struct irq_domain_ops msm_gpio_irq_domain_ops = {
+	.dt_translate = msm_gpio_domain_dt_translate,
+};
+
+int __init msm_gpio_of_init(struct device_node *node,
+			    struct device_node *parent)
+{
+	struct irq_domain *domain = &msm_gpio.domain;
+	int start;
+
+	start = irq_domain_find_free_range(0, NR_MSM_GPIOS);
+	domain->irq_base = irq_alloc_descs(start, 0, NR_MSM_GPIOS,
+							numa_node_id());
+	if (IS_ERR_VALUE(domain->irq_base)) {
+		WARN(1, "Cannot allocate irq_descs @ IRQ%d\n", start);
+		return domain->irq_base;
+	}
+
+	domain->irq_base = irq_domain_find_free_range(0, NR_MSM_GPIOS);
+	domain->nr_irq = NR_MSM_GPIOS;
+	domain->of_node = of_node_get(node);
+	domain->priv = &msm_gpio;
+	domain->ops = &msm_gpio_irq_domain_ops;
+	irq_domain_add(domain);
+	pr_debug("%s: irq_base = %u\n", __func__, domain->irq_base);
+
+	return 0;
+}
+#endif
+
 MODULE_AUTHOR("Gregory Bean <gbean@codeaurora.org>");
 MODULE_DESCRIPTION("Driver for Qualcomm MSM TLMMv2 SoC GPIOs");
 MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:msmgpio");
+MODULE_ALIAS("sysdev:msmgpio");
diff --git a/drivers/gpio/gpio-pm8xxx-rpc.c b/drivers/gpio/gpio-pm8xxx-rpc.c
new file mode 100644
index 0000000..1acc741
--- /dev/null
+++ b/drivers/gpio/gpio-pm8xxx-rpc.c
@@ -0,0 +1,241 @@
+/*
+ * Qualcomm PMIC8XXX GPIO driver based on RPC
+ *
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt)	"%s: " fmt, __func__
+
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/gpio-pm8xxx-rpc.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <mach/pmic.h>
+
+struct pm8xxx_gpio_rpc_chip {
+	struct list_head	link;
+	struct gpio_chip	gpio_chip;
+};
+
+static LIST_HEAD(pm8xxx_gpio_rpc_chips);
+static DEFINE_MUTEX(pm8xxx_gpio_chips_lock);
+
+static int pm8xxx_gpio_rpc_get(struct pm8xxx_gpio_rpc_chip *pm8xxx_gpio_chip,
+								unsigned gpio)
+{
+	int rc;
+
+	if (gpio >= pm8xxx_gpio_chip->gpio_chip.ngpio
+					|| pm8xxx_gpio_chip == NULL)
+		return -EINVAL;
+
+	rc =  pmic_gpio_get_value(gpio);
+
+	return rc;
+}
+
+static int pm8xxx_gpio_rpc_set(struct pm8xxx_gpio_rpc_chip *pm8xxx_gpio_chip,
+						 unsigned gpio, int value)
+{
+	int rc;
+
+	if (gpio >= pm8xxx_gpio_chip->gpio_chip.ngpio ||
+					pm8xxx_gpio_chip == NULL)
+		return -EINVAL;
+
+	rc = pmic_gpio_set_value(gpio, value);
+
+	return rc;
+}
+
+static int pm8xxx_gpio_rpc_set_direction(struct pm8xxx_gpio_rpc_chip
+			*pm8xxx_gpio_chip, unsigned gpio, int direction)
+{
+	int rc = 0;
+
+	if (!direction || pm8xxx_gpio_chip == NULL)
+		return -EINVAL;
+
+	if (direction ==  PM_GPIO_DIR_IN)
+		rc = pmic_gpio_direction_input(gpio);
+	else if (direction == PM_GPIO_DIR_OUT)
+		rc = pmic_gpio_direction_output(gpio);
+
+	return rc;
+}
+
+static int pm8xxx_gpio_rpc_read(struct gpio_chip *gpio_chip, unsigned offset)
+{
+	struct pm8xxx_gpio_rpc_chip *pm8xxx_gpio_chip =
+					dev_get_drvdata(gpio_chip->dev);
+
+	return pm8xxx_gpio_rpc_get(pm8xxx_gpio_chip, offset);
+}
+
+static void pm8xxx_gpio_rpc_write(struct gpio_chip *gpio_chip,
+						unsigned offset, int val)
+{
+	struct pm8xxx_gpio_rpc_chip *pm8xxx_gpio_chip =
+					dev_get_drvdata(gpio_chip->dev);
+
+	pm8xxx_gpio_rpc_set(pm8xxx_gpio_chip, offset, !!val);
+}
+
+static int pm8xxx_gpio_rpc_direction_input(struct gpio_chip *gpio_chip,
+							unsigned offset)
+{
+	struct pm8xxx_gpio_rpc_chip *pm8xxx_gpio_chip =
+					dev_get_drvdata(gpio_chip->dev);
+
+	return pm8xxx_gpio_rpc_set_direction(pm8xxx_gpio_chip, offset,
+							PM_GPIO_DIR_IN);
+}
+
+static int pm8xxx_gpio_rpc_direction_output(struct gpio_chip *gpio_chip,
+						unsigned offset, int val)
+{
+	int ret = 0;
+
+	struct pm8xxx_gpio_rpc_chip *pm8xxx_gpio_chip =
+					dev_get_drvdata(gpio_chip->dev);
+
+	ret = pm8xxx_gpio_rpc_set_direction(pm8xxx_gpio_chip, offset,
+							PM_GPIO_DIR_OUT);
+	if (!ret)
+		ret = pm8xxx_gpio_rpc_set(pm8xxx_gpio_chip, offset, !!val);
+
+	return ret;
+}
+
+static void pm8xxx_gpio_rpc_dbg_show(struct seq_file *s, struct gpio_chip
+								*gpio_chip)
+{
+	struct pm8xxx_gpio_rpc_chip *pmxx_gpio_chip =
+					dev_get_drvdata(gpio_chip->dev);
+	u8 state, mode;
+	const char *label;
+	int i;
+
+	for (i = 0; i < gpio_chip->ngpio; i++) {
+		label = gpiochip_is_requested(gpio_chip, i);
+		state = pm8xxx_gpio_rpc_get(pmxx_gpio_chip, i);
+		mode =  pmic_gpio_get_direction(i);
+		seq_printf(s, "gpio-%-3d (%-12.12s) %s %s",
+				gpio_chip->base + i,
+				label ? label : " ", mode ? "out" : "in",
+				state ? "hi" : "lo");
+		seq_printf(s, "\n");
+	}
+}
+
+static int __devinit pm8xxx_gpio_rpc_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct pm8xxx_gpio_rpc_chip *pm8xxx_gpio_chip;
+	const struct pm8xxx_gpio_rpc_platform_data *pdata =
+					pdev->dev.platform_data;
+
+	if (!pdata) {
+		pr_err("missing platform data\n");
+		return -EINVAL;
+	}
+
+	pm8xxx_gpio_chip = kzalloc(sizeof(struct pm8xxx_gpio_rpc_chip),
+								GFP_KERNEL);
+	if (!pm8xxx_gpio_chip) {
+		pr_err("Cannot allocate pm8xxx_gpio_chip\n");
+		return -ENOMEM;
+	}
+
+	pm8xxx_gpio_chip->gpio_chip.label = "pm8xxx-gpio-rpc";
+	pm8xxx_gpio_chip->gpio_chip.direction_input	=
+					pm8xxx_gpio_rpc_direction_input;
+	pm8xxx_gpio_chip->gpio_chip.direction_output	=
+					pm8xxx_gpio_rpc_direction_output;
+	pm8xxx_gpio_chip->gpio_chip.get		= pm8xxx_gpio_rpc_read;
+	pm8xxx_gpio_chip->gpio_chip.set		= pm8xxx_gpio_rpc_write;
+	pm8xxx_gpio_chip->gpio_chip.dbg_show	= pm8xxx_gpio_rpc_dbg_show;
+	pm8xxx_gpio_chip->gpio_chip.ngpio	= pdata->ngpios;
+	pm8xxx_gpio_chip->gpio_chip.can_sleep	= 1;
+	pm8xxx_gpio_chip->gpio_chip.dev		= &pdev->dev;
+	pm8xxx_gpio_chip->gpio_chip.base	= pdata->gpio_base;
+
+	mutex_lock(&pm8xxx_gpio_chips_lock);
+	list_add(&pm8xxx_gpio_chip->link, &pm8xxx_gpio_rpc_chips);
+	mutex_unlock(&pm8xxx_gpio_chips_lock);
+	platform_set_drvdata(pdev, pm8xxx_gpio_chip);
+
+	ret = gpiochip_add(&pm8xxx_gpio_chip->gpio_chip);
+	if (ret) {
+		pr_err("gpiochip_add failed ret = %d\n", ret);
+		goto reset_drvdata;
+	}
+
+	pr_info("OK: base=%d, ngpio=%d\n", pm8xxx_gpio_chip->gpio_chip.base,
+		pm8xxx_gpio_chip->gpio_chip.ngpio);
+
+	return 0;
+
+reset_drvdata:
+	mutex_lock(&pm8xxx_gpio_chips_lock);
+	list_del(&pm8xxx_gpio_chip->link);
+	mutex_unlock(&pm8xxx_gpio_chips_lock);
+	platform_set_drvdata(pdev, NULL);
+	kfree(pm8xxx_gpio_chip);
+	mutex_destroy(&pm8xxx_gpio_chips_lock);
+	return ret;
+}
+
+static int __devexit pm8xxx_gpio_rpc_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_gpio_rpc_chip *pm8xxx_gpio_chip =
+						platform_get_drvdata(pdev);
+
+	mutex_lock(&pm8xxx_gpio_chips_lock);
+	list_del(&pm8xxx_gpio_chip->link);
+	mutex_unlock(&pm8xxx_gpio_chips_lock);
+	platform_set_drvdata(pdev, NULL);
+	if (gpiochip_remove(&pm8xxx_gpio_chip->gpio_chip))
+		pr_err("failed to remove gpio chip\n");
+	kfree(pm8xxx_gpio_chip);
+	mutex_destroy(&pm8xxx_gpio_chips_lock);
+	return 0;
+}
+
+static struct platform_driver pm8xxx_gpio_rpc_driver = {
+	.probe		= pm8xxx_gpio_rpc_probe,
+	.remove		= __devexit_p(pm8xxx_gpio_rpc_remove),
+	.driver		= {
+		.name	= PM8XXX_GPIO_DEV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pm8xxx_gpio_rpc_init(void)
+{
+	return platform_driver_register(&pm8xxx_gpio_rpc_driver);
+}
+postcore_initcall(pm8xxx_gpio_rpc_init);
+
+static void __exit pm8xxx_gpio_rpc_exit(void)
+{
+	platform_driver_unregister(&pm8xxx_gpio_rpc_driver);
+}
+module_exit(pm8xxx_gpio_rpc_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC GPIO driver based on RPC");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8XXX_GPIO_DEV_NAME);
diff --git a/drivers/gpio/gpio-sx150x.c b/drivers/gpio/gpio-sx150x.c
index a4f7353..93b94bd 100644
--- a/drivers/gpio/gpio-sx150x.c
+++ b/drivers/gpio/gpio-sx150x.c
@@ -8,11 +8,6 @@
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
  */
 #include <linux/gpio.h>
 #include <linux/i2c.h>
@@ -189,9 +184,9 @@
 	return err;
 }
 
-static void sx150x_set_oscio(struct sx150x_chip *chip, int val)
+static s32 sx150x_set_oscio(struct sx150x_chip *chip, int val)
 {
-	sx150x_i2c_write(chip->client,
+	return sx150x_i2c_write(chip->client,
 			chip->dev_cfg->reg_clock,
 			(val ? 0x1f : 0x10));
 }
@@ -286,11 +281,13 @@
 
 	chip = container_of(gc, struct sx150x_chip, gpio_chip);
 
-	if (!offset_is_oscio(chip, offset)) {
-		mutex_lock(&chip->lock);
+	mutex_lock(&chip->lock);
+	if (offset_is_oscio(chip, offset))
+		status = sx150x_set_oscio(chip, val);
+	else
 		status = sx150x_io_output(chip, offset, val);
-		mutex_unlock(&chip->lock);
-	}
+	mutex_unlock(&chip->lock);
+
 	return status;
 }
 
diff --git a/drivers/gpio/pm8xxx-gpio.c b/drivers/gpio/pm8xxx-gpio.c
new file mode 100644
index 0000000..cb874e8
--- /dev/null
+++ b/drivers/gpio/pm8xxx-gpio.c
@@ -0,0 +1,462 @@
+/*
+ * Qualcomm PMIC8XXX GPIO driver
+ *
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt)	"%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/gpio.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+/* GPIO registers */
+#define	SSBI_REG_ADDR_GPIO_BASE		0x150
+#define	SSBI_REG_ADDR_GPIO(n)		(SSBI_REG_ADDR_GPIO_BASE + n)
+
+/* GPIO */
+#define	PM_GPIO_BANK_MASK		0x70
+#define	PM_GPIO_BANK_SHIFT		4
+#define	PM_GPIO_WRITE			0x80
+
+/* Bank 0 */
+#define	PM_GPIO_VIN_MASK		0x0E
+#define	PM_GPIO_VIN_SHIFT		1
+#define	PM_GPIO_MODE_ENABLE		0x01
+
+/* Bank 1 */
+#define	PM_GPIO_MODE_MASK		0x0C
+#define	PM_GPIO_MODE_SHIFT		2
+#define	PM_GPIO_OUT_BUFFER		0x02
+#define	PM_GPIO_OUT_INVERT		0x01
+
+#define	PM_GPIO_MODE_OFF		3
+#define	PM_GPIO_MODE_OUTPUT		2
+#define	PM_GPIO_MODE_INPUT		0
+#define	PM_GPIO_MODE_BOTH		1
+
+/* Bank 2 */
+#define	PM_GPIO_PULL_MASK		0x0E
+#define	PM_GPIO_PULL_SHIFT		1
+
+/* Bank 3 */
+#define	PM_GPIO_OUT_STRENGTH_MASK	0x0C
+#define	PM_GPIO_OUT_STRENGTH_SHIFT	2
+#define PM_GPIO_PIN_ENABLE		0x00
+#define	PM_GPIO_PIN_DISABLE		0x01
+
+/* Bank 4 */
+#define	PM_GPIO_FUNC_MASK		0x0E
+#define	PM_GPIO_FUNC_SHIFT		1
+
+/* Bank 5 */
+#define	PM_GPIO_NON_INT_POL_INV	0x08
+#define PM_GPIO_BANKS		6
+
+struct pm_gpio_chip {
+	struct list_head	link;
+	struct gpio_chip	gpio_chip;
+	spinlock_t		pm_lock;
+	u8			*bank1;
+	int			irq_base;
+};
+
+static LIST_HEAD(pm_gpio_chips);
+static DEFINE_MUTEX(pm_gpio_chips_lock);
+
+static int pm_gpio_get(struct pm_gpio_chip *pm_gpio_chip, unsigned gpio)
+{
+	int	mode;
+
+	if (gpio >= pm_gpio_chip->gpio_chip.ngpio || pm_gpio_chip == NULL)
+		return -EINVAL;
+
+	/* Get gpio value from config bank 1 if output gpio.
+	   Get gpio value from IRQ RT status register for all other gpio modes.
+	 */
+	mode = (pm_gpio_chip->bank1[gpio] & PM_GPIO_MODE_MASK) >>
+		PM_GPIO_MODE_SHIFT;
+	if (mode == PM_GPIO_MODE_OUTPUT)
+		return pm_gpio_chip->bank1[gpio] & PM_GPIO_OUT_INVERT;
+	else
+		return pm8xxx_read_irq_stat(pm_gpio_chip->gpio_chip.dev->parent,
+				pm_gpio_chip->irq_base + gpio);
+}
+
+static int pm_gpio_set(struct pm_gpio_chip *pm_gpio_chip,
+		unsigned gpio, int value)
+{
+	int	rc;
+	u8	bank1;
+	unsigned long flags;
+
+	if (gpio >= pm_gpio_chip->gpio_chip.ngpio || pm_gpio_chip == NULL)
+		return -EINVAL;
+
+	spin_lock_irqsave(&pm_gpio_chip->pm_lock, flags);
+	bank1 = PM_GPIO_WRITE
+			| (pm_gpio_chip->bank1[gpio] & ~PM_GPIO_OUT_INVERT);
+
+	if (value)
+		bank1 |= PM_GPIO_OUT_INVERT;
+
+	pm_gpio_chip->bank1[gpio] = bank1;
+	rc = pm8xxx_writeb(pm_gpio_chip->gpio_chip.dev->parent,
+				SSBI_REG_ADDR_GPIO(gpio), bank1);
+	spin_unlock_irqrestore(&pm_gpio_chip->pm_lock, flags);
+
+	if (rc)
+		pr_err("FAIL pm8xxx_writeb(): rc=%d. "
+		       "(gpio=%d, value=%d)\n",
+		       rc, gpio, value);
+
+	return rc;
+}
+
+static int dir_map[] = {
+	PM_GPIO_MODE_OFF,
+	PM_GPIO_MODE_OUTPUT,
+	PM_GPIO_MODE_INPUT,
+	PM_GPIO_MODE_BOTH,
+};
+
+static int pm_gpio_set_direction(struct pm_gpio_chip *pm_gpio_chip,
+			      unsigned gpio, int direction)
+{
+	int	rc;
+	u8	bank1;
+	unsigned long flags;
+
+	if (!direction || pm_gpio_chip == NULL)
+		return -EINVAL;
+
+	spin_lock_irqsave(&pm_gpio_chip->pm_lock, flags);
+	bank1 = PM_GPIO_WRITE
+			| (pm_gpio_chip->bank1[gpio] & ~PM_GPIO_MODE_MASK);
+
+	bank1 |= ((dir_map[direction] << PM_GPIO_MODE_SHIFT)
+		  & PM_GPIO_MODE_MASK);
+
+	pm_gpio_chip->bank1[gpio] = bank1;
+	rc = pm8xxx_writeb(pm_gpio_chip->gpio_chip.dev->parent,
+				SSBI_REG_ADDR_GPIO(gpio), bank1);
+	spin_unlock_irqrestore(&pm_gpio_chip->pm_lock, flags);
+
+	if (rc)
+		pr_err("Failed on pm8xxx_writeb(): rc=%d (GPIO config)\n",
+			rc);
+
+	return rc;
+}
+
+static int pm_gpio_init_bank1(struct pm_gpio_chip *pm_gpio_chip)
+{
+	int i, rc;
+	u8 bank;
+
+	for (i = 0; i < pm_gpio_chip->gpio_chip.ngpio; i++) {
+		bank = 1 << PM_GPIO_BANK_SHIFT;
+		rc = pm8xxx_writeb(pm_gpio_chip->gpio_chip.dev->parent,
+				SSBI_REG_ADDR_GPIO(i),
+				bank);
+		if (rc) {
+			pr_err("error setting bank rc=%d\n", rc);
+			return rc;
+		}
+
+		rc = pm8xxx_readb(pm_gpio_chip->gpio_chip.dev->parent,
+				SSBI_REG_ADDR_GPIO(i),
+				&pm_gpio_chip->bank1[i]);
+		if (rc) {
+			pr_err("error reading bank 1 rc=%d\n", rc);
+			return rc;
+		}
+	}
+	return 0;
+}
+
+static int pm_gpio_to_irq(struct gpio_chip *gpio_chip, unsigned offset)
+{
+	struct pm_gpio_chip *pm_gpio_chip = dev_get_drvdata(gpio_chip->dev);
+
+	return pm_gpio_chip->irq_base + offset;
+}
+
+static int pm_gpio_read(struct gpio_chip *gpio_chip, unsigned offset)
+{
+	struct pm_gpio_chip *pm_gpio_chip = dev_get_drvdata(gpio_chip->dev);
+
+	return pm_gpio_get(pm_gpio_chip, offset);
+}
+
+static void pm_gpio_write(struct gpio_chip *gpio_chip,
+		unsigned offset, int val)
+{
+	struct pm_gpio_chip *pm_gpio_chip = dev_get_drvdata(gpio_chip->dev);
+
+	pm_gpio_set(pm_gpio_chip, offset, val);
+}
+
+static int pm_gpio_direction_input(struct gpio_chip *gpio_chip,
+		unsigned offset)
+{
+	struct pm_gpio_chip *pm_gpio_chip = dev_get_drvdata(gpio_chip->dev);
+
+	return pm_gpio_set_direction(pm_gpio_chip, offset, PM_GPIO_DIR_IN);
+}
+
+static int pm_gpio_direction_output(struct gpio_chip *gpio_chip,
+		unsigned offset,
+		int val)
+{
+	int ret;
+	struct pm_gpio_chip *pm_gpio_chip = dev_get_drvdata(gpio_chip->dev);
+
+	ret = pm_gpio_set_direction(pm_gpio_chip, offset, PM_GPIO_DIR_OUT);
+	if (!ret)
+		ret = pm_gpio_set(pm_gpio_chip, offset, val);
+
+	return ret;
+}
+
+static void pm_gpio_dbg_show(struct seq_file *s, struct gpio_chip *gpio_chip)
+{
+	static const char * const cmode[] = { "in", "in/out", "out", "off" };
+	struct pm_gpio_chip *pm_gpio_chip = dev_get_drvdata(gpio_chip->dev);
+	u8 mode, state, bank;
+	const char *label;
+	int i, j;
+
+	for (i = 0; i < gpio_chip->ngpio; i++) {
+		label = gpiochip_is_requested(gpio_chip, i);
+		mode = (pm_gpio_chip->bank1[i] & PM_GPIO_MODE_MASK) >>
+			PM_GPIO_MODE_SHIFT;
+		state = pm_gpio_get(pm_gpio_chip, i);
+		seq_printf(s, "gpio-%-3d (%-12.12s) %-10.10s"
+				" %s",
+				gpio_chip->base + i,
+				label ? label : "--",
+				cmode[mode],
+				state ? "hi" : "lo");
+		for (j = 0; j < PM_GPIO_BANKS; j++) {
+			bank = j << PM_GPIO_BANK_SHIFT;
+			pm8xxx_writeb(gpio_chip->dev->parent,
+					SSBI_REG_ADDR_GPIO(i),
+					bank);
+			pm8xxx_readb(gpio_chip->dev->parent,
+					SSBI_REG_ADDR_GPIO(i),
+					&bank);
+			seq_printf(s, " 0x%02x", bank);
+		}
+		seq_printf(s, "\n");
+	}
+}
+
+static int __devinit pm_gpio_probe(struct platform_device *pdev)
+{
+	int ret;
+	const struct pm8xxx_gpio_platform_data *pdata = pdev->dev.platform_data;
+	struct pm_gpio_chip *pm_gpio_chip;
+
+	if (!pdata) {
+		pr_err("missing platform data\n");
+		return -EINVAL;
+	}
+
+	pm_gpio_chip = kzalloc(sizeof(struct pm_gpio_chip), GFP_KERNEL);
+	if (!pm_gpio_chip) {
+		pr_err("Cannot allocate pm_gpio_chip\n");
+		return -ENOMEM;
+	}
+
+	pm_gpio_chip->bank1 = kzalloc(sizeof(u8) * pdata->gpio_cdata.ngpios,
+					GFP_KERNEL);
+	if (!pm_gpio_chip->bank1) {
+		pr_err("Cannot allocate pm_gpio_chip->bank1\n");
+		ret = -ENOMEM;
+		goto free_chip;
+	}
+
+	spin_lock_init(&pm_gpio_chip->pm_lock);
+	pm_gpio_chip->gpio_chip.label = "pm-gpio";
+	pm_gpio_chip->gpio_chip.direction_input	= pm_gpio_direction_input;
+	pm_gpio_chip->gpio_chip.direction_output = pm_gpio_direction_output;
+	pm_gpio_chip->gpio_chip.to_irq = pm_gpio_to_irq;
+	pm_gpio_chip->gpio_chip.get = pm_gpio_read;
+	pm_gpio_chip->gpio_chip.set = pm_gpio_write;
+	pm_gpio_chip->gpio_chip.dbg_show = pm_gpio_dbg_show;
+	pm_gpio_chip->gpio_chip.ngpio = pdata->gpio_cdata.ngpios;
+	pm_gpio_chip->gpio_chip.can_sleep = 0;
+	pm_gpio_chip->gpio_chip.dev = &pdev->dev;
+	pm_gpio_chip->gpio_chip.base = pdata->gpio_base;
+	pm_gpio_chip->irq_base = platform_get_irq(pdev, 0);
+	mutex_lock(&pm_gpio_chips_lock);
+	list_add(&pm_gpio_chip->link, &pm_gpio_chips);
+	mutex_unlock(&pm_gpio_chips_lock);
+	platform_set_drvdata(pdev, pm_gpio_chip);
+
+	ret = gpiochip_add(&pm_gpio_chip->gpio_chip);
+	if (ret) {
+		pr_err("gpiochip_add failed ret = %d\n", ret);
+		goto reset_drvdata;
+	}
+
+	ret = pm_gpio_init_bank1(pm_gpio_chip);
+	if (ret) {
+		pr_err("gpio init bank failed ret = %d\n", ret);
+		goto remove_chip;
+	}
+
+	pr_info("OK: base=%d, ngpio=%d\n", pm_gpio_chip->gpio_chip.base,
+		pm_gpio_chip->gpio_chip.ngpio);
+
+	return 0;
+
+remove_chip:
+	if (gpiochip_remove(&pm_gpio_chip->gpio_chip))
+		pr_err("failed to remove gpio chip\n");
+reset_drvdata:
+	platform_set_drvdata(pdev, NULL);
+	kfree(pm_gpio_chip->bank1);
+free_chip:
+	kfree(pm_gpio_chip);
+	return ret;
+}
+
+static int __devexit pm_gpio_remove(struct platform_device *pdev)
+{
+	struct pm_gpio_chip *pm_gpio_chip
+		= platform_get_drvdata(pdev);
+
+	mutex_lock(&pm_gpio_chips_lock);
+	list_del(&pm_gpio_chip->link);
+	mutex_unlock(&pm_gpio_chips_lock);
+	platform_set_drvdata(pdev, NULL);
+	if (gpiochip_remove(&pm_gpio_chip->gpio_chip))
+		pr_err("failed to remove gpio chip\n");
+	kfree(pm_gpio_chip->bank1);
+	kfree(pm_gpio_chip);
+	return 0;
+}
+
+int pm8xxx_gpio_config(int gpio, struct pm_gpio *param)
+{
+	int	rc, pm_gpio = -EINVAL;
+	u8	bank[8];
+	unsigned long flags;
+	struct pm_gpio_chip *pm_gpio_chip;
+	struct gpio_chip *gpio_chip;
+
+	if (param == NULL)
+		return -EINVAL;
+
+	mutex_lock(&pm_gpio_chips_lock);
+	list_for_each_entry(pm_gpio_chip, &pm_gpio_chips, link) {
+		gpio_chip = &pm_gpio_chip->gpio_chip;
+		if (gpio >= gpio_chip->base
+			&& gpio < gpio_chip->base + gpio_chip->ngpio) {
+			pm_gpio = gpio - gpio_chip->base;
+			break;
+		}
+	}
+	mutex_unlock(&pm_gpio_chips_lock);
+	if (pm_gpio < 0) {
+		pr_err("called on gpio %d not handled by any pmic\n", gpio);
+		return -EINVAL;
+	}
+
+	/* Select banks and configure the gpio */
+	bank[0] = PM_GPIO_WRITE |
+		((param->vin_sel << PM_GPIO_VIN_SHIFT) &
+			PM_GPIO_VIN_MASK) |
+		PM_GPIO_MODE_ENABLE;
+	bank[1] = PM_GPIO_WRITE |
+		((1 << PM_GPIO_BANK_SHIFT) &
+			PM_GPIO_BANK_MASK) |
+		((dir_map[param->direction] <<
+			PM_GPIO_MODE_SHIFT) &
+			PM_GPIO_MODE_MASK) |
+		((param->direction & PM_GPIO_DIR_OUT) ?
+			((param->output_buffer & 1) ?
+			 PM_GPIO_OUT_BUFFER : 0) : 0) |
+		((param->direction & PM_GPIO_DIR_OUT) ?
+			param->output_value & 0x01 : 0);
+	bank[2] = PM_GPIO_WRITE |
+		((2 << PM_GPIO_BANK_SHIFT) &
+			PM_GPIO_BANK_MASK) |
+		((param->pull << PM_GPIO_PULL_SHIFT) &
+			PM_GPIO_PULL_MASK);
+	bank[3] = PM_GPIO_WRITE |
+		((3 << PM_GPIO_BANK_SHIFT) &
+			PM_GPIO_BANK_MASK) |
+		((param->out_strength <<
+			PM_GPIO_OUT_STRENGTH_SHIFT) &
+			PM_GPIO_OUT_STRENGTH_MASK) |
+		(param->disable_pin ?
+			PM_GPIO_PIN_DISABLE : PM_GPIO_PIN_ENABLE);
+	bank[4] = PM_GPIO_WRITE |
+		((4 << PM_GPIO_BANK_SHIFT) &
+			PM_GPIO_BANK_MASK) |
+		((param->function << PM_GPIO_FUNC_SHIFT) &
+			PM_GPIO_FUNC_MASK);
+	bank[5] = PM_GPIO_WRITE |
+		((5 << PM_GPIO_BANK_SHIFT) & PM_GPIO_BANK_MASK) |
+		(param->inv_int_pol ? 0 : PM_GPIO_NON_INT_POL_INV);
+
+	spin_lock_irqsave(&pm_gpio_chip->pm_lock, flags);
+	/* Remember bank1 for later use */
+	pm_gpio_chip->bank1[pm_gpio] = bank[1];
+	rc = pm8xxx_write_buf(pm_gpio_chip->gpio_chip.dev->parent,
+			SSBI_REG_ADDR_GPIO(pm_gpio), bank, 6);
+	spin_unlock_irqrestore(&pm_gpio_chip->pm_lock, flags);
+
+	if (rc)
+		pr_err("Failed on pm8xxx_write_buf() rc=%d (GPIO config)\n",
+			rc);
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_gpio_config);
+
+static struct platform_driver pm_gpio_driver = {
+	.probe		= pm_gpio_probe,
+	.remove		= __devexit_p(pm_gpio_remove),
+	.driver		= {
+		.name	= PM8XXX_GPIO_DEV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pm_gpio_init(void)
+{
+	return platform_driver_register(&pm_gpio_driver);
+}
+postcore_initcall(pm_gpio_init);
+
+static void __exit pm_gpio_exit(void)
+{
+	platform_driver_unregister(&pm_gpio_driver);
+}
+module_exit(pm_gpio_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC GPIO driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8XXX_GPIO_DEV_NAME);
diff --git a/drivers/gpio/pm8xxx-mpp.c b/drivers/gpio/pm8xxx-mpp.c
new file mode 100644
index 0000000..affe980
--- /dev/null
+++ b/drivers/gpio/pm8xxx-mpp.c
@@ -0,0 +1,335 @@
+/*
+ * Qualcomm PM8XXX Multi-Purpose Pin (MPP) driver
+ *
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/mpp.h>
+
+/* MPP Type */
+#define	PM8XXX_MPP_TYPE_MASK		0xE0
+#define	PM8XXX_MPP_TYPE_SHIFT		5
+
+/* MPP Config Level */
+#define	PM8XXX_MPP_CONFIG_LVL_MASK	0x1C
+#define	PM8XXX_MPP_CONFIG_LVL_SHIFT	2
+
+/* MPP Config Control */
+#define	PM8XXX_MPP_CONFIG_CTRL_MASK	0x03
+#define	PM8XXX_MPP_CONFIG_CTRL_SHIFT	0
+
+struct pm8xxx_mpp_chip {
+	struct list_head	link;
+	struct gpio_chip	gpio_chip;
+	spinlock_t		pm_lock;
+	u8			*ctrl_reg;
+	int			mpp_base;
+	int			irq_base;
+	int			nmpps;
+	u16			base_addr;
+};
+
+static LIST_HEAD(pm8xxx_mpp_chips);
+static DEFINE_MUTEX(pm8xxx_mpp_chips_lock);
+
+static int pm8xxx_mpp_write(struct pm8xxx_mpp_chip *mpp_chip, u16 offset,
+				u8 val, u8 mask)
+{
+	u8 reg;
+	int rc;
+	unsigned long flags;
+
+	spin_lock_irqsave(&mpp_chip->pm_lock, flags);
+
+	reg = (mpp_chip->ctrl_reg[offset] & ~mask) | (val & mask);
+	rc = pm8xxx_writeb(mpp_chip->gpio_chip.dev->parent,
+				mpp_chip->base_addr + offset, reg);
+	if (!rc)
+		mpp_chip->ctrl_reg[offset] = reg;
+
+	spin_unlock_irqrestore(&mpp_chip->pm_lock, flags);
+
+	return rc;
+}
+
+static int pm8xxx_mpp_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	struct pm8xxx_mpp_chip *mpp_chip = dev_get_drvdata(chip->dev);
+
+	return mpp_chip->irq_base + offset;
+}
+
+static int pm8xxx_mpp_get(struct gpio_chip *chip, unsigned offset)
+{
+	struct pm8xxx_mpp_chip *mpp_chip = dev_get_drvdata(chip->dev);
+	int rc;
+
+	if ((mpp_chip->ctrl_reg[offset] & PM8XXX_MPP_TYPE_MASK) >>
+			PM8XXX_MPP_TYPE_SHIFT == PM8XXX_MPP_TYPE_D_OUTPUT)
+		rc = mpp_chip->ctrl_reg[offset] & PM8XXX_MPP_CONFIG_CTRL_MASK;
+	else
+		rc = pm8xxx_read_irq_stat(mpp_chip->gpio_chip.dev->parent,
+				mpp_chip->irq_base + offset);
+
+	return rc;
+}
+
+static void pm8xxx_mpp_set(struct gpio_chip *chip, unsigned offset, int val)
+{
+	struct pm8xxx_mpp_chip *mpp_chip = dev_get_drvdata(chip->dev);
+	u8 reg = val ? PM8XXX_MPP_DOUT_CTRL_HIGH : PM8XXX_MPP_DOUT_CTRL_LOW;
+	int rc;
+
+	rc = pm8xxx_mpp_write(mpp_chip, offset, reg,
+			PM8XXX_MPP_CONFIG_CTRL_MASK);
+	if (rc)
+		pr_err("pm8xxx_mpp_write(): rc=%d\n", rc);
+}
+
+static int pm8xxx_mpp_dir_input(struct gpio_chip *chip, unsigned offset)
+{
+	struct pm8xxx_mpp_chip *mpp_chip = dev_get_drvdata(chip->dev);
+	int rc = pm8xxx_mpp_write(mpp_chip, offset,
+			PM8XXX_MPP_TYPE_D_INPUT << PM8XXX_MPP_TYPE_SHIFT,
+			PM8XXX_MPP_TYPE_MASK);
+
+	if (rc)
+		pr_err("pm8xxx_mpp_write(): rc=%d\n", rc);
+	return rc;
+}
+
+static int pm8xxx_mpp_dir_output(struct gpio_chip *chip,
+		unsigned offset, int val)
+{
+	struct pm8xxx_mpp_chip *mpp_chip = dev_get_drvdata(chip->dev);
+	u8 reg = (PM8XXX_MPP_TYPE_D_OUTPUT << PM8XXX_MPP_TYPE_SHIFT) |
+		(val & PM8XXX_MPP_CONFIG_CTRL_MASK);
+	u8 mask = PM8XXX_MPP_TYPE_MASK | PM8XXX_MPP_CONFIG_CTRL_MASK;
+	int rc = pm8xxx_mpp_write(mpp_chip, offset, reg, mask);
+
+	if (rc)
+		pr_err("pm8xxx_mpp_write(): rc=%d\n", rc);
+	return rc;
+}
+
+static void pm8xxx_mpp_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+	static const char * const ctype[] = {	"d_in", "d_out", "bi_dir",
+						"a_in", "a_out", "sink",
+						"dtest_sink", "dtest_out"
+	};
+	struct pm8xxx_mpp_chip *mpp_chip = dev_get_drvdata(chip->dev);
+	u8 type, state;
+	const char *label;
+	int i;
+
+	for (i = 0; i < mpp_chip->nmpps; i++) {
+		label = gpiochip_is_requested(chip, i);
+		type = (mpp_chip->ctrl_reg[i] & PM8XXX_MPP_TYPE_MASK) >>
+			PM8XXX_MPP_TYPE_SHIFT;
+		state = pm8xxx_mpp_get(chip, i);
+		seq_printf(s, "gpio-%-3d (%-12.12s) %-10.10s"
+				" %s 0x%02x\n",
+				chip->base + i,
+				label ? label : "--",
+				ctype[type],
+				state ? "hi" : "lo",
+				mpp_chip->ctrl_reg[i]);
+	}
+}
+
+int pm8xxx_mpp_config(unsigned mpp, struct pm8xxx_mpp_config_data *config)
+{
+	struct pm8xxx_mpp_chip *mpp_chip;
+	int rc, found = 0;
+	u8 config_reg, mask;
+
+	if (!config) {
+		pr_err("config not specified for MPP %d\n", mpp);
+		return -EINVAL;
+	}
+
+	mutex_lock(&pm8xxx_mpp_chips_lock);
+	list_for_each_entry(mpp_chip, &pm8xxx_mpp_chips, link) {
+		if (mpp >= mpp_chip->mpp_base
+		    && mpp < mpp_chip->mpp_base + mpp_chip->nmpps) {
+			found = 1;
+			break;
+		}
+	}
+	mutex_unlock(&pm8xxx_mpp_chips_lock);
+	if (!found) {
+		pr_err("called on mpp %d not handled by any pmic\n", mpp);
+		return -EINVAL;
+	}
+
+	mask = PM8XXX_MPP_TYPE_MASK | PM8XXX_MPP_CONFIG_LVL_MASK |
+		PM8XXX_MPP_CONFIG_CTRL_MASK;
+	config_reg = (config->type << PM8XXX_MPP_TYPE_SHIFT)
+			& PM8XXX_MPP_TYPE_MASK;
+	config_reg |= (config->level << PM8XXX_MPP_CONFIG_LVL_SHIFT)
+			& PM8XXX_MPP_CONFIG_LVL_MASK;
+	config_reg |= config->control & PM8XXX_MPP_CONFIG_CTRL_MASK;
+
+	rc = pm8xxx_mpp_write(mpp_chip, mpp - mpp_chip->mpp_base, config_reg,
+			      mask);
+
+	if (rc)
+		pr_err("pm8xxx_mpp_write(): rc=%d\n", rc);
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_mpp_config);
+
+static int __devinit pm8xxx_mpp_reg_init(struct pm8xxx_mpp_chip *mpp_chip)
+{
+	int rc, i;
+
+	for (i = 0; i < mpp_chip->nmpps; i++) {
+		rc = pm8xxx_readb(mpp_chip->gpio_chip.dev->parent,
+					mpp_chip->base_addr + i,
+					&mpp_chip->ctrl_reg[i]);
+		if (rc) {
+			pr_err("failed to read register 0x%x rc=%d\n",
+						mpp_chip->base_addr + i, rc);
+			return rc;
+		}
+	}
+	return 0;
+}
+
+static int __devinit pm8xxx_mpp_probe(struct platform_device *pdev)
+{
+	int rc;
+	const struct pm8xxx_mpp_platform_data *pdata = pdev->dev.platform_data;
+	struct pm8xxx_mpp_chip *mpp_chip;
+
+	if (!pdata) {
+		pr_err("missing platform data\n");
+		return -EINVAL;
+	}
+
+	mpp_chip = kzalloc(sizeof(struct pm8xxx_mpp_chip), GFP_KERNEL);
+	if (!mpp_chip) {
+		pr_err("Cannot allocate %d bytes\n",
+			sizeof(struct pm8xxx_mpp_chip));
+		return -ENOMEM;
+	}
+
+	mpp_chip->ctrl_reg = kzalloc(pdata->core_data.nmpps, GFP_KERNEL);
+	if (!mpp_chip->ctrl_reg) {
+		pr_err("Cannot allocate %d bytes\n", pdata->core_data.nmpps);
+		rc = -ENOMEM;
+		goto free_mpp_chip;
+	}
+
+	spin_lock_init(&mpp_chip->pm_lock);
+
+	mpp_chip->gpio_chip.label = PM8XXX_MPP_DEV_NAME;
+	mpp_chip->gpio_chip.direction_input = pm8xxx_mpp_dir_input;
+	mpp_chip->gpio_chip.direction_output = pm8xxx_mpp_dir_output;
+	mpp_chip->gpio_chip.to_irq = pm8xxx_mpp_to_irq;
+	mpp_chip->gpio_chip.get = pm8xxx_mpp_get;
+	mpp_chip->gpio_chip.set = pm8xxx_mpp_set;
+	mpp_chip->gpio_chip.dbg_show = pm8xxx_mpp_dbg_show;
+	mpp_chip->gpio_chip.ngpio = pdata->core_data.nmpps;
+	mpp_chip->gpio_chip.can_sleep = 0;
+	mpp_chip->gpio_chip.dev = &pdev->dev;
+	mpp_chip->gpio_chip.base = pdata->mpp_base;
+	mpp_chip->irq_base = platform_get_irq(pdev, 0);
+	mpp_chip->mpp_base = pdata->mpp_base;
+	mpp_chip->base_addr = pdata->core_data.base_addr;
+	mpp_chip->nmpps = pdata->core_data.nmpps;
+
+	mutex_lock(&pm8xxx_mpp_chips_lock);
+	list_add(&mpp_chip->link, &pm8xxx_mpp_chips);
+	mutex_unlock(&pm8xxx_mpp_chips_lock);
+
+	platform_set_drvdata(pdev, mpp_chip);
+
+	rc = gpiochip_add(&mpp_chip->gpio_chip);
+	if (rc) {
+		pr_err("gpiochip_add failed, rc=%d\n", rc);
+		goto reset_drvdata;
+	}
+
+	rc = pm8xxx_mpp_reg_init(mpp_chip);
+	if (rc) {
+		pr_err("failed to read MPP ctrl registers, rc=%d\n", rc);
+		goto remove_chip;
+	}
+
+	pr_info("OK: base=%d, ngpio=%d\n", mpp_chip->gpio_chip.base,
+		mpp_chip->gpio_chip.ngpio);
+
+	return 0;
+
+remove_chip:
+	if (gpiochip_remove(&mpp_chip->gpio_chip))
+		pr_err("failed to remove gpio chip\n");
+reset_drvdata:
+	platform_set_drvdata(pdev, NULL);
+free_mpp_chip:
+	kfree(mpp_chip);
+	return rc;
+}
+
+static int __devexit pm8xxx_mpp_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_mpp_chip *mpp_chip = platform_get_drvdata(pdev);
+
+	mutex_lock(&pm8xxx_mpp_chips_lock);
+	list_del(&mpp_chip->link);
+	mutex_unlock(&pm8xxx_mpp_chips_lock);
+	platform_set_drvdata(pdev, NULL);
+	if (gpiochip_remove(&mpp_chip->gpio_chip))
+		pr_err("failed to remove gpio chip\n");
+	kfree(mpp_chip->ctrl_reg);
+	kfree(mpp_chip);
+
+	return 0;
+}
+
+static struct platform_driver pm8xxx_mpp_driver = {
+	.probe		= pm8xxx_mpp_probe,
+	.remove		= __devexit_p(pm8xxx_mpp_remove),
+	.driver		= {
+		.name	= PM8XXX_MPP_DEV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pm8xxx_mpp_init(void)
+{
+	return platform_driver_register(&pm8xxx_mpp_driver);
+}
+postcore_initcall(pm8xxx_mpp_init);
+
+static void __exit pm8xxx_mpp_exit(void)
+{
+	platform_driver_unregister(&pm8xxx_mpp_driver);
+}
+module_exit(pm8xxx_mpp_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PM8XXX MPP driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8XXX_MPP_DEV_NAME);
diff --git a/drivers/gpio/qpnp-gpio.c b/drivers/gpio/qpnp-gpio.c
new file mode 100644
index 0000000..d9a23e1
--- /dev/null
+++ b/drivers/gpio/qpnp-gpio.c
@@ -0,0 +1,1090 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/qpnp/gpio.h>
+#include <linux/export.h>
+
+#include <mach/qpnp.h>
+
+#define Q_REG_ADDR(q_spec, reg_index)	\
+		((q_spec)->offset + reg_index)
+
+#define Q_REG_STATUS1			0x8
+#define Q_NUM_CTL_REGS			7
+
+/* type registers base address offsets */
+#define Q_REG_TYPE			0x10
+#define Q_REG_SUBTYPE			0x11
+
+/* gpio peripheral type and subtype values */
+#define Q_GPIO_TYPE			0x10
+#define Q_GPIO_SUBTYPE_GPIO_4CH		0x1
+#define Q_GPIO_SUBTYPE_GPIOC_4CH	0x5
+#define Q_GPIO_SUBTYPE_GPIO_8CH		0x9
+#define Q_GPIO_SUBTYPE_GPIOC_8CH	0xD
+
+/* control register base address offsets */
+#define Q_REG_MODE_CTL			0x40
+#define Q_REG_DIG_PULL_CTL		0x42
+#define Q_REG_DIG_IN_CTL		0x43
+#define Q_REG_DIG_VIN_CTL		0x44
+#define Q_REG_DIG_OUT_CTL		0x45
+#define Q_REG_EN_CTL			0x46
+
+/* control register regs array indices */
+#define Q_REG_I_MODE_CTL		0
+#define Q_REG_I_DIG_PULL_CTL		2
+#define Q_REG_I_DIG_IN_CTL		3
+#define Q_REG_I_DIG_VIN_CTL		4
+#define Q_REG_I_DIG_OUT_CTL		5
+#define Q_REG_I_EN_CTL			6
+
+/* control reg: mode */
+#define Q_REG_OUT_INVERT_SHIFT		0
+#define Q_REG_OUT_INVERT_MASK		0x1
+#define Q_REG_SRC_SEL_SHIFT		1
+#define Q_REG_SRC_SEL_MASK		0xE
+#define Q_REG_MODE_SEL_SHIFT		4
+#define Q_REG_MODE_SEL_MASK		0x70
+
+/* control reg: dig_vin */
+#define Q_REG_VIN_SHIFT			0
+#define Q_REG_VIN_MASK			0x7
+
+/* control reg: dig_pull */
+#define Q_REG_PULL_SHIFT		0
+#define Q_REG_PULL_MASK			0x7
+
+/* control reg: dig_out */
+#define Q_REG_OUT_STRENGTH_SHIFT	0
+#define Q_REG_OUT_STRENGTH_MASK		0x3
+#define Q_REG_OUT_TYPE_SHIFT		4
+#define Q_REG_OUT_TYPE_MASK		0x30
+
+/* control reg: en */
+#define Q_REG_MASTER_EN_SHIFT		7
+#define Q_REG_MASTER_EN_MASK		0x80
+
+enum qpnp_gpio_param_type {
+	Q_GPIO_CFG_DIRECTION,
+	Q_GPIO_CFG_OUTPUT_TYPE,
+	Q_GPIO_CFG_INVERT,
+	Q_GPIO_CFG_PULL,
+	Q_GPIO_CFG_VIN_SEL,
+	Q_GPIO_CFG_OUT_STRENGTH,
+	Q_GPIO_CFG_SRC_SELECT,
+	Q_GPIO_CFG_MASTER_EN,
+	Q_GPIO_CFG_INVALID,
+};
+
+#define Q_NUM_PARAMS			Q_GPIO_CFG_INVALID
+
+/* param error checking */
+#define QPNP_GPIO_DIR_INVALID		3
+#define QPNP_GPIO_INVERT_INVALID	2
+#define QPNP_GPIO_OUT_BUF_INVALID	3
+#define QPNP_GPIO_VIN_INVALID		8
+#define QPNP_GPIO_PULL_INVALID		6
+#define QPNP_GPIO_OUT_STRENGTH_INVALID	4
+#define QPNP_GPIO_SRC_INVALID		8
+#define QPNP_GPIO_MASTER_INVALID	2
+
+struct qpnp_gpio_spec {
+	uint8_t slave;			/* 0-15 */
+	uint16_t offset;		/* 0-255 */
+	uint32_t gpio_chip_idx;		/* offset from gpio_chip base */
+	uint32_t pmic_gpio;		/* PMIC gpio number */
+	int irq;			/* logical IRQ number */
+	u8 regs[Q_NUM_CTL_REGS];	/* Control regs */
+	u8 type;			/* peripheral type */
+	u8 subtype;			/* peripheral subtype */
+	struct device_node *node;
+	enum qpnp_gpio_param_type params[Q_NUM_PARAMS];
+	struct qpnp_gpio_chip *q_chip;
+};
+
+struct qpnp_gpio_chip {
+	struct gpio_chip	gpio_chip;
+	struct spmi_device	*spmi;
+	struct qpnp_gpio_spec	**pmic_gpios;
+	struct qpnp_gpio_spec	**chip_gpios;
+	uint32_t		pmic_gpio_lowest;
+	uint32_t		pmic_gpio_highest;
+	struct device_node	*int_ctrl;
+	struct list_head	chip_list;
+	struct dentry		*dfs_dir;
+};
+
+static LIST_HEAD(qpnp_gpio_chips);
+static DEFINE_MUTEX(qpnp_gpio_chips_lock);
+
+static inline void qpnp_pmic_gpio_set_spec(struct qpnp_gpio_chip *q_chip,
+					      uint32_t pmic_gpio,
+					      struct qpnp_gpio_spec *spec)
+{
+	q_chip->pmic_gpios[pmic_gpio - q_chip->pmic_gpio_lowest] = spec;
+}
+
+static inline struct qpnp_gpio_spec *qpnp_pmic_gpio_get_spec(
+						struct qpnp_gpio_chip *q_chip,
+						uint32_t pmic_gpio)
+{
+	if (pmic_gpio < q_chip->pmic_gpio_lowest ||
+	    pmic_gpio > q_chip->pmic_gpio_highest)
+		return NULL;
+
+	return q_chip->pmic_gpios[pmic_gpio - q_chip->pmic_gpio_lowest];
+}
+
+static inline struct qpnp_gpio_spec *qpnp_chip_gpio_get_spec(
+						struct qpnp_gpio_chip *q_chip,
+						uint32_t chip_gpio)
+{
+	if (chip_gpio > q_chip->gpio_chip.ngpio)
+		return NULL;
+
+	return q_chip->chip_gpios[chip_gpio];
+}
+
+static inline void qpnp_chip_gpio_set_spec(struct qpnp_gpio_chip *q_chip,
+					      uint32_t chip_gpio,
+					      struct qpnp_gpio_spec *spec)
+{
+	q_chip->chip_gpios[chip_gpio] = spec;
+}
+
+static int qpnp_gpio_check_config(struct qpnp_gpio_spec *q_spec,
+				  struct qpnp_gpio_cfg *param)
+{
+	int gpio = q_spec->pmic_gpio;
+
+	if (param->direction >= QPNP_GPIO_DIR_INVALID)
+		pr_err("invalid direction for gpio %d\n", gpio);
+	else if (param->invert >= QPNP_GPIO_INVERT_INVALID)
+		pr_err("invalid invert polarity for gpio %d\n", gpio);
+	else if (param->src_select >= QPNP_GPIO_SRC_INVALID)
+		pr_err("invalid source select for gpio %d\n", gpio);
+	else if (param->out_strength >= QPNP_GPIO_OUT_STRENGTH_INVALID ||
+		 param->out_strength == 0)
+		pr_err("invalid out strength for gpio %d\n", gpio);
+	else if (param->output_type >= QPNP_GPIO_OUT_BUF_INVALID)
+		pr_err("invalid out type for gpio %d\n", gpio);
+	else if ((param->output_type == QPNP_GPIO_OUT_BUF_OPEN_DRAIN_NMOS ||
+		 param->output_type == QPNP_GPIO_OUT_BUF_OPEN_DRAIN_PMOS) &&
+		 (q_spec->subtype == Q_GPIO_SUBTYPE_GPIOC_4CH ||
+		 (q_spec->subtype == Q_GPIO_SUBTYPE_GPIOC_8CH)))
+		pr_err("invalid out type for gpio %d\n"
+		       "gpioc does not support open-drain\n", gpio);
+	else if (param->vin_sel >= QPNP_GPIO_VIN_INVALID)
+		pr_err("invalid vin select value for gpio %d\n", gpio);
+	else if (param->pull >= QPNP_GPIO_PULL_INVALID)
+		pr_err("invalid pull value for gpio %d\n", gpio);
+	else if (param->master_en >= QPNP_GPIO_MASTER_INVALID)
+		pr_err("invalid master_en value for gpio %d\n", gpio);
+	else
+		return 0;
+
+	return -EINVAL;
+}
+
+static inline u8 q_reg_get(u8 *reg, int shift, int mask)
+{
+	return (*reg & mask) >> shift;
+}
+
+static inline void q_reg_set(u8 *reg, int shift, int mask, int value)
+{
+	*reg |= (value << shift) & mask;
+}
+
+static inline void q_reg_clr_set(u8 *reg, int shift, int mask, int value)
+{
+	*reg &= ~mask;
+	*reg |= (value << shift) & mask;
+}
+
+static int qpnp_gpio_cache_regs(struct qpnp_gpio_chip *q_chip,
+				struct qpnp_gpio_spec *q_spec)
+{
+	int rc;
+	struct device *dev = &q_chip->spmi->dev;
+
+	rc = spmi_ext_register_readl(q_chip->spmi->ctrl, q_spec->slave,
+				     Q_REG_ADDR(q_spec, Q_REG_MODE_CTL),
+				     &q_spec->regs[Q_REG_I_MODE_CTL],
+				     Q_NUM_CTL_REGS);
+	if (rc)
+		dev_err(dev, "%s: unable to read control regs\n", __func__);
+
+	return rc;
+}
+
+static int _qpnp_gpio_config(struct qpnp_gpio_chip *q_chip,
+			     struct qpnp_gpio_spec *q_spec,
+			     struct qpnp_gpio_cfg *param)
+{
+	struct device *dev = &q_chip->spmi->dev;
+	int rc;
+
+	rc = qpnp_gpio_check_config(q_spec, param);
+	if (rc)
+		goto gpio_cfg;
+
+	/* set direction */
+	q_reg_clr_set(&q_spec->regs[Q_REG_I_MODE_CTL],
+			  Q_REG_MODE_SEL_SHIFT, Q_REG_MODE_SEL_MASK,
+			  param->direction);
+
+	/* output specific configuration */
+	q_reg_clr_set(&q_spec->regs[Q_REG_I_MODE_CTL],
+			  Q_REG_OUT_INVERT_SHIFT, Q_REG_OUT_INVERT_MASK,
+			  param->invert);
+	q_reg_clr_set(&q_spec->regs[Q_REG_I_MODE_CTL],
+			  Q_REG_SRC_SEL_SHIFT, Q_REG_SRC_SEL_MASK,
+			  param->src_select);
+	q_reg_clr_set(&q_spec->regs[Q_REG_I_DIG_OUT_CTL],
+			  Q_REG_OUT_STRENGTH_SHIFT, Q_REG_OUT_STRENGTH_MASK,
+			  param->out_strength);
+	q_reg_clr_set(&q_spec->regs[Q_REG_I_DIG_OUT_CTL],
+			  Q_REG_OUT_TYPE_SHIFT, Q_REG_OUT_TYPE_MASK,
+			  param->output_type);
+
+	/* config applicable for both input / output */
+	q_reg_clr_set(&q_spec->regs[Q_REG_I_DIG_VIN_CTL],
+			  Q_REG_VIN_SHIFT, Q_REG_VIN_MASK,
+			  param->vin_sel);
+	q_reg_clr_set(&q_spec->regs[Q_REG_I_DIG_PULL_CTL],
+			  Q_REG_PULL_SHIFT, Q_REG_PULL_MASK,
+			  param->pull);
+	q_reg_clr_set(&q_spec->regs[Q_REG_I_EN_CTL],
+			  Q_REG_MASTER_EN_SHIFT, Q_REG_MASTER_EN_MASK,
+			  param->master_en);
+
+	rc = spmi_ext_register_writel(q_chip->spmi->ctrl, q_spec->slave,
+			      Q_REG_ADDR(q_spec, Q_REG_MODE_CTL),
+			      &q_spec->regs[Q_REG_I_MODE_CTL], Q_NUM_CTL_REGS);
+	if (rc) {
+		dev_err(&q_chip->spmi->dev, "%s: unable to write master"
+						" enable\n", __func__);
+		goto gpio_cfg;
+	}
+
+	return 0;
+
+gpio_cfg:
+	dev_err(dev, "%s: unable to set default config for"
+		     " pmic gpio %d\n", __func__, q_spec->pmic_gpio);
+
+	return rc;
+}
+
+int qpnp_gpio_config(int gpio, struct qpnp_gpio_cfg *param)
+{
+	int rc, chip_offset;
+	struct qpnp_gpio_chip *q_chip;
+	struct qpnp_gpio_spec *q_spec = NULL;
+	struct gpio_chip *gpio_chip;
+
+	if (param == NULL)
+		return -EINVAL;
+
+	mutex_lock(&qpnp_gpio_chips_lock);
+	list_for_each_entry(q_chip, &qpnp_gpio_chips, chip_list) {
+		gpio_chip = &q_chip->gpio_chip;
+		if (gpio >= gpio_chip->base
+				&& gpio < gpio_chip->base + gpio_chip->ngpio) {
+			chip_offset = gpio - gpio_chip->base;
+			q_spec = qpnp_chip_gpio_get_spec(q_chip, chip_offset);
+			if (WARN_ON(!q_spec)) {
+				mutex_unlock(&qpnp_gpio_chips_lock);
+				return -ENODEV;
+			}
+			break;
+		}
+	}
+	mutex_unlock(&qpnp_gpio_chips_lock);
+
+	rc = _qpnp_gpio_config(q_chip, q_spec, param);
+
+	return rc;
+}
+EXPORT_SYMBOL(qpnp_gpio_config);
+
+int qpnp_gpio_map_gpio(uint16_t slave_id, uint32_t pmic_gpio)
+{
+	struct qpnp_gpio_chip *q_chip;
+	struct qpnp_gpio_spec *q_spec = NULL;
+
+	mutex_lock(&qpnp_gpio_chips_lock);
+	list_for_each_entry(q_chip, &qpnp_gpio_chips, chip_list) {
+		if (q_chip->spmi->sid != slave_id)
+			continue;
+		if (q_chip->pmic_gpio_lowest <= pmic_gpio &&
+		    q_chip->pmic_gpio_highest >= pmic_gpio) {
+			q_spec = qpnp_pmic_gpio_get_spec(q_chip, pmic_gpio);
+			mutex_unlock(&qpnp_gpio_chips_lock);
+			if (WARN_ON(!q_spec))
+				return -ENODEV;
+			return q_chip->gpio_chip.base + q_spec->gpio_chip_idx;
+		}
+	}
+	mutex_unlock(&qpnp_gpio_chips_lock);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(qpnp_gpio_map_gpio);
+
+static int qpnp_gpio_to_irq(struct gpio_chip *gpio_chip, unsigned offset)
+{
+	struct qpnp_gpio_chip *q_chip = dev_get_drvdata(gpio_chip->dev);
+	struct qpnp_gpio_spec *q_spec;
+
+	q_spec = qpnp_chip_gpio_get_spec(q_chip, offset);
+	if (!q_spec)
+		return -EINVAL;
+
+	return q_spec->irq;
+}
+
+static int qpnp_gpio_get(struct gpio_chip *gpio_chip, unsigned offset)
+{
+	int rc, ret_val;
+	struct qpnp_gpio_chip *q_chip = dev_get_drvdata(gpio_chip->dev);
+	struct qpnp_gpio_spec *q_spec = NULL;
+	u8 buf[1];
+
+	if (WARN_ON(!q_chip))
+		return -ENODEV;
+
+	q_spec = qpnp_chip_gpio_get_spec(q_chip, offset);
+	if (WARN_ON(!q_spec))
+		return -ENODEV;
+
+	/* gpio val is from RT status iff input is enabled */
+	if ((q_spec->regs[Q_REG_I_MODE_CTL] & Q_REG_MODE_SEL_MASK)
+						== QPNP_GPIO_DIR_IN) {
+		/* INT_RT_STS */
+		rc = spmi_ext_register_readl(q_chip->spmi->ctrl, q_spec->slave,
+				Q_REG_ADDR(q_spec, Q_REG_STATUS1),
+				&buf[0], 1);
+		return buf[0];
+
+	} else {
+		ret_val = (q_spec->regs[Q_REG_I_MODE_CTL] &
+			       Q_REG_OUT_INVERT_MASK) >> Q_REG_OUT_INVERT_SHIFT;
+		return ret_val;
+	}
+
+	return 0;
+}
+
+static int __qpnp_gpio_set(struct qpnp_gpio_chip *q_chip,
+			   struct qpnp_gpio_spec *q_spec, int value)
+{
+	int rc;
+
+	if (!q_chip || !q_spec)
+		return -EINVAL;
+
+	if (value)
+		q_reg_clr_set(&q_spec->regs[Q_REG_I_MODE_CTL],
+			  Q_REG_OUT_INVERT_SHIFT, Q_REG_OUT_INVERT_MASK, 1);
+	else
+		q_reg_clr_set(&q_spec->regs[Q_REG_I_MODE_CTL],
+			  Q_REG_OUT_INVERT_SHIFT, Q_REG_OUT_INVERT_MASK, 0);
+
+	rc = spmi_ext_register_writel(q_chip->spmi->ctrl, q_spec->slave,
+			      Q_REG_ADDR(q_spec, Q_REG_I_MODE_CTL),
+			      &q_spec->regs[Q_REG_I_MODE_CTL], 1);
+	if (rc)
+		dev_err(&q_chip->spmi->dev, "%s: spmi write failed\n",
+								__func__);
+	return rc;
+}
+
+
+static void qpnp_gpio_set(struct gpio_chip *gpio_chip,
+		unsigned offset, int value)
+{
+	struct qpnp_gpio_chip *q_chip = dev_get_drvdata(gpio_chip->dev);
+	struct qpnp_gpio_spec *q_spec;
+
+	if (WARN_ON(!q_chip))
+		return;
+
+	q_spec = qpnp_chip_gpio_get_spec(q_chip, offset);
+	if (WARN_ON(!q_spec))
+		return;
+
+	__qpnp_gpio_set(q_chip, q_spec, value);
+}
+
+static int qpnp_gpio_set_direction(struct qpnp_gpio_chip *q_chip,
+				   struct qpnp_gpio_spec *q_spec, int direction)
+{
+	int rc;
+
+	if (!q_chip || !q_spec)
+		return -EINVAL;
+
+	if (direction >= QPNP_GPIO_DIR_INVALID) {
+		pr_err("invalid direction specification %d\n", direction);
+		return -EINVAL;
+	}
+
+	q_reg_clr_set(&q_spec->regs[Q_REG_I_MODE_CTL],
+			Q_REG_MODE_SEL_SHIFT,
+			Q_REG_MODE_SEL_MASK,
+			direction);
+
+	rc = spmi_ext_register_writel(q_chip->spmi->ctrl, q_spec->slave,
+			      Q_REG_ADDR(q_spec, Q_REG_I_MODE_CTL),
+			      &q_spec->regs[Q_REG_I_MODE_CTL], 1);
+	return rc;
+}
+
+static int qpnp_gpio_direction_input(struct gpio_chip *gpio_chip,
+		unsigned offset)
+{
+	struct qpnp_gpio_chip *q_chip = dev_get_drvdata(gpio_chip->dev);
+	struct qpnp_gpio_spec *q_spec;
+
+	if (WARN_ON(!q_chip))
+		return -ENODEV;
+
+	q_spec = qpnp_chip_gpio_get_spec(q_chip, offset);
+	if (WARN_ON(!q_spec))
+		return -ENODEV;
+
+	return qpnp_gpio_set_direction(q_chip, q_spec, QPNP_GPIO_DIR_IN);
+}
+
+static int qpnp_gpio_direction_output(struct gpio_chip *gpio_chip,
+		unsigned offset,
+		int val)
+{
+	int rc;
+	struct qpnp_gpio_chip *q_chip = dev_get_drvdata(gpio_chip->dev);
+	struct qpnp_gpio_spec *q_spec;
+
+	if (WARN_ON(!q_chip))
+		return -ENODEV;
+
+	q_spec = qpnp_chip_gpio_get_spec(q_chip, offset);
+	if (WARN_ON(!q_spec))
+		return -ENODEV;
+
+	rc = __qpnp_gpio_set(q_chip, q_spec, val);
+	if (rc)
+		return rc;
+
+	rc = qpnp_gpio_set_direction(q_chip, q_spec, QPNP_GPIO_DIR_OUT);
+
+	return rc;
+}
+
+static int qpnp_gpio_of_gpio_xlate(struct gpio_chip *gpio_chip,
+				   struct device_node *np,
+				   const void *gpio_spec, u32 *flags)
+{
+	struct qpnp_gpio_chip *q_chip = dev_get_drvdata(gpio_chip->dev);
+	struct qpnp_gpio_spec *q_spec;
+	const __be32 *gpio = gpio_spec;
+	u32 n = be32_to_cpup(gpio);
+
+	if (WARN_ON(gpio_chip->of_gpio_n_cells < 2)) {
+		pr_err("of_gpio_n_cells < 2\n");
+		return -EINVAL;
+	}
+
+	q_spec = qpnp_pmic_gpio_get_spec(q_chip, n);
+	if (!q_spec) {
+		pr_err("no such PMIC gpio %u in device topology\n", n);
+		return -EINVAL;
+	}
+
+	if (flags)
+		*flags = be32_to_cpu(gpio[1]);
+
+	return q_spec->gpio_chip_idx;
+}
+
+static int qpnp_gpio_apply_config(struct qpnp_gpio_chip *q_chip,
+				  struct qpnp_gpio_spec *q_spec)
+{
+	struct qpnp_gpio_cfg param;
+	struct device_node *node = q_spec->node;
+	int rc;
+
+	param.direction    = q_reg_get(&q_spec->regs[Q_REG_I_MODE_CTL],
+				       Q_REG_MODE_SEL_SHIFT,
+				       Q_REG_MODE_SEL_MASK);
+	param.output_type  = q_reg_get(&q_spec->regs[Q_REG_I_DIG_OUT_CTL],
+				       Q_REG_OUT_TYPE_SHIFT,
+				       Q_REG_OUT_TYPE_MASK);
+	param.invert	   = q_reg_get(&q_spec->regs[Q_REG_I_MODE_CTL],
+				       Q_REG_OUT_INVERT_MASK,
+				       Q_REG_OUT_INVERT_MASK);
+	param.pull	   = q_reg_get(&q_spec->regs[Q_REG_I_MODE_CTL],
+				       Q_REG_PULL_SHIFT, Q_REG_PULL_MASK);
+	param.vin_sel	   = q_reg_get(&q_spec->regs[Q_REG_I_DIG_VIN_CTL],
+				       Q_REG_VIN_SHIFT, Q_REG_VIN_MASK);
+	param.out_strength = q_reg_get(&q_spec->regs[Q_REG_I_DIG_OUT_CTL],
+				       Q_REG_OUT_STRENGTH_SHIFT,
+				       Q_REG_OUT_STRENGTH_MASK);
+	param.src_select   = q_reg_get(&q_spec->regs[Q_REG_I_MODE_CTL],
+				       Q_REG_SRC_SEL_SHIFT, Q_REG_SRC_SEL_MASK);
+	param.master_en    = q_reg_get(&q_spec->regs[Q_REG_I_EN_CTL],
+				       Q_REG_MASTER_EN_SHIFT,
+				       Q_REG_MASTER_EN_MASK);
+
+	of_property_read_u32(node, "qcom,direction",
+		&param.direction);
+	of_property_read_u32(node, "qcom,output-type",
+		&param.output_type);
+	of_property_read_u32(node, "qcom,invert",
+		&param.invert);
+	of_property_read_u32(node, "qcom,pull",
+		&param.pull);
+	of_property_read_u32(node, "qcom,vin-sel",
+		&param.vin_sel);
+	of_property_read_u32(node, "qcom,out-strength",
+		&param.out_strength);
+	of_property_read_u32(node, "qcom,src-select",
+		&param.src_select);
+	rc = of_property_read_u32(node, "qcom,master-en",
+		&param.master_en);
+
+	rc = _qpnp_gpio_config(q_chip, q_spec, &param);
+
+	return rc;
+}
+
+static int qpnp_gpio_free_chip(struct qpnp_gpio_chip *q_chip)
+{
+	struct spmi_device *spmi = q_chip->spmi;
+	int rc, i;
+
+	if (q_chip->chip_gpios)
+		for (i = 0; i < spmi->num_dev_node; i++)
+			kfree(q_chip->chip_gpios[i]);
+
+	mutex_lock(&qpnp_gpio_chips_lock);
+	list_del(&q_chip->chip_list);
+	mutex_unlock(&qpnp_gpio_chips_lock);
+	rc = gpiochip_remove(&q_chip->gpio_chip);
+	if (rc)
+		dev_err(&q_chip->spmi->dev, "%s: unable to remove gpio\n",
+				__func__);
+	kfree(q_chip->chip_gpios);
+	kfree(q_chip->pmic_gpios);
+	kfree(q_chip);
+	return rc;
+}
+
+#ifdef CONFIG_GPIO_QPNP_DEBUG
+struct qpnp_gpio_reg {
+	uint32_t addr;
+	uint32_t idx;
+	uint32_t shift;
+	uint32_t mask;
+};
+
+static struct dentry *driver_dfs_dir;
+
+static int qpnp_gpio_reg_attr(enum qpnp_gpio_param_type type,
+			     struct qpnp_gpio_reg *cfg)
+{
+	switch (type) {
+	case Q_GPIO_CFG_DIRECTION:
+		cfg->addr = Q_REG_MODE_CTL;
+		cfg->idx = Q_REG_I_MODE_CTL;
+		cfg->shift = Q_REG_MODE_SEL_SHIFT;
+		cfg->mask = Q_REG_MODE_SEL_MASK;
+		break;
+	case Q_GPIO_CFG_OUTPUT_TYPE:
+		cfg->addr = Q_REG_DIG_OUT_CTL;
+		cfg->idx = Q_REG_I_DIG_OUT_CTL;
+		cfg->shift = Q_REG_OUT_TYPE_SHIFT;
+		cfg->mask = Q_REG_OUT_TYPE_MASK;
+		break;
+	case Q_GPIO_CFG_INVERT:
+		cfg->addr = Q_REG_MODE_CTL;
+		cfg->idx = Q_REG_I_MODE_CTL;
+		cfg->shift = Q_REG_OUT_INVERT_SHIFT;
+		cfg->mask = Q_REG_OUT_INVERT_MASK;
+		break;
+	case Q_GPIO_CFG_PULL:
+		cfg->addr = Q_REG_DIG_PULL_CTL;
+		cfg->idx = Q_REG_I_DIG_PULL_CTL;
+		cfg->shift = Q_REG_PULL_SHIFT;
+		cfg->mask = Q_REG_PULL_MASK;
+		break;
+	case Q_GPIO_CFG_VIN_SEL:
+		cfg->addr = Q_REG_DIG_VIN_CTL;
+		cfg->idx = Q_REG_I_DIG_VIN_CTL;
+		cfg->shift = Q_REG_VIN_SHIFT;
+		cfg->mask = Q_REG_VIN_MASK;
+		break;
+	case Q_GPIO_CFG_OUT_STRENGTH:
+		cfg->addr = Q_REG_DIG_OUT_CTL;
+		cfg->idx = Q_REG_I_DIG_OUT_CTL;
+		cfg->shift = Q_REG_OUT_STRENGTH_SHIFT;
+		cfg->mask = Q_REG_OUT_STRENGTH_MASK;
+		break;
+	case Q_GPIO_CFG_SRC_SELECT:
+		cfg->addr = Q_REG_MODE_CTL;
+		cfg->idx = Q_REG_I_MODE_CTL;
+		cfg->shift = Q_REG_SRC_SEL_SHIFT;
+		cfg->mask = Q_REG_SRC_SEL_MASK;
+		break;
+	case Q_GPIO_CFG_MASTER_EN:
+		cfg->addr = Q_REG_EN_CTL;
+		cfg->idx = Q_REG_I_EN_CTL;
+		cfg->shift = Q_REG_MASTER_EN_SHIFT;
+		cfg->mask = Q_REG_MASTER_EN_MASK;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int qpnp_gpio_debugfs_get(void *data, u64 *val)
+{
+	enum qpnp_gpio_param_type *idx = data;
+	struct qpnp_gpio_spec *q_spec;
+	struct qpnp_gpio_reg cfg = {};
+	int rc;
+
+	rc = qpnp_gpio_reg_attr(*idx, &cfg);
+	if (rc)
+		return rc;
+	q_spec = container_of(idx, struct qpnp_gpio_spec, params[*idx]);
+	*val = q_reg_get(&q_spec->regs[cfg.idx], cfg.shift, cfg.mask);
+	return 0;
+}
+
+static int qpnp_gpio_check_reg_val(enum qpnp_gpio_param_type idx,
+				   struct qpnp_gpio_spec *q_spec,
+				   uint32_t val)
+{
+	switch (idx) {
+	case Q_GPIO_CFG_DIRECTION:
+		if (val >= QPNP_GPIO_DIR_INVALID)
+			return -EINVAL;
+		break;
+	case Q_GPIO_CFG_OUTPUT_TYPE:
+		if ((val >= QPNP_GPIO_OUT_BUF_INVALID) ||
+		   ((val == QPNP_GPIO_OUT_BUF_OPEN_DRAIN_NMOS ||
+		   val == QPNP_GPIO_OUT_BUF_OPEN_DRAIN_PMOS) &&
+		   (q_spec->subtype == Q_GPIO_SUBTYPE_GPIOC_4CH ||
+		   (q_spec->subtype == Q_GPIO_SUBTYPE_GPIOC_8CH))))
+			return -EINVAL;
+		break;
+	case Q_GPIO_CFG_INVERT:
+		if (val >= QPNP_GPIO_INVERT_INVALID)
+			return -EINVAL;
+		break;
+	case Q_GPIO_CFG_PULL:
+		if (val >= QPNP_GPIO_PULL_INVALID)
+			return -EINVAL;
+		break;
+	case Q_GPIO_CFG_VIN_SEL:
+		if (val >= QPNP_GPIO_VIN_INVALID)
+			return -EINVAL;
+		break;
+	case Q_GPIO_CFG_OUT_STRENGTH:
+		if (val >= QPNP_GPIO_OUT_STRENGTH_INVALID ||
+		    val == 0)
+			return -EINVAL;
+		break;
+	case Q_GPIO_CFG_SRC_SELECT:
+		if (val >= QPNP_GPIO_SRC_INVALID)
+			return -EINVAL;
+		break;
+	case Q_GPIO_CFG_MASTER_EN:
+		if (val >= QPNP_GPIO_MASTER_INVALID)
+			return -EINVAL;
+		break;
+	default:
+		pr_err("invalid param type %u specified\n", idx);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int qpnp_gpio_debugfs_set(void *data, u64 val)
+{
+	enum qpnp_gpio_param_type *idx = data;
+	struct qpnp_gpio_spec *q_spec;
+	struct qpnp_gpio_chip *q_chip;
+	struct qpnp_gpio_reg cfg = {};
+	int rc;
+
+	q_spec = container_of(idx, struct qpnp_gpio_spec, params[*idx]);
+	q_chip = q_spec->q_chip;
+
+	rc = qpnp_gpio_check_reg_val(*idx, q_spec, val);
+	if (rc)
+		return rc;
+
+	rc = qpnp_gpio_reg_attr(*idx, &cfg);
+	if (rc)
+		return rc;
+	q_reg_clr_set(&q_spec->regs[cfg.idx], cfg.shift, cfg.mask, val);
+	rc = spmi_ext_register_writel(q_chip->spmi->ctrl, q_spec->slave,
+				      Q_REG_ADDR(q_spec, cfg.addr),
+				      &q_spec->regs[cfg.idx], 1);
+
+	return rc;
+}
+DEFINE_SIMPLE_ATTRIBUTE(qpnp_gpio_fops, qpnp_gpio_debugfs_get,
+			qpnp_gpio_debugfs_set, "%llu\n");
+
+#define DEBUGFS_BUF_SIZE 11 /* supports 2^32 in decimal */
+
+struct qpnp_gpio_debugfs_args {
+	enum qpnp_gpio_param_type type;
+	const char *filename;
+};
+
+static struct qpnp_gpio_debugfs_args dfs_args[] = {
+	{ Q_GPIO_CFG_DIRECTION, "direction" },
+	{ Q_GPIO_CFG_OUTPUT_TYPE, "output_type" },
+	{ Q_GPIO_CFG_INVERT, "invert" },
+	{ Q_GPIO_CFG_PULL, "pull" },
+	{ Q_GPIO_CFG_VIN_SEL, "vin_sel" },
+	{ Q_GPIO_CFG_OUT_STRENGTH, "out_strength" },
+	{ Q_GPIO_CFG_SRC_SELECT, "src_select" },
+	{ Q_GPIO_CFG_MASTER_EN, "master_en" }
+};
+
+static int qpnp_gpio_debugfs_create(struct qpnp_gpio_chip *q_chip)
+{
+	struct spmi_device *spmi = q_chip->spmi;
+	struct device *dev = &spmi->dev;
+	struct qpnp_gpio_spec *q_spec;
+	enum qpnp_gpio_param_type *params;
+	enum qpnp_gpio_param_type type;
+	char pmic_gpio[DEBUGFS_BUF_SIZE];
+	const char *filename;
+	struct dentry *dfs, *dfs_io_dir;
+	int i, j;
+
+	BUG_ON(Q_NUM_PARAMS != ARRAY_SIZE(dfs_args));
+
+	q_chip->dfs_dir = debugfs_create_dir(dev->of_node->name,
+							driver_dfs_dir);
+	if (q_chip->dfs_dir == NULL) {
+		dev_err(dev, "%s: cannot register chip debugfs directory %s\n",
+						__func__, dev->of_node->name);
+		return -ENODEV;
+	}
+
+	for (i = 0; i < spmi->num_dev_node; i++) {
+		q_spec = qpnp_chip_gpio_get_spec(q_chip, i);
+		params = q_spec->params;
+		snprintf(pmic_gpio, DEBUGFS_BUF_SIZE, "%u", q_spec->pmic_gpio);
+		dfs_io_dir = debugfs_create_dir(pmic_gpio,
+							q_chip->dfs_dir);
+		if (dfs_io_dir == NULL)
+			goto dfs_err;
+
+		for (j = 0; j < Q_NUM_PARAMS; j++) {
+			type = dfs_args[j].type;
+			filename = dfs_args[j].filename;
+
+			params[type] = type;
+			dfs = debugfs_create_file(
+					filename,
+					S_IRUGO | S_IWUSR,
+					dfs_io_dir,
+					&q_spec->params[type],
+					&qpnp_gpio_fops);
+			if (dfs == NULL)
+				goto dfs_err;
+		}
+	}
+	return 0;
+dfs_err:
+	dev_err(dev, "%s: cannot register debugfs for pmic gpio %u on"
+				     " chip %s\n", __func__,
+				     q_spec->pmic_gpio, dev->of_node->name);
+	debugfs_remove_recursive(q_chip->dfs_dir);
+	return -ENFILE;
+}
+#else
+static int qpnp_gpio_debugfs_create(struct qpnp_gpio_chip *q_chip)
+{
+	return 0;
+}
+#endif
+
+static int qpnp_gpio_probe(struct spmi_device *spmi)
+{
+	struct qpnp_gpio_chip *q_chip;
+	struct resource *res;
+	struct qpnp_gpio_spec *q_spec;
+	int i, rc;
+	int lowest_gpio = UINT_MAX, highest_gpio = 0;
+	u32 intspec[3], gpio;
+	char buf[2];
+
+	q_chip = kzalloc(sizeof(*q_chip), GFP_KERNEL);
+	if (!q_chip) {
+		dev_err(&spmi->dev, "%s: Can't allocate gpio_chip\n",
+								__func__);
+		return -ENOMEM;
+	}
+	q_chip->spmi = spmi;
+	dev_set_drvdata(&spmi->dev, q_chip);
+
+	mutex_lock(&qpnp_gpio_chips_lock);
+	list_add(&q_chip->chip_list, &qpnp_gpio_chips);
+	mutex_unlock(&qpnp_gpio_chips_lock);
+
+	/* first scan through nodes to find the range required for allocation */
+	for (i = 0; i < spmi->num_dev_node; i++) {
+		rc = of_property_read_u32(spmi->dev_node[i].of_node,
+							"qcom,gpio-num", &gpio);
+		if (rc) {
+			dev_err(&spmi->dev, "%s: unable to get"
+				" qcom,gpio-num property\n", __func__);
+			goto err_probe;
+		}
+
+		if (gpio < lowest_gpio)
+			lowest_gpio = gpio;
+		if (gpio > highest_gpio)
+			highest_gpio = gpio;
+	}
+
+	if (highest_gpio < lowest_gpio) {
+		dev_err(&spmi->dev, "%s: no device nodes specified in"
+					" topology\n", __func__);
+		rc = -EINVAL;
+		goto err_probe;
+	} else if (lowest_gpio == 0) {
+		dev_err(&spmi->dev, "%s: 0 is not a valid PMIC GPIO\n",
+								__func__);
+		rc = -EINVAL;
+		goto err_probe;
+	}
+
+	q_chip->pmic_gpio_lowest = lowest_gpio;
+	q_chip->pmic_gpio_highest = highest_gpio;
+
+	/* allocate gpio lookup tables */
+	q_chip->pmic_gpios = kzalloc(sizeof(struct qpnp_gpio_spec *) *
+						highest_gpio - lowest_gpio + 1,
+						GFP_KERNEL);
+	q_chip->chip_gpios = kzalloc(sizeof(struct qpnp_gpio_spec *) *
+						spmi->num_dev_node, GFP_KERNEL);
+	if (!q_chip->pmic_gpios || !q_chip->chip_gpios) {
+		dev_err(&spmi->dev, "%s: unable to allocate memory\n",
+								__func__);
+		rc = -ENOMEM;
+		goto err_probe;
+	}
+
+	/* get interrupt controller device_node */
+	q_chip->int_ctrl = of_irq_find_parent(spmi->dev.of_node);
+	if (!q_chip->int_ctrl) {
+		dev_err(&spmi->dev, "%s: Can't find interrupt parent\n",
+								__func__);
+		rc = -EINVAL;
+		goto err_probe;
+	}
+
+	/* now scan through again and populate the lookup table */
+	for (i = 0; i < spmi->num_dev_node; i++) {
+		res = qpnp_get_resource(spmi, i, IORESOURCE_MEM, 0);
+		if (!res) {
+			dev_err(&spmi->dev, "%s: node %s is missing has no"
+				" base address definition\n",
+				__func__, spmi->dev_node[i].of_node->full_name);
+		}
+
+		rc = of_property_read_u32(spmi->dev_node[i].of_node,
+							"qcom,gpio-num", &gpio);
+		if (rc) {
+			dev_err(&spmi->dev, "%s: unable to get"
+				" qcom,gpio-num property\n", __func__);
+			goto err_probe;
+		}
+
+		q_spec = kzalloc(sizeof(struct qpnp_gpio_spec),
+							GFP_KERNEL);
+		if (!q_spec) {
+			dev_err(&spmi->dev, "%s: unable to allocate"
+						" memory\n",
+					__func__);
+			rc = -ENOMEM;
+			goto err_probe;
+		}
+
+		q_spec->slave = spmi->sid;
+		q_spec->offset = res->start;
+		q_spec->gpio_chip_idx = i;
+		q_spec->pmic_gpio = gpio;
+		q_spec->node = spmi->dev_node[i].of_node;
+		q_spec->q_chip = q_chip;
+
+		rc = spmi_ext_register_readl(spmi->ctrl, q_spec->slave,
+				Q_REG_ADDR(q_spec, Q_REG_TYPE), &buf[0], 2);
+		if (rc) {
+			dev_err(&spmi->dev, "%s: unable to read type regs\n",
+						__func__);
+			goto err_probe;
+		}
+		q_spec->type	= buf[0];
+		q_spec->subtype = buf[1];
+
+		/* call into irq_domain to get irq mapping */
+		intspec[0] = q_chip->spmi->sid;
+		intspec[1] = (q_spec->offset >> 8) & 0xFF;
+		intspec[2] = 0;
+		q_spec->irq = irq_create_of_mapping(q_chip->int_ctrl,
+							intspec, 3);
+		if (!q_spec->irq) {
+			dev_err(&spmi->dev, "%s: invalid irq for gpio"
+					" %u\n", __func__, gpio);
+			rc = -EINVAL;
+			goto err_probe;
+		}
+		/* initialize lookup table params */
+		qpnp_pmic_gpio_set_spec(q_chip, gpio, q_spec);
+		qpnp_chip_gpio_set_spec(q_chip, i, q_spec);
+	}
+
+	q_chip->gpio_chip.base = -1;
+	q_chip->gpio_chip.ngpio = spmi->num_dev_node;
+	q_chip->gpio_chip.label = "qpnp-gpio";
+	q_chip->gpio_chip.direction_input = qpnp_gpio_direction_input;
+	q_chip->gpio_chip.direction_output = qpnp_gpio_direction_output;
+	q_chip->gpio_chip.to_irq = qpnp_gpio_to_irq;
+	q_chip->gpio_chip.get = qpnp_gpio_get;
+	q_chip->gpio_chip.set = qpnp_gpio_set;
+	q_chip->gpio_chip.dev = &spmi->dev;
+	q_chip->gpio_chip.of_xlate = qpnp_gpio_of_gpio_xlate;
+	q_chip->gpio_chip.of_gpio_n_cells = 2;
+	q_chip->gpio_chip.can_sleep = 0;
+
+	rc = gpiochip_add(&q_chip->gpio_chip);
+	if (rc) {
+		dev_err(&spmi->dev, "%s: Can't add gpio chip, rc = %d\n",
+								__func__, rc);
+		goto err_probe;
+	}
+
+	/* now configure gpio config defaults if they exist */
+	for (i = 0; i < spmi->num_dev_node; i++) {
+		q_spec = qpnp_chip_gpio_get_spec(q_chip, i);
+		if (WARN_ON(!q_spec)) {
+			rc = -ENODEV;
+			goto err_probe;
+		}
+
+		rc = qpnp_gpio_cache_regs(q_chip, q_spec);
+		if (rc)
+			goto err_probe;
+
+		rc = qpnp_gpio_apply_config(q_chip, q_spec);
+		if (rc)
+			goto err_probe;
+	}
+
+	dev_dbg(&spmi->dev, "%s: gpio_chip registered between %d-%u\n",
+			__func__, q_chip->gpio_chip.base,
+			(q_chip->gpio_chip.base + q_chip->gpio_chip.ngpio) - 1);
+
+	rc = qpnp_gpio_debugfs_create(q_chip);
+	if (rc) {
+		dev_err(&spmi->dev, "%s: debugfs creation failed\n", __func__);
+		goto err_probe;
+	}
+
+	return 0;
+
+err_probe:
+	qpnp_gpio_free_chip(q_chip);
+	return rc;
+}
+
+static int qpnp_gpio_remove(struct spmi_device *spmi)
+{
+	struct qpnp_gpio_chip *q_chip = dev_get_drvdata(&spmi->dev);
+
+	debugfs_remove_recursive(q_chip->dfs_dir);
+
+	return qpnp_gpio_free_chip(q_chip);
+}
+
+static struct of_device_id spmi_match_table[] = {
+	{	.compatible = "qcom,qpnp-gpio",
+	},
+	{}
+};
+
+static const struct spmi_device_id qpnp_gpio_id[] = {
+	{ "qcom,qpnp-gpio", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(spmi, qpnp_gpio_id);
+
+static struct spmi_driver qpnp_gpio_driver = {
+	.driver		= {
+		.name	= "qcom,qpnp-gpio",
+		.of_match_table = spmi_match_table,
+	},
+	.probe		= qpnp_gpio_probe,
+	.remove		= qpnp_gpio_remove,
+	.id_table	= qpnp_gpio_id,
+};
+
+static int __init qpnp_gpio_init(void)
+{
+#ifdef CONFIG_GPIO_QPNP_DEBUG
+	driver_dfs_dir = debugfs_create_dir("qpnp_gpio", NULL);
+	if (driver_dfs_dir == NULL)
+		pr_err("Cannot register top level debugfs directory\n");
+#endif
+
+	return spmi_driver_register(&qpnp_gpio_driver);
+}
+
+static void __exit qpnp_gpio_exit(void)
+{
+#ifdef CONFIG_GPIO_QPNP_DEBUG
+	debugfs_remove_recursive(driver_dfs_dir);
+#endif
+	spmi_driver_unregister(&qpnp_gpio_driver);
+}
+
+MODULE_DESCRIPTION("QPNP PMIC gpio driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(qpnp_gpio_init);
+module_exit(qpnp_gpio_exit);