From c0d722fe7ee1cb452dfd9246419188b3f6d9c4df Mon Sep 17 00:00:00 2001 From: Remy Böhmer Date: Sat, 13 Dec 2008 22:51:58 +0100 Subject: EHCI fix code and ixp4xx test. USB ehci configuration parameter: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #define CONFIG_CMD_USB 1 #define CONFIG_USB_STORAGE 1 #define CONFIG_USB_EHCI #define CONFIG_USB_EHCI_IXP4XX 1 #define CONFIG_EHCI_IS_TDI 1 #define CONFIG_EHCI_DESC_BIG_ENDIAN 1 #define CONFIG_EHCI_MMIO_BIG_ENDIAN 1 #define CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS 2 #define CONFIG_LEGACY_USB_INIT_SEQ 1 2 USB Device(s) found scanning bus for storage devices... 0 Storage Device(s) found => usb tree Device Tree: 1 Hub (1.5MBit/s, 0mA) | u-boot EHCI Host Controller | |+-2 Mass Storage (12MBit/s, 100mA) Sony Storage Media 0C07040930296 => Signed-off-by: Michael Trimarchi Signed-off-by: Remy Böhmer --- drivers/usb/usb_ehci_core.c | 127 +++++++++++++++++++++++++++----------------- 1 file changed, 78 insertions(+), 49 deletions(-) (limited to 'drivers/usb/usb_ehci_core.c') diff --git a/drivers/usb/usb_ehci_core.c b/drivers/usb/usb_ehci_core.c index ec50874f71..07e9a6b8a0 100644 --- a/drivers/usb/usb_ehci_core.c +++ b/drivers/usb/usb_ehci_core.c @@ -1,6 +1,8 @@ /*- * Copyright (c) 2007-2008, Juniper Networks, Inc. * Copyright (c) 2008, Excito Elektronik i Skåne AB + * Copyright (c) 2008, Michael Trimarchi + * * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -18,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ -#define DEBUG #include #include #include @@ -99,6 +100,12 @@ static struct descriptor { }, }; +#if defined(CONFIG_EHCI_IS_TDI) +#define ehci_is_TDI() (1) +#else +#define ehci_is_TDI() (0) +#endif + static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int msec) { uint32_t result; @@ -131,17 +138,17 @@ static int ehci_reset(void) cmd = ehci_readl(&hcor->or_usbcmd); cmd |= CMD_RESET; ehci_writel(&hcor->or_usbcmd, cmd); - ret = handshake(&hcor->or_usbcmd, CMD_RESET, 0, 250); + ret = handshake((uint32_t *)&hcor->or_usbcmd, CMD_RESET, 0, 250); if (ret < 0) { printf("EHCI fail to reset\n"); goto out; } -#if defined CONFIG_EHCI_IS_TDI +#if defined(CONFIG_EHCI_IS_TDI) reg_ptr = (uint32_t *)((u8 *)hcor + USBMODE); tmp = ehci_readl(reg_ptr); tmp |= USBMODE_CM_HC; -#if defined CONFIG_EHCI_MMIO_BIG_ENDIAN +#if defined(CONFIG_EHCI_MMIO_BIG_ENDIAN) tmp |= USBMODE_BE; #endif ehci_writel(reg_ptr, tmp); @@ -427,7 +434,15 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, void *srcptr = NULL; int len, srclen; uint32_t reg; + uint32_t *status_reg; + if (le16_to_cpu(req->index) >= CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) { + printf("The request port(%d) is not configured\n", + le16_to_cpu(req->index) - 1); + return -1; + } + status_reg = (uint32_t *)&hcor->or_portsc[ + le16_to_cpu(req->index) - 1]; srclen = 0; debug("req=%u (%#x), type=%u (%#x), value=%u, index=%u\n", @@ -506,8 +521,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, break; case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8): memset(tmpbuf, 0, 4); - reg = ehci_readl(&hcor->or_portsc[le16_to_cpu(req->index) - - 1]); + reg = ehci_readl(status_reg); if (reg & EHCI_PS_CS) tmpbuf[0] |= USB_PORT_STAT_CONNECTION; if (reg & EHCI_PS_PE) @@ -516,8 +530,19 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, tmpbuf[0] |= USB_PORT_STAT_SUSPEND; if (reg & EHCI_PS_OCA) tmpbuf[0] |= USB_PORT_STAT_OVERCURRENT; - if (reg & EHCI_PS_PR) - tmpbuf[0] |= USB_PORT_STAT_RESET; + if (reg & EHCI_PS_PR && + (portreset & (1 << le16_to_cpu(req->index)))) { + int ret; + /* force reset to complete */ + reg = reg & ~(EHCI_PS_PR | EHCI_PS_CLEAR); + ehci_writel(status_reg, reg); + ret = handshake(status_reg, EHCI_PS_PR, 0, 2); + if (!ret) + tmpbuf[0] |= USB_PORT_STAT_RESET; + else + printf("port(%d) reset error\n", + le16_to_cpu(req->index) - 1); + } if (reg & EHCI_PS_PP) tmpbuf[1] |= USB_PORT_STAT_POWER >> 8; tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8; @@ -530,73 +555,71 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, tmpbuf[2] |= USB_PORT_STAT_C_OVERCURRENT; if (portreset & (1 << le16_to_cpu(req->index))) tmpbuf[2] |= USB_PORT_STAT_C_RESET; + srcptr = tmpbuf; srclen = 4; break; case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): - reg = ehci_readl(&hcor->or_portsc[le16_to_cpu(req->index) - 1]); + reg = ehci_readl(status_reg); reg &= ~EHCI_PS_CLEAR; switch (le16_to_cpu(req->value)) { case USB_PORT_FEAT_ENABLE: reg |= EHCI_PS_PE; + ehci_writel(status_reg, reg); break; case USB_PORT_FEAT_POWER: - reg |= EHCI_PS_PP; + if (HCS_PPC(ehci_readl(&hccr->cr_hcsparams))) { + reg |= EHCI_PS_PP; + ehci_writel(status_reg, reg); + } break; case USB_PORT_FEAT_RESET: - debug("USB FEAT RESET\n"); - if (EHCI_PS_IS_LOWSPEED(reg)) { + if ((reg & (EHCI_PS_PE | EHCI_PS_CS)) == EHCI_PS_CS && + !ehci_is_TDI() && + EHCI_PS_IS_LOWSPEED(reg)) { /* Low speed device, give up ownership. */ + debug("port %d low speed --> companion\n", + req->index - 1); reg |= EHCI_PS_PO; + ehci_writel(status_reg, reg); break; + } else { + reg |= EHCI_PS_PR; + reg &= ~EHCI_PS_PE; + ehci_writel(status_reg, reg); + /* + * caller must wait, then call GetPortStatus + * usb 2.0 specification say 50 ms resets on + * root + */ + wait_ms(50); + portreset |= 1 << le16_to_cpu(req->index); } - /* Start reset sequence. */ - reg &= ~EHCI_PS_PE; - reg |= EHCI_PS_PR; - ehci_writel(&hcor->or_portsc[ - le16_to_cpu(req->index) - 1], reg); - /* Wait for reset to complete. */ - wait_ms(500); - /* Terminate reset sequence. */ - reg &= ~EHCI_PS_PR; - /* TODO: is it only fsl chip that requires this - * manual setting of port enable? - */ - reg |= EHCI_PS_PE; - ehci_writel(&hcor->or_portsc[ - le16_to_cpu(req->index) - 1], reg); - /* Wait for HC to complete reset. */ - wait_ms(10); - reg = - ehci_readl(&hcor->or_portsc[le16_to_cpu(req->index) - - 1]); - reg &= ~EHCI_PS_CLEAR; - if ((reg & EHCI_PS_PE) == 0) { - /* Not a high speed device, give up - * ownership. */ - reg |= EHCI_PS_PO; - break; - } - portreset |= 1 << le16_to_cpu(req->index); break; default: debug("unknown feature %x\n", le16_to_cpu(req->value)); goto unknown; } - ehci_writel(&hcor->or_portsc[le16_to_cpu(req->index) - 1], reg); + /* unblock posted writes */ + ehci_readl(&hcor->or_usbcmd); break; case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): - reg = ehci_readl(&hcor->or_portsc[le16_to_cpu(req->index) - 1]); - reg &= ~EHCI_PS_CLEAR; + reg = ehci_readl(status_reg); switch (le16_to_cpu(req->value)) { case USB_PORT_FEAT_ENABLE: reg &= ~EHCI_PS_PE; break; + case USB_PORT_FEAT_C_ENABLE: + reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_PE; + break; + case USB_PORT_FEAT_POWER: + if (HCS_PPC(ehci_readl(&hccr->cr_hcsparams))) + reg = reg & ~(EHCI_PS_CLEAR | EHCI_PS_PP); case USB_PORT_FEAT_C_CONNECTION: - reg |= EHCI_PS_CSC; + reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_CSC; break; case USB_PORT_FEAT_OVER_CURRENT: - reg |= EHCI_PS_OCC; + reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_OCC; break; case USB_PORT_FEAT_C_RESET: portreset &= ~(1 << le16_to_cpu(req->index)); @@ -605,7 +628,9 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, debug("unknown feature %x\n", le16_to_cpu(req->value)); goto unknown; } - ehci_writel(&hcor->or_portsc[le16_to_cpu(req->index) - 1], reg); + ehci_writel(status_reg, reg); + /* unblock posted write */ + ehci_readl(&hcor->or_usbcmd); break; default: debug("Unknown request\n"); @@ -665,9 +690,11 @@ int usb_lowlevel_init(void) reg = ehci_readl(&hccr->cr_hcsparams); descriptor.hub.bNbrPorts = HCS_N_PORTS(reg); printf("Register %x NbrPorts %d\n", reg, descriptor.hub.bNbrPorts); - if (reg & 0x10000) /* Port Indicators */ + /* Port Indicators */ + if (HCS_INDICATOR(reg)) descriptor.hub.wHubCharacteristics |= 0x80; - if (reg & 0x10) /* Port Power Control */ + /* Port Power Control */ + if (HCS_PPC(reg)) descriptor.hub.wHubCharacteristics |= 0x01; /* Start the host controller. */ @@ -682,9 +709,11 @@ int usb_lowlevel_init(void) cmd = ehci_readl(&hcor->or_configflag); cmd |= FLAG_CF; ehci_writel(&hcor->or_configflag, cmd); - /* unblock posted writes */ + /* unblock posted write */ cmd = ehci_readl(&hcor->or_usbcmd); wait_ms(5); + reg = HC_VERSION(ehci_readl(&hccr->cr_capbase)); + printf("USB EHCI %x.%02x\n", reg >> 8, reg & 0xff); rootdev = 0; -- cgit