Newer
Older
mbed-os / targets / TARGET_Cypress / TARGET_PSOC6 / mtb-pdl-cat1 / drivers / source / cy_usbfs_dev_drv.c
@Dustin Crossman Dustin Crossman on 4 Jun 2021 47 KB Fix file modes.
/***************************************************************************//**
* \file cy_usbfs_dev_drv.c
* \version 2.20.2
*
* Provides general API implementation of the USBFS driver.
*
********************************************************************************
* \copyright
* Copyright 2018-2020 Cypress Semiconductor Corporation
* SPDX-License-Identifier: Apache-2.0
*
* 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 "cy_device.h"

#if defined (CY_IP_MXUSBFS) && defined (CY_IP_MXPERI)

#include "cy_usbfs_dev_drv.h"
#include "cy_usbfs_dev_drv_pvt.h"

#if defined(__cplusplus)
extern "C" {
#endif

/*******************************************************************************
*                        Internal Constants
*******************************************************************************/

/* After suspend, disable wait 2 us */
#define WAIT_SUSPEND_DISABLE    (2U)

/* The bus reset counter is driven by the 100 kHz clock and detects a bus reset
* condition after 100 us.
*/
#define BUS_RESET_PERIOD        (10UL)


/*******************************************************************************
*                        Internal Functions Prototypes
*******************************************************************************/

static void LpmIntrHandler(USBFS_Type *base, cy_stc_usbfs_dev_drv_context_t *context);
static void SofIntrHandler(USBFS_Type *base, cy_stc_usbfs_dev_drv_context_t *context);
static void Ep0IntrHandler(USBFS_Type *base, cy_stc_usbfs_dev_drv_context_t *context);
static void BusResetIntrHandler(USBFS_Type *base, cy_stc_usbfs_dev_drv_context_t *context);
static void ArbiterIntrHandler (USBFS_Type *base, cy_stc_usbfs_dev_drv_context_t *context);
static void SieEnpointIntrHandler(USBFS_Type *base, uint32_t endpoint, cy_stc_usbfs_dev_drv_context_t *context);

static uint32_t WriteEp0Buffer(USBFS_Type *base, uint8_t const *buffer, uint32_t size);
static uint32_t ReadEp0Buffer (USBFS_Type const *base, uint8_t *buffer, uint32_t size);

static void RestoreDeviceConfiguration(USBFS_Type *base, cy_stc_usbfs_dev_drv_context_t *context);

static void EndpointTransferComplete(USBFS_Type *base, uint32_t endpoint,
                                    cy_stc_usbfs_dev_drv_endpoint_data_t *endpointData,
                                    cy_stc_usbfs_dev_drv_context_t *context);

/*******************************************************************************
* Function Name: Cy_USBFS_Dev_Drv_Init
****************************************************************************//**
*
* Initializes the USBFS in device mode. If DMAs are used, initialize the DMAs.
*
* \param base
* The pointer to the USBFS instance.
*
* \param config
* The pointer to the configuration structure \ref cy_stc_usbfs_dev_drv_config_t.
*
* \param context
* The pointer to the context structure \ref cy_stc_usbfs_dev_drv_context_t
* allocated by the user. The structure is used during the USBFS Device
* operation for internal configuration and data retention. The user must not
* modify anything in this structure.
*
* \return
* The status code of the function execution \ref cy_en_usbfs_dev_drv_status_t.
*
*******************************************************************************/
cy_en_usbfs_dev_drv_status_t Cy_USBFS_Dev_Drv_Init(USBFS_Type *base,
                                                   cy_stc_usbfs_dev_drv_config_t const *config,
                                                   cy_stc_usbfs_dev_drv_context_t      *context)
{
    cy_en_usbfs_dev_drv_status_t retStatus = CY_USBFS_DEV_DRV_BAD_PARAM;

    /* Input parameters verification */
    if ((NULL == base) || (NULL == config) || (NULL == context))
    {
        return CY_USBFS_DEV_DRV_BAD_PARAM;
    }

    CY_ASSERT_L3(CY_USBFS_DEV_DRV_IS_MODE_VALID(config->mode));

    /* Enables clock to mxusb IP */
    USBFS_DEV_USB_CLK_EN(base) = CY_USBFS_DEV_DRV_WRITE_ODD(USBFS_USBDEV_USB_CLK_EN_CSR_CLK_EN_Msk);

    /* Clears register (except reserved): the IOMODE = 0 means usb IP controls the usb pins */
    USBFS_DEV_USBIO_CR1(base) = (USBFS_DEV_USBIO_CR1(base) & USBFS_USBDEV_USBIO_CR1_RESERVED_2_Msk);

    /* Sets the number of clocks (divided version of Clk_Peri) to detect bus reset */
    USBFS_DEV_BUS_RST_CNT(base) = BUS_RESET_PERIOD;

    /* Enable PHY detector and single-ended and differential receivers */
    USBFS_DEV_LPM_POWER_CTL(base) = (USBFS_USBLPM_POWER_CTL_SUSPEND_Msk    |
                                     USBFS_USBLPM_POWER_CTL_ENABLE_DPO_Msk |
                                     USBFS_USBLPM_POWER_CTL_ENABLE_DMO_Msk);
    (void) USBFS_DEV_LPM_POWER_CTL(base);

    /* Suspends a clear sequence */
    Cy_SysLib_DelayUs(WAIT_SUSPEND_DISABLE);
    USBFS_DEV_LPM_POWER_CTL(base) &= ~USBFS_USBLPM_POWER_CTL_SUSPEND_Msk;
    (void) USBFS_DEV_LPM_POWER_CTL(base);

    /* Clears the register (except reserved) and enable IMO lock */
    USBFS_DEV_CR1(base) = USBFS_USBDEV_CR1_ENABLE_LOCK_Msk |
                      (USBFS_DEV_CR1(base) & USBFS_USBDEV_CR1_RESERVED_3_Msk);

    /* Configures the level selection (HI, MED, LO) for each interrupt source */
    USBFS_DEV_LPM_INTR_LVL_SEL(base) = config->intrLevelSel;

    /* Enables the interrupt sources: Bus Reset and EP0 */
    USBFS_DEV_LPM_INTR_SIE_MASK(base) = (USBFS_USBLPM_INTR_SIE_BUS_RESET_INTR_Msk |
                                         USBFS_USBLPM_INTR_SIE_EP0_INTR_Msk);

    /* Clears the LPM register (disable LPM response) */
    USBFS_DEV_LPM_LPM_CTL(base) = 0UL;

    if (config->enableLpm)
    {
        /* Enables the device to ACK LPM requests */
        USBFS_DEV_LPM_LPM_CTL(base) = (USBFS_USBLPM_LPM_CTL_LPM_EN_Msk |
                                       USBFS_USBLPM_LPM_CTL_LPM_ACK_RESP_Msk);
    }

    /* Copies the configuration in the context */
    context->mode     = config->mode;
    context->useReg16 = (config->epAccess == CY_USBFS_DEV_DRV_USE_16_BITS_DR);
    context->epSharedBuf     = config->epBuffer;
    context->epSharedBufSize = config->epBufferSize;

    /* Initializes the pointers to functions that work with data endpoint */
    switch(config->mode)
    {
        case CY_USBFS_DEV_DRV_EP_MANAGEMENT_CPU:
        {
            context->addEndpoint     = &AddEndpointHwBuffer;
            context->loadInEndpoint  = &LoadInEndpointCpu;
            context->readOutEndpoint = &ReadOutEndpointCpu;

            USBFS_DEV_ARB_CFG(base) = _VAL2FLD(USBFS_USBDEV_ARB_CFG_DMA_CFG,
                                            CY_USBFS_DEV_DRV_EP_MANAGEMENT_CPU);

            retStatus = CY_USBFS_DEV_DRV_SUCCESS;
        }
        break;

        case CY_USBFS_DEV_DRV_EP_MANAGEMENT_DMA:
        {
            context->addEndpoint     = &AddEndpointHwBuffer;
            context->loadInEndpoint  = &LoadInEndpointDma;
            context->readOutEndpoint = &ReadOutEndpointDma;

            USBFS_DEV_ARB_CFG(base) = _VAL2FLD(USBFS_USBDEV_ARB_CFG_DMA_CFG,
                                            CY_USBFS_DEV_DRV_EP_MANAGEMENT_DMA);
        }
        break;

        case CY_USBFS_DEV_DRV_EP_MANAGEMENT_DMA_AUTO:
        {
            context->addEndpoint     = &AddEndpointRamBuffer;
            context->loadInEndpoint  = &LoadInEndpointDmaAuto;
            context->readOutEndpoint = &ReadOutEndpointDmaAuto;

            USBFS_DEV_ARB_CFG(base) = (_VAL2FLD(USBFS_USBDEV_ARB_CFG_DMA_CFG,
                                             CY_USBFS_DEV_DRV_EP_MANAGEMENT_DMA_AUTO) |
                                             USBFS_USBDEV_ARB_CFG_AUTO_MEM_Msk);
        }
        break;

        default:
            /* Unknown mode */
            break;
    }

    /* Configure DMA and store info about DMA channels */
    if (CY_USBFS_DEV_DRV_EP_MANAGEMENT_CPU != context->mode)
    {
        retStatus = DmaInit(config, context);
    }

    return retStatus;
}


