summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2013-08-24 11:34:24 -0600
committerGregory Nutt <gnutt@nuttx.org>2013-08-24 11:34:24 -0600
commitcb9ed2733015b48819a34ed4357e5e916216636a (patch)
tree53161b08fbe24f81490a1f1760bb971eaad7eeaa
parente2c9e4545e08c93482a36dcaebd1819a00412ad8 (diff)
downloadnuttx-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/ChangeLog9
-rw-r--r--nuttx/arch/arm/src/sama5/Kconfig71
-rw-r--r--nuttx/arch/arm/src/sama5/chip/sam_sfr.h7
-rwxr-xr-xnuttx/arch/arm/src/sama5/sam_ehci.c146
-rw-r--r--nuttx/arch/arm/src/sama5/sam_ohci.c263
-rw-r--r--nuttx/arch/arm/src/sama5/sam_usbhost.h14
-rw-r--r--nuttx/configs/sama5d3x-ek/README.txt11
-rw-r--r--nuttx/configs/sama5d3x-ek/include/board.h13
-rw-r--r--nuttx/configs/sama5d3x-ek/src/sam_usb.c69
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