From patchwork Thu Dec 19 16:26:00 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lubomir Popov X-Patchwork-Id: 303618 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 7FBD62C009E for ; Fri, 20 Dec 2013 03:26:20 +1100 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id CDFAD4A874; Thu, 19 Dec 2013 17:26:16 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id NmyV1bt+mSjl; Thu, 19 Dec 2013 17:26:16 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 738354A876; Thu, 19 Dec 2013 17:26:15 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id AE7C94A876 for ; Thu, 19 Dec 2013 17:26:12 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id ut3pEtFlyNiw for ; Thu, 19 Dec 2013 17:26:07 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 BL_NJABL=SKIP(-1.5) (only DNSBL check requested) Received: from extserv.mm-sol.com (ns.mm-sol.com [37.157.136.199]) by theia.denx.de (Postfix) with ESMTPS id 328444A874 for ; Thu, 19 Dec 2013 17:26:06 +0100 (CET) Received: from [10.167.144.42] (unknown [37.157.136.199]) by extserv.mm-sol.com (Postfix) with ESMTPSA id 48DA8C717; Thu, 19 Dec 2013 18:26:02 +0200 (EET) Message-ID: <52B31E18.2070801@mm-sol.com> Date: Thu, 19 Dec 2013 18:26:00 +0200 From: Lubomir Popov User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:24.0) Gecko/20100101 Thunderbird/24.2.0 MIME-Version: 1.0 To: u-boot Cc: Tom Rini Subject: [U-Boot] [RFC PATCH V3 1/2] ARM: OMAP4/5: Add alternative method for HSIC USB devices reset X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.11 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de 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 --- 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(-) 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); /* 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 * Shashi Ranjan * + * (C) Copyright 2013 Lubomir Popov, MM Solutions + * - 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) {