[U-Boot] [RFC PATCH V4 1/2] ARM: OMAP4/5: Add alternative method for HSIC USB devices reset
Lubomir Popov
lpopov at mm-sol.com
Thu Dec 19 22:12:06 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 <lpopov at 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 <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(®, 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);
/*
--
1.7.12.4 (Apple Git-37)
More information about the U-Boot
mailing list