diff options
author | Gregory Nutt <gnutt@nuttx.org> | 2013-08-21 11:07:42 -0600 |
---|---|---|
committer | Gregory Nutt <gnutt@nuttx.org> | 2013-08-21 11:07:42 -0600 |
commit | bd328a7de0472a500a5aed2b6b6642ba946f5aed (patch) | |
tree | 6ca56f66b11891dad5f829dc545a0238aa5bc777 | |
parent | 5ef1ef85e034790f9a3e5f799912f15e55ede1bf (diff) | |
download | nuttx-bd328a7de0472a500a5aed2b6b6642ba946f5aed.tar.gz nuttx-bd328a7de0472a500a5aed2b6b6642ba946f5aed.tar.bz2 nuttx-bd328a7de0472a500a5aed2b6b6642ba946f5aed.zip |
Move all SAMA5 EHCI interrupt handling to the worker thread
-rw-r--r-- | nuttx/arch/arm/src/sama5/Kconfig | 6 | ||||
-rwxr-xr-x | nuttx/arch/arm/src/sama5/sam_ehci.c | 430 | ||||
-rw-r--r-- | nuttx/configs/sama5d3x-ek/README.txt | 28 | ||||
-rwxr-xr-x | nuttx/include/nuttx/usb/ehci.h | 7 |
4 files changed, 442 insertions, 29 deletions
diff --git a/nuttx/arch/arm/src/sama5/Kconfig b/nuttx/arch/arm/src/sama5/Kconfig index 0bd3ac4d4..aadf40224 100644 --- a/nuttx/arch/arm/src/sama5/Kconfig +++ b/nuttx/arch/arm/src/sama5/Kconfig @@ -386,14 +386,14 @@ config SAMA5_EHCI_NQHS default 4 ---help--- Configurable number of Queue Head (QH) structures. The default is - one per Root hub port plus one for EP0. + one per Root hub port plus one for EP0 (4). config SAMA5_EHCI_NQTDS int "Number of Queue Element Transfer Descriptor (qTDs)" - default 4 + default 6 ---help--- Configurable number of Queue Element Transfer Descriptor (qTDs). - The default is one per root hub plus three from EP0. + The default is one per root hub plus three from EP0 (6). config SAMA5_EHCI_BUFSIZE int "Size of one request/descriptor buffer" diff --git a/nuttx/arch/arm/src/sama5/sam_ehci.c b/nuttx/arch/arm/src/sama5/sam_ehci.c index e4bd5113a..d46909626 100755 --- a/nuttx/arch/arm/src/sama5/sam_ehci.c +++ b/nuttx/arch/arm/src/sama5/sam_ehci.c @@ -49,6 +49,7 @@ #include <nuttx/arch.h> #include <nuttx/kmalloc.h> +#include <nuttx/wqueue.h> #include <nuttx/usb/usb.h> #include <nuttx/usb/usbhost.h> #include <nuttx/usb/ehci.h> @@ -67,6 +68,11 @@ * Pre-processor Definitions *******************************************************************************/ /* Configuration ***************************************************************/ +/* Pre-requisites */ + +#ifndef CONFIG_SCHED_WORKQUEUE +# error Work queue support is required (CONFIG_SCHED_WORKQUEUE) +#endif /* Configurable number of Queue Head (QH) structures. The default is one per * Root hub port plus one for EP0. @@ -96,6 +102,12 @@ # undef CONFIG_SAMA5_EHCI_REGDEBUG #endif +/* Driver-private Definitions **************************************************/ + +#define EHCI_HANDLED_INTS (EHCI_INT_USBINT | EHCI_INT_USBERRINT | \ + EHCI_INT_PORTSC | EHCI_INT_SYSERROR | \ + EHCI_INT_AAINT) + /******************************************************************************* * Private Types *******************************************************************************/ @@ -140,11 +152,12 @@ typedef int (*foreach_qtd_t)(struct sam_qtd_s *qtd, uint32_t **bp, void *arg); struct sam_epinfo_s { - uint8_t epno; /* Endpoint number */ - uint8_t devaddr; /* Device address */ - uint8_t xfrtype; /* See USB_EP_ATTR_XFER_* definitions in usb.h */ - uint8_t speed; /* See USB_*_SPEED definitions in ehci.h */ - uint8_t flags; /* See EPINFO_FLAG_* definitions above */ + uint8_t epno:7; /* Endpoint number */ + uint8_t dirin:1; /* 1:IN endpoint 0:OUT endpoint */ + uint8_t devaddr:7; /* Device address */ + uint8_t toggle:1; /* Next data toggle */ + uint8_t xfrtype:2; /* See USB_EP_ATTR_XFER_* definitions in usb.h */ + uint8_t speed:2; /* See USB_*_SPEED definitions in ehci.h */ volatile bool wait; /* TRUE: Thread is waiting for transfer completion */ uint16_t maxpacket; /* Maximum packet size */ sem_t wsem; /* Semaphore used to wait for transfer completion */ @@ -176,13 +189,14 @@ struct sam_rhport_s struct sam_ehci_s { - volatile bool rhwait; /* TRUE: Thread is waiting for root hub event */ + volatile bool pscwait; /* TRUE: Thread is waiting for port status change event */ sem_t exclsem; /* Support mutually exclusive access */ - sem_t rhsem; /* Semaphore to wait for root hub events */ + sem_t pscsem; /* Semaphore to wait for port status change events */ struct sam_epinfo_s ep0; /* Endpoint 0 */ struct sam_list_s *qhfree; /* List of free Queue Head (QH) structures */ struct sam_list_s *qtdfree; /* List of free Queue Element Transfer Descriptor (qTD) */ + struct work_s work; /* Supports interrupt bottom half */ /* Root hub ports */ @@ -251,7 +265,13 @@ static int sam_qh_flush(struct sam_qh_s *qh); /* Interrupt Handling **********************************************************/ -static int sam_ehci_interrupt(int irq, FAR void *context); +static inline void sam_ioc_bottomhalf(void); +static inline void sam_err_bottomhalf(void); +static inline void sam_portsc_bottomhalf(void); +static inline void sam_syserr_bottomhalf(void); +static inline void sam_async_advance_bottomhalf(void); +static void sam_ehci_bottomhalf(FAR void *arg); +static int sam_ehci_tophalf(int irq, FAR void *context); /* USB Host Controller Operations **********************************************/ @@ -587,7 +607,7 @@ static int ehci_wait_usbsts(uint32_t maskbits, uint32_t donebits, /* We got here because either the waited for condition or a timeout * occurred. Return a value to indicate which. */ - + return (regval == donebits) ? OK : -ETIMEDOUT; } @@ -1000,16 +1020,374 @@ static int sam_qh_flush(struct sam_qh_s *qh) *******************************************************************************/ /******************************************************************************* - * Name: sam_ehci_interrupt + * Name: sam_ioc_bottomhalf + * + * Description: + * EHCI USB Interrupt (USBINT) "Bottom Half" interrupt handler + * + * "The Host Controller sets this bit to 1 on the completion of a USB + * transaction, which results in the retirement of a Transfer Descriptor that + * had its IOC bit set. + * + * "The Host Controller also sets this bit to 1 when a short packet is detected + * (actual number of bytes received was less than the expected number of + * bytes)." + * + *******************************************************************************/ + +static inline void sam_ioc_bottomhalf(void) +{ + ullvdbg("USB Interrupt (USBINT) Interrupt\n"); +} + +/******************************************************************************* + * Name: sam_ioc_bottomhalf + * + * Description: + * EHCI USB Error Interrupt (USBERRINT) "Bottom Half" interrupt handler + * + * "The Host Controller sets this bit to 1 when completion of a USB transaction + * results in an error condition (e.g., error counter underflow). If the TD on + * which the error interrupt occurred also had its IOC bit set, both this bit + * and USBINT bit are set. ..." + * + *******************************************************************************/ + +static inline void sam_err_bottomhalf(void) +{ + ulldbg("USB Error Interrupt (USBERRINT) Interrupt\n"); + + /* Remove all queued transfers */ +#warning Missing logic +} + +/******************************************************************************* + * Name: sam_portsc_bottomhalf + * + * Description: + * EHCI Port Change Detect "Bottom Half" interrupt handler + * + * "The Host Controller sets this bit to a one when any port for which the Port + * Owner bit is set to zero ... has a change bit transition from a zero to a + * one or a Force Port Resume bit transition from a zero to a one as a result + * of a J-K transition detected on a suspended port. This bit will also be set + * as a result of the Connect Status Change being set to a one after system + * software has relinquished ownership of a connected port by writing a one + * to a port's Port Owner bit... + * + * "This bit is allowed to be maintained in the Auxiliary power well. + * Alternatively, it is also acceptable that on a D3 to D0 transition of the + * EHCI HC device, this bit is loaded with the OR of all of the PORTSC change + * bits (including: Force port resume, over-current change, enable/disable + * change and connect status change)." + * + *******************************************************************************/ + +static inline void sam_portsc_bottomhalf(void) +{ + struct sam_rhport_s *rhport; + uint32_t portsc; + int rhpndx; + + /* Handle root hub status change on each root port */ + + for (rhpndx = 0; rhpndx < SAM_EHCI_NRHPORT; rhpndx++) + { + rhport = &g_ehci.rhport[rhpndx]; + portsc = sam_getreg(&HCOR->portsc[rhpndx]); + + ullvdbg("PORTSC%d: %08x\n", rhpndx + 1, portsc); + + /* Handle port connection status change (CSC) events */ + + if ((portsc & EHCI_PORTSC_CSC) != 0) + { + ullvdbg("Connect Status Change\n"); + + /* Check current connect status */ + + if ((portsc & EHCI_PORTSC_CCS) != 0) + { + /* Connected ... Did we just become connected? */ + + if (!rhport->connected) + { + /* Yes.. connected. */ + + rhport->connected = true; + + ullvdbg("RHPort%d connected, pscwait: %d\n", + rhpndx + 1, g_ehci.pscwait); + + /* Notify any waiters */ + + if (g_ehci.pscwait) + { + sam_givesem(&g_ehci.pscsem); + g_ehci.pscwait = false; + } + } + else + { + ulldbg("Already connected\n"); + } + } + else + { + /* Disconnected... Did we just become disconnected? */ + + if (rhport->connected) + { + /* Yes.. disconnect the device */ + + ullvdbg("RHport%d disconnected\n", rhpndx+1); + rhport->connected = false; + rhport->lowspeed = false; + + /* Are we bound to a class instance? */ + + if (rhport->class) + { + /* Yes.. Disconnect the class */ + + CLASS_DISCONNECTED(rhport->class); + rhport->class = NULL; + } + + /* Notify any waiters for the Root Hub Status change + * event. + */ + + if (g_ehci.pscwait) + { + sam_givesem(&g_ehci.pscsem); + g_ehci.pscwait = false; + } + } + else + { + ulldbg("Already disconnected\n"); + } + } + } + + /* Clear all pending port interrupt sources by writing a '1' to the + * corresponding bit in the PORTSC register. + */ + + sam_putreg(portsc & EHCI_PORTSC_ALLINTS, &HCOR->portsc[rhpndx]); + } +} + +/******************************************************************************* + * Name: sam_syserr_bottomhalf + * + * Description: + * EHCI Host System Error "Bottom Half" interrupt handler + * + * "The Host Controller sets this bit to 1 when a serious error occurs during a + * host system access involving the Host Controller module. ... When this + * error occurs, the Host Controller clears the Run/Stop bit in the Command + * register to prevent further execution of the scheduled TDs." + * + *******************************************************************************/ + +static inline void sam_syserr_bottomhalf(void) +{ + ulldbg("Host System Error Interrupt\n"); + PANIC(); +} + +/******************************************************************************* + * Name: sam_async_advance_bottomhalf + * + * Description: + * EHCI Async Advance "Bottom Half" interrupt handler + * + * "System software can force the host controller to issue an interrupt the + * next time the host controller advances the asynchronous schedule by writing + * a one to the Interrupt on Async Advance Doorbell bit in the USBCMD + * register. This status bit indicates the assertion of that interrupt + * source." + * + *******************************************************************************/ + +static inline void sam_async_advance_bottomhalf(void) +{ + ulldbg("Async Advance Interrupt\n"); + + /* Remove all tagged QH entries */ +#warning Missing logic +} + +/******************************************************************************* + * Name: sam_ehci_bottomhalf + * + * Description: + * EHCI "Bottom Half" interrupt handler + * + *******************************************************************************/ + +static void sam_ehci_bottomhalf(FAR void *arg) +{ + uint32_t pending = (uint32_t)arg; + uint32_t regval; + + /* Handle all unmasked interrupt sources */ + /* USB Interrupt (USBINT) + * + * "The Host Controller sets this bit to 1 on the completion of a USB + * transaction, which results in the retirement of a Transfer Descriptor + * that had its IOC bit set. + * + * "The Host Controller also sets this bit to 1 when a short packet is + * detected (actual number of bytes received was less than the expected + * number of bytes)." + */ + + if ((pending & EHCI_INT_USBINT) != 0) + { + sam_ioc_bottomhalf(); + } + + /* USB Error Interrupt (USBERRINT) + * + * "The Host Controller sets this bit to 1 when completion of a USB + * transaction results in an error condition (e.g., error counter + * underflow). If the TD on which the error interrupt occurred also + * had its IOC bit set, both this bit and USBINT bit are set. ..." + */ + + if ((pending & EHCI_INT_USBERRINT) != 0) + { + sam_err_bottomhalf(); + } + + /* Port Change Detect + * + * "The Host Controller sets this bit to a one when any port for which + * the Port Owner bit is set to zero ... has a change bit transition + * from a zero to a one or a Force Port Resume bit transition from a zero + * to a one as a result of a J-K transition detected on a suspended port. + * This bit will also be set as a result of the Connect Status Change + * being set to a one after system software has relinquished ownership + * of a connected port by writing a one to a port's Port Owner bit... + * + * "This bit is allowed to be maintained in the Auxiliary power well. + * Alternatively, it is also acceptable that on a D3 to D0 transition + * of the EHCI HC device, this bit is loaded with the OR of all of the + * PORTSC change bits (including: Force port resume, over-current change, + * enable/disable change and connect status change)." + */ + + if ((pending & EHCI_INT_PORTSC) != 0) + { + sam_portsc_bottomhalf(); + } + + /* Frame List Rollover + * + * "The Host Controller sets this bit to a one when the Frame List Index ... + * rolls over from its maximum value to zero. The exact value at which + * the rollover occurs depends on the frame list size. For example, if + * the frame list size (as programmed in the Frame List Size field of the + * USBCMD register) is 1024, the Frame Index Register rolls over every + * time FRINDEX[13] toggles. Similarly, if the size is 512, the Host + * Controller sets this bit to a one every time FRINDEX[12] toggles." + */ + +#if 0 /* Not used */ + if ((pending & EHCI_INT_FLROLL) != 0) + { + sam_flroll_bottomhalf(); + } +#endif + + /* Host System Error + * + * "The Host Controller sets this bit to 1 when a serious error occurs + * during a host system access involving the Host Controller module. ... + * When this error occurs, the Host Controller clears the Run/Stop bit + * in the Command register to prevent further execution of the scheduled + * TDs." + */ + + if ((pending & EHCI_INT_SYSERROR) != 0) + { + sam_syserr_bottomhalf(); + } + + /* Interrupt on Async Advance + * + * "System software can force the host controller to issue an interrupt + * the next time the host controller advances the asynchronous schedule + * by writing a one to the Interrupt on Async Advance Doorbell bit in + * the USBCMD register. This status bit indicates the assertion of that + * interrupt source." + */ + + if ((pending & EHCI_INT_AAINT) != 0) + { + sam_async_advance_bottomhalf(); + } + + /* Re-enable relevant EHCI interrupts. Interrupts should still be enabled + * at the level of the AIC. + */ + + sam_putreg(EHCI_HANDLED_INTS, &HCOR->usbintr); +} + +/******************************************************************************* + * Name: sam_ehci_tophalf * * Description: - * EHCI interrupt handler + * EHCI "Top Half" interrupt handler * *******************************************************************************/ -static int sam_ehci_interrupt(int irq, FAR void *context) +static int sam_ehci_tophalf(int irq, FAR void *context) { -#warning "Missing logic" + uint32_t usbsts; + uint32_t pending; + uint32_t regval; + + /* Read Interrupt Status and mask out interrupts that are not enabled. */ + + usbsts = sam_getreg(&HCOR->usbsts); + regval = sam_getreg(&HCOR->usbintr); + ullvdbg("USBSTS: %08x USBINTR: %08x\n", usbsts, regval); + + /* Handle all unmasked interrupt sources */ + + pending = usbsts & 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 EHCI interrupts. + */ + + DEBUGASSERT(work_available(&g_ehci.work)); + DEBUGVERIFY(work_queue(HPWORK, &g_ehci.work, sam_ehci_bottomhalf, + (FAR void *)pending, 0)); + + /* Disable further EHCI interrupts so that we do not overrun the work + * queue. + */ + + sam_putreg(0, &HCOR->usbintr); + + /* Clear all pending status bits by writing the value of the pending + * interrupt bits back to the status register. + */ + + sam_putreg(usbsts & ECHI_INT_ALLINTS, &HCOR->usbsts); + } + return OK; } @@ -1080,8 +1458,8 @@ static int sam_wait(FAR struct usbhost_connection_s *conn, * and check again */ - g_ehci.rhwait = true; - sam_takesem(&g_ehci.rhsem); + g_ehci.pscwait = true; + sam_takesem(&g_ehci.pscsem); } } @@ -1833,7 +2211,7 @@ FAR struct usbhost_connection_s *sam_ehci_initialize(int controller) /* Initialize the EHCI state data structure */ sem_init(&g_ehci.exclsem, 0, 1); - sem_init(&g_ehci.rhsem, 0, 0); + sem_init(&g_ehci.pscsem, 0, 0); /* Initialize EP0 */ @@ -1894,20 +2272,26 @@ FAR struct usbhost_connection_s *sam_ehci_initialize(int controller) /* Interrupt Configuration ***************************************************/ - /* Clear pending interrupts */ -#warning Missing logic - - /* Enable EHCI interrupts */ -#warning Missing logic - /* Attach USB host controller interrupt handler */ - if (irq_attach(SAM_IRQ_UHPHS, sam_ehci_interrupt) != 0) + if (irq_attach(SAM_IRQ_UHPHS, sam_ehci_tophalf) != 0) { udbg("ERROR: Failed to attach IRQ\n"); return NULL; } + /* Clear pending interrupts. Bits in the USBSTS register are cleared by + * writing a '1' to the corresponding bit. + */ + + sam_putreg(ECHI_INT_ALLINTS, &HCOR->usbsts); + + /* Enable EHCI interrupts. Interrupts are still disabled at the level of + * the AIC. + */ + + sam_putreg(EHCI_HANDLED_INTS, &HCOR->usbintr); + /* Drive Vbus +5V (the smoke test). Should be done elsewhere in OTG * mode. */ diff --git a/nuttx/configs/sama5d3x-ek/README.txt b/nuttx/configs/sama5d3x-ek/README.txt index e34da3036..d7eadc4cd 100644 --- a/nuttx/configs/sama5d3x-ek/README.txt +++ b/nuttx/configs/sama5d3x-ek/README.txt @@ -1166,7 +1166,7 @@ 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 change + 10) Support the USB full-speed OHCI host driver can be enabled by changing the NuttX configuration file as follows: System Type -> ATSAMA5 Peripheral Support @@ -1193,6 +1193,28 @@ Configurations multiple of the 48MHz needed for OHCI. The delay loop calibration 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: + + 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 + Device Drivers + CONFIG_USBHOST=y : Enable USB host support + + Device Drivers -> USB Host Driver Support + CONFIG_USBHOST_ISOC_DISABLE=y : Isochronous endpoints not used + CONFIG_USBHOST_MSC=y : Enable the mass storage class driver + + Library Routines + CONFIG_SCHED_WORKQUEUE : Worker thread support is required + + Application Configuration -> NSH Library + CONFIG_NSH_ARCHINIT=y : NSH board-initialization + STATUS: 2013-7-19: This configuration (as do the others) run at 396MHz. The SAMA5D3 can run at 536MHz. I still need to figure out the @@ -1255,6 +1277,10 @@ Configurations d) OHCI will support 3 downstream points, but I currently have only one enabled. + 2013-8-20: Added description to add EHCI to the configuration. At + present, however, EHCI is still a work in progress and not ready for + prime time. + ostest: This configuration directory, performs a simple OS test using examples/ostest. diff --git a/nuttx/include/nuttx/usb/ehci.h b/nuttx/include/nuttx/usb/ehci.h index ee65339e0..114736932 100755 --- a/nuttx/include/nuttx/usb/ehci.h +++ b/nuttx/include/nuttx/usb/ehci.h @@ -282,11 +282,11 @@ #define EHCI_INT_USBINT (1 << 0) /* Bit 0: USB Interrupt */ #define EHCI_INT_USBERRINT (1 << 1) /* Bit 1: USB Error Interrupt */ -#define EHCI_INT_PCHG (1 << 2) /* Bit 2: Port Change Detect */ +#define EHCI_INT_PORTSC (1 << 2) /* Bit 2: Port Change Detect */ #define EHCI_INT_FLROLL (1 << 3) /* Bit 3: Frame List Rollover */ #define EHCI_INT_SYSERROR (1 << 4) /* Bit 4: Host System Error */ #define EHCI_INT_AAINT (1 << 5) /* Bit 5: Interrupt on Async Advance */ - +#define ECHI_INT_ALLINTS (0x3f) /* Bits 0-5: All interrupts */ /* Bits 6-11: Reserved */ #define EHCI_USBSTS_HALTED (1 << 12) /* Bit 12: HC Halted */ #define EHCI_USBSTS_RECLAM (1 << 13) /* Bit 13: Reclamation */ @@ -352,6 +352,9 @@ #define EHCI_PORTSC_WKOCE (1 << 22) /* Bit 22: Wake on Over-current Enable */ /* Bits 23-31: Reserved */ +#define EHCI_PORTSC_ALLINTS (EHCI_PORTSC_CSC | EHCI_PORTSC_PEC | \ + EHCI_PORTSC_OCC | EHCI_PORTSC_RESUME) + /* Data Structures **************************************************************************/ /* Paragraph 3 */ |