Patchwork [U-Boot] OMAP4/5: Add USB EHCI support

login
register
mail settings
Submitter Lubomir Popov
Date April 1, 2013, 2:41 p.m.
Message ID <51599C9A.5090902@mm-sol.com>
Download mbox | patch
Permalink /patch/232742/
State Superseded
Delegated to: Tom Rini
Headers show

Comments

Lubomir Popov - April 1, 2013, 2:41 p.m.
Prerequisites (apart from proper config):

1. The HSUSBTLL clock has to be enabled along with the other
   HW_AUTO essentials, that is, cm_l3init_hsusbtll_clkctrl has
   to reside in the clk_modules_hw_auto_essential[] array and
   not in clk_modules_explicit_en_essential[]. Please see
   related patch to hw_data.c.

2. Other functional clocks have to be enabled prior to starting
   USB (and, possibly, disabled upon stopping). The proper place
   for this would be the board file, in the ehci_hcd_init() and
   ehci_hcd_stop() functions. Please see som5_evb.c as example.

3. If we have HSIC devices on some OMAP5 ports, they shall work
   only if the design employs reset capability via GPIO. This is
   performed by the ehci-hcd driver upon applying port power, and
   requires that we have defined CONFIG_OMAP_HSIC_PORTx_RESET_GPIO
   in the board config header as appropriate. Please see
   omap5_som5_evb.h as an example.
   Note that this additional reset is not needed for OMAP4 HSIC
   (tested on OMAP4460 and 4470).

4. Can somebody explain why arch/arm/include/asm/arch-omap5/ehci.h
   has disappeared? It is in fact needed for EHCI USB. Adding it.

Signed-off-by: Lubomir Popov <lpopov@mm-sol.com>
---
 arch/arm/include/asm/arch-omap5/ehci.h |   44 +++++++++++++++++
 drivers/usb/host/ehci-hcd.c            |    6 +++
 drivers/usb/host/ehci-omap.c           |   83 +++++++++++++++++++++++++++++---
 drivers/usb/host/ehci.h                |    9 ++++
 4 files changed, 135 insertions(+), 7 deletions(-)
 create mode 100644 arch/arm/include/asm/arch-omap5/ehci.h

Patch

diff --git a/arch/arm/include/asm/arch-omap5/ehci.h b/arch/arm/include/asm/arch-omap5/ehci.h
new file mode 100644
index 0000000..49197f2
--- /dev/null
+++ b/arch/arm/include/asm/arch-omap5/ehci.h
@@ -0,0 +1,44 @@ 
+/*
+ * OMAP EHCI port support
+ * Based on LINUX KERNEL
+ * drivers/usb/host/ehci-omap.c and drivers/mfd/omap-usb-host.c
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com*
+ * Author: Govindraj R <govindraj.raja@ti.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2  of
+ * the License 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _EHCI_H
+#define _EHCI_H
+
+#define OMAP_EHCI_BASE				(OMAP54XX_L4_CORE_BASE + 0x64C00)
+#define OMAP_UHH_BASE				(OMAP54XX_L4_CORE_BASE + 0x64000)
+#define OMAP_USBTLL_BASE			(OMAP54XX_L4_CORE_BASE + 0x62000)
+
+/* TLL Register Set */
+#define OMAP_USBTLL_SYSCONFIG_SIDLEMODE		(1 << 3)
+#define OMAP_USBTLL_SYSCONFIG_ENAWAKEUP		(1 << 2)
+#define OMAP_USBTLL_SYSCONFIG_SOFTRESET		(1 << 1)
+#define OMAP_USBTLL_SYSCONFIG_CACTIVITY		(1 << 8)
+#define OMAP_USBTLL_SYSSTATUS_RESETDONE		1
+
+#define OMAP_UHH_SYSCONFIG_SOFTRESET		1
+#define OMAP_UHH_SYSSTATUS_EHCI_RESETDONE	(1 << 2)
+#define OMAP_UHH_SYSCONFIG_NOIDLE		(1 << 2)
+#define OMAP_UHH_SYSCONFIG_NOSTDBY		(1 << 4)
+
+#define OMAP_UHH_SYSCONFIG_VAL	(OMAP_UHH_SYSCONFIG_NOIDLE | \
+					OMAP_UHH_SYSCONFIG_NOSTDBY)
+
+#endif /* _EHCI_H */
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index c816878..33d954d 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -750,6 +750,12 @@  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);
+    				/*
+				 * OMAP5: Reset device for 'fail to connect'
+				 * workaround. Compiled only for OMAP5, and
+				 * only if we have ports in HSIC mode.
+				 */
+				omap5_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 086c697..07f3774 100644
--- a/drivers/usb/host/ehci-omap.c
+++ b/drivers/usb/host/ehci-omap.c
@@ -92,12 +92,15 @@  static void omap_usbhs_hsic_init(int port)
 
 static void omap_ehci_soft_phy_reset(int port)
 {
+	/* This requires proper configs: */
+#if defined(CONFIG_USB_ULPI) && defined(CONFIG_USB_ULPI_VIEWPORT_OMAP)
 	struct ulpi_viewport ulpi_vp;
 
 	ulpi_vp.viewport_addr = (u32)&ehci->insreg05_utmi_ulpi;
 	ulpi_vp.port_num = port;
 
 	ulpi_reset(&ulpi_vp);
+#endif
 }
 
 inline int __board_usb_init(void)
