Newer
Older
mbed-os / targets / TARGET_Cypress / TARGET_PSOC6 / mtb-pdl-cat1 / drivers / source / cy_usbfs_dev_drv_io_dma.c
@Dustin Crossman Dustin Crossman on 4 Jun 2021 43 KB Fix file modes.
/***************************************************************************//**
* \file cy_usbfs_dev_drv_io_dma.c
* \version 2.20.2
*
* Provides data transfer 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 <string.h>
#include "cy_usbfs_dev_drv_pvt.h"

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

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

/* Invalid channel */
#define DMA_INVALID_CHANNEL     ((uint32_t) (-1))

/* Arbiter interrupt sources for OUT and IN endpoints when mode is
* CY_USBFS_DEV_DRV_EP_MANAGEMENT_DMA_AUTO.
*/
#define IN_ENDPOINT_ARB_INTR_SOURCES    (USBFS_USBDEV_ARB_EP_IN_BUF_FULL_Msk | \
                                         USBFS_USBDEV_ARB_EP_BUF_OVER_Msk    | \
                                         USBFS_USBDEV_ARB_EP_BUF_UNDER_Msk   | \
                                         USBFS_USBDEV_ARB_EP_ERR_Msk)

#define OUT_ENDPOINT_ARB_INTR_SOURCES   (USBFS_USBDEV_ARB_EP_DMA_TERMIN_Msk | \
                                         USBFS_USBDEV_ARB_EP_BUF_OVER_Msk   | \
                                         USBFS_USBDEV_ARB_EP_BUF_UNDER_Msk  | \
                                         USBFS_USBDEV_ARB_EP_ERR_Msk)

/* DMA configuration defines */
#define DMA_XLOOP_INCREMENT    (1)
#define DMA_YLOOP_INCREMENT    (32)
#define DMA_NO_INCREMENT       (0)

/* Timeout for dynamic reconfiguration */
#define DYN_RECONFIG_ONE_TICK   (1U)        /* 1 tick = 1 us */
#define DYN_RECONFIG_TIMEOUT    (25000UL)   /* (25000 * tick)us = 25 ms ( TDRQCMPLTND / 2 ) */

/* Timeout for DMA read operation */
#define DMA_READ_REQUEST_ONE_TICK   (1U)      /* 1 tick = 1 us */
#define DMA_READ_REQUEST_TIMEOUT    (25000UL) /* (25000 * tick)us = 25 ms ( TDRQCMPLTND / 2 ) */

#define DMA_WRITE_REQUEST_ONE_TICK   (1U)      /* 1 tick = 1 us */
#define DMA_WRITE_REQUEST_TIMEOUT    (25000UL) /* (25000 * tick)us = 25 ms ( TDRQCMPLTND / 2 ) */


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

static void DmaEndpointInit1D(cy_stc_dma_descriptor_t *descr,
                              bool inDirection,
                              cy_en_dma_data_size_t dataSize,
                              volatile uint32_t const *dataReg);

static void DmaEndpointInit2D(cy_stc_dma_descriptor_t *descr,
                              bool inDirection,
                              int32_t numElements);

static void DmaEndpointSetLength(bool inDirection,
                                 uint32_t size,
                                 cy_stc_usbfs_dev_drv_endpoint_data_t *endpoint);


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

/* Used for getting data in X loops */
#define DMA_DESCR_1D_CFG \
{                        \
    /* .retrigger      = */ CY_DMA_RETRIG_IM,        \
    /* .interruptType  = */ CY_DMA_DESCR,            \
    /* .triggerOutType = */ CY_DMA_DESCR,            \
    /* .channelState   = */ CY_DMA_CHANNEL_DISABLED, \
    /* .triggerInType  = */ CY_DMA_DESCR,            \
    /* .dataSize        = */ CY_DMA_BYTE,               \
    /* .srcTransferSize = */ CY_DMA_TRANSFER_SIZE_WORD, \
    /* .dstTransferSize = */ CY_DMA_TRANSFER_SIZE_WORD, \
    /* .descriptorType  = */ CY_DMA_1D_TRANSFER,        \
    /* .srcAddress     = */ NULL, \
    /* .dstAddress     = */ NULL, \
    /* .srcXincrement  = */ 0,    \
    /* .dstXincrement  = */ 0,    \
    /* .xCount         = */ 1UL,  \
    /* .srcYincrement  = */ 0,    \
    /* .dstYincrement  = */ 0,    \
    /* .yCount         = */ 1UL,  \
    /* .nextDescriptor = */ NULL, \
}

/* Used for getting data in Y loops */
#define DMA_DESCR_2D_CFG \
{                        \
    /* .retrigger      = */ CY_DMA_RETRIG_IM,       \
    /* .interruptType  = */ CY_DMA_X_LOOP,          \
    /* .triggerOutType = */ CY_DMA_X_LOOP,          \
    /* .channelState   = */ CY_DMA_CHANNEL_ENABLED, \
    /* .triggerInType  = */ CY_DMA_X_LOOP,          \
    /* .dataSize        = */ CY_DMA_BYTE,               \
    /* .srcTransferSize = */ CY_DMA_TRANSFER_SIZE_WORD, \
    /* .dstTransferSize = */ CY_DMA_TRANSFER_SIZE_WORD, \
    /* .descriptorType  = */ CY_DMA_2D_TRANSFER,        \
    /* .srcAddress     = */ NULL, \
    /* .dstAddress     = */ NULL, \
    /* .srcXincrement  = */ 0,    \
    /* .dstXincrement  = */ 0,    \
    /* .xCount         = */ 1UL,  \
    /* .srcYincrement  = */ 0,    \
    /* .dstYincrement  = */ 0,    \
    /* .yCount         = */ 1UL,  \
    /* .nextDescriptor = */ NULL, \
}


