diff options
author | Gregory Nutt <gnutt@nuttx.org> | 2013-08-21 13:45:54 -0600 |
---|---|---|
committer | Gregory Nutt <gnutt@nuttx.org> | 2013-08-21 13:45:54 -0600 |
commit | efe1cfc9beaec0ee4259100ef44bbe6b912b4b3c (patch) | |
tree | 147a0168acb2608539cb8ac86f2f4d87a6a40ab0 | |
parent | bd328a7de0472a500a5aed2b6b6642ba946f5aed (diff) | |
download | nuttx-efe1cfc9beaec0ee4259100ef44bbe6b912b4b3c.tar.gz nuttx-efe1cfc9beaec0ee4259100ef44bbe6b912b4b3c.tar.bz2 nuttx-efe1cfc9beaec0ee4259100ef44bbe6b912b4b3c.zip |
SAMA5 EHCI: Hardware initialization logic
-rwxr-xr-x | nuttx/arch/arm/src/sama5/sam_ehci.c | 214 | ||||
-rwxr-xr-x | nuttx/include/nuttx/usb/ehci.h | 15 |
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 */ |