SAMA5 EHCI: Hardware initialization logic

This commit is contained in:
Gregory Nutt 2013-08-21 13:45:54 -06:00
parent b1864a995e
commit a5eb830544
2 changed files with 213 additions and 16 deletions

View File

@ -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.
*/
/* 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.
*/

View File

@ -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 */