Merge changes I7cdf8e45,Id54dcc80 into msm-3.0
* changes:
USB: EHCI: msm: Configure GPIOs for HSIC strobe and data lines
USB: EHCI: msm: Add support for HSIC based Host Controller
diff --git a/arch/arm/mach-msm/board-msm8960-regulator.c b/arch/arm/mach-msm/board-msm8960-regulator.c
index e3f0e31..ca24e79 100644
--- a/arch/arm/mach-msm/board-msm8960-regulator.c
+++ b/arch/arm/mach-msm/board-msm8960-regulator.c
@@ -146,6 +146,7 @@
REGULATOR_SUPPLY("8921_s3", NULL),
REGULATOR_SUPPLY("HSUSB_VDDCX", "msm_otg"),
REGULATOR_SUPPLY("riva_vddcx", "wcnss_wlan.0"),
+ REGULATOR_SUPPLY("HSIC_VDDCX", "msm_hsic_host"),
};
VREG_CONSUMERS(S4) = {
REGULATOR_SUPPLY("8921_s4", NULL),
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/Kconfig b/drivers/usb/host/Kconfig
index 4447b0f..ece6785 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -178,6 +178,17 @@
This driver is not supported on boards like trout which
has an external PHY.
+config USB_EHCI_MSM_HSIC
+ bool "Support for HSIC based MSM on-chip EHCI USB controller"
+ depends on USB_EHCI_HCD && ARCH_MSM
+ ---help---
+ Enables support for the HSIC (High Speed Inter-Chip) based
+ USB Host controller present on the Qualcomm chipsets.
+
+ HSIC is a supplement to USB 2.0 specification and is preferred
+ for chip-to-chip interconnect (having maximum circuit length of
+ 10cm) as it removes the analog transceivers.
+
config USB_EHCI_TEGRA
boolean "NVIDIA Tegra HCD support"
depends on USB_EHCI_HCD && ARCH_TEGRA
@@ -258,7 +269,6 @@
Enables support for the full speed USB controller core present
on the Qualcomm chipsets
-
config USB_ISP116X_HCD
tristate "ISP116X HCD support"
depends on USB
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 0744395..dc11eaf 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -1187,31 +1187,6 @@
#define PCI_DRIVER ehci_pci_driver
#endif
-#ifdef CONFIG_USB_EHCI_FSL
-#include "ehci-fsl.c"
-#define PLATFORM_DRIVER ehci_fsl_driver
-#endif
-
-#ifdef CONFIG_USB_EHCI_MXC
-#include "ehci-mxc.c"
-#define PLATFORM_DRIVER ehci_mxc_driver
-#endif
-
-#ifdef CONFIG_USB_EHCI_SH
-#include "ehci-sh.c"
-#define PLATFORM_DRIVER ehci_hcd_sh_driver
-#endif
-
-#ifdef CONFIG_SOC_AU1200
-#include "ehci-au1xxx.c"
-#define PLATFORM_DRIVER ehci_hcd_au1xxx_driver
-#endif
-
-#ifdef CONFIG_USB_EHCI_HCD_OMAP
-#include "ehci-omap.c"
-#define PLATFORM_DRIVER ehci_hcd_omap_driver
-#endif
-
#ifdef CONFIG_PPC_PS3
#include "ehci-ps3.c"
#define PS3_SYSTEM_BUS_DRIVER ps3_ehci_driver
@@ -1227,90 +1202,204 @@
#define XILINX_OF_PLATFORM_DRIVER ehci_hcd_xilinx_of_driver
#endif
+#ifdef CONFIG_USB_EHCI_FSL
+#include "ehci-fsl.c"
+#define PLATFORM_DRIVER_PRESENT
+#endif
+
+#ifdef CONFIG_USB_EHCI_MXC
+#include "ehci-mxc.c"
+#define PLATFORM_DRIVER_PRESENT
+#endif
+
+#ifdef CONFIG_USB_EHCI_SH
+#include "ehci-sh.c"
+#define PLATFORM_DRIVER_PRESENT
+#endif
+
+#ifdef CONFIG_SOC_AU1200
+#include "ehci-au1xxx.c"
+#define PLATFORM_DRIVER_PRESENT
+#endif
+
+#ifdef CONFIG_USB_EHCI_HCD_OMAP
+#include "ehci-omap.c"
+#define PLATFORM_DRIVER_PRESENT
+#endif
+
#ifdef CONFIG_PLAT_ORION
#include "ehci-orion.c"
-#define PLATFORM_DRIVER ehci_orion_driver
+#define PLATFORM_DRIVER_PRESENT
#endif
#ifdef CONFIG_ARCH_IXP4XX
#include "ehci-ixp4xx.c"
-#define PLATFORM_DRIVER ixp4xx_ehci_driver
+#define PLATFORM_DRIVER_PRESENT
#endif
#ifdef CONFIG_USB_W90X900_EHCI
#include "ehci-w90x900.c"
-#define PLATFORM_DRIVER ehci_hcd_w90x900_driver
+#define PLATFORM_DRIVER_PRESENT
#endif
#ifdef CONFIG_ARCH_AT91
#include "ehci-atmel.c"
-#define PLATFORM_DRIVER ehci_atmel_driver
+#define PLATFORM_DRIVER_PRESENT
#endif
#ifdef CONFIG_USB_OCTEON_EHCI
#include "ehci-octeon.c"
-#define PLATFORM_DRIVER ehci_octeon_driver
+#define PLATFORM_DRIVER_PRESENT
#endif
#ifdef CONFIG_USB_CNS3XXX_EHCI
#include "ehci-cns3xxx.c"
-#define PLATFORM_DRIVER cns3xxx_ehci_driver
+#define PLATFORM_DRIVER_PRESENT
#endif
#ifdef CONFIG_ARCH_VT8500
#include "ehci-vt8500.c"
-#define PLATFORM_DRIVER vt8500_ehci_driver
+#define PLATFORM_DRIVER_PRESENT
#endif
#ifdef CONFIG_PLAT_SPEAR
#include "ehci-spear.c"
-#define PLATFORM_DRIVER spear_ehci_hcd_driver
+#define PLATFORM_DRIVER_PRESENT
#endif
#ifdef CONFIG_USB_EHCI_MSM_72K
#include "ehci-msm72k.c"
-#define PLATFORM_DRIVER ehci_msm_driver
+#define PLATFORM_DRIVER_PRESENT
#endif
#ifdef CONFIG_USB_EHCI_MSM
#include "ehci-msm.c"
-#define PLATFORM_DRIVER ehci_msm_driver
+#define PLATFORM_DRIVER_PRESENT
#endif
#ifdef CONFIG_USB_EHCI_HCD_PMC_MSP
#include "ehci-pmcmsp.c"
-#define PLATFORM_DRIVER ehci_hcd_msp_driver
+#define PLATFORM_DRIVER_PRESENT
#endif
#ifdef CONFIG_USB_EHCI_TEGRA
#include "ehci-tegra.c"
-#define PLATFORM_DRIVER tegra_ehci_driver
+#define PLATFORM_DRIVER_PRESENT
#endif
#ifdef CONFIG_USB_EHCI_S5P
#include "ehci-s5p.c"
-#define PLATFORM_DRIVER s5p_ehci_driver
+#define PLATFORM_DRIVER_PRESENT
#endif
#ifdef CONFIG_USB_EHCI_ATH79
#include "ehci-ath79.c"
-#define PLATFORM_DRIVER ehci_ath79_driver
+#define PLATFORM_DRIVER_PRESENT
#endif
#ifdef CONFIG_SPARC_LEON
#include "ehci-grlib.c"
-#define PLATFORM_DRIVER ehci_grlib_driver
+#define PLATFORM_DRIVER_PRESENT
#endif
-#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
+#ifdef CONFIG_USB_EHCI_MSM_HSIC
+#include "ehci-msm-hsic.c"
+#define PLATFORM_DRIVER_PRESENT
+#endif
+
+#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER_PRESENT) && \
!defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \
!defined(XILINX_OF_PLATFORM_DRIVER)
#error "missing bus glue for ehci-hcd"
#endif
+static struct platform_driver *plat_drivers[] = {
+#ifdef CONFIG_USB_EHCI_FSL
+ &ehci_fsl_driver,
+#endif
+
+#ifdef CONFIG_USB_EHCI_MXC
+ &ehci_mxc_driver,
+#endif
+
+#ifdef CONFIG_CPU_SUBTYPE_SH7786
+ &ehci_hcd_sh_driver,
+#endif
+
+#ifdef CONFIG_SOC_AU1200
+ &ehci_hcd_au1xxx_driver,
+#endif
+
+#ifdef CONFIG_USB_EHCI_HCD_OMAP
+ &ehci_hcd_omap_driver,
+#endif
+
+#ifdef CONFIG_PLAT_ORION
+ &ehci_orion_driver,
+#endif
+
+#ifdef CONFIG_ARCH_IXP4XX
+ &ixp4xx_ehci_driver,
+#endif
+
+#ifdef CONFIG_USB_W90X900_EHCI
+ &ehci_hcd_w90x900_driver,
+#endif
+
+#ifdef CONFIG_ARCH_AT91
+ &ehci_atmel_driver,
+#endif
+
+#ifdef CONFIG_USB_OCTEON_EHCI
+ &ehci_octeon_driver,
+#endif
+
+#ifdef CONFIG_USB_CNS3XXX_EHCI
+ &cns3xxx_ehci_driver,
+#endif
+
+#ifdef CONFIG_ARCH_VT8500
+ &vt8500_ehci_driver,
+#endif
+
+#ifdef CONFIG_PLAT_SPEAR
+ &spear_ehci_hcd_driver,
+#endif
+
+#ifdef CONFIG_USB_EHCI_HCD_PMC_MSP
+ &ehci_hcd_msp_driver
+#endif
+
+#ifdef CONFIG_USB_EHCI_TEGRA
+ &tegra_ehci_driver
+#endif
+
+#ifdef CONFIG_USB_EHCI_S5P
+ &s5p_ehci_driver
+#endif
+
+#ifdef CONFIG_USB_EHCI_ATH79
+ &ehci_ath79_driver
+#endif
+
+#ifdef CONFIG_SPARC_LEON
+ &ehci_grlib_driver
+#endif
+
+#if defined(CONFIG_USB_EHCI_MSM_72K) || defined(CONFIG_USB_EHCI_MSM)
+ &ehci_msm_driver,
+#endif
+
+#ifdef CONFIG_USB_EHCI_MSM_HSIC
+ &ehci_msm_hsic_driver
+#endif
+
+};
+
+
static int __init ehci_hcd_init(void)
{
- int retval = 0;
+ int i, retval = 0;
if (usb_disabled())
return -ENODEV;
@@ -1335,11 +1424,14 @@
}
#endif
-#ifdef PLATFORM_DRIVER
- retval = platform_driver_register(&PLATFORM_DRIVER);
- if (retval < 0)
- goto clean0;
-#endif
+ for (i = 0; i < ARRAY_SIZE(plat_drivers); i++) {
+ retval = platform_driver_register(plat_drivers[i]);
+ if (retval) {
+ while (--i >= 0)
+ platform_driver_unregister(plat_drivers[i]);
+ goto clean0;
+ }
+ }
#ifdef PCI_DRIVER
retval = pci_register_driver(&PCI_DRIVER);
@@ -1382,10 +1474,9 @@
pci_unregister_driver(&PCI_DRIVER);
clean1:
#endif
-#ifdef PLATFORM_DRIVER
- platform_driver_unregister(&PLATFORM_DRIVER);
+ for (i = 0; i < ARRAY_SIZE(plat_drivers); i++)
+ platform_driver_unregister(plat_drivers[i]);
clean0:
-#endif
#ifdef DEBUG
debugfs_remove(ehci_debug_root);
ehci_debug_root = NULL;
@@ -1398,15 +1489,17 @@
static void __exit ehci_hcd_cleanup(void)
{
+ int i;
#ifdef XILINX_OF_PLATFORM_DRIVER
platform_driver_unregister(&XILINX_OF_PLATFORM_DRIVER);
#endif
#ifdef OF_PLATFORM_DRIVER
platform_driver_unregister(&OF_PLATFORM_DRIVER);
#endif
-#ifdef PLATFORM_DRIVER
- platform_driver_unregister(&PLATFORM_DRIVER);
-#endif
+
+ for (i = 0; i < ARRAY_SIZE(plat_drivers); i++)
+ platform_driver_unregister(plat_drivers[i]);
+
#ifdef PCI_DRIVER
pci_unregister_driver(&PCI_DRIVER);
#endif
diff --git a/drivers/usb/host/ehci-msm-hsic.c b/drivers/usb/host/ehci-msm-hsic.c
new file mode 100644
index 0000000..1b8b5d6
--- /dev/null
+++ b/drivers/usb/host/ehci-msm-hsic.c
@@ -0,0 +1,795 @@
+/* ehci-msm-hsic.c - HSUSB Host Controller Driver Implementation
+ *
+ * Copyright (c) 2011, 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/msm_hsusb_hw.h>
+#include <linux/usb/msm_hsusb.h>
+#include <linux/gpio.h>
+#include <mach/clk.h>
+#include <mach/msm_iomap.h>
+
+#define MSM_USB_BASE (hcd->regs)
+
+struct msm_hsic_hcd {
+ struct ehci_hcd ehci;
+ struct device *dev;
+ struct clk *ahb_clk;
+ struct clk *sys_clk;
+ struct clk *fs_xcvr_clk;
+ struct clk *hsic_clk;
+ struct clk *cal_clk;
+ struct regulator *hsic_vddcx;
+ bool async_int;
+ atomic_t in_lpm;
+ struct wake_lock wlock;
+};
+
+static inline struct msm_hsic_hcd *hcd_to_hsic(struct usb_hcd *hcd)
+{
+ return (struct msm_hsic_hcd *) (hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *hsic_to_hcd(struct msm_hsic_hcd *mehci)
+{
+ return container_of((void *) mehci, struct usb_hcd, hcd_priv);
+}
+
+#define ULPI_IO_TIMEOUT_USEC (10 * 1000)
+
+#define USB_PHY_VDD_DIG_VOL_MIN 1000000 /* uV */
+#define USB_PHY_VDD_DIG_VOL_MAX 1320000 /* uV */
+#define USB_PHY_VDD_DIG_LOAD 49360 /* uA */
+
+static int msm_hsic_init_vddcx(struct msm_hsic_hcd *mehci, int init)
+{
+ int ret = 0;
+
+ if (!init)
+ goto disable_reg;
+
+ mehci->hsic_vddcx = regulator_get(mehci->dev, "HSIC_VDDCX");
+ if (IS_ERR(mehci->hsic_vddcx)) {
+ dev_err(mehci->dev, "unable to get hsic vddcx\n");
+ return PTR_ERR(mehci->hsic_vddcx);
+ }
+
+ ret = regulator_set_voltage(mehci->hsic_vddcx,
+ USB_PHY_VDD_DIG_VOL_MIN,
+ USB_PHY_VDD_DIG_VOL_MAX);
+ if (ret) {
+ dev_err(mehci->dev, "unable to set the voltage"
+ "for hsic vddcx\n");
+ goto reg_set_voltage_err;
+ }
+
+ ret = regulator_set_optimum_mode(mehci->hsic_vddcx,
+ USB_PHY_VDD_DIG_LOAD);
+ if (ret < 0) {
+ pr_err("%s: Unable to set optimum mode of the regulator:"
+ "VDDCX\n", __func__);
+ goto reg_optimum_mode_err;
+ }
+
+ ret = regulator_enable(mehci->hsic_vddcx);
+ if (ret) {
+ dev_err(mehci->dev, "unable to enable hsic vddcx\n");
+ goto reg_enable_err;
+ }
+
+ return 0;
+
+disable_reg:
+ regulator_disable(mehci->hsic_vddcx);
+reg_enable_err:
+ regulator_set_optimum_mode(mehci->hsic_vddcx, 0);
+reg_optimum_mode_err:
+ regulator_set_voltage(mehci->hsic_vddcx, 0,
+ USB_PHY_VDD_DIG_VOL_MIN);
+reg_set_voltage_err:
+ regulator_put(mehci->hsic_vddcx);
+
+ return ret;
+
+}
+
+static int ulpi_write(struct msm_hsic_hcd *mehci, u32 val, u32 reg)
+{
+ struct usb_hcd *hcd = hsic_to_hcd(mehci);
+ int cnt = 0;
+
+ /* initiate write operation */
+ writel_relaxed(ULPI_RUN | ULPI_WRITE |
+ ULPI_ADDR(reg) | ULPI_DATA(val),
+ USB_ULPI_VIEWPORT);
+
+ /* wait for completion */
+ while (cnt < ULPI_IO_TIMEOUT_USEC) {
+ if (!(readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_RUN))
+ break;
+ udelay(1);
+ cnt++;
+ }
+
+ if (cnt >= ULPI_IO_TIMEOUT_USEC) {
+ dev_err(mehci->dev, "ulpi_write: timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ 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;
+
+ clk_enable(mehci->fs_xcvr_clk);
+
+ ret = clk_reset(mehci->sys_clk, CLK_RESET_ASSERT);
+ if (ret) {
+ clk_disable(mehci->fs_xcvr_clk);
+ dev_err(mehci->dev, "usb phy clk assert failed\n");
+ return ret;
+ }
+ usleep_range(10000, 12000);
+ clk_disable(mehci->fs_xcvr_clk);
+
+ ret = clk_reset(mehci->sys_clk, CLK_RESET_DEASSERT);
+ if (ret)
+ dev_err(mehci->dev, "usb phy clk deassert failed\n");
+
+ return ret;
+}
+
+static int msm_hsic_phy_reset(struct msm_hsic_hcd *mehci)
+{
+ struct usb_hcd *hcd = hsic_to_hcd(mehci);
+ u32 val;
+ int ret;
+
+ ret = msm_hsic_phy_clk_reset(mehci);
+ if (ret)
+ return ret;
+
+ val = readl_relaxed(USB_PORTSC) & ~PORTSC_PTS_MASK;
+ writel_relaxed(val | PORTSC_PTS_ULPI, USB_PORTSC);
+
+ /* Ensure that RESET operation is completed before turning off clock */
+ mb();
+ dev_dbg(mehci->dev, "phy_reset: success\n");
+
+ 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)
+{
+ struct usb_hcd *hcd = hsic_to_hcd(mehci);
+ int cnt = 0;
+ int ret;
+
+ ret = msm_hsic_phy_reset(mehci);
+ if (ret) {
+ dev_err(mehci->dev, "phy_reset failed\n");
+ return ret;
+ }
+
+ writel_relaxed(USBCMD_RESET, USB_USBCMD);
+ while (cnt < LINK_RESET_TIMEOUT_USEC) {
+ if (!(readl_relaxed(USB_USBCMD) & USBCMD_RESET))
+ break;
+ udelay(1);
+ cnt++;
+ }
+ if (cnt >= LINK_RESET_TIMEOUT_USEC)
+ return -ETIMEDOUT;
+
+ /* select ULPI phy */
+ writel_relaxed(0x80000000, USB_PORTSC);
+
+ /* TODO: Need to confirm if HSIC PHY also requires delay after RESET */
+ 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 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;
+}
+
+#define PHY_SUSPEND_TIMEOUT_USEC (500 * 1000)
+#define PHY_RESUME_TIMEOUT_USEC (100 * 1000)
+
+#ifdef CONFIG_PM_SLEEP
+static int msm_hsic_suspend(struct msm_hsic_hcd *mehci)
+{
+ struct usb_hcd *hcd = hsic_to_hcd(mehci);
+ int cnt = 0;
+ u32 val;
+
+ if (atomic_read(&mehci->in_lpm)) {
+ dev_dbg(mehci->dev, "%s called in lpm\n", __func__);
+ return 0;
+ }
+
+ disable_irq(hcd->irq);
+ /*
+ * 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.
+ */
+ val = readl_relaxed(USB_PORTSC) | PORTSC_PHCD;
+ writel_relaxed(val, USB_PORTSC);
+ while (cnt < PHY_SUSPEND_TIMEOUT_USEC) {
+ if (readl_relaxed(USB_PORTSC) & PORTSC_PHCD)
+ break;
+ udelay(1);
+ cnt++;
+ }
+
+ 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;
+ }
+
+ /*
+ * 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(mehci->sys_clk);
+ clk_disable(mehci->hsic_clk);
+ clk_disable(mehci->cal_clk);
+ clk_disable(mehci->ahb_clk);
+
+ atomic_set(&mehci->in_lpm, 1);
+ enable_irq(hcd->irq);
+ wake_unlock(&mehci->wlock);
+
+ dev_info(mehci->dev, "HSIC-USB in low power mode\n");
+
+ return 0;
+}
+
+static int msm_hsic_resume(struct msm_hsic_hcd *mehci)
+{
+ struct usb_hcd *hcd = hsic_to_hcd(mehci);
+ int cnt = 0;
+ unsigned temp;
+
+ if (!atomic_read(&mehci->in_lpm)) {
+ dev_dbg(mehci->dev, "%s called in !in_lpm\n", __func__);
+ return 0;
+ }
+
+ wake_lock(&mehci->wlock);
+
+ clk_enable(mehci->sys_clk);
+ clk_enable(mehci->hsic_clk);
+ clk_enable(mehci->cal_clk);
+ clk_enable(mehci->ahb_clk);
+
+ 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);
+ while (cnt < PHY_RESUME_TIMEOUT_USEC) {
+ if (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD) &&
+ (readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_SYNC_STATE))
+ break;
+ udelay(1);
+ cnt++;
+ }
+
+ if (cnt >= PHY_RESUME_TIMEOUT_USEC) {
+ /*
+ * This is a fatal error. Reset the link and
+ * 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);
+ }
+
+skip_phy_resume:
+
+ atomic_set(&mehci->in_lpm, 0);
+
+ if (mehci->async_int) {
+ mehci->async_int = false;
+ pm_runtime_put_noidle(mehci->dev);
+ enable_irq(hcd->irq);
+ }
+
+ dev_info(mehci->dev, "HSIC-USB exited from low power mode\n");
+
+ return 0;
+}
+#endif
+
+static irqreturn_t msm_hsic_irq(struct usb_hcd *hcd)
+{
+ struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
+
+ if (atomic_read(&mehci->in_lpm)) {
+ disable_irq_nosync(hcd->irq);
+ mehci->async_int = true;
+ pm_runtime_get(mehci->dev);
+ return IRQ_HANDLED;
+ }
+
+ return ehci_irq(hcd);
+}
+
+static int ehci_hsic_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_hsic_driver = {
+ .description = hcd_name,
+ .product_desc = "Qualcomm EHCI Host Controller using HSIC",
+ .hcd_priv_size = sizeof(struct msm_hsic_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = msm_hsic_irq,
+ .flags = HCD_USB2 | HCD_MEMORY,
+
+ .reset = ehci_hsic_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_hsic_init_clocks(struct msm_hsic_hcd *mehci, u32 init)
+{
+ int ret = 0;
+
+ if (!init)
+ goto put_clocks;
+
+ /*sys_clk is required for LINK protocol engine,it should be at 60MHz */
+ mehci->sys_clk = clk_get(mehci->dev, "usb_hsic_system_clk");
+ if (IS_ERR(mehci->sys_clk)) {
+ dev_err(mehci->dev, "failed to get hsic_sys_clk\n");
+ ret = PTR_ERR(mehci->sys_clk);
+ return ret;
+ }
+ clk_set_rate(mehci->sys_clk, 60000000);
+
+ /* 60MHz fs_xcvr_clk is for LINK to be used during PHY RESET */
+ mehci->fs_xcvr_clk = clk_get(mehci->dev, "usb_hsic_xcvr_fs_clk");
+ if (IS_ERR(mehci->fs_xcvr_clk)) {
+ dev_err(mehci->dev, "failed to get fs_xcvr_clk\n");
+ ret = PTR_ERR(mehci->fs_xcvr_clk);
+ goto put_sys_clk;
+ }
+ clk_set_rate(mehci->fs_xcvr_clk, 60000000);
+
+ /* 480MHz hsic_clk is required for HSIC PHY operation */
+ mehci->hsic_clk = clk_get(mehci->dev, "usb_hsic_hsic_clk");
+ if (IS_ERR(mehci->hsic_clk)) {
+ dev_err(mehci->dev, "failed to get hsic_clk\n");
+ ret = PTR_ERR(mehci->hsic_clk);
+ goto put_fs_xcvr_clk;
+ }
+ clk_set_rate(mehci->hsic_clk, 480000000);
+
+ /* 10MHz cal_clk is required for calibration of I/O pads */
+ mehci->cal_clk = clk_get(mehci->dev, "usb_hsic_hsio_cal_clk");
+ if (IS_ERR(mehci->cal_clk)) {
+ dev_err(mehci->dev, "failed to get hsic_cal_clk\n");
+ ret = PTR_ERR(mehci->cal_clk);
+ goto put_hsic_clk;
+ }
+ clk_set_rate(mehci->cal_clk, 10000000);
+
+ /* ahb_clk is required for data transfers */
+ mehci->ahb_clk = clk_get(mehci->dev, "usb_hsic_p_clk");
+ if (IS_ERR(mehci->ahb_clk)) {
+ dev_err(mehci->dev, "failed to get hsic_ahb_clk\n");
+ ret = PTR_ERR(mehci->ahb_clk);
+ goto put_cal_clk;
+ }
+
+ clk_enable(mehci->sys_clk);
+ clk_enable(mehci->hsic_clk);
+ clk_enable(mehci->cal_clk);
+ clk_enable(mehci->ahb_clk);
+
+ return 0;
+
+put_clocks:
+ clk_disable(mehci->sys_clk);
+ clk_disable(mehci->hsic_clk);
+ clk_disable(mehci->cal_clk);
+ clk_disable(mehci->ahb_clk);
+ clk_put(mehci->ahb_clk);
+put_cal_clk:
+ clk_put(mehci->cal_clk);
+put_hsic_clk:
+ clk_put(mehci->hsic_clk);
+put_fs_xcvr_clk:
+ clk_put(mehci->fs_xcvr_clk);
+put_sys_clk:
+ clk_put(mehci->sys_clk);
+
+ return ret;
+}
+static int __devinit ehci_hsic_msm_probe(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+ struct resource *res;
+ struct msm_hsic_hcd *mehci;
+ int ret;
+
+ dev_dbg(&pdev->dev, "ehci_msm-hsic probe\n");
+
+ hcd = usb_create_hcd(&msm_hsic_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;
+ }
+
+ mehci = hcd_to_hsic(hcd);
+ mehci->dev = &pdev->dev;
+
+ ret = msm_hsic_init_clocks(mehci, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to initialize clocks\n");
+ ret = -ENODEV;
+ goto unmap;
+ }
+
+ ret = msm_hsic_init_vddcx(mehci, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to initialize VDDCX\n");
+ ret = -ENODEV;
+ goto deinit_clocks;
+ }
+
+ ret = msm_hsic_reset(mehci);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to initialize PHY\n");
+ goto deinit_vddcx;
+ }
+
+ ret = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to register HCD\n");
+ goto unconfig_gpio;
+ }
+
+ device_init_wakeup(&pdev->dev, 1);
+ wake_lock_init(&mehci->wlock, WAKE_LOCK_SUSPEND, dev_name(&pdev->dev));
+ wake_lock(&mehci->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;
+
+unconfig_gpio:
+ msm_hsic_config_gpios(mehci, 0);
+deinit_vddcx:
+ msm_hsic_init_vddcx(mehci, 0);
+deinit_clocks:
+ msm_hsic_init_clocks(mehci, 0);
+unmap:
+ iounmap(hcd->regs);
+put_hcd:
+ usb_put_hcd(hcd);
+
+ return ret;
+}
+
+static int __devexit ehci_hsic_msm_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
+
+ device_init_wakeup(&pdev->dev, 0);
+ pm_runtime_disable(&pdev->dev);
+ 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);
+ wake_lock_destroy(&mehci->wlock);
+ iounmap(hcd->regs);
+ usb_put_hcd(hcd);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int msm_hsic_pm_suspend(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
+
+ dev_dbg(dev, "ehci-msm-hsic PM suspend\n");
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(hcd->irq);
+
+ return msm_hsic_suspend(mehci);
+
+}
+
+static int msm_hsic_pm_resume(struct device *dev)
+{
+ int ret;
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
+
+ dev_dbg(dev, "ehci-msm-hsic PM resume\n");
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(hcd->irq);
+
+ ret = msm_hsic_resume(mehci);
+ 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 msm_hsic_runtime_idle(struct device *dev)
+{
+ dev_dbg(dev, "EHCI runtime idle\n");
+
+ return 0;
+}
+
+static int msm_hsic_runtime_suspend(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
+
+ dev_dbg(dev, "EHCI runtime suspend\n");
+ return msm_hsic_suspend(mehci);
+}
+
+static int msm_hsic_runtime_resume(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
+
+ dev_dbg(dev, "EHCI runtime resume\n");
+ return msm_hsic_resume(mehci);
+}
+#endif
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops msm_hsic_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(msm_hsic_pm_suspend, msm_hsic_pm_resume)
+ SET_RUNTIME_PM_OPS(msm_hsic_runtime_suspend, msm_hsic_runtime_resume,
+ msm_hsic_runtime_idle)
+};
+#endif
+
+static struct platform_driver ehci_msm_hsic_driver = {
+ .probe = ehci_hsic_msm_probe,
+ .remove = __devexit_p(ehci_hsic_msm_remove),
+ .driver = {
+ .name = "msm_hsic_host",
+#ifdef CONFIG_PM
+ .pm = &msm_hsic_dev_pm_ops,
+#endif
+ },
+};
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
diff --git a/include/linux/usb/msm_hsusb_hw.h b/include/linux/usb/msm_hsusb_hw.h
index 55dc134..79b0415 100644
--- a/include/linux/usb/msm_hsusb_hw.h
+++ b/include/linux/usb/msm_hsusb_hw.h
@@ -38,6 +38,7 @@
#define ULPI_RUN (1 << 30)
#define ULPI_WRITE (1 << 29)
#define ULPI_READ (0 << 29)
+#define ULPI_SYNC_STATE (1 << 27)
#define ULPI_ADDR(n) (((n) & 255) << 16)
#define ULPI_DATA(n) ((n) & 255)
#define ULPI_DATA_READ(n) (((n) >> 8) & 255)