Newer
Older
mbed-os / targets / TARGET_Cypress / TARGET_PSOC6 / mtb-hal-cat1 / source / cyhal_usb_dev.c
@Dustin Crossman Dustin Crossman on 4 Jun 2021 32 KB Fix file modes.
/*******************************************************************************
* File Name: cyhal_usb_dev.c
*
* \brief
* Provides a high level interface for interacting with the Cypress USB Device.
* This interface abstracts out the chip specific details.
* If any chip specific functionality is necessary, or performance is critical
* the low level functions can be used directly.
*
********************************************************************************
* \copyright
* Copyright 2019-2021 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 "cyhal_usb_dev.h"
#include "cyhal_gpio.h"
#include "cyhal_hwmgr.h"
#include "cyhal_syspm.h"
#include "cyhal_utils.h"

#if defined(CY_IP_MXUSBFS)

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

/******************************************************************************
 ********************* Internal Support Macros/Functions **********************
 *****************************************************************************/

/* Interrupt configuration and access */
#define _CYHAL_USB_DEV_DEFAULT_IRQ_PRIORITY    (3U)

/* Assign all interrupt sources to Low interrupt */
#define _CYHAL_USB_DEV_IRQ_LVL                 (CY_USBFS_DEV_DRV_LVL_LOW)
#define _CYHAL_USB_DEV_IRQ_LVL_DEFAULT         (CY_USBFS_DEV_DRV_SET_SOF_LVL(_CYHAL_USB_DEV_IRQ_LVL)      | \
                                                CY_USBFS_DEV_DRV_SET_BUS_RESET_LVL(_CYHAL_USB_DEV_IRQ_LVL) | \
                                                CY_USBFS_DEV_DRV_SET_EP0_LVL(_CYHAL_USB_DEV_IRQ_LVL)       | \
                                                CY_USBFS_DEV_DRV_SET_LPM_LVL(_CYHAL_USB_DEV_IRQ_LVL)       | \
                                                CY_USBFS_DEV_DRV_SET_ARB_EP_LVL(_CYHAL_USB_DEV_IRQ_LVL)    | \
                                                CY_USBFS_DEV_DRV_SET_EP1_LVL(_CYHAL_USB_DEV_IRQ_LVL)       | \
                                                CY_USBFS_DEV_DRV_SET_EP2_LVL(_CYHAL_USB_DEV_IRQ_LVL)       | \
                                                CY_USBFS_DEV_DRV_SET_EP3_LVL(_CYHAL_USB_DEV_IRQ_LVL)       | \
                                                CY_USBFS_DEV_DRV_SET_EP4_LVL(_CYHAL_USB_DEV_IRQ_LVL)       | \
                                                CY_USBFS_DEV_DRV_SET_EP5_LVL(_CYHAL_USB_DEV_IRQ_LVL)       | \
                                                CY_USBFS_DEV_DRV_SET_EP6_LVL(_CYHAL_USB_DEV_IRQ_LVL)       | \
                                                CY_USBFS_DEV_DRV_SET_EP7_LVL(_CYHAL_USB_DEV_IRQ_LVL)       | \
                                                CY_USBFS_DEV_DRV_SET_EP8_LVL(_CYHAL_USB_DEV_IRQ_LVL))

#define _CYHAL_USB_DEV_IS_EVENT_VALID(event)    ( ((event) == CYHAL_USB_DEV_EVENT_BUS_RESET) || \
                                                  ((event) == CYHAL_USB_DEV_EVENT_EP0_SETUP) || \
                                                  ((event) == CYHAL_USB_DEV_EVENT_EP0_IN)    || \
                                                  ((event) == CYHAL_USB_DEV_EVENT_EP0_OUT) )

#define _CYHAL_USB_DEV_IS_EP_NUM_VALID(endpoint)     CY_USBFS_DEV_DRV_IS_EP_VALID(endpoint)
#define _CYHAL_USB_DEV_IS_EP0_SIZE_VALID(ep0_size)   (ep0_size >= CY_USBFS_DEV_DRV_EP0_BUFFER_SIZE)

#define _CYHAL_USB_DEV_EVENT_NUM          (4)
#define _CYHAL_USB_DEV_EP_EVENT_NUM       (CY_USBFS_DEV_DRV_NUM_EPS_MAX)

/* Clock configuration constants */
#define _CYHAL_USB_DEV_USB_CLK_HF                (3U)        /* USBFS block dedicated path clock: CLK_HF3 */
#define _CYHAL_USB_DEV_USB_CLK_HF_FREQ           (48000000U) /* CLK_HF3 required frequency, Hz */
#define _CYHAL_USB_DEV_BUS_RESET_CLOCK_HZ        (100000U)   /* Bus Reset require frequency, Hz */

typedef enum
{
    CYHAL_USB_DEV_EP1_IDX = 0,     /* Callback number for Endpoint 1 completion interrupt */
    CYHAL_USB_DEV_EP2_IDX = 1,     /* Callback number for Endpoint 2 completion interrupt */
    CYHAL_USB_DEV_EP3_IDX = 2,     /* Callback number for Endpoint 3 completion interrupt */
    CYHAL_USB_DEV_EP4_IDX = 3,     /* Callback number for Endpoint 4 completion interrupt */
    CYHAL_USB_DEV_EP5_IDX = 4,     /* Callback number for Endpoint 5 completion interrupt */
    CYHAL_USB_DEV_EP6_IDX = 5,     /* Callback number for Endpoint 6 completion interrupt */
    CYHAL_USB_DEV_EP7_IDX = 6,     /* Callback number for Endpoint 7 completion interrupt */
    CYHAL_USB_DEV_EP8_IDX = 7,     /* Callback number for Endpoint 8 completion interrupt */
} _cyhal_usb_dev_ep_cb_num_t;