/*******************************************************************************
* Function Name: Cy_USBFS_Dev_Drv_DeInit
****************************************************************************//**
*
* De-initializes the USBFS Device hardware (returns the register values to
* default) and removes all registered callbacks.
*
* \param base
* The pointer to the USBFS instance.
*
* \param context
* The pointer to the context structure \ref cy_stc_usbfs_dev_drv_context_t
* allocated by the user. The structure is used during the USBFS Device
* operation for internal configuration and data retention. The user must not
* modify anything in this structure.
*
*******************************************************************************/
void Cy_USBFS_Dev_Drv_DeInit(USBFS_Type *base, cy_stc_usbfs_dev_drv_context_t *context)
{
    uint32_t regVal;
    uint32_t endpoint;

    /* Set USBLPM registers into the default state */
    USBFS_DEV_LPM_POWER_CTL(base)     = 0UL;
    USBFS_DEV_LPM_USBIO_CTL(base)     = 0UL;
    USBFS_DEV_LPM_FLOW_CTL(base)      = 0UL;
    USBFS_DEV_LPM_LPM_CTL(base)       = 0UL;
    USBFS_DEV_LPM_INTR_SIE(base)      = 0UL;
    USBFS_DEV_LPM_INTR_SIE_SET(base)  = 0UL;
    USBFS_DEV_LPM_INTR_SIE_MASK(base) = 0UL;
    USBFS_DEV_LPM_INTR_LVL_SEL(base)  = 0UL;

    /* Set USBDEV registers into the default state */
    USBFS_DEV_CR0(base) = 0UL;
    USBFS_DEV_CR1(base) = (USBFS_DEV_CR1(base) & USBFS_USBDEV_CR1_RESERVED_3_Msk);
    USBFS_DEV_USBIO_CR0(base) = 0UL;
    USBFS_DEV_USBIO_CR1(base) = (USBFS_DEV_USBIO_CR1(base) & USBFS_USBDEV_USBIO_CR1_RESERVED_2_Msk);
    regVal = CY_USBFS_DEV_READ_ODD(USBFS_DEV_USBIO_CR2(base));
    USBFS_DEV_USBIO_CR2(base) = (CY_USBFS_DEV_DRV_WRITE_ODD(regVal) & USBFS_USBDEV_USBIO_CR2_RESERVED_7_Msk);

    USBFS_DEV_BUS_RST_CNT(base) = BUS_RESET_PERIOD;
    USBFS_DEV_USB_CLK_EN(base)  = CY_USBFS_DEV_DRV_WRITE_ODD(0UL);

    USBFS_DEV_EP0_CR(base)  = 0UL;
    USBFS_DEV_EP0_CNT(base) = CY_USBFS_DEV_DRV_WRITE_ODD(0UL);

    USBFS_DEV_ARB_CFG(base)    = 0UL;
    USBFS_DEV_ARB_INT_EN(base) = 0UL;

    USBFS_DEV_DYN_RECONFIG(base) = 0UL;
    USBFS_DEV_BUF_SIZE(base)     = 0UL;
    USBFS_DEV_DMA_THRES16(base)  = 0UL;
    USBFS_DEV_EP_ACTIVE(base)    = 0UL;
    USBFS_DEV_EP_TYPE(base)      = CY_USBFS_DEV_DRV_WRITE_ODD(0UL);
    USBFS_DEV_CWA16(base)        = 0UL;

    USBFS_DEV_SIE_EP_INT_EN(base) = 0UL;
    USBFS_DEV_SIE_EP_INT_SR(base) = 0UL;

    for (endpoint = 0UL; endpoint < CY_USBFS_DEV_DRV_NUM_EPS_MAX; ++endpoint)
    {
        /* Sets the SIE endpoint register into the default state */
        USBFS_DEV_SIE_EP_CR0(base, endpoint)  = 0UL;
        USBFS_DEV_SIE_EP_CNT0(base, endpoint) = 0UL;
        USBFS_DEV_SIE_EP_CNT1(base, endpoint) = 0UL;

        /* Sets the ARBITER endpoint register into the default state */
        USBFS_DEV_ARB_EP_CFG(base, endpoint)    = 0UL;
        USBFS_DEV_ARB_EP_INT_EN(base, endpoint) = 0UL;
        USBFS_DEV_ARB_RW_WA16(base, endpoint)   = 0UL;
        USBFS_DEV_ARB_RW_RA16(base, endpoint)   = 0UL;
    }

    /* Cleans the context callbacks */
    context->cbSof = NULL;
    context->cbLpm = NULL;

    for (endpoint = 0UL; endpoint < CY_USBFS_DEV_DRV_NUM_EPS_MAX; ++endpoint)
    {
        context->epPool[endpoint].address    = 0U;
        context->epPool[endpoint].copyData   = NULL;
        context->epPool[endpoint].epComplete = NULL;
        context->epPool[endpoint].buffer     = NULL;
    }
}


