X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=drivers%2Fusb%2Fhost%2Fehci-hcd.c;h=6323c508375af8ce3b98009a85bfbc2f5e8a5564;hb=e89d623f099c44b0b166ccf46bce2e6a0b99c984;hp=c8168782069a3ded3c053c16b04ec0c0c7c9a6ba;hpb=3c47f2f4871c345c20b9d986b11fec550ef6cc9f;p=u-boot diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index c816878206..6323c50837 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -36,16 +36,13 @@ #define CONFIG_USB_MAX_CONTROLLER_COUNT 1 #endif -static struct ehci_ctrl { - struct ehci_hccr *hccr; /* R/O registers, not need for volatile */ - struct ehci_hcor *hcor; - int rootdev; - uint16_t portreset; - struct QH qh_list __aligned(USB_DMA_MINALIGN); - struct QH periodic_queue __aligned(USB_DMA_MINALIGN); - uint32_t *periodic_list; - int ntds; -} ehcic[CONFIG_USB_MAX_CONTROLLER_COUNT]; +/* + * EHCI spec page 20 says that the HC may take up to 16 uFrames (= 4ms) to halt. + * Let's time out after 8 to have a little safety margin on top of that. + */ +#define HCHALT_TIMEOUT (8 * 1000) + +static struct ehci_ctrl ehcic[CONFIG_USB_MAX_CONTROLLER_COUNT]; #define ALIGN_END_ADDR(type, ptr, size) \ ((uint32_t)(ptr) + roundup((size) * sizeof(type), USB_DMA_MINALIGN)) @@ -122,6 +119,31 @@ static struct descriptor { #define ehci_is_TDI() (0) #endif +int __ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg) +{ + return PORTSC_PSPD(reg); +} + +int ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg) + __attribute__((weak, alias("__ehci_get_port_speed"))); + +void __ehci_set_usbmode(int index) +{ + uint32_t tmp; + uint32_t *reg_ptr; + + reg_ptr = (uint32_t *)((u8 *)&ehcic[index].hcor->or_usbcmd + 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); +} + +void ehci_set_usbmode(int index) + __attribute__((weak, alias("__ehci_set_usbmode"))); + void __ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg) { mdelay(50); @@ -149,8 +171,6 @@ static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec) static int ehci_reset(int index) { uint32_t cmd; - uint32_t tmp; - uint32_t *reg_ptr; int ret = 0; cmd = ehci_readl(&ehcic[index].hcor->or_usbcmd); @@ -163,15 +183,8 @@ static int ehci_reset(int index) goto out; } - if (ehci_is_TDI()) { - reg_ptr = (uint32_t *)((u8 *)ehcic[index].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); - } + if (ehci_is_TDI()) + ehci_set_usbmode(index); #ifdef CONFIG_USB_EHCI_TXFIFO_THRESH cmd = ehci_readl(&ehcic[index].hcor->or_txfilltuning); @@ -183,6 +196,39 @@ out: return ret; } +static int ehci_shutdown(struct ehci_ctrl *ctrl) +{ + int i, ret = 0; + uint32_t cmd, reg; + + if (!ctrl || !ctrl->hcor) + return -EINVAL; + + cmd = ehci_readl(&ctrl->hcor->or_usbcmd); + cmd &= ~(CMD_PSE | CMD_ASE); + ehci_writel(&ctrl->hcor->or_usbcmd, cmd); + ret = handshake(&ctrl->hcor->or_usbsts, STS_ASS | STS_PSS, 0, + 100 * 1000); + + if (!ret) { + for (i = 0; i < CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS; i++) { + reg = ehci_readl(&ctrl->hcor->or_portsc[i]); + reg |= EHCI_PS_SUSP; + ehci_writel(&ctrl->hcor->or_portsc[i], reg); + } + + cmd &= ~CMD_RUN; + ehci_writel(&ctrl->hcor->or_usbcmd, cmd); + ret = handshake(&ctrl->hcor->or_usbsts, STS_HALT, STS_HALT, + HCHALT_TIMEOUT); + } + + if (ret) + puts("EHCI failed to shut down host controller.\n"); + + return ret; +} + static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz) { uint32_t delta, next; @@ -227,6 +273,29 @@ static inline u8 ehci_encode_speed(enum usb_device_speed speed) return QH_FULL_SPEED; } +static void ehci_update_endpt2_dev_n_port(struct usb_device *dev, + struct QH *qh) +{ + struct usb_device *ttdev; + + if (dev->speed != USB_SPEED_LOW && dev->speed != USB_SPEED_FULL) + return; + + /* + * For full / low speed devices we need to get the devnum and portnr of + * the tt, so of the first upstream usb-2 hub, there may be usb-1 hubs + * in the tree before that one! + */ + ttdev = dev; + while (ttdev->parent && ttdev->parent->speed != USB_SPEED_HIGH) + ttdev = ttdev->parent; + if (!ttdev->parent) + return; + + qh->qh_endpt2 |= cpu_to_hc32(QH_ENDPT2_PORTNUM(ttdev->portnr) | + QH_ENDPT2_HUBADDR(ttdev->parent->devnum)); +} + static int ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, int length, struct devrequest *req) @@ -344,11 +413,11 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, QH_ENDPT1_ENDPT(usb_pipeendpoint(pipe)) | QH_ENDPT1_I(0) | QH_ENDPT1_DEVADDR(usb_pipedevice(pipe)); qh->qh_endpt1 = cpu_to_hc32(endpt); - endpt = QH_ENDPT2_MULT(1) | QH_ENDPT2_PORTNUM(dev->portnr) | - QH_ENDPT2_HUBADDR(dev->parent->devnum) | - QH_ENDPT2_UFCMASK(0) | QH_ENDPT2_UFSMASK(0); + endpt = QH_ENDPT2_MULT(1) | QH_ENDPT2_UFCMASK(0) | QH_ENDPT2_UFSMASK(0); qh->qh_endpt2 = cpu_to_hc32(endpt); + ehci_update_endpt2_dev_n_port(dev, qh); qh->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + qh->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); tdp = &qh->qh_overlay.qt_next; @@ -573,10 +642,12 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, dev->act_len = length - QT_TOKEN_GET_TOTALBYTES(token); } else { dev->act_len = 0; +#ifndef CONFIG_USB_EHCI_FARADAY debug("dev=%u, usbsts=%#x, p[1]=%#x, p[2]=%#x\n", dev->devnum, ehci_readl(&ctrl->hcor->or_usbsts), ehci_readl(&ctrl->hcor->or_portsc[0]), ehci_readl(&ctrl->hcor->or_portsc[1])); +#endif } free(qtd); @@ -587,14 +658,15 @@ fail: return -1; } -static inline int min3(int a, int b, int c) +__weak uint32_t *ehci_get_portsc_register(struct ehci_hcor *hcor, int port) { + if (port < 0 || port >= CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) { + /* Printing the message would cause a scan failure! */ + debug("The request port(%u) is not configured\n", port); + return NULL; + } - if (b < a) - a = b; - if (c < a) - a = c; - return a; + return (uint32_t *)&hcor->or_portsc[port]; } int @@ -607,15 +679,9 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, int len, srclen; uint32_t reg; uint32_t *status_reg; + int port = le16_to_cpu(req->index) & 0xff; struct ehci_ctrl *ctrl = dev->controller; - 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 *)&ctrl->hcor->or_portsc[ - le16_to_cpu(req->index) - 1]; srclen = 0; debug("req=%u (%#x), type=%u (%#x), value=%u, index=%u\n", @@ -625,6 +691,19 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, typeReq = req->request | req->requesttype << 8; + switch (typeReq) { + case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8): + case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): + case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): + status_reg = ehci_get_portsc_register(ctrl->hcor, port - 1); + if (!status_reg) + return -1; + break; + default: + status_reg = NULL; + break; + } + switch (typeReq) { case DeviceRequest | USB_REQ_GET_DESCRIPTOR: switch (le16_to_cpu(req->value) >> 8) { @@ -711,7 +790,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, tmpbuf[1] |= USB_PORT_STAT_POWER >> 8; if (ehci_is_TDI()) { - switch (PORTSC_PSPD(reg)) { + switch (ehci_get_port_speed(ctrl->hcor, reg)) { case PORTSC_PSPD_FS: break; case PORTSC_PSPD_LS: @@ -732,7 +811,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, tmpbuf[2] |= USB_PORT_STAT_C_ENABLE; if (reg & EHCI_PS_OCC) tmpbuf[2] |= USB_PORT_STAT_C_OVERCURRENT; - if (ctrl->portreset & (1 << le16_to_cpu(req->index))) + if (ctrl->portreset & (1 << port)) tmpbuf[2] |= USB_PORT_STAT_C_RESET; srcptr = tmpbuf; @@ -758,7 +837,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, EHCI_PS_IS_LOWSPEED(reg)) { /* Low speed device, give up ownership. */ debug("port %d low speed --> companion\n", - req->index - 1); + port - 1); reg |= EHCI_PS_PO; ehci_writel(status_reg, reg); break; @@ -784,13 +863,18 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, ret = handshake(status_reg, EHCI_PS_PR, 0, 2 * 1000); if (!ret) - ctrl->portreset |= - 1 << le16_to_cpu(req->index); + ctrl->portreset |= 1 << port; else printf("port(%d) reset error\n", - le16_to_cpu(req->index) - 1); + port - 1); } break; + case USB_PORT_FEAT_TEST: + ehci_shutdown(ctrl); + reg &= ~(0xf << 16); + reg |= ((le16_to_cpu(req->index) >> 8) & 0xf) << 16; + ehci_writel(status_reg, reg); + break; default: debug("unknown feature %x\n", le16_to_cpu(req->value)); goto unknown; @@ -800,24 +884,26 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, break; case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): reg = ehci_readl(status_reg); + reg &= ~EHCI_PS_CLEAR; 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; + reg |= EHCI_PS_PE; break; case USB_PORT_FEAT_POWER: if (HCS_PPC(ehci_readl(&ctrl->hccr->cr_hcsparams))) - reg = reg & ~(EHCI_PS_CLEAR | EHCI_PS_PP); + reg &= ~EHCI_PS_PP; + break; case USB_PORT_FEAT_C_CONNECTION: - reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_CSC; + reg |= EHCI_PS_CSC; break; case USB_PORT_FEAT_OVER_CURRENT: - reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_OCC; + reg |= EHCI_PS_OCC; break; case USB_PORT_FEAT_C_RESET: - ctrl->portreset &= ~(1 << le16_to_cpu(req->index)); + ctrl->portreset &= ~(1 << port); break; default: debug("unknown feature %x\n", le16_to_cpu(req->value)); @@ -855,31 +941,37 @@ unknown: int usb_lowlevel_stop(int index) { + ehci_shutdown(&ehcic[index]); return ehci_hcd_stop(index); } -int usb_lowlevel_init(int index, void **controller) +int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) { uint32_t reg; uint32_t cmd; struct QH *qh_list; struct QH *periodic; int i; + int rc; - if (ehci_hcd_init(index, &ehcic[index].hccr, &ehcic[index].hcor)) - return -1; + rc = ehci_hcd_init(index, init, &ehcic[index].hccr, &ehcic[index].hcor); + if (rc) + return rc; + if (init == USB_INIT_DEVICE) + goto done; /* EHCI spec section 4.1 */ if (ehci_reset(index)) return -1; #if defined(CONFIG_EHCI_HCD_INIT_AFTER_RESET) - if (ehci_hcd_init(index, &ehcic[index].hccr, &ehcic[index].hcor)) - return -1; + rc = ehci_hcd_init(index, init, &ehcic[index].hccr, &ehcic[index].hcor); + if (rc) + return rc; #endif /* Set the high address word (aka segment) for 64-bit controller */ if (ehci_readl(&ehcic[index].hccr->cr_hccparams) & 1) - ehci_writel(ehcic[index].hcor->or_ctrldssegment, 0); + ehci_writel(&ehcic[index].hcor->or_ctrldssegment, 0); qh_list = &ehcic[index].qh_list; @@ -894,6 +986,9 @@ int usb_lowlevel_init(int index, void **controller) qh_list->qh_overlay.qt_token = cpu_to_hc32(QT_TOKEN_STATUS(QT_TOKEN_STATUS_HALTED)); + flush_dcache_range((uint32_t)qh_list, + ALIGN_END_ADDR(struct QH, qh_list, 1)); + /* Set async. queue head pointer. */ ehci_writel(&ehcic[index].hcor->or_asynclistaddr, (uint32_t)qh_list); @@ -901,12 +996,16 @@ int usb_lowlevel_init(int index, void **controller) * Set up periodic list * Step 1: Parent QH for all periodic transfers. */ + ehcic[index].periodic_schedules = 0; periodic = &ehcic[index].periodic_queue; memset(periodic, 0, sizeof(*periodic)); periodic->qh_link = cpu_to_hc32(QH_LINK_TERMINATE); periodic->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); periodic->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + flush_dcache_range((uint32_t)periodic, + ALIGN_END_ADDR(struct QH, periodic, 1)); + /* * Step 2: Setup frame-list: Every microframe, USB tries the same list. * In particular, device specifications on polling frequency @@ -916,14 +1015,20 @@ int usb_lowlevel_init(int index, void **controller) * Split Transactions will be spread across microframes using * S-mask and C-mask. */ - ehcic[index].periodic_list = memalign(4096, 1024*4); + if (ehcic[index].periodic_list == NULL) + ehcic[index].periodic_list = memalign(4096, 1024 * 4); + if (!ehcic[index].periodic_list) return -ENOMEM; for (i = 0; i < 1024; i++) { - ehcic[index].periodic_list[i] = (uint32_t)periodic - | QH_LINK_TYPE_QH; + ehcic[index].periodic_list[i] = cpu_to_hc32((uint32_t)periodic + | QH_LINK_TYPE_QH); } + flush_dcache_range((uint32_t)ehcic[index].periodic_list, + ALIGN_END_ADDR(uint32_t, ehcic[index].periodic_list, + 1024)); + /* Set periodic list base address */ ehci_writel(&ehcic[index].hcor->or_periodiclistbase, (uint32_t)ehcic[index].periodic_list); @@ -950,10 +1055,13 @@ int usb_lowlevel_init(int index, void **controller) cmd |= CMD_RUN; ehci_writel(&ehcic[index].hcor->or_usbcmd, cmd); +#ifndef CONFIG_USB_EHCI_FARADAY /* take control over the ports */ cmd = ehci_readl(&ehcic[index].hcor->or_configflag); cmd |= FLAG_CF; ehci_writel(&ehcic[index].hcor->or_configflag, cmd); +#endif + /* unblock posted write */ cmd = ehci_readl(&ehcic[index].hcor->or_usbcmd); mdelay(5); @@ -961,7 +1069,7 @@ int usb_lowlevel_init(int index, void **controller) printf("USB EHCI %x.%02x\n", reg >> 8, reg & 0xff); ehcic[index].rootdev = 0; - +done: *controller = &ehcic[index]; return 0; } @@ -1004,7 +1112,7 @@ struct int_queue { struct qTD *tds; }; -#define NEXT_QH(qh) (struct QH *)((qh)->qh_link & ~0x1f) +#define NEXT_QH(qh) (struct QH *)(hc32_to_cpu((qh)->qh_link) & ~0x1f) static int enable_periodic(struct ehci_ctrl *ctrl) @@ -1047,8 +1155,6 @@ disable_periodic(struct ehci_ctrl *ctrl) return 0; } -static int periodic_schedules; - struct int_queue * create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, int elementsize, void *buffer) @@ -1077,14 +1183,16 @@ create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, debug("ehci intr queue: out of memory\n"); goto fail1; } - result->first = memalign(32, sizeof(struct QH) * queuesize); + result->first = memalign(USB_DMA_MINALIGN, + sizeof(struct QH) * queuesize); if (!result->first) { debug("ehci intr queue: out of memory\n"); goto fail2; } result->current = result->first; result->last = result->first + queuesize - 1; - result->tds = memalign(32, sizeof(struct qTD) * queuesize); + result->tds = memalign(USB_DMA_MINALIGN, + sizeof(struct qTD) * queuesize); if (!result->tds) { debug("ehci intr queue: out of memory\n"); goto fail3; @@ -1097,44 +1205,59 @@ create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, struct qTD *td = result->tds + i; void **buf = &qh->buffer; - qh->qh_link = (uint32_t)(qh+1) | QH_LINK_TYPE_QH; + qh->qh_link = cpu_to_hc32((uint32_t)(qh+1) | QH_LINK_TYPE_QH); if (i == queuesize - 1) - qh->qh_link = QH_LINK_TERMINATE; + qh->qh_link = cpu_to_hc32(QH_LINK_TERMINATE); - qh->qh_overlay.qt_next = (uint32_t)td; - qh->qh_endpt1 = (0 << 28) | /* No NAK reload (ehci 4.9) */ + qh->qh_overlay.qt_next = cpu_to_hc32((uint32_t)td); + qh->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + qh->qh_endpt1 = + cpu_to_hc32((0 << 28) | /* No NAK reload (ehci 4.9) */ (usb_maxpacket(dev, pipe) << 16) | /* MPS */ (1 << 14) | QH_ENDPT1_EPS(ehci_encode_speed(dev->speed)) | (usb_pipeendpoint(pipe) << 8) | /* Endpoint Number */ - (usb_pipedevice(pipe) << 0); - qh->qh_endpt2 = (1 << 30) | /* 1 Tx per mframe */ - (1 << 0); /* S-mask: microframe 0 */ + (usb_pipedevice(pipe) << 0)); + qh->qh_endpt2 = cpu_to_hc32((1 << 30) | /* 1 Tx per mframe */ + (1 << 0)); /* S-mask: microframe 0 */ if (dev->speed == USB_SPEED_LOW || dev->speed == USB_SPEED_FULL) { - debug("TT: port: %d, hub address: %d\n", - dev->portnr, dev->parent->devnum); - qh->qh_endpt2 |= (dev->portnr << 23) | - (dev->parent->devnum << 16) | - (0x1c << 8); /* C-mask: microframes 2-4 */ + /* C-mask: microframes 2-4 */ + qh->qh_endpt2 |= cpu_to_hc32((0x1c << 8)); } + ehci_update_endpt2_dev_n_port(dev, qh); - td->qt_next = QT_NEXT_TERMINATE; - td->qt_altnext = QT_NEXT_TERMINATE; + td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); debug("communication direction is '%s'\n", usb_pipein(pipe) ? "in" : "out"); - td->qt_token = (elementsize << 16) | + td->qt_token = cpu_to_hc32((elementsize << 16) | ((usb_pipein(pipe) ? 1 : 0) << 8) | /* IN/OUT token */ - 0x80; /* active */ - td->qt_buffer[0] = (uint32_t)buffer + i * elementsize; - td->qt_buffer[1] = (td->qt_buffer[0] + 0x1000) & ~0xfff; - td->qt_buffer[2] = (td->qt_buffer[0] + 0x2000) & ~0xfff; - td->qt_buffer[3] = (td->qt_buffer[0] + 0x3000) & ~0xfff; - td->qt_buffer[4] = (td->qt_buffer[0] + 0x4000) & ~0xfff; + 0x80); /* active */ + td->qt_buffer[0] = + cpu_to_hc32((uint32_t)buffer + i * elementsize); + td->qt_buffer[1] = + cpu_to_hc32((td->qt_buffer[0] + 0x1000) & ~0xfff); + td->qt_buffer[2] = + cpu_to_hc32((td->qt_buffer[0] + 0x2000) & ~0xfff); + td->qt_buffer[3] = + cpu_to_hc32((td->qt_buffer[0] + 0x3000) & ~0xfff); + td->qt_buffer[4] = + cpu_to_hc32((td->qt_buffer[0] + 0x4000) & ~0xfff); *buf = buffer + i * elementsize; } + flush_dcache_range((uint32_t)buffer, + ALIGN_END_ADDR(char, buffer, + queuesize * elementsize)); + flush_dcache_range((uint32_t)result->first, + ALIGN_END_ADDR(struct QH, result->first, + queuesize)); + flush_dcache_range((uint32_t)result->tds, + ALIGN_END_ADDR(struct qTD, result->tds, + queuesize)); + if (disable_periodic(ctrl) < 0) { debug("FATAL: periodic should never fail, but did"); goto fail3; @@ -1143,13 +1266,18 @@ create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, /* hook up to periodic list */ struct QH *list = &ctrl->periodic_queue; result->last->qh_link = list->qh_link; - list->qh_link = (uint32_t)result->first | QH_LINK_TYPE_QH; + list->qh_link = cpu_to_hc32((uint32_t)result->first | QH_LINK_TYPE_QH); + + flush_dcache_range((uint32_t)result->last, + ALIGN_END_ADDR(struct QH, result->last, 1)); + flush_dcache_range((uint32_t)list, + ALIGN_END_ADDR(struct QH, list, 1)); if (enable_periodic(ctrl) < 0) { debug("FATAL: periodic should never fail, but did"); goto fail3; } - periodic_schedules++; + ctrl->periodic_schedules++; debug("Exit create_int_queue\n"); return result; @@ -1168,6 +1296,7 @@ fail1: void *poll_int_queue(struct usb_device *dev, struct int_queue *queue) { struct QH *cur = queue->current; + struct qTD *cur_td; /* depleted queue */ if (cur == NULL) { @@ -1175,18 +1304,21 @@ void *poll_int_queue(struct usb_device *dev, struct int_queue *queue) return NULL; } /* still active */ - if (cur->qh_overlay.qt_token & 0x80) { - debug("Exit poll_int_queue with no completed intr transfer. " - "token is %x\n", cur->qh_overlay.qt_token); + cur_td = &queue->tds[queue->current - queue->first]; + invalidate_dcache_range((uint32_t)cur_td, + ALIGN_END_ADDR(struct qTD, cur_td, 1)); + if (QT_TOKEN_GET_STATUS(hc32_to_cpu(cur_td->qt_token)) & + QT_TOKEN_STATUS_ACTIVE) { + debug("Exit poll_int_queue with no completed intr transfer. token is %x\n", + hc32_to_cpu(cur_td->qt_token)); return NULL; } if (!(cur->qh_link & QH_LINK_TERMINATE)) queue->current++; else queue->current = NULL; - debug("Exit poll_int_queue with completed intr transfer. " - "token is %x at %p (first at %p)\n", cur->qh_overlay.qt_token, - &cur->qh_overlay.qt_token, queue->first); + debug("Exit poll_int_queue with completed intr transfer. token is %x at %p (first at %p)\n", + hc32_to_cpu(cur_td->qt_token), cur, queue->first); return cur->buffer; } @@ -1202,15 +1334,17 @@ destroy_int_queue(struct usb_device *dev, struct int_queue *queue) debug("FATAL: periodic should never fail, but did"); goto out; } - periodic_schedules--; + ctrl->periodic_schedules--; struct QH *cur = &ctrl->periodic_queue; timeout = get_timer(0) + 500; /* abort after 500ms */ - while (!(cur->qh_link & QH_LINK_TERMINATE)) { + while (!(cur->qh_link & cpu_to_hc32(QH_LINK_TERMINATE))) { debug("considering %p, with qh_link %x\n", cur, cur->qh_link); if (NEXT_QH(cur) == queue->first) { debug("found candidate. removing from chain\n"); cur->qh_link = queue->last->qh_link; + flush_dcache_range((uint32_t)cur, + ALIGN_END_ADDR(struct QH, cur, 1)); result = 0; break; } @@ -1222,7 +1356,7 @@ destroy_int_queue(struct usb_device *dev, struct int_queue *queue) } } - if (periodic_schedules > 0) { + if (ctrl->periodic_schedules > 0) { result = enable_periodic(ctrl); if (result < 0) debug("FATAL: periodic should never fail, but did"); @@ -1281,6 +1415,9 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, return -EINVAL; } + invalidate_dcache_range((uint32_t)buffer, + ALIGN_END_ADDR(char, buffer, length)); + ret = destroy_int_queue(dev, queue); if (ret < 0) return ret;