static USBFS_Type* const _CYHAL_USB_DEV_BASE_ADDRESSES[CY_IP_MXUSBFS_INSTANCES] =
{
#ifdef USBFS0
    USBFS0,
#endif
};

static const IRQn_Type _CYHAL_USB_DEV_IRQ_N[CY_IP_MXUSBFS_INSTANCES] =
{
#ifdef USBFS0
    usb_interrupt_lo_IRQn,
#endif
};


static bool      _cyhal_usb_dev_set_hf_divider(uint32_t clock, uint32_t input_freq, uint32_t target_freq);
static cy_rslt_t _cyhal_usb_dev_reseve_pll(cyhal_resource_inst_t *rsc);
static cy_rslt_t _cyhal_usb_dev_init_pll(uint32_t clock, uint32_t pll, uint32_t target_freq);
static uint32_t _cyhal_usb_dev_get_pll_freq(uint32_t path);
static cy_rslt_t _cyhal_usb_dev_hf_clock_setup(cyhal_usb_dev_t *obj);
static cy_rslt_t _cyhal_usb_dev_peri_clock_setup(cyhal_usb_dev_t *obj, const cyhal_clock_t *clk);

static cy_rslt_t _cyhal_usb_dev_pin_setup(cyhal_usb_dev_t *obj, cyhal_gpio_t dp, cyhal_gpio_t dm);
static void _cyhal_usb_dev_free_resources(cyhal_usb_dev_t *obj);

static void _cyhal_usb_0_dev_bus_reset_callback(USBFS_Type *base, struct cy_stc_usbfs_dev_drv_context *drvContext);
static void _cyhal_usb_0_dev_ep0_setup_callback(USBFS_Type *base, struct cy_stc_usbfs_dev_drv_context *drvContext);
static void _cyhal_usb_0_dev_ep0_in_callback(USBFS_Type *base, struct cy_stc_usbfs_dev_drv_context *drvContext);
static void _cyhal_usb_0_dev_ep0_out_callback(USBFS_Type *base, struct cy_stc_usbfs_dev_drv_context *drvContext);
static cyhal_usb_dev_event_callback_t _cyhal_usb_dev_event_callback_table[CY_IP_MXUSBFS_INSTANCES][_CYHAL_USB_DEV_EVENT_NUM];
static const cy_cb_usbfs_dev_drv_callback_t _cyhal_usb_dev_drv_event_cb_table[CY_IP_MXUSBFS_INSTANCES][_CYHAL_USB_DEV_EVENT_NUM] =
{
    /* USBFS0 */
    {
        &_cyhal_usb_0_dev_bus_reset_callback,    /* CY_USB_DEV_BUS_RESET = 0U */
        &_cyhal_usb_0_dev_ep0_setup_callback,    /* CY_USB_DEV_EP0_SETUP = 1U */
        &_cyhal_usb_0_dev_ep0_in_callback,       /* CY_USB_DEV_EP0_IN    = 2U */
        &_cyhal_usb_0_dev_ep0_out_callback       /* CY_USB_DEV_EP0_OUT   = 3U */
    },
};

static void _cyhal_usb_0_dev_sof_callback(USBFS_Type *base, struct cy_stc_usbfs_dev_drv_context *drvContext);
static cyhal_usb_dev_sof_callback_t _cyhal_usb_dev_sof_user_callback[CY_IP_MXUSBFS_INSTANCES];
static const cy_cb_usbfs_dev_drv_callback_t _cyhal_usb_dev_drv_sof_cb_table[CY_IP_MXUSBFS_INSTANCES]=
{
    /* USBFS0 */
    &_cyhal_usb_0_dev_sof_callback,
};

static void _cyhal_usb_0_dev_ep_callback(USBFS_Type *base, uint32_t ep_addr, uint32_t errorType, cy_stc_usbfs_dev_drv_context_t *drvContext);
static cyhal_usb_dev_endpoint_callback_t _cyhal_usb_dev_ep_handler_table[CY_IP_MXUSBFS_INSTANCES][_CYHAL_USB_DEV_EP_EVENT_NUM];
static const cy_cb_usbfs_dev_drv_ep_callback_t _cyhal_usb_dev_drv_ep_cb_table[CY_IP_MXUSBFS_INSTANCES] =
{
    /* USBFS0 */
    &_cyhal_usb_0_dev_ep_callback,
};

static void _cyhal_usb_0_dev_bus_reset_callback(USBFS_Type *base, struct cy_stc_usbfs_dev_drv_context *drvContext)
{
    (void) base; (void) drvContext;

    _cyhal_usb_dev_event_callback_table[0][CYHAL_USB_DEV_EVENT_BUS_RESET]();
}

static void _cyhal_usb_0_dev_ep0_setup_callback(USBFS_Type *base, struct cy_stc_usbfs_dev_drv_context *drvContext)
{
    (void) base; (void) drvContext;

    _cyhal_usb_dev_event_callback_table[0][CYHAL_USB_DEV_EVENT_EP0_SETUP]();
}

