Add SAMA5 EHCI list traversal logic

This commit is contained in:
Gregory Nutt 2013-08-20 17:01:30 -06:00
parent 320a2e2a0a
commit 0f2ce573e4

View File

@ -55,6 +55,7 @@
#include "up_arch.h"
#include "sam_periphclks.h"
#include "sam_memories.h"
#include "sam_usbhost.h"
#include "chip/sam_ehci.h"
@ -128,6 +129,11 @@ struct sam_list_s
/* Variable length entry data follows */
};
/* List traversal callout functions */
typedef int (*foreach_qh_t)(struct sam_qh_s *qh, uint32_t **bp, void *arg);
typedef int (*foreach_qtd_t)(struct sam_qtd_s *qtd, uint32_t **bp, void *arg);
/* This structure describes one endpoint. */
struct sam_epinfo_s
@ -220,10 +226,19 @@ static void sam_takesem(sem_t *sem);
/* Allocators ******************************************************************/
static struct sam_qh_s *sam_qhalloc(void);
static void sam_qhfree(struct sam_qh_s *qh);
static struct sam_qtd_s *sam_qtdalloc(void);
static void sam_qtdfree(struct sam_qtd_s *qtd);
static struct sam_qh_s *sam_qh_alloc(void);
static void sam_qh_free(struct sam_qh_s *qh);
static struct sam_qtd_s *sam_qtd_alloc(void);
static void sam_qtd_free(struct sam_qtd_s *qtd);
/* List Management *************************************************************/
static int sam_qh_foreach(struct sam_qh_s *qh, uint32_t *bp,
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);
static int sam_qh_discard(struct sam_qh_s *qh);
/* Interrupt Handling **********************************************************/
@ -597,14 +612,14 @@ static void sam_takesem(sem_t *sem)
* Allocators
*******************************************************************************/
/*******************************************************************************
* Name: sam_qhalloc
* Name: sam_qh_alloc
*
* Description:
* Allocate a Queue Head (QH) structure by removing it from the free list
*
*******************************************************************************/
static struct sam_qh_s *sam_qhalloc(void)
static struct sam_qh_s *sam_qh_alloc(void)
{
struct sam_qh_s *qh;
@ -621,14 +636,14 @@ static struct sam_qh_s *sam_qhalloc(void)
}
/*******************************************************************************
* Name: sam_qhfree
* Name: sam_qh_free
*
* Description:
* Free a Queue Head (QH) structure by returning it to the free list
*
*******************************************************************************/
static void sam_qhfree(struct sam_qh_s *qh)
static void sam_qh_free(struct sam_qh_s *qh)
{
struct sam_list_s *entry = (struct sam_list_s *)qh;
@ -639,7 +654,7 @@ static void sam_qhfree(struct sam_qh_s *qh)
}
/*******************************************************************************
* Name: sam_qtdalloc
* Name: sam_qtd_alloc
*
* Description:
* Allocate a Queue Element Transfer Descriptor (qTD) by removing it from the
@ -647,7 +662,7 @@ static void sam_qhfree(struct sam_qh_s *qh)
*
*******************************************************************************/
static struct sam_qtd_s *sam_qtdalloc(void)
static struct sam_qtd_s *sam_qtd_alloc(void)
{
struct sam_qtd_s *qtd;
@ -672,7 +687,7 @@ static struct sam_qtd_s *sam_qtdalloc(void)
*
*******************************************************************************/
static void sam_qtdfree(struct sam_qtd_s *qtd)
static void sam_qtd_free(struct sam_qtd_s *qtd)
{
struct sam_list_s *entry = (struct sam_list_s *)qtd;
@ -682,6 +697,211 @@ static void sam_qtdfree(struct sam_qtd_s *qtd)
g_ehci.qtdfree = entry;
}
/*******************************************************************************
* List Management
*******************************************************************************/
/*******************************************************************************
* Name: sam_qh_foreach
*
* Description:
* Give the first entry in a list of Queue Head (QH) structures, call the
* handler for each QH structure in the list (including the one at the head
* of the list).
*
*******************************************************************************/
static int sam_qh_foreach(struct sam_qh_s *qh, uint32_t *bp, foreach_qh_t handler,
void *arg)
{
struct sam_qh_s *next;
uintptr_t physaddr;
int ret;
DEBUGASSERT(qh && handler);
while (qh)
{
/* Is this the end of the list? Check the horizontal link pointer (HLP)
* terminate (T) bit. If T==1, then the HLP address is not valid.
*/
if ((qh->hw.hlp & QH_HLP_T) != 0)
{
/* Set the next pointer to NULL. This will terminate the loop. */
next = NULL;
}
else
{
physaddr = qh->hw.hlp & QH_HLP_MASK;
next = (struct sam_qh_s *)sam_virtramaddr(physaddr);
}
/* Perform the user action on this entry. The action might result in
* unlinking the entry! But that is okay because we already have the
* next QH pointer.
*
* Notice that we do not manage the back pointer (bp). If the callout
* uses it, it must update it as necessary.
*/
ret = handler(qh, &bp, arg);
/* If the handler returns any non-zero value, then terminate the traversal
* early.
*/
if (ret != 0)
{
return ret;
}
/* Set up to visit the next entry */
qh = next;
}
return OK;
}
/*******************************************************************************
* Name: sam_qtd_foreach
*
* Description:
* Give a Queue Head (QH) instance, call the handler for each qTD structure
* in the queue.
*
*******************************************************************************/
static int sam_qtd_foreach(struct sam_qh_s *qh, foreach_qtd_t handler, void *arg)
{
struct sam_qtd_s *qtd;
struct sam_qtd_s *next;
uintptr_t physaddr;
uint32_t *bp;
int ret;
DEBUGASSERT(qh && handler);
/* Handle the special case where the queue is empty */
bp = &qh->hw.overlay.nqp;
if ((*bp & QH_NQP_T) != 0)
{
return 0;
}
/* Start with the first qTD in the queue */
physaddr = sam_read32(bp);
qtd = (struct sam_qtd_s *)sam_virtramaddr(physaddr);
next = NULL;
/* Now loop until we encounter the end of the qTD list */
while (qtd)
{
/* Is this the end of the list? Check the next qTD pointer (NQP)
* terminate (T) bit. If T==1, then the NQP address is not valid.
*/
if ((qtd->hw.nqp & QTD_NQP_T) != 0)
{
/* Set the next pointer to NULL. This will terminate the loop. */
next = NULL;
}
else
{
physaddr = qtd->hw.nqp & QTD_NQP_NTEP_MASK;
next = (struct sam_qtd_s *)sam_virtramaddr(physaddr);
}
/* Perform the user action on this entry. The action might result in
* unlinking the entry! But that is okay because we already have the
* next qTD pointer.
*
* Notice that we do not manage the back pointer (bp). If the callout
* uses it, it must update it as necessary.
*/
ret = handler(qtd, &bp, arg);
/* If the handler returns any non-zero value, then terminate the traversal
* early.
*/
if (ret != 0)
{
return ret;
}
/* Set up to visit the next entry */
qtd = next;
}
return OK;
}
/*******************************************************************************
* Name: sam_qtd_discard
*
* Description:
* This is a sam_qtd_foreach callback. It simply unlinks the QTD, updates
* the back pointer, and frees the QTD structure.
*
*******************************************************************************/
static int sam_qtd_discard(struct sam_qtd_s *qtd, uint32_t **bp, void *arg)
{
DEBUGASSERT(qtd && bp && *bp);
/* Remove the qTD from the list by updating the forward pointer to skip
* around this qTD. We do not change that pointer because are repeatedly
* removing the aTD at the head of the QH list.
*/
**bp = qtd->hw.nqp;
/* Then free the qTD */
sam_qtd_free(qtd);
return OK;
}
/*******************************************************************************
* Name: sam_qh_discard
*
* Description:
* Free the Queue Head (QH) and all qTD's attached to the QH.
*
* Assumptions:
* The QH structure itself has already been unlinked from whatever list it
* may have been in.
*
*******************************************************************************/
static int sam_qh_discard(struct sam_qh_s *qh)
{
int ret;
DEBUGASSERT(qh);
/* Free all of the qTD's attached to the QH */
ret = sam_gtd_foreach(qh, sam_qtd_discard, NULL);
if (ret < 0)
{
udbg("ERROR: sam_gtd_foreach failed: %d\n", ret);
}
/* Then free the QH itself */
sam_qh_free(qh);
return ret;
}
/*******************************************************************************
* EHCI Interrupt Handling
*******************************************************************************/
@ -690,7 +910,7 @@ static void sam_qtdfree(struct sam_qtd_s *qtd)
* Name: sam_ehci_interrupt
*
* Description:
* OHCI interrupt handler
* EHCI interrupt handler
*
*******************************************************************************/
@ -1553,7 +1773,7 @@ FAR struct usbhost_connection_s *sam_ehci_initialize(int controller)
{
/* Put the QH structure in a free list */
sam_qhfree(&g_ghpool[i]);
sam_qh_free(&g_ghpool[i]);
}
/* Initialize the list of free Queue Head (QH) structures */
@ -1562,7 +1782,7 @@ FAR struct usbhost_connection_s *sam_ehci_initialize(int controller)
{
/* Put the TD in a free list */
sam_qtdfree(&g_qtdpool[i]);
sam_qtd_free(&g_qtdpool[i]);
}
/* EHCI Hardware Configuration ***********************************************/
@ -1614,7 +1834,7 @@ FAR struct usbhost_connection_s *sam_ehci_initialize(int controller)
/* Enable interrupts at the interrupt controller */
up_enable_irq(SAM_IRQ_UHPHS); /* enable USB interrupt */
uvdbg("USB OHCI Initialized\n");
uvdbg("USB EHCI Initialized\n");
/* Initialize and return the connection interface */