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

This commit is contained in:
Gregory Nutt 2013-08-24 11:34:24 -06:00
parent 1f3cebdb40
commit 28a103f1f2
5 changed files with 345 additions and 180 deletions

View File

@ -339,7 +339,7 @@ if SAMA5_UHPHS
menu "USB High Speed Host device driver options" menu "USB High Speed Host device driver options"
config SAMA5_OHCI config SAMA5_OHCI
bool "Full speed OHCI support" bool "Full/low speed OHCI support"
default n default n
---help--- ---help---
Build support for the SAMA5 USB full speed Open Host Controller Build support for the SAMA5 USB full speed Open Host Controller
@ -377,7 +377,8 @@ config SAMA5_EHCI
default n default n
---help--- ---help---
Build support for the SAMA5 USB high speed Enhanced Host Controller 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 if SAMA5_EHCI
@ -410,72 +411,22 @@ config SAMA5_EHCI_REGDEBUG
endif # SAMA5_EHCI endif # SAMA5_EHCI
if SAMA5_OHCI && SAMA5_EHCI if SAMA5_OHCI || SAMA5_EHCI
config SAMA5_OHCI_RHPORT1 config SAMA5_UHPHS_RHPORT1
bool "Use Port A for OHCI" bool "Use Port A"
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 default y
depends on !SAMA5_UDPHS depends on !SAMA5_UDPHS
config SAMA5_OHCI_RHPORT2 config SAMA5_UHPHS_RHPORT2
bool bool "Use Port B"
default y default y
config SAMA5_OHCI_RHPORT3 config SAMA5_UHPHS_RHPORT3
bool bool "Use Port C"
default y default y
endif # SAMA5_OHCI && !SAMA5_EHCI 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

View File

@ -77,9 +77,10 @@
/* OHCI Interrupt Configuration Register */ /* OHCI Interrupt Configuration Register */
#define SFR_OHCIICR_RES0 (1 << 0) /* Bit 0: USB port 0 reset */ #define SFR_OHCIICR_RES(n) (1 << (n)) /* Bit 0: USB port n reset, n=0..2 */
#define SFR_OHCIICR_RES1 (1 << 1) /* Bit 1: USB port 1 reset */ # define SFR_OHCIICR_RES0 (1 << 0) /* Bit 0: USB port 0 reset */
#define SFR_OHCIICR_RES2 (1 << 2) /* Bit 2: USB port 2 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_ARIE (1 << 4) /* Bit 4: OHCI asynchronous resume interrupt enable */
#define SFR_OHCIICR_APPSTART (0) /* Bit 5: Reserved, must write 0 */ #define SFR_OHCIICR_APPSTART (0) /* Bit 5: Reserved, must write 0 */
#define SFR_OHCIICR_UDPPUDIS (1 << 23) /* Bit 23: USB device pull-up disable */ #define SFR_OHCIICR_UDPPUDIS (1 << 23) /* Bit 23: USB device pull-up disable */

View File

