diff --git a/arch/arm/src/sama5/sam_dmac.c b/arch/arm/src/sama5/sam_dmac.c index 07ec999465..41429388e4 100644 --- a/arch/arm/src/sama5/sam_dmac.c +++ b/arch/arm/src/sama5/sam_dmac.c @@ -1439,8 +1439,8 @@ sam_allocdesc(struct sam_dmach_s *dmach, struct dma_linklist_s *prev, * that hardware will be accessing the descriptor via DMA. */ - cp15_coherent_dcache((uintptr_t)desc, - (uintptr_t)desc + sizeof(struct dma_linklist_s)); + cp15_clean_dcache((uintptr_t)desc, + (uintptr_t)desc + sizeof(struct dma_linklist_s)); break; } } @@ -2184,7 +2184,7 @@ int sam_dmatxsetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr, /* Clean caches associated with the DMA memory */ - cp15_coherent_dcache(maddr, maddr + nbytes); + cp15_clean_dcache(maddr, maddr + nbytes); return ret; } @@ -2265,7 +2265,7 @@ int sam_dmarxsetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr, /* Clean caches associated with the DMA memory */ - cp15_coherent_dcache(maddr, maddr + nbytes); + cp15_clean_dcache(maddr, maddr + nbytes); return ret; } diff --git a/arch/arm/src/sama5/sam_ehci.c b/arch/arm/src/sama5/sam_ehci.c index cbc0bb1bd1..56cf2aa9db 100755 --- a/arch/arm/src/sama5/sam_ehci.c +++ b/arch/arm/src/sama5/sam_ehci.c @@ -1162,11 +1162,18 @@ static int sam_qh_invalidate(struct sam_qh_s *qh) static int sam_qtd_flush(struct sam_qtd_s *qtd, uint32_t **bp, void *arg) { /* Flush the D-Cache, i.e., make the contents of the memory match the contents - * of the D-Cache in the specified address range. + * of the D-Cache in the specified address range. If debug is enabled, then + * we will also invalidate the contents of the D-cache so that we can examine + * the modified memory contents in the event of a failure. */ - cp15_coherent_dcache((uintptr_t)&qtd->hw, - (uintptr_t)&qtd->hw + sizeof(struct ehci_qtd_s)); + cp15_clean_dcache((uintptr_t)&qtd->hw, + (uintptr_t)&qtd->hw + sizeof(struct ehci_qtd_s)); +#ifdef CONFIG_DEBUG_USB + cp15_invalidate_dcache((uintptr_t)&qtd->hw, + (uintptr_t)&qtd->hw + sizeof(struct ehci_qtd_s)); +#endif + return OK; } @@ -1180,10 +1187,18 @@ static int sam_qtd_flush(struct sam_qtd_s *qtd, uint32_t **bp, void *arg) static int sam_qh_flush(struct sam_qh_s *qh) { - /* Flush the QH first */ + /* Flush the QH first. Normally this just means writing the contents of the + * D-cache to RAM. If debug is enabled, then we will also invalidate the + * contents of the D-cache so that we can examine the modified memory contents + * in the event of a failure. + */ - cp15_coherent_dcache((uintptr_t)&qh->hw, - (uintptr_t)&qh->hw + sizeof(struct ehci_qh_s)); + cp15_clean_dcache((uintptr_t)&qh->hw, + (uintptr_t)&qh->hw + sizeof(struct ehci_qh_s)); +#ifdef CONFIG_DEBUG_USB + cp15_invalidate_dcache((uintptr_t)&qh->hw, + (uintptr_t)&qh->hw + sizeof(struct ehci_qh_s)); +#endif /* Then flush all of the qTD entries in the queue */ @@ -1412,8 +1427,8 @@ static void sam_qh_enqueue(struct sam_qh_s *qh) physaddr = (uintptr_t)sam_physramaddr((uintptr_t)qh); g_asynchead.hw.hlp = sam_swap32(physaddr | QH_HLP_TYP_QH); - cp15_coherent_dcache((uintptr_t)&g_asynchead.hw, - (uintptr_t)&g_asynchead.hw + sizeof(struct ehci_qh_s)); + cp15_clean_dcache((uintptr_t)&g_asynchead.hw, + (uintptr_t)&g_asynchead.hw + sizeof(struct ehci_qh_s)); } /******************************************************************************* @@ -1529,7 +1544,16 @@ static int sam_qtd_addbpl(struct sam_qtd_s *qtd, const void *buffer, size_t bufl * will be accessed for an OUT DMA. */ - cp15_coherent_dcache((uintptr_t)buffer, (uintptr_t)buffer + buflen); + cp15_clean_dcache((uintptr_t)buffer, (uintptr_t)buffer + buflen); + + /* If debug is enabled, then we will also invalidate the contents of the + * D-cache so that we can examine the modified memory contents in the event + * of a failure. + */ + +#ifdef CONFIG_DEBUG_USB + cp15_invalidate_dcache((uintptr_t)buffer, (uintptr_t)buffer + buflen); +#endif /* Loop, adding the aligned physical addresses of the buffer to the buffer page * list. Only the first entry need not be aligned (because only the first @@ -1803,8 +1827,8 @@ static ssize_t sam_async_transfer(struct sam_rhport_s *rhport, struct sam_qtd_s *qtd; uintptr_t physaddr; uint32_t *flink; + uint32_t *alt; uint32_t toggle; - uint32_t datapid; int ret; uvdbg("RHport%d EP%d: buffer=%p, buflen=%d, req=%p\n", @@ -1834,21 +1858,6 @@ static ssize_t sam_async_transfer(struct sam_rhport_s *rhport, return ret; } - /* Get the data token direction */ - - datapid = QTD_TOKEN_PID_OUT; - if (req) - { - if ((req->type & USB_REQ_DIR_MASK) == USB_REQ_DIR_IN) - { - datapid = QTD_TOKEN_PID_IN; - } - } - else if (epinfo->dirin) - { - datapid = QTD_TOKEN_PID_IN; - } - /* Create and initialize a Queue Head (QH) structure for this transfer */ qh = sam_qh_create(rhport, epinfo); @@ -1867,7 +1876,16 @@ static ssize_t sam_async_transfer(struct sam_rhport_s *rhport, toggle = (uint32_t)epinfo->toggle << QTD_TOKEN_TOGGLE_SHIFT; ret = -EIO; - /* Is the an EP0 SETUP request? If req will be non-NULL */ + /* Is the an EP0 SETUP request? If so, req will be non-NULL and we will + * queue two or three qTDs: + * + * 1) One for the SETUP phase, + * 2) One for the DATA phase (if there is data), and + * 3) One for the STATUS phase. + * + * If this is not an EP0 SETUP request, then only a data transfer will be + * enqueued. + */ if (req != NULL) { @@ -1897,22 +1915,54 @@ static ssize_t sam_async_transfer(struct sam_rhport_s *rhport, * always be present for normal endpoint data transfers. */ + alt = NULL; if (buffer && buflen > 0) { - /* Extra TOKEN bits include the data toggle, the data PID, and if there - * is no request, and indication to interrupt at the end of this + uint32_t tokenbits; + bool dirin; + + /* Extra TOKEN bits include the data toggle, the data PID, and if + * there is no request, and indication to interrupt at the end of this * transfer. */ - uint32_t tokenbits = toggle | datapid; + tokenbits = toggle; - /* If this is not an EP0 SETUP request, then nothing follows the data and - * we want the IOC interrupt when the data transfer completes. + /* Get the data token direction. + * + * If this is a SETUP request, use the direction contained in the + * request. The IOC bit is not set. */ - if (!req) + if (req) { - tokenbits |= QTD_TOKEN_IOC; + if ((req->type & USB_REQ_DIR_MASK) == USB_REQ_DIR_IN) + { + tokenbits |= QTD_TOKEN_PID_IN; + dirin = true; + } + else + { + tokenbits |= QTD_TOKEN_PID_OUT; + dirin = false; + } + } + + /* Otherwise, the endpoint is uni-directional. Get the direction from + * the epinfo structure. Since this is not an EP0 SETUP request, + * nothing follows the data and we want the IOC interrupt when the + * data transfer completes. + */ + + else if (epinfo->dirin) + { + tokenbits |= (QTD_TOKEN_PID_IN | QTD_TOKEN_IOC); + dirin = true; + } + else + { + tokenbits |= (QTD_TOKEN_PID_OUT | QTD_TOKEN_IOC); + dirin = false; } /* Allocate a new Queue Element Transfer Descriptor (qTD) for the data @@ -1934,8 +1984,21 @@ static ssize_t sam_async_transfer(struct sam_rhport_s *rhport, /* Set the forward link pointer to this new qTD */ flink = &qtd->hw.nqp; + + /* If this was an IN transfer, then setup a pointer alternate link. + * The EHCI hardware will use this link if a short packet is received. + */ + + if (dirin) + { + alt = &qtd->hw.alt; + } } + /* If this is an EP0 SETUP request, then enqueue one more qTD for the + * STATUS phase transfer. + */ + if (req != NULL) { /* Extra TOKEN bits include the data toggle and the correct data PID. */ @@ -1974,6 +2037,16 @@ static ssize_t sam_async_transfer(struct sam_rhport_s *rhport, physaddr = sam_physramaddr((uintptr_t)qtd); *flink = sam_swap32(physaddr); + + /* In an IN data qTD was also enqueued, then linke the data qTD's + * alternate pointer to this STATUS phase qTD in order to handle short + * transfers. + */ + + if (alt) + { + *alt = sam_swap32(physaddr); + } } /* Add the new QH to the head of the asynchronous queue list */ @@ -2149,7 +2222,7 @@ static int sam_qh_ioccheck(struct sam_qh_s *qh, uint32_t **bp, void *arg) */ **bp = qh->hw.hlp; - cp15_coherent_dcache((uintptr_t)*bp, (uintptr_t)*bp + sizeof(uint32_t)); + cp15_clean_dcache((uintptr_t)*bp, (uintptr_t)*bp + sizeof(uint32_t)); /* Check for errors, update the data toggle */ @@ -3861,8 +3934,8 @@ FAR struct usbhost_connection_s *sam_ehci_initialize(int controller) g_asynchead.hw.overlay.token = sam_swap32(QH_TOKEN_HALTED); g_asynchead.fqp = sam_swap32(QTD_NQP_T); - cp15_coherent_dcache((uintptr_t)&g_asynchead.hw, - (uintptr_t)&g_asynchead.hw + sizeof(struct ehci_qh_s)); + cp15_clean_dcache((uintptr_t)&g_asynchead.hw, + (uintptr_t)&g_asynchead.hw + sizeof(struct ehci_qh_s)); /* Set the Current Asynchronous List Address. */ @@ -3888,10 +3961,10 @@ FAR struct usbhost_connection_s *sam_ehci_initialize(int controller) /* Set the Periodic Frame List Base Address. */ - cp15_coherent_dcache((uintptr_t)&g_perhead.hw, - (uintptr_t)&g_perhead.hw + sizeof(struct ehci_qh_s)); - cp15_coherent_dcache((uintptr_t)g_framelist, - (uintptr_t)g_framelist + FRAME_LIST_SIZE * sizeof(uint32_t)); + cp15_clean_dcache((uintptr_t)&g_perhead.hw, + (uintptr_t)&g_perhead.hw + sizeof(struct ehci_qh_s)); + cp15_clean_dcache((uintptr_t)g_framelist, + (uintptr_t)g_framelist + FRAME_LIST_SIZE * sizeof(uint32_t)); sam_putreg(sam_swap32(physaddr), &HCOR->periodiclistbase); #endif diff --git a/arch/arm/src/sama5/sam_ohci.c b/arch/arm/src/sama5/sam_ohci.c index a1039d7db6..526859537d 100644 --- a/arch/arm/src/sama5/sam_ohci.c +++ b/arch/arm/src/sama5/sam_ohci.c @@ -791,8 +791,8 @@ static inline int sam_addbulked(struct sam_ed_s *ed) /* Add the new bulk ED to the head of the bulk list */ ed->hw.nexted = sam_getreg(SAM_USBHOST_BULKHEADED); - cp15_coherent_dcache((uintptr_t)ed, - (uintptr_t)ed + sizeof(struct ohci_ed_s) - 1); + cp15_clean_dcache((uintptr_t)ed, + (uintptr_t)ed + sizeof(struct ohci_ed_s) - 1); physed = sam_physramaddr((uintptr_t)ed); sam_putreg((uint32_t)physed, SAM_USBHOST_BULKHEADED); @@ -866,8 +866,8 @@ static inline int sam_rembulked(struct sam_ed_s *ed) */ prev->hw.nexted = ed->hw.nexted; - cp15_coherent_dcache((uintptr_t)prev, - (uintptr_t)prev + sizeof(struct sam_ed_s)); + cp15_clean_dcache((uintptr_t)prev, + (uintptr_t)prev + sizeof(struct sam_ed_s)); } } @@ -951,8 +951,8 @@ static void sam_setinttab(uint32_t value, unsigned int interval, unsigned int of /* Make sure that the modified table value is flushed to RAM */ - cp15_coherent_dcache((uintptr_t)&g_hcca.inttbl[i], - (uintptr_t)&g_hcca.inttbl[i] + sizeof(uint32_t) - 1); + cp15_clean_dcache((uintptr_t)&g_hcca.inttbl[i], + (uintptr_t)&g_hcca.inttbl[i] + sizeof(uint32_t) - 1); } } #endif @@ -1056,8 +1056,8 @@ static inline int sam_addinted(const FAR struct usbhost_epdesc_s *epdesc, */ ed->hw.nexted = physhead; - cp15_coherent_dcache((uintptr_t)ed, - (uintptr_t)ed + sizeof(struct ohci_ed_s) - 1); + cp15_clean_dcache((uintptr_t)ed, + (uintptr_t)ed + sizeof(struct ohci_ed_s) - 1); physed = sam_physramaddr((uintptr_t)ed); sam_setinttab((uint32_t)physed, interval, offset); @@ -1286,8 +1286,8 @@ static int sam_enqueuetd(struct sam_rhport_s *rhport, struct sam_ed_s *ed, /* Skip processing of this ED while we modify the TD list. */ ed->hw.ctrl |= ED_CONTROL_K; - cp15_coherent_dcache((uintptr_t)ed, - (uintptr_t)ed + sizeof(struct ohci_ed_s) - 1); + cp15_clean_dcache((uintptr_t)ed, + (uintptr_t)ed + sizeof(struct ohci_ed_s) - 1); /* Get the tail ED for this root hub port */ @@ -1326,20 +1326,20 @@ static int sam_enqueuetd(struct sam_rhport_s *rhport, struct sam_ed_s *ed, if (buffer && buflen > 0) { - cp15_coherent_dcache((uintptr_t)buffer, - (uintptr_t)buffer + buflen - 1); + cp15_clean_dcache((uintptr_t)buffer, + (uintptr_t)buffer + buflen - 1); } - cp15_coherent_dcache((uintptr_t)tdtail, - (uintptr_t)tdtail + sizeof(struct ohci_gtd_s) - 1); - cp15_coherent_dcache((uintptr_t)td, - (uintptr_t)td + sizeof(struct ohci_gtd_s) - 1); + cp15_clean_dcache((uintptr_t)tdtail, + (uintptr_t)tdtail + sizeof(struct ohci_gtd_s) - 1); + cp15_clean_dcache((uintptr_t)td, + (uintptr_t)td + sizeof(struct ohci_gtd_s) - 1); /* Resume processing of this ED */ ed->hw.ctrl &= ~ED_CONTROL_K; - cp15_coherent_dcache((uintptr_t)ed, - (uintptr_t)ed + sizeof(struct ohci_ed_s) - 1); + cp15_clean_dcache((uintptr_t)ed, + (uintptr_t)ed + sizeof(struct ohci_ed_s) - 1); ret = OK; } @@ -1436,10 +1436,10 @@ static int sam_ep0enqueue(struct sam_rhport_s *rhport) /* Flush the affected control ED and tail TD to RAM */ - cp15_coherent_dcache((uintptr_t)edctrl, - (uintptr_t)edctrl + sizeof(struct ohci_ed_s) - 1); - cp15_coherent_dcache((uintptr_t)tdtail, - (uintptr_t)tdtail + sizeof(struct ohci_gtd_s) - 1); + cp15_clean_dcache((uintptr_t)edctrl, + (uintptr_t)edctrl + sizeof(struct ohci_ed_s) - 1); + cp15_clean_dcache((uintptr_t)tdtail, + (uintptr_t)tdtail + sizeof(struct ohci_gtd_s) - 1); /* ControlListEnable. This bit is set to (re-)enable the processing of the * Control list. Note: once enabled, it remains enabled and we may even @@ -1519,8 +1519,8 @@ static void sam_ep0dequeue(struct sam_rhport_s *rhport) /* Flush the modified ED to RAM */ - cp15_coherent_dcache((uintptr_t)preved, - (uintptr_t)preved + sizeof(struct ohci_ed_s) - 1); + cp15_clean_dcache((uintptr_t)preved, + (uintptr_t)preved + sizeof(struct ohci_ed_s) - 1); } else { @@ -2257,8 +2257,8 @@ static int sam_ep0configure(FAR struct usbhost_driver_s *drvr, uint8_t funcaddr, /* Flush the modified control ED to RAM */ - cp15_coherent_dcache((uintptr_t)edctrl, - (uintptr_t)edctrl + sizeof(struct ohci_ed_s) - 1); + cp15_clean_dcache((uintptr_t)edctrl, + (uintptr_t)edctrl + sizeof(struct ohci_ed_s) - 1); sam_givesem(&g_ohci.exclsem); uvdbg("RHPort%d EP0 CTRL: %08x\n", rhport->rhpndx + 1, edctrl->hw.ctrl); @@ -2428,10 +2428,10 @@ static int sam_epalloc(FAR struct usbhost_driver_s *drvr, /* Make sure these settings are flushed to RAM */ - cp15_coherent_dcache((uintptr_t)ed, - (uintptr_t)ed + sizeof(struct ohci_ed_s) - 1); - cp15_coherent_dcache((uintptr_t)td, - (uintptr_t)td + sizeof(struct ohci_gtd_s) - 1); + cp15_clean_dcache((uintptr_t)ed, + (uintptr_t)ed + sizeof(struct ohci_ed_s) - 1); + cp15_clean_dcache((uintptr_t)td, + (uintptr_t)td + sizeof(struct ohci_gtd_s) - 1); /* Now add the endpoint descriptor to the appropriate list */ diff --git a/drivers/usbhost/usbhost_enumerate.c b/drivers/usbhost/usbhost_enumerate.c index 44e1439cde..bddff4d94b 100644 --- a/drivers/usbhost/usbhost_enumerate.c +++ b/drivers/usbhost/usbhost_enumerate.c @@ -314,6 +314,7 @@ int usbhost_enumerate(FAR struct usbhost_driver_s *drvr, uint8_t funcaddr, size_t maxlen; unsigned int cfglen; uint8_t maxpacketsize; + uint8_t descsize; uint8_t *buffer; int ret; @@ -364,25 +365,27 @@ int usbhost_enumerate(FAR struct usbhost_driver_s *drvr, uint8_t funcaddr, /* For high-speed, we must use 64 bytes */ maxpacketsize = 64; + descsize = USB_SIZEOF_DEVDESC; } else { /* Eight will work for both low- and full-speed */ maxpacketsize = 8; + descsize = 8; } /* Set the initial maximum packet size */ DRVR_EP0CONFIGURE(drvr, 0, maxpacketsize); - /* Read first 'maxpacketsize' bytes of the device descriptor */ + /* Read first 8 bytes of the device descriptor */ ctrlreq->type = USB_REQ_DIR_IN|USB_REQ_RECIPIENT_DEVICE; ctrlreq->req = USB_REQ_GETDESCRIPTOR; usbhost_putle16(ctrlreq->value, (USB_DESC_TYPE_DEVICE << 8)); usbhost_putle16(ctrlreq->index, 0); - usbhost_putle16(ctrlreq->len, maxpacketsize); + usbhost_putle16(ctrlreq->len, descsize); ret = DRVR_CTRLIN(drvr, ctrlreq, buffer); if (ret != OK) @@ -400,19 +403,23 @@ int usbhost_enumerate(FAR struct usbhost_driver_s *drvr, uint8_t funcaddr, DRVR_EP0CONFIGURE(drvr, 0, maxpacketsize); - /* Now read the full device descriptor */ + /* Now read the full device descriptor (if we have not already done so) */ - ctrlreq->type = USB_REQ_DIR_IN|USB_REQ_RECIPIENT_DEVICE; - ctrlreq->req = USB_REQ_GETDESCRIPTOR; - usbhost_putle16(ctrlreq->value, (USB_DESC_TYPE_DEVICE << 8)); - usbhost_putle16(ctrlreq->index, 0); - usbhost_putle16(ctrlreq->len, USB_SIZEOF_DEVDESC); - - ret = DRVR_CTRLIN(drvr, ctrlreq, buffer); - if (ret != OK) + if (descsize < USB_SIZEOF_DEVDESC) { - udbg("ERROR: GETDESCRIPTOR/DEVICE, DRVR_CTRLIN returned %d\n", ret); - goto errout; + ctrlreq->type = USB_REQ_DIR_IN|USB_REQ_RECIPIENT_DEVICE; + ctrlreq->req = USB_REQ_GETDESCRIPTOR; + usbhost_putle16(ctrlreq->value, (USB_DESC_TYPE_DEVICE << 8)); + usbhost_putle16(ctrlreq->index, 0); + usbhost_putle16(ctrlreq->len, USB_SIZEOF_DEVDESC); + + ret = DRVR_CTRLIN(drvr, ctrlreq, buffer); + if (ret != OK) + { + udbg("ERROR: GETDESCRIPTOR/DEVICE, DRVR_CTRLIN returned %d\n", + ret); + goto errout; + } } /* Get class identification information from the device descriptor. Most