static void _cyhal_usb_0_dev_ep0_in_callback(USBFS_Type *base, struct cy_stc_usbfs_dev_drv_context *drvContext)
{
    (void) base; (void) drvContext;

    _cyhal_usb_dev_event_callback_table[0][CYHAL_USB_DEV_EVENT_EP0_IN]();
}

static void _cyhal_usb_0_dev_ep0_out_callback(USBFS_Type *base, struct cy_stc_usbfs_dev_drv_context *drvContext)
{
    (void) base; (void) drvContext;

    _cyhal_usb_dev_event_callback_table[0][CYHAL_USB_DEV_EVENT_EP0_OUT]();
}

static void _cyhal_usb_0_dev_sof_callback(USBFS_Type *base, struct cy_stc_usbfs_dev_drv_context *drvContext)
{
    (void) drvContext;

    if (NULL != _cyhal_usb_dev_sof_user_callback[0])
    {
        uint32_t frame_number = Cy_USBFS_Dev_Drv_GetSofNubmer(base);
        _cyhal_usb_dev_sof_user_callback[0](frame_number);
    }
}

static void _cyhal_usb_0_dev_ep_callback(USBFS_Type *base, uint32_t ep_addr, uint32_t errorType, cy_stc_usbfs_dev_drv_context_t *drvContext)
{
    (void) base; (void) errorType; (void) drvContext;
    uint32_t endpoint_idx = CYHAL_USB_DEV_GET_EP_IDX(ep_addr);
    cyhal_usb_dev_endpoint_callback_t ep_handler = _cyhal_usb_dev_ep_handler_table[0U][endpoint_idx];

    if (NULL != ep_handler)
    {
        ep_handler((cyhal_usb_dev_ep_t)ep_addr);
    }
}

static bool _cyhal_usb_dev_set_hf_divider(uint32_t clock, uint32_t input_freq, uint32_t target_freq)
{
    bool divider_found;
    cy_en_clkhf_dividers_t divider;

    if (target_freq == input_freq)
    {
        divider_found = true;
        divider = CY_SYSCLK_CLKHF_NO_DIVIDE;
    }
    else if ((target_freq * 2U) == input_freq)
    {
        divider_found = true;
        divider = CY_SYSCLK_CLKHF_DIVIDE_BY_2;
    }
    else if ((target_freq * 4U) == input_freq)
    {
        divider_found = true;
        divider = CY_SYSCLK_CLKHF_DIVIDE_BY_4;
    }
    else if ((target_freq * 8U) == input_freq)
    {
        divider_found = true;
        divider = CY_SYSCLK_CLKHF_DIVIDE_BY_8;
    }
    else
    {
        divider_found = false;
    }

    if (divider_found)
    {
        Cy_SysClk_ClkHfSetDivider(clock, divider);
    }

    return divider_found;
}

static cy_rslt_t _cyhal_usb_dev_reseve_pll(cyhal_resource_inst_t *rsc)
{
    cy_rslt_t result = CYHAL_USB_DEV_RSLT_ERR_CLK_CFG;
    uint32_t i;

    rsc->type = CYHAL_RSC_CLKPATH;
    rsc->channel_num = 0U;

    for (i = 1U; i <= SRSS_NUM_PLL; ++i)
    {
        /* Set block number to path number that belongs to PLL */
        rsc->block_num = (uint8_t)i;

        if (CY_RSLT_SUCCESS == cyhal_hwmgr_reserve(rsc))
        {
            result = CY_RSLT_SUCCESS;
            break;
        }
    }

    if (CY_RSLT_SUCCESS != result)
    {
        /* Reservation failed mark type invalid */
        rsc->type = CYHAL_RSC_INVALID;
    }

    return result;
}

static cy_rslt_t _cyhal_usb_dev_init_pll(uint32_t clock, uint32_t pll, uint32_t target_freq)
{
    cy_stc_pll_config_t cfg;

    Cy_SysClk_PllDisable(pll);
    Cy_SysClk_ClkHfSetSource(clock, (cy_en_clkhf_in_sources_t)(pll));

    cfg.inputFreq  = CY_SYSCLK_IMO_FREQ;
    cfg.outputFreq = target_freq;
    cfg.lfMode     = false;
    cfg.outputMode = CY_SYSCLK_FLLPLL_OUTPUT_AUTO;

    Cy_SysClk_ClkPathSetSource(pll, CY_SYSCLK_CLKPATH_IN_IMO);
    cy_rslt_t result = Cy_SysClk_PllConfigure(pll, &cfg);

    if (result == CY_RSLT_SUCCESS)
    {
        /* Wait up to 1 seconds for PLL to lock */
        result = Cy_SysClk_PllEnable(pll, 1000000);
    }

    if (result == CY_RSLT_SUCCESS)
    {
        Cy_SysClk_ClkHfSetDivider(clock, CY_SYSCLK_CLKHF_NO_DIVIDE);
    }

    return result;
}

static uint32_t _cyhal_usb_dev_get_pll_freq(uint32_t path)
{
    /* PLL sourced from the IMO */
    uint32_t freq = CY_SYSCLK_IMO_FREQ;

    cy_stc_pll_manual_config_t pll_config;
    Cy_SysClk_PllGetConfiguration(path, &pll_config);

    if (pll_config.outputMode != CY_SYSCLK_FLLPLL_OUTPUT_INPUT)
    {
        freq = (uint32_t)CY_SYSLIB_DIV_ROUND(((uint64_t)freq * (uint64_t)pll_config.feedbackDiv),
                                             ((uint64_t)pll_config.referenceDiv * (uint64_t)pll_config.outputDiv));
    }

    return freq;
}