/*******************************************************************************
* Function Name: Cy_USBFS_Dev_Drv_Enable
****************************************************************************//**
*
* Enables the USBFS Device operation.
*
* \param base
* The pointer to the USBFS instance.
*
* \param context
* The pointer to the context structure \ref cy_stc_usbfs_dev_drv_context_t
* allocated by the user. The structure is used during the USBFS Device
* operation for internal configuration and data retention. The user must not
* modify anything in this structure.
*
*******************************************************************************/
void Cy_USBFS_Dev_Drv_Enable(USBFS_Type *base, cy_stc_usbfs_dev_drv_context_t const *context)
{
    /* Suppresses a compiler warning about unused variables */
    (void) context;

    /* Clears the  EP0 count register */
    USBFS_DEV_EP0_CNT(base) = CY_USBFS_DEV_DRV_WRITE_ODD(0UL);

    /* Sets EP0.CR: ACK Setup, NAK IN/OUT */
    USBFS_DEV_EP0_CR(base)  = CY_USBFS_DEV_DRV_EP_CR_NAK_INOUT;

    /* Enables D+ pull-up, the device appears on the bus */
    USBFS_DEV_LPM_POWER_CTL(base) |= USBFS_USBLPM_POWER_CTL_DP_UP_EN_Msk;
    (void) USBFS_DEV_LPM_POWER_CTL(base);
}


/*******************************************************************************
* Function Name: Cy_USBFS_Dev_Drv_Disable
****************************************************************************//**
*
* Disables the USBFS Device operation. If DMAs are used, disables the DMAs.
*
* \param base
* The pointer to the USBFS instance.
*
* \param context
* The pointer to the context structure \ref cy_stc_usbfs_dev_drv_context_t
* allocated by the user. The structure is used during the USBFS Device
* operation for internal configuration and data retention. The user must not
* modify anything in this structure.
*
*******************************************************************************/
void Cy_USBFS_Dev_Drv_Disable(USBFS_Type *base, cy_stc_usbfs_dev_drv_context_t *context)
{
    /* Suppresses a compiler warning about unused variables */
    (void) context;

    /* Disables D+ pull-up */
    USBFS_DEV_LPM_POWER_CTL(base) &= ~USBFS_USBLPM_POWER_CTL_DP_UP_EN_Msk;
    (void) USBFS_DEV_LPM_POWER_CTL(base);

    /* Disables the device to respond to usb traffic */
    USBFS_DEV_CR0(base) &= ~USBFS_USBDEV_CR0_USB_ENABLE_Msk;

    /* Disables the DMA channels */
    if (CY_USBFS_DEV_DRV_EP_MANAGEMENT_CPU != context->mode)
    {
        DmaDisable(context);
    }
}


/*******************************************************************************
* Function Name: LpmIntrHandler
****************************************************************************//**
*
* LPM (Link Power Manager) interrupt handler.
*
* \param base
* The pointer to the USBFS instance.
*
* \param context
* The pointer to the context structure \ref cy_stc_usbfs_dev_drv_context_t
* allocated by the user. The structure is used during the USBFS Device
* operation for internal configuration and data retention. The user must not
* modify anything in this structure.
*
*******************************************************************************/
static void LpmIntrHandler(USBFS_Type *base, cy_stc_usbfs_dev_drv_context_t *context)
{
    if (context->cbLpm != NULL)
    {
        context->cbLpm(base, context);
    }
}


/*******************************************************************************
* Function Name: SofIntrHandler
****************************************************************************//**
*
* SOF (Start Of Frame) interrupt handler.
*
* \param base
* The pointer to the USBFS instance.
*
* \param context
* The pointer to the context structure \ref cy_stc_usbfs_dev_drv_context_t
* allocated by the user. The structure is used during the USBFS Device
* operation for internal configuration and data retention. The user must not
* modify anything in this structure.
*
*******************************************************************************/
static void SofIntrHandler(USBFS_Type *base, cy_stc_usbfs_dev_drv_context_t *context)
{
    if (context->cbSof != NULL)
    {
        context->cbSof(base, context);
    }
}


