SAMA5: Add support for sharing ports when both OHCI and EHCI are enabled

This commit is contained in:
Gregory Nutt 2013-08-23 10:58:30 -06:00
parent 2b3fd9e9c3
commit 9a109ba4ba
6 changed files with 246 additions and 70 deletions

View File

@ -370,7 +370,7 @@ config SAMA5_OHCI_REGDEBUG
default n
depends on DEBUG
endif # OHCI
endif # SAMA5_OHCI
config SAMA5_EHCI
bool "High speed EHCI support"
@ -408,7 +408,75 @@ config SAMA5_EHCI_REGDEBUG
default n
depends on DEBUG
endif # EHCI
endif # SAMA5_EHCI
if SAMA5_OHCI && SAMA5_EHCI
config SAMA5_OHCI_RHPORT1
bool "Use Port A for OHCI"
default n
depends on !SAMA5_UDPHS
config SAMA5_OHCI_RHPORT2
bool "Use Port B for OHCI"
default n
config SAMA5_OHCI_RHPORT3
bool "Use Port C for OHCI"
default y
config SAMA5_EHCI_RHPORT1
bool
default y if !SAMA5_OHCI_RHPORT1
default n if SAMA5_OHCI_RHPORT1
depends on !SAMA5_UDPHS
config SAMA5_EHCI_RHPORT2
bool
default y if !SAMA5_OHCI_RHPORT2
default n if SAMA5_OHCI_RHPORT2
config SAMA5_EHCI_RHPORT3
bool
default y if !SAMA5_OHCI_RHPORT3
default n if SAMA5_OHCI_RHPORT3
endif # SAMA5_OHCI && SAMA5_EHCI
if SAMA5_OHCI && !SAMA5_EHCI
config SAMA5_OHCI_RHPORT1
bool
default y
depends on !SAMA5_UDPHS
config SAMA5_OHCI_RHPORT2
bool
default y
config SAMA5_OHCI_RHPORT3
bool
default y
endif # SAMA5_OHCI && !SAMA5_EHCI
if !SAMA5_OHCI && SAMA5_EHCI
config SAMA5_EHCI_RHPORT1
bool
default y
depends on !SAMA5_UDPHS
config SAMA5_EHCI_RHPORT2
bool
default y
config SAMA5_EHCI_RHPORT3
bool
default y
endif # !SAMA5_OHCI && SAMA5_EHCI
endmenu # USB High Speed Host driver option
endif # SAMA5_UHPHS

View File

