diff options
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/usb_ehci.h | 27 | ||||
-rw-r--r-- | drivers/usb/usb_ehci_core.c | 147 | ||||
-rw-r--r-- | drivers/usb/usb_ehci_fsl.c | 3 |
3 files changed, 133 insertions, 44 deletions
diff --git a/drivers/usb/usb_ehci.h b/drivers/usb/usb_ehci.h index 3e7a2ab..90b137a 100644 --- a/drivers/usb/usb_ehci.h +++ b/drivers/usb/usb_ehci.h @@ -45,15 +45,24 @@ struct ehci_hccr { #define HC_LENGTH(p) (((p) >> 0) & 0x00ff) #define HC_VERSION(p) (((p) >> 16) & 0xffff) uint32_t cr_hcsparams; +#define HCS_N_PORTS(p) (((p) >> 0) & 0xf) uint32_t cr_hccparams; uint8_t cr_hcsp_portrt[8]; }; struct ehci_hcor { uint32_t or_usbcmd; -#define CMD_ASE (1 << 5) +#define CMD_PARK (1 << 11) /* enable "park" */ +#define CMD_PARK_CNT(c) (((c) >> 8) & 3) /* how many transfers to park */ +#define CMD_ASE (1 << 5) /* async schedule enable */ +#define CMD_LRESET (1 << 7) /* partial reset */ +#define CMD_IAAD (1 << 5) /* "doorbell" interrupt */ +#define CMD_PSE (1 << 4) /* periodic schedule enable */ +#define CMD_RESET (1 << 1) /* reset HC not bus */ +#define CMD_RUN (1 << 0) /* start/stop HC */ uint32_t or_usbsts; #define STD_ASS (1 << 15) +#define STS_HALT (1 << 12) uint32_t or_usbintr; uint32_t or_frindex; uint32_t or_ctrldssegment; @@ -61,10 +70,17 @@ struct ehci_hcor { uint32_t or_asynclistaddr; 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_systune; }; +#define USBMODE 0x68 /* USB Device mode */ +#define USBMODE_SDIS (1 << 3) /* Stream disable */ +#define USBMODE_BE (1 << 2) /* BE/LE endiannes select */ +#define USBMODE_CM_HC (3 << 0) /* host controller mode */ +#define USBMODE_CM_IDLE (0 << 0) /* idle state */ + /* Interface descriptor */ struct usb_linux_interface_descriptor { unsigned char bLength; @@ -91,11 +107,12 @@ struct usb_linux_config_descriptor { } __attribute__ ((packed)); #if defined CONFIG_EHCI_DESC_BIG_ENDIAN -#define ehci_readl(x) (x) -#define ehci_writel(a, b) (a) = (b) +#define ehci_readl(x) (*((volatile u32 *)(x))) +#define ehci_writel(a, b) (*((volatile u32 *)(a)) = ((volatile u32)b)) #else -#define ehci_readl(x) cpu_to_le32((x)) -#define ehci_writel(a, b) (a) = cpu_to_le32((b)) +#define ehci_readl(x) cpu_to_le32((*((volatile u32 *)(x)))) +#define ehci_writel(a, b) (*((volatile u32 *)(a)) = \ + cpu_to_le32(((volatile u32)b))) #endif #if defined CONFIG_EHCI_MMIO_BIG_ENDIAN diff --git a/drivers/usb/usb_ehci_core.c b/drivers/usb/usb_ehci_core.c index 869bea3..ec50874 100644 --- a/drivers/usb/usb_ehci_core.c +++ b/drivers/usb/usb_ehci_core.c @@ -99,8 +99,55 @@ static struct descriptor { }, }; -static void ehci_free (void *p, size_t sz) +static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int msec) { + uint32_t result; + do { + result = ehci_readl(ptr); + debug("handshake read reg(%x)=%x\n", (uint32_t)ptr, result); + if (result == ~(uint32_t)0) + return -1; + result &= mask; + if (result == done) + return 0; + wait_ms(1); + msec--; + } while (msec > 0); + return -1; +} + +static void ehci_free(void *p, size_t sz) +{ + +} + +static int ehci_reset(void) +{ + uint32_t cmd; + uint32_t tmp; + uint32_t *reg_ptr; + int ret = 0; + + cmd = ehci_readl(&hcor->or_usbcmd); + cmd |= CMD_RESET; + ehci_writel(&hcor->or_usbcmd, cmd); + ret = handshake(&hcor->or_usbcmd, CMD_RESET, 0, 250); + if (ret < 0) { + printf("EHCI fail to reset\n"); + goto out; + } + +#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 + tmp |= USBMODE_BE; +#endif + ehci_writel(reg_ptr, tmp); +#endif +out: + return ret; } static void *ehci_alloc(size_t sz, size_t align) @@ -170,6 +217,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, uint32_t endpt, token, usbsts; uint32_t c, toggle; uint32_t cmd; + uint32_t sts; debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p\n", dev, pipe, buffer, length, req); @@ -277,16 +325,19 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, qh_list.qh_link = cpu_to_hc32((uint32_t) qh | QH_LINK_TYPE_QH); - usbsts = ehci_readl(hcor->or_usbsts); - ehci_writel(hcor->or_usbsts, (usbsts & 0x3f)); + usbsts = ehci_readl(&hcor->or_usbsts); + ehci_writel(&hcor->or_usbsts, (usbsts & 0x3f)); /* Enable async. schedule. */ - cmd = ehci_readl(hcor->or_usbcmd); - hcor->or_usbcmd |= CMD_ASE; - ehci_writel(hcor->or_usbcmd, cmd); - - while ((ehci_readl(hcor->or_usbsts) & STD_ASS) == 0) - udelay(1); + cmd = ehci_readl(&hcor->or_usbcmd); + cmd |= CMD_ASE; + ehci_writel(&hcor->or_usbcmd, cmd); + + sts = ehci_readl(&hcor->or_usbsts); + while ((sts & STD_ASS) == 0) { + sts = ehci_readl(&hcor->or_usbsts); + udelay(10); + } /* Wait for TDs to be processed. */ ts = get_timer(0); @@ -298,11 +349,15 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, } while (get_timer(ts) < CONFIG_SYS_HZ); /* Disable async schedule. */ - cmd = ehci_readl(hcor->or_usbcmd); + cmd = ehci_readl(&hcor->or_usbcmd); cmd &= ~CMD_ASE; - ehci_writel(hcor->or_usbcmd, cmd); - while ((ehci_readl(hcor->or_usbsts) & STD_ASS) != 0) - udelay(1); + ehci_writel(&hcor->or_usbcmd, cmd); + + sts = ehci_readl(&hcor->or_usbsts); + while ((sts & STD_ASS) != 0) { + sts = ehci_readl(&hcor->or_usbsts); + udelay(10); + } qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); @@ -335,9 +390,9 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, } else { dev->act_len = 0; debug("dev=%u, usbsts=%#x, p[1]=%#x, p[2]=%#x\n", - dev->devnum, ehci_readl(hcor->or_usbsts), - ehci_readl(hcor->or_portsc[0]), - ehci_readl(hcor->or_portsc[1])); + dev->devnum, ehci_readl(&hcor->or_usbsts), + ehci_readl(&hcor->or_portsc[0]), + ehci_readl(&hcor->or_portsc[1])); } return (dev->status != USB_ST_NOT_PROC) ? 0 : -1; @@ -451,7 +506,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) + reg = ehci_readl(&hcor->or_portsc[le16_to_cpu(req->index) - 1]); if (reg & EHCI_PS_CS) tmpbuf[0] |= USB_PORT_STAT_CONNECTION; @@ -479,9 +534,12 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, 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(&hcor->or_portsc[le16_to_cpu(req->index) - 1]); reg &= ~EHCI_PS_CLEAR; switch (le16_to_cpu(req->value)) { + case USB_PORT_FEAT_ENABLE: + reg |= EHCI_PS_PE; + break; case USB_PORT_FEAT_POWER: reg |= EHCI_PS_PP; break; @@ -495,22 +553,22 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, /* Start reset sequence. */ reg &= ~EHCI_PS_PE; reg |= EHCI_PS_PR; - ehci_writel(hcor->or_portsc[ + ehci_writel(&hcor->or_portsc[ le16_to_cpu(req->index) - 1], reg); /* Wait for reset to complete. */ - udelay(500000); + 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[ + ehci_writel(&hcor->or_portsc[ le16_to_cpu(req->index) - 1], reg); /* Wait for HC to complete reset. */ - udelay(2000); + wait_ms(10); reg = - ehci_readl(hcor->or_portsc[le16_to_cpu(req->index) + ehci_readl(&hcor->or_portsc[le16_to_cpu(req->index) - 1]); reg &= ~EHCI_PS_CLEAR; if ((reg & EHCI_PS_PE) == 0) { @@ -525,10 +583,10 @@ 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(&hcor->or_portsc[le16_to_cpu(req->index) - 1], reg); 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_readl(&hcor->or_portsc[le16_to_cpu(req->index) - 1]); reg &= ~EHCI_PS_CLEAR; switch (le16_to_cpu(req->value)) { case USB_PORT_FEAT_ENABLE: @@ -537,6 +595,9 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, case USB_PORT_FEAT_C_CONNECTION: reg |= EHCI_PS_CSC; break; + case USB_PORT_FEAT_OVER_CURRENT: + reg |= EHCI_PS_OCC; + break; case USB_PORT_FEAT_C_RESET: portreset &= ~(1 << le16_to_cpu(req->index)); break; @@ -544,7 +605,7 @@ 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(&hcor->or_portsc[le16_to_cpu(req->index) - 1], reg); break; default: debug("Unknown request\n"); @@ -585,6 +646,10 @@ int usb_lowlevel_init(void) if (ehci_hcd_init() != 0) return -1; + /* EHCI spec section 4.1 */ + if (ehci_reset() != 0) + return -1; + /* Set head of reclaim list */ memset(&qh_list, 0, sizeof(qh_list)); qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); @@ -595,25 +660,31 @@ int usb_lowlevel_init(void) qh_list.qh_overlay.qt_token = cpu_to_hc32(0x40); /* Set async. queue head pointer. */ - ehci_writel(hcor->or_asynclistaddr, (uint32_t)&qh_list); + ehci_writel(&hcor->or_asynclistaddr, (uint32_t)&qh_list); - reg = ehci_readl(hccr->cr_hcsparams); - descriptor.hub.bNbrPorts = reg & 0xf; - printf("NbrPorts %x\n", descriptor.hub.bNbrPorts); + 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 */ descriptor.hub.wHubCharacteristics |= 0x80; if (reg & 0x10) /* Port Power Control */ descriptor.hub.wHubCharacteristics |= 0x01; - /* take control over the ports */ - cmd = ehci_readl(hcor->or_configflag); - cmd |= 1; - ehci_writel(hcor->or_configflag, cmd); - /* Start the host controller. */ - cmd = ehci_readl(hcor->or_configflag); - cmd |= 1; - ehci_writel(hcor->or_usbcmd, cmd); + cmd = ehci_readl(&hcor->or_usbcmd); + /* Philips, Intel, and maybe others need CMD_RUN before the + * root hub will detect new devices (why?); NEC doesn't */ + cmd &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); + cmd |= CMD_RUN; + ehci_writel(&hcor->or_usbcmd, cmd); + + /* take control over the ports */ + cmd = ehci_readl(&hcor->or_configflag); + cmd |= FLAG_CF; + ehci_writel(&hcor->or_configflag, cmd); + /* unblock posted writes */ + cmd = ehci_readl(&hcor->or_usbcmd); + wait_ms(5); rootdev = 0; diff --git a/drivers/usb/usb_ehci_fsl.c b/drivers/usb/usb_ehci_fsl.c index 9ca6c56..81d5d21 100644 --- a/drivers/usb/usb_ehci_fsl.c +++ b/drivers/usb/usb_ehci_fsl.c @@ -43,7 +43,8 @@ int ehci_hcd_init(void) addr = (uint32_t)&(im->usb[0]); hccr = (struct ehci_hccr *)(addr + FSL_SKIP_PCI); - hcor = (struct ehci_hcor *)((uint32_t) hccr + hccr->cr_caplength); + hcor = (struct ehci_hcor *)((uint32_t) hccr + + HC_LENGTH(ehci_readl(&hccr->cr_capbase))); /* Configure clock */ clrsetbits_be32(&(im->clk.sccr), MPC83XX_SCCR_USB_MASK, |