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",
+ ¶m.direction);
+ of_property_read_u32(node, "qcom,output-type",
+ ¶m.output_type);
+ of_property_read_u32(node, "qcom,invert",
+ ¶m.invert);
+ of_property_read_u32(node, "qcom,pull",
+ ¶m.pull);
+ of_property_read_u32(node, "qcom,vin-sel",
+ ¶m.vin_sel);
+ of_property_read_u32(node, "qcom,out-strength",
+ ¶m.out_strength);
+ of_property_read_u32(node, "qcom,src-select",
+ ¶m.src_select);
+ rc = of_property_read_u32(node, "qcom,master-en",
+ ¶m.master_en);
+
+ rc = _qpnp_gpio_config(q_chip, q_spec, ¶m);
+
+ 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);