static cy_rslt_t _cyhal_usb_dev_hf_clock_setup(cyhal_usb_dev_t *obj)
{
    cy_rslt_t result = CYHAL_USB_DEV_RSLT_ERR_CLK_CFG;
    uint32_t  clock  = _CYHAL_USB_DEV_USB_CLK_HF;
    uint32_t path;

    /* Start CLK_HF3 configuration */
    Cy_SysClk_ClkHfDisable(clock);

    /* Loop through all enabled PLLs and find one that matches requirements:
    * sourced from IMO and frequency in multiple of 48MHz.
    */
    for (path = 1U; path <= SRSS_NUM_PLL; ++path)
    {
        if ( Cy_SysClk_PllIsEnabled(path) &&
            (CY_SYSCLK_CLKPATH_IN_IMO == Cy_SysClk_ClkPathGetSource(path)) )
        {
            /* Get PLL frequency */
            uint32_t clk_pll_freq = _cyhal_usb_dev_get_pll_freq(path);

            /* Try to adjust CLK_HF3 divider to meet frequency requirements (48 MHz) */
            if (_cyhal_usb_dev_set_hf_divider(clock, clk_pll_freq, _CYHAL_USB_DEV_USB_CLK_HF_FREQ))
            {
                /* Change path if it does not match after successful divider update */
                if (Cy_SysClk_ClkHfGetSource(clock) != path)
                {
                    Cy_SysClk_ClkHfSetSource(clock, (cy_en_clkhf_in_sources_t) path);
                }

                /* The existing PLL is used, mark to not try clear it */
                result = CY_RSLT_SUCCESS;
                break;
            }
        }
    }

    /* None of existing PLLs do meet USB requirements, try to allocate free */
    if (CY_RSLT_SUCCESS != result)
    {
        result = _cyhal_usb_dev_reseve_pll(&(obj->pll_resource));

        if (CY_RSLT_SUCCESS == result)
        {
            /* Sets PLL source IMO and clear CLK_HF3 divider */
            result = _cyhal_usb_dev_init_pll(clock, obj->pll_resource.block_num, _CYHAL_USB_DEV_USB_CLK_HF_FREQ);
        }
    }

    /* End CLK_HF3 configuration */
    Cy_SysClk_ClkHfEnable(clock);

    if (result == CY_RSLT_SUCCESS)
    {
        SystemCoreClockUpdate();
    }

    return result;
}

static cy_rslt_t _cyhal_usb_dev_peri_clock_setup(cyhal_usb_dev_t *obj, const cyhal_clock_t *clk)
{
    cy_rslt_t result;
    cy_en_sysclk_status_t status = CY_SYSCLK_BAD_PARAM;

    if (NULL == clk)
    {
        obj->shared_clock = false;

        /* USB bus reset clock must be 100KHz. Usual peri clock frequency is > 26 MHz, which requires 16-bit divider */
        result = cyhal_hwmgr_allocate_clock(&(obj->clock), CY_SYSCLK_DIV_16_BIT, true);

        if (CY_RSLT_SUCCESS == result)
        {
            /* Get divider to provide 100kHz clock or less */
            uint32_t div_value = (Cy_SysClk_ClkPeriGetFrequency() / _CYHAL_USB_DEV_BUS_RESET_CLOCK_HZ) - 1U;

            (void) Cy_SysClk_PeriphDisableDivider(obj->clock.div_type, obj->clock.div_num);
            status = Cy_SysClk_PeriphSetDivider(obj->clock.div_type, obj->clock.div_num, div_value);
            (void) Cy_SysClk_PeriphEnableDivider(obj->clock.div_type, obj->clock.div_num);
        }
    }
    else
    {
        obj->clock = *clk;
        obj->shared_clock = true;
        status = CY_SYSCLK_SUCCESS;
    }

    if (CY_SYSCLK_SUCCESS == status)
    {
        status = Cy_SysClk_PeriphAssignDivider(PCLK_USB_CLOCK_DEV_BRS, obj->clock.div_type, obj->clock.div_num);
    }

    result = (CY_SYSCLK_SUCCESS == status) ? CY_RSLT_SUCCESS : CYHAL_USB_DEV_RSLT_ERR_CLK_CFG;

    return result;
}

static cy_rslt_t _cyhal_usb_dev_pin_setup(cyhal_usb_dev_t *obj, cyhal_gpio_t dp, cyhal_gpio_t dm)
{
    cy_rslt_t result = CYHAL_USB_DEV_RSLT_ERR;

    const cyhal_resource_pin_mapping_t *dp_map = _CYHAL_UTILS_GET_RESOURCE(dp, cyhal_pin_map_usb_usb_dp_pad);
    const cyhal_resource_pin_mapping_t *dm_map = _CYHAL_UTILS_GET_RESOURCE(dm, cyhal_pin_map_usb_usb_dm_pad);

    if ((NULL != dp_map) && (NULL != dm_map) &&
       _cyhal_utils_resources_equal(dp_map->inst, dm_map->inst))
    {
        obj->resource = *dp_map->inst;

        /* reserve DM and DP pins */
        result = _cyhal_utils_reserve_and_connect(dp, dp_map);
        if (CY_RSLT_SUCCESS == result)
        {
            obj->pin_dp = dp;

            result = _cyhal_utils_reserve_and_connect(dm, dm_map);
            if (CY_RSLT_SUCCESS == result)
            {
                obj->pin_dm = dm;
            }
        }
    }

    return result;
}

