USB: EHCI: msm: Configure GPIOs for HSIC strobe and data lines

As part of HSIC initialization, in addition to enabling HSIC host
mode and periodic pad calibration, need to configure GPIO150 and
GPIO151 pins for HSIC strobe and data lines respectively for
proper data communication between HSIC devices.

Change-Id: I7cdf8e4532045d813ac6c6e3f53455d0a640245b
Signed-off-by: Vijayavardhan Vennapusa <vvreddy@codeaurora.org>
diff --git a/arch/arm/mach-msm/board-msm8960.c b/arch/arm/mach-msm/board-msm8960.c
index 1c277c8..2615cd4 100644
--- a/arch/arm/mach-msm/board-msm8960.c
+++ b/arch/arm/mach-msm/board-msm8960.c
@@ -711,6 +711,37 @@
 	},
 };
 
+#ifdef CONFIG_USB_EHCI_MSM_HSIC
+static struct gpiomux_setting hsic_act_cfg = {
+	.func = GPIOMUX_FUNC_1,
+	.drv = GPIOMUX_DRV_12MA,
+	.pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting hsic_sus_cfg = {
+	.func = GPIOMUX_FUNC_GPIO,
+	.drv = GPIOMUX_DRV_2MA,
+	.pull = GPIOMUX_PULL_DOWN,
+};
+
+static struct msm_gpiomux_config msm8960_hsic_configs[] = {
+	{
+		.gpio = 150,               /*HSIC_STROBE */
+		.settings = {
+			[GPIOMUX_ACTIVE] = &hsic_act_cfg,
+			[GPIOMUX_SUSPENDED] = &hsic_sus_cfg,
+		},
+	},
+	{
+		.gpio = 151,               /* HSIC_DATA */
+		.settings = {
+			[GPIOMUX_ACTIVE] = &hsic_act_cfg,
+			[GPIOMUX_SUSPENDED] = &hsic_sus_cfg,
+		},
+	},
+};
+#endif
+
 #if defined(CONFIG_GPIO_SX150X) || defined(CONFIG_GPIO_SX150X_MODULE)
 enum {
 	SX150X_CAM,
@@ -2545,6 +2576,11 @@
 	msm_gpiomux_install(wcnss_5wire_interface,
 			ARRAY_SIZE(wcnss_5wire_interface));
 
+#ifdef CONFIG_USB_EHCI_MSM_HSIC
+	msm_gpiomux_install(msm8960_hsic_configs,
+			ARRAY_SIZE(msm8960_hsic_configs));
+#endif
+
 	return 0;
 }
 
@@ -2972,6 +3008,15 @@
 };
 #endif
 
+#ifdef CONFIG_USB_EHCI_MSM_HSIC
+static struct msm_hsic_host_platform_data msm_hsic_pdata = {
+	.strobe		= 150,
+	.data		= 151,
+};
+#else
+static struct msm_hsic_host_platform_data msm_hsic_pdata;
+#endif
+
 #define PID_MAGIC_ID		0x71432909
 #define SERIAL_NUM_MAGIC_ID	0x61945374
 #define SERIAL_NUMBER_LENGTH	127
@@ -4434,6 +4479,7 @@
 	msm8960_device_otg.dev.platform_data = &msm_otg_pdata;
 	msm8960_device_gadget_peripheral.dev.parent = &msm8960_device_otg.dev;
 	msm_device_hsusb_host.dev.parent = &msm8960_device_otg.dev;
+	msm_device_hsic_host.dev.platform_data = &msm_hsic_pdata;
 	gpiomux_init();
 	if (machine_is_msm8960_cdp())
 		fpga_init();
diff --git a/drivers/usb/host/ehci-msm-hsic.c b/drivers/usb/host/ehci-msm-hsic.c
index 7a15348..1b8b5d6 100644
--- a/drivers/usb/host/ehci-msm-hsic.c
+++ b/drivers/usb/host/ehci-msm-hsic.c
@@ -30,7 +30,8 @@
 #include <linux/regulator/consumer.h>
 
 #include <linux/usb/msm_hsusb_hw.h>
-
+#include <linux/usb/msm_hsusb.h>
+#include <linux/gpio.h>
 #include <mach/clk.h>
 #include <mach/msm_iomap.h>
 
@@ -144,6 +145,44 @@
 	return 0;
 }
 