/*******************************************************************************
* Function Name: DmaInit
****************************************************************************//**
*
* Initializes all DMA channels used by the USBFS Device.
*
* \param config
* The pointer to the driver 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
* Status code of the function execution \ref cy_en_usbfs_dev_drv_status_t.
*
*******************************************************************************/
cy_en_usbfs_dev_drv_status_t DmaInit(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_DMA_CFG_FAILED;

    uint32_t endpoint;

    /* Configure DMA descriptors and channels for data endpoints */
    for (endpoint = 0UL; endpoint < CY_USBFS_DEV_DRV_NUM_EPS_MAX; ++endpoint)
    {
        /* DMA configuration status for endpoint */
        retStatus = CY_USBFS_DEV_DRV_DMA_CFG_FAILED;

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

        if (config->dmaConfig[endpoint] != NULL)
        {
            cy_en_usbfs_dev_drv_status_t locStatus = CY_USBFS_DEV_DRV_DMA_CFG_FAILED;

            cy_stc_dma_channel_config_t chConfig;

            /* Descriptors configurations */
            const cy_stc_dma_descriptor_config_t DmaDescr1DCfg = DMA_DESCR_1D_CFG;
            const cy_stc_dma_descriptor_config_t DmaDescr2DCfg = DMA_DESCR_2D_CFG;

            /* Store DMA configuration required for operation */
            endpointData->base   = config->dmaConfig[endpoint]->base;
            endpointData->chNum  = config->dmaConfig[endpoint]->chNum;
            endpointData->descr0 = config->dmaConfig[endpoint]->descr0;
            endpointData->descr1 = config->dmaConfig[endpoint]->descr1;
            endpointData->outTrigMux = config->dmaConfig[endpoint]->outTrigMux;
            endpointData->copyData   = NULL;

            if (CY_USBFS_DEV_DRV_EP_MANAGEMENT_DMA == context->mode)
            {
                if (NULL != endpointData->descr0)
                {
                    /* Initialize DMA descriptor 0 for 1D operation.
                    * Discard return because descriptor configuration (defined in driver) is valid.
                    */
                    (void) Cy_DMA_Descriptor_Init(endpointData->descr0, &DmaDescr1DCfg);

                    locStatus = CY_USBFS_DEV_DRV_SUCCESS;
                }
            }
            else
            {
                if ((NULL != endpointData->descr0) && (NULL != endpointData->descr1))
                {
                    /* Initialize DMA descriptor 0 for 2D operation.
                    * Discard return because descriptor configuration (defined in driver) is valid.
                    */
                    (void) Cy_DMA_Descriptor_Init(endpointData->descr0, &DmaDescr2DCfg);


                    /* Initialize DMA descriptor 0 for 1D operation.
                    * Discard return because descriptor configuration (defined in driver) is valid.
                    */
                    (void)Cy_DMA_Descriptor_Init(endpointData->descr1, &DmaDescr1DCfg);

                    locStatus = CY_USBFS_DEV_DRV_SUCCESS;
                }
            }

            /* Set DMA channel configuration */
            chConfig.enable      = false;
            chConfig.bufferable  = false;
            chConfig.descriptor  = config->dmaConfig[endpoint]->descr0;
            chConfig.preemptable = config->dmaConfig[endpoint]->preemptable;
            chConfig.priority    = config->dmaConfig[endpoint]->priority;

            /* Initialize DMA channel */
            if ((CY_DMA_SUCCESS != Cy_DMA_Channel_Init(endpointData->base, endpointData->chNum, &chConfig)) ||
                (CY_USBFS_DEV_DRV_SUCCESS != locStatus))
            {
                break;
            }

            /* Enable DMA block */
            Cy_DMA_Enable(endpointData->base);
        }
        else
        {
            endpointData->chNum = DMA_INVALID_CHANNEL;
        }

        /* Configuration complete successfully */
        retStatus = CY_USBFS_DEV_DRV_SUCCESS;
    }

    /* Disable all DMAs if any configuration failed */
    if (CY_USBFS_DEV_DRV_SUCCESS != retStatus)
    {
        DmaDisable(context);
    }

    return retStatus;
}


/*******************************************************************************
* Function Name: DmaDisable
****************************************************************************//**
*
* Disables all DMA channels used by the USBFS Device.
* The channel state is not verified before being disabled.
*
* \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 DmaDisable(cy_stc_usbfs_dev_drv_context_t *context)
{
    uint32_t endpoint;

    /* Disable DMA channels for data endpoints */
    for (endpoint = 0UL; endpoint < CY_USBFS_DEV_DRV_NUM_EPS_MAX; ++endpoint)
    {
        /* Get pointer to endpoint data */
        cy_stc_usbfs_dev_drv_endpoint_data_t *endpointData = &context->epPool[endpoint];

        if (endpointData->chNum != DMA_INVALID_CHANNEL)
        {
            Cy_DMA_Channel_Disable(endpointData->base, endpointData->chNum);
        }
    }
}