static void _cyhal_usb_dev_free_resources(cyhal_usb_dev_t *obj)
{
    /* The object set into the default state in the cyhal_usb_dev_init() */

    if (CYHAL_RSC_INVALID != obj->resource.type)
    {
        cyhal_hwmgr_free(&(obj->resource));
    }

    if (CYHAL_RSC_INVALID != obj->pll_resource.type)
    {
        cyhal_hwmgr_free(&(obj->pll_resource));
    }

    if (!obj->shared_clock)
    {
        cyhal_hwmgr_free_clock(&(obj->clock));
    }

    _cyhal_utils_release_if_used(&(obj->pin_dp));
    _cyhal_utils_release_if_used(&(obj->pin_dm));
}

static bool _cyhal_usb_dev_pm_callback(cyhal_syspm_callback_state_t state, cyhal_syspm_callback_mode_t mode, void* callback_arg)
{
    CY_UNUSED_PARAMETER(state);
    CY_UNUSED_PARAMETER(mode);
    cyhal_usb_dev_t *obj = (cyhal_usb_dev_t *)callback_arg;
    return USBFS_DEV_LPM_POWER_CTL(obj->base) & USBFS_USBLPM_POWER_CTL_SUSPEND_Msk;
}


/******************************************************************************
 ***************************** Public Functions *******************************
 *****************************************************************************/

cy_rslt_t cyhal_usb_dev_init(cyhal_usb_dev_t *obj, cyhal_gpio_t dp, cyhal_gpio_t dm, const cyhal_clock_t *clk)
{
    cy_rslt_t result;

    CY_ASSERT(NULL != obj);
    memset(obj, 0, sizeof(cyhal_usb_dev_t));

    memset(obj, 0, sizeof(cyhal_usb_dev_t));

    /* Reset object into the default state to handle resource free */
    obj->base = NULL;
    obj->resource.type     = CYHAL_RSC_INVALID;
    obj->pll_resource.type = CYHAL_RSC_INVALID;
    obj->shared_clock     = true;
    obj->pin_dp = CYHAL_NC_PIN_VALUE;
    obj->pin_dm = CYHAL_NC_PIN_VALUE;

    result = _cyhal_usb_dev_pin_setup(obj, dp, dm);

    if (CY_RSLT_SUCCESS == result)
    {
        /* Configure CLK_HF3 frequency and proper path */
        result = _cyhal_usb_dev_hf_clock_setup(obj);

        if (CY_RSLT_SUCCESS == result)
        {
            /* Configure CLK_PERI divider */
            result = _cyhal_usb_dev_peri_clock_setup(obj, clk);
        }
    }

    if (CY_RSLT_SUCCESS == result)
    {
        result = cyhal_hwmgr_reserve(&(obj->resource));

        obj->base = _CYHAL_USB_DEV_BASE_ADDRESSES[obj->resource.block_num];
    }

    if (CY_RSLT_SUCCESS == result)
    {
        /* Configure driver */
        static cy_stc_usbfs_dev_drv_config_t default_cfg =
        {
            .mode         = CY_USBFS_DEV_DRV_EP_MANAGEMENT_CPU,
            .dmaConfig    = { 0 },
            .epBuffer     = NULL,
            .epBufferSize = 0U,
            .intrLevelSel = _CYHAL_USB_DEV_IRQ_LVL_DEFAULT,
            .enableLpm    = false,
            .epAccess     = CY_USBFS_DEV_DRV_USE_8_BITS_DR
        };

        if (CY_USBFS_DEV_DRV_SUCCESS != Cy_USBFS_Dev_Drv_Init(obj->base,
                                                                &default_cfg,
                                                                &(obj->context)))
        {
            result = CYHAL_USB_DEV_RSLT_ERR;
        }
    }

    if (CY_RSLT_SUCCESS == result)
    {
        uint32_t instance = obj->resource.block_num;
        uint32_t cb_num;

        /* Register service callbacks */
        Cy_USBFS_Dev_Drv_RegisterServiceCallback(obj->base, CY_USB_DEV_BUS_RESET,
                                                 _cyhal_usb_dev_drv_event_cb_table[instance][CY_USB_DEV_BUS_RESET],
                                                 &(obj->context));
        Cy_USBFS_Dev_Drv_RegisterServiceCallback(obj->base, CY_USB_DEV_EP0_SETUP,
                                                 _cyhal_usb_dev_drv_event_cb_table[instance][CY_USB_DEV_EP0_SETUP],
                                                 &(obj->context));
        Cy_USBFS_Dev_Drv_RegisterServiceCallback(obj->base, CY_USB_DEV_EP0_IN,
                                                 _cyhal_usb_dev_drv_event_cb_table[instance][CY_USB_DEV_EP0_IN],
                                                 &(obj->context));
        Cy_USBFS_Dev_Drv_RegisterServiceCallback(obj->base, CY_USB_DEV_EP0_OUT,
                                                 _cyhal_usb_dev_drv_event_cb_table[instance][CY_USB_DEV_EP0_OUT],
                                                 &(obj->context));

        /* Register sof callback (it enables sof interrupt, so disable it after registration) */
        Cy_USBFS_Dev_Drv_RegisterSofCallback(obj->base, _cyhal_usb_dev_drv_sof_cb_table[instance],
                                             &(obj->context));
        cyhal_usb_dev_sof_enable(obj, false);

        obj->pm_callback.states = (cyhal_syspm_callback_state_t)(CYHAL_SYSPM_CB_CPU_DEEPSLEEP | CYHAL_SYSPM_CB_SYSTEM_HIBERNATE);
        obj->pm_callback.callback = &_cyhal_usb_dev_pm_callback;
        obj->pm_callback.args = (void *)obj;
        obj->pm_callback.next = NULL;
        obj->pm_callback.ignore_modes = (cyhal_syspm_callback_mode_t)(CYHAL_SYSPM_CHECK_FAIL | CYHAL_SYSPM_BEFORE_TRANSITION | CYHAL_SYSPM_AFTER_TRANSITION);
        _cyhal_syspm_register_peripheral_callback(&(obj->pm_callback));

        /* Register data endpoint handlers */
        for (cb_num = 0; cb_num < _CYHAL_USB_DEV_EP_EVENT_NUM; cb_num++)
        {
            Cy_USBFS_Dev_Drv_RegisterEndpointCallback(obj->base,
                                                      (cb_num + 1U),
                                                      _cyhal_usb_dev_drv_ep_cb_table[instance],
                                                      &(obj->context));
        }
    }

    if (CY_RSLT_SUCCESS != result)
    {
        _cyhal_usb_dev_free_resources(obj);
    }

    return result;
}