/*******************************************************************************
* Function Name: Ep0IntrHandler
****************************************************************************//**
*
* Endpoint 0 (Control Endpoint) interrupt handler.
*
* \param base
* The pointer to the USBFS instance.
*
* \param context
* The pointer to the context structure \ref cy_stc_usbfs_dev_drv_context_t
* allocated by the user. The structure is used during the USBFS Device
* operation for internal configuration and data retention. The user must not
* modify anything in this structure.
*
*******************************************************************************/
static void Ep0IntrHandler(USBFS_Type *base, cy_stc_usbfs_dev_drv_context_t *context)
{
    /* Reads the CR register */
    uint32_t ep0Cr = Cy_USBFS_Dev_Drv_ReadEp0Mode(base);

    /* Checks whether the packet was ACKed */
    if (0U != (ep0Cr & USBFS_USBDEV_EP0_CR_ACKED_TXN_Msk))
    {
        /* Checks the packet direction */
        if (_FLD2BOOL(USBFS_USBDEV_EP0_CR_SETUP_RCVD, ep0Cr))
        {
            /* A setup packet received */
            context->ep0CtrlState = CY_USBFS_DEV_DRV_EP0_CTRL_STATE_SETUP;

            /* Handles SETUP */
            if (_FLD2VAL(USBFS_USBDEV_EP0_CR_MODE, ep0Cr) == CY_USBFS_DEV_DRV_EP_CR_NAK_INOUT)
            {
                /* Tries to unlock the CR0 register: read and then write.
                * The success write clears 8-4 bits in the register.
                */
                Cy_USBFS_Dev_Drv_WriteEp0Mode(base, ep0Cr);

                /* Checks whether the CR0 register unlocked (bits cleared) */
                ep0Cr = Cy_USBFS_Dev_Drv_ReadEp0Mode(base);
                if (false == _FLD2BOOL(USBFS_USBDEV_EP0_CR_SETUP_RCVD, ep0Cr))
                {
                    /* Resets the EP0 data toggle */
                    context->ep0DataToggle = 0U;

                    /* Calls Device layer to service a request */
                    context->ep0Setup(base, context);
                }
                else
                {
                    /* CR0 is still locked, sets an interrupt pending to retry */
                    Cy_USBFS_Dev_Drv_SetSieInterrupt(base, USBFS_USBLPM_INTR_CAUSE_EP0_INTR_Msk);
                }
            }
        }
        /* Handles IN */
        else if (_FLD2BOOL(USBFS_USBDEV_EP0_CR_IN_RCVD, ep0Cr))
        {
            if (CY_USBFS_DEV_DRV_EP0_CTRL_STATE_DATA == context->ep0CtrlState)
            {
                /* Data stage: invokes a callback to proceed the control transfer */
                context->ep0In(base, context);
            }
            else if (CY_USBFS_DEV_DRV_EP0_CTRL_STATE_STATUS_IN == context->ep0CtrlState)
            {
                /* Sets an address after the Status stage completion */
                if (context->setAddress)
                {
                    Cy_USBFS_Dev_Drv_SetDeviceAddress(base, context->address);
                    context->setAddress = false;
                }

                /* Completes the control transfer */
                context->ep0CtrlState = CY_USBFS_DEV_DRV_EP0_CTRL_STATE_IDLE;
            }
            else if (CY_USBFS_DEV_DRV_EP0_CTRL_STATE_STATUS_OUT == context->ep0CtrlState)
            {
                /* Updates the CNT and CR registers to continue the IN/OUT transfer */
                Cy_USBFS_Dev_Drv_SetEp0Count (base, 0U, USBFS_USBDEV_EP0_CNT_DATA_TOGGLE_Msk);
                Cy_USBFS_Dev_Drv_WriteEp0Mode(base, CY_USBFS_DEV_DRV_EP_CR_STATUS_OUT_ONLY);

                /* The transfer is completed */
                context->ep0CtrlState = CY_USBFS_DEV_DRV_EP0_CTRL_STATE_IDLE;
            }
            else
            {
                /* Nothing to handle in this state */
            }
        }
        /* Handles OUT */
        else if (_FLD2BOOL(USBFS_USBDEV_EP0_CR_OUT_RCVD, ep0Cr))
        {
            if (CY_USBFS_DEV_DRV_EP0_CTRL_STATE_DATA == context->ep0CtrlState)
            {
                /* Data stage: invokes a callback to proceed the control transfer */
                context->ep0Out(base, context);
            }
        }
        else
        {
            /* Does nothing - an unknown source */
        }
    }
}


/*******************************************************************************
* Function Name: BusResetIntrHandler
****************************************************************************//**
*
* Bus Reset interrupt handler.
*
* \param base
* The pointer to the USBFS instance.
*
* \param context
* The pointer to the context structure \ref cy_stc_usbfs_dev_drv_context_t
* allocated by the user. The structure is used during the USBFS Device
* operation for internal configuration and data retention. The user must not
* modify anything in this structure.
*
*******************************************************************************/
static void BusResetIntrHandler(USBFS_Type *base, cy_stc_usbfs_dev_drv_context_t *context)
{
    /* Resets the driver variables if any */

    /* Cypress ID# 293217: write CR0 when pull-up is enabled */
    if (_FLD2BOOL(USBFS_USBLPM_POWER_CTL_DP_UP_EN, USBFS_DEV_LPM_POWER_CTL(base)))
    {
        /* Passes an event to the Device layer */
        context->busReset(base, context);

        /* Clears the EP0 count register */
        USBFS_DEV_EP0_CNT(base) = CY_USBFS_DEV_DRV_WRITE_ODD(0UL);

        /* Sets EP0.CR: ACK Setup, NAK IN/OUT */
        USBFS_DEV_EP0_CR(base) = CY_USBFS_DEV_DRV_EP_CR_NAK_INOUT;

        /* Resets the driver context variables into the default state */
        context->setAddress   = false;
        context->ep0CtrlState = CY_USBFS_DEV_DRV_EP0_CTRL_STATE_IDLE;
        context->curBufAddr   = 0U;
        context->activeEpMask = 0U;
        context->epAbortMask  = 0U;

        /* Enable device to responds to USB traffic with address 0 */
        USBFS_DEV_CR0(base) = USBFS_USBDEV_CR0_USB_ENABLE_Msk;
        (void) USBFS_DEV_CR0(base);
    }
}