@ -114,14 +114,14 @@
/* If UDPHS is enabled, then don't use port A */ /* If UDPHS is enabled, then don't use port A */
#ifdef CONFIG_SAMA5_UDPHS #ifdef CONFIG_SAMA5_UDPHS
# undef CONFIG_SAMA5_EHCI_RHPORT1 # undef CONFIG_SAMA5_UHPHS_RHPORT1
#endif #endif
/* For now, suppress use of PORTA in any event. I use that for SAM-BA and /* 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! * 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 **************************************************/ /* Driver-private Definitions **************************************************/
@ -2547,6 +2547,32 @@ static int sam_ehci_tophalf(int irq, FAR void *context)
return OK; return OK;
} }
/*******************************************************************************
* 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 * USB Host Controller Operations
*******************************************************************************/ *******************************************************************************/
@ -2696,23 +2722,49 @@ static int sam_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx)
{ {
/* Paragraph 2.3.9: /* Paragraph 2.3.9:
* *
* "Port Owner ... This bit unconditionally goes to a 0b when the * "Port Owner ... This bit unconditionally goes to a 0b when the
* Configured bit in the CONFIGFLAG register makes a 0b to 1b * Configured bit in the CONFIGFLAG register makes a 0b to 1b
* transition. This bit unconditionally goes to 1b whenever the * transition. This bit unconditionally goes to 1b whenever the
* Configured bit is zero. * Configured bit is zero.
* *
* "System software uses this field to release ownership of the * "System software uses this field to release ownership of the
* port to a selected host controller (in the event that the * port to a selected host controller (in the event that the
* attached device is not a high-speed device). Software writes * attached device is not a high-speed device). Software writes
* a one to this bit when the attached device is not a high-speed * 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 * device. A one in this bit means that a companion host
* controller owns and controls the port. .... * 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; rhport->ep0.speed = EHCI_LOW_SPEED;
regval &= EHCI_PORTSC_OWNER; regval |= EHCI_PORTSC_OWNER;
sam_putreg(regval, &HCOR->portsc[rhpndx]); 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 else
{ {
@ -2802,10 +2854,38 @@ static int sam_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx)
} }
else 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]); 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 /* 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); regval = getreg32(SAM_SFR_OHCIICR);
#ifdef CONFIG_SAMA5_EHCI_RHPORT1 #ifdef CONFIG_SAMA5_UHPHS_RHPORT1
regval &= ~SFR_OHCIICR_RES0;
#endif
#ifdef CONFIG_SAMA5_UHPHS_RHPORT2
regval &= ~SFR_OHCIICR_RES1; regval &= ~SFR_OHCIICR_RES1;
#endif #endif
#ifdef CONFIG_SAMA5_EHCI_RHPORT2 #ifdef CONFIG_SAMA5_UHPHS_RHPORT3
regval &= ~SFR_OHCIICR_RES1;
#endif
#ifdef CONFIG_SAMA5_EHCI_RHPORT3
regval &= ~SFR_OHCIICR_RES2; regval &= ~SFR_OHCIICR_RES2;
#endif #endif
putreg32(regval, SAM_SFR_OHCIICR); putreg32(regval, SAM_SFR_OHCIICR);
@ -3767,9 +3847,16 @@ FAR struct usbhost_connection_s *sam_ehci_initialize(int controller)
} }
/* Interrupt Configuration ***************************************************/ /* 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"); udbg("ERROR: Failed to attach IRQ\n");
return NULL; return NULL;
@ -3781,17 +3868,22 @@ FAR struct usbhost_connection_s *sam_ehci_initialize(int controller)
sam_putreg(EHCI_HANDLED_INTS, &HCOR->usbintr); sam_putreg(EHCI_HANDLED_INTS, &HCOR->usbintr);
/* Drive Vbus +5V (the smoke test). Should be done elsewhere in OTG /* Drive Vbus +5V (the smoke test)
* mode. *
* 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); sam_usbhost_vbusdrive(SAM_RHPORT1, true);
#endif #endif
#ifndef CONFIG_SAMA5_EHCI_RHPORT2 #ifdef CONFIG_SAMA5_UHPHS_RHPORT2
sam_usbhost_vbusdrive(SAM_RHPORT2, true); sam_usbhost_vbusdrive(SAM_RHPORT2, true);
#endif #endif
#ifndef CONFIG_SAMA5_EHCI_RHPORT3 #ifdef CONFIG_SAMA5_UHPHS_RHPORT3
sam_usbhost_vbusdrive(SAM_RHPORT3, true); sam_usbhost_vbusdrive(SAM_RHPORT3, true);
#endif #endif
up_mdelay(50); up_mdelay(50);

View File

@ -50,6 +50,7 @@
#include <nuttx/arch.h> #include <nuttx/arch.h>
#include <nuttx/kmalloc.h> #include <nuttx/kmalloc.h>
#include <nuttx/wqueue.h>
#include <nuttx/usb/usb.h> #include <nuttx/usb/usb.h>
#include <nuttx/usb/ohci.h> #include <nuttx/usb/ohci.h>
#include <nuttx/usb/usbhost.h> #include <nuttx/usb/usbhost.h>
@ -70,10 +71,17 @@
#include "chip/sam_sfr.h" #include "chip/sam_sfr.h"
#include "chip/sam_ohci.h" #include "chip/sam_ohci.h"
#ifdef CONFIG_SAMA5_OHCI
/******************************************************************************* /*******************************************************************************
* Definitions * Definitions
*******************************************************************************/ *******************************************************************************/
/* Configuration ***************************************************************/ /* 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 /* Configurable number of user endpoint descriptors (EDs). This number excludes
* the control endpoint that is always allocated. * the control endpoint that is always allocated.
@ -120,14 +128,14 @@
/* If UDPHS is enabled, then don't use port A */ /* If UDPHS is enabled, then don't use port A */
#ifdef CONFIG_SAMA5_UDPHS #ifdef CONFIG_SAMA5_UDPHS
# undef CONFIG_SAMA5_OHCI_RHPORT1 # undef CONFIG_SAMA5_UHPHS_RHPORT1
#endif #endif
/* For now, suppress use of PORTA in any event. I use that for SAM-BA and /* 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! * would prefer that the board not try to drive VBUS on that port!
*/ */
#undef CONFIG_SAMA5_OHCI_RHPORT1 #undef CONFIG_SAMA5_UHPHS_RHPORT1
/* Debug */ /* Debug */
@ -235,6 +243,7 @@ struct sam_ohci_s
#endif #endif
sem_t exclsem; /* Support mutually exclusive access */ sem_t exclsem; /* Support mutually exclusive access */
sem_t rhssem; /* Semaphore to wait Writeback Done Head event */ sem_t rhssem; /* Semaphore to wait Writeback Done Head event */
struct work_s work; /* Supports interrupt bottom half */
/* Root hub ports */ /* Root hub ports */
@ -362,9 +371,9 @@ static int sam_ctrltd(struct sam_rhport_s *rhport, uint32_t dirpid,
/* Interrupt handling **********************************************************/ /* Interrupt handling **********************************************************/
static void sam_rhsc_interrupt(void); static void sam_rhsc_bottomhalf(void);
static void sam_wdh_interrupt(void); static void sam_wdh_bottomhalf(void);
static int sam_ohci_interrupt(int irq, FAR void *context); static void sam_ohci_bottomhalf(void *arg);
/* USB host controller operations **********************************************/ /* 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: * Description:
* OHCI root hub status change interrupt handler * OHCI root hub status change interrupt handler
* *
*******************************************************************************/ *******************************************************************************/
static void sam_rhsc_interrupt(void) static void sam_rhsc_bottomhalf(void)
{ {
struct sam_rhport_s *rhport; struct sam_rhport_s *rhport;
uint32_t regaddr; uint32_t regaddr;
@ -1813,14 +1822,14 @@ static void sam_rhsc_interrupt(void)
} }
/******************************************************************************* /*******************************************************************************
* Name: sam_wdh_interrupt * Name: sam_wdh_bottomhalf
* *
* Description: * Description:
* OHCI write done head interrupt handler * 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_eplist_s *eplist;
struct sam_gtd_s *td; struct sam_gtd_s *td;
@ -1913,82 +1922,79 @@ static void sam_wdh_interrupt(void)
} }
/******************************************************************************* /*******************************************************************************
* Name: sam_ohci_interrupt * Name: sam_ohci_bottomhalf
* *
* Description: * 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)arg;
uint32_t pending;
uint32_t regval;
/* 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); sam_takesem(&g_ohci.exclsem);
regval = sam_getreg(SAM_USBHOST_INTEN);
ullvdbg("INST: %08x INTEN: %08x\n", intst, regval);
pending = intst & regval; /* Root hub status change interrupt */
if (pending != 0)
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) ullvdbg("Root Hub Status Change\n");
{ sam_rhsc_bottomhalf();
/* Handle root hub status change on each root port */
ullvdbg("Root Hub Status Change\n");
sam_rhsc_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_interrupt();
}
#ifdef CONFIG_DEBUG_USB
if ((pending & SAM_DEBUG_INTS) != 0)
{
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);
}
}
#endif
/* Clear interrupt status register */
sam_putreg(intst, SAM_USBHOST_INTST);
} }
return OK; /* 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();
}
#ifdef CONFIG_DEBUG_USB
if ((pending & SAM_DEBUG_INTS) != 0)
{
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. pending: %08x\n", pending);
sam_wdh_bottomhalf();
}
else
{
ulldbg("ERROR: Unhandled interrupts pending: %08x\n", pending);
}
}
#endif
/* Now re-enable interrupts */
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++) 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? */ /* Has the connection state changed on the RH port? */
if (g_ohci.rhport[rhpndx].connected != connected[rhpndx]) 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); 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); 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 /* Wait for the Writeback Done Head interrupt Loop to handle any false
* alarm semaphore counts. * 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); 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 */ /* Invalidate the D cache to force the ED to be reloaded from RAM */
cp15_invalidate_dcache((uintptr_t)ed, 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 /* "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 * selection between Host Port A and USB Device is controlled by the UDPHS
* enable bit (EN_UDPHS) located in the UDPHS_CTRL control register." * 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 * enabled; then let the device manage port zero. Zero is the reset
* value for all ports; one makes the corresponding port available to OHCI. * value for all ports; one makes the corresponding port available to OHCI.
*/ */
regval = getreg32(SAM_SFR_OHCIICR); regval = getreg32(SAM_SFR_OHCIICR);
#ifdef CONFIG_SAMA5_OHCI_RHPORT1 #ifdef CONFIG_SAMA5_UHPHS_RHPORT1
regval |= SFR_OHCIICR_RES0;
#endif
#ifdef CONFIG_SAMA5_UHPHS_RHPORT2
regval |= SFR_OHCIICR_RES1; regval |= SFR_OHCIICR_RES1;
#endif #endif
#ifdef CONFIG_SAMA5_OHCI_RHPORT2 #ifdef CONFIG_SAMA5_UHPHS_RHPORT3
regval |= SFR_OHCIICR_RES1;
#endif
#ifdef CONFIG_SAMA5_OHCI_RHPORT3
regval |= SFR_OHCIICR_RES2; regval |= SFR_OHCIICR_RES2;
#endif #endif
putreg32(regval, SAM_SFR_OHCIICR); putreg32(regval, SAM_SFR_OHCIICR);
#endif
irqrestore(flags); irqrestore(flags);
/* Note that no pin configuration is required. All USB HS pins have /* 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 */ /* 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"); udbg("Failed to attach IRQ\n");
return NULL; return NULL;
} }
/* Drive Vbus +5V (the smoke test). Should be done elsewhere in OTG /* Drive Vbus +5V (the smoke test).
* mode. *
* 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); sam_usbhost_vbusdrive(SAM_RHPORT1, true);
#endif #endif
#ifndef CONFIG_SAMA5_OHCI_RHPORT2 #ifdef CONFIG_SAMA5_UHPHS_RHPORT2
sam_usbhost_vbusdrive(SAM_RHPORT2, true); sam_usbhost_vbusdrive(SAM_RHPORT2, true);
#endif #endif
#ifndef CONFIG_SAMA5_OHCI_RHPORT3 #ifdef CONFIG_SAMA5_UHPHS_RHPORT3
sam_usbhost_vbusdrive(SAM_RHPORT3, true); sam_usbhost_vbusdrive(SAM_RHPORT3, true);
#endif #endif
up_mdelay(50); 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"); 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 */ up_enable_irq(SAM_IRQ_UHPHS); /* enable USB interrupt */
#endif /* CONFIG_SAMA5_EHCI */
uvdbg("USB OHCI Initialized\n"); uvdbg("USB OHCI Initialized\n");
/* Initialize and return the connection interface */ /* 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; g_ohciconn.enumerate = sam_enumerate;
return &g_ohciconn; 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 */

View File

@ -119,6 +119,20 @@ struct usbhost_connection_s;
FAR struct usbhost_connection_s *sam_ohci_initialize(int controller); FAR struct usbhost_connection_s *sam_ohci_initialize(int controller);
#endif #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 * Name: sam_ehci_initialize
* *