[msm8660]: Add fastboot key detection for QT8660

Due to a hardware design bug, keypad controller in PM8058 could not be
used for key detection. Hence detecting it by configuring PM8058 GPIOs
as normal function and accessing it over SSBI

Change-Id: I92db69fe504598a6d1dfc0736642b1cb7a02be4c
diff --git a/dev/keys/gpio_keypad.c b/dev/keys/gpio_keypad.c
index d021a78..2ec7c5d 100644
--- a/dev/keys/gpio_keypad.c
+++ b/dev/keys/gpio_keypad.c
@@ -2,7 +2,7 @@
  * Copyright (c) 2009, Google Inc.
  * All rights reserved.
  *
- * Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -43,6 +43,8 @@
 #include <reg.h>
 #include <platform/iomap.h>
 
+#define LINUX_MACHTYPE_8660_QT      3298
+
 struct gpio_kp {
 	struct gpio_keypad_info *keypad_info;
 	struct timer timer;
@@ -455,7 +457,7 @@
 	return 0;
 }
 
-int pm8058_gpio_config_kypd_drv(int gpio_start, int num_gpios)
+int pm8058_gpio_config_kypd_drv(int gpio_start, int num_gpios, unsigned mach_id)
 {
 	int	rc;
 	struct pm8058_gpio kypd_drv = {
@@ -467,6 +469,9 @@
 		.inv_int_pol	= 1,
 	};
 
+	if(mach_id == LINUX_MACHTYPE_8660_QT)
+		kypd_drv.function = PM_GPIO_FUNC_NORMAL;
+
 	while (num_gpios--) {
 		rc = pm8058_gpio_config(gpio_start++, &kypd_drv);
 		if (rc) {
@@ -501,7 +506,7 @@
 	return 0;
 }
 
-void ssbi_gpio_init(void)
+static void ssbi_gpio_init(unsigned int mach_id)
 {
     unsigned char kypd_cntl_init;
     unsigned char kypd_scan_init = 0x20;
@@ -520,8 +525,16 @@
     if ((*wr_function)(&kypd_scan_init, 1, SSBI_REG_KYPD_SCAN_ADDR))
       dprintf (CRITICAL, "Error in initializing SSBI_REG_KYPD_SCAN register\n");
 
-    pm8058_gpio_config_kypd_sns(SSBI_OFFSET_ADDR_GPIO_KYPD_SNS, columns);
-    pm8058_gpio_config_kypd_drv(SSBI_OFFSET_ADDR_GPIO_KYPD_DRV,  rows);
+    if(mach_id == LINUX_MACHTYPE_8660_QT)
+    {
+        pm8058_gpio_config_kypd_sns(QT_PMIC_GPIO_KYPD_SNS, rows);
+        pm8058_gpio_config_kypd_drv(QT_PMIC_GPIO_KYPD_DRV,  columns, mach_id);
+    }
+    else
+    {
+        pm8058_gpio_config_kypd_sns(SSBI_OFFSET_ADDR_GPIO_KYPD_SNS, columns);
+        pm8058_gpio_config_kypd_drv(SSBI_OFFSET_ADDR_GPIO_KYPD_DRV,  rows, mach_id);
+    }
 }
 
 static enum handler_return
@@ -584,8 +597,54 @@
 
 }
 
+static enum handler_return
+scan_qt_keypad(struct timer *timer, time_t now, void *arg)
+{
+    unsigned int gpio;
+    unsigned int last_state=0;
+    unsigned int new_state=0;
+    unsigned int bits_changed;
+    static unsigned int key_detected=0;
+
+    /* Row GPIOs 8,9,10 are used for sensing here */
+    for(gpio=8;gpio<=10;gpio++)
+    {
+        bool status;
+        status = pm8058_gpio_get(gpio);
+        if(status == 0)
+            new_state |= (1<<(gpio-8));
+    }
+
+    bits_changed = last_state ^ new_state;
+
+    if(bits_changed)
+    {
+        unsigned int shift;
+        for(int rows=0;rows<(qwerty_keypad->keypad_info)->rows;rows++)
+        {
+            if((bits_changed & (1<<rows)) == 0)
+                continue;
+            shift = rows*8 + 3;
+        }
+        if ((qwerty_keypad->keypad_info)->keymap[shift])
+        {
+            if (shift != key_detected)
+            {
+                key_detected = shift;
+                keys_post_event((qwerty_keypad->keypad_info)->keymap[shift], 1);
+                timer_set_oneshot(timer,(qwerty_keypad->keypad_info)->poll_time,
+                                  scan_qt_keypad, NULL);
+                return INT_RESCHEDULE;
+            }
+        }
+    }
+    event_signal(&qwerty_keypad->full_scan, false);
+    return INT_RESCHEDULE;
+}
+
 void ssbi_keypad_init(struct qwerty_keypad_info  *qwerty_kp)
 {
+    unsigned int mach_id;
     int len;
 
     len = sizeof(struct gpio_qwerty_kp);
@@ -594,13 +653,25 @@
 
     memset(qwerty_keypad, 0, len);
     qwerty_keypad->keypad_info = qwerty_kp;
-    ssbi_gpio_init();
-
     qwerty_keypad->num_of_scans = 0;
 
     event_init(&qwerty_keypad->full_scan, false, EVENT_FLAG_AUTOUNSIGNAL);
     timer_initialize(&qwerty_keypad->timer);
-    timer_set_oneshot(&qwerty_keypad->timer, 0, scan_qwerty_keypad, NULL);
+
+#ifdef QT_8660_KEYPAD_HW_BUG
+    mach_id = board_machtype();
+#endif
+    ssbi_gpio_init(mach_id);
+
+    if(mach_id == LINUX_MACHTYPE_8660_QT)
+    {
+        mdelay((qwerty_keypad->keypad_info)->settle_time);
+#ifdef QT_8660_KEYPAD_HW_BUG
+        timer_set_oneshot(&qwerty_keypad->timer, 0, scan_qt_keypad, NULL);
+#endif
+    }
+    else
+        timer_set_oneshot(&qwerty_keypad->timer, 0, scan_qwerty_keypad, NULL);
 
     /* wait for the keypad to complete one full scan */
     event_wait(&qwerty_keypad->full_scan);
diff --git a/include/dev/gpio_keypad.h b/include/dev/gpio_keypad.h
index f467729..04093cc 100644
--- a/include/dev/gpio_keypad.h
+++ b/include/dev/gpio_keypad.h
@@ -2,7 +2,7 @@
  * Copyright (c) 2008, Google Inc.
  * All rights reserved.
  *
- * Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -112,8 +112,13 @@
 // GPIO configurations
 
 #define	SSBI_REG_ADDR_GPIO_BASE		0x150
+
+#define QT_PMIC_GPIO_KYPD_SNS           0x008
+#define QT_PMIC_GPIO_KYPD_DRV           0x003
+
 #define SSBI_OFFSET_ADDR_GPIO_KYPD_SNS  0x000
 #define SSBI_OFFSET_ADDR_GPIO_KYPD_DRV  0x008
+
 #define	SSBI_REG_ADDR_GPIO(n)		(SSBI_REG_ADDR_GPIO_BASE + n)
 
 #define	PM_GPIO_DIR_OUT			0x01
diff --git a/platform/msm8x60/pmic.c b/platform/msm8x60/pmic.c
index 1dd0c23..0da7291 100755
--- a/platform/msm8x60/pmic.c
+++ b/platform/msm8x60/pmic.c
@@ -1,5 +1,5 @@
 /*
- * * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ * * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -91,6 +91,21 @@
     return 0;
 }
 
+bool pm8058_gpio_get(unsigned int gpio)
+{
+	pm_sec_gpio_irq_id_type gpio_irq;
+	bool status;
+	int ret;
+
+	gpio_irq = gpio + PM_GPIO01_CHGED_ST_IRQ_ID;
+	ret = pm8058_get_gpio_status(gpio_irq, &status);
+
+	if(ret)
+		dprintf(CRITICAL,"pm8058_gpio_get failed\n");
+
+	return status;
+}
+
 /*PM8901*/
 extern int pa2_ssbi2_write_bytes(unsigned char *buffer, unsigned short length,
                                  unsigned short slave_addr);
diff --git a/platform/msm8x60/rules.mk b/platform/msm8x60/rules.mk
index bf5e923..e4adc5b 100644
--- a/platform/msm8x60/rules.mk
+++ b/platform/msm8x60/rules.mk
@@ -10,6 +10,8 @@
 DEFINES += WITH_CPU_EARLY_INIT=0 WITH_CPU_WARM_BOOT=0 \
 	   MMC_SLOT=$(MMC_SLOT) MDP4=1
 
+DEFINES += QT_8660_KEYPAD_HW_BUG=1
+
 INCLUDES += -I$(LOCAL_DIR)/include -I$(LK_TOP_DIR)/platform/msm_shared/include
 
 DEVS += fbcon
diff --git a/target/msm8660_surf/init.c b/target/msm8660_surf/init.c
index f63b842..3f8616b 100644
--- a/target/msm8660_surf/init.c
+++ b/target/msm8660_surf/init.c
@@ -87,8 +87,10 @@
 	unsigned hw_platform = 0;
 	unsigned fused_chip = 0;
 	unsigned platform_subtype = 0;
-	unsigned mach_id = LINUX_MACHTYPE_8660_FFA;
+	static unsigned mach_id = -1;
 
+	if(mach_id != -1)
+		return mach_id;
 	/* Detect external msm if this is a "fusion" */
 	smem_status = smem_read_alloc_entry_offset(SMEM_BOARD_INFO_LOCATION,
 					&format, sizeof(format), 0);
diff --git a/target/msm8660_surf/keypad.c b/target/msm8660_surf/keypad.c
index a83bde3..adcb10e 100644
--- a/target/msm8660_surf/keypad.c
+++ b/target/msm8660_surf/keypad.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -30,12 +30,30 @@
 #include <dev/keys.h>
 #include <dev/gpio_keypad.h>
 
+#define LINUX_MACHTYPE_8660_QT      3298
 #define BITS_IN_ELEMENT(x) (sizeof(x)[0] * 8)
+#define KEYMAP_INDEX(row, col) (row)* BITS_IN_ELEMENT(qwerty_keys_new) + (col)
 
 static unsigned char qwerty_keys_old[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 static unsigned char qwerty_keys_new[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 
-#define KEYMAP_INDEX(row, col) (row)* BITS_IN_ELEMENT(qwerty_keys_new) + (col)
+static unsigned int qt_keymap[] = {
+    [KEYMAP_INDEX(0, 3)] = KEY_BACK,          /* Volume down key */
+    [KEYMAP_INDEX(1, 3)] = KEY_HOME,          /* Volume up key */
+};
+
+static struct qwerty_keypad_info qt_keypad = {
+    .keymap         = qt_keymap,
+    .old_keys       = qwerty_keys_old,
+    .rec_keys       = qwerty_keys_new,
+    .rows           = 3,
+    .columns        = 1,
+    .num_of_reads   = 3,
+    .rd_func        = &pa1_ssbi2_read_bytes,
+    .wr_func        = &pa1_ssbi2_write_bytes,
+    .settle_time    = 32 /* msec */,
+    .poll_time	    = 10 /* msec */,
+};
 
 static unsigned int qwerty_keymap[] = {
     [KEYMAP_INDEX(1, 3)] = KEY_BACK,          /* Volume down key */
@@ -57,5 +75,12 @@
 
 void keypad_init(void)
 {
-    ssbi_keypad_init(&qwerty_keypad);
+    unsigned int mach_id;
+
+    mach_id = board_machtype();
+
+    if(mach_id == LINUX_MACHTYPE_8660_QT)
+        ssbi_keypad_init(&qt_keypad);
+    else
+        ssbi_keypad_init(&qwerty_keypad);
 }