[U-Boot] [RFC PATCH V3 1/2] ARM: OMAP4/5: Add alternative method for HSIC USB devices reset

Lubomir Popov lpopov at mm-sol.com
Thu Dec 19 17:26:00 CET 2013


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 at ti.com>
---
V1 and V2 got garbled during transmission. V3 is just a resend (again).

 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..47b4097 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..071739d 100644
--- a/drivers/usb/host/ehci-omap.c
+++ b/drivers/usb/host/ehci-omap.c
@@ -7,6 +7,13 @@
  *	Sunil Kumar <sunilsaini05 at gmail.com>
  *	Shashi Ranjan <shashiranjanmca05 at gmail.com>
  *
+ * (C) Copyright 2013 Lubomir Popov, MM Solutions <lpopov at 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.9.5


More information about the U-Boot mailing list