@ -60,6 +60,7 @@
#include "sam_periphclks.h"
#include "sam_memories.h"
#include "sam_usbhost.h"
#include "chip/sam_sfr.h"
#include "chip/sam_ehci.h"
#ifdef CONFIG_SAMA5_EHCI
@ -110,6 +111,18 @@
#undef CONFIG_USBHOST_ISOC_DISABLE
#define CONFIG_USBHOST_ISOC_DISABLE 1
/* If UDPHS is enabled, then don't use port A */
#ifdef CONFIG_SAMA5_UDPHS
# undef CONFIG_SAMA5_EHCI_RHPORT1
#endif
/* For now, suppress use of PORTA in any event. I use that for SAM-BA and
* would prefer that the board not try to drive VBUS on that port!
*/
#undef CONFIG_SAMA5_EHCI_RHPORT1
/* Driver-private Definitions **************************************************/
/* This is the set of interrupts handled by this driver */
@ -874,7 +887,7 @@ static int sam_qh_foreach(struct sam_qh_s *qh, uint32_t **bp, foreach_qh_t handl
* the end of the asynchronous queue?
*/
else if (sam_virtramaddr(physaddr & QH_HLP_MASK) == &g_asynchead)
else if (sam_virtramaddr(physaddr & QH_HLP_MASK) == (uintptr_t)&g_asynchead)
{
/* That will also terminate the loop */
@ -1973,16 +1986,16 @@ static int sam_qtd_ioccheck(struct sam_qtd_s *qtd, uint32_t **bp, void *arg)
(uintptr_t)&qtd->hw + sizeof(struct ehci_qtd_s));
sam_qtd_print(qtd);
/* Remove the qTD from the list */
/* Remove the qTD from the list
*
* NOTE that we don't check if the qTD is active nor do we check if there
* are any errors reported in the qTD. If the transfer halted due to
* an error, then qTDs in the list after the error qTD will still appear
* to be active.
*/
**bp = qtd->hw.nqp;
/* NOTE that we don't check if the qTD is active nor do we check if there
* are any errors reported in the qTD. If the transfer halted due to
* an error, then I am not sure if we can believe this information anyway.
* The only sure place to check for errors in in the QH overlay.
*/
/* Release this QH by returning it to the free list */
sam_qtd_free(qtd);
@ -2064,6 +2077,7 @@ static int sam_qh_ioccheck(struct sam_qh_s *qh, uint32_t **bp, void *arg)
*/
**bp = qh->hw.hlp;
cp15_coherent_dcache((uintptr_t)*bp, (uintptr_t)*bp + sizeof(uint32_t));
/* Check for errors, update the data toggle */
@ -3377,9 +3391,30 @@ FAR struct usbhost_connection_s *sam_ehci_initialize(int controller)
regval = sam_getreg((volatile uint32_t *)SAM_PMC_SCER);
regval |= PMC_UHP;
sam_putreg(regval, (volatile uint32_t *)SAM_PMC_SCER);
/* "One transceiver is shared with the USB High Speed Device (port A). The
* selection between Host Port A and USB Device is controlled by the UDPHS
* enable bit (EN_UDPHS) located in the UDPHS_CTRL control register."
*
* Make all three ports usable for EHCI unless the high speed device is
* enabled; then let the device manage port zero. Zero is the reset
* value for all ports; one makes the corresponding port available to OHCI.
*/
regval = getreg32(SAM_SFR_OHCIICR);
#ifdef CONFIG_SAMA5_EHCI_RHPORT1
regval &= ~SFR_OHCIICR_RES1;
#endif
#ifdef CONFIG_SAMA5_EHCI_RHPORT2
regval &= ~SFR_OHCIICR_RES1;
#endif
#ifdef CONFIG_SAMA5_EHCI_RHPORT3
regval &= ~SFR_OHCIICR_RES2;
#endif
putreg32(regval, SAM_SFR_OHCIICR);
irqrestore(flags);
/* Note that no pin pinconfiguration is required. All USB HS pins have
/* Note that no pin configuration is required. All USB HS pins have
* dedicated function
*/
@ -3631,7 +3666,15 @@ FAR struct usbhost_connection_s *sam_ehci_initialize(int controller)
* mode.
*/
sam_usbhost_vbusdrive(SAM_EHCI_IFACE, true);
#ifndef CONFIG_SAMA5_EHCI_RHPORT1
sam_usbhost_vbusdrive(SAM_RHPORT1, true);
#endif
#ifndef CONFIG_SAMA5_EHCI_RHPORT2
sam_usbhost_vbusdrive(SAM_RHPORT2, true);
#endif
#ifndef CONFIG_SAMA5_EHCI_RHPORT3
sam_usbhost_vbusdrive(SAM_RHPORT3, true);
#endif
up_mdelay(50);
/* If there is a USB device in the slot at power up, then we will not

View File

@ -117,6 +117,18 @@
#define SAM_BUFALLOC (CONFIG_SAMA5_OHCI_TDBUFFERS * CONFIG_SAMA5_OHCI_TDBUFSIZE)
/* If UDPHS is enabled, then don't use port A */
#ifdef CONFIG_SAMA5_UDPHS
# undef CONFIG_SAMA5_OHCI_RHPORT1
#endif
/* For now, suppress use of PORTA in any event. I use that for SAM-BA and
* would prefer that the board not try to drive VBUS on that port!
*/
#undef CONFIG_SAMA5_OHCI_RHPORT1
/* Debug */
#ifndef CONFIG_DEBUG
@ -3013,20 +3025,30 @@ FAR struct usbhost_connection_s *sam_ohci_initialize(int controller)
regval = getreg32(SAM_PMC_SCER);
regval |= PMC_UHP;
putreg32(regval, SAM_PMC_SCER);
irqrestore(flags);
/* Make all three ports usable. Zero is the reset value and holds the
* ports in reset.
* REVISIT: This will have to change in future. Should be a configuration
* setting
/* "One transceiver is shared with the USB High Speed Device (port A). The
* selection between Host Port A and USB Device is controlled by the UDPHS
* enable bit (EN_UDPHS) located in the UDPHS_CTRL control register."
*
* Make all three ports usable for OHCI unless the high speed device is
* enabled; then let the device manage port zero. Zero is the reset
* value for all ports; one makes the corresponding port available to OHCI.
*/
regval = getreg32(SAM_SFR_OHCIICR);
regval |= (SFR_OHCIICR_RES0 | SFR_OHCIICR_RES1 | SFR_OHCIICR_RES2);
#ifdef CONFIG_SAMA5_OHCI_RHPORT1
regval |= SFR_OHCIICR_RES1;
#endif
#ifdef CONFIG_SAMA5_OHCI_RHPORT2
regval |= SFR_OHCIICR_RES1;
#endif
#ifdef CONFIG_SAMA5_OHCI_RHPORT3
regval |= SFR_OHCIICR_RES2;
#endif
putreg32(regval, SAM_SFR_OHCIICR);
irqrestore(flags);
/* Note that no pin pinconfiguration is required. All USB HS pins have
/* Note that no pin configuration is required. All USB HS pins have
* dedicated function
*/
@ -3140,7 +3162,15 @@ FAR struct usbhost_connection_s *sam_ohci_initialize(int controller)
* mode.
*/
sam_usbhost_vbusdrive(SAM_OHCI_IFACE, true);
#ifndef CONFIG_SAMA5_OHCI_RHPORT1
sam_usbhost_vbusdrive(SAM_RHPORT1, true);
#endif
#ifndef CONFIG_SAMA5_OHCI_RHPORT2
sam_usbhost_vbusdrive(SAM_RHPORT2, true);
#endif
#ifndef CONFIG_SAMA5_OHCI_RHPORT3
sam_usbhost_vbusdrive(SAM_RHPORT3, true);
#endif
up_mdelay(50);
/* If there is a USB device in the slot at power up, then we will not

View File

@ -54,6 +54,14 @@
#define SAM_EHCI_IFACE 0
#define SAM_OHCI_IFACE 1
/* This is the interface argument for call outs to board-specific functions which
* need to know which root hub port is being used.
*/
#define SAM_RHPORT1 0
#define SAM_RHPORT2 1
#define SAM_RHPORT3 2
/************************************************************************************
* Public Types
************************************************************************************/
@ -149,9 +157,8 @@ FAR struct usbhost_connection_s *sam_ehci_initialize(int controller);
* each platform that implements the OHCI or EHCI host interface
*
* Input Parameters:
* iface - Selects USB host interface:
* 0 = EHCI
* 1 = OHCI
* rhport - Selects root hub port to be powered host interface. See SAM_RHPORT_*
* definitions above.
* enable - true: enable VBUS power; false: disable VBUS power
*
* Returned Value:
@ -159,7 +166,7 @@ FAR struct usbhost_connection_s *sam_ehci_initialize(int controller);
*
***********************************************************************************/
void sam_usbhost_vbusdrive(int iface, bool enable);
void sam_usbhost_vbusdrive(int rhport, bool enable);
#undef EXTERN
#if defined(__cplusplus)

View File

@ -83,6 +83,12 @@ static struct usbhost_connection_s *g_ohciconn;
static struct usbhost_connection_s *g_ehciconn;
#endif
/* Overcurrent interrupt handler */
#if defined(HAVE_USBHOST) && defined(CONFIG_SAMA5_PIOD_IRQ)
static xcpt_t g_ochandler;
#endif
/************************************************************************************
* Private Functions
************************************************************************************/
@ -229,7 +235,7 @@ static int ehci_waiter(int argc, char *argv[])
void weak_function sam_usbinitialize(void)
{
#if 0
#ifdef HAVE_USBDEV
/* Configure Port A to support the USB device function */
sam_configpio(PIO_USBA_VBUS_SENSE); /* VBUS sense */
@ -237,21 +243,20 @@ void weak_function sam_usbinitialize(void)
/* TODO: Configure an interrupt on VBUS sense */
#endif
#ifdef CONFIG_SAMA5_OHCI
/* Configure Port C to support the USB OHCI function */
sam_configpio(PIO_USBC_VBUS_ENABLE); /* VBUS enable, initially OFF */
#ifdef HAVE_USBHOST
#ifndef HAVE_USBDEV
/* Configure Port A to support the USB OHCI/EHCI function only if USB
* device is not also supported.
*/
sam_configpio(PIO_USBA_VBUS_ENABLE); /* VBUS enable, initially OFF */
#endif
#ifdef CONFIG_SAMA5_EHCI
/* Configure Port B to support the USB OHCI function */
/* Configure Ports B and C to support the USB OHCI/EHCI function */
sam_configpio(PIO_USBB_VBUS_ENABLE); /* VBUS enable, initially OFF */
sam_configpio(PIO_USBC_VBUS_ENABLE); /* VBUS enable, initially OFF */
#endif
#if defined(CONFIG_SAMA5_OHCI) || defined(CONFIG_SAMA5_EHCI)
/* Configure Port B/C VBUS overrcurrent detection */
sam_configpio(PIO_USBBC_VBUS_OVERCURRENT); /* VBUS overcurrent */
@ -271,7 +276,7 @@ void weak_function sam_usbinitialize(void)
#if HAVE_USBHOST
int sam_usbhost_initialize(void)
{
int pid;
pid_t pid;
int ret;
/* First, register all of the class drivers needed to support the drivers
@ -338,9 +343,8 @@ int sam_usbhost_initialize(void)
* each platform that implements the OHCI or EHCI host interface
*
* Input Parameters:
* iface - Selects USB host interface:
* 0 = EHCI (Port B)
* 1 = OHCI (Port C)
* rhport - Selects root hub port to be powered host interface. See SAM_RHPORT_*
* definitions above.
* enable - true: enable VBUS power; false: disable VBUS power
*
* Returned Value:
@ -349,34 +353,37 @@ int sam_usbhost_initialize(void)
***********************************************************************************/
#if HAVE_USBHOST
void sam_usbhost_vbusdrive(int iface, bool enable)
void sam_usbhost_vbusdrive(int rhport, bool enable)
{
pio_pinset_t pinset;
pio_pinset_t pinset = 0;
/* Pick the PIO associated with the OHCI or EHCI interface */
uvdbg("RHPort%d: enable=%d\n", rhport+1, enable);
#ifdef CONFIG_SAMA5_OHCI
if (iface == SAM_OHCI_IFACE)
/* Pick the PIO configuration associated with the selected root hub port */
switch (rhport)
{
uvdbg("OHCI: iface %d enable %d\n", iface, enable);
pinset = PIO_USBC_VBUS_ENABLE;
}
else
case SAM_RHPORT1:
#ifdef HAVE_USBDEV
udbg("ERROR: RHPort1 is not available in this configuration\n");
return;
#else
pinset = PIO_USBA_VBUS_ENABLE;
break;
#endif
#ifdef CONFIG_SAMA5_EHCI
if (iface == SAM_EHCI_IFACE)
{
uvdbg("EHCI: iface %d enable %d\n", iface, enable);
case SAM_RHPORT2:
pinset = PIO_USBB_VBUS_ENABLE;
}
else
#endif
break;
{
udbg("ERROR: Unsupported iface %d\n", iface);
return;
}
case SAM_RHPORT3:
pinset = PIO_USBC_VBUS_ENABLE;
break;
default:
udbg("ERROR: RHPort%d is not supported\n", rhport+1);
return;
}
/* Then enable or disable VBUS power */
@ -384,13 +391,13 @@ void sam_usbhost_vbusdrive(int iface, bool enable)
{
/* Enable the Power Switch by driving the enable pin low */
sam_piowrite(pinset, false);
sam_piowrite(pinset, false);
}
else
{
/* Disable the Power Switch by driving the enable pin high */
sam_piowrite(pinset, false);
sam_piowrite(pinset, true);
}
}
#endif
@ -402,6 +409,9 @@ void sam_usbhost_vbusdrive(int iface, bool enable)
* Setup to receive an interrupt-level callback if an overcurrent condition is
* detected.
*
* REVISIT: Since this is a common signal, we will need to come up with some way
* to inform both EHCI and OHCI drivers when this error occurs.
*
* Input paramter:
* handler - New overcurrent interrupt handler
*
@ -413,12 +423,33 @@ void sam_usbhost_vbusdrive(int iface, bool enable)
#if HAVE_USBHOST
xcpt_t sam_setup_overcurrent(xcpt_t handler)
{
/* Since this is a common signal, we will need to come up with some way to inform
* both EHCI and OHCI drivers when this error occurs.
#if defined(CONFIG_SAMA5_PIOD_IRQ)
xcpt_t oldhandler;
irqstate_t flags;
/* Disable interrupts until we are done. This guarantees that the
* following operations are atomic.
*/
# warning Missing logic
flags = irqsave();
/* Get the old button interrupt handler and save the new one */
oldhandler = *g_ochandler;
*g_ochandler = handler;
/* Configure the interrupt */
sam_pioirq(IRQ_USBBC_VBUS_OVERCURRENT);
(void)irq_attach(IRQ_USBBC_VBUS_OVERCURRENT, handler);
sam_pioirqenable(IRQ_USBBC_VBUS_OVERCURRENT);
/* Return the old button handler (so that it can be restored) */
return oldhandler;
#else
return NULL;
#endif
}
#endif

View File

@ -58,6 +58,7 @@
#define HAVE_HSMCI_MTD 1
#define HAVE_AT25_MTD 1
#define HAVE_USBHOST 1
#define HAVE_USBDEV 1
/* HSMCI */
/* Can't support MMC/SD if the card interface(s) are not enable */
@ -120,12 +121,13 @@
#endif
#if !defined(CONFIG_SAMA5_UDPHS)
# undef HAVE_USBDEV
#endif
/* CONFIG_USBDEV and CONFIG_USBHOST must also be defined */
#if defined(CONFIG_USBDEV)
#else
#if !defined(CONFIG_USBDEV)
# undef HAVE_USBDEV
#endif
#if defined(CONFIG_USBHOST)
@ -137,11 +139,6 @@
# undef CONFIG_SAMA5_EHCI
#endif
#if defined(CONFIG_SAMA5_OHCI) && defined(CONFIG_SAMA5_EHCI)
# warning Both CONFIG_SAMA5_OHCI and CONFIG_SAMA5_EHCI are defined
# undef CONFIG_SAMA5_EHCI
#endif
#if !defined(CONFIG_SAMA5_OHCI) && !defined(CONFIG_SAMA5_EHCI)
# undef HAVE_USBHOST
#endif