/*******************************************************************************
* Function Name: DmaEndpointInit1D
****************************************************************************//**
*
* Configures DMA 1D descriptor used in DMA modes.
*
* \param descr
* The pointer to the DMA descriptor.
*
* \param inDirection
* Endpoint direction associated with DMA descriptor.
*
* \param dataSize
* The DMA transfer data size \ref cy_en_dma_data_size_t.
*
* \param dataReg
* The pointer to the data endpoint data register.
*
*******************************************************************************/
static void DmaEndpointInit1D(cy_stc_dma_descriptor_t *descr, bool inDirection,
                              cy_en_dma_data_size_t dataSize, volatile uint32_t const *dataReg)
{
    Cy_DMA_Descriptor_SetDataSize(descr, dataSize);

    if (inDirection)
    {
        Cy_DMA_Descriptor_SetSrcTransferSize(descr, CY_DMA_TRANSFER_SIZE_DATA);
        Cy_DMA_Descriptor_SetDstTransferSize(descr, CY_DMA_TRANSFER_SIZE_WORD);

        Cy_DMA_Descriptor_SetXloopSrcIncrement(descr, DMA_XLOOP_INCREMENT);
        Cy_DMA_Descriptor_SetXloopDstIncrement(descr, DMA_NO_INCREMENT);

        Cy_DMA_Descriptor_SetDstAddress(descr, (void const *) dataReg);
    }
    else
    {
        Cy_DMA_Descriptor_SetSrcTransferSize(descr, CY_DMA_TRANSFER_SIZE_WORD);
        Cy_DMA_Descriptor_SetDstTransferSize(descr, CY_DMA_TRANSFER_SIZE_DATA);

        Cy_DMA_Descriptor_SetXloopSrcIncrement(descr, DMA_NO_INCREMENT);
        Cy_DMA_Descriptor_SetXloopDstIncrement(descr, DMA_XLOOP_INCREMENT);

        Cy_DMA_Descriptor_SetSrcAddress(descr, (void const *) dataReg);
    }

    /* Link descriptor to itself */
    Cy_DMA_Descriptor_SetNextDescriptor(descr, descr);
}


/*******************************************************************************
* Function Name: DmaEndpointInit2D
****************************************************************************//**
*
* Configures DMA 2D descriptor used in the DMA Automatic mode.
*
* \param descr
* The pointer to the DMA descriptor.
*
* \param inDirection
* Endpoint direction associated with DMA descriptor.
*
* \param numElements
* Number of elements to transfer.
*
*******************************************************************************/
static void DmaEndpointInit2D(cy_stc_dma_descriptor_t *descr, bool inDirection,
                              int32_t numElements)
{
    /* Descriptor 0 (2D): it transfers number of data elements (X loop count)
    * and increments source/destination (depends on direction) by this amount
    * (Y loop increment).
    */
    Cy_DMA_Descriptor_SetXloopDataCount(descr, (uint32_t) numElements);

    if (inDirection)
    {
        Cy_DMA_Descriptor_SetYloopSrcIncrement(descr, numElements);
        Cy_DMA_Descriptor_SetYloopDstIncrement(descr, DMA_NO_INCREMENT);
    }
    else
    {
        Cy_DMA_Descriptor_SetYloopSrcIncrement(descr, DMA_NO_INCREMENT);
        Cy_DMA_Descriptor_SetYloopDstIncrement(descr, numElements);
    }
}


/*******************************************************************************
* Function Name: DmaEndpointSetLength
****************************************************************************//**
*
* Completes DMA initialization for the OUT data endpoint.
* Applicable only when mode is \ref CY_USBFS_DEV_DRV_EP_MANAGEMENT_DMA_AUTO.
*
* \param inDirection
* Endpoint direction associated with DMA descriptor.
*
* \param size
* The number of bytes to load into endpoint.
* This value must be less than or equal to the endpoint maximum packet size.
*
* \param endpoint
* The pointer to the structure that stores endpoint information.
*
*******************************************************************************/
static void DmaEndpointSetLength(bool inDirection, uint32_t size,
                                 cy_stc_usbfs_dev_drv_endpoint_data_t *endpoint)
{
    uint8_t *buf = endpoint->buffer;

    /*
    * Descriptor 0: get number of Y loops. It transfers data in multiples of 32 bytes.
    * Descriptor 1: get number of X loops. It transfers data what was left 1-31 bytes.
    */
    uint32_t numYloops = size / (uint32_t) DMA_YLOOP_INCREMENT;
    uint32_t numXloops = size % (uint32_t) DMA_YLOOP_INCREMENT;

    /* Configure */
    if (inDirection)
    {
        endpoint->startBuf = (uint16_t) size;  /* Store DMA transfer size */
        Cy_DMA_Descriptor_SetSrcAddress(endpoint->descr0, buf);
        Cy_DMA_Descriptor_SetSrcAddress(endpoint->descr1, &buf[size - numXloops]);
    }
    else
    {
        endpoint->startBuf = (numYloops > 0U) ? 1U : 0U; /* Store 1st DMA descriptor to execute */
        Cy_DMA_Descriptor_SetDstAddress(endpoint->descr0, buf);
        Cy_DMA_Descriptor_SetDstAddress(endpoint->descr1, &buf[size - numXloops]);
    }

    /* Configure loop length */
    if (numYloops > 0UL)
    {
        Cy_DMA_Descriptor_SetYloopDataCount(endpoint->descr0, numYloops);
    }

    if (numXloops > 0UL)
    {
        Cy_DMA_Descriptor_SetXloopDataCount(endpoint->descr1, numXloops);
    }

    /* Chain descriptors to operate */
    if (numYloops == 0UL)
    {
        /* (Size < 32): only Descriptor 1 transfers data */
        Cy_DMA_Descriptor_SetNextDescriptor(endpoint->descr1, endpoint->descr1);
    }
    else if (numXloops == 0UL)
    {
        /* Size multiple of 32: only Descriptor 0 transfers data */
        Cy_DMA_Descriptor_SetNextDescriptor(endpoint->descr0, endpoint->descr0);
    }
    else
    {
        /* (Size > 32): both Descriptor 0 and 1 transfer data */
        Cy_DMA_Descriptor_SetNextDescriptor(endpoint->descr0, endpoint->descr1);
        Cy_DMA_Descriptor_SetNextDescriptor(endpoint->descr1, endpoint->descr0);
    }

    /* Keep channel enabled after execution of Descriptor 0 to execute Descriptor 1 */
    Cy_DMA_Descriptor_SetChannelState(endpoint->descr0,
                                        ((numYloops > 0UL) && (numXloops > 0UL)) ?
                                            CY_DMA_CHANNEL_ENABLED : CY_DMA_CHANNEL_DISABLED);

    /* Start execution from Descriptor 0 (length >= 32) or Descriptor 1 (length < 32) */
    Cy_DMA_Channel_SetDescriptor(endpoint->base, endpoint->chNum,
                                    ((numYloops > 0UL) ? endpoint->descr0 : endpoint->descr1));

    /* Configuration complete: enable channel */
    Cy_DMA_Channel_Enable(endpoint->base, endpoint->chNum);
}


