diff options
Diffstat (limited to 'drivers/usb/host')
-rw-r--r-- | drivers/usb/host/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 312 | ||||
-rw-r--r-- | drivers/usb/host/ehci-mxs.c | 26 | ||||
-rw-r--r-- | drivers/usb/host/ehci.h | 60 | ||||
-rw-r--r-- | drivers/usb/host/ohci-da8xx.c | 48 | ||||
-rw-r--r-- | drivers/usb/host/ohci-hcd.c | 70 |
6 files changed, 368 insertions, 149 deletions
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 6de9164..bcb4662 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -28,6 +28,7 @@ LIB := $(obj)libusb_host.o # ohci COBJS-$(CONFIG_USB_OHCI_NEW) += ohci-hcd.o COBJS-$(CONFIG_USB_ATMEL) += ohci-at91.o +COBJS-$(CONFIG_USB_OHCI_DA8XX) += ohci-da8xx.o COBJS-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o COBJS-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o COBJS-$(CONFIG_USB_S3C64XX) += s3c64xx-hcd.o diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 2a82a29..392e286 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -22,6 +22,7 @@ */ #include <common.h> #include <asm/byteorder.h> +#include <asm/unaligned.h> #include <usb.h> #include <asm/io.h> #include <malloc.h> @@ -163,7 +164,7 @@ static int ehci_reset(void) #ifdef CONFIG_USB_EHCI_TXFIFO_THRESH cmd = ehci_readl(&hcor->or_txfilltuning); - cmd &= ~TXFIFO_THRESH(0x3f); + cmd &= ~TXFIFO_THRESH_MASK; cmd |= TXFIFO_THRESH(CONFIG_USB_EHCI_TXFIFO_THRESH); ehci_writel(&hcor->or_txfilltuning, cmd); #endif @@ -183,10 +184,10 @@ static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz) flush_dcache_range(addr, ALIGN(addr + sz, ARCH_DMA_MINALIGN)); idx = 0; - while (idx < 5) { + while (idx < QT_BUFFER_CNT) { td->qt_buffer[idx] = cpu_to_hc32(addr); td->qt_buffer_hi[idx] = 0; - next = (addr + 4096) & ~4095; + next = (addr + EHCI_PAGE_SIZE) & ~(EHCI_PAGE_SIZE - 1); delta = next - addr; if (delta >= sz) break; @@ -195,7 +196,7 @@ static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz) idx++; } - if (idx == 5) { + if (idx == QT_BUFFER_CNT) { printf("out of buffer pointers (%u bytes left)\n", sz); return -1; } @@ -208,13 +209,14 @@ 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); - ALLOC_ALIGN_BUFFER(struct qTD, qtd, 3, 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, token, usbsts; + uint32_t endpt, maxpacket, token, usbsts; uint32_t c, toggle; uint32_t cmd; int timeout; @@ -229,8 +231,73 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, 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 ((uint32_t)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, 3 * sizeof(*qtd)); + memset(qtd, 0, qtd_count * sizeof(*qtd)); toggle = usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); @@ -245,20 +312,18 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, * - 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); + c = usb_pipespeed(pipe) != 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(usb_pipespeed(pipe)) | + QH_ENDPT1_ENDPT(usb_pipeendpoint(pipe)) | QH_ENDPT1_I(0) | + QH_ENDPT1_DEVADDR(usb_pipedevice(pipe)); qh->qh_endpt1 = cpu_to_hc32(endpt); - endpt = (1 << 30) | - (dev->portnr << 23) | - (dev->parent->devnum << 16) | (0 << 8) | (0 << 0); + endpt = QH_ENDPT2_MULT(1) | QH_ENDPT2_PORTNUM(dev->portnr) | + QH_ENDPT2_HUBADDR(dev->parent->devnum) | + QH_ENDPT2_UFCMASK(0) | QH_ENDPT2_UFSMASK(0); qh->qh_endpt2 = cpu_to_hc32(endpt); qh->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); @@ -276,12 +341,13 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, */ 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); + 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)) != 0) { - printf("unable construct SETUP td\n"); + if (ehci_td_buffer(&qtd[qtd_counter], req, sizeof(*req))) { + printf("unable to construct SETUP TD\n"); goto fail; } /* Update previous qTD! */ @@ -291,31 +357,71 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, } 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) { - printf("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; + 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 -= (uint32_t)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((uint32_t)&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) { @@ -328,12 +434,11 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *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) | - (0 << 16) | - (1 << 15) | - (0 << 12) | - (3 << 10) | - ((usb_pipein(pipe) ? 0 : 1) << 8) | (0x80 << 0); + 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((uint32_t)&qtd[qtd_counter]); @@ -346,7 +451,8 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, flush_dcache_range((uint32_t)qh_list, ALIGN_END_ADDR(struct QH, qh_list, 1)); flush_dcache_range((uint32_t)qh, ALIGN_END_ADDR(struct QH, qh, 1)); - flush_dcache_range((uint32_t)qtd, ALIGN_END_ADDR(struct qTD, qtd, 3)); + flush_dcache_range((uint32_t)qtd, + ALIGN_END_ADDR(struct qTD, qtd, qtd_count)); /* Set async. queue head pointer. */ ehci_writel(&hcor->or_asynclistaddr, (uint32_t)qh_list); @@ -359,10 +465,10 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, cmd |= CMD_ASE; ehci_writel(&hcor->or_usbcmd, cmd); - ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, STD_ASS, + ret = handshake((uint32_t *)&hcor->or_usbsts, STS_ASS, STS_ASS, 100 * 1000); if (ret < 0) { - printf("EHCI fail timeout STD_ASS set\n"); + printf("EHCI fail timeout STS_ASS set\n"); goto fail; } @@ -377,10 +483,10 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, invalidate_dcache_range((uint32_t)qh, ALIGN_END_ADDR(struct QH, qh, 1)); invalidate_dcache_range((uint32_t)qtd, - ALIGN_END_ADDR(struct qTD, qtd, 3)); + ALIGN_END_ADDR(struct qTD, qtd, qtd_count)); token = hc32_to_cpu(vtd->qt_token); - if (!(token & 0x80)) + if (!(QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE)) break; WATCHDOG_RESET(); } while (get_timer(ts) < timeout); @@ -398,50 +504,50 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, ALIGN((uint32_t)buffer + length, ARCH_DMA_MINALIGN)); /* Check that the TD processing happened */ - if (token & 0x80) { + 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(&hcor->or_usbcmd); cmd &= ~CMD_ASE; ehci_writel(&hcor->or_usbcmd, cmd); - ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, 0, + ret = handshake((uint32_t *)&hcor->or_usbsts, STS_ASS, 0, 100 * 1000); if (ret < 0) { - printf("EHCI fail timeout STD_ASS reset\n"); + printf("EHCI fail timeout STS_ASS reset\n"); goto fail; } token = hc32_to_cpu(qh->qh_overlay.qt_token); - if (!(token & 0x80)) { + if (!(QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE)) { debug("TOKEN=%#x\n", token); - switch (token & 0xfc) { + switch (QT_TOKEN_GET_STATUS(token) & + ~(QT_TOKEN_STATUS_SPLITXSTATE | QT_TOKEN_STATUS_PERR)) { case 0: - toggle = token >> 31; + toggle = QT_TOKEN_GET_DT(token); usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), toggle); dev->status = 0; break; - case 0x40: + case QT_TOKEN_STATUS_HALTED: dev->status = USB_ST_STALLED; break; - case 0xa0: - case 0x20: + case QT_TOKEN_STATUS_ACTIVE | QT_TOKEN_STATUS_DATBUFERR: + case QT_TOKEN_STATUS_DATBUFERR: dev->status = USB_ST_BUF_ERR; break; - case 0x50: - case 0x10: + 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 ((token & 0x40) == 0x40) + if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_HALTED) dev->status |= USB_ST_STALLED; break; } - dev->act_len = length - ((token >> 16) & 0x7fff); + dev->act_len = length - QT_TOKEN_GET_TOTALBYTES(token); } else { dev->act_len = 0; debug("dev=%u, usbsts=%#x, p[1]=%#x, p[2]=%#x\n", @@ -450,9 +556,11 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, ehci_readl(&hcor->or_portsc[1])); } + free(qtd); return (dev->status != USB_ST_NOT_PROC) ? 0 : -1; fail: + free(qtd); return -1; } @@ -499,12 +607,14 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, case USB_DT_DEVICE: debug("USB_DT_DEVICE request\n"); srcptr = &descriptor.device; - srclen = 0x12; + srclen = descriptor.device.bLength; break; case USB_DT_CONFIG: debug("USB_DT_CONFIG config\n"); srcptr = &descriptor.config; - srclen = 0x19; + srclen = descriptor.config.bLength + + descriptor.interface.bLength + + descriptor.endpoint.bLength; break; case USB_DT_STRING: debug("USB_DT_STRING config\n"); @@ -539,7 +649,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, case USB_DT_HUB: debug("USB_DT_HUB config\n"); srcptr = &descriptor.hub; - srclen = 0x8; + srclen = descriptor.hub.bLength; break; default: debug("unknown value %x\n", le16_to_cpu(req->value)); @@ -577,13 +687,13 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, tmpbuf[1] |= USB_PORT_STAT_POWER >> 8; if (ehci_is_TDI()) { - switch ((reg >> 26) & 3) { - case 0: + switch (PORTSC_PSPD(reg)) { + case PORTSC_PSPD_FS: break; - case 1: + case PORTSC_PSPD_LS: tmpbuf[1] |= USB_PORT_STAT_LOW_SPEED >> 8; break; - case 2: + case PORTSC_PSPD_HS: default: tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8; break; @@ -729,36 +839,40 @@ int usb_lowlevel_init(void) uint32_t reg; uint32_t cmd; - if (ehci_hcd_init() != 0) + if (ehci_hcd_init()) return -1; /* EHCI spec section 4.1 */ - if (ehci_reset() != 0) + if (ehci_reset()) return -1; #if defined(CONFIG_EHCI_HCD_INIT_AFTER_RESET) - if (ehci_hcd_init() != 0) + if (ehci_hcd_init()) return -1; #endif /* 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); - qh_list->qh_endpt1 = cpu_to_hc32((1 << 15) | (USB_SPEED_HIGH << 12)); + qh_list->qh_endpt1 = cpu_to_hc32(QH_ENDPT1_H(1) | + QH_ENDPT1_EPS(USB_SPEED_HIGH)); qh_list->qh_curtd = cpu_to_hc32(QT_NEXT_TERMINATE); qh_list->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); qh_list->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); - qh_list->qh_overlay.qt_token = cpu_to_hc32(0x40); + qh_list->qh_overlay.qt_token = + cpu_to_hc32(QT_TOKEN_STATUS(QT_TOKEN_STATUS_HALTED)); reg = ehci_readl(&hccr->cr_hcsparams); descriptor.hub.bNbrPorts = HCS_N_PORTS(reg); printf("Register %x NbrPorts %d\n", reg, descriptor.hub.bNbrPorts); /* Port Indicators */ if (HCS_INDICATOR(reg)) - descriptor.hub.wHubCharacteristics |= 0x80; + put_unaligned(get_unaligned(&descriptor.hub.wHubCharacteristics) + | 0x80, &descriptor.hub.wHubCharacteristics); /* Port Power Control */ if (HCS_PPC(reg)) - descriptor.hub.wHubCharacteristics |= 0x01; + put_unaligned(get_unaligned(&descriptor.hub.wHubCharacteristics) + | 0x01, &descriptor.hub.wHubCharacteristics); /* Start the host controller. */ cmd = ehci_readl(&hcor->or_usbcmd); @@ -808,7 +922,7 @@ submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, } if (usb_pipedevice(pipe) == rootdev) { - if (rootdev == 0) + if (!rootdev) dev->speed = USB_SPEED_HIGH; return ehci_submit_root(dev, pipe, buffer, length, setup); } @@ -819,8 +933,24 @@ int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int length, int interval) { - debug("dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d", dev, pipe, buffer, length, interval); + + /* + * 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 (length > usb_maxpacket(dev, pipe)) { + printf("%s: Interrupt transfers requiring several transactions " + "are not supported.\n", __func__); + return -1; + } return ehci_submit_async(dev, pipe, buffer, length, NULL); } diff --git a/drivers/usb/host/ehci-mxs.c b/drivers/usb/host/ehci-mxs.c index e1bd37e..6e21669 100644 --- a/drivers/usb/host/ehci-mxs.c +++ b/drivers/usb/host/ehci-mxs.c @@ -23,7 +23,7 @@ #include <asm/io.h> #include <asm/arch/regs-common.h> #include <asm/arch/regs-base.h> -#include <asm/arch/regs-clkctrl.h> +#include <asm/arch/regs-clkctrl-mx28.h> #include <asm/arch/regs-usb.h> #include <asm/arch/regs-usbphy.h> @@ -39,8 +39,8 @@ #endif static struct ehci_mxs { - struct mx28_usb_regs *usb_regs; - struct mx28_usbphy_regs *phy_regs; + struct mxs_usb_regs *usb_regs; + struct mxs_usbphy_regs *phy_regs; } ehci_mxs; int mxs_ehci_get_port(struct ehci_mxs *mxs_usb, int port) @@ -60,8 +60,8 @@ int mxs_ehci_get_port(struct ehci_mxs *mxs_usb, int port) return -1; } - mxs_usb->usb_regs = (struct mx28_usb_regs *)usb_base; - mxs_usb->phy_regs = (struct mx28_usbphy_regs *)phy_base; + mxs_usb->usb_regs = (struct mxs_usb_regs *)usb_base; + mxs_usb->phy_regs = (struct mxs_usbphy_regs *)phy_base; return 0; } @@ -75,10 +75,10 @@ int ehci_hcd_init(void) int ret; uint32_t usb_base, cap_base; - struct mx28_register_32 *digctl_ctrl = - (struct mx28_register_32 *)HW_DIGCTL_CTRL; - struct mx28_clkctrl_regs *clkctrl_regs = - (struct mx28_clkctrl_regs *)MXS_CLKCTRL_BASE; + struct mxs_register_32 *digctl_ctrl = + (struct mxs_register_32 *)HW_DIGCTL_CTRL; + struct mxs_clkctrl_regs *clkctrl_regs = + (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE; ret = mxs_ehci_get_port(&ehci_mxs, CONFIG_EHCI_MXS_PORT); if (ret) @@ -119,10 +119,10 @@ int ehci_hcd_stop(void) { int ret; uint32_t tmp; - struct mx28_register_32 *digctl_ctrl = - (struct mx28_register_32 *)HW_DIGCTL_CTRL; - struct mx28_clkctrl_regs *clkctrl_regs = - (struct mx28_clkctrl_regs *)MXS_CLKCTRL_BASE; + struct mxs_register_32 *digctl_ctrl = + (struct mxs_register_32 *)HW_DIGCTL_CTRL; + struct mxs_clkctrl_regs *clkctrl_regs = + (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE; ret = mxs_ehci_get_port(&ehci_mxs, CONFIG_EHCI_MXS_PORT); if (ret) diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index cc00ce4..39acdf9 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -68,7 +68,7 @@ struct ehci_hcor { #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_ASS (1 << 15) #define STS_HALT (1 << 12) uint32_t or_usbintr; #define INTR_UE (1 << 0) /* USB interrupt enable */ @@ -83,11 +83,16 @@ struct ehci_hcor { uint32_t _reserved_0_; uint32_t or_burstsize; uint32_t or_txfilltuning; +#define TXFIFO_THRESH_MASK (0x3f << 16) #define TXFIFO_THRESH(p) ((p & 0x3f) << 16) uint32_t _reserved_1_[6]; uint32_t or_configflag; #define FLAG_CF (1 << 0) /* true: we'll support "high speed" */ uint32_t or_portsc[CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS]; +#define PORTSC_PSPD(x) (((x) >> 26) & 0x3) +#define PORTSC_PSPD_FS 0x0 +#define PORTSC_PSPD_LS 0x1 +#define PORTSC_PSPD_HS 0x2 uint32_t or_systune; } __attribute__ ((packed, aligned(4))); @@ -171,16 +176,40 @@ struct usb_linux_config_descriptor { /* Queue Element Transfer Descriptor (qTD). */ struct qTD { /* this part defined by EHCI spec */ - uint32_t qt_next; /* see EHCI 3.5.1 */ + uint32_t qt_next; /* see EHCI 3.5.1 */ #define QT_NEXT_TERMINATE 1 - uint32_t qt_altnext; /* see EHCI 3.5.2 */ - uint32_t qt_token; /* see EHCI 3.5.3 */ - uint32_t qt_buffer[5]; /* see EHCI 3.5.4 */ - uint32_t qt_buffer_hi[5]; /* Appendix B */ + uint32_t qt_altnext; /* see EHCI 3.5.2 */ + uint32_t qt_token; /* see EHCI 3.5.3 */ +#define QT_TOKEN_DT(x) (((x) & 0x1) << 31) /* Data Toggle */ +#define QT_TOKEN_GET_DT(x) (((x) >> 31) & 0x1) +#define QT_TOKEN_TOTALBYTES(x) (((x) & 0x7fff) << 16) /* Total Bytes to Transfer */ +#define QT_TOKEN_GET_TOTALBYTES(x) (((x) >> 16) & 0x7fff) +#define QT_TOKEN_IOC(x) (((x) & 0x1) << 15) /* Interrupt On Complete */ +#define QT_TOKEN_CPAGE(x) (((x) & 0x7) << 12) /* Current Page */ +#define QT_TOKEN_CERR(x) (((x) & 0x3) << 10) /* Error Counter */ +#define QT_TOKEN_PID(x) (((x) & 0x3) << 8) /* PID Code */ +#define QT_TOKEN_PID_OUT 0x0 +#define QT_TOKEN_PID_IN 0x1 +#define QT_TOKEN_PID_SETUP 0x2 +#define QT_TOKEN_STATUS(x) (((x) & 0xff) << 0) /* Status */ +#define QT_TOKEN_GET_STATUS(x) (((x) >> 0) & 0xff) +#define QT_TOKEN_STATUS_ACTIVE 0x80 +#define QT_TOKEN_STATUS_HALTED 0x40 +#define QT_TOKEN_STATUS_DATBUFERR 0x20 +#define QT_TOKEN_STATUS_BABBLEDET 0x10 +#define QT_TOKEN_STATUS_XACTERR 0x08 +#define QT_TOKEN_STATUS_MISSEDUFRAME 0x04 +#define QT_TOKEN_STATUS_SPLITXSTATE 0x02 +#define QT_TOKEN_STATUS_PERR 0x01 +#define QT_BUFFER_CNT 5 + uint32_t qt_buffer[QT_BUFFER_CNT]; /* see EHCI 3.5.4 */ + uint32_t qt_buffer_hi[QT_BUFFER_CNT]; /* Appendix B */ /* pad struct for 32 byte alignment */ uint32_t unused[3]; }; +#define EHCI_PAGE_SIZE 4096 + /* Queue Head (QH). */ struct QH { uint32_t qh_link; @@ -190,7 +219,26 @@ struct QH { #define QH_LINK_TYPE_SITD 4 #define QH_LINK_TYPE_FSTN 6 uint32_t qh_endpt1; +#define QH_ENDPT1_RL(x) (((x) & 0xf) << 28) /* NAK Count Reload */ +#define QH_ENDPT1_C(x) (((x) & 0x1) << 27) /* Control Endpoint Flag */ +#define QH_ENDPT1_MAXPKTLEN(x) (((x) & 0x7ff) << 16) /* Maximum Packet Length */ +#define QH_ENDPT1_H(x) (((x) & 0x1) << 15) /* Head of Reclamation List Flag */ +#define QH_ENDPT1_DTC(x) (((x) & 0x1) << 14) /* Data Toggle Control */ +#define QH_ENDPT1_DTC_IGNORE_QTD_TD 0x0 +#define QH_ENDPT1_DTC_DT_FROM_QTD 0x1 +#define QH_ENDPT1_EPS(x) (((x) & 0x3) << 12) /* Endpoint Speed */ +#define QH_ENDPT1_EPS_FS 0x0 +#define QH_ENDPT1_EPS_LS 0x1 +#define QH_ENDPT1_EPS_HS 0x2 +#define QH_ENDPT1_ENDPT(x) (((x) & 0xf) << 8) /* Endpoint Number */ +#define QH_ENDPT1_I(x) (((x) & 0x1) << 7) /* Inactivate on Next Transaction */ +#define QH_ENDPT1_DEVADDR(x) (((x) & 0x7f) << 0) /* Device Address */ uint32_t qh_endpt2; +#define QH_ENDPT2_MULT(x) (((x) & 0x3) << 30) /* High-Bandwidth Pipe Multiplier */ +#define QH_ENDPT2_PORTNUM(x) (((x) & 0x7f) << 23) /* Port Number */ +#define QH_ENDPT2_HUBADDR(x) (((x) & 0x7f) << 16) /* Hub Address */ +#define QH_ENDPT2_UFCMASK(x) (((x) & 0xff) << 8) /* Split Completion Mask */ +#define QH_ENDPT2_UFSMASK(x) (((x) & 0xff) << 0) /* Interrupt Schedule Mask */ uint32_t qh_curtd; struct qTD qh_overlay; /* diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c new file mode 100644 index 0000000..f0ccb83 --- /dev/null +++ b/drivers/usb/host/ohci-da8xx.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 Sughosh Ganu <urwithsughosh@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <common.h> + +#include <asm/arch/da8xx-usb.h> + +int usb_cpu_init(void) +{ + /* enable psc for usb2.0 */ + lpsc_on(DAVINCI_LPSC_USB20); + + /* enable psc for usb1.0 */ + lpsc_on(DAVINCI_LPSC_USB11); + + /* start the on-chip usb phy and its pll */ + if (usb_phy_on()) + return 0; + + return 1; +} + +int usb_cpu_stop(void) +{ + usb_phy_off(); + + /* turn off the usb clock and assert the module reset */ + lpsc_disable(DAVINCI_LPSC_USB11); + lpsc_disable(DAVINCI_LPSC_USB20); + + return 0; +} + +int usb_cpu_init_fail(void) +{ + return usb_cpu_stop(); +} diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index d24f2f1..9f47351 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1261,19 +1261,11 @@ static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, int leni = transfer_len; int len = 0; int stat = 0; - __u32 datab[4]; - union { - void *ptr; - __u8 *u8; - __u16 *u16; - __u32 *u32; - } databuf; __u16 bmRType_bReq; __u16 wValue; __u16 wIndex; __u16 wLength; - - databuf.u32 = (__u32 *)datab; + ALLOC_ALIGN_BUFFER(__u8, databuf, 16, sizeof(u32)); #ifdef DEBUG pkt_print(NULL, dev, pipe, buffer, transfer_len, @@ -1304,20 +1296,20 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, */ case RH_GET_STATUS: - databuf.u16[0] = cpu_to_le16(1); + *(u16 *)databuf = cpu_to_le16(1); OK(2); case RH_GET_STATUS | RH_INTERFACE: - databuf.u16[0] = cpu_to_le16(0); + *(u16 *)databuf = cpu_to_le16(0); OK(2); case RH_GET_STATUS | RH_ENDPOINT: - databuf.u16[0] = cpu_to_le16(0); + *(u16 *)databuf = cpu_to_le16(0); OK(2); case RH_GET_STATUS | RH_CLASS: - databuf.u32[0] = cpu_to_le32( + *(u32 *)databuf = cpu_to_le32( RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE)); OK(4); case RH_GET_STATUS | RH_OTHER | RH_CLASS: - databuf.u32[0] = cpu_to_le32(RD_RH_PORTSTAT); + *(u32 *)databuf = cpu_to_le32(RD_RH_PORTSTAT); OK(4); case RH_CLEAR_FEATURE | RH_ENDPOINT: @@ -1381,14 +1373,14 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, min_t(unsigned int, sizeof(root_hub_dev_des), wLength)); - databuf.ptr = root_hub_dev_des; OK(len); + databuf = root_hub_dev_des; OK(len); case (0x02): /* configuration descriptor */ len = min_t(unsigned int, leni, min_t(unsigned int, sizeof(root_hub_config_des), wLength)); - databuf.ptr = root_hub_config_des; OK(len); + databuf = root_hub_config_des; OK(len); case (0x03): /* string descriptors */ if (wValue == 0x0300) { len = min_t(unsigned int, @@ -1396,7 +1388,7 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, min_t(unsigned int, sizeof(root_hub_str_index0), wLength)); - databuf.ptr = root_hub_str_index0; + databuf = root_hub_str_index0; OK(len); } if (wValue == 0x0301) { @@ -1405,7 +1397,7 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, min_t(unsigned int, sizeof(root_hub_str_index1), wLength)); - databuf.ptr = root_hub_str_index1; + databuf = root_hub_str_index1; OK(len); } default: @@ -1417,40 +1409,40 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, { __u32 temp = roothub_a(&gohci); - databuf.u8[0] = 9; /* min length; */ - databuf.u8[1] = 0x29; - databuf.u8[2] = temp & RH_A_NDP; + databuf[0] = 9; /* min length; */ + databuf[1] = 0x29; + databuf[2] = temp & RH_A_NDP; #ifdef CONFIG_AT91C_PQFP_UHPBUG - databuf.u8[2] = (databuf.u8[2] == 2) ? 1 : 0; + databuf[2] = (databuf[2] == 2) ? 1 : 0; #endif - databuf.u8[3] = 0; + databuf[3] = 0; if (temp & RH_A_PSM) /* per-port power switching? */ - databuf.u8[3] |= 0x1; + databuf[3] |= 0x1; if (temp & RH_A_NOCP) /* no overcurrent reporting? */ - databuf.u8[3] |= 0x10; + databuf[3] |= 0x10; else if (temp & RH_A_OCPM)/* per-port overcurrent reporting? */ - databuf.u8[3] |= 0x8; + databuf[3] |= 0x8; - /* corresponds to databuf.u8[4-7] */ - databuf.u8[1] = 0; - databuf.u8[5] = (temp & RH_A_POTPGT) >> 24; + databuf[4] = 0; + databuf[5] = (temp & RH_A_POTPGT) >> 24; + databuf[6] = 0; temp = roothub_b(&gohci); - databuf.u8[7] = temp & RH_B_DR; - if (databuf.u8[2] < 7) { - databuf.u8[8] = 0xff; + databuf[7] = temp & RH_B_DR; + if (databuf[2] < 7) { + databuf[8] = 0xff; } else { - databuf.u8[0] += 2; - databuf.u8[8] = (temp & RH_B_DR) >> 8; - databuf.u8[10] = databuf.u8[9] = 0xff; + databuf[0] += 2; + databuf[8] = (temp & RH_B_DR) >> 8; + databuf[10] = databuf[9] = 0xff; } len = min_t(unsigned int, leni, - min_t(unsigned int, databuf.u8[0], wLength)); + min_t(unsigned int, databuf[0], wLength)); OK(len); } case RH_GET_CONFIGURATION: - databuf.u8[0] = 0x01; + databuf[0] = 0x01; OK(1); case RH_SET_CONFIGURATION: @@ -1469,8 +1461,8 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, #endif len = min_t(int, len, leni); - if (data != databuf.ptr) - memcpy(data, databuf.ptr, len); + if (data != databuf) + memcpy(data, databuf, len); dev->act_len = len; dev->status = stat; |