/*******************************************************************************
* Function Name: EndpointTransferComplete
****************************************************************************//**
*
* Handles the endpoint transfer complete: updates  the endpoint state,
* calls a transfer completion callback, handles the abort.
*
* \param base
* The pointer to the USBFS instance.
*
* \param endpoint
* The data endpoint index.
*
* \param endpointData
* The pointer to the endpoint data structure.
*
* \param context
* The pointer to the context structure \ref cy_stc_usbfs_dev_drv_context_t
* allocated by the user. The structure is used during the USBFS Device
* operation for internal configuration and data retention. The user must not
* modify anything in this structure.
*
*******************************************************************************/
static void EndpointTransferComplete(USBFS_Type *base, uint32_t endpoint,
                                    cy_stc_usbfs_dev_drv_endpoint_data_t *endpointData,
                                    cy_stc_usbfs_dev_drv_context_t *context)
{
    /* Updates the toggle (exclude ISOC endpoints) */
    if (false == IS_EP_ISOC(endpointData->sieMode))
    {
        endpointData->toggle ^= (uint8_t) USBFS_USBDEV_SIE_EP_DATA_TOGGLE_Msk;
    }

    if (0U != (context->epAbortMask & EP2MASK(endpoint)))
    {
        /* Clears the endpoint abort: Do not invoke callback, the state was set to idle */
        context->epAbortMask &= (uint8_t) ~EP2MASK(endpoint);
    }
    else
    {
        /* Data has been transferred to the bus set-endpoint complete state */
        endpointData->state = CY_USB_DEV_EP_COMPLETED;

        /* Involves a callback if it is registered */
        if (NULL != endpointData->epComplete)
        {
            uint32_t errorType = 0UL;

            /* Checks transfer errors (detect by hardware) */
            if (0U != Cy_USBFS_Dev_Drv_GetSieEpError(base, endpoint))
            {
                errorType = CY_USBFS_DEV_ENDPOINT_TRANSFER_ERROR;
            }

            /* Checks the data toggle bit of current transfer (exclude ISOC endpoints) */
            if (false == IS_EP_ISOC(endpointData->sieMode))
            {
                /* This may fail only for OUT endpoints */
                if (endpointData->toggle == Cy_USBFS_Dev_Drv_GetSieEpToggle(base, endpoint))
                {
                    errorType |= CY_USBFS_DEV_ENDPOINT_SAME_DATA_TOGGLE;

                    /* Restores the data toggle to recover it in the next OUT transfer */
                    endpointData->toggle ^= (uint8_t) USBFS_USBDEV_SIE_EP_DATA_TOGGLE_Msk;
                }
            }

            /* Calls the endpoint complete callback */
            endpointData->epComplete(base, (uint32_t) endpointData->address, errorType, context);
        }
    }
}


/*******************************************************************************
* Function Name: ArbiterIntrHandler
****************************************************************************//**
*
* Arbiter interrupt handler (enabled for operation with DMAs).
*
* \param base
* The pointer to the USBFS instance.
*
* \param context
* The pointer to the context structure \ref cy_stc_usbfs_dev_drv_context_t
* allocated by the user. The structure is used during the USBFS Device
* operation for internal configuration and data retention. The user must not
* modify anything in this structure.
*
*******************************************************************************/
static void ArbiterIntrHandler(USBFS_Type *base, cy_stc_usbfs_dev_drv_context_t *context)
{
    uint32_t endpoint = 0UL;
    uint32_t intrMask = Cy_USBFS_Dev_Drv_GetArbAllEpsInterruptStatus(base);

    /* Handle active interrupt sources */
    while (0U != intrMask)
    {
        if (0U != (intrMask & 0x01U))
        {
            /* Get the endpoint enable interrupt sources */
            uint32_t sourceMask = Cy_USBFS_Dev_Drv_GetArbEpInterruptStatusMasked(base, endpoint);

            /* Gets the pointer to the endpoint data */
            cy_stc_usbfs_dev_drv_endpoint_data_t *endpointData = &context->epPool[endpoint];

            Cy_USBFS_Dev_Drv_ClearArbEpInterrupt(base, endpoint, sourceMask);

            /* Mode 2/3: Handle IN endpoint buffer full event: trigger after
            * endpoint buffer has been loaded
            */
            if (0U != (sourceMask & USBFS_USBDEV_ARB_EP_IN_BUF_FULL_Msk))
            {
                Cy_USBFS_Dev_Drv_ClearArbCfgEpInReady(base, endpoint);

                /* Mode 2: Notifies the LoadInEndpointDma function that data has been copied into the endpoint buffer.
                *  Mode 3: No impact, the endpoint pending state is set in LoadInEndpointDmaAuto before.
                */
                endpointData->state = CY_USB_DEV_EP_PENDING;

                /* Arm IN endpoint */
                Cy_USBFS_Dev_Drv_SetSieEpMode(base, endpoint, (uint32_t) endpointData->sieMode);
            }

            /* Mode 2: Handle DMA completion event for OUT endpoints */
            if (0U != (sourceMask & USBFS_USBDEV_ARB_EP_DMA_GNT_Msk))
            {
                /* Notifies the ReadOutEndpointDma function that the data has been copied from endpoint buffer
                * into the user buffer.
                */
                endpointData->state = CY_USB_DEV_EP_COMPLETED;
            }

            /* Mode 3: Handles a DMA completion event for OUT endpoints */
            if (0U != (sourceMask & USBFS_USBDEV_ARB_EP_DMA_TERMIN_Msk))
            {
                /* Sets a DMA channel to start a new transfer */
                DmaOutEndpointRestore(endpointData);

                /* Completes an endpoint transfer */
                EndpointTransferComplete(base, endpoint, endpointData, context);
            }

            /* This error condition indicates system failure */
            if (0U != (sourceMask & USBFS_USBDEV_ARB_EP_BUF_OVER_Msk))
            {
                /* The DMA cannot move the data from the mxusbfs IP
                * hardware buffer fast enough and so caused an overflow. Give a DMA
                * channel for this endpoint greater priority or increase the clock
                * at which it operates.
                */
                CY_ASSERT_L1(false);
            }

            /* This error condition indicates system failure */
            if (0U != (sourceMask & USBFS_USBDEV_ARB_EP_BUF_UNDER_Msk))
            {
                /* The DMA cannot move the data into the mxusbfs IP
                * hardware buffer fast enough and so caused an underflow. Give a DMA
                * channel for this endpoint greater priority or increase the clock
                * at which it operates.
                */
                CY_ASSERT_L1(false);
            }
        }

        /* Moves to a next endpoint */
        intrMask >>= 1UL;
        ++endpoint;
    }
}


