@@ -119,6 +119,12 @@ static struct descriptor {
#define ehci_is_TDI() (0)
#endif
+/* OMAP HSIC workaround option: */
+__weak void omap_ehci_hsic_reset_device(int port)
+{
+ return;
+}
+
int __ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg)
{
return PORTSC_PSPD(reg);
@@ -803,6 +809,15 @@ ehci_submit_root(struct usb_device *dev, unsigned long
pipe, void *buffer,
if (HCS_PPC(ehci_readl(&ctrl->hccr->cr_hcsparams))) {
reg |= EHCI_PS_PP;
ehci_writel(status_reg, reg);
+ /*
+ * OMAP4/5: Reset device for 'fail to connect'
+ * workaround. Weak function, actual reset
+ * should happen in ehci-omap.c and only if we
+ * have defined HSIC devices (in the board file)
+ * that we want to reset at this moment.
+ */
+ omap_ehci_hsic_reset_device(
+ le16_to_cpu(req->index));
}
break;
case USB_PORT_FEAT_RESET:
@@ -7,6 +7,13 @@
* Sunil Kumar <sunilsaini05@gmail.com>
* Shashi Ranjan <shashiranjanmca05@gmail.com>
*
+ * (C) Copyright 2013 Lubomir Popov, MM Solutions <lpopov@mm-sol.com>
+ * - Add option for individual reset of HSIC-connected USB devices by the
+ * ehci-hcd.c driver upon applying port power, with per-device configurable
+ * reset hold and delay times. This may replace the reset functionality via
+ * usb_hub.c and board file;
+ * - Make HSIC work on all OMAP5430-ES1.0 ports;
+ * - Add explanatory comments where appropriate.
*
* SPDX-License-Identifier: GPL-2.0+
*/
@@ -26,6 +33,8 @@ static struct omap_uhh *const uhh = (struct omap_uhh
*)OMAP_UHH_BASE;
static struct omap_usbtll *const usbtll = (struct omap_usbtll *)OMAP_USBTLL_BASE;
static struct omap_ehci *const ehci = (struct omap_ehci *)OMAP_EHCI_BASE;
+static struct omap_usbhs_board_data *usbhs_bdp = NULL;
+
static int omap_uhh_reset(void)
{
Add option for individual reset of HSIC-connected USB devices by the ehci-hcd.c driver upon applying port power, with per-device configurable reset hold and delay times. This may replace the reset functionality via usb_hub.c and board file (which does not work on some boards). Make HSIC work on all OMAP543x-ES1.0 ports. Signed-off-by: Lubomir Popov <l-popov@ti.com> --- drivers/usb/host/ehci-hcd.c | 15 ++++ drivers/usb/host/ehci-omap.c | 174 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 170 insertions(+), 19 deletions(-) int timeout = 0; @@ -106,7 +115,7 @@ static void omap_usbhs_hsic_init(int port) writel(reg, &usbtll->channel_conf + port); } -#ifdef CONFIG_USB_ULPI +#if defined(CONFIG_USB_ULPI) && defined(CONFIG_USB_ULPI_VIEWPORT_OMAP) static void omap_ehci_soft_phy_reset(int port) { struct ulpi_viewport ulpi_vp; @@ -158,10 +167,141 @@ static inline void omap_ehci_phy_reset(int on, int delay) #define omap_ehci_phy_reset(on, delay) do {} while (0) #endif +/* + * Individual HSIC USB device reset to fix 'fail to connect' for some devices. + * Note that a HSIC-connected device is actually a permanently attached USB + * slave, while a PHY is just a hardware extension of the host port, and + * handling them in the same manner is not appropriate. + * In order to invoke this feature, define CONFIG_OMAP_HSIC_PORTx_RESET_GPIO + * in the board header (where x is the port number with HSIC-attached device + * that we want to reset via this method, and the value is the number of the + * particular GPIO) - the real functions shall then build and override the + * __weak dummy in ehci-hcd.c that is called upon applying port power. The + * active reset hold time, as well as the delay after release of reset, are + * configurable per device (port) via CONFIG_OMAP_PORTx_RST_HOLD_US and + * CONFIG_OMAP_PORTx_DLY_AFTER_US. + * + * Applicable to OMAP4/5 only (except for the OMAP4430, where HSIC is not + * functional). Valid HSIC ports are: + * OMAP4460/70: 1, 2 + * OMAP5430: 1, 2, 3 + * OMAP5432: 2, 3 + */ +#if defined(CONFIG_OMAP_HSIC_PORT1_RESET_GPIO) || \ + defined(CONFIG_OMAP_HSIC_PORT2_RESET_GPIO) || \ + defined(CONFIG_OMAP_HSIC_PORT3_RESET_GPIO) +/* Should not be called for non-HSIC ports */ +static void omap_ehci_hsic_reset(int port, int on, int delay) +{ + debug("HSIC device reset: port %d, reset %s, delay %d us\n", + port, on ? "On" : "Off", delay); +#ifdef CONFIG_OMAP_HSIC_PORT1_RESET_GPIO + if (port == 1) { + gpio_request(CONFIG_OMAP_HSIC_PORT1_RESET_GPIO, + "USB HSIC1 Reset"); + gpio_direction_output(CONFIG_OMAP_HSIC_PORT1_RESET_GPIO, !on); + } +#endif +#ifdef CONFIG_OMAP_HSIC_PORT2_RESET_GPIO + if (port == 2) { + gpio_request(CONFIG_OMAP_HSIC_PORT2_RESET_GPIO, + "USB HSIC2 Reset"); + gpio_direction_output(CONFIG_OMAP_HSIC_PORT2_RESET_GPIO, !on); + } +#endif +#ifdef CONFIG_OMAP_HSIC_PORT3_RESET_GPIO + if (port == 3) { + gpio_request(CONFIG_OMAP_HSIC_PORT3_RESET_GPIO, + "USB HSIC3 Reset"); + gpio_direction_output(CONFIG_OMAP_HSIC_PORT3_RESET_GPIO, !on); + } +#endif + if (delay) + udelay(delay); +} + +/* + * Called by ehci-hcd when setting the USB_PORT_FEAT_POWER feature + * (overrides __weak function in ehci-hcd.c) + */ +void omap_ehci_hsic_reset_device(int port) +{ + int rst_hold; /* Reset active hold time, us */ + int dly_after; /* Delay after releasing reset, us */ + + if ((port <= 0) || + !(usbhs_bdp) || + !(is_ehci_hsic_mode(usbhs_bdp->port_mode[port-1]))) + return; + +#ifdef CONFIG_OMAP_HSIC_PORT1_RESET_GPIO + if (port == 1) { +#ifdef CONFIG_OMAP_PORT1_RST_HOLD_US + rst_hold = CONFIG_OMAP_PORT1_RST_HOLD_US; +#else + rst_hold = 10; /* Provide a default hold time */ +#endif +#ifdef CONFIG_OMAP_PORT1_DLY_AFTER_US + dly_after = CONFIG_OMAP_PORT1_DLY_AFTER_US; +#else + dly_after = 0; /* No delay by default */ +#endif + } +#endif + +#ifdef CONFIG_OMAP_HSIC_PORT2_RESET_GPIO + if (port == 2) { +#ifdef CONFIG_OMAP_PORT2_RST_HOLD_US + rst_hold = CONFIG_OMAP_PORT2_RST_HOLD_US; +#else + rst_hold = 10; /* Provide a default hold time */ +#endif +#ifdef CONFIG_OMAP_PORT2_DLY_AFTER_US + dly_after = CONFIG_OMAP_PORT2_DLY_AFTER_US; +#else + dly_after = 0; /* No delay by default */ +#endif + } +#endif + +#ifdef CONFIG_OMAP_HSIC_PORT3_RESET_GPIO + if (port == 3) { +#ifdef CONFIG_OMAP_PORT3_RST_HOLD_US + rst_hold = CONFIG_OMAP_PORT3_RST_HOLD_US; +#else + rst_hold = 10; /* Provide a default hold time */ +#endif +#ifdef CONFIG_OMAP_PORT3_DLY_AFTER_US + dly_after = CONFIG_OMAP_PORT3_DLY_AFTER_US; +#else + dly_after = 0; /* No delay by default */ +#endif + } +#endif + omap_ehci_hsic_reset(port, 1, rst_hold); + omap_ehci_hsic_reset(port, 0, dly_after); +} + +#else +/* No CONFIG_OMAP_HSIC_PORTx_RESET_GPIO defined */ +#define omap_ehci_hsic_reset(port, on, delay) do {} while (0) +#endif + /* Reset is needed otherwise the kernel-driver will throw an error. */ int omap_ehci_hcd_stop(void) { debug("Resetting OMAP EHCI\n"); + /* Put HSIC devices, if any, in RESET */ +#ifdef CONFIG_OMAP_HSIC_PORT1_RESET_GPIO + omap_ehci_hsic_reset(1, 1, 0); +#endif +#ifdef CONFIG_OMAP_HSIC_PORT2_RESET_GPIO + omap_ehci_hsic_reset(2, 1, 0); +#endif +#ifdef CONFIG_OMAP_HSIC_PORT3_RESET_GPIO + omap_ehci_hsic_reset(3, 1, 0); +#endif + /* Reset PHYs, if any */ omap_ehci_phy_reset(1, 0); if (omap_uhh_reset() < 0) @@ -184,13 +324,15 @@ int omap_ehci_hcd_init(int index, struct omap_usbhs_board_data *usbhs_pdata, int ret; unsigned int i, reg = 0, rev = 0; - debug("Initializing OMAP EHCI\n"); + debug("Initializing OMAP EHCI %d\n", index); ret = board_usb_init(index, USB_INIT_HOST); if (ret < 0) return ret; - /* Put the PHY in RESET */ + usbhs_bdp = usbhs_pdata; + + /* Put the PHYs, if any, in RESET */ omap_ehci_phy_reset(1, 10); ret = omap_uhh_reset(); @@ -230,35 +372,28 @@ int omap_ehci_hcd_init(int index, struct omap_usbhs_board_data *usbhs_pdata, clrbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS); else setbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS); - } else if (rev == OMAP_USBHS_REV2) { - - clrsetbits_le32(®, (OMAP_P1_MODE_CLEAR | OMAP_P2_MODE_CLEAR), - OMAP4_UHH_HOSTCONFIG_APP_START_CLK); - - /* Clear port mode fields for PHY mode */ - - if (is_ehci_hsic_mode(usbhs_pdata->port_mode[0])) - setbits_le32(®, OMAP_P1_MODE_HSIC); - - if (is_ehci_hsic_mode(usbhs_pdata->port_mode[1])) - setbits_le32(®, OMAP_P2_MODE_HSIC); - - } else if (rev == OMAP_USBHS_REV2_1) { + } else if ((rev == OMAP_USBHS_REV2) || (rev == OMAP_USBHS_REV2_1)) { + /* + * OMAP4 and OMAP5-ES1 UHH are R.2.0, OMAP5-ES2 - R.2.1 + * + * Clear port mode fields for ULPI PHY mode. On OMAP4 the P3 + * field is reserved, but clearing it does not harm. + */ clrsetbits_le32(®, (OMAP_P1_MODE_CLEAR | OMAP_P2_MODE_CLEAR | OMAP_P3_MODE_CLEAR), OMAP4_UHH_HOSTCONFIG_APP_START_CLK); - /* Clear port mode fields for PHY mode */ - + /* Warning: HSIC mode for Port 1 not usable on OMAP5432 */ if (is_ehci_hsic_mode(usbhs_pdata->port_mode[0])) setbits_le32(®, OMAP_P1_MODE_HSIC); if (is_ehci_hsic_mode(usbhs_pdata->port_mode[1])) setbits_le32(®, OMAP_P2_MODE_HSIC); + /* Warning: HSIC mode for Port 3 possible on OMAP5 only */ if (is_ehci_hsic_mode(usbhs_pdata->port_mode[2])) setbits_le32(®, OMAP_P3_MODE_HSIC); } @@ -270,6 +405,7 @@ int omap_ehci_hcd_init(int index, struct omap_usbhs_board_data *usbhs_pdata, if (is_ehci_hsic_mode(usbhs_pdata->port_mode[i])) omap_usbhs_hsic_init(i); + /* Release ULPI PHY reset and let PLL lock (may need more delay...) */ omap_ehci_phy_reset(0, 10); /*