/* IN packets can be used with any type of endpoint. here we just * start the transfer, data from the peripheral may arrive later. * urb->iso_frame_desc is currently ignored here... */ static void in_packet( struct sl811 *sl811, struct sl811h_ep *ep, struct urb *urb, u8 bank, u8 control ) { u8 addr; u8 len; void __iomem *data_reg; /* avoid losing data on overflow */ len = ep->maxpacket; addr = SL811HS_PACKET_BUF(bank == 0); if (!(control & SL11H_HCTLMASK_ISOCH) && usb_gettoggle(urb->dev, ep->epnum, 0)) control |= SL11H_HCTLMASK_TOGGLE; data_reg = sl811->data_reg; /* autoincrementing */ sl811_write(sl811, bank + SL11H_BUFADDRREG, addr); writeb(len, data_reg); writeb(SL_IN | ep->epnum, data_reg); writeb(usb_pipedevice(urb->pipe), data_reg); sl811_write(sl811, bank + SL11H_HOSTCTLREG, control); ep->length = min((int)len, urb->transfer_buffer_length - urb->actual_length); PACKET("IN%s/%d qh%p len%d\n", ep->nak_count ? "/retry" : "", !!usb_gettoggle(urb->dev, ep->epnum, 0), ep, len); }
/* * This function writes the data toggle value. */ static void write_toggle(struct usb_device *dev, u8 ep, u8 dir_out) { u16 toggle = usb_gettoggle(dev, ep, dir_out); u16 csr; if (dir_out) { if (!toggle) writew(MUSB_TXCSR_CLRDATATOG, &musbr->txcsr); else { csr = readw(&musbr->txcsr); csr |= MUSB_TXCSR_H_WR_DATATOGGLE; writew(csr, &musbr->txcsr); csr |= (toggle << MUSB_TXCSR_H_DATATOGGLE_SHIFT); writew(csr, &musbr->txcsr); } } else { if (!toggle) writew(MUSB_RXCSR_CLRDATATOG, &musbr->rxcsr); else { csr = readw(&musbr->rxcsr); csr |= MUSB_RXCSR_H_WR_DATATOGGLE; writew(csr, &musbr->rxcsr); csr |= (toggle << MUSB_S_RXCSR_H_DATATOGGLE); writew(csr, &musbr->rxcsr); } } }
/* OUT packets can be used with any type of endpoint. * urb->iso_frame_desc is currently ignored here... */ static void out_packet(struct ohs900 *ohs900, struct ohs900h_ep *ep, struct urb *urb, u8 control) { void *buf; u8 len; buf = urb->transfer_buffer + urb->actual_length; prefetch(buf); len = min_t(int, ep->maxpacket, urb->transfer_buffer_length - urb->actual_length); ohs900_write(ohs900, OHS900_TXFIFOCONTROLREG, OHS900_FIFO_FORCE_EMPTY); if (!(control & OHS900_HCTLMASK_ISO_EN) && usb_gettoggle(urb->dev, ep->epnum, 1)) ohs900_write(ohs900, OHS900_TXTRANSTYPEREG, OHS900_OUT_DATA1); else ohs900_write(ohs900, OHS900_TXTRANSTYPEREG, OHS900_OUT_DATA0); ohs900_write_buf(ohs900, OHS900_HOST_TXFIFO_DATA, buf, len); ohs900_write(ohs900, OHS900_TXADDRREG, usb_pipedevice(urb->pipe)); ohs900_write(ohs900, OHS900_TXENDPREG, ep->epnum); ohs900_write(ohs900, OHS900_HOST_TX_CTLREG, control); ep->length = len; }
/* OUT packets can be used with any type of endpoint. * urb->iso_frame_desc is currently ignored here... */ static void out_packet( struct sl811 *sl811, struct sl811h_ep *ep, struct urb *urb, u8 bank, u8 control ) { void *buf; u8 addr; u8 len; void __iomem *data_reg; buf = urb->transfer_buffer + urb->actual_length; prefetch(buf); len = min((int)ep->maxpacket, urb->transfer_buffer_length - urb->actual_length); if (!(control & SL11H_HCTLMASK_ISOCH) && usb_gettoggle(urb->dev, ep->epnum, 1)) control |= SL11H_HCTLMASK_TOGGLE; addr = SL811HS_PACKET_BUF(bank == 0); data_reg = sl811->data_reg; sl811_write_buf(sl811, addr, buf, len); /* autoincrementing */ sl811_write(sl811, bank + SL11H_BUFADDRREG, addr); writeb(len, data_reg); writeb(SL_OUT | ep->epnum, data_reg); writeb(usb_pipedevice(urb->pipe), data_reg); sl811_write(sl811, bank + SL11H_HOSTCTLREG, control | SL11H_HCTLMASK_OUT); ep->length = len; PACKET("OUT%s/%d qh%p len%d\n", ep->nak_count ? "/retry" : "", !!usb_gettoggle(urb->dev, ep->epnum, 1), ep, len); }
static int usbhsh_queue_push(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) { struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd); struct usbhsh_ep *uep = usbhsh_ep_to_uep(urb->ep); struct usbhs_pipe *pipe = usbhsh_uep_to_pipe(uep); struct device *dev = usbhsh_hcd_to_dev(hcd); struct usbhsh_request *ureq; void *buf; int len, sequence; if (usb_pipeisoc(urb->pipe)) { dev_err(dev, "pipe iso is not supported now\n"); return -EIO; } /* this ureq will be freed on usbhsh_queue_done() */ ureq = usbhsh_ureq_alloc(hpriv, urb, mem_flags); if (unlikely(!ureq)) { dev_err(dev, "ureq alloc fail\n"); return -ENOMEM; } if (usb_pipein(urb->pipe)) pipe->handler = &usbhs_fifo_dma_pop_handler; else pipe->handler = &usbhs_fifo_dma_push_handler; buf = (void *)(urb->transfer_buffer + urb->actual_length); len = urb->transfer_buffer_length - urb->actual_length; sequence = usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); dev_dbg(dev, "%s\n", __func__); usbhs_pkt_push(pipe, &ureq->pkt, usbhsh_queue_done, buf, len, (urb->transfer_flags & URB_ZERO_PACKET), sequence); usbhs_pkt_start(pipe); return 0; }
/* this function must be called with interrupt disabled */ static void pipe_setting(struct r8a66597 *r8a66597, struct r8a66597_td *td) { struct r8a66597_pipe_info *info; struct urb *urb = td->urb; if (td->pipenum > 0) { info = &td->pipe->info; cfifo_change(r8a66597, 0); pipe_buffer_setting(r8a66597, info); if (!usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) && !usb_pipecontrol(urb->pipe)) { r8a66597_pipe_toggle(r8a66597, td->pipe, 0); pipe_toggle_set(r8a66597, td->pipe, urb, 0); clear_all_buffer(r8a66597, td->pipe); usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 1); } pipe_toggle_restore(r8a66597, td->pipe, urb); } }
static int ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, int length, struct devrequest *req) { ALLOC_ALIGN_BUFFER(struct QH, qh, 1, USB_DMA_MINALIGN); struct qTD *qtd; int qtd_count = 0; int qtd_counter = 0; volatile struct qTD *vtd; unsigned long ts; uint32_t *tdp; uint32_t endpt, maxpacket, token, usbsts; uint32_t c, toggle; uint32_t cmd; int timeout; int ret = 0; struct ehci_ctrl *ctrl = ehci_get_ctrl(dev); debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p\n", dev, pipe, buffer, length, req); if (req != NULL) debug("req=%u (%#x), type=%u (%#x), value=%u (%#x), index=%u\n", req->request, req->request, req->requesttype, req->requesttype, le16_to_cpu(req->value), le16_to_cpu(req->value), le16_to_cpu(req->index)); #define PKT_ALIGN 512 /* * The USB transfer is split into qTD transfers. Eeach qTD transfer is * described by a transfer descriptor (the qTD). The qTDs form a linked * list with a queue head (QH). * * Each qTD transfer starts with a new USB packet, i.e. a packet cannot * have its beginning in a qTD transfer and its end in the following * one, so the qTD transfer lengths have to be chosen accordingly. * * Each qTD transfer uses up to QT_BUFFER_CNT data buffers, mapped to * single pages. The first data buffer can start at any offset within a * page (not considering the cache-line alignment issues), while the * following buffers must be page-aligned. There is no alignment * constraint on the size of a qTD transfer. */ if (req != NULL) /* 1 qTD will be needed for SETUP, and 1 for ACK. */ qtd_count += 1 + 1; if (length > 0 || req == NULL) { /* * Determine the qTD transfer size that will be used for the * data payload (not considering the first qTD transfer, which * may be longer or shorter, and the final one, which may be * shorter). * * In order to keep each packet within a qTD transfer, the qTD * transfer size is aligned to PKT_ALIGN, which is a multiple of * wMaxPacketSize (except in some cases for interrupt transfers, * see comment in submit_int_msg()). * * By default, i.e. if the input buffer is aligned to PKT_ALIGN, * QT_BUFFER_CNT full pages will be used. */ int xfr_sz = QT_BUFFER_CNT; /* * However, if the input buffer is not aligned to PKT_ALIGN, the * qTD transfer size will be one page shorter, and the first qTD * data buffer of each transfer will be page-unaligned. */ if ((unsigned long)buffer & (PKT_ALIGN - 1)) xfr_sz--; /* Convert the qTD transfer size to bytes. */ xfr_sz *= EHCI_PAGE_SIZE; /* * Approximate by excess the number of qTDs that will be * required for the data payload. The exact formula is way more * complicated and saves at most 2 qTDs, i.e. a total of 128 * bytes. */ qtd_count += 2 + length / xfr_sz; } /* * Threshold value based on the worst-case total size of the allocated qTDs for * a mass-storage transfer of 65535 blocks of 512 bytes. */ #if CONFIG_SYS_MALLOC_LEN <= 64 + 128 * 1024 #warning CONFIG_SYS_MALLOC_LEN may be too small for EHCI #endif qtd = memalign(USB_DMA_MINALIGN, qtd_count * sizeof(struct qTD)); if (qtd == NULL) { printf("unable to allocate TDs\n"); return -1; } memset(qh, 0, sizeof(struct QH)); memset(qtd, 0, qtd_count * sizeof(*qtd)); toggle = usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); /* * Setup QH (3.6 in ehci-r10.pdf) * * qh_link ................. 03-00 H * qh_endpt1 ............... 07-04 H * qh_endpt2 ............... 0B-08 H * - qh_curtd * qh_overlay.qt_next ...... 13-10 H * - qh_overlay.qt_altnext */ qh->qh_link = cpu_to_hc32((unsigned long)&ctrl->qh_list | QH_LINK_TYPE_QH); c = (dev->speed != USB_SPEED_HIGH) && !usb_pipeendpoint(pipe); maxpacket = usb_maxpacket(dev, pipe); endpt = QH_ENDPT1_RL(8) | QH_ENDPT1_C(c) | QH_ENDPT1_MAXPKTLEN(maxpacket) | QH_ENDPT1_H(0) | QH_ENDPT1_DTC(QH_ENDPT1_DTC_DT_FROM_QTD) | QH_ENDPT1_EPS(ehci_encode_speed(dev->speed)) | 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_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; if (req != NULL) { /* * Setup request qTD (3.5 in ehci-r10.pdf) * * qt_next ................ 03-00 H * qt_altnext ............. 07-04 H * qt_token ............... 0B-08 H * * [ buffer, buffer_hi ] loaded with "req". */ qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); token = QT_TOKEN_DT(0) | QT_TOKEN_TOTALBYTES(sizeof(*req)) | QT_TOKEN_IOC(0) | QT_TOKEN_CPAGE(0) | QT_TOKEN_CERR(3) | QT_TOKEN_PID(QT_TOKEN_PID_SETUP) | QT_TOKEN_STATUS(QT_TOKEN_STATUS_ACTIVE); qtd[qtd_counter].qt_token = cpu_to_hc32(token); if (ehci_td_buffer(&qtd[qtd_counter], req, sizeof(*req))) { printf("unable to construct SETUP TD\n"); goto fail; } /* Update previous qTD! */ *tdp = cpu_to_hc32((unsigned long)&qtd[qtd_counter]); tdp = &qtd[qtd_counter++].qt_next; toggle = 1; } if (length > 0 || req == NULL) { uint8_t *buf_ptr = buffer; int left_length = length; do { /* * Determine the size of this qTD transfer. By default, * QT_BUFFER_CNT full pages can be used. */ int xfr_bytes = QT_BUFFER_CNT * EHCI_PAGE_SIZE; /* * However, if the input buffer is not page-aligned, the * portion of the first page before the buffer start * offset within that page is unusable. */ xfr_bytes -= (unsigned long)buf_ptr & (EHCI_PAGE_SIZE - 1); /* * In order to keep each packet within a qTD transfer, * align the qTD transfer size to PKT_ALIGN. */ xfr_bytes &= ~(PKT_ALIGN - 1); /* * This transfer may be shorter than the available qTD * transfer size that has just been computed. */ xfr_bytes = min(xfr_bytes, left_length); /* * Setup request qTD (3.5 in ehci-r10.pdf) * * qt_next ................ 03-00 H * qt_altnext ............. 07-04 H * qt_token ............... 0B-08 H * * [ buffer, buffer_hi ] loaded with "buffer". */ qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); token = QT_TOKEN_DT(toggle) | QT_TOKEN_TOTALBYTES(xfr_bytes) | QT_TOKEN_IOC(req == NULL) | QT_TOKEN_CPAGE(0) | QT_TOKEN_CERR(3) | QT_TOKEN_PID(usb_pipein(pipe) ? QT_TOKEN_PID_IN : QT_TOKEN_PID_OUT) | QT_TOKEN_STATUS(QT_TOKEN_STATUS_ACTIVE); qtd[qtd_counter].qt_token = cpu_to_hc32(token); if (ehci_td_buffer(&qtd[qtd_counter], buf_ptr, xfr_bytes)) { printf("unable to construct DATA TD\n"); goto fail; } /* Update previous qTD! */ *tdp = cpu_to_hc32((unsigned long)&qtd[qtd_counter]); tdp = &qtd[qtd_counter++].qt_next; /* * Data toggle has to be adjusted since the qTD transfer * size is not always an even multiple of * wMaxPacketSize. */ if ((xfr_bytes / maxpacket) & 1) toggle ^= 1; buf_ptr += xfr_bytes; left_length -= xfr_bytes; } while (left_length > 0); } if (req != NULL) { /* * Setup request qTD (3.5 in ehci-r10.pdf) * * qt_next ................ 03-00 H * qt_altnext ............. 07-04 H * qt_token ............... 0B-08 H */ qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); token = QT_TOKEN_DT(1) | QT_TOKEN_TOTALBYTES(0) | QT_TOKEN_IOC(1) | QT_TOKEN_CPAGE(0) | QT_TOKEN_CERR(3) | QT_TOKEN_PID(usb_pipein(pipe) ? QT_TOKEN_PID_OUT : QT_TOKEN_PID_IN) | QT_TOKEN_STATUS(QT_TOKEN_STATUS_ACTIVE); qtd[qtd_counter].qt_token = cpu_to_hc32(token); /* Update previous qTD! */ *tdp = cpu_to_hc32((unsigned long)&qtd[qtd_counter]); tdp = &qtd[qtd_counter++].qt_next; } ctrl->qh_list.qh_link = cpu_to_hc32((unsigned long)qh | QH_LINK_TYPE_QH); /* Flush dcache */ flush_dcache_range((unsigned long)&ctrl->qh_list, ALIGN_END_ADDR(struct QH, &ctrl->qh_list, 1)); flush_dcache_range((unsigned long)qh, ALIGN_END_ADDR(struct QH, qh, 1)); flush_dcache_range((unsigned long)qtd, ALIGN_END_ADDR(struct qTD, qtd, qtd_count)); /* Set async. queue head pointer. */ ehci_writel(&ctrl->hcor->or_asynclistaddr, (unsigned long)&ctrl->qh_list); usbsts = ehci_readl(&ctrl->hcor->or_usbsts); ehci_writel(&ctrl->hcor->or_usbsts, (usbsts & 0x3f)); /* Enable async. schedule. */ cmd = ehci_readl(&ctrl->hcor->or_usbcmd); cmd |= CMD_ASE; ehci_writel(&ctrl->hcor->or_usbcmd, cmd); ret = handshake((uint32_t *)&ctrl->hcor->or_usbsts, STS_ASS, STS_ASS, 100 * 1000); if (ret < 0) { printf("EHCI fail timeout STS_ASS set\n"); goto fail; } /* Wait for TDs to be processed. */ ts = get_timer(0); vtd = &qtd[qtd_counter - 1]; timeout = USB_TIMEOUT_MS(pipe); do { /* Invalidate dcache */ invalidate_dcache_range((unsigned long)&ctrl->qh_list, ALIGN_END_ADDR(struct QH, &ctrl->qh_list, 1)); invalidate_dcache_range((unsigned long)qh, ALIGN_END_ADDR(struct QH, qh, 1)); invalidate_dcache_range((unsigned long)qtd, ALIGN_END_ADDR(struct qTD, qtd, qtd_count)); token = hc32_to_cpu(vtd->qt_token); if (!(QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE)) break; WATCHDOG_RESET(); } while (get_timer(ts) < timeout); /* * Invalidate the memory area occupied by buffer * Don't try to fix the buffer alignment, if it isn't properly * aligned it's upper layer's fault so let invalidate_dcache_range() * vow about it. But we have to fix the length as it's actual * transfer length and can be unaligned. This is potentially * dangerous operation, it's responsibility of the calling * code to make sure enough space is reserved. */ invalidate_dcache_range((unsigned long)buffer, ALIGN((unsigned long)buffer + length, ARCH_DMA_MINALIGN)); /* Check that the TD processing happened */ if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE) printf("EHCI timed out on TD - token=%#x\n", token); /* Disable async schedule. */ cmd = ehci_readl(&ctrl->hcor->or_usbcmd); cmd &= ~CMD_ASE; ehci_writel(&ctrl->hcor->or_usbcmd, cmd); ret = handshake((uint32_t *)&ctrl->hcor->or_usbsts, STS_ASS, 0, 100 * 1000); if (ret < 0) { printf("EHCI fail timeout STS_ASS reset\n"); goto fail; } token = hc32_to_cpu(qh->qh_overlay.qt_token); if (!(QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE)) { debug("TOKEN=%#x\n", token); switch (QT_TOKEN_GET_STATUS(token) & ~(QT_TOKEN_STATUS_SPLITXSTATE | QT_TOKEN_STATUS_PERR)) { case 0: toggle = QT_TOKEN_GET_DT(token); usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), toggle); dev->status = 0; break; case QT_TOKEN_STATUS_HALTED: dev->status = USB_ST_STALLED; break; case QT_TOKEN_STATUS_ACTIVE | QT_TOKEN_STATUS_DATBUFERR: case QT_TOKEN_STATUS_DATBUFERR: dev->status = USB_ST_BUF_ERR; break; case QT_TOKEN_STATUS_HALTED | QT_TOKEN_STATUS_BABBLEDET: case QT_TOKEN_STATUS_BABBLEDET: dev->status = USB_ST_BABBLE_DET; break; default: dev->status = USB_ST_CRC_ERR; if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_HALTED) dev->status |= USB_ST_STALLED; break; } 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); return (dev->status != USB_ST_NOT_PROC) ? 0 : -1; fail: free(qtd); return -1; }
static int sl811_send_packet(struct usb_device *dev, unsigned long pipe, __u8 *buffer, int len) { __u8 ctrl = SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE; __u16 status = 0; int err = 0, time_start = get_timer(0); int need_preamble = !(rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED) && (dev->speed == USB_SPEED_LOW); if (len > 239) return -1; if (usb_pipeout(pipe)) ctrl |= SL811_USB_CTRL_DIR_OUT; if (usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) ctrl |= SL811_USB_CTRL_TOGGLE_1; if (need_preamble) ctrl |= SL811_USB_CTRL_PREAMBLE; sl811_write(SL811_INTRSTS, 0xff); while (err < 3) { sl811_write(SL811_ADDR_A, 0x10); sl811_write(SL811_LEN_A, len); if (usb_pipeout(pipe) && len) sl811_write_buf(0x10, buffer, len); if (!(rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED) && sl811_read(SL811_SOFCNTDIV)*64 < calc_needed_buswidth(len, need_preamble)) ctrl |= SL811_USB_CTRL_SOF; else ctrl &= ~SL811_USB_CTRL_SOF; sl811_write(SL811_CTRL_A, ctrl); while (!(sl811_read(SL811_INTRSTS) & SL811_INTR_DONE_A)) { if (5*CONFIG_SYS_HZ < get_timer(time_start)) { printf("USB transmit timed out\n"); return -USB_ST_CRC_ERR; } } sl811_write(SL811_INTRSTS, 0xff); status = sl811_read(SL811_STS_A); if (status & SL811_USB_STS_ACK) { int remainder = sl811_read(SL811_CNT_A); if (remainder) { PDEBUG(0, "usb transfer remainder = %d\n", remainder); len -= remainder; } if (usb_pipein(pipe) && len) sl811_read_buf(0x10, buffer, len); return len; } if ((status & SL811_USB_STS_NAK) == SL811_USB_STS_NAK) continue; PDEBUG(0, "usb transfer error %#x\n", (int)status); err++; } err = 0; if (status & SL811_USB_STS_ERROR) err |= USB_ST_BUF_ERR; if (status & SL811_USB_STS_TIMEOUT) err |= USB_ST_CRC_ERR; if (status & SL811_USB_STS_STALL) err |= USB_ST_STALLED; return -err; }
static int ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, int length, struct devrequest *req) { struct QH *qh; struct qTD *td; volatile struct qTD *vtd; unsigned long ts; uint32_t *tdp; uint32_t endpt, token, usbsts; uint32_t c, toggle; uint32_t cmd; int timeout; int ret = 0; debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p\n", dev, pipe, buffer, length, req); if (req != NULL) debug("req=%u (%#x), type=%u (%#x), value=%u (%#x), index=%u\n", req->request, req->request, req->requesttype, req->requesttype, le16_to_cpu(req->value), le16_to_cpu(req->value), le16_to_cpu(req->index)); qh = ehci_alloc(sizeof(struct QH), 32); if (qh == NULL) { debug("unable to allocate QH\n"); return -1; } qh->qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); c = (usb_pipespeed(pipe) != USB_SPEED_HIGH && usb_pipeendpoint(pipe) == 0) ? 1 : 0; endpt = (8 << 28) | (c << 27) | (usb_maxpacket(dev, pipe) << 16) | (0 << 15) | (1 << 14) | (usb_pipespeed(pipe) << 12) | (usb_pipeendpoint(pipe) << 8) | (0 << 7) | (usb_pipedevice(pipe) << 0); qh->qh_endpt1 = cpu_to_hc32(endpt); endpt = (1 << 30) | (dev->portnr << 23) | (dev->parent->devnum << 16) | (0 << 8) | (0 << 0); qh->qh_endpt2 = cpu_to_hc32(endpt); qh->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); td = NULL; tdp = &qh->qh_overlay.qt_next; toggle = usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); if (req != NULL) { td = ehci_alloc(sizeof(struct qTD), 32); if (td == NULL) { debug("unable to allocate SETUP td\n"); goto fail; } td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); token = (0 << 31) | (sizeof(*req) << 16) | (0 << 15) | (0 << 12) | (3 << 10) | (2 << 8) | (0x80 << 0); td->qt_token = cpu_to_hc32(token); if (ehci_td_buffer(td, req, sizeof(*req)) != 0) { debug("unable construct SETUP td\n"); ehci_free(td, sizeof(*td)); goto fail; } *tdp = cpu_to_hc32((uint32_t) td); tdp = &td->qt_next; toggle = 1; } if (length > 0 || req == NULL) { td = ehci_alloc(sizeof(struct qTD), 32); if (td == NULL) { debug("unable to allocate DATA td\n"); goto fail; } td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); token = (toggle << 31) | (length << 16) | ((req == NULL ? 1 : 0) << 15) | (0 << 12) | (3 << 10) | ((usb_pipein(pipe) ? 1 : 0) << 8) | (0x80 << 0); td->qt_token = cpu_to_hc32(token); if (ehci_td_buffer(td, buffer, length) != 0) { debug("unable construct DATA td\n"); ehci_free(td, sizeof(*td)); goto fail; } *tdp = cpu_to_hc32((uint32_t) td); tdp = &td->qt_next; } if (req != NULL) { td = ehci_alloc(sizeof(struct qTD), 32); if (td == NULL) { debug("unable to allocate ACK td\n"); goto fail; } td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); token = (toggle << 31) | (0 << 16) | (1 << 15) | (0 << 12) | (3 << 10) | ((usb_pipein(pipe) ? 0 : 1) << 8) | (0x80 << 0); td->qt_token = cpu_to_hc32(token); *tdp = cpu_to_hc32((uint32_t) td); tdp = &td->qt_next; } qh_list.qh_link = cpu_to_hc32((uint32_t) qh | QH_LINK_TYPE_QH); /* Flush dcache */ ehci_flush_dcache(&qh_list); usbsts = ehci_readl(&hcor->or_usbsts); ehci_writel(&hcor->or_usbsts, (usbsts & 0x3f)); /* Enable async. schedule. */ cmd = ehci_readl(&hcor->or_usbcmd); cmd |= CMD_ASE; ehci_writel(&hcor->or_usbcmd, cmd); ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, STD_ASS, 100 * 1000); if (ret < 0) { printf("EHCI fail timeout STD_ASS set\n"); goto fail; } /* Wait for TDs to be processed. */ ts = get_timer(0); vtd = td; timeout = USB_TIMEOUT_MS(pipe); do { /* Invalidate dcache */ ehci_invalidate_dcache(&qh_list); token = hc32_to_cpu(vtd->qt_token); if (!(token & 0x80)) break; WATCHDOG_RESET(); } while (get_timer(ts) < timeout); /* Check that the TD processing happened */ if (token & 0x80) { printf("EHCI timed out on TD - token=%#x\n", token); goto fail; } /* Disable async schedule. */ cmd = ehci_readl(&hcor->or_usbcmd); cmd &= ~CMD_ASE; ehci_writel(&hcor->or_usbcmd, cmd); ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, 0, 100 * 1000); if (ret < 0) { printf("EHCI fail timeout STD_ASS reset\n"); goto fail; } qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); token = hc32_to_cpu(qh->qh_overlay.qt_token); if (!(token & 0x80)) { debug("TOKEN=%#x\n", token); switch (token & 0xfc) { case 0: toggle = token >> 31; usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), toggle); dev->status = 0; break; case 0x40: dev->status = USB_ST_STALLED; break; case 0xa0: case 0x20: dev->status = USB_ST_BUF_ERR; break; case 0x50: case 0x10: dev->status = USB_ST_BABBLE_DET; break; default: dev->status = USB_ST_CRC_ERR; if ((token & 0x40) == 0x40) dev->status |= USB_ST_STALLED; break; } dev->act_len = length - ((token >> 16) & 0x7fff); } else {
//Create and return an interrupt queue object. struct int_queue* EHCICreateIntQueue(struct usb_device *dev, unsigned long pipe, int queuesize, int elementsize, void *buffer, int interval) { struct ehci_ctrl *ctrl = ehci_get_ctrl(dev); struct int_queue *result = NULL; uint32_t i, toggle; struct QH *list = NULL; int cmd = 0; DWORD dwFlags; /* * Interrupt transfers requiring several transactions are not supported * because bInterval is ignored. * * Also, ehci_submit_async() relies on wMaxPacketSize being a power of 2 * <= PKT_ALIGN if several qTDs are required, while the USB * specification does not constrain this for interrupt transfers. That * means that ehci_submit_async() would support interrupt transfers * requiring several transactions only as long as the transfer size does * not require more than a single qTD. */ if (elementsize > usb_maxpacket(dev, pipe)) { printf("%s: xfers requiring several transactions are not supported.\r\n", "_ehci_create_int_queue"); return NULL; } if (usb_pipetype(pipe) != PIPE_INTERRUPT) { debug("non-interrupt pipe (type=%lu)", usb_pipetype(pipe)); return NULL; } /* limit to 4 full pages worth of data - * we can safely fit them in a single TD, * no matter the alignment */ if (elementsize >= 16384) { debug("too large elements for interrupt transfers\r\n"); return NULL; } result = malloc(sizeof(*result)); if (!result) { debug("ehci intr queue: out of memory\r\n"); goto fail1; } //Create EVENT object to synchronizing the access. result->hEvent = CreateEvent(FALSE); if (NULL == result->hEvent) { goto fail1; } result->dwTimeOut = 0; result->pNext = NULL; result->pOwnerThread = KernelThreadManager.lpCurrentKernelThread; result->QueueIntHandler = _ehciQueueIntHandler; result->pUsbDev = dev; result->dwStatus = INT_QUEUE_STATUS_INITIALIZED; result->elementsize = elementsize; result->pipe = pipe; result->first = memalign(USB_DMA_MINALIGN, sizeof(struct QH) * queuesize); if (!result->first) { debug("ehci intr queue: out of memory\r\n"); goto fail2; } debug("%s: Allocate %d QH(s) at %X.\r\n", __func__,queuesize,result->first); result->current = result->first; result->last = result->first + queuesize - 1; result->tds = memalign(USB_DMA_MINALIGN, sizeof(struct qTD) * queuesize); if (!result->tds) { debug("ehci intr queue: out of memory\r\n"); goto fail3; } debug("%s: Allocate %d qTD(s) at %X.\r\n", __func__,queuesize, result->tds); memset(result->first, 0, sizeof(struct QH) * queuesize); memset(result->tds, 0, sizeof(struct qTD) * queuesize); toggle = usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); for (i = 0; i < (uint32_t)queuesize; i++) { struct QH *qh = result->first + i; struct qTD *td = result->tds + i; void **buf = &qh->buffer; qh->qh_link = cpu_to_hc32((unsigned long)(qh + 1) | QH_LINK_TYPE_QH); if (i == queuesize - 1) qh->qh_link = cpu_to_hc32(QH_LINK_TERMINATE); qh->qh_overlay.qt_next = cpu_to_hc32((unsigned long)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 = 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) { /* C-mask: microframes 2-4 */ qh->qh_endpt2 |= cpu_to_hc32((0x1c << 8)); } ehci_update_endpt2_dev_n_port(dev, qh); td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); debug("%s: communication direction is '%s'\r\n", __func__, usb_pipein(pipe) ? "in" : "out"); if (i == queuesize - 1) //Last one,set IoC bit. { td->qt_token = cpu_to_hc32( QT_TOKEN_DT(toggle) | (elementsize << 16) | (1 << 15) | //Interrupt On Completion. (3 << 10) | //CERR bits. ((usb_pipein(pipe) ? 1 : 0) << 8) | /* IN/OUT token */ 0x80); /* active */ } else { td->qt_token = cpu_to_hc32( QT_TOKEN_DT(toggle) | (elementsize << 16) | (3 << 10) | //CERR bits. ((usb_pipein(pipe) ? 1 : 0) << 8) | /* IN/OUT token */ 0x80); /* active */ } debug("%s: construct TD token = %X.\r\n", __func__, td->qt_token); td->qt_buffer[0] = cpu_to_hc32((unsigned long)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); #ifdef __MS_VC__ //MS VC can not support sizeof(void) operation,we should //convert the buffer type to char*. *buf = (void*)((char*)buffer + i * elementsize); #else //sizeof(void) is 1 under GCC or other environment,so the //following sentence is same as above one. *buf = buffer + i * elementsize; #endif toggle ^= 1; } flush_dcache_range((unsigned long)buffer, ALIGN_END_ADDR(char, buffer, queuesize * elementsize)); flush_dcache_range((unsigned long)result->first, ALIGN_END_ADDR(struct QH, result->first, queuesize)); flush_dcache_range((unsigned long)result->tds, ALIGN_END_ADDR(struct qTD, result->tds, queuesize)); //Acquire exclusively accessing of the controller. WaitForThisObject(ctrl->hMutex); if (ctrl->periodic_schedules > 0) { if (ehci_disable_periodic(ctrl) < 0) { ReleaseMutex(ctrl->hMutex); _hx_printf("FATAL %s: periodic should never fail, but did.\r\n",__func__); goto fail3; } } __ENTER_CRITICAL_SECTION(NULL, dwFlags); /* hook up to periodic list */ list = &ctrl->periodic_queue; result->last->qh_link = list->qh_link; list->qh_link = cpu_to_hc32((unsigned long)result->first | QH_LINK_TYPE_QH); //Link interrupt queue to Controller's pending queue. if (NULL == ctrl->pIntQueueFirst) { ctrl->pIntQueueFirst = result; ctrl->pIntQueueLast = result; } else { result->pNext = ctrl->pIntQueueFirst; ctrl->pIntQueueFirst = result; } __LEAVE_CRITICAL_SECTION(NULL, dwFlags); flush_dcache_range((unsigned long)result->last, ALIGN_END_ADDR(struct QH, result->last, 1)); flush_dcache_range((unsigned long)list, ALIGN_END_ADDR(struct QH, list, 1)); if (ehci_enable_periodic(ctrl) < 0) { ReleaseMutex(ctrl->hMutex); _hx_printf("FATAL %s: periodic should never fail, but did.\r\n", __func__);; goto fail3; } ctrl->periodic_schedules++; ReleaseMutex(ctrl->hMutex); debug("Exit create_int_queue\r\n"); return result; fail3: if (result->tds) free(result->tds); fail2: if (result->first) free(result->first); //if (result) // free(result); fail1: if (result) { if (NULL != result->hEvent) { DestroyEvent(result->hEvent); } free(result); } return NULL; }
/* Set up PTD's. */ static void prepare_ptd(struct isp1362_hcd *isp1362_hcd, struct urb *urb, struct isp1362_ep *ep, struct isp1362_ep_queue *epq, u16 fno) { struct ptd *ptd; int toggle; int dir; u16 len; size_t buf_len = urb->transfer_buffer_length - urb->actual_length; DBG(3, "%s: %s ep %p\n", __func__, epq->name, ep); ptd = &ep->ptd; ep->data = (unsigned char *)urb->transfer_buffer + urb->actual_length; switch (ep->nextpid) { case USB_PID_IN: toggle = usb_gettoggle(urb->dev, ep->epnum, 0); dir = PTD_DIR_IN; if (usb_pipecontrol(urb->pipe)) { len = min_t(size_t, ep->maxpacket, buf_len); } else if (usb_pipeisoc(urb->pipe)) { len = min_t(size_t, urb->iso_frame_desc[fno].length, MAX_XFER_SIZE); ep->data = urb->transfer_buffer + urb->iso_frame_desc[fno].offset; } else len = max_transfer_size(epq, buf_len, ep->maxpacket); DBG(1, "%s: IN len %d/%d/%d from URB\n", __func__, len, ep->maxpacket, (int)buf_len); break; case USB_PID_OUT: toggle = usb_gettoggle(urb->dev, ep->epnum, 1); dir = PTD_DIR_OUT; if (usb_pipecontrol(urb->pipe)) len = min_t(size_t, ep->maxpacket, buf_len); else if (usb_pipeisoc(urb->pipe)) len = min_t(size_t, urb->iso_frame_desc[0].length, MAX_XFER_SIZE); else len = max_transfer_size(epq, buf_len, ep->maxpacket); if (len == 0) pr_info("%s: Sending ZERO packet: %d\n", __func__, urb->transfer_flags & URB_ZERO_PACKET); DBG(1, "%s: OUT len %d/%d/%d from URB\n", __func__, len, ep->maxpacket, (int)buf_len); break; case USB_PID_SETUP: toggle = 0; dir = PTD_DIR_SETUP; len = sizeof(struct usb_ctrlrequest); DBG(1, "%s: SETUP len %d\n", __func__, len); ep->data = urb->setup_packet; break; case USB_PID_ACK: toggle = 1; len = 0; dir = (urb->transfer_buffer_length && usb_pipein(urb->pipe)) ? PTD_DIR_OUT : PTD_DIR_IN; DBG(1, "%s: ACK len %d\n", __func__, len); break; default: toggle = dir = len = 0; pr_err("%s@%d: ep->nextpid %02x\n", __func__, __LINE__, ep->nextpid); BUG_ON(1); } ep->length = len; if (!len) ep->data = NULL; ptd->count = PTD_CC_MSK | PTD_ACTIVE_MSK | PTD_TOGGLE(toggle); ptd->mps = PTD_MPS(ep->maxpacket) | PTD_SPD(urb->dev->speed == USB_SPEED_LOW) | PTD_EP(ep->epnum); ptd->len = PTD_LEN(len) | PTD_DIR(dir); ptd->faddr = PTD_FA(usb_pipedevice(urb->pipe)); if (usb_pipeint(urb->pipe)) { ptd->faddr |= PTD_SF_INT(ep->branch); ptd->faddr |= PTD_PR(ep->interval ? __ffs(ep->interval) : 0); } if (usb_pipeisoc(urb->pipe)) ptd->faddr |= PTD_SF_ISO(fno); DBG(1, "%s: Finished\n", __func__); }
static int ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, int length, struct devrequest *req) { static struct QH qh __attribute__((aligned(32))); static struct qTD qtd[3] __attribute__((aligned (32))); int qtd_counter = 0; volatile struct qTD *vtd; unsigned long ts; uint32_t *tdp; uint32_t endpt, token, usbsts; uint32_t c, toggle; uint32_t cmd; int timeout; int ret = 0; debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p\n", dev, pipe, buffer, length, req); if (req != NULL) debug("req=%u (%#x), type=%u (%#x), value=%u (%#x), index=%u\n", req->request, req->request, req->requesttype, req->requesttype, le16_to_cpu(req->value), le16_to_cpu(req->value), le16_to_cpu(req->index)); memset(&qh, 0, sizeof(struct QH)); memset(qtd, 0, sizeof(qtd)); toggle = usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); /* * Setup QH (3.6 in ehci-r10.pdf) * * qh_link ................. 03-00 H * qh_endpt1 ............... 07-04 H * qh_endpt2 ............... 0B-08 H * - qh_curtd * qh_overlay.qt_next ...... 13-10 H * - qh_overlay.qt_altnext */ qh.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); c = (usb_pipespeed(pipe) != USB_SPEED_HIGH && usb_pipeendpoint(pipe) == 0) ? 1 : 0; endpt = (8 << 28) | (c << 27) | (usb_maxpacket(dev, pipe) << 16) | (0 << 15) | (1 << 14) | (usb_pipespeed(pipe) << 12) | (usb_pipeendpoint(pipe) << 8) | (0 << 7) | (usb_pipedevice(pipe) << 0); qh.qh_endpt1 = cpu_to_hc32(endpt); endpt = (1 << 30) | (dev->portnr << 23) | (dev->parent->devnum << 16) | (0 << 8) | (0 << 0); qh.qh_endpt2 = cpu_to_hc32(endpt); qh.qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); tdp = &qh.qh_overlay.qt_next; if (req != NULL) { /* * Setup request qTD (3.5 in ehci-r10.pdf) * * qt_next ................ 03-00 H * qt_altnext ............. 07-04 H * qt_token ............... 0B-08 H * * [ buffer, buffer_hi ] loaded with "req". */ qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); token = (0 << 31) | (sizeof(*req) << 16) | (0 << 15) | (0 << 12) | (3 << 10) | (2 << 8) | (0x80 << 0); qtd[qtd_counter].qt_token = cpu_to_hc32(token); if (ehci_td_buffer(&qtd[qtd_counter], req, sizeof(*req)) != 0) { debug("unable construct SETUP td\n"); goto fail; } /* Update previous qTD! */ *tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]); tdp = &qtd[qtd_counter++].qt_next; toggle = 1; } if (length > 0 || req == NULL) { /* * Setup request qTD (3.5 in ehci-r10.pdf) * * qt_next ................ 03-00 H * qt_altnext ............. 07-04 H * qt_token ............... 0B-08 H * * [ buffer, buffer_hi ] loaded with "buffer". */ qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); token = (toggle << 31) | (length << 16) | ((req == NULL ? 1 : 0) << 15) | (0 << 12) | (3 << 10) | ((usb_pipein(pipe) ? 1 : 0) << 8) | (0x80 << 0); qtd[qtd_counter].qt_token = cpu_to_hc32(token); if (ehci_td_buffer(&qtd[qtd_counter], buffer, length) != 0) { debug("unable construct DATA td\n"); goto fail; } /* Update previous qTD! */ *tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]); tdp = &qtd[qtd_counter++].qt_next; } if (req != NULL) { /* * Setup request qTD (3.5 in ehci-r10.pdf) * * qt_next ................ 03-00 H * qt_altnext ............. 07-04 H * qt_token ............... 0B-08 H */ qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); token = (toggle << 31) | (0 << 16) | (1 << 15) | (0 << 12) | (3 << 10) | ((usb_pipein(pipe) ? 0 : 1) << 8) | (0x80 << 0); qtd[qtd_counter].qt_token = cpu_to_hc32(token); /* Update previous qTD! */ *tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]); tdp = &qtd[qtd_counter++].qt_next; } qh_list.qh_link = cpu_to_hc32((uint32_t)&qh | QH_LINK_TYPE_QH); /* Flush dcache */ flush_dcache_range((uint32_t)&qh_list, (uint32_t)&qh_list + sizeof(struct QH)); flush_dcache_range((uint32_t)&qh, (uint32_t)&qh + sizeof(struct QH)); flush_dcache_range((uint32_t)qtd, (uint32_t)qtd + sizeof(qtd)); usbsts = ehci_readl(&hcor->or_usbsts); ehci_writel(&hcor->or_usbsts, (usbsts & 0x3f)); /* Enable async. schedule. */ cmd = ehci_readl(&hcor->or_usbcmd); cmd |= CMD_ASE; ehci_writel(&hcor->or_usbcmd, cmd); ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, STD_ASS, 100 * 1000); if (ret < 0) { printf("EHCI fail timeout STD_ASS set\n"); goto fail; } /* Wait for TDs to be processed. */ ts = get_timer(0); vtd = &qtd[qtd_counter - 1]; timeout = USB_TIMEOUT_MS(pipe); do { /* Invalidate dcache */ invalidate_dcache_range((uint32_t)&qh_list, (uint32_t)&qh_list + sizeof(struct QH)); invalidate_dcache_range((uint32_t)&qh, (uint32_t)&qh + sizeof(struct QH)); invalidate_dcache_range((uint32_t)qtd, (uint32_t)qtd + sizeof(qtd)); token = hc32_to_cpu(vtd->qt_token); if (!(token & 0x80)) break; WATCHDOG_RESET(); } while (get_timer(ts) < timeout); /* Invalidate the memory area occupied by buffer */ invalidate_dcache_range(((uint32_t)buffer & ~31), ((uint32_t)buffer & ~31) + roundup(length, 32)); /* Check that the TD processing happened */ if (token & 0x80) { printf("EHCI timed out on TD - token=%#x\n", token); } /* Disable async schedule. */ cmd = ehci_readl(&hcor->or_usbcmd); cmd &= ~CMD_ASE; ehci_writel(&hcor->or_usbcmd, cmd); ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, 0, 100 * 1000); if (ret < 0) { printf("EHCI fail timeout STD_ASS reset\n"); goto fail; } qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); token = hc32_to_cpu(qh.qh_overlay.qt_token); if (!(token & 0x80)) { debug("TOKEN=%#x\n", token); switch (token & 0xfc) { case 0: toggle = token >> 31; usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), toggle); dev->status = 0; break; case 0x40: dev->status = USB_ST_STALLED; break; case 0xa0: case 0x20: dev->status = USB_ST_BUF_ERR; break; case 0x50: case 0x10: dev->status = USB_ST_BABBLE_DET; break; default: dev->status = USB_ST_CRC_ERR; if ((token & 0x40) == 0x40) dev->status |= USB_ST_STALLED; break; } dev->act_len = length - ((token >> 16) & 0x7fff); } else {
static int dwc2_transfer(struct usb_device *dev, unsigned long pipe, int size, int pid, ep_dir_t dir, uint32_t ch_num, u8 *data_buf) { struct dwc_ctrl *ctrl = dev->controller; pUSB_OTG_REG reg = ctrl->otgReg; uint32_t do_copy; int ret; uint32_t packet_cnt; uint32_t packet_size; uint32_t transferred = 0; uint32_t inpkt_length; uint32_t eptype; HCTSIZ_DATA hctsiz = { .d32 = 0 }; HCCHAR_DATA hcchar = { .d32 = 0 }; void *aligned_buf; debug("# %s #dev %p, size %d, pid %d, dir %d, buf %p\n", __func__, dev, size, pid, dir, data_buf); if (dev->speed != USB_SPEED_HIGH) { printf("Support high-speed only\n"); return -1; } if (size > DMA_SIZE) { printf("Transfer too large: %d\n", size); return -1; } packet_size = usb_maxpacket(dev, pipe); packet_cnt = DIV_ROUND_UP(size, packet_size); inpkt_length = roundup(size, packet_size); /* At least 1 packet should be programed */ packet_cnt = (packet_cnt == 0) ? 1 : packet_cnt; /* * For an IN, this field is the buffer size that the application has * reserved for the transfer. The application should program this field * as integer multiple of the maximum packet size for IN transactions. */ hctsiz.xfersize = (dir == EPDIR_OUT) ? size : inpkt_length; hctsiz.pktcnt = packet_cnt; hctsiz.pid = pid; hcchar.mps = packet_size; hcchar.epnum = usb_pipeendpoint(pipe); hcchar.epdir = dir; switch (usb_pipetype(pipe)) { case PIPE_CONTROL: eptype = 0; break; case PIPE_BULK: eptype = 2; break; default: printf("Un-supported type\n"); return -EOPNOTSUPP; } hcchar.eptype = eptype; hcchar.multicnt = 1; hcchar.devaddr = usb_pipedevice(pipe); hcchar.chdis = 0; hcchar.chen = 1; /* * Check the buffer address which should be 4-byte aligned and DMA * coherent */ //do_copy = !dma_coherent(data_buf) || ((uintptr_t)data_buf & 0x3); do_copy = 1;//(uintptr_t)data_buf & 0x3; aligned_buf = do_copy ? ctrl->align_buf : data_buf; if (do_copy && (dir == EPDIR_OUT)) memcpy(aligned_buf, data_buf, size); if (dir == EPDIR_OUT) flush_dcache_range(aligned_buf, aligned_buf + roundup(size, ARCH_DMA_MINALIGN)); writel(hctsiz.d32, ®->Host.hchn[ch_num].hctsizn); writel((uint32_t)aligned_buf, ®->Host.hchn[ch_num].hcdman); writel(hcchar.d32, ®->Host.hchn[ch_num].hccharn); ret = dwc_wait_for_complete(dev, ch_num); if (ret >= 0) { /* Calculate actual transferred length */ transferred = (dir == EPDIR_IN) ? inpkt_length - ret : ret; if (dir == EPDIR_IN) invalidate_dcache_range(aligned_buf, aligned_buf + roundup(transferred, ARCH_DMA_MINALIGN)); if (do_copy && (dir == EPDIR_IN)) memcpy(data_buf, aligned_buf, transferred); } /* Save data toggle */ hctsiz.d32 = readl(®->Host.hchn[ch_num].hctsizn); usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), (hctsiz.pid >> 1)); if (ret < 0) { printf("%s Transfer stop code: %d\n", __func__, ret); return ret; } return transferred; } int usb_lowlevel_stop(int index) { pUSB_OTG_REG reg = (pUSB_OTG_REG)rkusb_active_hcd->regbase; HPRT0_DATA hprt0 = { .d32 = 0 }; GUSBCFG_DATA gusbcfg = { .d32 = 0 }; /* Stop connect and put everything of port state in reset. */ hprt0.prtena = 1; hprt0.prtenchng = 1; hprt0.prtconndet = 1; writel(hprt0.d32, ®->Host.hprt); gusbcfg.d32 = 0x1400; writel(gusbcfg.d32, ®->Core.gusbcfg); return 0; } static void dwc2_reinit(pUSB_OTG_REG regbase) { pUSB_OTG_REG reg = regbase; GUSBCFG_DATA gusbcfg = { .d32 = 0 }; GRSTCTL_DATA grstctl = { .d32 = 0 }; GINTSTS_DATA gintsts = { .d32 = 0 }; GAHBCFG_DATA gahbcfg = { .d32 = 0 }; RXFIFOSIZE_DATA grxfsiz = { .d32 = 0 }; HCINTMSK_DATA hcintmsk = { .d32 = 0 }; TXFIFOSIZE_DATA gnptxfsiz = { .d32 = 0 }; const int timeout = 10000; int i; /* Wait for AHB idle */ for (i = 0; i < timeout; i++) { udelay(1); grstctl.d32 = readl(®->Core.grstctl); if (grstctl.ahbidle) break; } if (i == timeout) { printf("DWC2 Init error AHB Idle\n"); return; } /* Restart the Phy Clock */ writel(0x0, ®->ClkGate.PCGCR); /* Core soft reset */ grstctl.csftrst = 1; writel(grstctl.d32, ®->Core.grstctl); for (i = 0; i < timeout; i++) { udelay(1); grstctl.d32 = readl(®->Core.grstctl); if (!grstctl.csftrst) break; } if (i == timeout) { printf("DWC2 Init error reset fail\n"); return; } /* Set 16bit PHY if & Force host mode */ gusbcfg.d32 = readl(®->Core.gusbcfg); gusbcfg.phyif = 1; gusbcfg.forcehstmode = 1; gusbcfg.forcedevmode = 0; writel(gusbcfg.d32, ®->Core.gusbcfg); /* Wait for force host mode effect, it may takes 100ms */ for (i = 0; i < timeout; i++) { udelay(10); gintsts.d32 = readl(®->Core.gintsts); if (gintsts.curmod) break; } if (i == timeout) { printf("DWC2 Init error force host mode fail\n"); return; } /* * Config FIFO * The non-periodic tx fifo and rx fifo share one continuous * piece of IP-internal SRAM. */ grxfsiz.rxfdep = DWC2_RXFIFO_DEPTH; writel(grxfsiz.d32, ®->Core.grxfsiz); gnptxfsiz.nptxfstaddr = DWC2_RXFIFO_DEPTH; gnptxfsiz.nptxfdep = DWC2_NPTXFIFO_DEPTH; writel(gnptxfsiz.d32, ®->Core.gnptxfsiz); /* Init host channels */ hcintmsk.xfercomp = 1; hcintmsk.xacterr = 1; hcintmsk.stall = 1; hcintmsk.chhltd = 1; hcintmsk.bblerr = 1; for (i = 0; i < MAX_EPS_CHANNELS; i++) writel(hcintmsk.d32, ®->Host.hchn[i].hcintmaskn); /* Unmask interrupt & Use configure dma mode */ gahbcfg.glblintrmsk = 1; gahbcfg.hbstlen = DWC_GAHBCFG_INT_DMA_BURST_INCR8; gahbcfg.dmaen = 1; writel(gahbcfg.d32, ®->Core.gahbcfg); printf("DWC2@0x%p init finished!\n", regbase); } int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) { pUSB_OTG_REG reg = (pUSB_OTG_REG)rkusb_active_hcd->regbase; struct dwc_ctrl *dwcctl; dwcctl = malloc(sizeof(struct dwc_ctrl)); if (!dwcctl) return -ENOMEM; dwcctl->otgReg = reg; dwcctl->rootdev = 0; dwcctl->align_buf = memalign(USB_DMA_MINALIGN, DMA_SIZE); if (!dwcctl->align_buf) return -ENOMEM; dwc2_reinit(reg); *controller = dwcctl; return 0; } int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int length) { ep_dir_t data_dir; int pid; int ret = 0; if (usb_pipetype(pipe) != PIPE_BULK) { debug("non-bulk pipe (type=%lu)", usb_pipetype(pipe)); return -1; } if (usb_pipein(pipe)) data_dir = EPDIR_IN; else if (usb_pipeout(pipe)) data_dir = EPDIR_OUT; else return -1; pid = usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); if (pid) pid = DWC_HCTSIZ_DATA1; else pid = DWC_HCTSIZ_DATA0; ret = dwc2_transfer(dev, pipe, length, pid, data_dir, 0, buffer); if (ret < 0) return -1; dev->act_len = ret; dev->status = 0; return 0; } int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int length, struct devrequest *setup) { int ret = 0; struct dwc_ctrl *ctrl = dev->controller; ep_dir_t data_dir; if (usb_pipetype(pipe) != PIPE_CONTROL) { debug("non-control pipe (type=%lu)", usb_pipetype(pipe)); return -1; } if (usb_pipedevice(pipe) == ctrl->rootdev) { if (!ctrl->rootdev) dev->speed = USB_SPEED_HIGH; return dwc2_submit_root(dev, pipe, buffer, length, setup); } if (usb_pipein(pipe)) data_dir = EPDIR_IN; else if (usb_pipeout(pipe)) data_dir = EPDIR_OUT; else return -1; /* Setup Phase */ if (dwc2_transfer(dev, pipe, 8, PID_SETUP, EPDIR_OUT, 0, (u8 *)setup) < 0) return -1; /* Data Phase */ if (length > 0) { ret = dwc2_transfer(dev, pipe, length, PID_DATA1, data_dir, 0, buffer); if (ret < 0) return -1; } /* Status Phase */ if (dwc2_transfer(dev, pipe, 0, PID_DATA1, !data_dir, 0, NULL) < 0) return -1; dev->act_len = ret; dev->status = 0; return 0; } int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int length, int interval) { return 0; }