Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Diff: targets/TARGET_Silicon_Labs/src/em_usbdint.c
- Revision:
- 72:53949e6131f6
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/TARGET_Silicon_Labs/src/em_usbdint.c Thu Jul 27 12:14:04 2017 +0100
@@ -0,0 +1,946 @@
+/**************************************************************************//**
+ * @file em_usbdint.c
+ * @brief USB protocol stack library, USB device peripheral interrupt handlers.
+ * @version 3.20.14
+ ******************************************************************************
+ * @section License
+ * <b>(C) Copyright 2014 Silicon Labs, http://www.silabs.com</b>
+ *******************************************************************************
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+#include "em_device.h"
+#if defined( USB_PRESENT ) && ( USB_COUNT == 1 )
+#include "em_usb.h"
+#if defined( USB_DEVICE )
+
+#include "em_cmu.h"
+#include "em_usbtypes.h"
+#include "em_usbhal.h"
+#include "em_usbd.h"
+
+#ifdef __MBED__
+extern void usbhal_allow_em2(bool em2_allow);
+#endif
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+#define HANDLE_INT( x ) if ( status & x ) { Handle_##x(); status &= ~x; }
+
+static void Handle_USB_GINTSTS_ENUMDONE ( void );
+static void Handle_USB_GINTSTS_IEPINT ( void );
+static void Handle_USB_GINTSTS_OEPINT ( void );
+static void Handle_USB_GINTSTS_RESETDET ( void );
+static void Handle_USB_GINTSTS_SOF ( void );
+static void Handle_USB_GINTSTS_USBRST ( void );
+static void Handle_USB_GINTSTS_USBSUSP ( void );
+static void Handle_USB_GINTSTS_WKUPINT ( void );
+#if defined( USB_DOEP0INT_STUPPKTRCVD )
+static void HandleOutEpIntr( uint32_t status, USBD_Ep_TypeDef *ep );
+#else
+static void ProcessSetup ( void );
+static void ProcessOepData ( USBD_Ep_TypeDef *ep );
+#endif
+
+#if ( USB_PWRSAVE_MODE )
+/* Variables and prototypes for USB powerdown (suspend) functionality. */
+static bool UsbPowerDown( void );
+static bool UsbPowerUp( void );
+
+volatile bool USBD_poweredDown = false;
+
+/* Storage for backing up USB core registers. */
+static uint32_t x_USB_GINTMSK;
+#if defined(_USB_GOTGCTL_MASK)
+static uint32_t x_USB_GOTGCTL;
+#endif
+static uint32_t x_USB_GAHBCFG;
+static uint32_t x_USB_GUSBCFG;
+static uint32_t x_USB_GRXFSIZ;
+static uint32_t x_USB_GNPTXFSIZ;
+static uint32_t x_USB_DCFG;
+static uint32_t x_USB_DCTL;
+static uint32_t x_USB_DAINTMSK;
+static uint32_t x_USB_DIEPMSK;
+static uint32_t x_USB_DOEPMSK;
+static uint32_t x_USB_PCGCCTL;
+
+#if ( NUM_EP_USED > 0 )
+static uint32_t x_USB_EP_CTL[ NUM_EP_USED ];
+static uint32_t x_USB_EP_TSIZ[ NUM_EP_USED ];
+static uint32_t x_USB_EP_DMAADDR[ NUM_EP_USED ];
+#endif
+
+#if ( NUM_EP_USED > MAX_NUM_TX_FIFOS )
+#define FIFO_CNT MAX_NUM_TX_FIFOS
+#else
+#define FIFO_CNT NUM_EP_USED
+#endif
+
+#if ( FIFO_CNT > 0 )
+static uint32_t x_USB_DIEPTXFS[ FIFO_CNT ];
+#endif
+
+#if ( USB_PWRSAVE_MODE )
+static uint32_t cmuStatus = 0;
+#endif
+
+#endif /* if ( USB_PWRSAVE_MODE ) */
+
+/*
+ * USB_IRQHandler() is the first level handler for the USB peripheral interrupt.
+ */
+void USB_IRQHandler( void )
+{
+ uint32_t status;
+ bool servedVbusInterrupt = false;
+
+ INT_Disable();
+
+#if ( USB_PWRSAVE_MODE )
+ if ( USBD_poweredDown )
+ {
+ /* Switch USBC clock from 32kHz to a 48MHz clock to be able to */
+ /* read USB peripheral registers. */
+ /* If we woke up from EM2, HFCLK is now HFRCO. */
+
+ /* Restore clock oscillators.*/
+#if defined( CMU_OSCENCMD_USHFRCOEN )
+ if ( ( CMU->STATUS & CMU_STATUS_USHFRCOENS ) == 0 )/*Wakeup from EM2 ?*/
+ {
+ CMU->OSCENCMD = ( cmuStatus
+ & ( CMU_STATUS_AUXHFRCOENS | CMU_STATUS_HFXOENS ) )
+ | CMU_OSCENCMD_USHFRCOEN;
+ }
+#else
+ if ( ( CMU->STATUS & CMU_STATUS_HFXOENS ) == 0 ) /* Wakeup from EM2 ? */
+ {
+ CMU->OSCENCMD = cmuStatus
+ & ( CMU_STATUS_AUXHFRCOENS | CMU_STATUS_HFXOENS );
+ }
+#endif
+
+ /* Select correct USBC clock.*/
+#if defined( CMU_OSCENCMD_USHFRCOEN )
+ CMU->CMD = CMU_CMD_USBCCLKSEL_USHFRCO;
+ while ( ( CMU->STATUS & CMU_STATUS_USBCUSHFRCOSEL ) == 0 ){}
+#else
+ CMU->CMD = CMU_CMD_USBCCLKSEL_HFCLKNODIV;
+ while ( ( CMU->STATUS & CMU_STATUS_USBCHFCLKSEL ) == 0 ){}
+#endif
+ }
+#endif /* if ( USB_PWRSAVE_MODE ) */
+
+ if ( USB->IF && ( USB->CTRL & USB_CTRL_VREGOSEN ) )
+ {
+ if ( USB->IF & USB_IF_VREGOSH )
+ {
+ USB->IFC = USB_IFC_VREGOSH;
+
+ if ( USB->STATUS & USB_STATUS_VREGOS )
+ {
+ servedVbusInterrupt = true;
+ DEBUG_USB_INT_LO_PUTS( "\nVboN" );
+
+#if ( USB_PWRSAVE_MODE )
+ if ( UsbPowerUp() )
+ {
+ USBDHAL_EnableUsbResetAndSuspendInt();
+ }
+ USBD_SetUsbState( USBD_STATE_POWERED );
+#endif
+ }
+ }
+
+ if ( USB->IF & USB_IF_VREGOSL )
+ {
+ USB->IFC = USB_IFC_VREGOSL;
+
+ if ( ( USB->STATUS & USB_STATUS_VREGOS ) == 0 )
+ {
+ servedVbusInterrupt = true;
+ DEBUG_USB_INT_LO_PUTS( "\nVboF" );
+
+#if ( USB_PWRSAVE_MODE )
+#if ( USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF )
+ if ( !USBD_poweredDown )
+ {
+ USB->GINTMSK = 0;
+ USB->GINTSTS = 0xFFFFFFFF;
+ }
+
+ UsbPowerDown();
+#endif
+ USBD_SetUsbState( USBD_STATE_NONE );
+#endif
+ }
+ }
+ }
+
+ status = USBHAL_GetCoreInts();
+ if ( status == 0 )
+ {
+ INT_Enable();
+ if ( !servedVbusInterrupt )
+ {
+ DEBUG_USB_INT_LO_PUTS( "\nSinT" );
+ }
+ return;
+ }
+
+ HANDLE_INT( USB_GINTSTS_RESETDET )
+ HANDLE_INT( USB_GINTSTS_WKUPINT )
+ HANDLE_INT( USB_GINTSTS_USBSUSP )
+ HANDLE_INT( USB_GINTSTS_SOF )
+ HANDLE_INT( USB_GINTSTS_ENUMDONE )
+ HANDLE_INT( USB_GINTSTS_USBRST )
+ HANDLE_INT( USB_GINTSTS_IEPINT )
+ HANDLE_INT( USB_GINTSTS_OEPINT )
+
+ INT_Enable();
+
+ if ( status != 0 )
+ {
+ DEBUG_USB_INT_LO_PUTS( "\nUinT" );
+ }
+}
+
+/*
+ * Handle port enumeration interrupt. This has nothing to do with normal
+ * device enumeration.
+ */
+static void Handle_USB_GINTSTS_ENUMDONE( void )
+{
+#if ( USB_PWRSAVE_MODE )
+ UsbPowerUp();
+#endif
+
+ USBDHAL_Ep0Activate( dev->ep0MpsCode );
+ dev->ep[ 0 ].state = D_EP_IDLE;
+ USBDHAL_EnableInts( dev );
+ DEBUG_USB_INT_LO_PUTS( "EnumD" );
+}
+
+/*
+ * Handle IN endpoint transfer interrupt.
+ */
+static void Handle_USB_GINTSTS_IEPINT( void )
+{
+ int epnum;
+ uint16_t epint;
+ uint16_t epmask;
+ uint32_t status;
+ USBD_Ep_TypeDef *ep;
+
+ DEBUG_USB_INT_HI_PUTCHAR( 'i' );
+
+ epint = USBDHAL_GetAllInEpInts();
+ for ( epnum = 0, epmask = 1;
+ epnum <= MAX_NUM_IN_EPS;
+ epnum++, epmask <<= 1 )
+ {
+ if ( epint & epmask )
+ {
+ ep = USBD_GetEpFromAddr( USB_SETUP_DIR_MASK | epnum );
+ status = USBDHAL_GetInEpInts( ep );
+
+ if ( status & USB_DIEP_INT_XFERCOMPL )
+ {
+ USB_DINEPS[ epnum ].INT = USB_DIEP_INT_XFERCOMPL;
+
+ DEBUG_USB_INT_HI_PUTCHAR( 'c' );
+
+ if ( epnum == 0 )
+ {
+ if ( ep->remaining > ep->packetSize )
+ {
+ ep->remaining -= ep->packetSize;
+ ep->xferred += ep->packetSize;
+ }
+ else
+ {
+ ep->xferred += ep->remaining;
+ ep->remaining = 0;
+ }
+ USBDEP_Ep0Handler( dev );
+ }
+ else
+ {
+ ep->xferred = ep->remaining -
+ ( ( USB_DINEPS[ epnum ].TSIZ &
+ _USB_DIEP_TSIZ_XFERSIZE_MASK ) >>
+ _USB_DIEP_TSIZ_XFERSIZE_SHIFT );
+ ep->remaining -= ep->xferred;
+
+ USBDEP_EpHandler( ep->addr );
+#if defined( USB_DOEP0INT_STUPPKTRCVD )
+ if ( USB_DINEPS[ ep->num ].INT & USB_DIEP_INT_NAKINTRPT )
+ {
+ USB_DINEPS[ ep->num ].INT = USB_DIEP_INT_NAKINTRPT;
+ }
+#endif
+ }
+ }
+ }
+ }
+}
+
+/*
+ * Handle OUT endpoint transfer interrupt.
+ */
+static void Handle_USB_GINTSTS_OEPINT( void )
+{
+ int epnum;
+ uint16_t epint;
+ uint16_t epmask;
+ uint32_t status;
+ USBD_Ep_TypeDef *ep;
+
+ DEBUG_USB_INT_HI_PUTCHAR( 'o' );
+
+ epint = USBDHAL_GetAllOutEpInts();
+ for ( epnum = 0, epmask = 1;
+ epnum <= MAX_NUM_OUT_EPS;
+ epnum++, epmask <<= 1 )
+ {
+ if ( epint & epmask )
+ {
+ ep = USBD_GetEpFromAddr( epnum );
+ status = USBDHAL_GetOutEpInts( ep );
+
+#if defined( USB_DOEP0INT_STUPPKTRCVD )
+ HandleOutEpIntr( status, ep );
+#else
+ if ( status & USB_DOEP_INT_XFERCOMPL )
+ {
+ USB_DOUTEPS[ epnum ].INT = USB_DOEP_INT_XFERCOMPL;
+ DEBUG_USB_INT_HI_PUTCHAR( 'c' );
+ ProcessOepData( ep );
+ }
+
+ /* Setup Phase Done */
+ if ( status & USB_DOEP0INT_SETUP )
+ {
+ ProcessSetup();
+ }
+#endif
+ }
+ }
+}
+
+#if !defined( USB_DOEP0INT_STUPPKTRCVD )
+static void ProcessOepData( USBD_Ep_TypeDef *ep )
+{
+ if ( ep->num == 0 )
+ {
+
+#ifdef __MBED__
+ int xfer_size = ep->packetSize - (( USB->DOEP0TSIZ & _USB_DOEP0TSIZ_XFERSIZE_MASK )
+ >> _USB_DOEP0TSIZ_XFERSIZE_SHIFT);
+ int setup_pkt_received = USBDHAL_GetOutEpInts( ep ) & USB_DOEP0INT_SETUP;
+
+ if ( (!setup_pkt_received && xfer_size == 0) ||
+ (setup_pkt_received && xfer_size == 8) )
+ {
+ /* Higher levels need to see the correct transfer amount for ZLPs */
+ ep->remaining = 0;
+ ep->xferred = 0;
+ }
+ else
+ {
+ /* FIXME - does not work if actual read size > 56 */
+ if ( setup_pkt_received ) xfer_size -= 8;
+
+ ep->xferred = xfer_size;
+ ep->remaining -= xfer_size;
+ }
+#else
+ if ( ep->remaining > ep->packetSize )
+ {
+ ep->remaining -= ep->packetSize;
+ ep->xferred += ep->packetSize;
+ }
+ else
+ {
+ ep->xferred += ep->remaining;
+ ep->remaining = 0;
+ }
+#endif
+
+ USBDEP_Ep0Handler( dev );
+ }
+ else
+ {
+ ep->xferred = ep->hwXferSize -
+ ( ( USB_DOUTEPS[ ep->num ].TSIZ & _USB_DOEP_TSIZ_XFERSIZE_MASK )>>
+ _USB_DOEP_TSIZ_XFERSIZE_SHIFT );
+ ep->remaining -= ep->xferred;
+ USBDEP_EpHandler( ep->addr );
+ }
+}
+#endif
+
+#if !defined( USB_DOEP0INT_STUPPKTRCVD )
+static void ProcessSetup( void )
+{
+ DEBUG_USB_INT_LO_PUTS( "\nS" );
+
+ if ( USB->DOEP0INT & USB_DOEP0INT_BACK2BACKSETUP )
+ { /* Back to back setup packets received */
+ USB->DOEP0INT = USB_DOEP0INT_BACK2BACKSETUP;
+ DEBUG_USB_INT_LO_PUTS( "B2B" );
+
+ dev->setup = (USB_Setup_TypeDef*)( USB->DOEP0DMAADDR - USB_SETUP_PKT_SIZE );
+ }
+ else
+ {
+ /* Read SETUP packet counter from hw. */
+ int supCnt = ( USB->DOEP0TSIZ & _USB_DOEP0TSIZ_SUPCNT_MASK )
+ >> _USB_DOEP0TSIZ_SUPCNT_SHIFT;
+
+ if ( supCnt == 3 )
+ supCnt = 2;
+
+ dev->setup = &dev->setupPkt[ 2 - supCnt ];
+ }
+ USB->DOEP0TSIZ |= 3 << _USB_DOEP0TSIZ_SUPCNT_SHIFT;
+ USB->DOEP0DMAADDR = (uint32_t)dev->setupPkt;
+ USB->DOEP0INT = USB_DOEP0INT_SETUP;
+
+ USBDEP_Ep0Handler( dev ); /* Call the SETUP handler for EP0 */
+}
+#endif
+
+/*
+ * Handle USB reset detected interrupt in suspend mode.
+ */
+static void Handle_USB_GINTSTS_RESETDET ( void )
+{
+#if ( USB_PWRSAVE_MODE )
+ if ( ! USBD_poweredDown )
+ {
+ USB->GINTSTS = USB_GINTSTS_RESETDET;
+ }
+
+ if ( UsbPowerUp() )
+ {
+ USB->GINTSTS = USB_GINTSTS_RESETDET;
+ }
+
+#if ( USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF )
+ /* Power down immediately if VBUS is off. */
+ if ( ! ( USB->STATUS & USB_STATUS_VREGOS ) )
+ {
+ UsbPowerDown();
+ }
+#endif
+
+#else
+ USB->GINTSTS = USB_GINTSTS_RESETDET;
+#endif /* if ( USB_PWRSAVE_MODE ) */
+
+ if ( USB->STATUS & USB_STATUS_VREGOS )
+ {
+ USBD_SetUsbState( USBD_STATE_DEFAULT );
+ }
+ else
+ {
+ USBD_SetUsbState( USBD_STATE_NONE );
+ }
+ DEBUG_USB_INT_LO_PUTS( "RsuP\n" );
+}
+
+/*
+ * Handle Start Of Frame (SOF) interrupt.
+ */
+static void Handle_USB_GINTSTS_SOF( void )
+{
+ USB->GINTSTS = USB_GINTSTS_SOF;
+
+ if ( dev->callbacks->sofInt )
+ {
+ dev->callbacks->sofInt(
+ ( USB->DSTS & _USB_DSTS_SOFFN_MASK ) >> _USB_DSTS_SOFFN_SHIFT );
+ }
+}
+
+/*
+ * Handle USB port reset interrupt.
+ */
+static void Handle_USB_GINTSTS_USBRST( void )
+{
+ int i;
+
+ DEBUG_USB_INT_LO_PUTS( "ReseT" );
+
+ /* Clear Remote Wakeup Signalling */
+ USB->DCTL &= ~( DCTL_WO_BITMASK | USB_DCTL_RMTWKUPSIG );
+ USBHAL_FlushTxFifo( 0 );
+
+ /* Clear pending interrupts */
+ for ( i = 0; i <= MAX_NUM_IN_EPS; i++ )
+ {
+ USB_DINEPS[ i ].INT = 0xFFFFFFFF;
+ }
+
+ for ( i = 0; i <= MAX_NUM_OUT_EPS; i++ )
+ {
+ USB_DOUTEPS[ i ].INT = 0xFFFFFFFF;
+ }
+
+ USB->DAINTMSK = USB_DAINTMSK_INEPMSK0 | USB_DAINTMSK_OUTEPMSK0;
+#if defined( USB_DOEPMSK_STSPHSERCVDMSK )
+ USB->DOEPMSK = USB_DOEPMSK_SETUPMSK | USB_DOEPMSK_XFERCOMPLMSK
+ | USB_DOEPMSK_STSPHSERCVDMSK;
+#else
+ USB->DOEPMSK = USB_DOEPMSK_SETUPMSK | USB_DOEPMSK_XFERCOMPLMSK;
+#endif
+ USB->DIEPMSK = USB_DIEPMSK_XFERCOMPLMSK;
+
+ /* Reset Device Address */
+ USB->DCFG &= ~_USB_DCFG_DEVADDR_MASK;
+
+ /* Setup EP0 to receive SETUP packets */
+ USBDHAL_StartEp0Setup( dev );
+ USBDHAL_EnableInts( dev );
+
+ if ( dev->callbacks->usbReset )
+ {
+ dev->callbacks->usbReset();
+ }
+
+ USBD_SetUsbState( USBD_STATE_DEFAULT );
+ USBDHAL_AbortAllTransfers( USB_STATUS_DEVICE_RESET );
+}
+
+/*
+ * Handle USB port suspend interrupt.
+ */
+static void Handle_USB_GINTSTS_USBSUSP( void )
+{
+ USBD_State_TypeDef state;
+
+ USB->GINTSTS = USB_GINTSTS_USBSUSP;
+ USBDHAL_AbortAllTransfers( USB_STATUS_DEVICE_SUSPENDED );
+ DEBUG_USB_INT_LO_PUTS( "\nSusP" );
+
+ if ( USBD_GetUsbState() == USBD_STATE_NONE )
+ {
+ USBD_SetUsbState( USBD_STATE_POWERED );
+ }
+
+ state = USBD_GetUsbState();
+ if ( ( state == USBD_STATE_POWERED ) ||
+ ( state == USBD_STATE_DEFAULT ) ||
+ ( state == USBD_STATE_ADDRESSED ) ||
+ ( state == USBD_STATE_CONFIGURED ) )
+ {
+#if ( USB_PWRSAVE_MODE )
+ UsbPowerDown();
+#endif
+ USBD_SetUsbState( USBD_STATE_SUSPENDED );
+ }
+}
+
+/*
+ * Handle USB port wakeup interrupt.
+ */
+static void Handle_USB_GINTSTS_WKUPINT( void )
+{
+#if ( USB_PWRSAVE_MODE )
+ if ( ! USBD_poweredDown )
+ {
+ USB->GINTSTS = USB_GINTSTS_WKUPINT;
+ }
+
+ if ( UsbPowerUp() )
+ {
+ USB->GINTSTS = USB_GINTSTS_WKUPINT;
+ USBDHAL_StartEp0Setup( dev );
+ USBDHAL_Ep0Activate( dev->ep0MpsCode );
+ }
+#else
+ USB->GINTSTS = USB_GINTSTS_WKUPINT;
+#endif
+
+ USBD_SetUsbState( dev->savedState );
+ DEBUG_USB_INT_LO_PUTS( "WkuP\n" );
+}
+
+#if ( USB_PWRSAVE_MODE )
+/*
+ * Backup essential USB core registers, and set the core in partial powerdown
+ * mode. Optionally prepare entry into EM2.
+ */
+static bool UsbPowerDown( void )
+{
+#if ( NUM_EP_USED > 0 ) || ( FIFO_CNT > 0 )
+ int i;
+#endif
+#if ( NUM_EP_USED > 0 )
+ int epNum;
+ USBD_Ep_TypeDef *ep;
+#endif
+
+ if ( !USBD_poweredDown )
+ {
+ USBD_poweredDown = true;
+ DEBUG_USB_INT_LO_PUTCHAR( '\\' );
+
+ /* Backup USB core registers. */
+ x_USB_GINTMSK = USB->GINTMSK;
+#if defined(_USB_GOTGCTL_MASK)
+ x_USB_GOTGCTL = USB->GOTGCTL;
+#endif
+ x_USB_GAHBCFG = USB->GAHBCFG;
+ x_USB_GUSBCFG = USB->GUSBCFG;
+ x_USB_GRXFSIZ = USB->GRXFSIZ;
+ x_USB_GNPTXFSIZ = USB->GNPTXFSIZ;
+ x_USB_DCFG = USB->DCFG;
+ x_USB_DCTL = USB->DCTL;
+ x_USB_DAINTMSK = USB->DAINTMSK;
+ x_USB_DIEPMSK = USB->DIEPMSK;
+ x_USB_DOEPMSK = USB->DOEPMSK;
+ x_USB_PCGCCTL = USB->PCGCCTL;
+
+#if ( NUM_EP_USED > 0 )
+ for ( i = 0; i < NUM_EP_USED; i++ )
+ {
+ ep = &dev->ep[ i+1 ];
+ epNum = ep->num;
+ if ( ep->in )
+ {
+ x_USB_EP_CTL[ i ] = USB_DINEPS[ epNum ].CTL;
+ x_USB_EP_TSIZ[ i ] = USB_DINEPS[ epNum ].TSIZ;
+ x_USB_EP_DMAADDR[ i ] = USB_DINEPS[ epNum ].DMAADDR;
+ }
+ else
+ {
+ x_USB_EP_CTL[ i ] = USB_DOUTEPS[ epNum ].CTL;
+ x_USB_EP_TSIZ[ i ] = USB_DOUTEPS[ epNum ].TSIZ;
+ x_USB_EP_DMAADDR[ i ] = USB_DOUTEPS[ epNum ].DMAADDR;
+ }
+ }
+#endif
+
+#if ( FIFO_CNT > 0 )
+ for ( i = 0; i < FIFO_CNT; i++ )
+ {
+ x_USB_DIEPTXFS[ i ] = USB_DIEPTXFS[ i ];
+ }
+#endif
+
+ /* Prepare for wakeup on resume and reset. */
+ USB->DCFG = (USB->DCFG & ~_USB_DCFG_RESVALID_MASK) |
+ (4 << _USB_DCFG_RESVALID_SHIFT);
+ USB->DCFG |= USB_DCFG_ENA32KHZSUSP;
+ USB->GINTMSK = USB_GINTMSK_RESETDETMSK | USB_GINTMSK_WKUPINTMSK;
+
+ /* Enter partial powerdown mode. */
+ USB->PCGCCTL |= USB_PCGCCTL_PWRCLMP;
+ USB->PCGCCTL |= USB_PCGCCTL_RSTPDWNMODULE;
+ USB->PCGCCTL |= USB_PCGCCTL_STOPPCLK;
+
+ /* Record current clock settings. */
+ cmuStatus = CMU->STATUS;
+
+#if ( USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ENTEREM2 )
+#ifndef __MBED__
+ /* Enter EM2 on interrupt exit. */
+ SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk | SCB_SCR_SLEEPONEXIT_Msk;
+#else
+ usbhal_allow_em2(true);
+#endif
+#endif
+
+ /* Switch USBC clock to 32 kHz. */
+#if ( USB_USBC_32kHz_CLK == USB_USBC_32kHz_CLK_LFXO )
+ CMU->CMD = CMU_CMD_USBCCLKSEL_LFXO;
+ while ( ( CMU->STATUS & CMU_STATUS_USBCLFXOSEL ) == 0 ){}
+#else
+ CMU->CMD = CMU_CMD_USBCCLKSEL_LFRCO;
+ while ( ( CMU->STATUS & CMU_STATUS_USBCLFRCOSEL ) == 0 ){}
+#endif
+
+ return true;
+ }
+ return false;
+}
+#endif /* if ( USB_PWRSAVE_MODE ) */
+
+#if ( USB_PWRSAVE_MODE )
+/*
+ * Exit USB core partial powerdown mode, restore essential USB core registers.
+ * Will prevent re-entry back to EM2.
+ * Returns true if a powerup sequence was performed.
+ */
+static bool UsbPowerUp( void )
+{
+#if ( NUM_EP_USED > 0 ) || ( FIFO_CNT > 0 )
+ int i;
+#endif
+#if ( NUM_EP_USED > 0 )
+ int epNum;
+ uint32_t tmp;
+ USBD_Ep_TypeDef *ep;
+#endif
+
+ if ( USBD_poweredDown )
+ {
+ USBD_poweredDown = false;
+ DEBUG_USB_INT_LO_PUTCHAR( '/' );
+
+#if !defined( USB_CORECLK_HFRCO ) || !defined( CMU_OSCENCMD_USHFRCOEN )
+ /* Switch HFCLK from HFRCO to HFXO. */
+ CMU_ClockSelectSet( cmuClock_HF, cmuSelect_HFXO );
+#endif
+
+ /* Turn off HFRCO when not needed. */
+ if ( ( cmuStatus & CMU_STATUS_HFRCOENS ) == 0 )
+ {
+ CMU->OSCENCMD = CMU_OSCENCMD_HFRCODIS;
+ }
+
+ /* Exit partial powerdown mode. */
+ USB->PCGCCTL &= ~USB_PCGCCTL_STOPPCLK;
+ USB->PCGCCTL &= ~(USB_PCGCCTL_PWRCLMP | USB_PCGCCTL_RSTPDWNMODULE);
+
+ if (( USB->GINTSTS & ( USB_GINTSTS_WKUPINT | USB_GINTSTS_RESETDET ) ) == 0)
+ {
+ USB->DCTL = x_USB_DCTL | USB_DCTL_RMTWKUPSIG;
+ USB->DCTL = x_USB_DCTL;
+ }
+
+ /* Restore USB core registers. */
+ USB->GUSBCFG = x_USB_GUSBCFG;
+ USB->DCFG = x_USB_DCFG;
+
+#if ( FIFO_CNT > 0 )
+ for ( i = 0; i < FIFO_CNT; i++ )
+ {
+ USB_DIEPTXFS[ i ] = x_USB_DIEPTXFS[ i ];
+ }
+#endif
+
+#if ( NUM_EP_USED > 0 )
+ for ( i = 0; i < NUM_EP_USED; i++ )
+ {
+ ep = &dev->ep[ i+1 ];
+ epNum = ep->num;
+
+ tmp = x_USB_EP_CTL[ i ] &
+ ~( USB_DIEP_CTL_CNAK | USB_DIEP_CTL_SNAK |
+ USB_DIEP_CTL_SETD0PIDEF | USB_DIEP_CTL_SETD1PIDOF );
+
+ if ( x_USB_EP_CTL[ i ] & USB_DIEP_CTL_DPIDEOF )
+ tmp |= USB_DIEP_CTL_SETD1PIDOF;
+ else
+ tmp |= USB_DIEP_CTL_SETD0PIDEF;
+
+ if ( x_USB_EP_CTL[ i ] & USB_DIEP_CTL_NAKSTS )
+ tmp |= USB_DIEP_CTL_SNAK;
+ else
+ tmp |= USB_DIEP_CTL_CNAK;
+
+ if ( ep->in )
+ {
+ USB_DINEPS[ epNum ].CTL = tmp;
+ USB_DINEPS[ epNum ].TSIZ = x_USB_EP_TSIZ[ i ];
+ USB_DINEPS[ epNum ].DMAADDR = x_USB_EP_DMAADDR[ i ];
+ }
+ else
+ {
+ USB_DOUTEPS[ epNum ].CTL = tmp;
+ USB_DOUTEPS[ epNum ].TSIZ = x_USB_EP_TSIZ[ i ];
+ USB_DOUTEPS[ epNum ].DMAADDR = x_USB_EP_DMAADDR[ i ];
+ }
+ }
+#endif
+
+ USB->PCGCCTL = x_USB_PCGCCTL;
+ USB->DOEPMSK = x_USB_DOEPMSK;
+ USB->DIEPMSK = x_USB_DIEPMSK;
+ USB->DAINTMSK = x_USB_DAINTMSK;
+ USB->DCTL = x_USB_DCTL;
+ USB->GNPTXFSIZ = x_USB_GNPTXFSIZ;
+ USB->GRXFSIZ = x_USB_GRXFSIZ;
+ USB->GAHBCFG = x_USB_GAHBCFG;
+#if defined(_USB_GOTGCTL_MASK)
+ USB->GOTGCTL = x_USB_GOTGCTL;
+#endif
+ USB->GINTMSK = x_USB_GINTMSK;
+
+ USB->DCTL |= USB_DCTL_PWRONPRGDONE;
+
+#if ( USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ENTEREM2 )
+#ifndef __MBED__
+ /* Do not reenter EM2 on interrupt exit. */
+ SCB->SCR &= ~(SCB_SCR_SLEEPDEEP_Msk | SCB_SCR_SLEEPONEXIT_Msk);
+#else
+ usbhal_allow_em2(false);
+#endif
+#endif
+
+ return true;
+ }
+ return false;
+}
+#endif /* if ( USB_PWRSAVE_MODE ) */
+
+#if defined( USB_DOEP0INT_STUPPKTRCVD )
+static void HandleOutEpIntr( uint32_t status, USBD_Ep_TypeDef *ep )
+{
+ uint32_t doeptsiz;
+
+ if ( ep->num == 0 )
+ {
+ if ( status & USB_DOEP0INT_XFERCOMPL )
+ {
+ USB->DOEP0INT = USB_DOEP0INT_XFERCOMPL;
+ doeptsiz = USB->DOEP0TSIZ;
+
+ if ( ep->state == D_EP_IDLE )
+ {
+ if ( status & USB_DOEP0INT_STUPPKTRCVD )
+ {
+ USB->DOEP0INT = USB_DOEP0INT_STUPPKTRCVD;
+ }
+ status = USBDHAL_GetOutEpInts( ep );
+ doeptsiz = USB->DOEP0TSIZ;
+
+ if ( status & USB_DOEP0INT_SETUP )
+ {
+retry:
+ /* Already started data stage, clear setup */
+ USB->DOEP0INT = USB_DOEP0INT_SETUP;
+ status &= ~USB_DOEP0INT_SETUP;
+ {
+ int supCnt = ( doeptsiz & _USB_DOEP0TSIZ_SUPCNT_MASK )
+ >> _USB_DOEP0TSIZ_SUPCNT_SHIFT;
+
+ if ( supCnt == 3 )
+ supCnt = 2;
+
+ dev->setup = &dev->setupPkt[ 2 - supCnt ];
+ }
+ DEBUG_USB_INT_LO_PUTS( "\nS" );
+ USBDEP_Ep0Handler( dev );
+
+ /* Prepare for more setup packets */
+ if ( ep->state == D_EP0_IN_STATUS || ep->state == D_EP_TRANSMITTING )
+ {
+ USBDHAL_StartEp0Setup( dev );
+ }
+ }
+ else /* xfercompl && idle && !setup */
+ {
+ status = USBDHAL_GetOutEpInts( ep );
+ if ( status & USB_DOEP0INT_SETUP )
+ goto retry;
+ USBDHAL_StartEp0Setup( dev );
+ }
+ }
+ else /* ep0state != EP0_IDLE */
+ {
+#ifdef __MBED__
+ if ( ep->state == D_EP_RECEIVING )
+ {
+ int xfer_size = ep->packetSize - (( USB->DOEP0TSIZ & _USB_DOEP0TSIZ_XFERSIZE_MASK )
+ >> _USB_DOEP0TSIZ_XFERSIZE_SHIFT);
+ int setup_pkt_received = status & USB_DOEP0INT_SETUP;
+
+ if ( (!setup_pkt_received && xfer_size == 0) ||
+ (setup_pkt_received && xfer_size == 8) )
+ {
+ /* Higher levels need to see the correct transfer amount for ZLPs */
+ ep->remaining = 0;
+ ep->xferred = 0;
+ }
+ else
+ {
+ /* FIXME - does not work if actual read size > 56 */
+ if ( setup_pkt_received ) xfer_size -= 8;
+
+ ep->xferred = xfer_size;
+ ep->remaining -= xfer_size;
+ }
+
+ USBDEP_Ep0Handler( dev );
+ }
+#else
+ if ( ep->state == D_EP_RECEIVING )
+ {
+ if ( ep->remaining > ep->packetSize )
+ {
+ ep->remaining -= ep->packetSize;
+ ep->xferred += ep->packetSize;
+ }
+ else
+ {
+ ep->xferred += ep->remaining;
+ ep->remaining = 0;
+ }
+ USBDEP_Ep0Handler( dev );
+ }
+ else if ( ep->state == D_EP0_OUT_STATUS )
+ {
+ USBDEP_Ep0Handler( dev );
+ }
+#endif
+ }
+ } /* if ( status & USB_DOEP0INT_XFERCOMPL ) */
+
+ if ( status & USB_DOEP0INT_STSPHSERCVD )
+ {
+ USB->DOEP0INT = USB_DOEP0INT_STSPHSERCVD;
+ }
+
+ if ( status & USB_DOEP0INT_SETUP )
+ {
+ USB->DOEP0INT = USB_DOEP0INT_SETUP;
+ {
+ int supCnt = ( USB->DOEP0TSIZ & _USB_DOEP0TSIZ_SUPCNT_MASK )
+ >> _USB_DOEP0TSIZ_SUPCNT_SHIFT;
+
+ if ( supCnt == 3 )
+ supCnt = 2;
+
+ dev->setup = &dev->setupPkt[ 2 - supCnt ];
+ }
+ DEBUG_USB_INT_LO_PUTS( "\nS" );
+ USBDEP_Ep0Handler( dev );
+ }
+ }
+ else /* epnum != 0 */
+ {
+ if ( status & USB_DOEP_INT_XFERCOMPL )
+ {
+ USB_DOUTEPS[ ep->num ].INT = USB_DOEP_INT_XFERCOMPL;
+
+ ep->xferred = ep->hwXferSize -
+ ( ( USB_DOUTEPS[ ep->num ].TSIZ & _USB_DOEP_TSIZ_XFERSIZE_MASK )>>
+ _USB_DOEP_TSIZ_XFERSIZE_SHIFT );
+ ep->remaining -= ep->xferred;
+
+ USBDEP_EpHandler( ep->addr );
+ }
+ }
+}
+#endif
+
+/** @endcond */
+
+#endif /* defined( USB_DEVICE ) */
+#endif /* defined( USB_PRESENT ) && ( USB_COUNT == 1 ) */