/*******************************************************************************
* Function Name: SieEnpointIntrHandler
****************************************************************************//**
*
* SIE (Serial Interface Engine) endpoint interrupt handler.
* It triggers when communication was completed with data endpoint.
*
* \param base
* The pointer to the USBFS instance.
*
* \param endpoint
* The data endpoint number.
*
* \param context
* The pointer to the context structure \ref cy_stc_usbfs_dev_drv_context_t
* allocated by the user. The structure is used during the USBFS Device
* operation for internal configuration and data retention. The user must not
* modify anything in this structure.
*
*******************************************************************************/
static void SieEnpointIntrHandler(USBFS_Type *base, uint32_t endpoint,
                                  cy_stc_usbfs_dev_drv_context_t *context)
{
    bool modeDmaAuto;
    bool inEndpoint;
    bool zeroLengthPacket;

    /* Gets the pointer to endpoint data */
    cy_stc_usbfs_dev_drv_endpoint_data_t *endpointData = &context->epPool[endpoint];

    Cy_USBFS_Dev_Drv_ClearSieEpInterrupt(base, endpoint);

    /*
    * DMA Auto requires special processing:
    * IN endpoints: Updates the endpoint state here to complete the transfer (includes a zero-length packet).
    * OUT endpoints: Updates the endpoint state in ArbiterIntrHandler when DMA is done to complete the transfer
    *                (interrupt source DMA_TERMIN).
    *                In the case of a zero-length packet, updates the endpoint state here to complete the transfer.
    * Other modes (CPU mode and DMA mode): Updates the endpoint state here to complete the transfer for the IN and OUT endpoints.
    */
    modeDmaAuto = (CY_USBFS_DEV_DRV_EP_MANAGEMENT_DMA_AUTO == context->mode);
    inEndpoint   = CY_USBFS_DEV_DRV_IS_EP_DIR_IN(endpointData->address);
    zeroLengthPacket = (0U == Cy_USBFS_Dev_Drv_GetSieEpCount(base, endpoint));

    CY_MISRA_FP_LINE('MISRA C-2012 Rule 14.3', 'Checked manually. No issues.');
    if ( (!modeDmaAuto) ||
         (modeDmaAuto && (inEndpoint || zeroLengthPacket)) )
    {
        EndpointTransferComplete(base, endpoint, endpointData, context);
    }
}


/*******************************************************************************
* Function Name: Cy_USBFS_Dev_Drv_Interrupt
****************************************************************************//**
*
* Processes interrupt events generated by the USBFS Device.
* The interrupts are mandatory for USBFS Device operation and this function
* must be called inside the user-defined interrupt service routine.
*
* \param base
* The pointer to the USBFS instance.
*
* \param intrCause
* The interrupt cause register value. Call appropriate function to get
* interrupt cause (Low, Medium or High):
* * \ref Cy_USBFS_Dev_Drv_GetInterruptCauseLo
* * \ref Cy_USBFS_Dev_Drv_GetInterruptCauseMed
* * \ref Cy_USBFS_Dev_Drv_GetInterruptCauseHi
*
* \param context
* The pointer to the context structure \ref cy_stc_usbfs_dev_drv_context_t
* allocated by the user. The structure is used during the USBFS Device
* operation for internal configuration and data retention. The user must not
* modify anything in this structure.
*
*******************************************************************************/
void Cy_USBFS_Dev_Drv_Interrupt(USBFS_Type *base, uint32_t intrCause,
                                cy_stc_usbfs_dev_drv_context_t *context)
{
    uint32_t endpoint = 0U;
    uint32_t intrCauseEp;

    /* Clears the SIE interrupts while the below are served */
    Cy_USBFS_Dev_Drv_ClearSieInterrupt(base, intrCause);

    /* LPM */
    if (0U != (intrCause & USBFS_USBLPM_INTR_CAUSE_LPM_INTR_Msk))
    {
        LpmIntrHandler(base, context);
    }

    /* Arbiter: Data endpoints */
    if (0U != (intrCause & USBFS_USBLPM_INTR_CAUSE_ARB_EP_INTR_Msk))
    {
        /* This interrupt is cleared inside the handler */
        ArbiterIntrHandler(base, context);
    }

    /* SIE: Data endpoints */
    intrCauseEp = (intrCause >> USBFS_USBLPM_INTR_CAUSE_EP1_INTR_Pos);
    while (0U != intrCauseEp)
    {
        if (0u != (intrCauseEp & 0x1U))
        {
            /* These interrupts are cleared inside the handler */
            SieEnpointIntrHandler(base, endpoint, context);
        }

        intrCauseEp >>= 1U;
        ++endpoint;
    }

    /* SOF */
    if (0U != (intrCause & USBFS_USBLPM_INTR_CAUSE_SOF_INTR_Msk))
    {
        SofIntrHandler(base, context);
    }

    /* Controls EP0 */
    if (0U != (intrCause & USBFS_USBLPM_INTR_CAUSE_EP0_INTR_Msk))
    {
        Ep0IntrHandler(base, context);
    }

    /* Bus Reset */
    if (0U != (intrCause & USBFS_USBLPM_INTR_CAUSE_BUS_RESET_INTR_Msk))
    {
        BusResetIntrHandler(base, context);
    }
}


/*******************************************************************************
* Function Name: WriteEp0Buffer
****************************************************************************//**
*
* Writes data into the Endpoint 0 hardware buffer and returns how many bytes
* were written.
*
* \param base
* The pointer to the USBFS instance.
*
* \param buffer
* The pointer to the data to write in the Endpoint 0.
*
* \param size
* The number of bytes to write into the Endpoint 0.
*
* \return
* The number of bytes that were written.
*
*******************************************************************************/
static uint32_t WriteEp0Buffer(USBFS_Type *base, uint8_t const *buffer, uint32_t size)
{
    uint32_t idx;

    /* Cuts the message size if too many bytes are requested to write */
    if (size > CY_USBFS_DEV_DRV_EP0_BUFFER_SIZE)
    {
        size = CY_USBFS_DEV_DRV_EP0_BUFFER_SIZE;
    }

    /* Writes data into the hardware buffer */
    for (idx = 0UL; idx < size; ++idx)
    {
        Cy_USBFS_Dev_Drv_WriteEp0Data(base, idx, (uint32_t) buffer[idx]);
    }

    return idx;
}


/*******************************************************************************
* Function Name: ReadEp0Buffer
****************************************************************************//**
*
* Reads data from Endpoint 0 hardware and returns how many bytes were read.
*
* \param base
* The pointer to the USBFS instance.
*
* \param buffer
* The pointer to the buffer for data read from the Endpoint 0.
*
* \param size
* The number of bytes to read from the Endpoint 0.
*
* \return
* The number of bytes that were read.
*
*******************************************************************************/
static uint32_t ReadEp0Buffer(USBFS_Type const *base, uint8_t *buffer, uint32_t size)
{
    uint32_t idx;

    /* Gets the number of received bytes */
    uint32_t numToCopy = Cy_USBFS_Dev_Drv_GetEp0Count(base);

    /* Reads received bytes only */
    if (size > numToCopy)
    {
        size = numToCopy;
    }

    /* Gets the data from the buffer */
    for (idx = 0UL; idx < size; ++idx)
    {
        buffer[idx] = (uint8_t) Cy_USBFS_Dev_Drv_ReadEp0Data(base, idx);
    }

    return idx;
}


