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
+70 -2
View File
@@ -370,7 +370,7 @@ config SAMA5_OHCI_REGDEBUG
default n default n
depends on DEBUG depends on DEBUG
endif # OHCI endif # SAMA5_OHCI
config SAMA5_EHCI config SAMA5_EHCI
bool "High speed EHCI support" bool "High speed EHCI support"
@@ -408,7 +408,75 @@ config SAMA5_EHCI_REGDEBUG
default n default n
depends on DEBUG 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 endmenu # USB High Speed Host driver option
endif # SAMA5_UHPHS endif # SAMA5_UHPHS
+53 -10
View File
@@ -60,6 +60,7 @@
#include "sam_periphclks.h" #include "sam_periphclks.h"
#include "sam_memories.h" #include "sam_memories.h"
#include "sam_usbhost.h" #include "sam_usbhost.h"
#include "chip/sam_sfr.h"
#include "chip/sam_ehci.h" #include "chip/sam_ehci.h"
#ifdef CONFIG_SAMA5_EHCI #ifdef CONFIG_SAMA5_EHCI
@@ -110,6 +111,18 @@
#undef CONFIG_USBHOST_ISOC_DISABLE #undef CONFIG_USBHOST_ISOC_DISABLE
#define CONFIG_USBHOST_ISOC_DISABLE 1 #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 **************************************************/ /* Driver-private Definitions **************************************************/
/* This is the set of interrupts handled by this driver */ /* 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? * 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 */ /* 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)); (uintptr_t)&qtd->hw + sizeof(struct ehci_qtd_s));
sam_qtd_print(qtd); 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; **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 */ /* Release this QH by returning it to the free list */
sam_qtd_free(qtd); 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; **bp = qh->hw.hlp;
cp15_coherent_dcache((uintptr_t)*bp, (uintptr_t)*bp + sizeof(uint32_t));
/* Check for errors, update the data toggle */ /* 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 = sam_getreg((volatile uint32_t *)SAM_PMC_SCER);
regval |= PMC_UHP; regval |= PMC_UHP;
sam_putreg(regval, (volatile uint32_t *)SAM_PMC_SCER); 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); 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 * dedicated function
*/ */
@@ -3631,7 +3666,15 @@ FAR struct usbhost_connection_s *sam_ehci_initialize(int controller)
* mode. * 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); up_mdelay(50);
/* If there is a USB device in the slot at power up, then we will not /* If there is a USB device in the slot at power up, then we will not
+38 -8
View File
@@ -117,6 +117,18 @@
#define SAM_BUFALLOC (CONFIG_SAMA5_OHCI_TDBUFFERS * CONFIG_SAMA5_OHCI_TDBUFSIZE) #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 */ /* Debug */
#ifndef CONFIG_DEBUG #ifndef CONFIG_DEBUG
@@ -3013,20 +3025,30 @@ FAR struct usbhost_connection_s *sam_ohci_initialize(int controller)
regval = getreg32(SAM_PMC_SCER); regval = getreg32(SAM_PMC_SCER);
regval |= PMC_UHP; regval |= PMC_UHP;
putreg32(regval, SAM_PMC_SCER); putreg32(regval, SAM_PMC_SCER);
irqrestore(flags);
/* Make all three ports usable. Zero is the reset value and holds the /* "One transceiver is shared with the USB High Speed Device (port A). The
* ports in reset. * selection between Host Port A and USB Device is controlled by the UDPHS
* REVISIT: This will have to change in future. Should be a configuration * enable bit (EN_UDPHS) located in the UDPHS_CTRL control register."
* setting *
* 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 = 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); putreg32(regval, SAM_SFR_OHCIICR);
irqrestore(flags); 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 * dedicated function
*/ */
@@ -3140,7 +3162,15 @@ FAR struct usbhost_connection_s *sam_ohci_initialize(int controller)
* mode. * 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); up_mdelay(50);
/* If there is a USB device in the slot at power up, then we will not /* If there is a USB device in the slot at power up, then we will not
+11 -4
View File
@@ -54,6 +54,14 @@
#define SAM_EHCI_IFACE 0 #define SAM_EHCI_IFACE 0
#define SAM_OHCI_IFACE 1 #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 * 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 * each platform that implements the OHCI or EHCI host interface
* *
* Input Parameters: * Input Parameters:
* iface - Selects USB host interface: * rhport - Selects root hub port to be powered host interface. See SAM_RHPORT_*
* 0 = EHCI * definitions above.
* 1 = OHCI
* enable - true: enable VBUS power; false: disable VBUS power * enable - true: enable VBUS power; false: disable VBUS power
* *
* Returned Value: * 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 #undef EXTERN
#if defined(__cplusplus) #if defined(__cplusplus)
+70 -39
View File
@@ -83,6 +83,12 @@ static struct usbhost_connection_s *g_ohciconn;
static struct usbhost_connection_s *g_ehciconn; static struct usbhost_connection_s *g_ehciconn;
#endif #endif
/* Overcurrent interrupt handler */
#if defined(HAVE_USBHOST) && defined(CONFIG_SAMA5_PIOD_IRQ)
static xcpt_t g_ochandler;
#endif
/************************************************************************************ /************************************************************************************
* Private Functions * Private Functions
************************************************************************************/ ************************************************************************************/
@@ -229,7 +235,7 @@ static int ehci_waiter(int argc, char *argv[])
void weak_function sam_usbinitialize(void) void weak_function sam_usbinitialize(void)
{ {
#if 0 #ifdef HAVE_USBDEV
/* Configure Port A to support the USB device function */ /* Configure Port A to support the USB device function */
sam_configpio(PIO_USBA_VBUS_SENSE); /* VBUS sense */ 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 */ /* TODO: Configure an interrupt on VBUS sense */
#endif #endif
#ifdef CONFIG_SAMA5_OHCI #ifdef HAVE_USBHOST
/* Configure Port C to support the USB OHCI function */ #ifndef HAVE_USBDEV
/* Configure Port A to support the USB OHCI/EHCI function only if USB
sam_configpio(PIO_USBC_VBUS_ENABLE); /* VBUS enable, initially OFF */ * device is not also supported.
*/
sam_configpio(PIO_USBA_VBUS_ENABLE); /* VBUS enable, initially OFF */
#endif #endif
#ifdef CONFIG_SAMA5_EHCI /* Configure Ports B and C to support the USB OHCI/EHCI function */
/* Configure Port B to support the USB OHCI function */
sam_configpio(PIO_USBB_VBUS_ENABLE); /* VBUS enable, initially OFF */ 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 */ /* Configure Port B/C VBUS overrcurrent detection */
sam_configpio(PIO_USBBC_VBUS_OVERCURRENT); /* VBUS overcurrent */ sam_configpio(PIO_USBBC_VBUS_OVERCURRENT); /* VBUS overcurrent */
@@ -271,7 +276,7 @@ void weak_function sam_usbinitialize(void)
#if HAVE_USBHOST #if HAVE_USBHOST
int sam_usbhost_initialize(void) int sam_usbhost_initialize(void)
{ {
int pid; pid_t pid;
int ret; int ret;
/* First, register all of the class drivers needed to support the drivers /* 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 * each platform that implements the OHCI or EHCI host interface
* *
* Input Parameters: * Input Parameters:
* iface - Selects USB host interface: * rhport - Selects root hub port to be powered host interface. See SAM_RHPORT_*
* 0 = EHCI (Port B) * definitions above.
* 1 = OHCI (Port C)
* enable - true: enable VBUS power; false: disable VBUS power * enable - true: enable VBUS power; false: disable VBUS power
* *
* Returned Value: * Returned Value:
@@ -349,34 +353,37 @@ int sam_usbhost_initialize(void)
***********************************************************************************/ ***********************************************************************************/
#if HAVE_USBHOST #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 /* Pick the PIO configuration associated with the selected root hub port */
if (iface == SAM_OHCI_IFACE)
switch (rhport)
{ {
uvdbg("OHCI: iface %d enable %d\n", iface, enable); case SAM_RHPORT1:
pinset = PIO_USBC_VBUS_ENABLE; #ifdef HAVE_USBDEV
} udbg("ERROR: RHPort1 is not available in this configuration\n");
else return;
#else
pinset = PIO_USBA_VBUS_ENABLE;
break;
#endif #endif
#ifdef CONFIG_SAMA5_EHCI case SAM_RHPORT2:
if (iface == SAM_EHCI_IFACE)
{
uvdbg("EHCI: iface %d enable %d\n", iface, enable);
pinset = PIO_USBB_VBUS_ENABLE; pinset = PIO_USBB_VBUS_ENABLE;
} break;
else
#endif
{ case SAM_RHPORT3:
udbg("ERROR: Unsupported iface %d\n", iface); pinset = PIO_USBC_VBUS_ENABLE;
return; break;
}
default:
udbg("ERROR: RHPort%d is not supported\n", rhport+1);
return;
}
/* Then enable or disable VBUS power */ /* 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 */ /* Enable the Power Switch by driving the enable pin low */
sam_piowrite(pinset, false); sam_piowrite(pinset, false);
} }
else else
{ {
/* Disable the Power Switch by driving the enable pin high */ /* Disable the Power Switch by driving the enable pin high */
sam_piowrite(pinset, false); sam_piowrite(pinset, true);
} }
} }
#endif #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 * Setup to receive an interrupt-level callback if an overcurrent condition is
* detected. * 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: * Input paramter:
* handler - New overcurrent interrupt handler * handler - New overcurrent interrupt handler
* *
@@ -413,12 +423,33 @@ void sam_usbhost_vbusdrive(int iface, bool enable)
#if HAVE_USBHOST #if HAVE_USBHOST
xcpt_t sam_setup_overcurrent(xcpt_t handler) 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 #if defined(CONFIG_SAMA5_PIOD_IRQ)
* both EHCI and OHCI drivers when this error occurs. 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; return NULL;
#endif
} }
#endif #endif
+4 -7
View File
@@ -58,6 +58,7 @@
#define HAVE_HSMCI_MTD 1 #define HAVE_HSMCI_MTD 1
#define HAVE_AT25_MTD 1 #define HAVE_AT25_MTD 1
#define HAVE_USBHOST 1 #define HAVE_USBHOST 1
#define HAVE_USBDEV 1
/* HSMCI */ /* HSMCI */
/* Can't support MMC/SD if the card interface(s) are not enable */ /* Can't support MMC/SD if the card interface(s) are not enable */
@@ -120,12 +121,13 @@
#endif #endif
#if !defined(CONFIG_SAMA5_UDPHS) #if !defined(CONFIG_SAMA5_UDPHS)
# undef HAVE_USBDEV
#endif #endif
/* CONFIG_USBDEV and CONFIG_USBHOST must also be defined */ /* CONFIG_USBDEV and CONFIG_USBHOST must also be defined */
#if defined(CONFIG_USBDEV) #if !defined(CONFIG_USBDEV)
#else # undef HAVE_USBDEV
#endif #endif
#if defined(CONFIG_USBHOST) #if defined(CONFIG_USBHOST)
@@ -137,11 +139,6 @@
# undef CONFIG_SAMA5_EHCI # undef CONFIG_SAMA5_EHCI
#endif #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) #if !defined(CONFIG_SAMA5_OHCI) && !defined(CONFIG_SAMA5_EHCI)
# undef HAVE_USBHOST # undef HAVE_USBHOST
#endif #endif