summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2013-08-28 13:07:35 -0600
committerGregory Nutt <gnutt@nuttx.org>2013-08-28 13:07:35 -0600
commit1265dc7fd91686617ab99c897d3418f665907c6a (patch)
treefebb987b29cef2c57248f7a91ac17ddc53ab8b04
parentb071bddd64cd63c399e518ba45394984d10bb5a0 (diff)
downloadnuttx-1265dc7fd91686617ab99c897d3418f665907c6a.tar.gz
nuttx-1265dc7fd91686617ab99c897d3418f665907c6a.tar.bz2
nuttx-1265dc7fd91686617ab99c897d3418f665907c6a.zip
SAMA5 EHCI: Implemented (but did not test) interrupt endpoint logic
-rw-r--r--nuttx/ChangeLog2
-rwxr-xr-xnuttx/arch/arm/src/sama5/sam_ehci.c419
-rw-r--r--nuttx/configs/sama5d3x-ek/README.txt20
3 files changed, 338 insertions, 103 deletions
diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog
index ca8f89c9f..83c046af2 100644
--- a/nuttx/ChangeLog
+++ b/nuttx/ChangeLog
@@ -5473,4 +5473,6 @@
determines which memory regions get added to the heap. When
CONFIG_MM_NREGIONS > 1, the logic was adding the ISRAM region to
the heap twice! (2013-6-27).
+ * nuttx/arch/arm/src/sama5/sam_ehci.c: Add (untested) support for
+ interrupt endpoints (2013-8-28).
diff --git a/nuttx/arch/arm/src/sama5/sam_ehci.c b/nuttx/arch/arm/src/sama5/sam_ehci.c
index 45b53d154..447a6e024 100755
--- a/nuttx/arch/arm/src/sama5/sam_ehci.c
+++ b/nuttx/arch/arm/src/sama5/sam_ehci.c
@@ -103,10 +103,7 @@
# undef CONFIG_SAMA5_EHCI_REGDEBUG
#endif
-/* Periodic transfers will be supported later */
-
-#undef CONFIG_USBHOST_INT_DISABLE
-#define CONFIG_USBHOST_INT_DISABLE 1
+/* Isochronous transfers are not currently supported */
#undef CONFIG_USBHOST_ISOC_DISABLE
#define CONFIG_USBHOST_ISOC_DISABLE 1
@@ -297,7 +294,6 @@ static void sam_qtd_free(struct sam_qtd_s *qtd);
static int sam_qh_foreach(struct sam_qh_s *qh, uint32_t **bp,
foreach_qh_t handler, void *arg);
-static int sam_qh_forall(foreach_qh_t handler, void *arg);
static int sam_qtd_foreach(struct sam_qh_s *qh, foreach_qtd_t handler,
void *arg);
static int sam_qtd_discard(struct sam_qtd_s *qtd, uint32_t **bp, void *arg);
@@ -319,20 +315,16 @@ static void sam_qtd_print(struct sam_qtd_s *qtd);
static void sam_qh_print(struct sam_qh_s *qh);
static int sam_qtd_dump(struct sam_qtd_s *qtd, uint32_t **bp, void *arg);
static int sam_qh_dump(struct sam_qh_s *qh, uint32_t **bp, void *arg);
-#if 0 /* not used */
-static int sam_qh_dumpall(void);
-#endif
#else
# define sam_qtd_print(qtd)
# define sam_qh_print(qh)
# define sam_qtd_dump(qtd, bp, arg) OK
# define sam_qh_dump(qh, bp, arg) OK
-# define sam_qh_dumpall() OK
#endif
static int sam_ioc_setup(struct sam_rhport_s *rhport, struct sam_epinfo_s *epinfo);
static int sam_ioc_wait(struct sam_epinfo_s *epinfo);
-static void sam_qh_enqueue(struct sam_qh_s *qh);
+static void sam_qh_enqueue(struct sam_qh_s *qhead, struct sam_qh_s *qh);
static struct sam_qh_s *sam_qh_create(struct sam_rhport_s *rhport,
struct sam_epinfo_s *epinfo);
static int sam_qtd_addbpl(struct sam_qtd_s *qtd, const void *buffer, size_t buflen);
@@ -344,6 +336,10 @@ static struct sam_qtd_s *sam_qtd_statusphase(uint32_t tokenbits);
static ssize_t sam_async_transfer(struct sam_rhport_s *rhport,
struct sam_epinfo_s *epinfo, const struct usb_ctrlreq_s *req,
uint8_t *buffer, size_t buflen);
+#ifndef CONFIG_USBHOST_INT_DISABLE
+static ssize_t sam_intr_transfer(struct sam_rhport_s *rhport,
+ struct sam_epinfo_s *epinfo, uint8_t *buffer, size_t buflen);
+#endif
/* Interrupt Handling **********************************************************/
@@ -408,11 +404,15 @@ static struct sam_qh_s g_asynchead __attribute__ ((aligned(32)));
#ifndef CONFIG_USBHOST_INT_DISABLE
/* The head of the periodic queue */
-static struct sam_qh_s g_perhead __attribute__ ((aligned(32)));
+static struct sam_qh_s g_intrhead __attribute__ ((aligned(32)));
/* The frame list */
+#ifdef CONFIG_SAMA5_EHCI_PREALLOCATE
static uint32_t g_framelist[FRAME_LIST_SIZE] __attribute__ ((aligned(4096)));
+#else
+static uint32_t *g_framelist;
+#endif
#endif
#ifdef CONFIG_SAMA5_EHCI_PREALLOCATE
@@ -952,34 +952,6 @@ static int sam_qh_foreach(struct sam_qh_s *qh, uint32_t **bp, foreach_qh_t handl
}
/*******************************************************************************
- * Name: sam_qh_forall
- *
- * Description:
- * Setup and call sam_qh_foreach to that every element of the asynchronous
- * queue is examined.
- *
- * Assumption: The caller holds the EHCI exclsem
- *
- *******************************************************************************/
-
-static int sam_qh_forall(foreach_qh_t handler, void *arg)
-{
- struct sam_qh_s *qh;
- uint32_t *bp;
-
- /* Set the back pointer to the forward qTD pointer of the asynchronous
- * queue head.
- */
-
- bp = (uint32_t *)&g_asynchead.hw.hlp;
- qh = (struct sam_qh_s *)sam_virtramaddr(sam_swap32(*bp) & QH_HLP_MASK);
-
- /* Then traverse and operate on every QH and qTD in the list */
-
- return sam_qh_foreach(qh, &bp, handler, arg);
-}
-
-/*******************************************************************************
* Name: sam_qtd_foreach
*
* Description:
@@ -1323,26 +1295,6 @@ static int sam_qh_dump(struct sam_qh_s *qh, uint32_t **bp, void *arg)
#endif
/*******************************************************************************
- * Name: sam_qh_dumpall
- *
- * Description:
- * Set the request for the IOC event well BEFORE enabling the transfer (as
- * soon as we are absolutely committed to the to avoid transfer). We do this
- * to minimize race conditions. This logic would have to be expanded if we
- * want to have more than one packet in flight at a time!
- *
- *******************************************************************************/
-
-#ifdef CONFIG_SAMA5_EHCI_REGDEBUG
-#if 0 /* not used */
-static int sam_qh_dumpall(void)
-{
- return sam_qh_forall(sam_qh_dump, NULL);
-}
-#endif
-#endif
-
-/*******************************************************************************
* Name: sam_ioc_setup
*
* Description:
@@ -1416,7 +1368,7 @@ static int sam_ioc_wait(struct sam_epinfo_s *epinfo)
*
*******************************************************************************/
-static void sam_qh_enqueue(struct sam_qh_s *qh)
+static void sam_qh_enqueue(struct sam_qh_s *qhead, struct sam_qh_s *qh)
{
uintptr_t physaddr;
@@ -1434,7 +1386,7 @@ static void sam_qh_enqueue(struct sam_qh_s *qh)
* attached qTDs to RAM.
*/
- qh->hw.hlp = g_asynchead.hw.hlp;
+ qh->hw.hlp = qhead->hw.hlp;
sam_qh_flush(qh);
/* Then set the new QH as the first QH in the asychronous queue and flush the
@@ -1442,9 +1394,9 @@ 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_clean_dcache((uintptr_t)&g_asynchead.hw,
- (uintptr_t)&g_asynchead.hw + sizeof(struct ehci_qh_s));
+ qhead->hw.hlp = sam_swap32(physaddr | QH_HLP_TYP_QH);
+ cp15_clean_dcache((uintptr_t)&qhead->hw,
+ (uintptr_t)&qhead->hw + sizeof(struct ehci_qh_s));
}
/*******************************************************************************
@@ -1515,7 +1467,7 @@ static struct sam_qh_s *sam_qh_create(struct sam_rhport_s *rhport,
*
* FIELD DESCRIPTION VALUE/SOURCE
* -------- ------------------------------- --------------------
- * SSMASK Interrupt Schedule Mask 0
+ * 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
@@ -1525,9 +1477,29 @@ static struct sam_qh_s *sam_qh_create(struct sam_rhport_s *rhport,
* and HUB device address to be included here.
*/
- regval = ((uint32_t)0 << QH_EPCAPS_HUBADDR_SHIFT) |
- ((uint32_t)(rhport->rhpndx + 1) << QH_EPCAPS_PORT_SHIFT) |
- ((uint32_t)1 << QH_EPCAPS_MULT_SHIFT);
+ regval = ((uint32_t)0 << QH_EPCAPS_HUBADDR_SHIFT) |
+ ((uint32_t)(rhport->rhpndx + 1) << QH_EPCAPS_PORT_SHIFT) |
+ ((uint32_t)1 << QH_EPCAPS_MULT_SHIFT);
+
+#ifndef CONFIG_USBHOST_INT_DISABLE
+ if (epinfo->xfrtype == USB_EP_ATTR_XFER_INT)
+ {
+ /* Here, the S-Mask field in the queue head is set to 1, indicating
+ * that the transaction for the endpoint should be executed on the bus
+ * during micro-frame 0 of the frame.
+ *
+ * REVISIT: The polling interval should be controlled by the which
+ * entry is the framelist holds the QH pointer for a given micro-frame
+ * and the QH pointer should be replicated for different polling rates.
+ * This implementation currently just sets all frame_list entry to
+ * all the same interrupt queue. That should work but will not give
+ * any control over polling rates.
+ */
+#warning REVISIT
+
+ regval |= ((uint32_t)1 << QH_EPCAPS_SSMASK_SHIFT);
+ }
+#endif
qh->hw.epcaps = sam_swap32(regval);
@@ -1845,6 +1817,7 @@ static ssize_t sam_async_transfer(struct sam_rhport_s *rhport,
uint32_t *flink;
uint32_t *alt;
uint32_t toggle;
+ uint32_t regval;
int ret;
uvdbg("RHport%d EP%d: buffer=%p, buflen=%d, req=%p\n",
@@ -1931,7 +1904,7 @@ static ssize_t sam_async_transfer(struct sam_rhport_s *rhport,
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
+ * there is no request, an indication to interrupt at the end of this
* transfer.
*/
@@ -2058,9 +2031,200 @@ static ssize_t sam_async_transfer(struct sam_rhport_s *rhport,
}
}
+ /* Disable the asynchronous schedule */
+
+ regval = sam_getreg(&HCOR->usbcmd);
+ regval &= ~EHCI_USBCMD_ASEN;
+ sam_putreg(regval, &HCOR->usbcmd);
+
/* Add the new QH to the head of the asynchronous queue list */
- sam_qh_enqueue(qh);
+ sam_qh_enqueue(&g_asynchead, qh);
+
+ /* Re-enable the asynchronous schedule */
+
+ regval |= EHCI_USBCMD_ASEN;
+ sam_putreg(regval, &HCOR->usbcmd);
+
+ /* Release the EHCI semaphore while we wait. Other threads need the
+ * opportunity to access the EHCI resources while we wait.
+ *
+ * REVISIT: Is this safe? NO. This is a bug and needs rethinking.
+ * We need to lock all of the port-resources (not EHCI common) until
+ * the transfer is complete. But we can't use the common ECHI exclsem
+ * or we will deadlock while waiting (because the working thread that
+ * wakes this thread up needs the exclsem).
+ */
+#warning REVISIT
+ sam_givesem(&g_ehci.exclsem);
+
+ /* Wait for the IOC completion event */
+
+ ret = sam_ioc_wait(epinfo);
+
+ /* Re-aquire the ECHI semaphore. The caller expects to be holding
+ * this upon return.
+ */
+
+ sam_takesem(&g_ehci.exclsem);
+
+ /* Did sam_ioc_wait() report an error? */
+
+ if (ret < 0)
+ {
+ udbg("ERROR: Transfer failed\n");
+ goto errout_with_iocwait;
+ }
+
+ /* Transfer completed successfully. Return the number of bytes transferred */
+
+ return epinfo->xfrd;
+
+ /* Clean-up after an error */
+
+errout_with_qh:
+ sam_qh_discard(qh);
+errout_with_iocwait:
+ epinfo->iocwait = false;
+ return (ssize_t)ret;
+}
+
+/*******************************************************************************
+ * Name: sam_intr_transfer
+ *
+ * Description:
+ * Process a IN or OUT request on any interrupt endpoint by inserting a qTD
+ * into the periodic frame list.
+ *
+ * Paragraph 4.10.7 "Adding Interrupt Queue Heads to the Periodic Schedule"
+ * "The link path(s) from the periodic frame list to a queue head establishes
+ * in which frames a transaction can be executed for the queue head. Queue
+ * heads are linked into the periodic schedule so they are polled at
+ * the appropriate rate. System software sets a bit in a queue head's
+ * S-Mask to indicate which micro-frame with-in a 1 millisecond period a
+ * transaction should be executed for the queue head. Software must ensure
+ * that all queue heads in the periodic schedule have S-Mask set to a non-
+ * zero value. An S-mask with a zero value in the context of the periodic
+ * schedule yields undefined results.
+ *
+ * "If the desired poll rate is greater than one frame, system software can
+ * use a combination of queue head linking and S-Mask values to spread
+ * interrupts of equal poll rates through the schedule so that the
+ * periodic bandwidth is allocated and managed in the most efficient
+ * manner possible."
+ *
+ * Paragraph 4.6 "Periodic Schedule"
+ *
+ * "The periodic schedule is used to manage all isochronous and interrupt
+ * transfer streams. The base of the periodic schedule is the periodic
+ * frame list. Software links schedule data structures to the periodic
+ * frame list to produce a graph of scheduled data structures. The graph
+ * represents an appropriate sequence of transactions on the USB. ...
+ * isochronous transfers (using iTDs and siTDs) with a period of one are
+ * linked directly to the periodic frame list. Interrupt transfers (are
+ * managed with queue heads) and isochronous streams with periods other
+ * than one are linked following the period-one iTD/siTDs. Interrupt
+ * queue heads are linked into the frame list ordered by poll rate.
+ * Longer poll rates are linked first (e.g. closest to the periodic
+ * frame list), followed by shorter poll rates, with queue heads with a
+ * poll rate of one, on the very end."
+ *
+ * Assumption: The caller holds the EHCI exclsem. The caller must be aware
+ * that the EHCI exclsem will released while waiting for the transfer to
+ * complete, but will be re-aquired when before returning. The state of
+ * EHCI resources could be very different upon return.
+ *
+ * Returned value:
+ * On success, this function returns the number of bytes actually transferred.
+ * For control transfers, this size includes the size of the control request
+ * plus the size of the data (which could be short); For bulk transfers, this
+ * will be the number of data bytes transfers (which could be short).
+ *
+ *******************************************************************************/
+
+#ifndef CONFIG_USBHOST_INT_DISABLE
+static ssize_t sam_intr_transfer(struct sam_rhport_s *rhport,
+ struct sam_epinfo_s *epinfo,
+ uint8_t *buffer, size_t buflen)
+{
+ struct sam_qh_s *qh;
+ struct sam_qtd_s *qtd;
+ uintptr_t physaddr;
+ uint32_t tokenbits;
+ uint32_t regval;
+ int ret;
+
+ uvdbg("RHport%d EP%d: buffer=%p, buflen=%d\n",
+ rhport->rhpndx+1, epinfo->epno, buffer, buflen);
+
+ DEBUGASSERT(rhport && epinfo && buffer && buflen > 0);
+
+ /* Set the request for the IOC event well BEFORE enabling the transfer. */
+
+ ret = sam_ioc_setup(rhport, epinfo);
+ if (ret != OK)
+ {
+ udbg("ERROR: Device disconnected\n");
+ return ret;
+ }
+
+ /* Create and initialize a Queue Head (QH) structure for this transfer */
+
+ qh = sam_qh_create(rhport, epinfo);
+ if (qh == NULL)
+ {
+ udbg("ERROR: sam_qh_create failed\n");
+ ret = -ENOMEM;
+ goto errout_with_iocwait;
+ }
+
+ /* Extra TOKEN bits include the data toggle, the data PID, and and indication to interrupt at the end of this
+ * transfer.
+ */
+
+ tokenbits = (uint32_t)epinfo->toggle << QTD_TOKEN_TOGGLE_SHIFT;
+
+ /* Get the data token direction. */
+
+ if (epinfo->dirin)
+ {
+ tokenbits |= (QTD_TOKEN_PID_IN | QTD_TOKEN_IOC);
+ }
+ else
+ {
+ tokenbits |= (QTD_TOKEN_PID_OUT | QTD_TOKEN_IOC);
+ }
+
+ /* Allocate a new Queue Element Transfer Descriptor (qTD) for the data
+ * buffer.
+ */
+
+ qtd = sam_qtd_dataphase(epinfo, buffer, buflen, tokenbits);
+ if (qtd == NULL)
+ {
+ udbg("ERROR: sam_qtd_dataphase failed\n");
+ goto errout_with_qh;
+ }
+
+ /* Link the new qTD to the QH. */
+
+ physaddr = sam_physramaddr((uintptr_t)qtd);
+ qh->hw.overlay.nqp = sam_swap32(physaddr);
+
+ /* Disable the periodic schedule */
+
+ regval = sam_getreg(&HCOR->usbcmd);
+ regval &= ~EHCI_USBCMD_PSEN;
+ sam_putreg(regval, &HCOR->usbcmd);
+
+ /* Add the new QH to the head of the interrupt transfer list */
+
+ sam_qh_enqueue(&g_intrhead, qh);
+
+ /* Re-enable the periodic schedule */
+
+ regval |= EHCI_USBCMD_PSEN;
+ sam_putreg(regval, &HCOR->usbcmd);
/* Release the EHCI semaphore while we wait. Other threads need the
* opportunity to access the EHCI resources while we wait.
@@ -2104,13 +2268,14 @@ errout_with_iocwait:
epinfo->iocwait = false;
return (ssize_t)ret;
}
+#endif
/*******************************************************************************
* EHCI Interrupt Handling
*******************************************************************************/
/*******************************************************************************
- * Name: sam_qh_ioccheck
+ * Name: sam_qtd_ioccheck
*
* Description:
* This function is a sam_qtd_foreach() callback function. It services one
@@ -2217,7 +2382,7 @@ static int sam_qh_ioccheck(struct sam_qh_s *qh, uint32_t **bp, void *arg)
ret = sam_qtd_foreach(qh, sam_qtd_ioccheck, (void *)qh->epinfo);
if (ret < 0)
{
- udbg("ERROR: sam_qh_forall failed: %d\n", ret);
+ udbg("ERROR: sam_qtd_foreach failed: %d\n", ret);
}
/* If there is no longer anything attached to the QH, then remove it from
@@ -2300,20 +2465,61 @@ static int sam_qh_ioccheck(struct sam_qh_s *qh, uint32_t **bp, void *arg)
static inline void sam_ioc_bottomhalf(void)
{
+ struct sam_qh_s *qh;
+ uint32_t *bp;
int ret;
+ /* Check the Asynchronous Queue */
/* Make sure that the head of the asynchronous queue is invalidated */
cp15_invalidate_dcache((uintptr_t)&g_asynchead.hw,
(uintptr_t)&g_asynchead.hw + sizeof(struct ehci_qh_s));
- /* Traverse all elements in the asynchronous queue */
+ /* Set the back pointer to the forward qTD pointer of the asynchronous
+ * queue head.
+ */
- ret = sam_qh_forall(sam_qh_ioccheck, NULL);
- if (ret < 0)
+ bp = (uint32_t *)&g_asynchead.hw.hlp;
+ qh = (struct sam_qh_s *)sam_virtramaddr(sam_swap32(*bp) & QH_HLP_MASK);
+ if (qh)
{
- udbg("ERROR: sam_qh_forall failed: %d\n", ret);
+ /* Then traverse and operate on every QH and qTD in the asynchronous
+ * queue
+ */
+
+ ret = sam_qh_foreach(qh, &bp, sam_qh_ioccheck, NULL);
+ if (ret < 0)
+ {
+ udbg("ERROR: sam_qh_foreach failed: %d\n", ret);
+ }
}
+
+#ifndef CONFIG_USBHOST_INT_DISABLE
+ /* Check the Interrupt Queue */
+ /* Make sure that the head of the interrupt queue is invalidated */
+
+ cp15_invalidate_dcache((uintptr_t)&g_intrhead.hw,
+ (uintptr_t)&g_intrhead.hw + sizeof(struct ehci_qh_s));
+
+ /* Set the back pointer to the forward qTD pointer of the asynchronous
+ * queue head.
+ */
+
+ bp = (uint32_t *)&g_intrhead.hw.hlp;
+ qh = (struct sam_qh_s *)sam_virtramaddr(sam_swap32(*bp) & QH_HLP_MASK);
+ if (qh)
+ {
+ /* Then traverse and operate on every QH and qTD in the asynchronous
+ * queue.
+ */
+
+ ret = sam_qh_foreach(qh, &bp, sam_qh_ioccheck, NULL);
+ if (ret < 0)
+ {
+ udbg("ERROR: sam_qh_foreach failed: %d\n", ret);
+ }
+ }
+#endif
}
/*******************************************************************************
@@ -2959,7 +3165,6 @@ static int sam_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx)
* driver sets the PortOwner bit in the PORTSC register to a one to
* release port ownership to a companion host controller."
*/
-#warning REVISIT
regval = sam_getreg(&HCOR->portsc[rhpndx]);
if ((regval & EHCI_PORTSC_PE) != 0)
@@ -3521,8 +3726,10 @@ static int sam_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
#ifndef CONFIG_USBHOST_INT_DISABLE
case USB_EP_ATTR_XFER_INT:
-# warning "Interrupt endpoint support not emplemented"
+ nbytes = sam_intr_transfer(rhport, epinfo, buffer, buflen);
+ break;
#endif
+
#ifndef CONFIG_USBHOST_ISOC_DISABLE
case USB_EP_ATTR_XFER_ISOC:
# warning "Isochronous endpoint support not emplemented"
@@ -3729,16 +3936,18 @@ FAR struct usbhost_connection_s *sam_ehci_initialize(int controller)
DEBUGASSERT((sizeof(struct sam_qh_s) & 0x1f) == 0);
DEBUGASSERT((sizeof(struct sam_qtd_s) & 0x1f) == 0);
-#ifndef CONFIG_USBHOST_INT_DISABLE
- DEBUGASSERT(((uintptr_t)&g_perhead & 0x1f) == 0);
- DEBUGASSERT(((uintptr_t)g_framelist & 0xfff) == 0);
-#endif
-
#ifdef CONFIG_SAMA5_EHCI_PREALLOCATE
DEBUGASSERT(((uintptr_t)&g_qhpool & 0x1f) == 0);
DEBUGASSERT(((uintptr_t)&g_qtdpool & 0x1f) == 0);
#endif
+#ifndef CONFIG_USBHOST_INT_DISABLE
+ DEBUGASSERT(((uintptr_t)&g_intrhead & 0x1f) == 0);
+#ifdef CONFIG_SAMA5_EHCI_PREALLOCATE
+ DEBUGASSERT(((uintptr_t)g_framelist & 0xfff) == 0);
+#endif
+#endif
+
/* SAMA5 Configuration *******************************************************/
/* For High-speed operations, the user has to perform the following:
*
@@ -3873,6 +4082,20 @@ FAR struct usbhost_connection_s *sam_ehci_initialize(int controller)
}
#endif
+#ifndef CONFIG_SAMA5_EHCI_PREALLOCATE
+ /* Allocate the periodic framelist */
+
+ g_framelist = (uint32_t *)
+ kmemalign(4096, FRAME_LIST_SIZE * sizeof(uint32_t));
+ if (!g_framelist)
+ {
+ udbg("ERROR: Failed to allocate the periodic frame list\n");
+ kfree(g_qhpool);
+ kfree(g_qtdpool);
+ return NULL;
+ }
+#endif
+
/* Initialize the list of free Transfer Descriptor (qTD) structures */
for (i = 0; i < CONFIG_SAMA5_EHCI_NQTDS; i++)
@@ -3979,18 +4202,22 @@ FAR struct usbhost_connection_s *sam_ehci_initialize(int controller)
sam_putreg(sam_swap32(physaddr), &HCOR->asynclistaddr);
#ifndef CONFIG_USBHOST_INT_DISABLE
- /* Initialize the head of the periodic list */
+ /* Initialize the head of the periodic list. Since Isochronous
+ * endpoints are not not yet supported, each element of the
+ * frame list is initialized to point to the Interrupt Queue
+ * Head (g_intrhead).
+ */
- memset(&g_perhead, 0, sizeof(struct sam_qh_s));
- g_perhead.hw.hlp = sam_swap32(QH_HLP_T);
- g_perhead.hw.overlay.nqp = sam_swap32(QH_NQP_T);
- g_perhead.hw.overlay.alt = sam_swap32(QH_NQP_T);
- g_perhead.hw.overlay.token = sam_swap32(QH_TOKEN_HALTED);
- g_perhead.hw.epcaps = sam_swap32(QH_EPCAPS_SSMASK(1));
+ memset(&g_intrhead, 0, sizeof(struct sam_qh_s));
+ g_intrhead.hw.hlp = sam_swap32(QH_HLP_T);
+ g_intrhead.hw.overlay.nqp = sam_swap32(QH_NQP_T);
+ g_intrhead.hw.overlay.alt = sam_swap32(QH_NQP_T);
+ g_intrhead.hw.overlay.token = sam_swap32(QH_TOKEN_HALTED);
+ g_intrhead.hw.epcaps = sam_swap32(QH_EPCAPS_SSMASK(1));
/* Attach the periodic QH to Period Frame List */
- physaddr = sam_physramaddr((uintptr_t)&g_perhead);
+ physaddr = sam_physramaddr((uintptr_t)&g_intrhead);
for (i = 0; i < FRAME_LIST_SIZE; i++)
{
g_framelist[i] = sam_swap32(physaddr) | PFL_TYP_QH;
@@ -3998,15 +4225,18 @@ FAR struct usbhost_connection_s *sam_ehci_initialize(int controller)
/* Set the Periodic Frame List Base Address. */
- cp15_clean_dcache((uintptr_t)&g_perhead.hw,
- (uintptr_t)&g_perhead.hw + sizeof(struct ehci_qh_s));
+ cp15_clean_dcache((uintptr_t)&g_intrhead.hw,
+ (uintptr_t)&g_intrhead.hw + sizeof(struct ehci_qh_s));
cp15_clean_dcache((uintptr_t)g_framelist,
(uintptr_t)g_framelist + FRAME_LIST_SIZE * sizeof(uint32_t));
+ physaddr = sam_physramaddr((uintptr_t)g_framelist);
sam_putreg(sam_swap32(physaddr), &HCOR->periodiclistbase);
#endif
- /* Enable the asynchronous schedule and, possibly set the frame list size */
+ /* Enable the asynchronous schedule and, possibly enable the periodic
+ * schedule and set the frame list size.
+ */
regval = sam_getreg(&HCOR->usbcmd);
regval &= ~(EHCI_USBCMD_HCRESET | EHCI_USBCMD_FLSIZE_MASK |
@@ -4015,6 +4245,7 @@ FAR struct usbhost_connection_s *sam_ehci_initialize(int controller)
regval |= EHCI_USBCMD_ASEN;
#ifndef CONFIG_USBHOST_INT_DISABLE
+ regval |= EHCI_USBCMD_PSEN;
# if FRAME_LIST_SIZE == 1024
regval |= EHCI_USBCMD_FLSIZE_1024;
# elif FRAME_LIST_SIZE == 512
diff --git a/nuttx/configs/sama5d3x-ek/README.txt b/nuttx/configs/sama5d3x-ek/README.txt
index 50b69e541..d1b2f3675 100644
--- a/nuttx/configs/sama5d3x-ek/README.txt
+++ b/nuttx/configs/sama5d3x-ek/README.txt
@@ -1207,6 +1207,8 @@ Configurations
: Defaults for values probably OK for both
Device Drivers
CONFIG_USBHOST=y : Enable USB host support
+ CONFIG_USBHOST_INT_DISABLE=y : Interrupt endpoints not needed
+ CONFIG_USBHOST_ISOC_DISABLE=y : Isochronous endpoints not needed
Device Drivers -> USB Host Driver Support
CONFIG_USBHOST_ISOC_DISABLE=y : Isochronous endpoints not used
@@ -1215,8 +1217,8 @@ Configurations
Library Routines
CONFIG_SCHED_WORKQUEUE : Worker thread support is required
- Application Configuration -> NSH Library
- CONFIG_NSH_ARCHINIT=y : NSH board-initialization
+ Application Configuration -> NSH Library
+ CONFIG_NSH_ARCHINIT=y : NSH board-initialization
STATUS:
2013-7-19: This configuration (as do the others) run at 396MHz.
@@ -1283,13 +1285,13 @@ Configurations
2013-8-20: Added description to add EHCI to the configuration. At
present, however, EHCI is still a work in progress and not ready for
prime time.
- 2013-8-26: EHCI is still non-functional. After days of work, it is
- able to exchange a SETUP transfer or two, but it still does not make
- it through the full enumeration sequence.
- Nor does the hand-off of high speed devices to OHCI work. In this
- case, OHCI gets the port, but the port is reset, lost by OCHI and
- returned to EHCI. EHCI sees the full-speed port and hands it off
- to OHCI and this sequence continues forever.
+ 2013-8-26:
+ The hand-off of full speed devices to OHCI does not work. In this
+ case, OHCI gets the port, but the port is reset, lost by OHCI and
+ returned to EHCI. EHCI sees the full-speed port and hands it off to
+ OHCI and this sequence continues forever.
+ 2013-8-28: EHCI is partially functional. It is able to mount a high-
+ speed USB FLASH drive using the Mass Storage Class (MSC) interface.
ostest:
This configuration directory, performs a simple OS test using