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.
Fork of USBDevice by
targets/TARGET_Silicon_Labs/src/em_usbdint.c
- Committer:
- lukeoftheaura
- Date:
- 2018-01-13
- Revision:
- 73:6fb59df5258d
- Parent:
- 71:53949e6131f6
File content as of revision 73:6fb59df5258d:
/**************************************************************************//**
 * @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 ) */
            
    