Merge "USB: EHCI: msm: Add support for EHCI based Host Controller" into msm-3.0
diff --git a/arch/arm/mach-msm/board-8064-regulator.c b/arch/arm/mach-msm/board-8064-regulator.c
index 3f3359b..0d12bb5 100644
--- a/arch/arm/mach-msm/board-8064-regulator.c
+++ b/arch/arm/mach-msm/board-8064-regulator.c
@@ -42,8 +42,6 @@
 VREG_CONSUMERS(L4) = {
 	REGULATOR_SUPPLY("8921_l4",		NULL),
 	REGULATOR_SUPPLY("HSUSB_1p8",		"msm_otg"),
-	REGULATOR_SUPPLY("HSUSB_1p8",		"msm_ehci_host.0"),
-	REGULATOR_SUPPLY("HSUSB_1p8",		"msm_ehci_host.1"),
 	REGULATOR_SUPPLY("iris_vddxo",		"wcnss_wlan.0"),
 };
 VREG_CONSUMERS(L5) = {
@@ -104,7 +102,8 @@
 	REGULATOR_SUPPLY("8921_l23",		NULL),
 	REGULATOR_SUPPLY("pll_vdd",		"pil_qdsp6v4.1"),
 	REGULATOR_SUPPLY("pll_vdd",		"pil_qdsp6v4.2"),
-
+	REGULATOR_SUPPLY("HSUSB_1p8",		"msm_ehci_host.0"),
+	REGULATOR_SUPPLY("HSUSB_1p8",		"msm_ehci_host.1"),
 };
 VREG_CONSUMERS(L24) = {
 	REGULATOR_SUPPLY("8921_l24",		NULL),
@@ -219,6 +218,7 @@
 };
 VREG_CONSUMERS(EXT_5V) = {
 	REGULATOR_SUPPLY("ext_5v",		NULL),
+	REGULATOR_SUPPLY("vbus",		"msm_ehci_host.0"),
 };
 VREG_CONSUMERS(EXT_MPP8) = {
 	REGULATOR_SUPPLY("ext_mpp8",		NULL),
diff --git a/arch/arm/mach-msm/board-8064.c b/arch/arm/mach-msm/board-8064.c
index fbc9bf5..8e7fb1a 100644
--- a/arch/arm/mach-msm/board-8064.c
+++ b/arch/arm/mach-msm/board-8064.c
@@ -475,6 +475,19 @@
 	.power_budget		= 750,
 };
 
+static struct msm_usb_host_platform_data msm_ehci_host_pdata = {
+	.power_budget = 500,
+};
+
+static void __init apq8064_ehci_host_init(void)
+{
+	if (machine_is_apq8064_liquid()) {
+		apq8064_device_ehci_host3.dev.platform_data =
+				&msm_ehci_host_pdata;
+		platform_device_register(&apq8064_device_ehci_host3);
+	}
+}
+
 #define TABLA_INTERRUPT_BASE (NR_MSM_IRQS + NR_GPIO_IRQS + NR_PM8921_IRQS)
 
 /* Micbias setting is based on 8660 CDP/MTP/FLUID requirement
@@ -1861,6 +1874,7 @@
 	if (machine_is_apq8064_liquid())
 		msm_otg_pdata.mhl_enable = true;
 	apq8064_device_otg.dev.platform_data = &msm_otg_pdata;
+	apq8064_ehci_host_init();
 	apq8064_init_buses();
 	platform_add_devices(common_devices, ARRAY_SIZE(common_devices));
 	if (machine_is_apq8064_mtp()) {
diff --git a/arch/arm/mach-msm/devices-8064.c b/arch/arm/mach-msm/devices-8064.c
index dbca6ca..ca85a0a 100644
--- a/arch/arm/mach-msm/devices-8064.c
+++ b/arch/arm/mach-msm/devices-8064.c
@@ -68,6 +68,10 @@
 #define MSM_HSUSB1_PHYS		0x12500000
 #define MSM_HSUSB1_SIZE		SZ_4K
 
+/* Address of HS USB3 */
+#define MSM_HSUSB3_PHYS		0x12520000
+#define MSM_HSUSB3_SIZE		SZ_4K
+
 static struct msm_watchdog_pdata msm_watchdog_pdata = {
 	.pet_time = 10000,
 	.bark_time = 11000,
@@ -650,6 +654,30 @@
 	},
 };
 
