diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2015-10-07 08:23:51 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2015-10-07 08:23:51 +0200 |
commit | 54bf38665024f797518726d0ecf612c3cf50e4d9 (patch) | |
tree | d9ddecce8162b28f61eab6242c54bbc74a4476fd /drivers/usb | |
parent | dd4da6d328ebb6e871a5584b3864d8c5cc64af86 (diff) | |
parent | d0a5ea479e6d3d89ea3ce0586de7eb51c9c39131 (diff) | |
download | barebox-54bf38665024f797518726d0ecf612c3cf50e4d9.tar.gz barebox-54bf38665024f797518726d0ecf612c3cf50e4d9.tar.xz |
Merge branch 'for-next/usb'
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/core/usb.c | 10 | ||||
-rw-r--r-- | drivers/usb/gadget/fsl_udc.c | 13 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 474 | ||||
-rw-r--r-- | drivers/usb/host/ehci.h | 15 | ||||
-rw-r--r-- | drivers/usb/imx/chipidea-imx.c | 7 |
5 files changed, 500 insertions, 19 deletions
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 9073fff4be..a3fb1e8808 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -191,7 +191,7 @@ static int usb_parse_config(struct usb_device *dev, unsigned char *buffer, int c return -1; } memcpy(&dev->config, buffer, buffer[0]); - le16_to_cpus(&(dev->config.wTotalLength)); + le16_to_cpus(&(dev->config.desc.wTotalLength)); dev->config.no_of_if = 0; index = dev->config.desc.bLength; @@ -445,9 +445,9 @@ int usb_new_device(struct usb_device *dev) dev_add_param_fixed(&dev->dev, "Product", dev->prod); dev_add_param_fixed(&dev->dev, "SerialNumber", dev->serial); dev_add_param_int_ro(&dev->dev, "idVendor", - le16_to_cpu(dev->descriptor->idVendor), "%04x"); + dev->descriptor->idVendor, "%04x"); dev_add_param_int_ro(&dev->dev, "idProduct", - le16_to_cpu(dev->descriptor->idProduct), "%04x"); + dev->descriptor->idProduct, "%04x"); list_add_tail(&dev->list, &usb_device_list); dev_count++; @@ -938,11 +938,11 @@ int usb_driver_register(struct usb_driver *drv) static int usb_match_device(struct usb_device *dev, const struct usb_device_id *id) { if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) && - id->idVendor != le16_to_cpu(dev->descriptor->idVendor)) + id->idVendor != dev->descriptor->idVendor) return 0; if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) && - id->idProduct != le16_to_cpu(dev->descriptor->idProduct)) + id->idProduct != dev->descriptor->idProduct) return 0; return 1; diff --git a/drivers/usb/gadget/fsl_udc.c b/drivers/usb/gadget/fsl_udc.c index 19f3da65bb..9b59669773 100644 --- a/drivers/usb/gadget/fsl_udc.c +++ b/drivers/usb/gadget/fsl_udc.c @@ -2293,6 +2293,13 @@ err_out: return ret; } +void ci_udc_unregister(void) +{ + if (udc_controller) + usb_del_gadget_udc(&udc_controller->gadget); + +} + static int fsl_udc_probe(struct device_d *dev) { void __iomem *regs = dev_request_mem_region(dev, 0); @@ -2303,8 +2310,14 @@ static int fsl_udc_probe(struct device_d *dev) return ci_udc_register(dev, regs); } +static void fsl_udc_remove(struct device_d *dev) +{ + ci_udc_unregister(); +} + static struct driver_d fsl_udc_driver = { .name = "fsl-udc", .probe = fsl_udc_probe, + .remove = fsl_udc_remove, }; device_platform_driver(fsl_udc_driver); diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 25e7a3e520..b43b6c7f51 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -48,6 +48,21 @@ struct ehci_priv { int (*init)(void *drvdata); int (*post_init)(void *drvdata); void *drvdata; + int periodic_schedules; + struct QH *periodic_queue; + uint32_t *periodic_list; + int sem; + struct device_d *usedby; +}; + +struct int_queue { + int elementsize; + int queuesize; + unsigned long pipe; + struct QH *first; + struct QH *current; + struct QH *last; + struct qTD *tds; }; #define to_ehci(ptr) container_of(ptr, struct ehci_priv, host) @@ -123,6 +138,20 @@ static struct descriptor { #define ehci_is_TDI() (ehci->flags & EHCI_HAS_TT) +static void inline ehci_reentrance_detect(struct ehci_priv *ehci, + struct usb_device *dev, + const char *fname) +{ + if (ehci->sem) + dev_err(&dev->dev, "%s: re-entrance %d (%s:%s)\n", + fname, + ehci->sem, + ehci->usedby->driver->name, + ehci->usedby->name); + ehci->sem++; + ehci->usedby = &dev->dev; +} + static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec) { uint32_t result; @@ -137,7 +166,7 @@ static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec) result &= mask; if (result == done) return 0; - if (is_timeout(start, usec * USECOND)) + if (is_timeout_non_interruptible(start, usec * USECOND)) return -ETIMEDOUT; } } @@ -356,7 +385,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, vtd = td; do { token = hc32_to_cpu(vtd->qt_token); - if (is_timeout(start, timeout_val)) { + if (is_timeout_non_interruptible(start, timeout_val)) { /* Disable async schedule. */ cmd = ehci_readl(&ehci->hcor->or_usbcmd); cmd &= ~CMD_ASE; @@ -659,7 +688,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, * root */ ehci_powerup_fixup(ehci); - mdelay(50); + mdelay_non_interruptible(50); ehci->portreset |= 1 << port; /* terminate the reset */ ehci_writel(status_reg, reg & ~EHCI_PS_PR); @@ -722,7 +751,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, goto unknown; } - mdelay(1); + mdelay_non_interruptible(1); len = min3(srclen, (int)le16_to_cpu(req->length), length); if (srcptr != NULL && len > 0) memcpy(buffer, srcptr, len); @@ -768,6 +797,8 @@ static int ehci_init(struct usb_host *host) uint32_t reg; uint32_t cmd; int ret = 0; + struct QH *periodic; + int i; ehci_halt(ehci); @@ -793,6 +824,44 @@ static int ehci_init(struct usb_host *host) /* Set async. queue head pointer. */ ehci_writel(&ehci->hcor->or_asynclistaddr, (uint32_t)ehci->qh_list); + /* + * Set up periodic list + * Step 1: Parent QH for all periodic transfers. + */ + ehci->periodic_schedules = 0; + periodic = ehci->periodic_queue; + memset(periodic, 0, sizeof(*periodic)); + periodic->qh_link = cpu_to_hc32(QH_LINK_TERMINATE); + periodic->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + periodic->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + + /* + * Step 2: Setup frame-list: Every microframe, USB tries the same list. + * In particular, device specifications on polling frequency + * are disregarded. Keyboards seem to send NAK/NYet reliably + * when polled with an empty buffer. + * + * Split Transactions will be spread across microframes using + * S-mask and C-mask. + */ + if (ehci->periodic_list == NULL) + /* + * FIXME: this memory chunk have to be 4k aligned AND + * reside in coherent memory. Current implementation of + * dma_alloc_coherent() allocates PAGE_SIZE aligned memory chunks. + * PAGE_SIZE less then 4k will break this code. + */ + ehci->periodic_list = dma_alloc_coherent(1024 * 4, + DMA_ADDRESS_BROKEN); + for (i = 0; i < 1024; i++) { + ehci->periodic_list[i] = cpu_to_hc32((unsigned long)periodic + | QH_LINK_TYPE_QH); + } + + /* Set periodic list base address */ + ehci_writel(&ehci->hcor->or_periodiclistbase, + (unsigned long)ehci->periodic_list); + reg = ehci_readl(&ehci->hccr->cr_hcsparams); descriptor.hub.bNbrPorts = HCS_N_PORTS(reg); @@ -819,7 +888,7 @@ static int ehci_init(struct usb_host *host) ehci_writel(&ehci->hcor->or_configflag, cmd); /* unblock posted write */ cmd = ehci_readl(&ehci->hcor->or_usbcmd); - mdelay(5); + mdelay_non_interruptible(5); ehci->rootdev = 0; @@ -835,12 +904,18 @@ submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, { struct usb_host *host = dev->host; struct ehci_priv *ehci = to_ehci(host); + int ret; + + ehci_reentrance_detect(ehci, dev, __func__); if (usb_pipetype(pipe) != PIPE_BULK) { dev_dbg(ehci->dev, "non-bulk pipe (type=%lu)", usb_pipetype(pipe)); + ehci->sem--; return -1; } - return ehci_submit_async(dev, pipe, buffer, length, NULL); + ret = ehci_submit_async(dev, pipe, buffer, length, NULL); + ehci->sem--; + return ret; } static int @@ -849,6 +924,9 @@ submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, { struct usb_host *host = dev->host; struct ehci_priv *ehci = to_ehci(host); + int ret; + + ehci_reentrance_detect(ehci, dev, __func__); if (usb_pipetype(pipe) != PIPE_CONTROL) { dev_dbg(ehci->dev, "non-control pipe (type=%lu)", usb_pipetype(pipe)); @@ -858,9 +936,322 @@ submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, if (usb_pipedevice(pipe) == ehci->rootdev) { if (ehci->rootdev == 0) dev->speed = USB_SPEED_HIGH; - return ehci_submit_root(dev, pipe, buffer, length, setup); + ret = ehci_submit_root(dev, pipe, buffer, length, setup); + ehci->sem--; + return ret; } - return ehci_submit_async(dev, pipe, buffer, length, setup); + ret = ehci_submit_async(dev, pipe, buffer, length, setup); + ehci->sem--; + return ret; +} + +static int +disable_periodic(struct ehci_priv *ehci) +{ + uint32_t cmd; + struct ehci_hcor *hcor = ehci->hcor; + int ret; + + cmd = ehci_readl(&hcor->or_usbcmd); + cmd &= ~CMD_PSE; + ehci_writel(&hcor->or_usbcmd, cmd); + + ret = handshake((uint32_t *)&hcor->or_usbsts, + STS_PSS, 0, 100 * 1000); + if (ret < 0) { + printf("EHCI failed: timeout when disabling periodic list\n"); + return -ETIMEDOUT; + } + return 0; +} + +#define NEXT_QH(qh) (struct QH *)((unsigned long)hc32_to_cpu((qh)->qh_link) & ~0x1f) + +static int +enable_periodic(struct ehci_priv *ehci) +{ + uint32_t cmd; + struct ehci_hcor *hcor = ehci->hcor; + int ret; + + cmd = ehci_readl(&hcor->or_usbcmd); + cmd |= CMD_PSE; + ehci_writel(&hcor->or_usbcmd, cmd); + ret = handshake((uint32_t *)&hcor->or_usbsts, + STS_PSS, STS_PSS, 100 * 1000); + if (ret < 0) { + printf("EHCI failed: timeout when enabling periodic list\n"); + return -ETIMEDOUT; + } + mdelay_non_interruptible(1); + return 0; +} + +static inline u8 ehci_encode_speed(enum usb_device_speed speed) +{ + #define QH_HIGH_SPEED 2 + #define QH_FULL_SPEED 0 + #define QH_LOW_SPEED 1 + if (speed == USB_SPEED_HIGH) + return QH_HIGH_SPEED; + if (speed == USB_SPEED_LOW) + return QH_LOW_SPEED; + return QH_FULL_SPEED; +} + +static void ehci_update_endpt2_dev_n_port(struct usb_device *udev, + struct QH *qh) +{ + struct usb_device *ttdev; + int parent_devnum; + + if (udev->speed != USB_SPEED_LOW && udev->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 = udev; + while (ttdev->parent && ttdev->parent->speed != USB_SPEED_HIGH) + ttdev = ttdev->parent; + if (!ttdev->parent) + return; + parent_devnum = ttdev->parent->devnum; + + qh->qh_endpt2 |= cpu_to_hc32(QH_ENDPT2_PORTNUM(ttdev->portnr) | + QH_ENDPT2_HUBADDR(parent_devnum)); +} + +static struct int_queue *ehci_create_int_queue(struct usb_device *dev, + unsigned long pipe, int queuesize, int elementsize, + void *buffer, int interval) +{ + struct usb_host *host = dev->host; + struct ehci_priv *ehci = to_ehci(host); + struct int_queue *result = NULL; + uint32_t i, toggle; + struct QH *list = ehci->periodic_queue; + + /* + * 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)) { + dev_err(&dev->dev, + "%s: xfers requiring several transactions are not supported.\n", + __func__); + return NULL; + } + + debug("Enter create_int_queue\n"); + if (usb_pipetype(pipe) != PIPE_INTERRUPT) { + dev_dbg(&dev->dev, + "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) { + dev_dbg(&dev->dev, + "too large elements for interrupt transfers\n"); + return NULL; + } + + result = xzalloc(sizeof(*result)); + result->elementsize = elementsize; + result->queuesize = queuesize; + result->pipe = pipe; + result->first = dma_alloc_coherent(sizeof(struct QH) * queuesize, + DMA_ADDRESS_BROKEN); + result->current = result->first; + result->last = result->first + queuesize - 1; + result->tds = dma_alloc_coherent(sizeof(struct qTD) * queuesize, + DMA_ADDRESS_BROKEN); + 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 < 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->qt_next = cpu_to_hc32((unsigned long)td); + qh->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); + dev_dbg(&dev->dev, + "communication direction is '%s'\n", + usb_pipein(pipe) ? "in" : "out"); + td->qt_token = cpu_to_hc32( + QT_TOKEN_DT(toggle) | + (elementsize << 16) | + ((usb_pipein(pipe) ? 1 : 0) << 8) | /* IN/OUT token */ + 0x80); /* active */ + 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); + + *buf = buffer + i * elementsize; + toggle ^= 1; + } + + if (ehci->periodic_schedules > 0) { + if (disable_periodic(ehci) < 0) { + dev_err(&dev->dev, + "FATAL: periodic should never fail, but did"); + goto fail3; + } + } + + /* hook up to periodic list */ + result->last->qh_link = list->qh_link; + list->qh_link = cpu_to_hc32((unsigned long)result->first | QH_LINK_TYPE_QH); + + if (enable_periodic(ehci) < 0) { + dev_err(&dev->dev, + "FATAL: periodic should never fail, but did"); + goto fail3; + } + ehci->periodic_schedules++; + + dev_dbg(&dev->dev, "Exit create_int_queue\n"); + return result; +fail3: + dma_free_coherent(result->tds, 0, sizeof(struct qTD) * queuesize); + dma_free_coherent(result->first, 0, sizeof(struct QH) * queuesize); + free(result); + return NULL; +} + +static void *ehci_poll_int_queue(struct usb_device *dev, + struct int_queue *queue) +{ + struct QH *cur = queue->current; + struct qTD *cur_td; + uint32_t token, toggle; + unsigned long pipe = queue->pipe; + + /* depleted queue */ + if (cur == NULL) { + dev_dbg(&dev->dev, "Exit poll_int_queue with completed queue\n"); + return NULL; + } + /* still active */ + cur_td = &queue->tds[queue->current - queue->first]; + token = hc32_to_cpu(cur_td->qt_token); + if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE) { + dev_dbg(&dev->dev, + "Exit poll_int_queue with no completed intr transfer. token is %x\n", + token); + return NULL; + } + + toggle = QT_TOKEN_GET_DT(token); + usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), toggle); + + if (!(cur->qh_link & QH_LINK_TERMINATE)) + queue->current++; + else + queue->current = NULL; + + dev_dbg(&dev->dev, + "Exit poll_int_queue with completed intr transfer. token is %x at %p (first at %p)\n", + token, cur, queue->first); + return cur->buffer; +} + +static int ehci_destroy_int_queue(struct usb_device *dev, + struct int_queue *queue) +{ + int result = -EINVAL; + struct usb_host *host = dev->host; + struct ehci_priv *ehci = to_ehci(host); + struct QH *cur = ehci->periodic_queue; + uint64_t start; + + if (disable_periodic(ehci) < 0) { + dev_err(&dev->dev, + "FATAL: periodic should never fail, but did\n"); + goto out; + } + ehci->periodic_schedules--; + + start = get_time_ns(); + while (!(cur->qh_link & cpu_to_hc32(QH_LINK_TERMINATE))) { + dev_dbg(&dev->dev, + "considering %p, with qh_link %x\n", + cur, cur->qh_link); + if (NEXT_QH(cur) == queue->first) { + dev_dbg(&dev->dev, + "found candidate. removing from chain\n"); + cur->qh_link = queue->last->qh_link; + result = 0; + break; + } + cur = NEXT_QH(cur); + if (is_timeout_non_interruptible(start, 500 * MSECOND)) { + dev_err(&dev->dev, + "Timeout destroying interrupt endpoint queue\n"); + result = -ETIMEDOUT; + goto out; + } + } + + if (ehci->periodic_schedules > 0) { + result = enable_periodic(ehci); + if (result < 0) + dev_err(&dev->dev, + "FATAL: periodic should never fail, but did"); + } + +out: + dma_free_coherent(queue->tds, 0, sizeof(struct qTD) * queue->queuesize); + dma_free_coherent(queue->first, 0, sizeof(struct QH) * queue->queuesize); + free(queue); + return result; } static int @@ -869,10 +1260,51 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, { struct usb_host *host = dev->host; struct ehci_priv *ehci = to_ehci(host); + struct int_queue *queue; + uint64_t start; + void *backbuffer; + int result = 0, ret; + + ehci_reentrance_detect(ehci, dev, __func__); dev_dbg(ehci->dev, "dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d", dev, pipe, buffer, length, interval); - return -1; + + dma_sync_single_for_device((unsigned long)buffer, length, + DMA_BIDIRECTIONAL); + + queue = ehci_create_int_queue(dev, pipe, 1, length, buffer, interval); + if (!queue) { + ehci->sem--; + return -EINVAL; + } + + start = get_time_ns(); + while ((backbuffer = ehci_poll_int_queue(dev, queue)) == NULL) + if (is_timeout_non_interruptible(start, + USB_CNTL_TIMEOUT * MSECOND)) { + dev_err(&dev->dev, + "Timeout poll on interrupt endpoint\n"); + result = -ETIMEDOUT; + break; + } + + if (backbuffer != buffer) { + dev_err(&dev->dev, + "got wrong buffer back (%p instead of %p)\n", + backbuffer, buffer); + if (!result) + result = -EINVAL; + } + + dma_sync_single_for_cpu((unsigned long)buffer, length, + DMA_BIDIRECTIONAL); + + ret = ehci_destroy_int_queue(dev, queue); + if (!result) + result = ret; + ehci->sem--; + return result; } static int ehci_detect(struct device_d *dev) @@ -907,6 +1339,8 @@ int ehci_register(struct device_d *dev, struct ehci_data *data) ehci->qh_list = dma_alloc_coherent(sizeof(struct QH) * NUM_TD, DMA_ADDRESS_BROKEN); + ehci->periodic_queue = dma_alloc_coherent(sizeof(struct QH), + DMA_ADDRESS_BROKEN); ehci->td = dma_alloc_coherent(sizeof(struct qTD) * NUM_TD, DMA_ADDRESS_BROKEN); @@ -934,13 +1368,18 @@ static int ehci_probe(struct device_d *dev) { struct ehci_data data = {}; struct ehci_platform_data *pdata = dev->platform_data; + struct device_node *dn = dev->device_node; - /* default to EHCI_HAS_TT to not change behaviour of boards - * without platform_data - */ if (pdata) data.flags = pdata->flags; - else + else if (dn) { + data.flags = 0; + if (of_property_read_bool(dn, "has-transaction-translator")) + data.flags |= EHCI_HAS_TT; + } else + /* default to EHCI_HAS_TT to not change behaviour of boards + * without platform_data + */ data.flags = EHCI_HAS_TT; data.hccr = dev_request_mem_region(dev, 0); @@ -961,9 +1400,18 @@ static void ehci_remove(struct device_d *dev) ehci_halt(ehci); } +static __maybe_unused struct of_device_id ehci_platform_dt_ids[] = { + { + .compatible = "generic-ehci", + }, { + /* sentinel */ + } +}; + static struct driver_d ehci_driver = { .name = "ehci", .probe = ehci_probe, .remove = ehci_remove, + .of_compatible = DRV_OF_COMPAT(ehci_platform_dt_ids), }; device_platform_driver(ehci_driver); diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index d71d0565e8..39de763749 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -20,6 +20,15 @@ #include <io.h> +#define QH_ENDPT1_EPS(x) (((x) & 0x3) << 12) /* Endpoint Speed */ +#define QH_ENDPT2_PORTNUM(x) (((x) & 0x7f) << 23) /* Port Number */ +#define QH_ENDPT2_HUBADDR(x) (((x) & 0x7f) << 16) /* Hub Address */ + +#define QT_TOKEN_DT(x) (((x) & 0x1) << 31) /* Data Toggle */ +#define QT_TOKEN_GET_STATUS(x) (((x) >> 0) & 0xff) +#define QT_TOKEN_STATUS_ACTIVE 0x80 +#define QT_TOKEN_GET_DT(x) (((x) >> 31) & 0x1) + #if !defined(CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) #define CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS 16 #endif @@ -51,6 +60,7 @@ struct ehci_hcor { #define CMD_RUN (1 << 0) /* start/stop HC */ uint32_t or_usbsts; #define STD_ASS (1 << 15) +#define STS_PSS (1 << 14) #define STS_HALT (1 << 12) uint32_t or_usbintr; uint32_t or_frindex; @@ -148,7 +158,10 @@ struct QH { * Add dummy fill value to make the size of this struct * aligned to 32 bytes */ - uint8_t fill[16]; + union { + uint8_t fill[16]; + void* buffer; + }; }; #endif /* USB_EHCI_H */ diff --git a/drivers/usb/imx/chipidea-imx.c b/drivers/usb/imx/chipidea-imx.c index 28a0fff328..ae307183bc 100644 --- a/drivers/usb/imx/chipidea-imx.c +++ b/drivers/usb/imx/chipidea-imx.c @@ -276,6 +276,12 @@ static int imx_chipidea_probe(struct device_d *dev) return ret; }; +static void imx_chipidea_remove(struct device_d *dev) +{ + if (IS_ENABLED(CONFIG_USB_GADGET_DRIVER_ARC)) + ci_udc_unregister(); +} + static __maybe_unused struct of_device_id imx_chipidea_dt_ids[] = { { .compatible = "fsl,imx27-usb", @@ -288,5 +294,6 @@ static struct driver_d imx_chipidea_driver = { .name = "imx-usb", .probe = imx_chipidea_probe, .of_compatible = DRV_OF_COMPAT(imx_chipidea_dt_ids), + .remove = imx_chipidea_remove, }; device_platform_driver(imx_chipidea_driver); |