/*******************************************************************************
* Function Name: DmaOutEndpointRestore
****************************************************************************//**
*
* Restores the DMA channel after transfer is completed for the the OUT data endpoint.
* Applicable only when mode is \ref CY_USBFS_DEV_DRV_EP_MANAGEMENT_DMA_AUTO.
*
* \param endpoint
* The pointer to the structure that stores endpoint information.
*
*******************************************************************************/
void DmaOutEndpointRestore(cy_stc_usbfs_dev_drv_endpoint_data_t *endpoint)
{
    /* Number of clocks for DMA completion pulse */
    const uint32_t CY_USBFS_DEV_DRV_TRIG_CYCLES = 2UL;

    /* Define which descriptor is 1st in the chain */
    bool setDescr0 = (0U != endpoint->startBuf);

    /* Channel disable aborts on-going transfer */
    Cy_DMA_Channel_Disable(endpoint->base, endpoint->chNum);

    /* Send pulse to UBSFS IP to indicate end of DMA transfer */
    (void) Cy_TrigMux_SwTrigger(endpoint->outTrigMux, CY_USBFS_DEV_DRV_TRIG_CYCLES);

    /* Set 1st DMA descriptor for the following transfer */
    Cy_DMA_Channel_SetDescriptor(endpoint->base, endpoint->chNum,
                                                (setDescr0 ? endpoint->descr0 : endpoint->descr1));

    Cy_DMA_Channel_Enable(endpoint->base, endpoint->chNum);
}


/*******************************************************************************
* Function Name: DmaEndpointInit
****************************************************************************//**
*
* Initializes DMA descriptor for a certain data endpoint.
*
* \param base
* The pointer to the USBFS instance.
*
* \param mode
*
* \param useReg16
* Defines which endpoint registers to use: 8-bits or 16-bits.
*
* \param endpointData
* The pointer to the endpoint data structure.
*
* \return
* Status code of the function execution \ref cy_en_usbfs_dev_drv_status_t.
*
*******************************************************************************/
cy_en_usbfs_dev_drv_status_t DmaEndpointInit(USBFS_Type *base,
                                             cy_en_usbfs_dev_drv_ep_management_mode_t mode,
                                             bool useReg16,
                                             cy_stc_usbfs_dev_drv_endpoint_data_t *endpointData)
{
    cy_en_dma_data_size_t regSize;
    int32_t numElements;
    uint32_t volatile *dataReg;

    /* Get direction and endpoint number */
    bool inDirection  = IS_EP_DIR_IN(endpointData->address);
    uint32_t endpoint = EPADDR2PHY(endpointData->address);

    if (DMA_INVALID_CHANNEL == endpointData->chNum)
    {
        return CY_USBFS_DEV_DRV_DMA_CFG_FAILED;
    }

    /* Set configuration variables depending on access type */
    if (useReg16)
    {
        dataReg     = Cy_USBFS_Dev_Drv_GetDataReg16Addr(base, endpoint);
        regSize     = CY_DMA_HALFWORD;
        numElements = (DMA_YLOOP_INCREMENT / 2);
    }
    else
    {
        dataReg     = Cy_USBFS_Dev_Drv_GetDataRegAddr(base, endpoint);
        regSize     = CY_DMA_BYTE;
        numElements = DMA_YLOOP_INCREMENT;
    }

    /* Disable channel before configuration */
    Cy_DMA_Channel_Disable(endpointData->base, endpointData->chNum);

    /* Configure Descriptor 0 for 1D operation */
    DmaEndpointInit1D(endpointData->descr0, inDirection, regSize, dataReg);

    if (CY_USBFS_DEV_DRV_EP_MANAGEMENT_DMA_AUTO == mode)
    {
        /* Configure Descriptor 0 for 2D operation */
        DmaEndpointInit2D(endpointData->descr0, inDirection, numElements);

        /* Configure Descriptor 1 for 1D operation */
        DmaEndpointInit1D(endpointData->descr1, inDirection, regSize, dataReg);

        /* Configure descriptors to access buffer */
        DmaEndpointSetLength(inDirection, (uint32_t) endpointData->bufferSize, endpointData);
    }

    return CY_USBFS_DEV_DRV_SUCCESS;
}


