summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2013-08-21 13:45:54 -0600
committerGregory Nutt <gnutt@nuttx.org>2013-08-21 13:45:54 -0600
commitefe1cfc9beaec0ee4259100ef44bbe6b912b4b3c (patch)
tree147a0168acb2608539cb8ac86f2f4d87a6a40ab0
parentbd328a7de0472a500a5aed2b6b6642ba946f5aed (diff)
downloadnuttx-efe1cfc9beaec0ee4259100ef44bbe6b912b4b3c.tar.gz
nuttx-efe1cfc9beaec0ee4259100ef44bbe6b912b4b3c.tar.bz2
nuttx-efe1cfc9beaec0ee4259100ef44bbe6b912b4b3c.zip
SAMA5 EHCI: Hardware initialization logic
-rwxr-xr-xnuttx/arch/arm/src/sama5/sam_ehci.c214
-rwxr-xr-xnuttx/include/nuttx/usb/ehci.h15
2 files changed, 213 insertions, 16 deletions
diff --git a/nuttx/arch/arm/src/sama5/sam_ehci.c b/nuttx/arch/arm/src/sama5/sam_ehci.c
index d46909626..f42bb4b0f 100755
--- a/nuttx/arch/arm/src/sama5/sam_ehci.c
+++ b/nuttx/arch/arm/src/sama5/sam_ehci.c
@@ -102,12 +102,32 @@
# undef CONFIG_SAMA5_EHCI_REGDEBUG
#endif
+/* Periodic transfers will be supported later */
+
+#undef CONFIG_USBHOST_INT_DISABLE
+#define CONFIG_USBHOST_INT_DISABLE 1
+
+#undef CONFIG_USBHOST_ISOC_DISABLE
+#define CONFIG_USBHOST_INT_DISABLE 1
+
/* Driver-private Definitions **************************************************/
+/* This is the set of interrupts handled by this driver */
+
#define EHCI_HANDLED_INTS (EHCI_INT_USBINT | EHCI_INT_USBERRINT | \
EHCI_INT_PORTSC | EHCI_INT_SYSERROR | \
EHCI_INT_AAINT)
+/* The periodic frame list is a 4K-page aligned array of Frame List Link
+ * pointers. The length of the frame list may be programmable. The programmability
+ * of the periodic frame list is exported to system software via the HCCPARAMS
+ * register. If non-programmable, the length is 1024 elements. If programmable,
+ * the length can be selected by system software as one of 256, 512, or 1024
+ * elements.
+ */
+
+#define FRAME_LIST_SIZE 1024
+
/*******************************************************************************
* Private Types
*******************************************************************************/
@@ -316,13 +336,27 @@ static struct sam_ehci_s g_ehci;
static struct usbhost_connection_s g_ehciconn;
+/* The head of the asynchronous queue */
+
+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)));
+
+/* The frame list */
+
+static uint32_t g_framelist[FRAME_LIST_SIZE] __attribute__ ((aligned(4096)));
+#endif
+
/* Pools of pre-allocated data structures. These will all be linked into the
* free lists within g_ehci. These must all be aligned to 32-byte boundaries
*/
/* Queue Head (QH) pool */
-static struct sam_qh_s g_ghpool[CONFIG_SAMA5_EHCI_NQHS]
+static struct sam_qh_s g_qhpool[CONFIG_SAMA5_EHCI_NQHS]
__attribute__ ((aligned(32)));
/* Queue Element Transfer Descriptor (qTD) pool */
@@ -1231,7 +1265,6 @@ static inline void sam_async_advance_bottomhalf(void)
static void sam_ehci_bottomhalf(FAR void *arg)
{
uint32_t pending = (uint32_t)arg;
- uint32_t regval;
/* Handle all unmasked interrupt sources */
/* USB Interrupt (USBINT)
@@ -2161,12 +2194,27 @@ FAR struct usbhost_connection_s *sam_ehci_initialize(int controller)
{
irqstate_t flags;
uint32_t regval;
+#ifdef CONFIG_DEBUG_USB
+ uint16_t regval16;
+ unsigned int nports;
+#endif
+ uintptr_t physaddr;
int ret;
int i;
/* Sanity checks */
DEBUGASSERT(controller == 0);
+ DEBUGASSERT(((uintptr_t)&g_asynchead & 0x1f) == 0);
+ DEBUGASSERT(((uintptr_t)&g_qhpool & 0x1f) == 0);
+ DEBUGASSERT(((uintptr_t)&g_qtdpool & 0x1f) == 0);
+ 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
/* SAMA5 Configuration *******************************************************/
/* For High-speed operations, the user has to perform the following:
@@ -2245,7 +2293,7 @@ FAR struct usbhost_connection_s *sam_ehci_initialize(int controller)
{
/* Put the QH structure in a free list */
- sam_qh_free(&g_ghpool[i]);
+ sam_qh_free(&g_qhpool[i]);
}
/* Initialize the list of free Queue Head (QH) structures */
@@ -2258,7 +2306,7 @@ FAR struct usbhost_connection_s *sam_ehci_initialize(int controller)
}
/* EHCI Hardware Configuration ***********************************************/
-
+ /* Host Controller Initialization. Paragraph 4.1 */
/* Reset the EHCI hardware */
ret = sam_reset();
@@ -2268,10 +2316,158 @@ FAR struct usbhost_connection_s *sam_ehci_initialize(int controller)
return NULL;
}
-#warning Missing logic
+ /* "In order to initialize the host controller, software should perform the
+ * following steps:
+ *
+ * • "Program the CTRLDSSEGMENT register with 4-Gigabyte segment where all
+ * of the interface data structures are allocated. [64-bit mode]
+ * • "Write the appropriate value to the USBINTR register to enable the
+ * appropriate interrupts.
+ * • "Write the base address of the Periodic Frame List to the PERIODICLIST
+ * BASE register. If there are no work items in the periodic schedule,
+ * all elements of the Periodic Frame List should have their T-Bits set
+ * to a one.
+ * • "Write the USBCMD register to set the desired interrupt threshold,
+ * frame list size (if applicable) and turn the host controller ON via
+ * setting the Run/Stop bit.
+ * • Write a 1 to CONFIGFLAG register to route all ports to the EHCI controller
+ * ...
+ *
+ * "At this point, the host controller is up and running and the port registers
+ * will begin reporting device connects, etc. System software can enumerate a
+ * port through the reset process (where the port is in the enabled state). At
+ * this point, the port is active with SOFs occurring down the enabled por
+ * enabled Highspeed ports, but the schedules have not yet been enabled. The
+ * EHCI Host controller will not transmit SOFs to enabled Full- or Low-speed
+ * ports.
+ */
- /* Interrupt Configuration ***************************************************/
+ /* Disable all interrupts */
+
+ sam_putreg(0, &HCOR->usbintr);
+
+ /* Clear pending interrupts. Bits in the USBSTS register are cleared by
+ * writing a '1' to the corresponding bit.
+ */
+
+ sam_putreg(ECHI_INT_ALLINTS, &HCOR->usbsts);
+
+#ifdef CONFIG_DEBUG
+ /* Show the ECHI version */
+
+ regval16 = sam_read16(&HCCR->hciversion);
+ uvdbg("HCIVERSIONI %x.%02x", 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);
+ DEBUGASSERT(nports == SAM_EHCI_NRHPORT);
+
+ /* Show the HCCPARAMS register */
+
+ regval = sam_getreg(&HCCR->hccparams);
+ uvdbg("HCCPARAMS=%08x\n", regval);
+#endif
+
+ /* Initialize the head of the asynchronous queue/reclamation list.
+ *
+ * "In order to communicate with devices via the asynchronous schedule,
+ * system software must write the ASYNDLISTADDR register with the address
+ * of a control or bulk queue head. Software must then enable the
+ * asynchronous schedule by writing a one to the Asynchronous Schedule
+ * Enable bit in the USBCMD register. In order to communicate with devices
+ * via the periodic schedule, system software must enable the periodic
+ * schedule by writing a one to the Periodic Schedule Enable bit in the
+ * USBCMD register. Note that the schedules can be turned on before the
+ * first port is reset (and enabled)."
+ */
+
+ memset(&g_asynchead, 0, sizeof(struct sam_qh_s));
+ sam_write32((uint32_t)&g_asynchead | QH_HLP_TYP_QH, &g_asynchead.hw.hlp);
+ sam_write32(QH_EPCHAR_H | QH_EPCHAR_EPS_FULL, &g_asynchead.hw.epchar);
+ sam_write32(QH_NQP_T, &g_asynchead.hw.overlay.nqp);
+ sam_write32(QH_NQP_T, &g_asynchead.hw.overlay.alt);
+ sam_write32(QH_TOKEN_HALTED, &g_asynchead.hw.overlay.token);
+
+ /* Set the Current Asynchronous List Address. */
+
+ physaddr = sam_physramaddr((uintptr_t)&g_asynchead);
+ sam_putreg(physaddr, &HCOR->asynclistaddr);
+
+#ifndef CONFIG_USBHOST_INT_DISABLE
+ /* Initialize the head of the periodic list */
+
+ memset(&g_perhead, 0, sizeof(struct sam_qh_s));
+ sam_write32(QH_HLP_T, &g_perhead.hw.hlp);
+ sam_write32(QH_NQP_T, &g_perhead.hw.overlay.nqp);
+ sam_write32(QH_NQP_T, &g_perhead.hw.overlay.alt);
+ sam_write32(QH_TOKEN_HALTED, &g_perhead.hw.overlay.token);
+ sam_write32(QH_EPCAPS_SSMASK(1), &g_perhead.hw.epcaps);
+
+ /* Attach the periodic QH to Period Frame List */
+
+ physaddr = sam_physramaddr((uintptr_t)&g_perhead);
+ for (i = 0; i < FRAME_LIST_SIZE; i++)
+ {
+ g_framelist[i] = physaddr | PFL_TYP_QH;
+ }
+
+ /* Set the Periodic Frame List Base Address. */
+
+ sam_putreg(physaddr, &HCOR->periodiclistbase);
+#endif
+
+ /* Enable the asynchronous schedule and, possibly set the frame list size */
+
+ regval = sam_getreg(&HCOR->usbcmd);
+ regval &= ~(EHCI_USBCMD_HCRESET | EHCI_USBCMD_FLSIZE_MASK |
+ EHCI_USBCMD_FLSIZE_MASK | EHCI_USBCMD_PSEN |
+ EHCI_USBCMD_IAADB | EHCI_USBCMD_LRESET);
+ regval |= EHCI_USBCMD_ASEN;
+
+#ifndef CONFIG_USBHOST_INT_DISABLE
+# if FRAME_LIST_SIZE == 1024
+ regval |= EHCI_USBCMD_FLSIZE_1024;
+# elif FRAME_LIST_SIZE == 512
+ regval |= EHCI_USBCMD_FLSIZE_512;
+# elif FRAME_LIST_SIZE == 512
+ regval |= EHCI_USBCMD_FLSIZE_256;
+# else
+# error Unsupported frame size list size
+# endif
+#endif
+
+ sam_putreg(regval, &HCOR->usbcmd);
+ /* Start the host controller by setting the RUN bit in the USBCMD regsiter. */
+
+ regval = sam_getreg(&HCOR->usbcmd);
+ regval &= ~(EHCI_USBCMD_LRESET | EHCI_USBCMD_IAADB | EHCI_USBCMD_PSEN |
+ EHCI_USBCMD_ASEN | EHCI_USBCMD_HCRESET);
+ regval |= EHCI_USBCMD_RUN;
+ sam_putreg(regval, &HCOR->usbcmd);
+
+ /* Route all ports to this host controller by setting the CONFIG flag. */
+
+ regval = sam_getreg(&HCOR->configflag);
+ regval |= EHCI_CONFIGFLAG;
+ sam_putreg(regval, &HCOR->configflag);
+
+ /* Wait for the ECHI to run (i.e., not longer report halted) */
+
+ ret = ehci_wait_usbsts(EHCI_USBSTS_HALTED, 0, 100*1000);
+ if (ret < 0)
+ {
+ udbg("ERROR: EHCI Failed to run: USBSTS=%08x\n",
+ sam_getreg(&HCOR->usbsts));
+
+ return NULL;
+ }
+
+ /* Interrupt Configuration ***************************************************/
/* Attach USB host controller interrupt handler */
if (irq_attach(SAM_IRQ_UHPHS, sam_ehci_tophalf) != 0)
@@ -2280,12 +2476,6 @@ FAR struct usbhost_connection_s *sam_ehci_initialize(int controller)
return NULL;
}
- /* Clear pending interrupts. Bits in the USBSTS register are cleared by
- * writing a '1' to the corresponding bit.
- */
-
- sam_putreg(ECHI_INT_ALLINTS, &HCOR->usbsts);
-
/* Enable EHCI interrupts. Interrupts are still disabled at the level of
* the AIC.
*/
diff --git a/nuttx/include/nuttx/usb/ehci.h b/nuttx/include/nuttx/usb/ehci.h
index 114736932..62b02e4a5 100755
--- a/nuttx/include/nuttx/usb/ehci.h
+++ b/nuttx/include/nuttx/usb/ehci.h
@@ -360,7 +360,7 @@
/* Periodic Frame List. Paragraph 3.1 */
-#define PFL_T (1 << 0) /* Bit 0: Not memory pointer, see TYP */
+#define PFL_T (1 << 0) /* Bit 0: Terminate, Link pointer invalid */
#define PFL_TYP_SHIFT (1) /* Bits 1-2: Type */
#define PFL_TYP_MASK (3 << PFL_TYP_SHIFT)
# define PFL_TYP_ITD (0 << PFL_TYP_SHIFT) /* Isochronous Transfer Descriptor */
@@ -375,7 +375,7 @@
/* Isochronous (High-Speed) Transfer Descriptor (iTD). Paragraph 3.3 */
/* iTD Next Link Pointer. Paragraph 3.3.1 */
-#define ITD_NLP_T (1 << 0) /* Bit 0: Link pointer invalid, see TYP */
+#define ITD_NLP_T (1 << 0) /* Bit 0: Terminate, Link pointer invalid */
#define ITD_NLP_TYP_SHIFT (1) /* Bits 1-2: Type */
#define ITD_NLP_TYP_MASK (3 << ITD_NLP_TYP_SHIFT)
# define ITD_NLP_TYP_ITD (0 << ITD_NLP_TYP_SHIFT) /* Isochronous Transfer Descriptor */
@@ -434,7 +434,7 @@
/* Split Transaction Isochronous Transfer Descriptor (siTD). Paragraph 3.4 */
/* siTD Next Link Pointer. Paragraph 3.4.1 */
-#define SITD_NLP_T (1 << 0) /* Bit 0: Link pointer invalid, see TYP */
+#define SITD_NLP_T (1 << 0) /* Bit 0: Terminate, Link pointer invalid */
#define SITD_NLP_TYP_SHIFT (1) /* Bits 1-2: Type */
#define SITD_NLP_TYP_MASK (3 << SITD_NLP_TYP_SHIFT)
# define SITD_NLP_TYP_ITD (0 << SITD_NLP_TYP_SHIFT) /* Isochronous Transfer Descriptor */
@@ -463,8 +463,10 @@
#define SITD_FMSCHED_SSMASK_SHIFT (0) /* Bitx 0-7: Split Start Mask (µFrame S-mask) */
#define SITD_FMSCHED_SSMASK_MASK (0xff << SITD_FMSCHED_SSMASK_SHIFT)
+# define SITD_FMSCHED_SSMASK(n) ((n) << SITD_FMSCHED_SSMASK_SHIFT)
#define SITD_FMSCHED_SCMASK_SHIFT (8) /* Bitx 8-15: Split Completion Mask (µFrame C-Mask) */
#define SITD_FMSCHED_SCMASK_MASK (0xff << SITD_FMSCHED_SCMASK_SHIFT)
+# define SITD_FMSCHED_SCMASK(n) ((n) << SITD_FMSCHED_SCMASK_SHIFT)
/* Bits 16-31: Reserved */
/* siTD Transfer State. Paragraph 3.4.3 */
@@ -557,7 +559,7 @@
/* Queue Head. Paragraph 3.6 */
/* Queue Head Horizontal Link Pointer. Paragraph 3.6.1 */
-#define QH_HLP_T (1 << 0) /* Bit 0: Terminate, Last QH invalid, see TYP */
+#define QH_HLP_T (1 << 0) /* Bit 0: Terminate, QH HL pointer invalid */
#define QH_HLP_TYP_SHIFT (1) /* Bits 1-2: Type */
#define QH_HLP_TYP_MASK (3 << QH_HLP_TYP_SHIFT)
# define QH_HLP_TYP_ITD (0 << QH_HLP_TYP_SHIFT) /* Isochronous Transfer Descriptor */
@@ -592,14 +594,19 @@
#define QH_EPCAPS_SSMASK_SHIFT (0) /* Bitx 0-7: Interrupt Schedule Mask (µFrame S-mask) */
#define QH_EPCAPS_SSMASK_MASK (0xff << QH_EPCAPS_SSMASK_SHIFT)
+# define QH_EPCAPS_SSMASK(n) ((n) << QH_EPCAPS_SSMASK_SHIFT)
#define QH_EPCAPS_SCMASK_SHIFT (8) /* Bitx 8-15: Split Completion Mask (µFrame C-Mask) */
#define QH_EPCAPS_SCMASK_MASK (0xff << QH_EPCAPS_SCMASK_SHIFT)
+# define QH_EPCAPS_SCMASK(n) ((n) << QH_EPCAPS_SCMASK_SHIFT)
#define QH_EPCAPS_HUBADDR_SHIFT (16) /* Bitx 16-22: Hub Address */
#define QH_EPCAPS_HUBADDR_MASK (0x7f << QH_EPCAPS_HUBADDR_SHIFT)
+# define QH_EPCAPS_HUBADDR(n) ((n) << QH_EPCAPS_HUBADDR_SHIFT)
#define QH_EPCAPS_PORT_SHIFT (23) /* Bit 23-29: Port Number */
#define QH_EPCAPS_PORT_MASK (0x7f << QH_EPCAPS_PORT_SHIFT)
+# define QH_EPCAPS_PORT(n) ((n) << QH_EPCAPS_PORT_SHIFT)
#define QH_EPCAPS_MULT_SHIFT (30) /* Bit 30-31: High-Bandwidth Pipe Multiplier */
#define QH_EPCAPS_MULT_MASK (3 << QH_EPCAPS_MULT_SHIFT)
+# define QH_EPCAPS_MULT(n) ((n) << QH_EPCAPS_MULT_SHIFT)
/* Current qTD Link Pointer. Table 3-21 */