diff options
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/usb_ehci.h | 51 | ||||
-rw-r--r-- | drivers/usb/usb_ehci_core.c | 127 |
2 files changed, 106 insertions, 72 deletions
diff --git a/drivers/usb/usb_ehci.h b/drivers/usb/usb_ehci.h index 90b137a..b72498b 100644 --- a/drivers/usb/usb_ehci.h +++ b/drivers/usb/usb_ehci.h @@ -1,5 +1,6 @@ /*- * Copyright (c) 2007-2008, Juniper Networks, Inc. + * Copyright (c) 2008, Michael Trimarchi <trimarchimichael@yahoo.it> * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -21,6 +22,10 @@ #ifndef USB_EHCI_H #define USB_EHCI_H +#if !defined(CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) +#define CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS 2 +#endif + /* (shifted) direction/type/recipient from the USB 2.0 spec, table 9.2 */ #define DeviceRequest \ ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE) << 8) @@ -45,10 +50,12 @@ struct ehci_hccr { #define HC_LENGTH(p) (((p) >> 0) & 0x00ff) #define HC_VERSION(p) (((p) >> 16) & 0xffff) uint32_t cr_hcsparams; +#define HCS_PPC(p) ((p) & (1 << 4)) +#define HCS_INDICATOR(p) ((p) & (1 << 16)) /* Port indicators */ #define HCS_N_PORTS(p) (((p) >> 0) & 0xf) uint32_t cr_hccparams; uint8_t cr_hcsp_portrt[8]; -}; +} __attribute__ ((packed)); struct ehci_hcor { uint32_t or_usbcmd; @@ -71,9 +78,9 @@ struct ehci_hcor { uint32_t _reserved_[9]; uint32_t or_configflag; #define FLAG_CF (1 << 0) /* true: we'll support "high speed" */ - uint32_t or_portsc[2]; + uint32_t or_portsc[CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS]; uint32_t or_systune; -}; +} __attribute__ ((packed)); #define USBMODE 0x68 /* USB Device mode */ #define USBMODE_SDIS (1 << 3) /* Stream disable */ @@ -123,26 +130,24 @@ struct usb_linux_config_descriptor { #define cpu_to_hc32(x) cpu_to_le32((x)) #endif -#define EHCI_PS_WKOC_E 0x00400000 /* RW wake on over current */ -#define EHCI_PS_WKDSCNNT_E 0x00200000 /* RW wake on disconnect */ -#define EHCI_PS_WKCNNT_E 0x00100000 /* RW wake on connect */ -#define EHCI_PS_PTC 0x000f0000 /* RW port test control */ -#define EHCI_PS_PIC 0x0000c000 /* RW port indicator control */ -#define EHCI_PS_PO 0x00002000 /* RW port owner */ -#define EHCI_PS_PP 0x00001000 /* RW,RO port power */ -#define EHCI_PS_LS 0x00000c00 /* RO line status */ -#define EHCI_PS_PR 0x00000100 /* RW port reset */ -#define EHCI_PS_SUSP 0x00000080 /* RW suspend */ -#define EHCI_PS_FPR 0x00000040 /* RW force port resume */ -#define EHCI_PS_OCC 0x00000020 /* RWC over current change */ -#define EHCI_PS_OCA 0x00000010 /* RO over current active */ -#define EHCI_PS_PEC 0x00000008 /* RWC port enable change */ -#define EHCI_PS_PE 0x00000004 /* RW port enable */ -#define EHCI_PS_CSC 0x00000002 /* RWC connect status change */ -#define EHCI_PS_CS 0x00000001 /* RO connect status */ +#define EHCI_PS_WKOC_E (1 << 22) /* RW wake on over current */ +#define EHCI_PS_WKDSCNNT_E (1 << 21) /* RW wake on disconnect */ +#define EHCI_PS_WKCNNT_E (1 << 20) /* RW wake on connect */ +#define EHCI_PS_PO (1 << 13) /* RW port owner */ +#define EHCI_PS_PP (1 << 12) /* RW,RO port power */ +#define EHCI_PS_LS (3 << 10) /* RO line status */ +#define EHCI_PS_PR (1 << 8) /* RW port reset */ +#define EHCI_PS_SUSP (1 << 7) /* RW suspend */ +#define EHCI_PS_FPR (1 << 6) /* RW force port resume */ +#define EHCI_PS_OCC (1 << 5) /* RWC over current change */ +#define EHCI_PS_OCA (1 << 4) /* RO over current active */ +#define EHCI_PS_PEC (1 << 3) /* RWC port enable change */ +#define EHCI_PS_PE (1 << 2) /* RW port enable */ +#define EHCI_PS_CSC (1 << 1) /* RWC connect status change */ +#define EHCI_PS_CS (1 << 0) /* RO connect status */ #define EHCI_PS_CLEAR (EHCI_PS_OCC | EHCI_PS_PEC | EHCI_PS_CSC) -#define EHCI_PS_IS_LOWSPEED(x) (((x) & EHCI_PS_LS) == 0x00000400) +#define EHCI_PS_IS_LOWSPEED(x) (((x) & EHCI_PS_LS) == (1 << 10)) /* * Schedule Interface Space. @@ -177,8 +182,8 @@ struct QH { struct qTD qh_overlay; }; -/* Low level intit functions */ - +/* Low level init functions */ int ehci_hcd_init(void); int ehci_hcd_stop(void); + #endif /* USB_EHCI_H */ diff --git a/drivers/usb/usb_ehci_core.c b/drivers/usb/usb_ehci_core.c index ec50874..07e9a6b 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 <trimarchimichael@yahoo.it> + * * 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 <common.h> #include <asm/byteorder.h> #include <usb.h> @@ -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; |