/*******************************************************************************
* Function Name: DynamicEndpointReConfiguration
****************************************************************************//**
*
* Changes endpoint direction (IN -> OUT or OUT -> IN).
* This function is also used to flush IN or OUT endpoint buffer.
* Applicable only when mode is \ref CY_USBFS_DEV_DRV_EP_MANAGEMENT_DMA_AUTO.
*
* \param base
* The pointer to the USBFS instance.
*
* \param inDirection
* True - endpoint direction is IN; False - endpoint direction is OUT.
*
* \param endpoint
* The data endpoint number.
*
* \return
* Status code of the function execution \ref cy_en_usbfs_dev_drv_status_t.
*
*******************************************************************************/
cy_en_usbfs_dev_drv_status_t DynamicEndpointReConfiguration(USBFS_Type *base,
                                                            bool        inDirection,
                                                            uint32_t    endpoint)
{
    cy_en_usbfs_dev_drv_status_t retStatus = CY_USBFS_DEV_DRV_EP_DYN_RECONFIG_TIMEOUT;

    uint32_t timeout = DYN_RECONFIG_TIMEOUT;

    /* Only enabled endpoint can be re-configured */
    if (0U == (USBFS_DEV_EP_ACTIVE(base) & EP2MASK(endpont)))
    {
        return CY_USBFS_DEV_DRV_BUF_ALLOC_FAILED;
    }

    /* Request reconfiguration for endpoint */
    USBFS_DEV_DYN_RECONFIG(base) = (USBFS_USBDEV_DYN_RECONFIG_EN_Msk |
                                    _VAL2FLD(USBFS_USBDEV_DYN_RECONFIG_EPNO, endpoint));

    /* Cypress ID#295549: execute dummy read */
    (void) USBFS_DEV_DYN_RECONFIG(base);

    /* Wait for dynamic re-configuration completion */
    while ((0U == (USBFS_DEV_DYN_RECONFIG(base) & USBFS_USBDEV_DYN_RECONFIG_RDY_STS_Msk)) &&
           (timeout > 0U))
    {
        Cy_SysLib_DelayUs(DYN_RECONFIG_ONE_TICK);
        --timeout;
    }

    /* Verify operation result */
    if (timeout > 0U)
    {
        Cy_USBFS_Dev_Drv_SetEpType(base, inDirection, endpoint);
        retStatus = CY_USBFS_DEV_DRV_SUCCESS;
    }

    /* Complete endpoint reconfiguration: clear request */
    USBFS_DEV_DYN_RECONFIG(base) &= ~USBFS_USBDEV_DYN_RECONFIG_EN_Msk;
    (void) USBFS_DEV_DYN_RECONFIG(base);

    /* Clear register for next re-configuration */
    USBFS_DEV_DYN_RECONFIG(base) = 0U;

    return retStatus;
}


/*******************************************************************************
* Function Name: AddEndpointRamBuffer
****************************************************************************//**
*
* Implements \ref Cy_USBFS_Dev_Drv_AddEndpoint for
* \ref CY_USBFS_DEV_DRV_EP_MANAGEMENT_DMA_AUTO mode.
*
* \param base
* The pointer to the USBFS instance.
*
* \param config
* The pointer to data endpoint configuration \ref cy_stc_usb_dev_ep_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
* Status code of the function execution \ref cy_en_usbfs_dev_drv_status_t.
*
*******************************************************************************/
cy_en_usbfs_dev_drv_status_t AddEndpointRamBuffer(USBFS_Type *base,
                                                  cy_stc_usb_dev_ep_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;

    uint32_t endpoint = EPADDR2PHY(config->endpointAddr);

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

    /* Get buffer for endpoint using buffer allocated by the user */
    if (config->allocBuffer)
    {
        uint32_t startBufIdx;

        /* Configure active endpoint */
        context->activeEpMask    |= (uint8_t) EP2MASK(endpont);
        USBFS_DEV_EP_ACTIVE(base) = context->activeEpMask;

        /* Allocate buffer for endpoint */
        retStatus = GetEndpointBuffer((uint32_t) config->bufferSize, &startBufIdx, context);
        if (CY_USBFS_DEV_DRV_SUCCESS != retStatus)
        {
            return retStatus;
        }

        /* Store pointer to buffer for this endpoint */
        endpointData->buffer = &context->epSharedBuf[startBufIdx];
    }

    /* Enable endpoint for operation */
    if (config->enableEndpoint)
    {
        bool inDirection = IS_EP_DIR_IN(config->endpointAddr);

        /* Clear variables related to endpoint */
        endpointData->state   = CY_USB_DEV_EP_IDLE;
        endpointData->address = config->endpointAddr;
        endpointData->toggle  = 0U;
        endpointData->bufferSize = config->maxPacketSize;
        endpointData->sieMode    = GetEndpointActiveMode(inDirection, config->attributes);
        endpointData->isPending  = false;

        /* Set arbiter configuration (clears DMA requests) */
        Cy_USBFS_Dev_Drv_SetArbEpConfig(base, endpoint, (USBFS_USBDEV_ARB_EP1_CFG_CRC_BYPASS_Msk |
                                                         USBFS_USBDEV_ARB_EP1_CFG_RESET_PTR_Msk));

        /* Performs dynamic reconfiguration to make sure that the DMA has completed the data transfer.
        * Also it flushes endpoint pre-fetch buffer (useful for IN endpoints).
        */
        retStatus = DynamicEndpointReConfiguration(base, inDirection, endpoint);
        if (CY_USBFS_DEV_DRV_SUCCESS != retStatus)
        {
            return retStatus;
        }

        /* Configure DMA for endpoint */
        retStatus = DmaEndpointInit(base, context->mode, context->useReg16, endpointData);
        if (CY_USBFS_DEV_DRV_SUCCESS != retStatus)
        {
            return retStatus;
        }

        /* Enable Arbiter interrupt sources for endpoint */
        Cy_USBFS_Dev_Drv_SetArbEpInterruptMask(base, endpoint,(inDirection ?
                                                             IN_ENDPOINT_ARB_INTR_SOURCES :
                                                             OUT_ENDPOINT_ARB_INTR_SOURCES));

        /* Enable SIE and arbiter interrupt for endpoint */
        Cy_USBFS_Dev_Drv_EnableSieEpInterrupt(base, endpoint);
        Cy_USBFS_Dev_Drv_EnableArbEpInterrupt(base, endpoint);

        /* Set SIE mode to respond to host */
        Cy_USBFS_Dev_Drv_SetSieEpMode(base, endpoint, GetEndpointInactiveMode((uint32_t) endpointData->sieMode));
    }

    return retStatus;
}