@@ -107,7 +110,7 @@  inline int __board_usb_init(void)
 int board_usb_init(void) __attribute__((weak, alias("__board_usb_init")));
 
 #if defined(CONFIG_OMAP_EHCI_PHY1_RESET_GPIO) || \
-	defined(CONFIG_OMAP_EHCI_PHY2_RESET_GPIO)
+    defined(CONFIG_OMAP_EHCI_PHY2_RESET_GPIO)
 /* controls PHY(s) reset signal(s) */
 static inline void omap_ehci_phy_reset(int on, int delay)
 {
@@ -136,10 +139,71 @@  static inline void omap_ehci_phy_reset(int on, int delay)
 #define omap_ehci_phy_reset(on, delay)	do {} while (0)
 #endif
 
+/* Separate HSIC USB devices reset to fix fail to connect on OMAP5.
+ * Patch needed in ehci-hcd. See driver code.
+ */
+#if defined(CONFIG_OMAP_HSIC_PORT1_RESET_GPIO) || \
+    defined(CONFIG_OMAP_HSIC_PORT2_RESET_GPIO) || \
+    defined(CONFIG_OMAP_HSIC_PORT3_RESET_GPIO)
+static inline void omap_ehci_hsic_reset(int port, int on, int delay)
+{
+	/*printf("omap_ehci_hsic_reset: port %d, reset %s, delay %d\n", port, on ? "On" : "Off", delay);*/
+	
+#ifdef CONFIG_OMAP_HSIC_PORT1_RESET_GPIO
+	if (1 == port) {
+		gpio_request(CONFIG_OMAP_HSIC_PORT1_RESET_GPIO, "USB HSIC1 reset");
+		gpio_direction_output(CONFIG_OMAP_HSIC_PORT1_RESET_GPIO, !on);
+		udelay(delay);
+	}
+#endif
+#ifdef CONFIG_OMAP_HSIC_PORT2_RESET_GPIO
+	if (2 == port) {
+		gpio_request(CONFIG_OMAP_HSIC_PORT2_RESET_GPIO, "USB HSIC2 reset");
+		gpio_direction_output(CONFIG_OMAP_HSIC_PORT2_RESET_GPIO, !on);
+		udelay(delay);
+	}
+#endif
+#ifdef CONFIG_OMAP_HSIC_PORT3_RESET_GPIO
+	if (3 == port) {
+		gpio_request(CONFIG_OMAP_HSIC_PORT3_RESET_GPIO, "USB HSIC3 reset");
+		gpio_direction_output(CONFIG_OMAP_HSIC_PORT3_RESET_GPIO, !on);
+		udelay(delay);
+	}
+#endif
+}
+#else
+#define omap_ehci_hsic_reset(port, on, delay)	do {} while (0)
+#endif
+
+#if defined(CONFIG_OMAP54XX) && \
+   (defined(CONFIG_OMAP_HSIC_PORT1_RESET_GPIO) || \
+    defined(CONFIG_OMAP_HSIC_PORT2_RESET_GPIO) || \
+    defined(CONFIG_OMAP_HSIC_PORT3_RESET_GPIO))
+/* OMAP5: Reset HSIC devices upon applying port power for
+ *        'fail to connect' workaround. Called by ehci-hcd.
+ */
+inline void omap5_ehci_hsic_reset_device(int port_index)
+{
+	omap_ehci_hsic_reset(port_index, 1, 100);
+	omap_ehci_hsic_reset(port_index, 0, 0);
+}
+#else
+inline void omap5_ehci_hsic_reset_device(int port_index) {}
+#endif
+
 /* Reset is needed otherwise the kernel-driver will throw an error. */
 int omap_ehci_hcd_stop(void)
 {
 	debug("Resetting OMAP EHCI\n");
+#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
 	omap_ehci_phy_reset(1, 0);
 
 	if (omap_uhh_reset() < 0)
@@ -168,7 +232,7 @@  int omap_ehci_hcd_init(struct omap_usbhs_board_data *usbhs_pdata,
 	if (ret < 0)
 		return ret;
 
-	/* Put the PHY in RESET */
+	/* Put the PHYs (if any) in RESET */
 	omap_ehci_phy_reset(1, 10);
 
 	ret = omap_uhh_reset();
@@ -209,11 +273,10 @@  int omap_ehci_hcd_init(struct omap_usbhs_board_data *usbhs_pdata,
 		else
 			setbits_le32(&reg, OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS);
 	} else if (rev == OMAP_USBHS_REV2) {
+		/* Clear port mode fields for PHY mode */
 		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);
 
@@ -227,10 +290,16 @@  int omap_ehci_hcd_init(struct omap_usbhs_board_data *usbhs_pdata,
 	debug("OMAP UHH_REVISION 0x%x\n", rev);
 	writel(reg, &uhh->hostconfig);
 
-	for (i = 0; i < OMAP_HS_USB_PORTS; i++)
-		if (is_ehci_hsic_mode(usbhs_pdata->port_mode[i]))
+	/* OMAP5 HSIC devices (if any) shall be reset again by the
+	 * ehci-hcd driver upon applying port power. See driver patch.
+	 */
+	for (i = 0; i < OMAP_HS_USB_PORTS; i++) {
+		if (is_ehci_hsic_mode(usbhs_pdata->port_mode[i])) {
 			omap_usbhs_hsic_init(i);
-
+			omap_ehci_hsic_reset(i + 1, 0, 0);	/* Release reset on HSIC port */
+		}
+	}
+	/* Release ULPI PHY reset and let PLL lock */
 	omap_ehci_phy_reset(0, 10);
 
 	/*
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index d090f0a..9e0230b 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -256,4 +256,13 @@  struct QH {
 int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor);
 int ehci_hcd_stop(int index);
 
+/* OMAP5: Reset HSIC devices upon applying port power for
+ *        'fail to connect' workaround (ehci-omap.c)
+ */
+#ifndef CONFIG_USB_EHCI_OMAP
+static inline void omap5_ehci_hsic_reset_device(int port_index) {}
+#else
+inline void omap5_ehci_hsic_reset_device(int port_index);
+#endif
+
 #endif /* USB_EHCI_H */