summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--nuttx/arch/arm/src/sama5/chip/sam_ehci.h6
-rw-r--r--nuttx/arch/arm/src/sama5/sam_clockconfig.c44
-rwxr-xr-xnuttx/arch/arm/src/sama5/sam_ehci.c102
-rwxr-xr-xnuttx/include/nuttx/usb/ehci.h63
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 */