From e4a56701c17bc67b04736bc3b803a6881fa1c741 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Fri, 23 Aug 2013 16:23:15 -0600 Subject: SAMA5/ECHI: Debug register access, add logic to determine transfer size, fix setting of control bit in token --- nuttx/arch/arm/src/sama5/chip/sam_ehci.h | 6 ++ nuttx/arch/arm/src/sama5/sam_clockconfig.c | 44 ++++++------- nuttx/arch/arm/src/sama5/sam_ehci.c | 102 ++++++++++++++++++++--------- nuttx/include/nuttx/usb/ehci.h | 63 ++++++++++++++++++ 4 files changed, 161 insertions(+), 54 deletions(-) diff --git a/nuttx/arch/arm/src/sama5/chip/sam_ehci.h b/nuttx/arch/arm/src/sama5/chip/sam_ehci.h index 6d6c801de..e564b1ecc 100644 --- a/nuttx/arch/arm/src/sama5/chip/sam_ehci.h +++ b/nuttx/arch/arm/src/sama5/chip/sam_ehci.h @@ -75,6 +75,12 @@ #define HCOR ((volatile struct ehci_hcor_s *)(SAM_UHPEHCI_VSECTION + 0x10)) +/* USB2 Debug Port Register Interface. These are not documented, but I was + * able to find these by registers by peeking at EHCI memory. + */ + +#define HDEBUG ((volatile struct ehci_debug_s *)(SAM_UHPEHCI_VSECTION + 0x90)) + /**************************************************************************** * Public Types ****************************************************************************/ diff --git a/nuttx/arch/arm/src/sama5/sam_clockconfig.c b/nuttx/arch/arm/src/sama5/sam_clockconfig.c index 42bdbe3e4..1c82e9e3e 100644 --- a/nuttx/arch/arm/src/sama5/sam_clockconfig.c +++ b/nuttx/arch/arm/src/sama5/sam_clockconfig.c @@ -330,28 +330,7 @@ static inline void sam_selectplla(void) static inline void sam_usbclockconfig(void) { -#ifdef CONFIG_SAMA5_OHCI - /* For OHCI Full-speed operations only, the user has to perform the - * following: - * - * 1) Enable UHP peripheral clock, bit (1 << AT91C_ID_UHPHS) in PMC_PCER - * register. - * 2) Select PLLACK as Input clock of OHCI part, USBS bit in PMC_USB - * register. - * 3) Program the OHCI clocks (UHP48M and UHP12M) with USBDIV field in - * PMC_USB register. USBDIV value is calculated regarding the PLLACK - * value and USB Full-speed accuracy. - * 4) Enable the OHCI clocks, UHP bit in PMC_SCER register. - * - * Steps 2 and 3 are done here. 1 and 2 are performed with the OHCI - * driver is initialized. - */ - - putreg32(BOARD_OHCI_INPUT | BOARD_OHCI_DIVIDER << PMC_USB_USBDIV_SHIFT, - SAM_PMC_USB); -#endif - -#ifdef CONFIG_SAMA5_EHCI +#if defined(CONFIG_SAMA5_EHCI) uint32_t regval; /* The USB Host High Speed requires a 480 MHz clock (UPLLCK) for the @@ -415,6 +394,27 @@ static inline void sam_usbclockconfig(void) regval |= PMC_USB_USBDIV(9); putreg32(regval, SAM_PMC_USB); + +#elif defined(CONFIG_SAMA5_OHCI) + /* For OHCI Full-speed operations only, the user has to perform the + * following: + * + * 1) Enable UHP peripheral clock, bit (1 << AT91C_ID_UHPHS) in PMC_PCER + * register. + * 2) Select PLLACK as Input clock of OHCI part, USBS bit in PMC_USB + * register. + * 3) Program the OHCI clocks (UHP48M and UHP12M) with USBDIV field in + * PMC_USB register. USBDIV value is calculated regarding the PLLACK + * value and USB Full-speed accuracy. + * 4) Enable the OHCI clocks, UHP bit in PMC_SCER register. + * + * Steps 2 and 3 are done here. 1 and 2 are performed with the OHCI + * driver is initialized. + */ + + putreg32(BOARD_OHCI_INPUT | BOARD_OHCI_DIVIDER << PMC_USB_USBDIV_SHIFT, + SAM_PMC_USB); + #endif } diff --git a/nuttx/arch/arm/src/sama5/sam_ehci.c b/nuttx/arch/arm/src/sama5/sam_ehci.c index 9a33a1a79..34e7c914c 100755 --- a/nuttx/arch/arm/src/sama5/sam_ehci.c +++ b/nuttx/arch/arm/src/sama5/sam_ehci.c @@ -201,6 +201,7 @@ struct sam_epinfo_s uint16_t xfrtype:2; /* See USB_EP_ATTR_XFER_* definitions in usb.h */ uint16_t speed:2; /* See USB_*_SPEED definitions in ehci.h */ int result; /* The result of the transfer */ + uint32_t xfrd; /* On completion, will hold the number of bytes transferred */ sem_t iocsem; /* Semaphore used to wait for transfer completion */ }; @@ -335,11 +336,12 @@ static void sam_qh_enqueue(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); -static struct sam_qtd_s *sam_qtd_setupphase(const struct usb_ctrlreq_s *req); -static struct sam_qtd_s *sam_qtd_dataphase(void *buffer, int buflen, - uint32_t tokenbits); +static struct sam_qtd_s *sam_qtd_setupphase(struct sam_epinfo_s *epinfo, + const struct usb_ctrlreq_s *req); +static struct sam_qtd_s *sam_qtd_dataphase(struct sam_epinfo_s *epinfo, + void *buffer, int buflen, uint32_t tokenbits); static struct sam_qtd_s *sam_qtd_statusphase(uint32_t tokenbits); -static int sam_async_transfer(struct sam_rhport_s *rhport, +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); @@ -1337,10 +1339,11 @@ static int sam_ioc_setup(struct sam_rhport_s *rhport, struct sam_epinfo_s *epinf * completed. */ - epinfo->iocwait = true; /* We want to be awakened by IOC interrupt */ - epinfo->status = 0; /* No status yet */ + epinfo->iocwait = true; /* We want to be awakened by IOC interrupt */ + epinfo->status = 0; /* No status yet */ + epinfo->xfrd = 0; /* Nothing transferred yet */ epinfo->result = -EBUSY; /* Transfer in progress */ - ret = OK; /* We are good to go */ + ret = OK; /* We are good to go */ } irqrestore(flags); @@ -1459,11 +1462,20 @@ static struct sam_qh_s *sam_qh_create(struct sam_rhport_s *rhport, ((uint32_t)epinfo->maxpacket << QH_EPCHAR_MAXPKT_SHIFT) | ((uint32_t)8 << QH_EPCHAR_RL_SHIFT); - if (epinfo->speed != EHCI_FULL_SPEED && epinfo->epno == 0) + /* 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 + * is an control endpoint, then software must set this bit to a one. + * Otherwise it should always set this bit to a zero." + */ + + if (epinfo->speed != EHCI_HIGH_SPEED && + epinfo->xfrtype == USB_EP_ATTR_XFER_CONTROL) { regval |= QH_EPCHAR_C; } + /* Save the endpoint characteristics word with the correct byte order */ + qh->hw.epchar = sam_swap32(regval); /* Write QH endpoint capabilities @@ -1566,7 +1578,8 @@ static int sam_qtd_addbpl(struct sam_qtd_s *qtd, const void *buffer, size_t bufl * *******************************************************************************/ -static struct sam_qtd_s *sam_qtd_setupphase(const struct usb_ctrlreq_s *req) +static struct sam_qtd_s *sam_qtd_setupphase(struct sam_epinfo_s *epinfo, + const struct usb_ctrlreq_s *req) { struct sam_qtd_s *qtd; uint32_t regval; @@ -1617,6 +1630,10 @@ static struct sam_qtd_s *sam_qtd_setupphase(const struct usb_ctrlreq_s *req) return NULL; } + /* Add the data transfer size to the count in the epinfo structure */ + + epinfo->xfrd += USB_SIZEOF_CTRLREQ; + return qtd; } @@ -1628,7 +1645,8 @@ static struct sam_qtd_s *sam_qtd_setupphase(const struct usb_ctrlreq_s *req) * *******************************************************************************/ -static struct sam_qtd_s *sam_qtd_dataphase(void *buffer, int buflen, +static struct sam_qtd_s *sam_qtd_dataphase(struct sam_epinfo_s *epinfo, + void *buffer, int buflen, uint32_t tokenbits) { struct sam_qtd_s *qtd; @@ -1680,6 +1698,10 @@ static struct sam_qtd_s *sam_qtd_dataphase(void *buffer, int buflen, return NULL; } + /* Add the data transfer size to the count in the epinfo structure */ + + epinfo->xfrd += buflen; + return qtd; } @@ -1720,14 +1742,13 @@ static struct sam_qtd_s *sam_qtd_statusphase(uint32_t tokenbits) * PID PID Code Contained in tokenbits * CERR Error Counter 3 * CPAGE Current Page 0 - * IOC Interrupt on complete QH_TOKEN_IOC + * IOC Interrupt on complete QTD_TOKEN_IOC * NBYTES Total Bytes to Transfer 0 * TOGGLE Data Toggle Contained in tokenbits */ - regval = tokenbits | QTD_TOKEN_ACTIVE | + regval = tokenbits | QTD_TOKEN_ACTIVE | QTD_TOKEN_IOC | ((uint32_t)3 << QTD_TOKEN_CERR_SHIFT) | - QH_TOKEN_IOC | ((uint32_t)USB_SIZEOF_CTRLREQ << QTD_TOKEN_NBYTES_SHIFT); qtd->hw.token = sam_swap32(regval); @@ -1751,12 +1772,18 @@ static struct sam_qtd_s *sam_qtd_statusphase(uint32_t tokenbits) * 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). + * *******************************************************************************/ -static int 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) +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) { struct sam_qh_s *qh; struct sam_qtd_s *qtd; @@ -1834,7 +1861,7 @@ static int sam_async_transfer(struct sam_rhport_s *rhport, * phase of the request sequence. */ - qtd = sam_qtd_setupphase(req); + qtd = sam_qtd_setupphase(epinfo, req); if (qtd == NULL) { udbg("ERROR: sam_qtd_setupphase failed\n"); @@ -1878,7 +1905,7 @@ static int sam_async_transfer(struct sam_rhport_s *rhport, * buffer. */ - qtd = sam_qtd_dataphase(buffer, buflen, tokenbits); + qtd = sam_qtd_dataphase(epinfo, buffer, buflen, tokenbits); if (qtd == NULL) { udbg("ERROR: sam_qtd_dataphase failed\n"); @@ -1950,9 +1977,9 @@ static int sam_async_transfer(struct sam_rhport_s *rhport, goto errout_with_iocwait; } - /* Transfer completed successfully */ + /* Transfer completed successfully. Return the number of bytes transferred */ - return OK; + return epinfo->xfrd; /* Clean-up after an error */ @@ -1960,7 +1987,7 @@ errout_with_qh: sam_qh_discard(qh); errout_with_iocwait: epinfo->iocwait = false; - return ret; + return (ssize_t)ret; } /******************************************************************************* @@ -1979,7 +2006,8 @@ errout_with_iocwait: static int sam_qtd_ioccheck(struct sam_qtd_s *qtd, uint32_t **bp, void *arg) { - DEBUGASSERT(qtd); + struct sam_epinfo_s *epinfo = (struct sam_epinfo_s *)arg; + DEBUGASSERT(qtd && epinfo); /* Make sure we reload the QH from memory */ @@ -1997,6 +2025,16 @@ static int sam_qtd_ioccheck(struct sam_qtd_s *qtd, uint32_t **bp, void *arg) **bp = qtd->hw.nqp; + /* Subtract the number of bytes left untransferred. The epinfo->xfrd + * field is initialized to the the total number of bytes to be transferred + * (all qTDs in the list). We subtract out the number of untransferred + * bytes on each transfer and the final result will be the number of bytes + * actually transferred. + */ + + epinfo->xfrd -= (sam_swap32(qtd->hw.token) & QTD_TOKEN_NBYTES_MASK) >> + QTD_TOKEN_NBYTES_SHIFT; + /* Release this QH by returning it to the free list */ sam_qtd_free(qtd); @@ -2061,7 +2099,7 @@ static int sam_qh_ioccheck(struct sam_qh_s *qh, uint32_t **bp, void *arg) /* Remove all active, attached qTD structures from the inactive QH */ - ret = sam_qtd_foreach(qh, sam_qtd_ioccheck, NULL); + ret = sam_qtd_foreach(qh, sam_qtd_ioccheck, (void *)qh->epinfo); if (ret < 0) { udbg("ERROR: sam_qh_forall failed: %d\n", ret); @@ -3056,7 +3094,7 @@ static int sam_ctrlin(FAR struct usbhost_driver_s *drvr, { struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr; uint16_t len; - int ret; + ssize_t nbytes; DEBUGASSERT(rhport && req); @@ -3071,9 +3109,9 @@ static int sam_ctrlin(FAR struct usbhost_driver_s *drvr, /* Now perform the transfer */ - ret = sam_async_transfer(rhport, &rhport->ep0, req, buffer, len); + nbytes = sam_async_transfer(rhport, &rhport->ep0, req, buffer, len); sam_givesem(&g_ehci.exclsem); - return ret; + return nbytes >=0 ? OK : (int)nbytes; } static int sam_ctrlout(FAR struct usbhost_driver_s *drvr, @@ -3125,11 +3163,11 @@ static int sam_ctrlout(FAR struct usbhost_driver_s *drvr, *******************************************************************************/ static int sam_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, - FAR uint8_t *buffer, size_t buflen) + FAR uint8_t *buffer, size_t buflen) { struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr; struct sam_epinfo_s *epinfo = (struct sam_epinfo_s *)ep; - int ret; + ssize_t nbytes; DEBUGASSERT(rhport && epinfo && buffer && buflen > 0); @@ -3142,7 +3180,7 @@ static int sam_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, switch (epinfo->xfrtype) { case USB_EP_ATTR_XFER_BULK: - ret = sam_async_transfer(rhport, epinfo, NULL, buffer, buflen); + nbytes = sam_async_transfer(rhport, epinfo, NULL, buffer, buflen); break; #ifndef CONFIG_USBHOST_INT_DISABLE @@ -3156,12 +3194,12 @@ static int sam_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, case USB_EP_ATTR_XFER_CONTROL: default: udbg("ERROR: Support for transfer type %d not implemented\n"); - ret = -ENOSYS; + nbytes = -ENOSYS; break; } sam_givesem(&g_ehci.exclsem); - return ret; + return nbytes >=0 ? OK : (int)nbytes; } /******************************************************************************* diff --git a/nuttx/include/nuttx/usb/ehci.h b/nuttx/include/nuttx/usb/ehci.h index 42e64884b..dd96514d1 100755 --- a/nuttx/include/nuttx/usb/ehci.h +++ b/nuttx/include/nuttx/usb/ehci.h @@ -134,6 +134,15 @@ #define EHCI_PORTSC14_OFFSET 0x0078 /* Port Status/Control, Port 14 */ #define EHCI_PORTSC15_OFFSET 0x007c /* Port Status/Control, Port 15 */ +/* Debug Register Offsets *******************************************************************/ +/* Paragraph C.3 */ + +#define ECHI_DEBUG_PCS_OFFSET 0x0000 /* Debug Port Control/Status Register */ +#define ECHI_DEBUG_USBPIDS_OFFSET 0x0004 /* Debug USB PIDs Register */ +#define ECHI_DEBUG_DATA0_OFFSET 0x0008 /* Debug Data Buffer 0 Register [31:0] */ +#define ECHI_DEBUG_DATA1_OFFSET 0x000c /* Debug Data Buffer 1 Register [63:32] */ +#define ECHI_DEBUG_DEVADDR_OFFSET 0x0010 /* Debug Device Address Register */ + /* PCI Configuration Space Register Bit Definitions *****************************************/ /* 0x0009-0x000b: Class Code. Paragraph 2.1.2 */ @@ -355,6 +364,47 @@ #define EHCI_PORTSC_ALLINTS (EHCI_PORTSC_CSC | EHCI_PORTSC_PEC | \ EHCI_PORTSC_OCC | EHCI_PORTSC_RESUME) +/* Debug Register Bit Definitions ***********************************************************/ + +/* Debug Port Control/Status Register. Paragraph C.3.1 */ + +#define ECHI_DEBUG_PCS_LENGTH_SHIFT (0) /* Bits 0-3: Data Length */ +#define ECHI_DEBUG_PCS_LENGTH_MASK (15 << ECHI_DEBUG_PCS_LENGTH_SHIFT) +#define ECHI_DEBUG_PCS_WRITE (1 << 4) /* Bit 6: Write/Read# */ +#define ECHI_DEBUG_PCS_GO (1 << 5) /* Bit 5: Go */ +#define ECHI_DEBUG_PCS_ERROR (1 << 6) /* Bit 6: Error/Good# */ +#define ECHI_DEBUG_PCS_EXCEPTION_SHIFT (17) /* Bits 7-9: Exception */ +#define ECHI_DEBUG_PCS_EXCEPTION_MASK (7 << ECHI_DEBUG_PCS_EXCEPTION_SHIFT) +#define ECHI_DEBUG_PCS_INUSE (1 << 10) /* Bit 10: In Use */ + /* Bits 11-15: Reserved */ +#define ECHI_DEBUG_PCS_DONE (1 << 16) /* Bit 16: Done */ + /* Bits 17-27: Reserved */ +#define ECHI_DEBUG_PCS_ENABLED (1 << 28) /* Bit 28: Enabled */ + /* Bit 29: Reserved */ +#define ECHI_DEBUG_PCS_OWNER (1 << 30) /* Bit 30: Owner */ + /* Bit 31: Reserved */ + +/* Debug USB PIDs Register. Paragraph C.3.2 */ + +#define ECHI_DEBUG_USBPIDS_TKPID_SHIFT (0) /* Bits 0-7: Token PID */ +#define ECHI_DEBUG_USBPIDS_TKPID_MASK (0xff << ECHI_DEBUG_USBPIDS_TKPID_SHIFT) +#define ECHI_DEBUG_USBPIDS_SPID_SHIFT (8) /* Bits 8-15: Sent PID */ +#define ECHI_DEBUG_USBPIDS_SPID_MASK (0xff << ECHI_DEBUG_USBPIDS_SPID_SHIFT) +#define ECHI_DEBUG_USBPIDS_RPID_SHIFT (16) /* Bits 16-23: Received PID */ +#define ECHI_DEBUG_USBPIDS_RPID_MASK (0xff << ECHI_DEBUG_USBPIDS_RPID_SHIFT) + /* Bits 24-31: Reserved */ + +/* Debug Data Buffer 0/1 Register [64:0]. Paragreph C.3.3. 64 bits of data. */ + +/* Debug Device Address Register. Paragraph C.3.4 */ + +#define ECHI_DEBUG_DEVADDR_ENDPT_SHIFT (0) /* Bit 0-3: USB Endpoint */ +#define ECHI_DEBUG_DEVADDR_ENDPT_MASK (15 << ECHI_DEBUG_DEVADDR_ENDPT_SHIFT) + /* Bits 4-7: Reserved */ +#define ECHI_DEBUG_DEVADDR_ADDR_SHIFT (8) /* Bits 8-14: USB Address */ +#define ECHI_DEBUG_DEVADDR_ADDR_MASK (0x7f << ECHI_DEBUG_DEVADDR_ADDR_SHIFT) + /* Bits 15-31: Reserved */ + /* Data Structures **************************************************************************/ /* Paragraph 3 */ @@ -755,6 +805,19 @@ struct ehci_hcor_s uint32_t portsc[15]; /* 0x44: Port Status/Control */ }; +/* USB2 Debug Port Register Interface. This register block is normally found via the PCI + * capabalities. In non-PCI implementions, you need apriori information about the location + * of these registers. + */ + +struct ehci_debug_s +{ + uint32_t psc; /* 0x00: Debug Port Control/Status Register */ + uint32_t pids; /* 0x04: Debug USB PIDs Register */ + uint32_t data[2]; /* 0x08: Debug Data buffer Registers */ + uint32_t addr; /* 0x10: Device Address Register */ +}; + /* Data Structures **************************************************************************/ /* Paragraph 3 */ -- cgit v1.2.3