gpio: msm: Add v3 gpio support for copper
Beginning with copper, the gpio TLMM register and interrupt
mappings have changed. Add a new version to support the new
mappings.
Change-Id: I5c8bd148dd6ac38aa59448c9aab98e04183e14f1
Signed-off-by: Sathish Ambley <sambley@codeaurora.org>
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index cbdb51a..a2cba84 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -227,7 +227,7 @@
config ARCH_MSMCOPPER
bool "MSM Copper"
select ARCH_MSM_KRAITMP
- select GPIO_MSM_V2
+ select GPIO_MSM_V3
select ARM_GIC
select CPU_V7
select MSM_SCM if SMP
diff --git a/arch/arm/mach-msm/include/mach/gpio.h b/arch/arm/mach-msm/include/mach/gpio.h
index 8aed079..1d32003 100644
--- a/arch/arm/mach-msm/include/mach/gpio.h
+++ b/arch/arm/mach-msm/include/mach/gpio.h
@@ -178,7 +178,7 @@
TLMM_PULL_SDC1_DATA,
};
-#ifdef CONFIG_GPIO_MSM_V2
+#if defined(CONFIG_GPIO_MSM_V2) || defined(CONFIG_GPIO_MSM_V3)
void msm_tlmm_set_hdrive(enum msm_tlmm_hdrive_tgt tgt, int drv_str);
void msm_tlmm_set_pull(enum msm_tlmm_pull_tgt tgt, int pull);
diff --git a/arch/arm/mach-msm/include/mach/irqs-copper.h b/arch/arm/mach-msm/include/mach/irqs-copper.h
index 6d27d69..8cd8620 100644
--- a/arch/arm/mach-msm/include/mach/irqs-copper.h
+++ b/arch/arm/mach-msm/include/mach/irqs-copper.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* 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
@@ -35,7 +35,7 @@
#define SPS_BAM_DMA_IRQ (GIC_SPI_START + 105)
#define NR_MSM_IRQS 1020 /* Should be 256 - but higher due to bug in sim */
-#define NR_GPIO_IRQS 156
+#define NR_GPIO_IRQS 146
#define NR_QPNP_IRQS 32768 /* SPARSE_IRQ is required to support this */
#define NR_BOARD_IRQS NR_QPNP_IRQS
#define NR_TLMM_MSM_DIR_CONN_IRQ 8
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index ef077a5..0dcf1a4 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -124,6 +124,15 @@
Qualcomm MSM chips. Most of the pins on the MSM can be
selected for GPIO, and are controlled by this driver.
+config GPIO_MSM_V3
+ tristate "Qualcomm MSM GPIO v3"
+ depends on GPIOLIB && ARCH_MSM
+ help
+ Say yes here to support the GPIO interface on ARM v7 based
+ Qualcomm MSM chips for v3 version of the interface. 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
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index af5fd38..d15b628 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-common.o gpio-msm-v2.o
+obj-$(CONFIG_GPIO_MSM_V3) += gpio-msm-common.o gpio-msm-v3.o
obj-$(CONFIG_GPIO_FSM9XXX) += gpio-fsm9xxx.o
obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o
obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o
diff --git a/drivers/gpio/gpio-msm-v3.c b/drivers/gpio/gpio-msm-v3.c
new file mode 100644
index 0000000..49ad517
--- /dev/null
+++ b/drivers/gpio/gpio-msm-v3.c
@@ -0,0 +1,217 @@
+/* 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.
+ *
+ */
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+
+#include <mach/msm_iomap.h>
+#include <mach/gpiomux.h>
+#include "gpio-msm-common.h"
+
+/* Bits of interest in the GPIO_IN_OUT register.
+ */
+enum {
+ GPIO_IN_BIT = 0,
+ GPIO_OUT_BIT = 1
+};
+
+/* Bits of interest in the GPIO_INTR_STATUS register.
+ */
+enum {
+ INTR_STATUS_BIT = 0,
+};
+
+/* Bits of interest in the GPIO_CFG register.
+ */
+enum {
+ 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 = 4,
+ INTR_TARGET_PROC_BIT = 5,
+ INTR_DIR_CONN_EN_BIT = 8,
+};
+
+/*
+ * 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_GPIO_SEL_BIT = 0,
+ DC_POLARITY_BIT = 8,
+};
+
+/*
+ * When a GPIO triggers, two separate decisions are made, controlled
+ * by two separate flags.
+ *
+ * - First, INTR_RAW_STATUS_EN controls whether or not the GPIO_INTR_STATUS
+ * register for that GPIO will be updated to reflect the triggering of that
+ * gpio. If this bit is 0, this register will not be updated.
+ * - Second, INTR_ENABLE controls whether an interrupt is triggered.
+ *
+ * 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.
+ */
+#define INTR_RAW_STATUS_EN BIT(INTR_RAW_STATUS_EN_BIT)
+#define INTR_ENABLE BIT(INTR_ENABLE_BIT)
+#define INTR_POL_CTL_HI BIT(INTR_POL_CTL_BIT)
+#define INTR_DIR_CONN_EN BIT(INTR_DIR_CONN_EN_BIT)
+#define DC_POLARITY_HI BIT(DC_POLARITY_BIT)
+
+#define INTR_TARGET_PROC_APPS (4 << INTR_TARGET_PROC_BIT)
+#define INTR_TARGET_PROC_NONE (7 << INTR_TARGET_PROC_BIT)
+
+#define INTR_DECT_CTL_LEVEL (0 << INTR_DECT_CTL_BIT)
+#define INTR_DECT_CTL_POS_EDGE (1 << INTR_DECT_CTL_BIT)
+#define INTR_DECT_CTL_NEG_EDGE (2 << INTR_DECT_CTL_BIT)
+#define INTR_DECT_CTL_DUAL_EDGE (3 << INTR_DECT_CTL_BIT)
+#define INTR_DECT_CTL_MASK (3 << INTR_DECT_CTL_BIT)
+
+#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)))
+#define GPIO_INTR_STATUS(gpio) (MSM_TLMM_BASE + 0x100c + (0x10 * (gpio)))
+#define GPIO_DIR_CONN_INTR(intr) (MSM_TLMM_BASE + 0x2800 + (0x04 * (intr)))
+
+static inline void set_gpio_bits(unsigned n, void __iomem *reg)
+{
+ __raw_writel(__raw_readl(reg) | n, reg);
+}
+
+static inline void clr_gpio_bits(unsigned n, void __iomem *reg)
+{
+ __raw_writel(__raw_readl(reg) & ~n, reg);
+}
+
+unsigned __msm_gpio_get_inout(unsigned gpio)
+{
+ return __raw_readl(GPIO_IN_OUT(gpio)) & BIT(GPIO_IN_BIT);
+}
+
+void __msm_gpio_set_inout(unsigned gpio, unsigned val)
+{
+ __raw_writel(val ? BIT(GPIO_OUT_BIT) : 0, GPIO_IN_OUT(gpio));
+}
+
+void __msm_gpio_set_config_direction(unsigned gpio, int input, int val)
+{
+ if (input) {
+ clr_gpio_bits(BIT(GPIO_OE_BIT), GPIO_CONFIG(gpio));
+ } else {
+ __msm_gpio_set_inout(gpio, val);
+ set_gpio_bits(BIT(GPIO_OE_BIT), GPIO_CONFIG(gpio));
+ }
+}
+
+void __msm_gpio_set_polarity(unsigned gpio, unsigned val)
+{
+ if (val)
+ clr_gpio_bits(INTR_POL_CTL_HI, GPIO_INTR_CFG(gpio));
+ else
+ set_gpio_bits(INTR_POL_CTL_HI, GPIO_INTR_CFG(gpio));
+}
+
+unsigned __msm_gpio_get_intr_status(unsigned gpio)
+{
+ return __raw_readl(GPIO_INTR_STATUS(gpio)) &
+ BIT(INTR_STATUS_BIT);
+}
+
+void __msm_gpio_set_intr_status(unsigned gpio)
+{
+ __raw_writel(BIT(INTR_STATUS_BIT), GPIO_INTR_STATUS(gpio));
+}
+
+unsigned __msm_gpio_get_intr_config(unsigned gpio)
+{
+ return __raw_readl(GPIO_INTR_CFG(gpio));
+}
+
+void __msm_gpio_set_intr_cfg_enable(unsigned gpio, unsigned val)
+{
+ unsigned cfg;
+
+ cfg = __raw_readl(GPIO_INTR_CFG(gpio));
+ if (val) {
+ cfg &= ~(INTR_TARGET_PROC_NONE | INTR_DIR_CONN_EN);
+ cfg |= INTR_RAW_STATUS_EN | INTR_ENABLE | INTR_TARGET_PROC_APPS;
+ } else {
+ cfg &= ~(INTR_TARGET_PROC_APPS | INTR_RAW_STATUS_EN |
+ INTR_ENABLE);
+ cfg |= INTR_TARGET_PROC_NONE;
+ }
+ __raw_writel(cfg, GPIO_INTR_CFG(gpio));
+}
+
+void __msm_gpio_set_intr_cfg_type(unsigned gpio, unsigned type)
+{
+ unsigned cfg;
+
+ cfg = __raw_readl(GPIO_INTR_CFG(gpio));
+ cfg &= ~INTR_DECT_CTL_MASK;
+ if (type == IRQ_TYPE_EDGE_RISING)
+ cfg |= INTR_DECT_CTL_POS_EDGE;
+ else if (type == IRQ_TYPE_EDGE_FALLING)
+ cfg |= INTR_DECT_CTL_NEG_EDGE;
+ else if (type == IRQ_TYPE_EDGE_BOTH)
+ cfg |= INTR_DECT_CTL_DUAL_EDGE;
+ else
+ cfg |= INTR_DECT_CTL_LEVEL;
+
+ if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_HIGH))
+ cfg |= INTR_POL_CTL_HI;
+ else
+ cfg &= ~INTR_POL_CTL_HI;
+
+ __raw_writel(cfg, GPIO_INTR_CFG(gpio));
+}
+
+void __gpio_tlmm_config(unsigned config)
+{
+ unsigned flags;
+ unsigned gpio = GPIO_PIN(config);
+
+ 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));
+}
+
+void __msm_gpio_install_direct_irq(unsigned gpio, unsigned irq,
+ unsigned int input_polarity)
+{
+ unsigned cfg;
+
+ set_gpio_bits(BIT(GPIO_OE_BIT), GPIO_CONFIG(gpio));
+ cfg = __raw_readl(GPIO_INTR_CFG(gpio));
+ cfg &= ~(INTR_TARGET_PROC_NONE | INTR_RAW_STATUS_EN | INTR_ENABLE);
+ cfg |= INTR_TARGET_PROC_APPS | INTR_DIR_CONN_EN;
+ __raw_writel(cfg, GPIO_INTR_CFG(gpio));
+
+ cfg = gpio;
+ if (input_polarity)
+ cfg |= DC_POLARITY_HI;
+ __raw_writel(cfg, GPIO_DIR_CONN_INTR(irq));
+}