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:
parent
1f3cebdb40
commit
28a103f1f2
@ -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
|
||||||
|
@ -77,6 +77,7 @@
|
|||||||
|
|
||||||
/* OHCI Interrupt Configuration Register */
|
/* OHCI Interrupt Configuration Register */
|
||||||
|
|
||||||
|
#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_RES0 (1 << 0) /* Bit 0: USB port 0 reset */
|
||||||
# define SFR_OHCIICR_RES1 (1 << 1) /* Bit 1: USB port 1 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_RES2 (1 << 2) /* Bit 2: USB port 2 reset */
|
||||||
|
@ -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
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -2707,12 +2733,38 @@ static int sam_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx)
|
|||||||
* 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);
|
||||||
|
@ -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,28 +1922,28 @@ 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;
|
|
||||||
if (pending != 0)
|
|
||||||
{
|
|
||||||
/* Root hub status change interrupt */
|
/* Root hub status change interrupt */
|
||||||
|
|
||||||
if ((pending & OHCI_INT_RHSC) != 0)
|
if ((pending & OHCI_INT_RHSC) != 0)
|
||||||
@ -1942,7 +1951,7 @@ static int sam_ohci_interrupt(int irq, FAR void *context)
|
|||||||
/* Handle root hub status change on each root port */
|
/* Handle root hub status change on each root port */
|
||||||
|
|
||||||
ullvdbg("Root Hub Status Change\n");
|
ullvdbg("Root Hub Status Change\n");
|
||||||
sam_rhsc_interrupt();
|
sam_rhsc_bottomhalf();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Writeback Done Head interrupt */
|
/* Writeback Done Head interrupt */
|
||||||
@ -1955,7 +1964,7 @@ static int sam_ohci_interrupt(int irq, FAR void *context)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
ullvdbg("Writeback Done Head interrupt\n");
|
ullvdbg("Writeback Done Head interrupt\n");
|
||||||
sam_wdh_interrupt();
|
sam_wdh_bottomhalf();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_DEBUG_USB
|
#ifdef CONFIG_DEBUG_USB
|
||||||
@ -1973,22 +1982,19 @@ static int sam_ohci_interrupt(int irq, FAR void *context)
|
|||||||
* interrupt will not be occurring).
|
* interrupt will not be occurring).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ulldbg("ERROR: Unrecoverable error. INTST: %08x\n", intst);
|
ulldbg("ERROR: Unrecoverable error. pending: %08x\n", pending);
|
||||||
sam_wdh_interrupt();
|
sam_wdh_bottomhalf();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ulldbg("ERROR: Unhandled interrupts INTST: %08x\n", intst);
|
ulldbg("ERROR: Unhandled interrupts pending: %08x\n", pending);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Clear interrupt status register */
|
/* Now re-enable interrupts */
|
||||||
|
|
||||||
sam_putreg(intst, SAM_USBHOST_INTST);
|
sam_putreg(OHCI_INT_MIE, SAM_USBHOST_INTEN);
|
||||||
}
|
|
||||||
|
|
||||||
return OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -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
|
||||||
@ -3150,25 +3192,30 @@ FAR struct usbhost_connection_s *sam_ohci_initialize(int controller)
|
|||||||
|
|
||||||
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 */
|
||||||
|
@ -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
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user