void cyhal_usb_dev_free(cyhal_usb_dev_t *obj)
{
    _cyhal_syspm_unregister_peripheral_callback(&(obj->pm_callback));
    cyhal_usb_dev_irq_enable(obj, false);
    cyhal_usb_dev_disconnect(obj);
    _cyhal_usb_dev_free_resources(obj);
}

void cyhal_usb_dev_connect(cyhal_usb_dev_t *obj)
{
    Cy_USBFS_Dev_Drv_Enable(obj->base, &(obj->context));
}

void cyhal_usb_dev_disconnect(cyhal_usb_dev_t *obj)
{
    Cy_USBFS_Dev_Drv_Disable(obj->base, &(obj->context));
}

void cyhal_usb_dev_suspend(cyhal_usb_dev_t *obj)
{
    Cy_USBFS_Dev_Drv_Suspend(obj->base, &(obj->context));
}

void cyhal_usb_dev_resume(cyhal_usb_dev_t *obj)
{
    Cy_USBFS_Dev_Drv_Resume(obj->base, &(obj->context));
}

void cyhal_usb_dev_set_configured(cyhal_usb_dev_t *obj)
{
    Cy_USBFS_Dev_Drv_ConfigDevice(obj->base, &(obj->context));
}

void cyhal_usb_dev_set_unconfigured(cyhal_usb_dev_t *obj)
{
    Cy_USBFS_Dev_Drv_UnConfigureDevice(obj->base, &(obj->context));
}

void cyhal_usb_dev_sof_enable(cyhal_usb_dev_t *obj, bool enable)
{
    uint32_t mask = Cy_USBFS_Dev_Drv_GetSieInterruptMask(obj->base);

    if (enable)
    {
        mask |= CY_USBFS_DEV_DRV_INTR_SIE_SOF;
        Cy_USBFS_Dev_Drv_ClearSieInterrupt(obj->base, CY_USBFS_DEV_DRV_INTR_SIE_SOF);
    }
    else
    {
        mask &= ~CY_USBFS_DEV_DRV_INTR_SIE_SOF;
    }

    Cy_USBFS_Dev_Drv_SetSieInterruptMask(obj->base, mask);
}

void cyhal_usb_dev_set_address(cyhal_usb_dev_t *obj, uint8_t address)
{
    Cy_USBFS_Dev_Drv_SetAddress(obj->base, address, &(obj->context));
}

uint32_t cyhal_usb_dev_ep0_get_max_packet(cyhal_usb_dev_t *obj)
{
    return Cy_USBFS_Dev_Drv_GetEp0MaxPacket(obj->base);
}

void cyhal_usb_dev_ep0_setup_read_result(cyhal_usb_dev_t *obj, uint8_t *buffer, uint32_t size)
{
    CY_UNUSED_PARAMETER(size);
    CY_ASSERT(_CYHAL_USB_DEV_IS_EP0_SIZE_VALID(size));

    Cy_USBFS_Dev_Drv_Ep0GetSetup(obj->base, buffer, &(obj->context));
}

void cyhal_usb_dev_ep0_read(cyhal_usb_dev_t *obj, uint8_t *buffer, uint32_t size)
{
    Cy_USBFS_Dev_Drv_Ep0Read(obj->base, buffer, size, &(obj->context));
}

uint32_t cyhal_usb_dev_ep0_read_result(cyhal_usb_dev_t *obj)
{
    return Cy_USBFS_Dev_Drv_Ep0ReadResult(obj->base, &(obj->context));
}

uint32_t cyhal_usb_dev_ep0_write(cyhal_usb_dev_t *obj, uint8_t *buffer, uint32_t size)
{
    return Cy_USBFS_Dev_Drv_Ep0Write(obj->base, buffer, size, &(obj->context));
}

void cyhal_usb_dev_ep0_stall(cyhal_usb_dev_t *obj)
{
    Cy_USBFS_Dev_Drv_Ep0Stall(obj->base);
}