/*******************************************************************************
* Function Name: RestoreEndpointRamBuffer
****************************************************************************//**
*
* Restores endpoint active configuration for
* \ref CY_USBFS_DEV_DRV_EP_MANAGEMENT_DMA_AUTO mode.
*
* \param base
* The pointer to the USBFS instance.
*
* \param endpointData
* The pointer to the endpoint data structure.
*
*******************************************************************************/
void RestoreEndpointRamBuffer(USBFS_Type *base,
                              cy_stc_usbfs_dev_drv_endpoint_data_t *endpointData)
{
    bool inDirection  = IS_EP_DIR_IN(endpointData->address);
    uint32_t endpoint = EPADDR2PHY(endpointData->address);

    /* Clear state */
    endpointData->state = CY_USB_DEV_EP_IDLE;

    /* Configure active endpoint */
    USBFS_DEV_EP_ACTIVE(base) |= EP2MASK(endpont);

    /* Configure endpoint type: OUT - 1, IN - 0 */
    Cy_USBFS_Dev_Drv_SetEpType(base, inDirection, endpoint);

    /* Enable Arbiter interrupt sources for endpoint */
    Cy_USBFS_Dev_Drv_SetArbEpInterruptMask(base, endpoint, (inDirection ?
                                                            IN_ENDPOINT_ARB_INTR_SOURCES :
                                                            OUT_ENDPOINT_ARB_INTR_SOURCES));

    /* Enable SIE and arbiter interrupt for endpoint */
    Cy_USBFS_Dev_Drv_EnableSieEpInterrupt(base, endpoint);
    Cy_USBFS_Dev_Drv_EnableArbEpInterrupt(base, endpoint);

    if (false == inDirection)
    {
        /* OUT Endpoint: enable DMA channel endpoint ready for operation.
        * IN Endpoint: keep disabled, it is enabled in LoadInEndpointDmaAuto.
        */
        Cy_DMA_Channel_Enable(endpointData->base, endpointData->chNum);
    }

    /* Sets an arbiter configuration */
    Cy_USBFS_Dev_Drv_SetArbEpConfig(base, endpoint, (USBFS_USBDEV_ARB_EP1_CFG_CRC_BYPASS_Msk |
                                                     USBFS_USBDEV_ARB_EP1_CFG_RESET_PTR_Msk));

    /* Set SIE mode to respond to host */
    Cy_USBFS_Dev_Drv_SetSieEpMode(base, endpoint, GetEndpointInactiveMode((uint32_t) endpointData->sieMode));
}


