SAMA5 EHCI: Initial debug changes

This commit is contained in:
Gregory Nutt 2013-08-22 17:25:00 -06:00
parent f356586fd3
commit eef0f392ec
2 changed files with 160 additions and 24 deletions

View File

@ -143,7 +143,7 @@ struct sam_qh_s
/* Internal fields used by the EHCI driver */
struct sam_epinfo_s *epinfo; /* Endpoint used for the transfer */
uint32_t pad[12]; /* Padding to assure 32-byte alignment */
uint8_t pad[12]; /* Padding to assure 32-byte alignment */
};
/* Internal representation of the EHCI Queue Element Transfer Descriptor (qTD) */
@ -299,6 +299,18 @@ static int sam_qh_flush(struct sam_qh_s *qh);
/* Endpoint Transfer Handling **************************************************/
#ifdef CONFIG_SAMA5_EHCI_REGDEBUG
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_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);
@ -673,7 +685,7 @@ static int ehci_wait_usbsts(uint32_t maskbits, uint32_t donebits,
regval = sam_getreg(&HCOR->usbsts);
if ((regval & EHCI_INT_SYSERROR) != 0)
{
udbg("ERROR: System error: 0x%08X", regval);
udbg("ERROR: System error: %08x\n", regval);
return -EIO;
}
@ -933,19 +945,20 @@ static int sam_qtd_foreach(struct sam_qh_s *qh, foreach_qtd_t handler, void *arg
/* Handle the special case where the queue is empty */
bp = &qh->hw.overlay.nqp;
if ((*bp & QH_NQP_T) != 0)
bp = &qh->hw.overlay.nqp; /* Start of qTDs remaining */
physaddr = sam_swap32(*bp); /* Physical address of first qTD in CPU order */
if ((physaddr & QH_NQP_T) != 0)
{
return 0;
}
/* Start with the first qTD in the queue */
/* Start with the first qTD in the list */
physaddr = sam_swap32(*bp);
qtd = (struct sam_qtd_s *)sam_virtramaddr(physaddr);
next = NULL;
qtd = (struct sam_qtd_s *)sam_virtramaddr(physaddr);
next = NULL;
/* Now loop until we encounter the end of the qTD list */
/* And loop until we encounter the end of the qTD list */
while (qtd)
{
@ -1130,8 +1143,8 @@ static int sam_qh_flush(struct sam_qh_s *qh)
{
/* Flush the QH first */
cp15_invalidate_dcache((uintptr_t)&qh->hw,
(uintptr_t)&qh->hw + sizeof(struct ehci_qh_s));
cp15_coherent_dcache((uintptr_t)&qh->hw,
(uintptr_t)&qh->hw + sizeof(struct ehci_qh_s));
/* Then flush all of the qTD entries in the queue */
@ -1143,7 +1156,73 @@ static int sam_qh_flush(struct sam_qh_s *qh)
*******************************************************************************/
/*******************************************************************************
* Name: sam_ioc_setup
* Name: sam_qtd_dump
*
* Description:
* This is a sam_qtd_foreach callout function. It dumps the context of one
* qTD
*
*******************************************************************************/
#ifdef CONFIG_SAMA5_EHCI_REGDEBUG
static int sam_qtd_dump(struct sam_qtd_s *qtd, uint32_t **bp, void *arg)
{
uvdbg(" QTD[%p]:\n", qtd);
uvdbg(" hw:\n");
uvdbg(" nqp: %08x alt: %08x token: %08x\n",
qtd->hw.nqp, qtd->hw.alt, qtd->hw.token);
uvdbg(" bpl: %08x %08x %08x %08x %08x\n",
qtd->hw.bpl[0], qtd->hw.bpl[1], qtd->hw.bpl[2],
qtd->hw.bpl[3], qtd->hw.bpl[4]);
return OK;
}
#endif
/*******************************************************************************
* Name: sam_qh_dump
*
* Description:
* This is a sam_qh_foreach callout function. It dumps a QH structure and
* all of the qTD structures linked to the QH.
*
*******************************************************************************/
#ifdef CONFIG_SAMA5_EHCI_REGDEBUG
static int sam_qh_dump(struct sam_qh_s *qh, uint32_t **bp, void *arg)
{
struct sam_epinfo_s *epinfo;
struct ehci_overlay_s *overlay;
uvdbg("QH[%p]:\n", qh);
uvdbg(" hw:\n");
uvdbg(" hlp: %08x epchar: %08x epcaps: %08x cqp: %08x\n",
qh->hw.hlp, qh->hw.epchar, qh->hw.epcaps, qh->hw.cqp);
overlay = &qh->hw.overlay;
uvdbg(" overlay:\n");
uvdbg(" nqp: %08x alt: %08x token: %08x\n",
overlay->nqp, overlay->alt, overlay->token);
uvdbg(" bpl: %08x %08x %08x %08x %08x\n",
overlay->bpl[0], overlay->bpl[1], overlay->bpl[2],
overlay->bpl[3], overlay->bpl[4]);
epinfo = qh->epinfo;
uvdbg(" epinfo[%p]:\n", epinfo);
if (epinfo)
{
uvdbg(" EP%d DIR=%s FA=%08x TYPE=%d MaxPacket=%d\n",
epinfo->epno, epinfo->dirin ? "IN" : "OUT", epinfo->devaddr,
epinfo->xfrtype, epinfo->maxpacket);
uvdbg(" Toggle=%d iocwait=%d speed=%d result=%d\n",
epinfo->toggle, epinfo->iocwait, epinfo->speed, epinfo->result);
}
return sam_qtd_foreach(qh, sam_qtd_dump, NULL);
}
#endif
/*******************************************************************************
* Name: sam_qh_dumpall
*
* Description:
* Set the request for the IOC event well BEFORE enabling the transfer (as
@ -1153,12 +1232,34 @@ static int sam_qh_flush(struct sam_qh_s *qh)
*
*******************************************************************************/
#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:
* 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!
*
* Assumption: The caller holds tex EHCI exclsem
*
*******************************************************************************/
static int sam_ioc_setup(struct sam_rhport_s *rhport, struct sam_epinfo_s *epinfo)
{
irqstate_t flags;
int ret = -ENODEV;
DEBUGASSERT(rhport && epinfo);
DEBUGASSERT(rhport && epinfo && !epinfo->iocwait);
/* Is the device still connected? */
@ -1186,6 +1287,9 @@ static int sam_ioc_setup(struct sam_rhport_s *rhport, struct sam_epinfo_s *epinf
* Description:
* Wait for the IOC event.
*
* Assumption: The caller does *NOT* hold the EHCI exclsem. That would cause
* a deadlock when the bottom-half, worker thread needs to take the semaphore.
*
*******************************************************************************/
static int sam_ioc_wait(struct sam_epinfo_s *epinfo)
@ -1311,7 +1415,10 @@ static struct sam_qh_s *sam_qh_create(struct sam_rhport_s *rhport,
* next qTD is added to the queue.
*/
qh->hw.hlp = sam_swap32(QH_HLP_T);
qh->hw.overlay.nqp = sam_swap32(QH_NQP_T);
qh->hw.overlay.alt = sam_swap32(QH_AQP_T);
return qh;
}
/*******************************************************************************
@ -1564,7 +1671,10 @@ static struct sam_qtd_s *sam_qtd_statusphase(uint32_t tokenbits)
* This is a blocking function; it will not return until the control transfer
* has completed.
*
* Assumption: The caller holds the EHCI exclsem
* 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.
*
*******************************************************************************/
@ -1638,7 +1748,7 @@ static int sam_async_transfer(struct sam_rhport_s *rhport,
*/
flink = &qh->hw.overlay.nqp;
toggle = epinfo->toggle ? 0 : QTD_TOKEN_TOGGLE;
toggle = (uint32_t)epinfo->toggle << QTD_TOKEN_TOGGLE_SHIFT;
ret = -EIO;
/* Is the an EP0 SETUP request? If req will be non-NULL */
@ -1714,7 +1824,7 @@ static int sam_async_transfer(struct sam_rhport_s *rhport,
{
/* Extra TOKEN bits include the data toggle and the data PID. */
uint32_t tokenbits = toggle | datapid ;
uint32_t tokenbits = toggle | datapid;
/* Allocate a new Queue Element Transfer Descriptor (qTD) for the status */
@ -1733,11 +1843,33 @@ static int sam_async_transfer(struct sam_rhport_s *rhport,
/* Add the new QH to the head of the asynchronous queue list */
(void)sam_qh_dump(qh, NULL, NULL);
sam_qh_enqueue(qh);
/* 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");
@ -1875,7 +2007,7 @@ static int sam_qh_ioccheck(struct sam_qh_s *qh, uint32_t **bp, void *arg)
{
/* No errors.. Save the last data toggle value */
epinfo->toggle = ((token & QTD_TOKEN_TOGGLE) != 0);
epinfo->toggle = (token >> QTD_TOKEN_TOGGLE_SHIFT) & 1;
/* Report success */
@ -1912,6 +2044,8 @@ static int sam_qh_ioccheck(struct sam_qh_s *qh, uint32_t **bp, void *arg)
*bp = &qh->hw.overlay.nqp;
}
return OK;
}
/*******************************************************************************
@ -2418,7 +2552,7 @@ static int sam_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx)
}
/* USB 2.0 spec says at least 50ms delay before port reset.
* REVISIT: I think this is wrong. It needs to hold the port in
* REVISIT: I think this is wrong. It needs to hold the port in
* reset for 50Msec, not wait 50Msec before resetting.
*/
@ -2574,7 +2708,7 @@ static int sam_epalloc(FAR struct usbhost_driver_s *drvr,
*/
DEBUGASSERT(drvr && epdesc && ep);
uvdbg("EP%d DIR=%s FA=%08x TYPE=%d Interval=%d Max Packet Size=%d\n",
uvdbg("EP%d DIR=%s FA=%08x TYPE=%d Interval=%d MaxPacket=%d\n",
epdesc->addr, epdesc->in ? "IN" : "OUT", epdesc->funcaddr,
epdesc->xfrtype, epdesc->interval, epdesc->mxpacketsize);
@ -3062,7 +3196,7 @@ static int sam_reset(void)
if ((regval & EHCI_USBSTS_HALTED) == 0)
{
udbg("ERROR: Timed out waiting for HCHalted. USBSTS: %08X", regval);
udbg("ERROR: Timed out waiting for HCHalted. USBSTS: %08x", regval);
return -ETIMEDOUT;
}
@ -3296,14 +3430,14 @@ FAR struct usbhost_connection_s *sam_ehci_initialize(int controller)
/* Show the ECHI version */
regval16 = sam_swap16(HCCR->hciversion);
uvdbg("HCIVERSIONI %x.%02x", regval16 >> 8, regval16 & 0xff);
uvdbg("HCIVERSION %x.%02x\n", regval16 >> 8, regval16 & 0xff);
/* Verify the the correct number of ports is reported */
regval = sam_getreg(&HCCR->hcsparams);
nports = (regval & EHCI_HCSPARAMS_NPORTS_MASK) >> EHCI_HCSPARAMS_NPORTS_SHIFT;
uvdbg("HCSPARAMS=%08x nports=%d", regval, nports);
uvdbg("HCSPARAMS=%08x nports=%d\n", regval, nports);
DEBUGASSERT(nports == SAM_EHCI_NRHPORT);
/* Show the HCCPARAMS register */

View File

@ -542,7 +542,8 @@
#define QTD_TOKEN_IOC (1 << 15) /* Bit 15: Interrupt On Complete */
#define QTD_TOKEN_NBYTES_SHIFT (16) /* Bits 16-30: Total Bytes to Transfer */
#define QTD_TOKEN_NBYTES_MASK (0x7fff << QTD_TOKEN_NBYTES_SHIFT)
#define QTD_TOKEN_TOGGLE (1 << 13) /* Bit 31: Data Toggle */
#define QTD_TOKEN_TOGGLE_SHIFT (31) /* Bit 31: Data Toggle */
#define QTD_TOKEN_TOGGLE (1 << 31) /* Bit 31: Data Toggle */
/* qTD Buffer Page Pointer List. Paragraph 3.5.4 */
/* Page 0 */
@ -662,7 +663,8 @@
#define QH_TOKEN_IOC (1 << 15) /* Bit 15: Interrupt On Complete */
#define QH_TOKEN_NBYTES_SHIFT (16) /* Bits 16-30: Total Bytes to Transfer */
#define QH_TOKEN_NBYTES_MASK (0x7fff << QH_TOKEN_NBYTES_SHIFT)
#define QH_TOKEN_TOGGLE (1 << 13) /* Bit 31: Data Toggle
#define QH_TOKEN_TOGGLE_SHIFT (31) /* Bit 31: Data Toggle */
#define QH_TOKEN_TOGGLE (1 << 31) /* Bit 31: Data Toggle */
/* Buffer Page Pointer List (NOTE 2)
/* Page 0 */