From 9a109ba4ba93d10944ef5684de31fffede613b17 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Fri, 23 Aug 2013 10:58:30 -0600 Subject: [PATCH] SAMA5: Add support for sharing ports when both OHCI and EHCI are enabled --- arch/arm/src/sama5/Kconfig | 72 ++++++++++++++++- arch/arm/src/sama5/sam_ehci.c | 63 ++++++++++++--- arch/arm/src/sama5/sam_ohci.c | 46 +++++++++-- arch/arm/src/sama5/sam_usbhost.h | 15 +++- configs/sama5d3x-ek/src/sam_usb.c | 109 +++++++++++++++++--------- configs/sama5d3x-ek/src/sama5d3x-ek.h | 11 +-- 6 files changed, 246 insertions(+), 70 deletions(-) diff --git a/arch/arm/src/sama5/Kconfig b/arch/arm/src/sama5/Kconfig index aadf40224a..ce16d974a6 100644 --- a/arch/arm/src/sama5/Kconfig +++ b/arch/arm/src/sama5/Kconfig @@ -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 diff --git a/arch/arm/src/sama5/sam_ehci.c b/arch/arm/src/sama5/sam_ehci.c index a08025b6b3..479d03bb97 100755 --- a/arch/arm/src/sama5/sam_ehci.c +++ b/arch/arm/src/sama5/sam_ehci.c @@ -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 diff --git a/arch/arm/src/sama5/sam_ohci.c b/arch/arm/src/sama5/sam_ohci.c index ee2160fd18..25c29302af 100644 --- a/arch/arm/src/sama5/sam_ohci.c +++ b/arch/arm/src/sama5/sam_ohci.c @@ -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 diff --git a/arch/arm/src/sama5/sam_usbhost.h b/arch/arm/src/sama5/sam_usbhost.h index 47bd7fe153..0a4494a455 100644 --- a/arch/arm/src/sama5/sam_usbhost.h +++ b/arch/arm/src/sama5/sam_usbhost.h @@ -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) diff --git a/configs/sama5d3x-ek/src/sam_usb.c b/configs/sama5d3x-ek/src/sam_usb.c index a4c4c16cc5..d6b41c2b0c 100644 --- a/configs/sama5d3x-ek/src/sam_usb.c +++ b/configs/sama5d3x-ek/src/sam_usb.c @@ -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 diff --git a/configs/sama5d3x-ek/src/sama5d3x-ek.h b/configs/sama5d3x-ek/src/sama5d3x-ek.h index 9f91aee92b..dd77e0f663 100644 --- a/configs/sama5d3x-ek/src/sama5d3x-ek.h +++ b/configs/sama5d3x-ek/src/sama5d3x-ek.h @@ -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