diff options
author | Gregory Nutt <gnutt@nuttx.org> | 2013-08-24 11:34:24 -0600 |
---|---|---|
committer | Gregory Nutt <gnutt@nuttx.org> | 2013-08-24 11:34:24 -0600 |
commit | cb9ed2733015b48819a34ed4357e5e916216636a (patch) | |
tree | 53161b08fbe24f81490a1f1760bb971eaad7eeaa | |
parent | e2c9e4545e08c93482a36dcaebd1819a00412ad8 (diff) | |
download | nuttx-cb9ed2733015b48819a34ed4357e5e916216636a.tar.gz nuttx-cb9ed2733015b48819a34ed4357e5e916216636a.tar.bz2 nuttx-cb9ed2733015b48819a34ed4357e5e916216636a.zip |
SAMA5: EHCI now handles low- and full-speed connections by giving them to OHCI; OHCI now uses the work queue to defer interrupt processing; If both OHCI and EHCI are enabled, EHCI is the master of the UHPHS interrupt
-rw-r--r-- | nuttx/ChangeLog | 9 | ||||
-rw-r--r-- | nuttx/arch/arm/src/sama5/Kconfig | 71 | ||||
-rw-r--r-- | nuttx/arch/arm/src/sama5/chip/sam_sfr.h | 7 | ||||
-rwxr-xr-x | nuttx/arch/arm/src/sama5/sam_ehci.c | 146 | ||||
-rw-r--r-- | nuttx/arch/arm/src/sama5/sam_ohci.c | 263 | ||||
-rw-r--r-- | nuttx/arch/arm/src/sama5/sam_usbhost.h | 14 | ||||
-rw-r--r-- | nuttx/configs/sama5d3x-ek/README.txt | 11 | ||||
-rw-r--r-- | nuttx/configs/sama5d3x-ek/include/board.h | 13 | ||||
-rw-r--r-- | nuttx/configs/sama5d3x-ek/src/sam_usb.c | 69 |
9 files changed, 412 insertions, 191 deletions
diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog index ae0124d4e..f919549fa 100644 --- a/nuttx/ChangeLog +++ b/nuttx/ChangeLog @@ -5433,4 +5433,11 @@ (2013-8-23). * arch/arm/src/lpc17xx/lpc17_usbhost.h: Fix #endif with missing #if condition. Reported by Andrew Bradford (2013-8-23). - + * nuttx/arch/arm/src/sama5/sam_ehci.c: Now handles low- and full-speed + connections by giving the port to the OHCI driver (2013-8-24). + * nuttx/arch/arm/src/sama5/sam_ohci.c: Now uses the work queue to + defer interrupt processing (2013-8-24). + * nuttx/arch/arm/src/sama5/sam_ohci.c and nuttx/arch/arm/src/sama5/sam_ehci.c: + EHCI is now the common interrupt "master." It will receive all UHPHS + interrupts and route the interrupt event to both the OHCI and EHCI + logic (2013-8-24). diff --git a/nuttx/arch/arm/src/sama5/Kconfig b/nuttx/arch/arm/src/sama5/Kconfig index ce16d974a..a492fd806 100644 --- a/nuttx/arch/arm/src/sama5/Kconfig +++ b/nuttx/arch/arm/src/sama5/Kconfig @@ -339,7 +339,7 @@ if SAMA5_UHPHS menu "USB High Speed Host device driver options" config SAMA5_OHCI - bool "Full speed OHCI support" + bool "Full/low speed OHCI support" default n ---help--- Build support for the SAMA5 USB full speed Open Host Controller @@ -377,7 +377,8 @@ config SAMA5_EHCI default n ---help--- Build support for the SAMA5 USB high speed Enhanced Host Controller - Interface (EHCI). + Interface (EHCI). If low/full speed is needed too, then you must + also enable the OHCI controller. if SAMA5_EHCI @@ -410,72 +411,22 @@ config SAMA5_EHCI_REGDEBUG endif # SAMA5_EHCI -if SAMA5_OHCI && 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 +config SAMA5_UHPHS_RHPORT1 + bool "Use Port A" default y depends on !SAMA5_UDPHS -config SAMA5_EHCI_RHPORT2 - bool +config SAMA5_UHPHS_RHPORT2 + bool "Use Port B" default y -config SAMA5_EHCI_RHPORT3 - bool +config SAMA5_UHPHS_RHPORT3 + bool "Use Port C" default y -endif # !SAMA5_OHCI && SAMA5_EHCI +endif # SAMA5_OHCI || SAMA5_EHCI endmenu # USB High Speed Host driver option endif # SAMA5_UHPHS diff --git a/nuttx/arch/arm/src/sama5/chip/sam_sfr.h b/nuttx/arch/arm/src/sama5/chip/sam_sfr.h index 9ea3600a5..ca6558e38 100644 --- a/nuttx/arch/arm/src/sama5/chip/sam_sfr.h +++ b/nuttx/arch/arm/src/sama5/chip/sam_sfr.h @@ -77,9 +77,10 @@ /* OHCI Interrupt Configuration Register */ -#define SFR_OHCIICR_RES0 (1 << 0) /* Bit 0: USB port 0 reset */ -#define SFR_OHCIICR_RES1 (1 << 1) /* Bit 1: USB port 1 reset */ -#define SFR_OHCIICR_RES2 (1 << 2) /* Bit 2: USB port 2 reset */ +#define SFR_OHCIICR_RES(n) (1 << (n)) /* Bit 0: USB port n reset, n=0..2 */ +# define SFR_OHCIICR_RES0 (1 << 0) /* Bit 0: USB port 0 reset */ +# define SFR_OHCIICR_RES1 (1 << 1) /* Bit 1: USB port 1 reset */ +# define SFR_OHCIICR_RES2 (1 << 2) /* Bit 2: USB port 2 reset */ #define SFR_OHCIICR_ARIE (1 << 4) /* Bit 4: OHCI asynchronous resume interrupt enable */ #define SFR_OHCIICR_APPSTART (0) /* Bit 5: Reserved, must write 0 */ #define SFR_OHCIICR_UDPPUDIS (1 << 23) /* Bit 23: USB device pull-up disable */ diff --git a/nuttx/arch/arm/src/sama5/sam_ehci.c b/nuttx/arch/arm/src/sama5/sam_ehci.c index f34d11d99..e42d2c7c6 100755 --- a/nuttx/arch/arm/src/sama5/sam_ehci.c +++ b/nuttx/arch/arm/src/sama5/sam_ehci.c @@ -114,14 +114,14 @@ /* If UDPHS is enabled, then don't use port A */ #ifdef CONFIG_SAMA5_UDPHS -# undef CONFIG_SAMA5_EHCI_RHPORT1 +# undef CONFIG_SAMA5_UHPHS_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 +#undef CONFIG_SAMA5_UHPHS_RHPORT1 /* Driver-private Definitions **************************************************/ @@ -2548,6 +2548,32 @@ static int sam_ehci_tophalf(int irq, FAR void *context) } /******************************************************************************* + * Name: sam_uhphs_interrupt + * + * Description: + * Common UHPHS interrupt handler. When both OHCI and EHCI are enabled, EHCI + * owns the interrupt and provides the interrupting event to both the OHCI and + * EHCI controllers. + * + *******************************************************************************/ + +#ifdef CONFIG_SAMA5_OHCI +static int sam_uhphs_interrupt(int irq, FAR void *context) +{ + int ohci; + int ehci; + + /* Provide the interrupting event to both the EHCI and OHCI top half */ + ohci = sam_ohci_tophalf(irq, context); + ehci = sam_ehci_tophalf(irq, context); + + /* Return OK only if both handlers returned OK */ + + return ohci == OK ? ehci : ohci; +} +#endif + +/******************************************************************************* * USB Host Controller Operations *******************************************************************************/ /******************************************************************************* @@ -2696,23 +2722,49 @@ static int sam_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx) { /* Paragraph 2.3.9: * - * "Port Owner ... This bit unconditionally goes to a 0b when the - * Configured bit in the CONFIGFLAG register makes a 0b to 1b - * transition. This bit unconditionally goes to 1b whenever the - * Configured bit is zero. + * "Port Owner ... This bit unconditionally goes to a 0b when the + * Configured bit in the CONFIGFLAG register makes a 0b to 1b + * transition. This bit unconditionally goes to 1b whenever the + * Configured bit is zero. + * + * "System software uses this field to release ownership of the + * port to a selected host controller (in the event that the + * attached device is not a high-speed device). Software writes + * a one to this bit when the attached device is not a high-speed + * device. A one in this bit means that a companion host + * controller owns and controls the port. .... * - * "System software uses this field to release ownership of the - * port to a selected host controller (in the event that the - * attached device is not a high-speed device). Software writes - * a one to this bit when the attached device is not a high-speed - * device. A one in this bit means that a companion host - * controller owns and controls the port. .... + * Paragraph 4.2: + * + * "When a port is routed to a companion HC, it remains under the + * control of the companion HC until the device is disconnected + * from the root por ... When a disconnect occurs, the disconnect + * event is detected by both the companion HC port control and the + * EHCI port ownership control. On the event, the port ownership + * is returned immediately to the EHCI controller. The companion + * HC stack detects the disconnect and acknowledges as it would + * in an ordinary standalone implementation. Subsequent connects + * will be detected by the EHCI port register and the process will + * repeat." */ -#warning REVISIT rhport->ep0.speed = EHCI_LOW_SPEED; - regval &= EHCI_PORTSC_OWNER; + regval |= EHCI_PORTSC_OWNER; sam_putreg(regval, &HCOR->portsc[rhpndx]); + +#ifndef CONFIG_SAMA5_OHCI + /* Give the port to the OHCI controller. Zero is the reset value for + * all ports; one makes the corresponding port available to OHCI. + */ + + regval = getreg32(SAM_SFR_OHCIICR); + regval |= SFR_OHCIICR_RES(rhpndx); + putreg32(regval, SAM_SFR_OHCIICR); +#endif + + /* And return a failure */ + + return -EPERM; } else { @@ -2802,10 +2854,38 @@ static int sam_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx) } else { - /* Low- or Full- speed device */ + /* Low- or Full- speed device. Set the port ownership bit. + * + * Paragraph 4.2: + * + * "When a port is routed to a companion HC, it remains under the + * control of the companion HC until the device is disconnected + * from the root por ... When a disconnect occurs, the disconnect + * event is detected by both the companion HC port control and the + * EHCI port ownership control. On the event, the port ownership + * is returned immediately to the EHCI controller. The companion + * HC stack detects the disconnect and acknowledges as it would + * in an ordinary standalone implementation. Subsequent connects + * will be detected by the EHCI port register and the process will + * repeat." + */ - regval &= EHCI_PORTSC_OWNER; + regval |= EHCI_PORTSC_OWNER; sam_putreg(regval, &HCOR->portsc[rhpndx]); + +#ifndef CONFIG_SAMA5_OHCI + /* Give the port to the OHCI controller. Zero is the reset value for + * all ports; one makes the corresponding port available to OHCI. + */ + + regval = getreg32(SAM_SFR_OHCIICR); + regval |= SFR_OHCIICR_RES(rhpndx); + putreg32(regval, SAM_SFR_OHCIICR); +#endif + + /* And return a failure */ + + return -EPERM; } /* Let the common usbhost_enumerate do all of the real work. Note that the @@ -3521,13 +3601,13 @@ FAR struct usbhost_connection_s *sam_ehci_initialize(int controller) */ regval = getreg32(SAM_SFR_OHCIICR); -#ifdef CONFIG_SAMA5_EHCI_RHPORT1 - regval &= ~SFR_OHCIICR_RES1; +#ifdef CONFIG_SAMA5_UHPHS_RHPORT1 + regval &= ~SFR_OHCIICR_RES0; #endif -#ifdef CONFIG_SAMA5_EHCI_RHPORT2 +#ifdef CONFIG_SAMA5_UHPHS_RHPORT2 regval &= ~SFR_OHCIICR_RES1; #endif -#ifdef CONFIG_SAMA5_EHCI_RHPORT3 +#ifdef CONFIG_SAMA5_UHPHS_RHPORT3 regval &= ~SFR_OHCIICR_RES2; #endif putreg32(regval, SAM_SFR_OHCIICR); @@ -3767,9 +3847,16 @@ FAR struct usbhost_connection_s *sam_ehci_initialize(int controller) } /* Interrupt Configuration ***************************************************/ - /* Attach USB host controller interrupt handler */ + /* Attach USB host controller interrupt handler. If OHCI is also enabled, + * then we have to use a common UHPHS interrupt handler. + */ - if (irq_attach(SAM_IRQ_UHPHS, sam_ehci_tophalf) != 0) +#ifdef CONFIG_SAMA5_OHCI + ret = irq_attach(SAM_IRQ_UHPHS, sam_uhphs_interrupt); +#else + ret = irq_attach(SAM_IRQ_UHPHS, sam_ehci_tophalf); +#endif + if (ret != 0) { udbg("ERROR: Failed to attach IRQ\n"); return NULL; @@ -3781,17 +3868,22 @@ FAR struct usbhost_connection_s *sam_ehci_initialize(int controller) sam_putreg(EHCI_HANDLED_INTS, &HCOR->usbintr); - /* Drive Vbus +5V (the smoke test). Should be done elsewhere in OTG - * mode. + /* Drive Vbus +5V (the smoke test) + * + * REVISIT: + * - Should be done elsewhere in OTG mode. + * - Can we postpone enabling VBUS to save power? + * - Some EHCI implementations require setting the power bit in the + * PORTSC register to enable power. */ -#ifndef CONFIG_SAMA5_EHCI_RHPORT1 +#ifdef CONFIG_SAMA5_UHPHS_RHPORT1 sam_usbhost_vbusdrive(SAM_RHPORT1, true); #endif -#ifndef CONFIG_SAMA5_EHCI_RHPORT2 +#ifdef CONFIG_SAMA5_UHPHS_RHPORT2 sam_usbhost_vbusdrive(SAM_RHPORT2, true); #endif -#ifndef CONFIG_SAMA5_EHCI_RHPORT3 +#ifdef CONFIG_SAMA5_UHPHS_RHPORT3 sam_usbhost_vbusdrive(SAM_RHPORT3, true); #endif up_mdelay(50); diff --git a/nuttx/arch/arm/src/sama5/sam_ohci.c b/nuttx/arch/arm/src/sama5/sam_ohci.c index 25c29302a..55088e728 100644 --- a/nuttx/arch/arm/src/sama5/sam_ohci.c +++ b/nuttx/arch/arm/src/sama5/sam_ohci.c @@ -50,6 +50,7 @@ #include <nuttx/arch.h> #include <nuttx/kmalloc.h> +#include <nuttx/wqueue.h> #include <nuttx/usb/usb.h> #include <nuttx/usb/ohci.h> #include <nuttx/usb/usbhost.h> @@ -70,10 +71,17 @@ #include "chip/sam_sfr.h" #include "chip/sam_ohci.h" +#ifdef CONFIG_SAMA5_OHCI + /******************************************************************************* * Definitions *******************************************************************************/ /* Configuration ***************************************************************/ +/* Pre-requisites */ + +#ifndef CONFIG_SCHED_WORKQUEUE +# error Work queue support is required (CONFIG_SCHED_WORKQUEUE) +#endif /* Configurable number of user endpoint descriptors (EDs). This number excludes * the control endpoint that is always allocated. @@ -120,14 +128,14 @@ /* If UDPHS is enabled, then don't use port A */ #ifdef CONFIG_SAMA5_UDPHS -# undef CONFIG_SAMA5_OHCI_RHPORT1 +# undef CONFIG_SAMA5_UHPHS_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 +#undef CONFIG_SAMA5_UHPHS_RHPORT1 /* Debug */ @@ -235,6 +243,7 @@ struct sam_ohci_s #endif sem_t exclsem; /* Support mutually exclusive access */ sem_t rhssem; /* Semaphore to wait Writeback Done Head event */ + struct work_s work; /* Supports interrupt bottom half */ /* Root hub ports */ @@ -362,9 +371,9 @@ static int sam_ctrltd(struct sam_rhport_s *rhport, uint32_t dirpid, /* Interrupt handling **********************************************************/ -static void sam_rhsc_interrupt(void); -static void sam_wdh_interrupt(void); -static int sam_ohci_interrupt(int irq, FAR void *context); +static void sam_rhsc_bottomhalf(void); +static void sam_wdh_bottomhalf(void); +static void sam_ohci_bottomhalf(void *arg); /* USB host controller operations **********************************************/ @@ -1681,14 +1690,14 @@ static int sam_ctrltd(struct sam_rhport_s *rhport, uint32_t dirpid, } /******************************************************************************* - * Name: sam_rhsc_interrupt + * Name: sam_rhsc_bottomhalf * * Description: * OHCI root hub status change interrupt handler * *******************************************************************************/ -static void sam_rhsc_interrupt(void) +static void sam_rhsc_bottomhalf(void) { struct sam_rhport_s *rhport; uint32_t regaddr; @@ -1813,14 +1822,14 @@ static void sam_rhsc_interrupt(void) } /******************************************************************************* - * Name: sam_wdh_interrupt + * Name: sam_wdh_bottomhalf * * Description: * OHCI write done head interrupt handler * *******************************************************************************/ -static void sam_wdh_interrupt(void) +static void sam_wdh_bottomhalf(void) { struct sam_eplist_s *eplist; struct sam_gtd_s *td; @@ -1913,82 +1922,79 @@ static void sam_wdh_interrupt(void) } /******************************************************************************* - * Name: sam_ohci_interrupt + * Name: sam_ohci_bottomhalf * * Description: - * OHCI interrupt handler + * OHCI interrupt bottom half. This function runs on the high priority worker + * thread and was xcheduled when the last interrupt occurred. The set of + * pending interrupts is provided as the argument. OHCI interrupts were + * disabled when this function is scheduled so no further interrupts can + * occur until this work re-enables OHCI interrupts * *******************************************************************************/ -static int sam_ohci_interrupt(int irq, FAR void *context) +static void sam_ohci_bottomhalf(void *arg) { - uint32_t intst; - uint32_t pending; - uint32_t regval; + uint32_t pending = (uint32_t)arg; - /* Read Interrupt Status and mask out interrupts that are not enabled. */ + /* We need to have exclusive access to the EHCI data structures. Waiting here + * is not a good thing to do on the worker thread, but there is no real option + * (other than to reschedule and delay). + */ - intst = sam_getreg(SAM_USBHOST_INTST); - regval = sam_getreg(SAM_USBHOST_INTEN); - ullvdbg("INST: %08x INTEN: %08x\n", intst, regval); + sam_takesem(&g_ohci.exclsem); - pending = intst & regval; - if (pending != 0) + /* Root hub status change interrupt */ + + if ((pending & OHCI_INT_RHSC) != 0) { - /* Root hub status change interrupt */ + /* Handle root hub status change on each root port */ - if ((pending & OHCI_INT_RHSC) != 0) - { - /* Handle root hub status change on each root port */ + ullvdbg("Root Hub Status Change\n"); + sam_rhsc_bottomhalf(); + } - ullvdbg("Root Hub Status Change\n"); - sam_rhsc_interrupt(); - } + /* Writeback Done Head interrupt */ - /* Writeback Done Head interrupt */ + if ((pending & OHCI_INT_WDH) != 0) + { + /* The host controller just wrote the list of finished TDs into the HCCA + * done head. This may include multiple packets that were transferred + * in the preceding frame. + */ + + ullvdbg("Writeback Done Head interrupt\n"); + sam_wdh_bottomhalf(); + } - if ((pending & OHCI_INT_WDH) != 0) +#ifdef CONFIG_DEBUG_USB + if ((pending & SAM_DEBUG_INTS) != 0) + { + if ((pending & OHCI_INT_UE) != 0) { - /* The host controller just wrote the list of finished TDs into the HCCA - * done head. This may include multiple packets that were transferred - * in the preceding frame. + /* An unrecoverable error occurred. Unrecoverable errors + * are usually the consequence of bad descriptor contents + * or DMA errors. + * + * Treat this like a normal write done head interrupt. We + * just want to see if there is any status information writen + * to the descriptors (and the normal write done head + * interrupt will not be occurring). */ - ullvdbg("Writeback Done Head interrupt\n"); - sam_wdh_interrupt(); + ulldbg("ERROR: Unrecoverable error. pending: %08x\n", pending); + sam_wdh_bottomhalf(); } - -#ifdef CONFIG_DEBUG_USB - if ((pending & SAM_DEBUG_INTS) != 0) + else { - if ((pending & OHCI_INT_UE) != 0) - { - /* An unrecoverable error occurred. Unrecoverable errors - * are usually the consequence of bad descriptor contents - * or DMA errors. - * - * Treat this like a normal write done head interrupt. We - * just want to see if there is any status information writen - * to the descriptors (and the normal write done head - * interrupt will not be occurring). - */ - - ulldbg("ERROR: Unrecoverable error. INTST: %08x\n", intst); - sam_wdh_interrupt(); - } - else - { - ulldbg("ERROR: Unhandled interrupts INTST: %08x\n", intst); - } + ulldbg("ERROR: Unhandled interrupts pending: %08x\n", pending); } + } #endif - /* Clear interrupt status register */ - - sam_putreg(intst, SAM_USBHOST_INTST); - } + /* Now re-enable interrupts */ - return OK; + sam_putreg(OHCI_INT_MIE, SAM_USBHOST_INTEN); } /******************************************************************************* @@ -2040,11 +2046,25 @@ static int sam_wait(FAR struct usbhost_connection_s *conn, for (rhpndx = 0; rhpndx < SAM_OHCI_NRHPORT; rhpndx++) { +#ifndef CONFIG_SAMA5_EHCI + /* If a device is no longer connected, return the port to the EHCI + * controller. Zero is the reset value for all ports; one makes + * the corresponding port available to OHCI. + */ + + if (!g_ohci.rhport[rhpndx].connected) + { + regval = getreg32(SAM_SFR_OHCIICR); + regval &= ~SFR_OHCIICR_RES(rhpndx); + putreg32(regval, SAM_SFR_OHCIICR); + } +#endif + /* Has the connection state changed on the RH port? */ if (g_ohci.rhport[rhpndx].connected != connected[rhpndx]) { - /* Yes.. Return the RH port number */ + /* Yes.. Return the RH port number ;to inform the call which */ irqrestore(flags); @@ -2864,6 +2884,18 @@ static int sam_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, sam_putreg(regval, SAM_USBHOST_CMDST); } + /* Release the OHCI semaphore while we wait. Other threads need the + * opportunity to access the EHCI resources while we wait. + * + * REVISIT: Is this safe? NO. This is a bug and needs rethinking. + * We need to lock all of the port-resources (not EHCI common) until + * the transfer is complete. But we can't use the common OHCI exclsem + * or we will deadlock while waiting (because the working thread that + * wakes this thread up needs the exclsem). + */ +#warning REVISIT + sam_givesem(&g_ohci.exclsem); + /* Wait for the Writeback Done Head interrupt Loop to handle any false * alarm semaphore counts. */ @@ -2873,6 +2905,12 @@ static int sam_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, sam_takesem(&eplist->wdhsem); } + /* Re-aquire the ECHI semaphore. The caller expects to be holding + * this upon return. + */ + + sam_takesem(&g_ohci.exclsem); + /* Invalidate the D cache to force the ED to be reloaded from RAM */ cp15_invalidate_dcache((uintptr_t)ed, @@ -3029,23 +3067,27 @@ FAR struct usbhost_connection_s *sam_ohci_initialize(int controller) /* "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 + */ + +#ifndef CONFIG_SAMA5_EHCI + /* 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); -#ifdef CONFIG_SAMA5_OHCI_RHPORT1 - regval |= SFR_OHCIICR_RES1; +#ifdef CONFIG_SAMA5_UHPHS_RHPORT1 + regval |= SFR_OHCIICR_RES0; #endif -#ifdef CONFIG_SAMA5_OHCI_RHPORT2 +#ifdef CONFIG_SAMA5_UHPHS_RHPORT2 regval |= SFR_OHCIICR_RES1; #endif -#ifdef CONFIG_SAMA5_OHCI_RHPORT3 +#ifdef CONFIG_SAMA5_UHPHS_RHPORT3 regval |= SFR_OHCIICR_RES2; #endif putreg32(regval, SAM_SFR_OHCIICR); +#endif + irqrestore(flags); /* Note that no pin configuration is required. All USB HS pins have @@ -3148,27 +3190,32 @@ FAR struct usbhost_connection_s *sam_ohci_initialize(int controller) /* Enable OHCI interrupts */ - sam_putreg((SAM_ALL_INTS|OHCI_INT_MIE), SAM_USBHOST_INTEN); + sam_putreg((SAM_ALL_INTS | OHCI_INT_MIE), SAM_USBHOST_INTEN); - /* Attach USB host controller interrupt handler */ +#ifndef CONFIG_SAMA5_EHCI + /* Attach USB host controller interrupt handler. If ECHI is enabled, + * then it will manage the shared interrupt. */ - if (irq_attach(SAM_IRQ_UHPHS, sam_ohci_interrupt) != 0) + if (irq_attach(SAM_IRQ_UHPHS, sam_ohci_tophalf) != 0) { udbg("Failed to attach IRQ\n"); return NULL; } - /* Drive Vbus +5V (the smoke test). Should be done elsewhere in OTG - * mode. + /* Drive Vbus +5V (the smoke test). + * + * REVISIT: + * - Should be done elsewhere in OTG mode. + * - Can we postpone enabling VBUS to save power? */ -#ifndef CONFIG_SAMA5_OHCI_RHPORT1 +#ifdef CONFIG_SAMA5_UHPHS_RHPORT1 sam_usbhost_vbusdrive(SAM_RHPORT1, true); #endif -#ifndef CONFIG_SAMA5_OHCI_RHPORT2 +#ifdef CONFIG_SAMA5_UHPHS_RHPORT2 sam_usbhost_vbusdrive(SAM_RHPORT2, true); #endif -#ifndef CONFIG_SAMA5_OHCI_RHPORT3 +#ifdef CONFIG_SAMA5_UHPHS_RHPORT3 sam_usbhost_vbusdrive(SAM_RHPORT3, true); #endif up_mdelay(50); @@ -3187,9 +3234,14 @@ FAR struct usbhost_connection_s *sam_ohci_initialize(int controller) i+1, g_ohci.rhport[i].connected ? "YES" : "NO"); } - /* Enable interrupts at the interrupt controller */ + /* Enable interrupts at the interrupt controller. If ECHI is enabled, + * then it will manage the shared interrupt. + */ up_enable_irq(SAM_IRQ_UHPHS); /* enable USB interrupt */ + +#endif /* CONFIG_SAMA5_EHCI */ + uvdbg("USB OHCI Initialized\n"); /* Initialize and return the connection interface */ @@ -3198,3 +3250,58 @@ FAR struct usbhost_connection_s *sam_ohci_initialize(int controller) g_ohciconn.enumerate = sam_enumerate; return &g_ohciconn; } + +/******************************************************************************* + * Name: sam_ohci_tophalf + * + * Description: + * OHCI "Top Half" interrupt handler. If both EHCI and OHCI are enabled, then + * EHCI will manage the common UHPHS interrupt and will forward the interrupt + * event to this function. + * + *******************************************************************************/ + +int sam_ohci_tophalf(int irq, FAR void *context) +{ + uint32_t intst; + uint32_t regval; + uint32_t pending; + + /* Read Interrupt Status and mask out interrupts that are not enabled. */ + + intst = sam_getreg(SAM_USBHOST_INTST); + regval = sam_getreg(SAM_USBHOST_INTEN); + ullvdbg("INST: %08x INTEN: %08x\n", intst, regval); + + pending = intst & regval; + if (pending != 0) + { + /* Schedule interrupt handling work for the high priority worker thread + * so that we are not pressed for time and so that we can interrupt with + * other USB threads gracefully. + * + * The worker should be available now because we implement a handshake + * by controlling the OHCI interrupts. + */ + + DEBUGASSERT(work_available(&g_ohci.work)); + DEBUGVERIFY(work_queue(HPWORK, &g_ohci.work, sam_ohci_bottomhalf, + (FAR void *)pending, 0)); + + /* Disable further OHCI interrupts so that we do not overrun the work + * queue. + */ + + sam_putreg(OHCI_INT_MIE, SAM_USBHOST_INTDIS); + + /* Clear all pending status bits by writing the value of the pending + * interrupt bits back to the status register. + */ + + sam_putreg(intst, SAM_USBHOST_INTST); + } + + return OK; +} + +#endif /* CONFIG_SAMA5_OHCI */ diff --git a/nuttx/arch/arm/src/sama5/sam_usbhost.h b/nuttx/arch/arm/src/sama5/sam_usbhost.h index 0a4494a45..7ab5cca85 100644 --- a/nuttx/arch/arm/src/sama5/sam_usbhost.h +++ b/nuttx/arch/arm/src/sama5/sam_usbhost.h @@ -120,6 +120,20 @@ FAR struct usbhost_connection_s *sam_ohci_initialize(int controller); #endif /******************************************************************************* + * Name: sam_ohci_tophalf + * + * Description: + * OHCI "Top Half" interrupt handler. If both EHCI and OHCI are enabled, then + * EHCI will manage the common UHPHS interrupt and will forward the interrupt + * event to this function. + * + *******************************************************************************/ + +#ifdef CONFIG_SAMA5_OHCI +int sam_ohci_tophalf(int irq, FAR void *context); +#endif + +/******************************************************************************* * Name: sam_ehci_initialize * * Description: diff --git a/nuttx/configs/sama5d3x-ek/README.txt b/nuttx/configs/sama5d3x-ek/README.txt index d7eadc4cd..e89d1d9ee 100644 --- a/nuttx/configs/sama5d3x-ek/README.txt +++ b/nuttx/configs/sama5d3x-ek/README.txt @@ -1166,14 +1166,14 @@ Configurations volume when it is removed. But those callbacks are not used in this configuration. - 10) Support the USB full-speed OHCI host driver can be enabled by changing + 10) Support the USB low/full-speed OHCI host driver can be enabled by changing the NuttX configuration file as follows: System Type -> ATSAMA5 Peripheral Support CONFIG_SAMA5_UHPHS=y : USB Host High Speed System Type -> USB High Speed Host driver options - CONFIG_SAMA5_OHCI=y : Full-speed OHCI support + CONFIG_SAMA5_OHCI=y : Low/full-speed OHCI support : Defaults for values probably OK Device Drivers CONFIG_USBHOST=y : Enable USB host support @@ -1194,14 +1194,17 @@ Configurations values that are used will be off slightly because of this. 10) Support the USB high-speed EHCI host driver can be enabled by changing - the NuttX configuration file as follows: + the NuttX configuration file as follows. If EHCI is enabled by itself, + then only high-speed devices can be supported. If OHCI is also enabled, + then all low-, full-, and high speed devices should work. System Type -> ATSAMA5 Peripheral Support CONFIG_SAMA5_UHPHS=y : USB Host High Speed System Type -> USB High Speed Host driver options CONFIG_SAMA5_EHCI=y : High-speed EHCI support - : Defaults for values probably OK + CONFIG_SAMA5_OHCI=y : Low/full-speed OHCI support + : Defaults for values probably OK for both Device Drivers CONFIG_USBHOST=y : Enable USB host support diff --git a/nuttx/configs/sama5d3x-ek/include/board.h b/nuttx/configs/sama5d3x-ek/include/board.h index 321a237e5..29e56028e 100644 --- a/nuttx/configs/sama5d3x-ek/include/board.h +++ b/nuttx/configs/sama5d3x-ek/include/board.h @@ -51,17 +51,20 @@ * definitions will configure operational clocking. */ -#ifndef CONFIG_SAMA5_OHCI +#if !defined(CONFIG_SAMA5_OHCI) || defined(CONFIG_SAMA5_EHCI) /* This is the configuration provided in the Atmel example code. This setup results - * in a CPU clock of 396MHz + * in a CPU clock of 396MHz. + * + * In this configuration, UPLL is the source of the UHPHS clock (if enabled). */ # include <arch/board/board_396MHz.h> #else -/* This is an alternative slower configuration that will produce a 48MHz USB clock - * with the required accuracy. When used with OHCI, an additional requirement is - * the PLLACK be a multiple of 48MHz. This setup results in a CPU clock of 384MHz. +/* OHCI Only. This is an alternative slower configuration that will produce a 48MHz + * USB clock with the required accuracy using only PLLA. When PPLA is used to clock + * OHCI, an additional requirement is the PLLACK be a multiple of 48MHz. This setup + * results in a CPU clock of 384MHz. */ # include <arch/board/board_384MHz.h> diff --git a/nuttx/configs/sama5d3x-ek/src/sam_usb.c b/nuttx/configs/sama5d3x-ek/src/sam_usb.c index d6b41c2b0..b8cc73d21 100644 --- a/nuttx/configs/sama5d3x-ek/src/sam_usb.c +++ b/nuttx/configs/sama5d3x-ek/src/sam_usb.c @@ -71,6 +71,10 @@ # define CONFIG_USBHOST_STACKSIZE 1024 #endif +#ifdef HAVE_USBDEV +# undef CONFIG_SAMA5_UHPHS_RHPORT1 +#endif + /************************************************************************************ * Private Data ************************************************************************************/ @@ -102,12 +106,17 @@ static xcpt_t g_ochandler; ************************************************************************************/ #if HAVE_USBHOST +#ifdef CONFIG_DEBUG_USB +static int usbhost_waiter(struct usbhost_connection_s *dev, const char *hcistr) +#else static int usbhost_waiter(struct usbhost_connection_s *dev) +#endif { bool connected[SAM_OHCI_NRHPORT] = {false, false, false}; int rhpndx; + int ret; - uvdbg("Running\n"); + uvdbg("%s Waiter Running\n", hcistr); for (;;) { /* Wait for the device to change state */ @@ -117,8 +126,8 @@ static int usbhost_waiter(struct usbhost_connection_s *dev) connected[rhpndx] = !connected[rhpndx]; - uvdbg("RHport%d %s\n", - rhpndx + 1, connected[rhpndx] ? "connected" : "disconnected"); + uvdbg("%s RHport%d %s\n", + hcistr, rhpndx + 1, connected[rhpndx] ? "connected" : "disconnected"); /* Did we just become connected? */ @@ -126,7 +135,12 @@ static int usbhost_waiter(struct usbhost_connection_s *dev) { /* Yes.. enumerate the newly connected device */ - (void)CONN_ENUMERATE(dev, rhpndx); + ret = CONN_ENUMERATE(dev, rhpndx); + if (ret < 0) + { + uvdbg("%s RHport%d CONN_ENUMERATE failed: %d\n", hcistr, rhpndx+1, ret); + connected[rhpndx] = false; + } } } @@ -147,7 +161,11 @@ static int usbhost_waiter(struct usbhost_connection_s *dev) #ifdef CONFIG_SAMA5_OHCI static int ohci_waiter(int argc, char *argv[]) { +#ifdef CONFIG_DEBUG_USB + return usbhost_waiter(g_ohciconn, "OHCI"); +#else return usbhost_waiter(g_ohciconn); +#endif } #endif @@ -162,7 +180,11 @@ static int ohci_waiter(int argc, char *argv[]) #ifdef CONFIG_SAMA5_EHCI static int ehci_waiter(int argc, char *argv[]) { +#ifdef CONFIG_DEBUG_USB + return usbhost_waiter(g_ehciconn, "EHCI"); +#else return usbhost_waiter(g_ehciconn); +#endif } #endif @@ -244,23 +266,30 @@ void weak_function sam_usbinitialize(void) #endif #ifdef HAVE_USBHOST -#ifndef HAVE_USBDEV - /* Configure Port A to support the USB OHCI/EHCI function only if USB - * device is not also supported. - */ +#ifdef CONFIG_SAMA5_UHPHS_RHPORT1 + /* Configure Port A to support the USB OHCI/EHCI function */ sam_configpio(PIO_USBA_VBUS_ENABLE); /* VBUS enable, initially OFF */ #endif - /* Configure Ports B and C to support the USB OHCI/EHCI function */ +#ifdef CONFIG_SAMA5_UHPHS_RHPORT2 + /* Configure Port B to support the USB OHCI/EHCI function */ sam_configpio(PIO_USBB_VBUS_ENABLE); /* VBUS enable, initially OFF */ +#endif + +#ifdef CONFIG_SAMA5_UHPHS_RHPORT3 + /* Configure Port C to support the USB OHCI/EHCI function */ + sam_configpio(PIO_USBC_VBUS_ENABLE); /* VBUS enable, initially OFF */ +#endif +#if defined(CONFIG_SAMA5_UHPHS_RHPORT2) || defined(CONFIG_SAMA5_UHPHS_RHPORT3) /* Configure Port B/C VBUS overrcurrent detection */ sam_configpio(PIO_USBBC_VBUS_OVERCURRENT); /* VBUS overcurrent */ #endif +#endif /* HAVE_USBHOST */ } /*********************************************************************************** @@ -364,7 +393,7 @@ void sam_usbhost_vbusdrive(int rhport, bool enable) switch (rhport) { case SAM_RHPORT1: -#ifdef HAVE_USBDEV +#ifdef CONFIG_SAMA5_UHPHS_RHPORT1 udbg("ERROR: RHPort1 is not available in this configuration\n"); return; #else @@ -373,12 +402,22 @@ void sam_usbhost_vbusdrive(int rhport, bool enable) #endif case SAM_RHPORT2: +#ifdef CONFIG_SAMA5_UHPHS_RHPORT2 + udbg("ERROR: RHPort2 is not available in this configuration\n"); + return; +#else pinset = PIO_USBB_VBUS_ENABLE; break; +#endif case SAM_RHPORT3: +#ifdef CONFIG_SAMA5_UHPHS_RHPORT3 + udbg("ERROR: RHPort3 is not available in this configuration\n"); + return; +#else pinset = PIO_USBC_VBUS_ENABLE; break; +#endif default: udbg("ERROR: RHPort%d is not supported\n", rhport+1); @@ -407,7 +446,7 @@ void sam_usbhost_vbusdrive(int rhport, bool enable) * * Description: * Setup to receive an interrupt-level callback if an overcurrent condition is - * detected. + * detected on port B or C. * * 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. @@ -423,7 +462,9 @@ void sam_usbhost_vbusdrive(int rhport, bool enable) #if HAVE_USBHOST xcpt_t sam_setup_overcurrent(xcpt_t handler) { -#if defined(CONFIG_SAMA5_PIOD_IRQ) +#if defined(CONFIG_SAMA5_PIOD_IRQ) && (defined(CONFIG_SAMA5_UHPHS_RHPORT2) || \ + defined(CONFIG_SAMA5_UHPHS_RHPORT3)) + xcpt_t oldhandler; irqstate_t flags; @@ -447,11 +488,13 @@ xcpt_t sam_setup_overcurrent(xcpt_t handler) /* Return the old button handler (so that it can be restored) */ return oldhandler; + #else return NULL; + #endif } -#endif +#endif /* CONFIG_SAMA5_PIOD_IRQ ... */ /************************************************************************************ * Name: sam_usbsuspend |