cy_rslt_t cyhal_usb_dev_endpoint_add(cyhal_usb_dev_t *obj, bool alloc, bool enable,
                                     cyhal_usb_dev_ep_t endpoint, uint32_t max_packet, cyhal_usb_dev_ep_type_t type)
{
    cy_rslt_t result = CYHAL_USB_DEV_RSLT_ERR;
    cy_stc_usb_dev_ep_config_t ep_config;

    /* Set parameters to allocate endpoint buffer */
    ep_config.allocBuffer    = alloc;
    ep_config.bufferSize     = (uint16_t) max_packet;

    /* Set parameters to enable endpoint operation */
    ep_config.enableEndpoint = enable;
    ep_config.endpointAddr   = (uint8_t) endpoint;
    ep_config.attributes     = (uint8_t) type;
    ep_config.maxPacketSize  = (uint16_t) max_packet;

    if (CY_USBFS_DEV_DRV_SUCCESS ==  Cy_USBFS_Dev_Drv_AddEndpoint(obj->base,
                                                                  &ep_config,
                                                                  &(obj->context)))
    {
        result = CY_RSLT_SUCCESS;
    }

    return result;
}

cy_rslt_t cyhal_usb_dev_endpoint_remove(cyhal_usb_dev_t *obj, cyhal_usb_dev_ep_t endpoint)
{
    cy_rslt_t result = CYHAL_USB_DEV_RSLT_ERR;

    if (CY_USBFS_DEV_DRV_SUCCESS == Cy_USBFS_Dev_Drv_RemoveEndpoint(obj->base,
                                                                    endpoint,
                                                                    &(obj->context)))
    {
        result = CY_RSLT_SUCCESS;
    }

    return result;
}

cy_rslt_t cyhal_usb_dev_endpoint_stall(cyhal_usb_dev_t *obj, cyhal_usb_dev_ep_t endpoint)
{
    cy_rslt_t result = CYHAL_USB_DEV_RSLT_ERR;

    if (CY_USBFS_DEV_DRV_SUCCESS == Cy_USBFS_Dev_Drv_StallEndpoint(obj->base,
                                                                   CYHAL_USB_DEV_GET_EP_NUM(endpoint),
                                                                   &(obj->context)))
    {
        result = CY_RSLT_SUCCESS;
    }

    return result;
}

cy_rslt_t cyhal_usb_dev_endpoint_unstall(cyhal_usb_dev_t *obj, cyhal_usb_dev_ep_t endpoint)
{
    cy_rslt_t result = CYHAL_USB_DEV_RSLT_ERR;

    if (CY_USBFS_DEV_DRV_SUCCESS == Cy_USBFS_Dev_Drv_UnStallEndpoint(obj->base,
                                                                     CYHAL_USB_DEV_GET_EP_NUM(endpoint),
                                                                     &(obj->context)))
    {
        result = CY_RSLT_SUCCESS;
    }

    return result;
}

bool cyhal_usb_dev_endpoint_is_stalled(cyhal_usb_dev_t *obj, cyhal_usb_dev_ep_t endpoint)
{
    bool is_stalled = (CY_USB_DEV_EP_STALLED == Cy_USBFS_Dev_Drv_GetEndpointState(obj->base,
                                                                                  CYHAL_USB_DEV_GET_EP_NUM(endpoint),
                                                                                  &(obj->context)));

    return is_stalled;
}

cy_rslt_t cyhal_usb_dev_endpoint_read(cyhal_usb_dev_t *obj, cyhal_usb_dev_ep_t endpoint, uint8_t *data, uint32_t size)
{
    cy_rslt_t result = CYHAL_USB_DEV_RSLT_ERR;
    uint32_t  ep_num = CYHAL_USB_DEV_GET_EP_NUM(endpoint);

    /* Check if endpoint is read for a read operation */
    cy_en_usb_dev_ep_state_t ep_state;
    ep_state = Cy_USBFS_Dev_Drv_GetEndpointState(obj->base, ep_num, &(obj->context));
    if ((CY_USB_DEV_EP_IDLE == ep_state) || (CY_USB_DEV_EP_COMPLETED == ep_state))
    {
        /* Save pointer and size to use in cyhal_usb_dev_endpoint_read_result */
        obj->rd_data[CYHAL_USB_DEV_GET_EP_IDX(endpoint)] = data;
        obj->rd_size[CYHAL_USB_DEV_GET_EP_IDX(endpoint)] = size;

        Cy_USBFS_Dev_Drv_EnableOutEndpoint(obj->base, ep_num, &obj->context);

        result = CY_RSLT_SUCCESS;
    }

    return result;
}

cy_rslt_t cyhal_usb_dev_endpoint_read_result(cyhal_usb_dev_t *obj, cyhal_usb_dev_ep_t endpoint, uint32_t *act_size)
{
    cy_rslt_t result = CYHAL_USB_DEV_RSLT_ERR;
    uint32_t  ep_num = CYHAL_USB_DEV_GET_EP_NUM(endpoint);

    /* Check if endpoint is read for a read result operation */
    if (CY_USB_DEV_EP_COMPLETED == Cy_USBFS_Dev_Drv_GetEndpointState(obj->base, ep_num, &(obj->context)))
    {
        cy_en_usbfs_dev_drv_status_t drvStatus;
        drvStatus = Cy_USBFS_Dev_Drv_ReadOutEndpoint(obj->base,
                                                     ep_num,
                                                     obj->rd_data[CYHAL_USB_DEV_GET_EP_IDX(endpoint)],
                                                     obj->rd_size[CYHAL_USB_DEV_GET_EP_IDX(endpoint)],
                                                     act_size,
                                                     &(obj->context));

        if (drvStatus == CY_USBFS_DEV_DRV_SUCCESS)
        {
            result = CY_RSLT_SUCCESS;
        }
    }

    return result;
}

