SAMA5 EHCI: Hardware initialization logic
This commit is contained in:
parent
b1864a995e
commit
a5eb830544
@ -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.
|
||||
*/
|
||||
|
@ -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 */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user