+static int msm_hsic_config_gpios(struct msm_hsic_hcd *mehci, int gpio_en)
+{
+	int rc = 0;
+	struct msm_hsic_host_platform_data *pdata;
+
+	pdata = mehci->dev->platform_data;
+	/*
+	 * In future versions, dedicated lines might be used for HSIC
+	 * strobe and data instead of gpios. Hence returning zero value.
+	 */
+	if (!pdata->strobe || !pdata->data)
+		return rc;
+
+	if (!gpio_en)
+		goto free_gpio;
+
+	rc = gpio_request(pdata->strobe, "HSIC_STROBE_GPIO");
+	if (rc < 0) {
+		dev_err(mehci->dev, "gpio request failed for HSIC STROBE\n");
+		return rc;
+	}
+
+	rc = gpio_request(pdata->data, "HSIC_DATA_GPIO");
+	if (rc < 0) {
+		dev_err(mehci->dev, "gpio request failed for HSIC DATA\n");
+		goto free_strobe;
+		}
+
+	return 0;
+
+free_gpio:
+	gpio_free(pdata->data);
+free_strobe:
+	gpio_free(pdata->strobe);
+
+	return rc;
+}
+
 static int msm_hsic_phy_clk_reset(struct msm_hsic_hcd *mehci)
 {
 	int ret;
@@ -186,6 +225,12 @@
 	return 0;
 }
 
+#define HSIC_GPIO150_PAD_CTL   (MSM_TLMM_BASE+0x20C0)
+#define HSIC_GPIO151_PAD_CTL   (MSM_TLMM_BASE+0x20C4)
+#define HSIC_CAL_PAD_CTL       (MSM_TLMM_BASE+0x20C8)
+#define HSIC_LV_MODE		0x04
+#define HSIC_PAD_CALIBRATION	0xA8
+#define HSIC_GPIO_PAD_VAL	0x0A0AAA10
 #define LINK_RESET_TIMEOUT_USEC		(250 * 1000)
 static int msm_hsic_reset(struct msm_hsic_hcd *mehci)
 {
@@ -216,10 +261,28 @@
 	msleep(100);
 
 	/* HSIC PHY Initialization */
+	/* Enable LV_MODE in HSIC_CAL_PAD_CTL register */
+	writel_relaxed(HSIC_LV_MODE, HSIC_CAL_PAD_CTL);
+
 	/*set periodic calibration interval to ~2.048sec in HSIC_IO_CAL_REG */
 	ulpi_write(mehci, 0xFF, 0x33);
-	/*enable periodic IO calibration,HSIC host mode in HSIC_CFG register*/
-	ulpi_write(mehci, 0xA9, 0x30);
+
+	/* Enable periodic IO calibration in HSIC_CFG register */
+	ulpi_write(mehci, HSIC_PAD_CALIBRATION, 0x30);
+
+	/* Configure GPIO 150/151 pins for HSIC functionality mode */
+	ret = msm_hsic_config_gpios(mehci, 1);
+	if (ret) {
+		dev_err(mehci->dev, " gpio configuarion failed\n");
+		return ret;
+	}
+
+	/* Set LV_MODE=0x1 and DCC=0x2 in HSIC_GPIO150/151_PAD_CTL register */
+	writel_relaxed(HSIC_GPIO_PAD_VAL, HSIC_GPIO150_PAD_CTL);
+	writel_relaxed(HSIC_GPIO_PAD_VAL, HSIC_GPIO151_PAD_CTL);
+
+	/* Enable HSIC mode in HSIC_CFG register */
+	ulpi_write(mehci, 0x01, 0x31);
 
 	return 0;
 }
@@ -256,6 +319,7 @@
 
 	if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) {
 		dev_err(mehci->dev, "Unable to suspend PHY\n");
+		msm_hsic_config_gpios(mehci, 0);
 		msm_hsic_reset(mehci);
 		enable_irq(hcd->irq);
 		return -ETIMEDOUT;
@@ -333,6 +397,7 @@
 		 * PHY to make hsic working.
 		 */
 		dev_err(mehci->dev, "Unable to resume USB. Reset the hsic\n");
+		msm_hsic_config_gpios(mehci, 0);
 		msm_hsic_reset(mehci);
 	}
 
@@ -593,7 +658,7 @@
 	ret = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
 	if (ret) {
 		dev_err(&pdev->dev, "unable to register HCD\n");
-		goto deinit_vddcx;
+		goto unconfig_gpio;
 	}
 
 	device_init_wakeup(&pdev->dev, 1);
@@ -609,6 +674,8 @@
 
 	return 0;
 
+unconfig_gpio:
+	msm_hsic_config_gpios(mehci, 0);
 deinit_vddcx:
 	msm_hsic_init_vddcx(mehci, 0);
 deinit_clocks:
@@ -631,6 +698,7 @@
 	pm_runtime_set_suspended(&pdev->dev);
 
 	usb_remove_hcd(hcd);
+	msm_hsic_config_gpios(mehci, 0);
 	msm_hsic_init_vddcx(mehci, 0);
 
 	msm_hsic_init_clocks(mehci, 0);
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index 1e158f5..d776718 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -237,4 +237,9 @@
 #define PHY_OTG_COMP_DISABLED		BIT(2)
 };
 
+struct msm_hsic_host_platform_data {
+	unsigned strobe;
+	unsigned data;
+};
+
 #endif