+static struct resource resources_ehci_host3[] = {
+{
+		.start  = MSM_HSUSB3_PHYS,
+		.end    = MSM_HSUSB3_PHYS + MSM_HSUSB3_SIZE - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+	{
+		.start  = USB3_HS_IRQ,
+		.end    = USB3_HS_IRQ,
+		.flags  = IORESOURCE_IRQ,
+	},
+};
+
+struct platform_device apq8064_device_ehci_host3 = {
+	.name           = "msm_ehci_host",
+	.id             = 0,
+	.num_resources  = ARRAY_SIZE(resources_ehci_host3),
+	.resource       = resources_ehci_host3,
+	.dev            = {
+		.dma_mask               = &dma_mask,
+		.coherent_dma_mask      = 0xffffffff,
+	},
+};
+
 /* MSM Video core device */
 #ifdef CONFIG_MSM_BUS_SCALING
 static struct msm_bus_vectors vidc_init_vectors[] = {
diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h
index 43d46cd..6da7b8f 100644
--- a/arch/arm/mach-msm/devices.h
+++ b/arch/arm/mach-msm/devices.h
@@ -110,6 +110,7 @@
 extern struct platform_device apq8064_device_gadget_peripheral;
 extern struct platform_device apq8064_device_hsusb_host;
 extern struct platform_device apq8064_device_hsic_host;
+extern struct platform_device apq8064_device_ehci_host3;
 
 extern struct platform_device msm_device_i2c;
 
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index dc11eaf..97b0a2e 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -1274,6 +1274,7 @@
 
 #ifdef CONFIG_USB_EHCI_MSM
 #include "ehci-msm.c"
+#include "ehci-msm2.c"
 #define PLATFORM_DRIVER_PRESENT
 #endif
 
@@ -1391,7 +1392,11 @@
 #endif
 
 #ifdef CONFIG_USB_EHCI_MSM_HSIC
-	&ehci_msm_hsic_driver
+	&ehci_msm_hsic_driver,
+#endif
+
+#ifdef CONFIG_USB_EHCI_MSM
+	&ehci_msm2_driver,
 #endif
 
 };
diff --git a/drivers/usb/host/ehci-msm2.c b/drivers/usb/host/ehci-msm2.c
new file mode 100644
index 0000000..4f6fe3e
--- /dev/null
+++ b/drivers/usb/host/ehci-msm2.c
@@ -0,0 +1,1003 @@
+/* ehci-msm2.c - HSUSB Host Controller Driver Implementation
+ *
+ * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ *
+ * Partly derived from ehci-fsl.c and ehci-hcd.c
+ * Copyright (c) 2000-2004 by David Brownell
+ * Copyright (c) 2005 MontaVista Software
+ *
+ * All source code in this file is licensed under the following license except
+ * where indicated.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org
+ */
+
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/wakelock.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/usb/ulpi.h>
+#include <linux/usb/msm_hsusb_hw.h>
+#include <linux/usb/msm_hsusb.h>
+#include <mach/clk.h>
+#include <mach/msm_iomap.h>
+
+#define MSM_USB_BASE (hcd->regs)
+
+struct msm_hcd {
+	struct ehci_hcd				ehci;
+	struct device				*dev;
+	struct clk				*iface_clk;
+	struct clk				*core_clk;
+	struct clk				*alt_core_clk;
+	struct regulator			*hsusb_vddcx;
+	struct regulator			*hsusb_3p3;
+	struct regulator			*hsusb_1p8;
+	struct regulator			*vbus;
+	bool					async_int;
+	atomic_t				in_lpm;
+	struct wake_lock			wlock;
+};
+
+static inline struct msm_hcd *hcd_to_mhcd(struct usb_hcd *hcd)
+{
+	return (struct msm_hcd *) (hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *mhcd_to_hcd(struct msm_hcd *mhcd)
+{
+	return container_of((void *) mhcd, struct usb_hcd, hcd_priv);
+}
+
+#define HSUSB_PHY_3P3_VOL_MIN		3050000 /* uV */
+#define HSUSB_PHY_3P3_VOL_MAX		3300000 /* uV */
+#define HSUSB_PHY_3P3_HPM_LOAD		50000	/* uA */
+
+#define HSUSB_PHY_1P8_VOL_MIN		1800000 /* uV */
+#define HSUSB_PHY_1P8_VOL_MAX		1800000 /* uV */
+#define HSUSB_PHY_1P8_HPM_LOAD		50000	/* uA */
+
+#define HSUSB_PHY_VDD_DIG_VOL_MIN	1045000	/* uV */
+#define HSUSB_PHY_VDD_DIG_VOL_MAX	1320000	/* uV */
+#define HSUSB_PHY_VDD_DIG_LOAD		49360	/* uA */
+
+static int msm_ehci_init_vddcx(struct msm_hcd *mhcd, int init)
+{
+	int ret = 0;
+
+	if (!init)
+		goto disable_reg;
+
+	mhcd->hsusb_vddcx = regulator_get(mhcd->dev, "HSUSB_VDDCX");
+	if (IS_ERR(mhcd->hsusb_vddcx)) {
+		dev_err(mhcd->dev, "unable to get ehci vddcx\n");
+		return PTR_ERR(mhcd->hsusb_vddcx);
+	}
+
+	ret = regulator_set_voltage(mhcd->hsusb_vddcx,
+			HSUSB_PHY_VDD_DIG_VOL_MIN,
+			HSUSB_PHY_VDD_DIG_VOL_MAX);
+	if (ret) {
+		dev_err(mhcd->dev, "unable to set the voltage"
+				"for ehci vddcx\n");
+		goto reg_set_voltage_err;
+	}
+
+	ret = regulator_set_optimum_mode(mhcd->hsusb_vddcx,
+				HSUSB_PHY_VDD_DIG_LOAD);
+	if (ret < 0) {
+		dev_err(mhcd->dev, "%s: Unable to set optimum mode of the"
+				" regulator: VDDCX\n", __func__);
+		goto reg_optimum_mode_err;
+	}
+
+	ret = regulator_enable(mhcd->hsusb_vddcx);
+	if (ret) {
+		dev_err(mhcd->dev, "unable to enable ehci vddcx\n");
+		goto reg_enable_err;
+	}
+
+	return 0;
+
+disable_reg:
+	regulator_disable(mhcd->hsusb_vddcx);
+reg_enable_err:
+	regulator_set_optimum_mode(mhcd->hsusb_vddcx, 0);
+reg_optimum_mode_err:
+	regulator_set_voltage(mhcd->hsusb_vddcx, 0,
+				HSUSB_PHY_VDD_DIG_VOL_MIN);
+reg_set_voltage_err:
+	regulator_put(mhcd->hsusb_vddcx);
+
+	return ret;
+
+}
+
+static int msm_ehci_ldo_init(struct msm_hcd *mhcd, int init)
+{
+	int rc = 0;
+
+	if (!init)
+		goto put_1p8;
+
+	mhcd->hsusb_3p3 = regulator_get(mhcd->dev, "HSUSB_3p3");
+	if (IS_ERR(mhcd->hsusb_3p3)) {
+		dev_err(mhcd->dev, "unable to get hsusb 3p3\n");
+		return PTR_ERR(mhcd->hsusb_3p3);
+	}
+
+	rc = regulator_set_voltage(mhcd->hsusb_3p3,
+			HSUSB_PHY_3P3_VOL_MIN, HSUSB_PHY_3P3_VOL_MAX);
+	if (rc) {
+		dev_err(mhcd->dev, "unable to set voltage level for"
+				"hsusb 3p3\n");
+		goto put_3p3;
+	}
+	mhcd->hsusb_1p8 = regulator_get(mhcd->dev, "HSUSB_1p8");
+	if (IS_ERR(mhcd->hsusb_1p8)) {
+		dev_err(mhcd->dev, "unable to get hsusb 1p8\n");
+		rc = PTR_ERR(mhcd->hsusb_1p8);
+		goto put_3p3_lpm;
+	}
+	rc = regulator_set_voltage(mhcd->hsusb_1p8,
+			HSUSB_PHY_1P8_VOL_MIN, HSUSB_PHY_1P8_VOL_MAX);
+	if (rc) {
+		dev_err(mhcd->dev, "unable to set voltage level for"
+				"hsusb 1p8\n");
+		goto put_1p8;
+	}
+
+	return 0;
+
+put_1p8:
+	regulator_set_voltage(mhcd->hsusb_1p8, 0, HSUSB_PHY_1P8_VOL_MAX);
+	regulator_put(mhcd->hsusb_1p8);
+put_3p3_lpm:
+	regulator_set_voltage(mhcd->hsusb_3p3, 0, HSUSB_PHY_3P3_VOL_MAX);
+put_3p3:
+	regulator_put(mhcd->hsusb_3p3);
+
+	return rc;
+}
+
+#ifdef CONFIG_PM_SLEEP
+#define HSUSB_PHY_SUSP_DIG_VOL  500000
+static int msm_ehci_config_vddcx(struct msm_hcd *mhcd, int high)
+{
+	int max_vol = HSUSB_PHY_VDD_DIG_VOL_MAX;
+	int min_vol;
+	int ret;
+
+	if (high)
+		min_vol = HSUSB_PHY_VDD_DIG_VOL_MIN;
+	else
+		min_vol = HSUSB_PHY_SUSP_DIG_VOL;
+
+	ret = regulator_set_voltage(mhcd->hsusb_vddcx, min_vol, max_vol);
+	if (ret) {
+		dev_err(mhcd->dev, "%s: unable to set the voltage of regulator"
+			" HSUSB_VDDCX\n", __func__);
+		return ret;
+	}
+
+	dev_dbg(mhcd->dev, "%s: min_vol:%d max_vol:%d\n", __func__, min_vol,
+								max_vol);
+
+	return ret;
+}
+#else
+static int msm_ehci_config_vddcx(struct msm_hcd *mhcd, int high)
+{
+	return 0;
+}
+#endif
+
+static int msm_ehci_init_vbus(struct msm_hcd *mhcd, int init)
+{
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+	struct msm_usb_host_platform_data *pdata;
+
+	if (!init) {
+		regulator_put(mhcd->vbus);
+		return 0;
+	}
+
+	mhcd->vbus = regulator_get(mhcd->dev, "vbus");
+	if (IS_ERR(mhcd->vbus)) {
+		pr_err("Unable to get vbus\n");
+		return -ENODEV;
+	}
+
+	pdata = mhcd->dev->platform_data;
+	if (pdata)
+		hcd->power_budget = pdata->power_budget;
+
+	return 0;
+}
+
+static void msm_ehci_vbus_power(struct msm_hcd *mhcd, bool on)
+{
+	int ret;
+
+	if (!mhcd->vbus) {
+		pr_err("vbus is NULL.");
+		return;
+	}
+	if (on) {
+		ret = regulator_enable(mhcd->vbus);
+		if (ret) {
+			pr_err("unable to enable vbus\n");
+			return;
+		}
+	} else {
+		ret = regulator_disable(mhcd->vbus);
+		if (ret) {
+			pr_err("unable to disable vbus\n");
+			return;
+		}
+	}
+}
+
+static int msm_ehci_ldo_enable(struct msm_hcd *mhcd, int on)
+{
+	int ret = 0;
+
+	if (IS_ERR(mhcd->hsusb_1p8)) {
+		dev_err(mhcd->dev, "%s: HSUSB_1p8 is not initialized\n",
+								__func__);
+		return -ENODEV;
+	}
+
+	if (IS_ERR(mhcd->hsusb_3p3)) {
+		dev_err(mhcd->dev, "%s: HSUSB_3p3 is not initialized\n",
+								__func__);
+		return -ENODEV;
+	}
+
+	if (on) {
+		ret = regulator_set_optimum_mode(mhcd->hsusb_1p8,
+						HSUSB_PHY_1P8_HPM_LOAD);
+		if (ret < 0) {
+			dev_err(mhcd->dev, "%s: Unable to set HPM of the"
+				" regulator: HSUSB_1p8\n", __func__);
+			return ret;
+		}
+
+		ret = regulator_enable(mhcd->hsusb_1p8);
+		if (ret) {
+			dev_err(mhcd->dev, "%s: unable to enable the hsusb"
+						" 1p8\n", __func__);
+			regulator_set_optimum_mode(mhcd->hsusb_1p8, 0);
+			return ret;
+		}
+
+		ret = regulator_set_optimum_mode(mhcd->hsusb_3p3,
+						HSUSB_PHY_3P3_HPM_LOAD);
+		if (ret < 0) {
+			dev_err(mhcd->dev, "%s: Unable to set HPM of the "
+				"regulator: HSUSB_3p3\n", __func__);
+			regulator_set_optimum_mode(mhcd->hsusb_1p8, 0);
+			regulator_disable(mhcd->hsusb_1p8);
+			return ret;
+		}
+
+		ret = regulator_enable(mhcd->hsusb_3p3);
+		if (ret) {
+			dev_err(mhcd->dev, "%s: unable to enable the "
+					"hsusb 3p3\n", __func__);
+			regulator_set_optimum_mode(mhcd->hsusb_3p3, 0);
+			regulator_set_optimum_mode(mhcd->hsusb_1p8, 0);
+			regulator_disable(mhcd->hsusb_1p8);
+			return ret;
+		}
+
+	} else {
+		ret = regulator_disable(mhcd->hsusb_1p8);
+		if (ret) {
+			dev_err(mhcd->dev, "%s: unable to disable the "
+					"hsusb 1p8\n", __func__);
+			return ret;
+		}
+
+		ret = regulator_set_optimum_mode(mhcd->hsusb_1p8, 0);
+		if (ret < 0)
+			dev_err(mhcd->dev, "%s: Unable to set LPM of the "
+				"regulator: HSUSB_1p8\n", __func__);
+
+		ret = regulator_disable(mhcd->hsusb_3p3);
+		if (ret) {
+			dev_err(mhcd->dev, "%s: unable to disable the "
+					"hsusb 3p3\n", __func__);
+			return ret;
+		}
+		ret = regulator_set_optimum_mode(mhcd->hsusb_3p3, 0);
+		if (ret < 0)
+			dev_err(mhcd->dev, "%s: Unable to set LPM of the "
+					"regulator: HSUSB_3p3\n", __func__);
+	}
+
+	dev_dbg(mhcd->dev, "reg (%s)\n", on ? "HPM" : "LPM");
+
+	return ret < 0 ? ret : 0;
+}
+
+
+#define ULPI_IO_TIMEOUT_USECS	(10 * 1000)
+static int msm_ulpi_read(struct msm_hcd *mhcd, u32 reg)
+{
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+	unsigned long timeout;
+
+	/* initiate read operation */
+	writel_relaxed(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg),
+	       USB_ULPI_VIEWPORT);
+
+	/* wait for completion */
+	timeout = jiffies + usecs_to_jiffies(ULPI_IO_TIMEOUT_USECS);
+	while (readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_RUN) {
+		if (time_after(jiffies, timeout)) {
+			dev_err(mhcd->dev, "msm_ulpi_read: timeout %08x\n",
+				readl_relaxed(USB_ULPI_VIEWPORT));
+			return -ETIMEDOUT;
+		}
+		udelay(1);
+	}
+
+	return ULPI_DATA_READ(readl_relaxed(USB_ULPI_VIEWPORT));
+}
+
+
+static int msm_ulpi_write(struct msm_hcd *mhcd, u32 val, u32 reg)
+{
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+	unsigned long timeout;
+
+	/* initiate write operation */
+	writel_relaxed(ULPI_RUN | ULPI_WRITE |
+	       ULPI_ADDR(reg) | ULPI_DATA(val),
+	       USB_ULPI_VIEWPORT);
+
+	/* wait for completion */
+	timeout = jiffies + usecs_to_jiffies(ULPI_IO_TIMEOUT_USECS);
+	while (readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_RUN) {
+		if (time_after(jiffies, timeout)) {
+			dev_err(mhcd->dev, "msm_ulpi_write: timeout\n");
+			return -ETIMEDOUT;
+		}
+		udelay(1);
+	}
+
+	return 0;
+}
+
+static int msm_ehci_link_clk_reset(struct msm_hcd *mhcd, bool assert)
+{
+	int ret;
+
+	if (assert) {
+		ret = clk_reset(mhcd->alt_core_clk, CLK_RESET_ASSERT);
+		if (ret)
+			dev_err(mhcd->dev, "usb alt_core_clk assert failed\n");
+	} else {
+		ret = clk_reset(mhcd->alt_core_clk, CLK_RESET_DEASSERT);
+		if (ret)
+			dev_err(mhcd->dev, "usb alt_core_clk deassert failed\n");
+	}
+
+	return ret;
+}
+
+static int msm_ehci_phy_reset(struct msm_hcd *mhcd)
+{
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+	u32 val;
+	int ret;
+	int retries;
+
+	ret = msm_ehci_link_clk_reset(mhcd, 1);
+	if (ret)
+		return ret;
+
+	udelay(1);
+
+	ret = msm_ehci_link_clk_reset(mhcd, 0);
+	if (ret)
+		return ret;
+
+	val = readl_relaxed(USB_PORTSC) & ~PORTSC_PTS_MASK;
+	writel_relaxed(val | PORTSC_PTS_ULPI, USB_PORTSC);
+
+	for (retries = 3; retries > 0; retries--) {
+		ret = msm_ulpi_write(mhcd, ULPI_FUNC_CTRL_SUSPENDM,
+				ULPI_CLR(ULPI_FUNC_CTRL));
+		if (!ret)
+			break;
+	}
+	if (!retries)
+		return -ETIMEDOUT;
+
+	/* Wakeup the PHY with a reg-access for calibration */
+	for (retries = 3; retries > 0; retries--) {
+		ret = msm_ulpi_read(mhcd, ULPI_DEBUG);
+		if (ret != -ETIMEDOUT)
+			break;
+	}
+	if (!retries)
+		return -ETIMEDOUT;
+
+	dev_info(mhcd->dev, "phy_reset: success\n");
+
+	return 0;
+}
+
+#define LINK_RESET_TIMEOUT_USEC		(250 * 1000)
+static int msm_hsusb_reset(struct msm_hcd *mhcd)
+{
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+	unsigned long timeout;
+	int ret;
+
+	clk_prepare_enable(mhcd->alt_core_clk);
+	ret = msm_ehci_phy_reset(mhcd);
+	if (ret) {
+		dev_err(mhcd->dev, "phy_reset failed\n");
+		return ret;
+	}
+
+	writel_relaxed(USBCMD_RESET, USB_USBCMD);
+
+	timeout = jiffies + usecs_to_jiffies(LINK_RESET_TIMEOUT_USEC);
+	while (readl_relaxed(USB_USBCMD) & USBCMD_RESET) {
+		if (time_after(jiffies, timeout))
+			return -ETIMEDOUT;
+		udelay(1);
+	}
+
+	/* select ULPI phy */
+	writel_relaxed(0x80000000, USB_PORTSC);
+
+	msleep(100);
+
+	writel_relaxed(0x0, USB_AHBBURST);
+	writel_relaxed(0x00, USB_AHBMODE);
+
+	/* Ensure that RESET operation is completed before turning off clock */
+	mb();
+	clk_disable_unprepare(mhcd->alt_core_clk);
+
+	/*rising edge interrupts with Dp rise and fall enabled*/
+	msm_ulpi_write(mhcd, ULPI_INT_DP, ULPI_USB_INT_EN_RISE);
+	msm_ulpi_write(mhcd, ULPI_INT_DP, ULPI_USB_INT_EN_FALL);
+
+	/*Clear the PHY interrupts by reading the PHY interrupt latch register*/
+	msm_ulpi_read(mhcd, ULPI_USB_INT_LATCH);
+
+	return 0;
+}
+
+#define PHY_SUSPEND_TIMEOUT_USEC	(500 * 1000)
+#define PHY_RESUME_TIMEOUT_USEC		(100 * 1000)
+
+#ifdef CONFIG_PM_SLEEP
+static int msm_ehci_suspend(struct msm_hcd *mhcd)
+{
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+	unsigned long timeout;
+	u32 portsc;
+
+	if (atomic_read(&mhcd->in_lpm)) {
+		dev_dbg(mhcd->dev, "%s called in lpm\n", __func__);
+		return 0;
+	}
+
+	disable_irq(hcd->irq);
+
+	/* Set the PHCD bit, only if it is not set by the controller.
+	 * PHY may take some time or even fail to enter into low power
+	 * mode (LPM). Hence poll for 500 msec and reset the PHY and link
+	 * in failure case.
+	 */
+	portsc = readl_relaxed(USB_PORTSC);
+	if (!(portsc & PORTSC_PHCD)) {
+		writel_relaxed(portsc | PORTSC_PHCD,
+				USB_PORTSC);
+
+		timeout = jiffies + usecs_to_jiffies(PHY_SUSPEND_TIMEOUT_USEC);
+		while (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD)) {
+			if (time_after(jiffies, timeout)) {
+				dev_err(mhcd->dev, "Unable to suspend PHY\n");
+				msm_hsusb_reset(mhcd);
+				break;
+			}
+			udelay(1);
+		}
+	}
+
+	/*
+	 * PHY has capability to generate interrupt asynchronously in low
+	 * power mode (LPM). This interrupt is level triggered. So USB IRQ
+	 * line must be disabled till async interrupt enable bit is cleared
+	 * in USBCMD register. Assert STP (ULPI interface STOP signal) to
+	 * block data communication from PHY.
+	 */
+	writel_relaxed(readl_relaxed(USB_USBCMD) | ASYNC_INTR_CTRL |
+				ULPI_STP_CTRL, USB_USBCMD);
+
+	/*
+	 * Ensure that hardware is put in low power mode before
+	 * clocks are turned OFF and VDD is allowed to minimize.
+	 */
+	mb();
+
+	clk_disable_unprepare(mhcd->iface_clk);
+	clk_disable_unprepare(mhcd->core_clk);
+
+	msm_ehci_config_vddcx(mhcd, 0);
+
+	atomic_set(&mhcd->in_lpm, 1);
+	enable_irq(hcd->irq);
+	wake_unlock(&mhcd->wlock);
+
+	dev_info(mhcd->dev, "EHCI USB in low power mode\n");
+
+	return 0;
+}
+
+static int msm_ehci_resume(struct msm_hcd *mhcd)
+{
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+	unsigned long timeout;
+	unsigned temp;
+
+	if (!atomic_read(&mhcd->in_lpm)) {
+		dev_dbg(mhcd->dev, "%s called in !in_lpm\n", __func__);
+		return 0;
+	}
+
+	wake_lock(&mhcd->wlock);
+
+	clk_prepare_enable(mhcd->core_clk);
+	clk_prepare_enable(mhcd->iface_clk);
+
+	msm_ehci_config_vddcx(mhcd, 1);
+
+	temp = readl_relaxed(USB_USBCMD);
+	temp &= ~ASYNC_INTR_CTRL;
+	temp &= ~ULPI_STP_CTRL;
+	writel_relaxed(temp, USB_USBCMD);
+
+	if (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD))
+		goto skip_phy_resume;
+
+	temp = readl_relaxed(USB_PORTSC) & ~PORTSC_PHCD;
+	writel_relaxed(temp, USB_PORTSC);
+
+	timeout = jiffies + usecs_to_jiffies(PHY_RESUME_TIMEOUT_USEC);
+	while ((readl_relaxed(USB_PORTSC) & PORTSC_PHCD) ||
+			!(readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_SYNC_STATE)) {
+		if (time_after(jiffies, timeout)) {
+			/*This is a fatal error. Reset the link and PHY*/
+			dev_err(mhcd->dev, "Unable to resume USB. Resetting the h/w\n");
+			msm_hsusb_reset(mhcd);
+			break;
+		}
+		udelay(1);
+	}
+
+skip_phy_resume:
+
+	atomic_set(&mhcd->in_lpm, 0);
+
+	if (mhcd->async_int) {
+		mhcd->async_int = false;
+		pm_runtime_put_noidle(mhcd->dev);
+		enable_irq(hcd->irq);
+	}
+
+	dev_info(mhcd->dev, "EHCI USB exited from low power mode\n");
+
+	return 0;
+}
+#endif
+
+static irqreturn_t msm_ehci_irq(struct usb_hcd *hcd)
+{
+	struct msm_hcd *mhcd = hcd_to_mhcd(hcd);
+
+	if (atomic_read(&mhcd->in_lpm)) {
+		disable_irq_nosync(hcd->irq);
+		mhcd->async_int = true;
+		pm_runtime_get(mhcd->dev);
+		return IRQ_HANDLED;
+	}
+
+	return ehci_irq(hcd);
+}
+
+static int msm_ehci_reset(struct usb_hcd *hcd)
+{
+	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+	int retval;
+
+	ehci->caps = USB_CAPLENGTH;
+	ehci->regs = USB_CAPLENGTH +
+		HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
+	dbg_hcs_params(ehci, "reset");
+	dbg_hcc_params(ehci, "reset");
+
+	/* cache the data to minimize the chip reads*/
+	ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
+
+	hcd->has_tt = 1;
+	ehci->sbrn = HCD_USB2;
+
+	retval = ehci_halt(ehci);
+	if (retval)
+		return retval;
+
+	/* data structure init */
+	retval = ehci_init(hcd);
+	if (retval)
+		return retval;
+
+	retval = ehci_reset(ehci);
+	if (retval)
+		return retval;
+
+	/* bursts of unspecified length. */
+	writel_relaxed(0, USB_AHBBURST);
+	/* Use the AHB transactor */
+	writel_relaxed(0, USB_AHBMODE);
+	/* Disable streaming mode and select host mode */
+	writel_relaxed(0x13, USB_USBMODE);
+
+	ehci_port_power(ehci, 1);
+	return 0;
+}
+
+static struct hc_driver msm_hc2_driver = {
+	.description		= hcd_name,
+	.product_desc		= "Qualcomm EHCI Host Controller",
+	.hcd_priv_size		= sizeof(struct msm_hcd),
+
+	/*
+	 * generic hardware linkage
+	 */
+	.irq			= msm_ehci_irq,
+	.flags			= HCD_USB2 | HCD_MEMORY,
+
+	.reset			= msm_ehci_reset,
+	.start			= ehci_run,
+
+	.stop			= ehci_stop,
+	.shutdown		= ehci_shutdown,
+
+	/*
+	 * managing i/o requests and associated device resources
+	 */
+	.urb_enqueue		= ehci_urb_enqueue,
+	.urb_dequeue		= ehci_urb_dequeue,
+	.endpoint_disable	= ehci_endpoint_disable,
+	.endpoint_reset		= ehci_endpoint_reset,
+	.clear_tt_buffer_complete	 = ehci_clear_tt_buffer_complete,
+
+	/*
+	 * scheduling support
+	 */
+	.get_frame_number	= ehci_get_frame,
+
+	/*
+	 * root hub support
+	 */
+	.hub_status_data	= ehci_hub_status_data,
+	.hub_control		= ehci_hub_control,
+	.relinquish_port	= ehci_relinquish_port,
+	.port_handed_over	= ehci_port_handed_over,
+
+	/*
+	 * PM support
+	 */
+	.bus_suspend		= ehci_bus_suspend,
+	.bus_resume		= ehci_bus_resume,
+};
+
+static int msm_ehci_init_clocks(struct msm_hcd *mhcd, u32 init)
+{
+	int ret = 0;
+
+	if (!init)
+		goto put_clocks;
+
+	/* 60MHz alt_core_clk is for LINK to be used during PHY RESET  */
+	mhcd->alt_core_clk = clk_get(mhcd->dev, "alt_core_clk");
+	if (IS_ERR(mhcd->alt_core_clk)) {
+		dev_err(mhcd->dev, "failed to get alt_core_clk\n");
+		ret = PTR_ERR(mhcd->alt_core_clk);
+		return ret;
+	}
+	clk_set_rate(mhcd->alt_core_clk, 60000000);
+
+	/* iface_clk is required for data transfers */
+	mhcd->iface_clk = clk_get(mhcd->dev, "iface_clk");
+	if (IS_ERR(mhcd->iface_clk)) {
+		dev_err(mhcd->dev, "failed to get iface_clk\n");
+		ret = PTR_ERR(mhcd->iface_clk);
+		goto put_alt_core_clk;
+	}
+
+	/* Link's protocol engine is based on pclk which must
+	 * be running >55Mhz and frequency should also not change.
+	 * Hence, vote for maximum clk frequency on its source
+	 */
+	mhcd->core_clk = clk_get(mhcd->dev, "core_clk");
+	if (IS_ERR(mhcd->core_clk)) {
+		dev_err(mhcd->dev, "failed to get core_clk\n");
+		ret = PTR_ERR(mhcd->core_clk);
+		goto put_iface_clk;
+	}
+	clk_set_rate(mhcd->core_clk, INT_MAX);
+
+	clk_prepare_enable(mhcd->core_clk);
+	clk_prepare_enable(mhcd->iface_clk);
+
+	return 0;
+
+put_clocks:
+	clk_disable_unprepare(mhcd->iface_clk);
+	clk_disable_unprepare(mhcd->core_clk);
+	clk_put(mhcd->core_clk);
+put_iface_clk:
+	clk_put(mhcd->iface_clk);
+put_alt_core_clk:
+	clk_put(mhcd->alt_core_clk);
+
+	return ret;
+}
+
+static int __devinit ehci_msm2_probe(struct platform_device *pdev)
+{
+	struct usb_hcd *hcd;
+	struct resource *res;
+	struct msm_hcd *mhcd;
+	int ret;
+
+	dev_dbg(&pdev->dev, "ehci_msm2 probe\n");
+
+	hcd = usb_create_hcd(&msm_hc2_driver, &pdev->dev,
+				dev_name(&pdev->dev));
+	if (!hcd) {
+		dev_err(&pdev->dev, "Unable to create HCD\n");
+		return  -ENOMEM;
+	}
+
+	hcd->irq = platform_get_irq(pdev, 0);
+	if (hcd->irq < 0) {
+		dev_err(&pdev->dev, "Unable to get IRQ resource\n");
+		ret = hcd->irq;
+		goto put_hcd;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "Unable to get memory resource\n");
+		ret = -ENODEV;
+		goto put_hcd;
+	}
+
+	hcd->rsrc_start = res->start;
+	hcd->rsrc_len = resource_size(res);
+	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+	if (!hcd->regs) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		ret = -ENOMEM;
+		goto put_hcd;
+	}
+
+	mhcd = hcd_to_mhcd(hcd);
+	mhcd->dev = &pdev->dev;
+
+	ret = msm_ehci_init_clocks(mhcd, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to initialize clocks\n");
+		ret = -ENODEV;
+		goto unmap;
+	}
+
+	ret = msm_ehci_init_vddcx(mhcd, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to initialize VDDCX\n");
+		ret = -ENODEV;
+		goto deinit_clocks;
+	}
+
+	ret = msm_ehci_config_vddcx(mhcd, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "hsusb vddcx configuration failed\n");
+		goto deinit_vddcx;
+	}
+
+	ret = msm_ehci_ldo_init(mhcd, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "hsusb vreg configuration failed\n");
+		goto deinit_vddcx;
+	}
+
+	ret = msm_ehci_ldo_enable(mhcd, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "hsusb vreg enable failed\n");
+		goto deinit_ldo;
+	}
+
+	ret = msm_ehci_init_vbus(mhcd, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to get vbus\n");
+		goto disable_ldo;
+	}
+
+	ret = msm_hsusb_reset(mhcd);
+	if (ret) {
+		dev_err(&pdev->dev, "hsusb PHY initialization failed\n");
+		goto vbus_deinit;
+	}
+
+	ret = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to register HCD\n");
+		goto vbus_deinit;
+	}
+
+	/*TBD:for now enable vbus here*/
+	msm_ehci_vbus_power(mhcd, 1);
+
+	device_init_wakeup(&pdev->dev, 1);
+	wake_lock_init(&mhcd->wlock, WAKE_LOCK_SUSPEND, dev_name(&pdev->dev));
+	wake_lock(&mhcd->wlock);
+	/*
+	 * This pdev->dev is assigned parent of root-hub by USB core,
+	 * hence, runtime framework automatically calls this driver's
+	 * runtime APIs based on root-hub's state.
+	 */
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+
+	return 0;
+
+vbus_deinit:
+	msm_ehci_init_vbus(mhcd, 0);
+disable_ldo:
+	msm_ehci_ldo_enable(mhcd, 0);
+deinit_ldo:
+	msm_ehci_ldo_init(mhcd, 0);
+deinit_vddcx:
+	msm_ehci_init_vddcx(mhcd, 0);
+deinit_clocks:
+	msm_ehci_init_clocks(mhcd, 0);
+unmap:
+	iounmap(hcd->regs);
+put_hcd:
+	usb_put_hcd(hcd);
+
+	return ret;
+}
+
+static int __devexit ehci_msm2_remove(struct platform_device *pdev)
+{
+	struct usb_hcd *hcd = platform_get_drvdata(pdev);
+	struct msm_hcd *mhcd = hcd_to_mhcd(hcd);
+
+	device_init_wakeup(&pdev->dev, 0);
+	pm_runtime_disable(&pdev->dev);
+	pm_runtime_set_suspended(&pdev->dev);
+
+	usb_remove_hcd(hcd);
+	msm_ehci_vbus_power(mhcd, 0);
+	msm_ehci_init_vbus(mhcd, 0);
+	msm_ehci_ldo_enable(mhcd, 0);
+	msm_ehci_ldo_init(mhcd, 0);
+	msm_ehci_init_vddcx(mhcd, 0);
+
+	msm_ehci_init_clocks(mhcd, 0);
+	wake_lock_destroy(&mhcd->wlock);
+	iounmap(hcd->regs);
+	usb_put_hcd(hcd);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ehci_msm2_pm_suspend(struct device *dev)
+{
+	struct usb_hcd *hcd = dev_get_drvdata(dev);
+	struct msm_hcd *mhcd = hcd_to_mhcd(hcd);
+
+	dev_dbg(dev, "ehci-msm2 PM suspend\n");
+
+	if (device_may_wakeup(dev))
+		enable_irq_wake(hcd->irq);
+
+	return msm_ehci_suspend(mhcd);
+
+}
+
+static int ehci_msm2_pm_resume(struct device *dev)
+{
+	int ret;
+	struct usb_hcd *hcd = dev_get_drvdata(dev);
+	struct msm_hcd *mhcd = hcd_to_mhcd(hcd);
+
+	dev_dbg(dev, "ehci-msm2 PM resume\n");
+
+	if (device_may_wakeup(dev))
+		disable_irq_wake(hcd->irq);
+
+	ret = msm_ehci_resume(mhcd);
+	if (ret)
+		return ret;
+
+	/* Bring the device to full powered state upon system resume */
+	pm_runtime_disable(dev);
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+static int ehci_msm2_runtime_idle(struct device *dev)
+{
+	dev_dbg(dev, "EHCI runtime idle\n");
+
+	return 0;
+}
+
+static int ehci_msm2_runtime_suspend(struct device *dev)
+{
+	struct usb_hcd *hcd = dev_get_drvdata(dev);
+	struct msm_hcd *mhcd = hcd_to_mhcd(hcd);
+
+	dev_dbg(dev, "EHCI runtime suspend\n");
+	return msm_ehci_suspend(mhcd);
+}
+
+static int ehci_msm2_runtime_resume(struct device *dev)
+{
+	struct usb_hcd *hcd = dev_get_drvdata(dev);
+	struct msm_hcd *mhcd = hcd_to_mhcd(hcd);
+
+	dev_dbg(dev, "EHCI runtime resume\n");
+	return msm_ehci_resume(mhcd);
+}
+#endif
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops ehci_msm2_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(ehci_msm2_pm_suspend, ehci_msm2_pm_resume)
+	SET_RUNTIME_PM_OPS(ehci_msm2_runtime_suspend, ehci_msm2_runtime_resume,
+				ehci_msm2_runtime_idle)
+};
+#endif
+
+static struct platform_driver ehci_msm2_driver = {
+	.probe	= ehci_msm2_probe,
+	.remove	= __devexit_p(ehci_msm2_remove),
+	.driver = {
+		.name = "msm_ehci_host",
+#ifdef CONFIG_PM
+		.pm = &ehci_msm2_dev_pm_ops,
+#endif
+	},
+};
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index 037cfe7..79fb177 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -277,6 +277,10 @@
 	unsigned hub_reset;
 };
 
+struct msm_usb_host_platform_data {
+	unsigned int power_budget;
+};
+
 struct usb_bam_pipe_connect {
 	u32 src_phy_addr;
 	int src_pipe_index;
diff --git a/include/linux/usb/ulpi.h b/include/linux/usb/ulpi.h
index 9595796..540a74c 100644
--- a/include/linux/usb/ulpi.h
+++ b/include/linux/usb/ulpi.h
@@ -145,6 +145,7 @@
 #define ULPI_INT_SESS_VALID			(1 << 2)
 #define ULPI_INT_SESS_END			(1 << 3)
 #define ULPI_INT_IDGRD				(1 << 4)
+#define ULPI_INT_DP				(1 << 7)
 
 /* Debug */
 #define ULPI_DEBUG_LINESTATE0			(1 << 0)