Initial Contribution

msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142

Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/arch/arm/mach-msm/gpio-v2.c b/arch/arm/mach-msm/gpio-v2.c
index cc9c4fd..d5d6b92 100644
--- a/arch/arm/mach-msm/gpio-v2.c
+++ b/arch/arm/mach-msm/gpio-v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-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
@@ -9,50 +9,118 @@
  * 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 <asm/mach/irq.h>
 
 #include <mach/msm_iomap.h>
 #include "gpiomux.h"
+#include "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 +132,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 +149,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 +162,9 @@
  */
 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);
 };
 
 static DEFINE_SPINLOCK(tlmm_lock);
@@ -117,22 +176,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 +203,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 +217,22 @@
 
 	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;
 }
 
+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);
+}
+
 static int msm_gpio_request(struct gpio_chip *chip, unsigned offset)
 {
 	return msm_gpiomux_get(chip->base + offset);
@@ -168,20 +243,10 @@
 	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 = {
 		.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 +257,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 +289,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 +335,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 +358,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 +380,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 +420,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 +445,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,16 +467,17 @@
 	.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)
+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;
@@ -373,61 +489,190 @@
 		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);
+
 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");