/*******************************************************************************
* Function Name: Cy_USBFS_Dev_Drv_Ep0GetSetup
****************************************************************************//**
*
* Reads the setup packed from the Endpoint 0 hardware buffer.
*
* \param base
* The pointer to the USBFS instance.
*
* \param buffer
* The pointer to the buffer for data read from the Endpoint 0.
*
* \param context
* The pointer to the context structure \ref cy_stc_usbfs_dev_drv_context_t
* allocated by the user. The structure is used during the USBFS Device
* operation for internal configuration and data retention. The user must not
* modify anything in this structure.
*
*******************************************************************************/
void Cy_USBFS_Dev_Drv_Ep0GetSetup(USBFS_Type const *base, uint8_t *buffer,
                                  cy_stc_usbfs_dev_drv_context_t const *context)
{
    /* Suppresses a compiler warning about unused variables */
    (void) context;

    (void) ReadEp0Buffer(base, buffer, CY_USBFS_DEV_DRV_EP0_BUFFER_SIZE);
}


/*******************************************************************************
* Function Name: Cy_USBFS_Dev_Drv_Ep0Write
****************************************************************************//**
*
* Writes data into Endpoint 0 hardware buffer and returns how many bytes were
* written.
*
* \param base
* The pointer to the USBFS instance.
*
* \param buffer
* The pointer to the buffer containing data bytes to write.
* To switch a transfer from the data stage to status, pass NULL as a pointer.
*
* \param size
* The number of bytes to write.
* Setting the size to zero sends to the bus zero-length data packet.
*
* \param context
* The pointer to the context structure \ref cy_stc_usbfs_dev_drv_context_t
* allocated by the user. The structure is used during the USBFS Device
* operation for internal configuration and data retention. The user must not
* modify anything in this structure.
*
* \return
* The number of bytes that were written.
*
*******************************************************************************/
uint32_t Cy_USBFS_Dev_Drv_Ep0Write(USBFS_Type *base, uint8_t const *buffer, uint32_t size,
                                    cy_stc_usbfs_dev_drv_context_t *context)
{
    uint32_t numBytes = 0UL;

    if (NULL != buffer)
    {
        /* Data stage (IN): Loads data to be sent */

        /* Puts the data into the buffer */
        if (size > 0U)
        {
            numBytes = WriteEp0Buffer(base, buffer, size);
        }

        /* Updates the data toggle and counter */
        context->ep0DataToggle ^= (uint8_t) USBFS_USBDEV_EP0_CNT_DATA_TOGGLE_Msk;

        /* Updates the CNT and CR registers to continue the IN transfer */
        Cy_USBFS_Dev_Drv_SetEp0Count (base, numBytes, (uint32_t) context->ep0DataToggle);
        Cy_USBFS_Dev_Drv_WriteEp0Mode(base, CY_USBFS_DEV_DRV_EP_CR_ACK_IN_STATUS_OUT);

        context->ep0CtrlState = CY_USBFS_DEV_DRV_EP0_CTRL_STATE_DATA;
    }
    else
    {
        /* Status stage (IN): Completes the status stage, sends an ACK handshake */

        /* Updates the CNT and CR registers to continue the IN transfer */
        Cy_USBFS_Dev_Drv_SetEp0Count (base, numBytes, USBFS_USBDEV_EP0_CNT_DATA_TOGGLE_Msk);
        Cy_USBFS_Dev_Drv_WriteEp0Mode(base, CY_USBFS_DEV_DRV_EP_CR_STATUS_IN_ONLY);

        context->ep0CtrlState = CY_USBFS_DEV_DRV_EP0_CTRL_STATE_STATUS_IN;
    }

    return numBytes;
}


/*******************************************************************************
* Function Name: Cy_USBFS_Dev_Drv_Ep0Read
****************************************************************************//**
*
* Start receiving a packet into the Endpoint 0 hardware buffer.
*
* \param base
* The pointer to the USBFS instance.
*
* \param buffer
* The pointer to buffer that stores data that was read.
*
* \param size
* The number of bytes to read.
* Reading zero bytes switch control transfer to status stage.
*
* \param context
* The pointer to the context structure \ref cy_stc_usbfs_dev_drv_context_t
* allocated by the user. The structure is used during the USBFS Device
* operation for internal configuration and data retention. The user must not
* modify anything in this structure.
*
*******************************************************************************/
void Cy_USBFS_Dev_Drv_Ep0Read(USBFS_Type *base, uint8_t *buffer, uint32_t size,
                                cy_stc_usbfs_dev_drv_context_t *context)
{
    if (0U != size)
    {
        /* Data stage (OUT): Prepares to receive data */

        /* Stores the Endpoint 0 buffer to put read operation results */
        context->ep0Buffer     = buffer;
        context->ep0BufferSize = (uint8_t) size; /* The Endpoint 0 max packet is 8 bytes */

        /* Updates the CNT and CR registers to continue the OUT transfer */
        Cy_USBFS_Dev_Drv_SetEp0Count (base, 0U, 0U);
        Cy_USBFS_Dev_Drv_WriteEp0Mode(base, CY_USBFS_DEV_DRV_EP_CR_ACK_OUT_STATUS_IN);

        context->ep0CtrlState = CY_USBFS_DEV_DRV_EP0_CTRL_STATE_DATA;
    }
    else
    {
        /* Status stage (OUT): prepare to complete status stage after IN transfer is finished */
        context->ep0CtrlState = CY_USBFS_DEV_DRV_EP0_CTRL_STATE_STATUS_OUT;
    }
}


/*******************************************************************************
* Function Name: Cy_USBFS_Dev_Drv_Ep0ReadResult
****************************************************************************//**
*
* Reads data from the Endpoint 0 hardware and returns the number of read bytes.
*
* \param base
* The pointer to the USBFS instance.
*
* \param context
* The pointer to the context structure \ref cy_stc_usbfs_dev_drv_context_t
* allocated by the user. The structure is used during the USBFS Device
* operation for internal configuration and data retention. The user must not
* modify anything in this structure.
*
* \return
* The number of read bytes.
*
*******************************************************************************/
uint32_t Cy_USBFS_Dev_Drv_Ep0ReadResult(USBFS_Type const *base, cy_stc_usbfs_dev_drv_context_t *context)
{
    /* Stores received data to the buffer */
    return ReadEp0Buffer(base, context->ep0Buffer, (uint32_t) context->ep0BufferSize);
}


