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 <lpo...@mm-sol.com>
---
V4 is just a resend from another machine. V1/2/3 were corrupted during
transmission.

 drivers/usb/host/ehci-hcd.c  |  15 ++++
 drivers/usb/host/ehci-omap.c | 174 ++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 170 insertions(+), 19 deletions(-)

diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 8bd1eb8..17efb69 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -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:
diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c
index 1b215c2..d3609a4 100644
--- a/drivers/usb/host/ehci-omap.c
+++ b/drivers/usb/host/ehci-omap.c
@@ -7,6 +7,13 @@
  *     Sunil Kumar <sunilsain...@gmail.com>
  *     Shashi Ranjan <shashiranjanmc...@gmail.com>
  *
+ * (C) Copyright 2013 Lubomir Popov, MM Solutions <lpo...@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;
+
 static int omap_uhh_reset(void)
 {
        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(&reg, OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS);
                else
                        setbits_le32(&reg, OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS);
-       } else if (rev == OMAP_USBHS_REV2) {
-
-               clrsetbits_le32(&reg, (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(&reg, OMAP_P1_MODE_HSIC);
-
-               if (is_ehci_hsic_mode(usbhs_pdata->port_mode[1]))
-                       setbits_le32(&reg, 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(&reg,
                                (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(&reg, OMAP_P1_MODE_HSIC);

                if (is_ehci_hsic_mode(usbhs_pdata->port_mode[1]))
                        setbits_le32(&reg, 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(&reg, 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);

        /*
-- 
1.7.12.4 (Apple Git-37)

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to