diff --git a/arch/arm/src/sama5/sam_ehci.c b/arch/arm/src/sama5/sam_ehci.c index 3f6a0c7f3c..7a281b86e6 100644 --- a/arch/arm/src/sama5/sam_ehci.c +++ b/arch/arm/src/sama5/sam_ehci.c @@ -140,6 +140,8 @@ /* Port numbers */ +#define HPNDX(hp) ((hp)->port) +#define HPORT(hp) (HPNDX(hp)+1) #define RHPNDX(rh) ((rh)->hport.hport.port) #define RHPORT(rh) (RHPNDX(rh)+1) @@ -210,9 +212,15 @@ struct sam_epinfo_s uint32_t xfrd; /* On completion, will hold the number of bytes transferred */ sem_t iocsem; /* Semaphore used to wait for transfer completion */ #ifdef CONFIG_USBHOST_ASYNCH - usbhost_asynch_t callback; /* Transfer complete callback */ - void *arg; /* Argument that accompanies the callback */ + usbhost_asynch_t callback; /* Transfer complete callback */ + void *arg; /* Argument that accompanies the callback */ #endif + + /* These fields are used in the split-transaction protocol. */ + + uint8_t hubaddr; /* USB device address of the high-speed hub below */ + /* which a full/low-speed device is attached. */ + uint8_t hubport; /* The port on the above high-speed hub. */ }; /* This structure retains the state of one root hub port */ @@ -242,7 +250,7 @@ struct sam_ehci_s { volatile bool pscwait; /* TRUE: Thread is waiting for port status change event */ - mutex_t lock; /* Support mutually exclusive access */ + rmutex_t lock; /* Support mutually exclusive access */ sem_t pscsem; /* Semaphore to wait for port status change events */ struct sam_epinfo_s ep0; /* Endpoint 0 */ @@ -437,7 +445,7 @@ static int sam_reset(void); static struct sam_ehci_s g_ehci = { - .lock = NXMUTEX_INITIALIZER, + .lock = NXRMUTEX_INITIALIZER, .pscsem = SEM_INITIALIZER(0), .ep0.iocsem = SEM_INITIALIZER(1), }; @@ -1470,10 +1478,8 @@ static struct sam_qh_s *sam_qh_create(struct sam_rhport_s *rhport, struct sam_epinfo_s *epinfo) { struct sam_qh_s *qh; - uint32_t rhpndx; uint32_t regval; - uint8_t hubaddr; - uint8_t hubport; + uint32_t rl = 0; /* Allocate a new queue head structure */ @@ -1499,15 +1505,22 @@ static struct sam_qh_s *sam_qh_create(struct sam_rhport_s *rhport, * DTC Data toggle control 1 * MAXPKT Max packet size Endpoint structure * C Control endpoint Calculated - * RL NAK count reloaded 8 + * RL NAK count reloaded Depends on speed and + * epinfo->xfrtype */ + if (epinfo->speed == USB_SPEED_HIGH && + epinfo->xfrtype != USB_EP_ATTR_XFER_INT) + { + rl = 8; + } + regval = ((uint32_t)epinfo->devaddr << QH_EPCHAR_DEVADDR_SHIFT) | ((uint32_t)epinfo->epno << QH_EPCHAR_ENDPT_SHIFT) | ((uint32_t)sam_ehci_speed(epinfo->speed) << QH_EPCHAR_EPS_SHIFT) | QH_EPCHAR_DTC | ((uint32_t)epinfo->maxpacket << QH_EPCHAR_MAXPKT_SHIFT) | - ((uint32_t)8 << QH_EPCHAR_RL_SHIFT); + (rl << QH_EPCHAR_RL_SHIFT); /* Paragraph 3.6.3: "Control Endpoint Flag (C). If the QH.EPS field * indicates the endpoint is not a high-speed device, and the endpoint @@ -1530,37 +1543,14 @@ static struct sam_qh_s *sam_qh_create(struct sam_rhport_s *rhport, * FIELD DESCRIPTION VALUE/SOURCE * -------- ------------------------------- -------------------- * SSMASK Interrupt Schedule Mask Depends on epinfo->xfrtype - * SCMASK Split Completion Mask 0 - * HUBADDR Hub Address Always 0 for now - * PORT Port number RH port index + 1 + * SCMASK Split Completion Mask Depends on epinfo->speed + * HUBADDR Hub Address epinfo->hubaddr + * PORT Port number epinfo->hubport * MULT High band width multiplier 1 */ - rhpndx = RHPNDX(rhport); - -#ifdef CONFIG_USBHOST_HUB - /* REVISIT: Future HUB support will require the HUB port number - * and HUB device address to be included here: - * - * - The HUB device address is the USB device address of the USB 2.0 Hub - * below which a full- or low-speed device is attached. - * - The HUB port number is the port number on the above USB 2.0 Hub - * - * These fields are used in the split-transaction protocol. The kludge - * below should work for hubs connected directly to a root hub port, - * but would not work for devices connected to downstream hubs. - */ - -#warning Missing logic - hubaddr = rhport->ep0.devaddr; - hubport = rhpndx + 1; -#else - hubaddr = rhport->ep0.devaddr; - hubport = rhpndx + 1; -#endif - - regval = ((uint32_t)hubaddr << QH_EPCAPS_HUBADDR_SHIFT) | - ((uint32_t)hubport << QH_EPCAPS_PORT_SHIFT) | + regval = ((uint32_t)(epinfo->hubaddr) << QH_EPCAPS_HUBADDR_SHIFT) | + ((uint32_t)(epinfo->hubport) << QH_EPCAPS_PORT_SHIFT) | ((uint32_t)1 << QH_EPCAPS_MULT_SHIFT); #ifndef CONFIG_USBHOST_INT_DISABLE @@ -1579,10 +1569,17 @@ static struct sam_qh_s *sam_qh_create(struct sam_rhport_s *rhport, */ #warning REVISIT - regval |= ((uint32_t)1 << QH_EPCAPS_SSMASK_SHIFT); + regval |= ((uint32_t)(1 << 0) << QH_EPCAPS_SSMASK_SHIFT); } #endif + if (epinfo->speed != USB_SPEED_HIGH) + { + /* Choose micro-frame 2. */ + + regval |= ((uint32_t)(1 << 2) << QH_EPCAPS_SCMASK_SHIFT); + } + qh->hw.epcaps = sam_swap32(regval); /* Mark this as the end of this list. This will be overwritten if/when the @@ -2288,7 +2285,7 @@ static ssize_t sam_transfer_wait(struct sam_epinfo_s *epinfo) * wakes this thread up needs the lock). */ #warning REVISIT - nxmutex_unlock(&g_ehci.lock); + nxrmutex_unlock(&g_ehci.lock); /* Wait for the IOC completion event */ @@ -2298,7 +2295,7 @@ static ssize_t sam_transfer_wait(struct sam_epinfo_s *epinfo) * this upon return. */ - ret2 = nxmutex_lock(&g_ehci.lock); + ret2 = nxrmutex_lock(&g_ehci.lock); if (ret2 < 0) { ret = ret2; @@ -2322,7 +2319,7 @@ static ssize_t sam_transfer_wait(struct sam_epinfo_s *epinfo) } #endif - /* Did sam_ioc_wait() or nxmutex_lock report an error? */ + /* Did sam_ioc_wait() or nxrmutex_lock report an error? */ if (ret < 0) { @@ -3032,7 +3029,7 @@ static void sam_ehci_bottomhalf(void *arg) * real option (other than to reschedule and delay). */ - nxmutex_lock(&g_ehci.lock); + nxrmutex_lock(&g_ehci.lock); /* Handle all unmasked interrupt sources */ @@ -3142,7 +3139,7 @@ static void sam_ehci_bottomhalf(void *arg) /* We are done with the EHCI structures */ - nxmutex_unlock(&g_ehci.lock); + nxrmutex_unlock(&g_ehci.lock); /* Re-enable relevant EHCI interrupts. Interrupts should still be enabled * at the level of the AIC. @@ -3318,7 +3315,7 @@ static int sam_wait(struct usbhost_connection_s *conn, leave_critical_section(flags); usbhost_vtrace2(EHCI_VTRACE2_MONWAKEUP, - connport->port + 1, connport->connected); + HPORT(connport), connport->connected); return OK; } #endif @@ -3443,9 +3440,13 @@ static int sam_rh_enumerate(struct usbhost_connection_s *conn, * repeat." */ +#if defined(CONFIG_SAMA5_OHCI) hport->speed = USB_SPEED_LOW; regval |= EHCI_PORTSC_OWNER; sam_putreg(regval, &HCOR->portsc[rhpndx]); +#else + uerr("EHCI Root hub does not support low speed, use a hub.\n"); +#endif #if 0 /* #ifdef CONFIG_SAMA5_OHCI */ /* Give the port to the OHCI controller. Zero is the reset value for @@ -3566,8 +3567,12 @@ static int sam_rh_enumerate(struct usbhost_connection_s *conn, * repeat." */ +#if defined(CONFIG_SAMA5_OHCI) regval |= EHCI_PORTSC_OWNER; sam_putreg(regval, &HCOR->portsc[rhpndx]); +#else + uerr("EHCI Root hub does not support this device, use a hub.\n"); +#endif #if 0 /* #ifdef CONFIG_SAMA5_OHCI */ /* Give the port to the OHCI controller. Zero is the reset value for @@ -3612,13 +3617,13 @@ static int sam_enumerate(struct usbhost_connection_s *conn, /* Then let the common usbhost_enumerate do the real enumeration. */ - usbhost_vtrace1(EHCI_VTRACE1_CLASSENUM, hport->port); + usbhost_vtrace1(EHCI_VTRACE1_CLASSENUM, HPORT(hport)); ret = usbhost_enumerate(hport, &hport->devclass); if (ret < 0) { /* Failed to enumerate */ - usbhost_trace2(EHCI_TRACE2_CLASSENUM_FAILED, hport->port + 1, -ret); + usbhost_trace2(EHCI_TRACE2_CLASSENUM_FAILED, HPORT(hport), -ret); /* If this is a root hub port, then marking the hub port not connected * will cause sam_wait() to return and we will try the connection @@ -3670,7 +3675,7 @@ static int sam_ep0configure(struct usbhost_driver_s *drvr, /* We must have exclusive access to the EHCI data structures. */ - ret = nxmutex_lock(&g_ehci.lock); + ret = nxrmutex_lock(&g_ehci.lock); if (ret >= 0) { /* Remember the new device address and max packet size */ @@ -3679,7 +3684,7 @@ static int sam_ep0configure(struct usbhost_driver_s *drvr, epinfo->speed = speed; epinfo->maxpacket = maxpacketsize; - nxmutex_unlock(&g_ehci.lock); + nxrmutex_unlock(&g_ehci.lock); } return ret; @@ -3758,6 +3763,32 @@ static int sam_epalloc(struct usbhost_driver_s *drvr, epinfo->speed = hport->speed; nxsem_init(&epinfo->iocsem, 0, 0); +#ifdef CONFIG_USBHOST_HUB + if (hport->speed != USB_SPEED_HIGH) + { + /* A high speed hub exists between this device and the root hub + * otherwise we would not get here. + */ + + struct usbhost_hubport_s *parent = hport->parent; + + for (; parent->speed != USB_SPEED_HIGH; parent = hport->parent) + { + hport = parent; + } + + if (parent->speed == USB_SPEED_HIGH) + { + epinfo->hubport = HPORT(hport); + epinfo->hubaddr = hport->parent->funcaddr; + } + else + { + return -EINVAL; + } + } +#endif + /* Success.. return an opaque reference to the endpoint information * structure instance */ @@ -4034,7 +4065,7 @@ static int sam_ctrlin(struct usbhost_driver_s *drvr, usbhost_ep_t ep0, * structures. */ - ret = nxmutex_lock(&g_ehci.lock); + ret = nxrmutex_lock(&g_ehci.lock); if (ret < 0) { return ret; @@ -4061,13 +4092,13 @@ static int sam_ctrlin(struct usbhost_driver_s *drvr, usbhost_ep_t ep0, /* And wait for the transfer to complete */ nbytes = sam_transfer_wait(ep0info); - nxmutex_unlock(&g_ehci.lock); + nxrmutex_unlock(&g_ehci.lock); return nbytes >= 0 ? OK : (int)nbytes; errout_with_iocwait: ep0info->iocwait = false; errout_with_lock: - nxmutex_unlock(&g_ehci.lock); + nxrmutex_unlock(&g_ehci.lock); return ret; } @@ -4136,7 +4167,7 @@ static ssize_t sam_transfer(struct usbhost_driver_s *drvr, * structures. */ - ret = nxmutex_lock(&g_ehci.lock); + ret = nxrmutex_lock(&g_ehci.lock); if (ret < 0) { return (ssize_t)ret; @@ -4187,13 +4218,13 @@ static ssize_t sam_transfer(struct usbhost_driver_s *drvr, /* Then wait for the transfer to complete */ nbytes = sam_transfer_wait(epinfo); - nxmutex_unlock(&g_ehci.lock); + nxrmutex_unlock(&g_ehci.lock); return nbytes; errout_with_iocwait: epinfo->iocwait = false; errout_with_lock: - nxmutex_unlock(&g_ehci.lock); + nxrmutex_unlock(&g_ehci.lock); return (ssize_t)ret; } @@ -4248,7 +4279,7 @@ static int sam_asynch(struct usbhost_driver_s *drvr, usbhost_ep_t ep, * structures. */ - ret = nxmutex_lock(&g_ehci.lock); + ret = nxrmutex_lock(&g_ehci.lock); if (ret < 0) { return ret; @@ -4297,14 +4328,14 @@ static int sam_asynch(struct usbhost_driver_s *drvr, usbhost_ep_t ep, /* The transfer is in progress */ - nxmutex_unlock(&g_ehci.lock); + nxrmutex_unlock(&g_ehci.lock); return OK; errout_with_callback: epinfo->callback = NULL; epinfo->arg = NULL; errout_with_lock: - nxmutex_unlock(&g_ehci.lock); + nxrmutex_unlock(&g_ehci.lock); return ret; } #endif /* CONFIG_USBHOST_ASYNCH */ @@ -4352,7 +4383,7 @@ static int sam_cancel(struct usbhost_driver_s *drvr, usbhost_ep_t ep) * interrupt level. */ - ret = nxmutex_lock(&g_ehci.lock); + ret = nxrmutex_lock(&g_ehci.lock); if (ret < 0) { return (ssize_t)ret; @@ -4499,7 +4530,7 @@ exit_terminate: #endif errout_with_lock: - nxmutex_unlock(&g_ehci.lock); + nxrmutex_unlock(&g_ehci.lock); return ret; }