/*******************************************************************************
* Function Name: LoadInEndpointDma
****************************************************************************//**
*
* Implements \ref Cy_USBFS_Dev_Drv_LoadInEndpoint for
* \ref CY_USBFS_DEV_DRV_EP_MANAGEMENT_DMA mode.
*
* \param base
* The pointer to the USBFS instance.
*
* \param endpoint
* The IN data endpoint number.
*
* \param buffer
* The pointer to the buffer containing data bytes to load.
*
* \param size
* The number of bytes to load into the endpoint.
* This value must be less than or equal to the endpoint maximum packet size.
*
* \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
* Status code of the function execution \ref cy_en_usbfs_dev_drv_status_t.
*
*******************************************************************************/
cy_en_usbfs_dev_drv_status_t LoadInEndpointDma(USBFS_Type    *base,
                                               uint32_t      endpoint,
                                               uint8_t const *buffer,
                                               uint32_t      size,
                                               cy_stc_usbfs_dev_drv_context_t *context)
{
    cy_en_usbfs_dev_drv_status_t retStatus = CY_USBFS_DEV_DRV_EP_DMA_WRITE_TIMEOUT;

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

    /* Request to load more bytes than endpoint buffer */
    if (size > endpointData->bufferSize)
    {
        return CY_USBFS_DEV_DRV_BAD_PARAM;
    }

    /* Clears the abort mask for the endpoint (there is no transfer during abort) */
    context->epAbortMask &= (uint8_t) ~EP2MASK(endpoint);

    /* Set count and data toggle */
    Cy_USBFS_Dev_Drv_SetSieEpCount(base, endpoint, size, (uint32_t) endpointData->toggle);

    if (0U == size)
    {
        /* Endpoint pending: Waits for the host read data after exiting this function */
        endpointData->state = CY_USB_DEV_EP_PENDING;

        /* Arm endpoint: Host is allowed to read data */
        Cy_USBFS_Dev_Drv_SetSieEpMode(base, endpoint, (uint32_t) endpointData->sieMode);

        retStatus = CY_USBFS_DEV_DRV_SUCCESS;
    }
    else
    {
        /* Channel is disabled after initialization or descriptor completion  */

        uint32_t timeout = DMA_WRITE_REQUEST_TIMEOUT;

        /* Get number of data elements to transfer */
        size = context->useReg16 ? GET_SIZE16(size) : size;

        /* 1D descriptor: configure source address and length */
        Cy_DMA_Descriptor_SetSrcAddress    (endpointData->descr0, (const void*) buffer);
        Cy_DMA_Descriptor_SetXloopDataCount(endpointData->descr0, size);

        /* Enable DMA channel: configuration complete */
        Cy_DMA_Channel_Enable(endpointData->base, endpointData->chNum);

        /* Generate DMA request: the endpoint will be armed when the DMA is
        * finished in the Arbiter interrupt
        */
        Cy_USBFS_Dev_Drv_TriggerArbCfgEpDmaReq(base, endpoint);

        /* Waits until DMA completes the write operation. The current endpoint state is
        * idle or completed and DMA completion interrupt changes state to pending
        * (endpoint waits for the host read data).
        */
        while ((CY_USB_DEV_EP_PENDING != endpointData->state) &&
               (timeout > 0U))
        {
            Cy_SysLib_DelayUs(DMA_WRITE_REQUEST_ONE_TICK);
            --timeout;
        }

        /* Check timeout */
        if (timeout > 0U)
        {
            retStatus = CY_USBFS_DEV_DRV_SUCCESS;
        }
    }

    return retStatus;
}


/*******************************************************************************
* Function Name: ReadOutEndpointDma
****************************************************************************//**
*
* Implements \ref Cy_USBFS_Dev_Drv_ReadOutEndpoint for
* \ref CY_USBFS_DEV_DRV_EP_MANAGEMENT_DMA mode.
*
* \param base
* The pointer to the USBFS instance.
*
* \param endpoint
* The OUT data endpoint number.
*
* \param buffer
* Pointer to buffer that stores data that was read.
*
* \param size
* The number of bytes to read from endpoint.
* This value must be less than or equal to the endpoint maximum packet size.
*
* \param actSize
* The number of bytes that were actually read.
*
* \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
* Status code of the function execution \ref cy_en_usbfs_dev_drv_status_t.
*
*******************************************************************************/
cy_en_usbfs_dev_drv_status_t ReadOutEndpointDma(USBFS_Type *base,
                                                uint32_t   endpoint,
                                                uint8_t    *buffer,
                                                uint32_t   size,
                                                uint32_t   *actSize,
                                                cy_stc_usbfs_dev_drv_context_t *context)
{
    cy_en_usbfs_dev_drv_status_t retStatus = CY_USBFS_DEV_DRV_EP_DMA_READ_TIMEOUT;

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

    uint32_t timeout = DMA_READ_REQUEST_TIMEOUT;

    /* Get number of received bytes */
    uint32_t numToCopy = Cy_USBFS_Dev_Drv_GetSieEpCount(base, endpoint);
    uint32_t actCopied = numToCopy;

    /* Initialize actual number of copied bytes */
    *actSize = 0U;

    /* Endpoint received more bytes than provided buffer */
    if (numToCopy > size)
    {
        return CY_USBFS_DEV_DRV_BAD_PARAM;
    }

    /* Nothing to copy; return success */
    if (0U == numToCopy)
    {
        return CY_USBFS_DEV_DRV_SUCCESS;
    }

    /* Channel is disabled after initialization or descriptor completion  */

    /* Get number of data elements to transfer */
    numToCopy = context->useReg16 ? GET_SIZE16(numToCopy) : numToCopy;

    /* 1D descriptor: configure destination address and length */
    Cy_DMA_Descriptor_SetDstAddress    (endpointData->descr0, buffer);
    Cy_DMA_Descriptor_SetXloopDataCount(endpointData->descr0, numToCopy);

    /* Enable DMA channel: configuration complete */
    Cy_DMA_Channel_Enable(endpointData->base, endpointData->chNum);

    /* The current endpoint state is completed, changes the state to pending to
    * track DMA read completion.
    */
    endpointData->state = CY_USB_DEV_EP_PENDING;

    /* Generate DMA request to read data from hardware buffer */
    Cy_USBFS_Dev_Drv_TriggerArbCfgEpDmaReq(base, endpoint);

    /* Waits until DMA completes the read operation */
    while ((CY_USB_DEV_EP_COMPLETED != endpointData->state) &&
           (timeout > 0U))
    {
        Cy_SysLib_DelayUs(DMA_READ_REQUEST_ONE_TICK);
        --timeout;
    }

    /* Check timeout */
    if (timeout > 0U)
    {
        /* Update number of copied bytes */
        *actSize = actCopied;
        retStatus = CY_USBFS_DEV_DRV_SUCCESS;
    }

    return retStatus;
}