/*******************************************************************************
* Function Name: Cy_USBFS_Dev_Drv_RegisterServiceCallback
****************************************************************************//**
*
* Registers a callback function to notify about service events (Bus Reset or
* Endpoint 0 communication) in \ref Cy_USBFS_Dev_Drv_Interrupt.
* To remove callback function, pass NULL as function pointer.
*
* \param base
* The pointer to the USBFS instance.
*
* \param source
* The event that involves the callback.
*
* \param callback
* The pointer to a callback function.
*
* \param context
* The pointer to the context structure \ref cy_stc_usbfs_dev_drv_context_t
* allocated by the user. The structure is used during the USBFS Device
* operation for internal configuration and data retention. The user must not
* modify anything in this structure.
*
* \return
* The status code of the function execution \ref cy_en_usbfs_dev_drv_status_t.
*
*******************************************************************************/
void Cy_USBFS_Dev_Drv_RegisterServiceCallback(USBFS_Type const *base,
                                              cy_en_usb_dev_service_cb_t     source,
                                              cy_cb_usbfs_dev_drv_callback_t callback,
                                              cy_stc_usbfs_dev_drv_context_t *context)
{
    /* Suppresses a compiler warning about unused variables */
    (void) base;

    switch(source)
    {
        case CY_USB_DEV_BUS_RESET:
            context->busReset = callback;
        break;

        case CY_USB_DEV_EP0_SETUP:
            context->ep0Setup = callback;
        break;

        case CY_USB_DEV_EP0_IN:
            context->ep0In = callback;
        break;

        case CY_USB_DEV_EP0_OUT:
            context->ep0Out = callback;
        break;

        default:
        /* Unknown callback */
        break;
    }
}

/*******************************************************************************
* Function Name: RestoreDeviceConfiguration
****************************************************************************//**
*
* Restores device configuration and data endpoints for the active mode
* operation.
*
* \param base
* The pointer to the USBFS instance.
*
* \param context
* The pointer to the context structure \ref cy_stc_usbfs_dev_drv_context_t
* allocated by the user. The structure is used during the USBFS Device
* operation for internal configuration and data retention. The user must not
* modify anything in this structure.
*
*******************************************************************************/
static void RestoreDeviceConfiguration(USBFS_Type *base,
                                       cy_stc_usbfs_dev_drv_context_t *context)
{
    uint32_t endpoint;

    /* Due to Deep Sleep, non-retention registers are set to the default state */

    for (endpoint = 0U; endpoint < CY_USBFS_DEV_DRV_NUM_EPS_MAX; ++endpoint)
    {
        if (0U != context->epPool[endpoint].address)
        {
            /* Restores the endpoint configuration */
            if (CY_USBFS_DEV_DRV_EP_MANAGEMENT_DMA_AUTO == context->mode)
            {
                RestoreEndpointRamBuffer(base, &context->epPool[endpoint]);
            }
            else
            {
                RestoreEndpointHwBuffer(base, context->mode, &context->epPool[endpoint]);
            }
        }
    }

    /* Completes the device configuration */
    Cy_USBFS_Dev_Drv_ConfigDevice(base, context);
}


/*******************************************************************************
* Function Name: Cy_USBFS_Dev_Drv_Suspend
****************************************************************************//**
*
* Prepares the USBFS component to enter Deep Sleep mode.
*
* \param base
* The pointer to the USBFS instance.
*
* \param context
* The pointer to the context structure \ref cy_stc_usbfs_dev_drv_context_t
* allocated by the user. The structure is used during the USBFS Device
* operation for internal configuration and data retention. The user must not
* modify anything in this structure.
*
* \note
* After entering low-power mode, the data that is left in the IN or OUT
* endpoint buffers is not restored after a wakeup, and is lost. Therefore, it should
* be stored in the SRAM for OUT endpoint or read by the host for the IN endpoint
* before entering low-power mode.
*
*******************************************************************************/
void Cy_USBFS_Dev_Drv_Suspend(USBFS_Type *base, cy_stc_usbfs_dev_drv_context_t *context)
{
    /* Puts PHY to suspend mode */
    USBFS_DEV_LPM_POWER_CTL(base) |= USBFS_USBLPM_POWER_CTL_SUSPEND_Msk;
    (void) USBFS_DEV_LPM_POWER_CTL(base);

    /* Disables all DMA channels: DMA registers are retention */
    if (CY_USBFS_DEV_DRV_EP_MANAGEMENT_CPU != context->mode)
    {
        DmaDisable(context);
    }
}


/*******************************************************************************
* Function Name: Cy_USBFS_Dev_Drv_Resume
****************************************************************************//**
*
* Prepares the USBFS component for operation after exiting Deep Sleep mode.
*
* \param base
* The pointer to the USBFS instance.
*
* \param context
* The pointer to the context structure \ref cy_stc_usbfs_dev_drv_context_t
* allocated by the user. The structure is used during the USBFS Device
* operation for internal configuration and data retention. The user must not
* modify anything in this structure.
*
*******************************************************************************/
void Cy_USBFS_Dev_Drv_Resume(USBFS_Type *base, cy_stc_usbfs_dev_drv_context_t *context)
{
    uint32_t lpmCtl;

    /* Enables the clock to mxusbfs IP */
    USBFS_DEV_USB_CLK_EN(base) = CY_USBFS_DEV_DRV_WRITE_ODD(USBFS_USBDEV_USB_CLK_EN_CSR_CLK_EN_Msk);

    /* Sets the number of clocks (divided version of Clk_Peri) to detect bus reset */
    USBFS_DEV_BUS_RST_CNT(base) = BUS_RESET_PERIOD;

    /* Sets EP0.CR: ACK Setup, NAK IN/OUT */
    USBFS_DEV_EP0_CR(base) = CY_USBFS_DEV_DRV_EP_CR_NAK_INOUT;

    /* Restores the data endpoints configuration  */
    RestoreDeviceConfiguration(base, context);

    /* Cypress ID# 337915: Restore response to LPM packets */
    lpmCtl = USBFS_DEV_LPM_LPM_CTL(base);
    USBFS_DEV_LPM_LPM_CTL(base) = lpmCtl;

    /* Releases PHY from suspend mode */
    USBFS_DEV_LPM_POWER_CTL(base) &= ~USBFS_USBLPM_POWER_CTL_SUSPEND_Msk;
    (void) USBFS_DEV_LPM_POWER_CTL(base);
}

#if defined(__cplusplus)
}
#endif

#endif /* CY_IP_MXUSBFS */


/* [] END OF FILE */