cy_rslt_t cyhal_usb_dev_endpoint_write(cyhal_usb_dev_t *obj, cyhal_usb_dev_ep_t endpoint, uint8_t const *data, uint32_t size)
{
    cy_rslt_t result = CYHAL_USB_DEV_RSLT_ERR;
    uint32_t  ep_num = CYHAL_USB_DEV_GET_EP_NUM(endpoint);

    /* Check if endpoint is read for a write operation */
    cy_en_usb_dev_ep_state_t ep_state;
    ep_state = Cy_USBFS_Dev_Drv_GetEndpointState(obj->base, ep_num, &(obj->context));
    if ((CY_USB_DEV_EP_IDLE == ep_state) || (CY_USB_DEV_EP_COMPLETED == ep_state))
    {
        cy_en_usbfs_dev_drv_status_t drvStatus;
        drvStatus = Cy_USBFS_Dev_Drv_LoadInEndpoint(obj->base,
                                                    ep_num,
                                                    data,
                                                    size,
                                                    &(obj->context));

        if (drvStatus == CY_USBFS_DEV_DRV_SUCCESS)
        {
            result = CY_RSLT_SUCCESS;
        }
    }

    return result;
}

cy_rslt_t cyhal_usb_dev_endpoint_abort(cyhal_usb_dev_t *obj, cyhal_usb_dev_ep_t endpoint)
{
    cy_rslt_t result = CYHAL_USB_DEV_RSLT_ERR;
    uint32_t  ep_num = CYHAL_USB_DEV_GET_EP_NUM(endpoint);

    if (CY_USBFS_DEV_DRV_SUCCESS == Cy_USBFS_Dev_Drv_Abort(obj->base, ep_num, &(obj->context)))
    {
        result = CY_RSLT_SUCCESS;
    }

    return result;
}

void cyhal_usb_dev_irq_enable(cyhal_usb_dev_t *obj, bool enable)
{
    uint32_t instance = (uint32_t)obj->resource.block_num;
    if (enable)
    {
        NVIC_ClearPendingIRQ(_CYHAL_USB_DEV_IRQ_N[instance]);
        NVIC_EnableIRQ(_CYHAL_USB_DEV_IRQ_N[instance]);
    }
    else
    {
        NVIC_DisableIRQ(_CYHAL_USB_DEV_IRQ_N[instance]);
    }
}

void cyhal_usb_dev_process_irq(cyhal_usb_dev_t *obj)
{
    Cy_USBFS_Dev_Drv_Interrupt(obj->base,
                               Cy_USBFS_Dev_Drv_GetInterruptCauseLo(obj->base),
                               &(obj->context));
}

cy_rslt_t cyhal_usb_dev_register_irq_callback(cyhal_usb_dev_t *obj, cyhal_usb_dev_irq_callback_t callback)
{
    cy_rslt_t result = CYHAL_USB_DEV_RSLT_ERR;
    cy_stc_sysint_t config;
    uint32_t instance = (uint32_t)obj->resource.block_num;

    /* Configure NVIC parameters */
    config.intrPriority = _CYHAL_USB_DEV_DEFAULT_IRQ_PRIORITY;
    config.intrSrc = _CYHAL_USB_DEV_IRQ_N[instance];

    /* Setup interrupt in NVIC to trigger the callback */
    if (CY_SYSINT_SUCCESS == Cy_SysInt_Init(&config, (cy_israddress)callback))
    {
        result = CY_RSLT_SUCCESS;
    }

    return result;
}

void cyhal_usb_dev_register_event_callback(cyhal_usb_dev_t *obj, cyhal_usb_dev_event_t event, cyhal_usb_dev_event_callback_t callback)
{
    CY_ASSERT(_CYHAL_USB_DEV_IS_EVENT_VALID(event));
    uint32_t instance = (uint32_t)obj->resource.block_num;

    _cyhal_usb_dev_event_callback_table[instance][event] = callback;
}

void cyhal_usb_dev_register_sof_callback( cyhal_usb_dev_t *obj, cyhal_usb_dev_sof_callback_t callback)
{
    uint32_t instance = (uint32_t)obj->resource.block_num;
    _cyhal_usb_dev_sof_user_callback[instance] = callback;
}

void cyhal_usb_dev_register_endpoint_callback(cyhal_usb_dev_t *obj, cyhal_usb_dev_ep_t endpoint, cyhal_usb_dev_endpoint_callback_t callback)
{
    uint32_t endpoint_num = CYHAL_USB_DEV_GET_EP_NUM(endpoint);
    uint32_t instance = (uint32_t)obj->resource.block_num;

    CY_ASSERT(_CYHAL_USB_DEV_IS_EP_NUM_VALID(endpoint_num));

    _cyhal_usb_dev_ep_handler_table[instance][CYHAL_USB_DEV_GET_EP_IDX(endpoint_num)]= callback;
}

#if defined(__cplusplus)
}
#endif

#endif /* CY_IP_MXUSBFS) */