[U-Boot] [PATCH] OMAP4/5: Add USB EHCI support
Lubomir Popov
lpopov at mm-sol.com
Mon Apr 1 16:41:30 CEST 2013
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 at 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
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 at 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(®, OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS);
} else if (rev == OMAP_USBHS_REV2) {
+ /* Clear port mode fields for PHY mode */
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);
@@ -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 */
--
1.7.9.5
More information about the U-Boot
mailing list