/*******************************************************************************
* Function Name: LoadInEndpointDmaAuto
****************************************************************************//**
*
* Implements \ref Cy_USBFS_Dev_Drv_LoadInEndpoint for
* \ref CY_USBFS_DEV_DRV_EP_MANAGEMENT_DMA_AUTO mode.
*
* \param base
* The pointer to the USBFS instance.
*
* \param endpoint
* The IN data endpoint number.
*
* \param buffer
* The pointer to the buffer containing data bytes to load.
*
* \param size
* The number of bytes to load into endpoint.
* This value must be less than or equal to the endpoint maximum packet size.
*
* \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
* Status code of the function execution \ref cy_en_usbfs_dev_drv_status_t.
*
*******************************************************************************/
cy_en_usbfs_dev_drv_status_t LoadInEndpointDmaAuto(USBFS_Type    *base,
                                                   uint32_t      endpoint,
                                                   uint8_t const *buffer,
                                                   uint32_t      size,
                                                   cy_stc_usbfs_dev_drv_context_t *context)
{
    /* Get pointer to endpoint data */
    cy_stc_usbfs_dev_drv_endpoint_data_t *endpointData = &context->epPool[endpoint];

    /* Request to load more bytes than endpoint buffer */
    if (size > endpointData->bufferSize)
    {
        return CY_USBFS_DEV_DRV_BAD_PARAM;
    }

    /* Clears the abort mask for the endpoint (there is no transfer during abort) */
    context->epAbortMask &= (uint8_t) ~EP2MASK(endpoint);

    /* Endpoint pending: Waits for the host read data after exiting this function */
    endpointData->state = CY_USB_DEV_EP_PENDING;

    /* Set count and data toggle */
    Cy_USBFS_Dev_Drv_SetSieEpCount(base, endpoint, size, (uint32_t) endpointData->toggle);

    if (0U == size)
    {
        /* Arm endpoint: Host is allowed to read data */
        Cy_USBFS_Dev_Drv_SetSieEpMode(base, endpoint, (uint32_t) endpointData->sieMode);
    }
    else
    {
        /* Copy data from user buffer to internal endpoint buffer */
        if (NULL != endpointData->copyData)
        {
            (void) endpointData->copyData(endpointData->buffer, buffer, size);
        }
        else
        {
            (void) memcpy(endpointData->buffer, buffer, size);
        }

        /* Configure transfer length */
        if (size != endpointData->startBuf)
        {
            /* Update transfer length, endpoint startBuf and enables DMA */
            DmaEndpointSetLength(true, size, &context->epPool[endpoint]);
        }
        else
        {
            /* Reset DMA channel indexes, they keep value after Resume or Abort */
            Cy_DMA_Channel_SetDescriptor(endpointData->base, endpointData->chNum,
                                            (endpointData->startBuf >= (uint32_t) DMA_YLOOP_INCREMENT) ?
                                                endpointData->descr0 : endpointData->descr1);

            /* Enable channel: configuration complete */
            Cy_DMA_Channel_Enable(endpointData->base, endpointData->chNum);
        }

        /* Generate DMA request to pre-load data into endpoint buffer */
        Cy_USBFS_Dev_Drv_SetArbCfgEpInReady(base, endpoint);

        /* IN endpoint will be armed in the Arbiter interrupt (source: IN_BUF_FULL)
        * after DMA pre-load data buffer.
        */
    }

    return CY_USBFS_DEV_DRV_SUCCESS;
}


/*******************************************************************************
* Function Name: ReadOutEndpointDmaAuto
****************************************************************************//**
*
* Implements \ref Cy_USBFS_Dev_Drv_ReadOutEndpoint for
* \ref CY_USBFS_DEV_DRV_EP_MANAGEMENT_DMA_AUTO mode.
*
* \param base
* The pointer to the USBFS instance.
*
* \param endpoint
* The OUT data endpoint number.
*
* \param buffer
* Pointer to buffer that stores data that was read.
*
* \param size
* The number of bytes to read from the endpoint.
* This value must be less than or equal to the endpoint maximum packet size.
*
* \param actSize
* The number of bytes which were actually read.
*
* \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
* Status code of the function execution \ref cy_en_usbfs_dev_drv_status_t.
*
*******************************************************************************/
cy_en_usbfs_dev_drv_status_t ReadOutEndpointDmaAuto(USBFS_Type *base,
                                                    uint32_t   endpoint,
                                                    uint8_t    *buffer,
                                                    uint32_t   size,
                                                    uint32_t   *actSize,
                                                    cy_stc_usbfs_dev_drv_context_t *context)
{
    /* Get pointer to endpoint data */
    cy_stc_usbfs_dev_drv_endpoint_data_t *endpointData = &context->epPool[endpoint];

    /* Get number of received bytes */
    uint32_t numToCopy = Cy_USBFS_Dev_Drv_GetSieEpCount(base, endpoint);

    /* Initialize actual number of copied bytes */
    *actSize = 0U;

    /* Endpoint received more bytes than provided buffer */
    if (numToCopy > size)
    {
        return CY_USBFS_DEV_DRV_BAD_PARAM;
    }

    /* Nothing to copy (zero length packet) return success */
    if (0U == numToCopy)
    {
        return CY_USBFS_DEV_DRV_SUCCESS;
    }

    /* Copy data from user buffer to internal endpoint buffer */
    if (NULL != endpointData->copyData)
    {
        (void) endpointData->copyData(buffer, endpointData->buffer, numToCopy);
    }
    else
    {
        (void) memcpy(buffer, endpointData->buffer, numToCopy);
    }

    /* Update number of copied bytes */
    *actSize = numToCopy;

    return CY_USBFS_DEV_DRV_SUCCESS;
}


#if defined(__cplusplus)
}
#endif

#endif /* CY_IP_MXUSBFS */


/* [] END OF FILE */