gpio: gpio-msm-v2/v3: Fix spurious interrupts when gpio-irq is unmasked

To unmask a gpio interrupt, the gpio driver sets the
INTR_RAW_STATUS_EN and INTR_ENABLE bits in the INTR_CFG register.
As soon as the INTR_RAW_STATUS_EN is set, the INTR_STATUS is updated
and this causes a spurious interrupt when the irq is enabled.
This is noticed frequently when the gpio is pulled high and requests
a rising edge interrupt or pulled low and is requesting a falling edge
interrupt.
Due the internal circuit design of the TLMM IRQ block, the above method
causes a spurious interrupt when the irq was initialized.
Hence, to avoid this behavior we set the INTR_RAW_STATUS_EN, clear the
INTR_STATUS only once during setup. Every mask and unmask only toggles
the INTR_ENABLE bit.

CRs-Fixed: 346861
Change-Id: I1c9852ed91432582c3d050ccf933053fd368b216
Signed-off-by: Rohit Vaswani <rvaswani@codeaurora.org>
Signed-off-by: Taniya Das <tdas@codeaurora.org>
diff --git a/drivers/gpio/gpio-msm-v2.c b/drivers/gpio/gpio-msm-v2.c
index fb43562..bac9f8a 100644
--- a/drivers/gpio/gpio-msm-v2.c
+++ b/drivers/gpio/gpio-msm-v2.c
@@ -12,6 +12,7 @@
  */
 #include <linux/bitmap.h>
 #include <linux/bitops.h>
+#include <linux/delay.h>
 #include <linux/gpio.h>
 #include <linux/init.h>
 #include <linux/io.h>
@@ -147,14 +148,12 @@
 void __msm_gpio_set_intr_cfg_enable(unsigned gpio, unsigned val)
 {
 	if (val) {
-		set_gpio_bits(INTR_RAW_STATUS_EN | INTR_ENABLE,
-				GPIO_INTR_CFG(gpio));
+		set_gpio_bits(INTR_ENABLE, GPIO_INTR_CFG(gpio));
 		__raw_writel(TARGET_PROC_SCORPION, GPIO_INTR_CFG_SU(gpio));
 
 	} else {
 		__raw_writel(TARGET_PROC_NONE, GPIO_INTR_CFG_SU(gpio));
-		clr_gpio_bits(INTR_RAW_STATUS_EN | INTR_ENABLE,
-				GPIO_INTR_CFG(gpio));
+		clr_gpio_bits(INTR_ENABLE, GPIO_INTR_CFG(gpio));
 	}
 }
 
@@ -173,7 +172,23 @@
 	else
 		cfg &= ~INTR_POL_CTL_HI;
 
+	/* RAW_STATUS_EN is left on for all gpio irqs. Due to the
+	 * internal circuitry of TLMM, toggling the RAW_STATUS
+	 * could cause the INTR_STATUS to be set for EDGE interrupts.
+	 */
+	cfg |= INTR_RAW_STATUS_EN;
 	__raw_writel(cfg, GPIO_INTR_CFG(gpio));
+
+	/* Sometimes it might take a little while to update
+	 * the interrupt status after the RAW_STATUS is enabled
+	 */
+	udelay(5);
+
+	/* Clear the interrupt status to clear out any spurious
+	 * irq as a result of the above operation
+	 */
+	__msm_gpio_set_intr_status(gpio);
+
 }
 
 void __gpio_tlmm_config(unsigned config)
diff --git a/drivers/gpio/gpio-msm-v3.c b/drivers/gpio/gpio-msm-v3.c
index 49ad517..6086de3 100644
--- a/drivers/gpio/gpio-msm-v3.c
+++ b/drivers/gpio/gpio-msm-v3.c
@@ -12,6 +12,7 @@
  */
 #include <linux/bitmap.h>
 #include <linux/bitops.h>
+#include <linux/delay.h>
 #include <linux/gpio.h>
 #include <linux/init.h>
 #include <linux/io.h>
@@ -155,10 +156,9 @@
 	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;
+		cfg |= INTR_ENABLE | INTR_TARGET_PROC_APPS;
 	} else {
-		cfg &= ~(INTR_TARGET_PROC_APPS | INTR_RAW_STATUS_EN |
-			INTR_ENABLE);
+		cfg &= ~(INTR_TARGET_PROC_APPS | INTR_ENABLE);
 		cfg |= INTR_TARGET_PROC_NONE;
 	}
 	__raw_writel(cfg, GPIO_INTR_CFG(gpio));
@@ -184,7 +184,22 @@
 	else
 		cfg &= ~INTR_POL_CTL_HI;
 
+	/* RAW_STATUS_EN is left on for all gpio irqs. Due to the
+	 * internal circuitry of TLMM, toggling the RAW_STATUS
+	 * could cause the INTR_STATUS to be set for EDGE interrupts.
+	 */
+	cfg |= INTR_RAW_STATUS_EN;
 	__raw_writel(cfg, GPIO_INTR_CFG(gpio));
+
+	/* Sometimes it might take a little while to update
+	 * the interrupt status after the RAW_STATUS is enabled
+	 */
+	udelay(5);
+
+	/* Clear the interrupt status to clear out any spurious
+	 * irq as a result of the above operation
+	 */
+	__msm_gpio_set_intr_status(gpio);
 }
 
 void __gpio_tlmm_config(unsigned config)