/**
 *******************************************************************************
 * @file  hc32_ll_usb.c
 * @brief USB core driver.
 @verbatim
   Change Logs:
   Date             Author          Notes
   2022-03-31       CDT             First version
   2022-06-30       CDT             Add USB core ID select function
   2022-10-31       CDT             Add USB DMA function
   2023-09-30       CDT             Fix bug for function usb_clearepstall()
                                    Modify typo
 @endverbatim
 *******************************************************************************
 * Copyright (C) 2022-2023, Xiaohua Semiconductor Co., Ltd. All rights reserved.
 *
 * This software component is licensed by XHSC under BSD 3-Clause license
 * (the "License"); You may not use this file except in compliance with the
 * License. You may obtain a copy of the License at:
 *                    opensource.org/licenses/BSD-3-Clause
 *
 *******************************************************************************
 */

/*******************************************************************************
 * Include files
 ******************************************************************************/
#include "hc32_ll_usb.h"
#include "hdl_clk.h"

/**
 * @addtogroup LL_Driver
 * @{
 */

/**
 * @defgroup LL_USB USB
 * @brief USB Driver Library
 * @{
 */

#if (LL_USB_ENABLE == DDL_ON)

/*******************************************************************************
 * Local type definitions ('typedef')
 ******************************************************************************/

/*******************************************************************************
 * Local pre-processor symbols/macros ('#define')
 ******************************************************************************/
/*  Parameters check */
#define IS_USB_CORE_ID(x)                                                      \
(   ((x) == USBFS_CORE_ID))

#define IS_USB_PHY_TYPE(x)                                                     \
(   ((x) == USBHS_PHY_EMBED))

/*******************************************************************************
 * Global variable definitions (declared in header file with 'extern')
 ******************************************************************************/

/*******************************************************************************
 * Local function prototypes ('static')
 ******************************************************************************/

/*******************************************************************************
 * Local variable definitions ('static')
 ******************************************************************************/

/*******************************************************************************
 * Function implementation - global ('extern') and local ('static')
 ******************************************************************************/
/**
 * @defgroup USB_Global_Functions USB Global Functions
 * @{
 */

/**
 * @brief  core software reset
 * @param  [in] USBx        usb instance
 * @retval None
 */
void usb_coresoftrst(LL_USB_TypeDef *USBx)
{
    __IO uint8_t u8Status = USB_OK;;
    __IO uint32_t u32grstctl = 0UL;
    __IO uint32_t u32Count = 0UL;

    /* Wait for AHB master to be idle. */
    do {
        hdl_delay_us(1UL);
        u32grstctl = READ_REG32(USBx->GREGS->GRSTCTL);
        if (++u32Count > 100000UL) {
            u8Status = USB_ERROR;
        }
    } while (0UL == (u32grstctl & USBFS_GRSTCTL_AHBIDL));

    if (USB_OK == u8Status) {
        /* Write the Core Soft Reset bit to reset the USB core */
        u32Count = 0UL;
        u32grstctl |= USBFS_GRSTCTL_CSRST;
        WRITE_REG32(USBx->GREGS->GRSTCTL, u32grstctl);

        /* Wait for the reset finishing */
        do {
            u32grstctl = READ_REG32(USBx->GREGS->GRSTCTL);
            if (u32Count > 100000UL) {
                break;
            }
            u32Count++;
            hdl_delay_us(1UL);
        } while (0UL != (u32grstctl & USBFS_GRSTCTL_CSRST));
        /* Wait for at least 3 PHY clocks after the core resets */
        hdl_delay_us(3UL);
    }
}

/**
 * @brief  Writes a packet whose byte number is len into the Tx FIFO associated
 *         with the EP
 * @param  [in] USBx        usb instance
 * @param  [in] src         source pointer used to hold the transmitted data
 * @param  [in] ch_ep_num   end point index
 * @param  [in] len         length in bytes
 * @param  [in] u8DmaEn     USB DMA status
 * @retval None
 */
void usb_wrpkt(LL_USB_TypeDef *USBx, uint8_t *src, uint8_t ch_ep_num, uint16_t len, uint8_t u8DmaEn)
{
    __IO uint32_t u32pAddr;
    __IO uint32_t *fifo;
    uint32_t u32Count32b;
    uint32_t u32Tmp;
    if (u8DmaEn == 0U) {
        u32Count32b = (len + 3UL);
        u32Count32b = u32Count32b >> 2U;
        fifo = USBx->DFIFO[ch_ep_num];
        u32Tmp = 0UL;
        while (u32Tmp < u32Count32b) {
            WRITE_REG32(*fifo, *((uint32_t *)src));
            u32pAddr = (uint32_t)src;
            src = (uint8_t *)(u32pAddr + 4U);
            u32Tmp++;
        }
    }
}

/**
 * @brief  Reads a packet whose byte number is len from the Rx FIFO
 * @param  [in] USBx        usb instance
 * @param  [in] dest        destination pointer that point to the received data
 * @param  [in] len         number of bytes
 * @retval None
 */
void usb_rdpkt(LL_USB_TypeDef *USBx, uint8_t *dest, uint16_t len)
{
    uint32_t u32Tmp;
    __IO uint32_t u32Count32b;
    __IO uint32_t u32pAddr;

    __IO uint32_t *fifo = USBx->DFIFO[0];
    u32Count32b = (len + 3UL);
    u32Count32b = u32Count32b >> 2U;
    u32pAddr = 0UL;
    u32Tmp = 0UL;
    while (u32Tmp < u32Count32b) {
        *(uint32_t *)dest = READ_REG32(*fifo);
        u32pAddr = (uint32_t)dest;
        dest = (uint8_t *)(u32pAddr + 4U);
        u32Tmp++;
    }
}

/**
 * @brief  Initialize the addresses of the core registers.
 * @param  [in] USBx                usb instance
 * @param  [in] pstcPortIdentify    usb core and phy select
 * @param  [in] basic_cfgs          usb core basic cfgs
 * @retval None
 */
void usb_setregaddr(LL_USB_TypeDef *USBx, stc_usb_port_identify *pstcPortIdentify, USB_CORE_BASIC_CFGS *basic_cfgs)
{
    uint32_t u32Tmp = 0UL;
    uint32_t u32baseAddr = CM_USBFS_BASE;
    DDL_ASSERT(IS_USB_CORE_ID(pstcPortIdentify->u8CoreID));
#if defined (USB_INTERNAL_DMA_ENABLED)
    basic_cfgs->dmaen = 1U;
#else
    basic_cfgs->dmaen = 0U;
#endif
    /* initialize device cfg following its address */
    basic_cfgs->host_chnum = USB_MAX_CH_NUM;
    basic_cfgs->dev_epnum = USB_MAX_EP_NUM;
    basic_cfgs->core_type = pstcPortIdentify->u8CoreID;
    basic_cfgs->phy_type = USBHS_PHY_EMBED;
    if (USBFS_CORE_ID == pstcPortIdentify->u8CoreID) {
#ifdef USB_FS_MODE
        u32baseAddr = CM_USBFS_BASE;
#endif
    } else {
#ifdef USB_HS_MODE
        u32baseAddr = CM_USBHS_BASE;
#endif
    }

    USBx->GREGS = (USB_CORE_GREGS *)(u32baseAddr + 0UL);
    USBx->DREGS = (USB_CORE_DREGS *)(u32baseAddr + 0x800UL);

    while (u32Tmp < basic_cfgs->dev_epnum) {
        USBx->INEP_REGS[u32Tmp]  = (USB_CORE_INEPREGS *)(u32baseAddr + 0x900UL + (u32Tmp * 0x20UL));
        USBx->OUTEP_REGS[u32Tmp] = (USB_CORE_OUTEPREGS *)(u32baseAddr + 0xb00UL + (u32Tmp * 0x20UL));
        u32Tmp++;
    }
    u32Tmp = 0UL;
    while (u32Tmp < basic_cfgs->dev_epnum) {
        USBx->DFIFO[u32Tmp] = (uint32_t *)(u32baseAddr + 0x1000UL + (u32Tmp * 0x1000UL));
        u32Tmp++;
    }
    USBx->GCCTL = (uint32_t *)(u32baseAddr + 0xe00UL);
#ifdef USE_HOST_MODE     /* if the application mode is host */
    USBx->HREGS = (USB_CORE_HREGS *)(u32baseAddr + 0x400UL);
    USBx->HPRT = (uint32_t *)(u32baseAddr + 0x440UL);
    u32Tmp = 0UL;
    while (u32Tmp < basic_cfgs->host_chnum) {
        USBx->HC_REGS[u32Tmp] = (USB_CORE_HC_REGS *)(u32baseAddr + 0x500UL + (u32Tmp * 0x20UL));
        u32Tmp++;
    }
#endif  /* USE_HOST_MODE */
}

/**
 * @brief  Initializes the USB controller registers and prepares the core
 *         device mode or host mode operation.
 * @param  [in] USBx        usb instance
 * @param  [in] basic_cfgs  usb core basic cfgs
 * @retval None
 */
void usb_initusbcore(LL_USB_TypeDef *USBx, USB_CORE_BASIC_CFGS *basic_cfgs)
{
    /* reset the core through core soft reset */
    usb_coresoftrst(USBx);

    /* Select PHY for USB core*/
    usb_PhySelect(USBx, basic_cfgs->phy_type);
    /* reset the core through core soft reset */
    usb_coresoftrst(USBx);
    hdl_delay_ms(20UL);
    if (basic_cfgs->dmaen == 1U) {
        /* burst length/type(HBstLen) 64-words x32-bit,  core operates in a DMA mode*/
        usb_BurstLenConfig(USBx, 5U);
        usb_DmaCmd(USBx, 1U);
    }
}

/**
 * @brief  Flush a Tx FIFO whose index is num
 * @param  [in] USBx        usb instance
 * @param  [in] num         txFIFO index
 * @retval None
 */
void usb_txfifoflush(LL_USB_TypeDef *USBx, uint32_t num)
{
    __IO uint32_t u32grstctl;
    __IO uint32_t u32Tmp = 0UL;

    u32grstctl = USBFS_GRSTCTL_TXFFLSH | ((num & 0x1FUL) << USBFS_GRSTCTL_TXFNUM_POS);
    /* set the TxFIFO Flush bit, set TxFIFO Number */
    WRITE_REG32(USBx->GREGS->GRSTCTL, u32grstctl);

    /* wait for the finishing of txFIFO flushing */
    do {
        u32grstctl = READ_REG32(USBx->GREGS->GRSTCTL);
        if (u32Tmp <= 200000UL) {
            u32Tmp++;
        } else {
            break;
        }
        hdl_delay_us(1UL);
    } while (0UL != (u32grstctl & USBFS_GRSTCTL_TXFFLSH));
    /* Wait for at least 3 PHY clocks after the txfifo has been flushed */
    hdl_delay_us(3UL);
}

/**
 * @brief  Flush the whole rxFIFO
 * @param  [in] USBx        usb instance
 * @retval None
 */
void usb_rxfifoflush(LL_USB_TypeDef *USBx)
{
    __IO uint32_t u32grstctl;
    __IO uint32_t u32Tmp = 0UL;

    u32grstctl = USBFS_GRSTCTL_RXFFLSH;    /* set the RxFIFO Flush bit */
    WRITE_REG32(USBx->GREGS->GRSTCTL, u32grstctl);
    /* wait for the finishing of rxFIFO flushing */
    do {
        u32grstctl = READ_REG32(USBx->GREGS->GRSTCTL);
        if (u32Tmp <= 200000UL) {
            u32Tmp++;
        } else {
            break;
        }
        hdl_delay_us(1UL);
    } while (0UL != (u32grstctl & USBFS_GRSTCTL_RXFFLSH));
    /* Wait for at least 3 PHY clocks after the rxfifo has been flushed */
    hdl_delay_us(3UL);
}

/**
 * @brief  set the core to be host mode or device mode through the second
 *         input parameter.
 * @param  [in] USBx        usb instance
 * @param  [in] mode        mode of HOST_MODE or DEVICE_MODE that the core would be
 * @retval None
 */
void usb_modeset(LL_USB_TypeDef *USBx, uint8_t mode)
{
    if (mode == HOST_MODE) {
        MODIFY_REG32(USBx->GREGS->GUSBCFG, USBFS_GUSBCFG_FHMOD | USBFS_GUSBCFG_FDMOD, USBFS_GUSBCFG_FHMOD);
    } else {
        MODIFY_REG32(USBx->GREGS->GUSBCFG, USBFS_GUSBCFG_FHMOD | USBFS_GUSBCFG_FDMOD, USBFS_GUSBCFG_FDMOD);
    }
    /* wate for the change to take effect */
    hdl_delay_ms(50UL);
}

#ifdef USE_DEVICE_MODE

/**
 * @brief  initializes the initial status of all endpoints of the device to be
 *         disable.
 * @param  [in] USBx        usb instance
 * @param  [in] u8EpNum     EP number
 * @retval None
 */
void usb_devepdis(LL_USB_TypeDef *USBx, uint8_t u8EpNum)
{
    uint8_t u8Tmp = 0U;

    while (u8Tmp < u8EpNum) {
        if (0UL != READ_REG32_BIT(USBx->INEP_REGS[u8Tmp]->DIEPCTL, USBFS_DIEPCTL_EPENA)) {
            WRITE_REG32(USBx->INEP_REGS[u8Tmp]->DIEPCTL, USBFS_DIEPCTL_EPDIS | USBFS_DIEPCTL_SNAK);
        } else {
            WRITE_REG32(USBx->INEP_REGS[u8Tmp]->DIEPCTL, 0UL);
        }
        WRITE_REG32(USBx->INEP_REGS[u8Tmp]->DIEPTSIZ, 0UL);
        WRITE_REG32(USBx->INEP_REGS[u8Tmp]->DIEPINT, 0xFFUL);
        u8Tmp++;
    }

    u8Tmp = 0U;
    while (u8Tmp < u8EpNum) {
        if (0UL != READ_REG32_BIT(USBx->OUTEP_REGS[u8Tmp]->DOEPCTL, USBFS_DOEPCTL_EPENA)) {
            WRITE_REG32(USBx->OUTEP_REGS[u8Tmp]->DOEPCTL, USBFS_DOEPCTL_EPDIS | USBFS_DOEPCTL_SNAK);
        } else {
            WRITE_REG32(USBx->OUTEP_REGS[u8Tmp]->DOEPCTL, 0UL);
        }
        WRITE_REG32(USBx->OUTEP_REGS[u8Tmp]->DOEPTSIZ, 0UL);
        WRITE_REG32(USBx->OUTEP_REGS[u8Tmp]->DOEPINT, 0xFFUL);
        u8Tmp++;
    }
}

#ifdef USB_FS_MODE
static void usb_DevFSFifoConfig(LL_USB_TypeDef *USBx)
{
    uint32_t u32StardAddr;

    WRITE_REG32(USBx->GREGS->GRXFSIZ, RX_FIFO_FS_SIZE);
    /* set txFIFO and rxFIFO size of EP0 */
    u32StardAddr = RX_FIFO_FS_SIZE;
    WRITE_REG32(USBx->GREGS->HNPTXFSIZ,
                (RX_FIFO_FS_SIZE << USBFS_HNPTXFSIZ_NPTXFSA_POS) | (TX0_FIFO_FS_SIZE << USBFS_HNPTXFSIZ_NPTXFD_POS));
    /* set txFIFO size of EP1 */
    u32StardAddr += TX0_FIFO_FS_SIZE;
    WRITE_REG32(USBx->GREGS->DIEPTXF[0],
                (u32StardAddr << USBFS_DIEPTXF_INEPTXSA_POS) | (TX1_FIFO_FS_SIZE << USBFS_DIEPTXF_INEPTXFD_POS));
    /* set txFIFO size of EP2 */
    u32StardAddr += TX1_FIFO_FS_SIZE;
    WRITE_REG32(USBx->GREGS->DIEPTXF[1],
                (u32StardAddr << USBFS_DIEPTXF_INEPTXSA_POS) | (TX2_FIFO_FS_SIZE << USBFS_DIEPTXF_INEPTXFD_POS));
    /* set txFIFO size of EP3 */
    u32StardAddr += TX2_FIFO_FS_SIZE;
    WRITE_REG32(USBx->GREGS->DIEPTXF[2],
                (u32StardAddr << USBFS_DIEPTXF_INEPTXSA_POS) | (TX3_FIFO_FS_SIZE << USBFS_DIEPTXF_INEPTXFD_POS));
    /* set txFIFO size of EP4 */
    u32StardAddr += TX3_FIFO_FS_SIZE;
    WRITE_REG32(USBx->GREGS->DIEPTXF[3],
                (u32StardAddr << USBFS_DIEPTXF_INEPTXSA_POS) | (TX4_FIFO_FS_SIZE << USBFS_DIEPTXF_INEPTXFD_POS));
    /* set txFIFO size of EP5 */
    u32StardAddr += TX4_FIFO_FS_SIZE;
    WRITE_REG32(USBx->GREGS->DIEPTXF[4],
                (u32StardAddr << USBFS_DIEPTXF_INEPTXSA_POS) | (TX5_FIFO_FS_SIZE << USBFS_DIEPTXF_INEPTXFD_POS));
}
#endif

#ifdef USB_HS_MODE
static void usb_DevHSFifoConfig(LL_USB_TypeDef *USBx)
{
    uint32_t u32StardAddr;

    WRITE_REG32(USBx->GREGS->GRXFSIZ, RX_FIFO_HS_SIZE);
    /* set txFIFO and rxFIFO size of EP0 */
    u32StardAddr = RX_FIFO_HS_SIZE;
    WRITE_REG32(USBx->GREGS->HNPTXFSIZ,
                (RX_FIFO_HS_SIZE << USBFS_HNPTXFSIZ_NPTXFSA_POS) | (TX0_FIFO_HS_SIZE << USBFS_HNPTXFSIZ_NPTXFD_POS));
    /* set txFIFO size of EP1 */
    u32StardAddr += TX0_FIFO_HS_SIZE;
    WRITE_REG32(USBx->GREGS->DIEPTXF[0],
                (u32StardAddr << USBFS_DIEPTXF_INEPTXSA_POS) | (TX1_FIFO_HS_SIZE << USBFS_DIEPTXF_INEPTXFD_POS));
    /* set txFIFO size of EP2 */
    u32StardAddr += TX1_FIFO_HS_SIZE;
    WRITE_REG32(USBx->GREGS->DIEPTXF[1],
                (u32StardAddr << USBFS_DIEPTXF_INEPTXSA_POS) | (TX2_FIFO_HS_SIZE << USBFS_DIEPTXF_INEPTXFD_POS));
    /* set txFIFO size of EP3 */
    u32StardAddr += TX2_FIFO_HS_SIZE;
    WRITE_REG32(USBx->GREGS->DIEPTXF[2],
                (u32StardAddr << USBFS_DIEPTXF_INEPTXSA_POS) | (TX3_FIFO_HS_SIZE << USBFS_DIEPTXF_INEPTXFD_POS));
    /* set txFIFO size of EP4 */
    u32StardAddr += TX3_FIFO_HS_SIZE;
    WRITE_REG32(USBx->GREGS->DIEPTXF[3],
                (u32StardAddr << USBFS_DIEPTXF_INEPTXSA_POS) | (TX4_FIFO_HS_SIZE << USBFS_DIEPTXF_INEPTXFD_POS));
    /* set txFIFO size of EP5 */
    u32StardAddr += TX4_FIFO_HS_SIZE;
    WRITE_REG32(USBx->GREGS->DIEPTXF[4],
                (u32StardAddr << USBFS_DIEPTXF_INEPTXSA_POS) | (TX5_FIFO_HS_SIZE << USBFS_DIEPTXF_INEPTXFD_POS));
    /* set txFIFO size of EP6 */
    u32StardAddr += TX5_FIFO_HS_SIZE;
    WRITE_REG32(USBx->GREGS->DIEPTXF[5],
                (u32StardAddr << USBFS_DIEPTXF_INEPTXSA_POS) | (TX6_FIFO_HS_SIZE << USBFS_DIEPTXF_INEPTXFD_POS));
    /* set txFIFO size of EP7 */
    u32StardAddr += TX6_FIFO_HS_SIZE;
    WRITE_REG32(USBx->GREGS->DIEPTXF[6],
                (u32StardAddr << USBFS_DIEPTXF_INEPTXSA_POS) | (TX7_FIFO_HS_SIZE << USBFS_DIEPTXF_INEPTXFD_POS));
    /* set txFIFO size of EP8 */
    u32StardAddr += TX7_FIFO_HS_SIZE;
    WRITE_REG32(USBx->GREGS->DIEPTXF[7],
                (u32StardAddr << USBFS_DIEPTXF_INEPTXSA_POS) | (TX8_FIFO_HS_SIZE << USBFS_DIEPTXF_INEPTXFD_POS));
    /* set txFIFO size of EP9 */
    u32StardAddr += TX8_FIFO_HS_SIZE;
    WRITE_REG32(USBx->GREGS->DIEPTXF[8],
                (u32StardAddr << USBFS_DIEPTXF_INEPTXSA_POS) | (TX9_FIFO_HS_SIZE << USBFS_DIEPTXF_INEPTXFD_POS));
    /* set txFIFO size of EP10 */
    u32StardAddr += TX9_FIFO_HS_SIZE;
    WRITE_REG32(USBx->GREGS->DIEPTXF[9],
                (u32StardAddr << USBFS_DIEPTXF_INEPTXSA_POS) | (TX10_FIFO_HS_SIZE << USBFS_DIEPTXF_INEPTXFD_POS));
    /* set txFIFO size of EP11 */
    u32StardAddr += TX10_FIFO_HS_SIZE;
    WRITE_REG32(USBx->GREGS->DIEPTXF[10],
                (u32StardAddr << USBFS_DIEPTXF_INEPTXSA_POS) | (TX11_FIFO_HS_SIZE << USBFS_DIEPTXF_INEPTXFD_POS));
    /* set txFIFO size of EP12 */
    u32StardAddr += TX11_FIFO_HS_SIZE;
    WRITE_REG32(USBx->GREGS->DIEPTXF[11],
                (u32StardAddr << USBFS_DIEPTXF_INEPTXSA_POS) | (TX12_FIFO_HS_SIZE << USBFS_DIEPTXF_INEPTXFD_POS));
    /* set txFIFO size of EP13 */
    u32StardAddr += TX12_FIFO_HS_SIZE;
    WRITE_REG32(USBx->GREGS->DIEPTXF[12],
                (u32StardAddr << USBFS_DIEPTXF_INEPTXSA_POS) | (TX13_FIFO_HS_SIZE << USBFS_DIEPTXF_INEPTXFD_POS));
    /* set txFIFO size of EP14 */
    u32StardAddr += TX13_FIFO_HS_SIZE;
    WRITE_REG32(USBx->GREGS->DIEPTXF[13],
                (u32StardAddr << USBFS_DIEPTXF_INEPTXSA_POS) | (TX14_FIFO_HS_SIZE << USBFS_DIEPTXF_INEPTXFD_POS));
    /* set txFIFO size of EP15 */
    u32StardAddr += TX14_FIFO_HS_SIZE;
    WRITE_REG32(USBx->GREGS->DIEPTXF[14],
                (u32StardAddr << USBFS_DIEPTXF_INEPTXSA_POS) | (TX15_FIFO_HS_SIZE << USBFS_DIEPTXF_INEPTXFD_POS));
}
#endif

/**
 * @brief  initializes the USB controller, include the size of txFIFO, rxFIFO
 *         status of endpoints, interrupt register etc. Details are shown as
 *         follows.
 * @param  [in] USBx        usb instance
 * @param  [in] basic_cfgs  usb core basic cfgs
 * @retval None
 */
void usb_devmodeinit(LL_USB_TypeDef *USBx, USB_CORE_BASIC_CFGS *basic_cfgs)
{
    usb_FrameIntervalConfig(USBx, USB_FRAME_INTERVAL_80);
    usb_DevPhySelect(USBx, basic_cfgs->phy_type);

    if (basic_cfgs->core_type == 0U) {
#ifdef USB_FS_MODE
        usb_DevFSFifoConfig(USBx);
#endif
    } else {
#ifdef USB_HS_MODE
        usb_DevHSFifoConfig(USBx);
#endif
    }

    usb_clrandmskepint(USBx);
    usb_devepdis(USBx, basic_cfgs->dev_epnum);
    usb_coreconn(USBx);
    usb_devinten(USBx, basic_cfgs->dmaen);
}

/**
 * @brief  Enable the interrupt setting when in device mode.
 * @param  [in] USBx        usb instance
 * @param  [in] u8DmaEn     USB DMA status
 * @retval None
 */
void usb_devinten(LL_USB_TypeDef *USBx, uint8_t u8DmaEn)
{
    uint32_t  u32gintmskTmp = 0UL;

    WRITE_REG32(USBx->GREGS->GINTMSK, 0UL);
    WRITE_REG32(USBx->GREGS->GINTSTS, 0xBFFFFFFFUL);
    /* Enable the normal interrupt setting */
    usb_normalinten(USBx);
    if (u8DmaEn == 0U) {
        u32gintmskTmp |= USBFS_GINTMSK_RXFNEM;
    }
    /* Enable interrupts bits corresponding to the Device mode */
    u32gintmskTmp |= (USBFS_GINTMSK_USBSUSPM | USBFS_GINTMSK_USBRSTM | USBFS_GINTMSK_ENUMDNEM
                      | USBFS_GINTMSK_IEPIM | USBFS_GINTMSK_OEPIM | USBFS_GINTMSK_SOFM
                      | USBFS_GINTMSK_IISOIXFRM | USBFS_GINTMSK_IPXFRM_INCOMPISOOUTM);
#ifdef VBUS_SENSING_ENABLED
    u32gintmskTmp |= USBFS_GINTMSK_VBUSVIM;
#endif
    SET_REG32_BIT(USBx->GREGS->GINTMSK, u32gintmskTmp);
}

/**
 * @brief  get the working status of endpoint.
 * @param  [in] USBx        usb instance
 * @param  [in] ep          endpoint instance
 * @retval current status of the endpoint
 */
uint32_t usb_epstatusget(LL_USB_TypeDef *USBx, USB_DEV_EP *ep)
{
    __IO uint32_t u32Status = 0UL;
    uint32_t u32dxepctl;

    if (ep->ep_dir == 1U) {
        u32dxepctl = READ_REG32(USBx->INEP_REGS[ep->epidx]->DIEPCTL);

        if (0UL != (u32dxepctl & USBFS_DIEPCTL_STALL)) {
            u32Status = USB_EP_TX_STALL;
        } else if (0UL != (u32dxepctl & USBFS_DIEPCTL_NAKSTS)) {
            u32Status = USB_EP_TX_NAK;
        } else {
            u32Status = USB_EP_TX_VALID;
        }
    } else {
        u32dxepctl = READ_REG32(USBx->OUTEP_REGS[ep->epidx]->DOEPCTL);
        if (0UL != (u32dxepctl & USBFS_DOEPCTL_STALL)) {
            u32Status = USB_EP_RX_STALL;
        } else if (0UL != (u32dxepctl & USBFS_DOEPCTL_NAKSTS)) {
            u32Status = USB_EP_RX_NAK;
        } else {
            u32Status = USB_EP_RX_VALID;
        }
    }

    return u32Status;
}

/**
 * @brief  set the working status of endpoint.
 * @param  [in] USBx        usb instance
 * @param  [in] ep          endpoint instance
 * @param  [in] Status      new Status that the endpoint would be
 * @retval None
 */
void usb_epstatusset(LL_USB_TypeDef *USBx, USB_DEV_EP *ep, uint32_t Status)
{
    uint32_t u32dxepctl;
    uint8_t u8RetFlag = 0U;

    if (ep->ep_dir == 1U) {
        u32dxepctl = READ_REG32(USBx->INEP_REGS[ep->epidx]->DIEPCTL);

        switch (Status) {
            case USB_EP_TX_STALL:
                usb_setepstall(USBx, ep);
                u8RetFlag = 1U;
                break;
            case USB_EP_TX_NAK:
                u32dxepctl |= USBFS_DIEPCTL_SNAK;
                break;
            case USB_EP_TX_VALID:
                if (0UL != (u32dxepctl & USBFS_DIEPCTL_STALL)) {
                    ep->datax_pid = 0U;
                    usb_clearepstall(USBx, ep);
                    u8RetFlag = 1U;
                }
                u32dxepctl |= (USBFS_DIEPCTL_CNAK | USBFS_DIEPCTL_USBAEP | USBFS_DIEPCTL_EPENA);
                break;
            case USB_EP_TX_DIS:
                u32dxepctl &= (~USBFS_DIEPCTL_USBAEP);
                break;
            default:
                break;
        }
        /* Write register */
        if (1U != u8RetFlag) {
            WRITE_REG32(USBx->INEP_REGS[ep->epidx]->DIEPCTL, u32dxepctl);
        }
    } else {
        u32dxepctl = READ_REG32(USBx->OUTEP_REGS[ep->epidx]->DOEPCTL);

        switch (Status) {
            case USB_EP_RX_STALL:
                u32dxepctl |= USBFS_DOEPCTL_STALL;
                break;
            case USB_EP_RX_NAK:
                u32dxepctl |= USBFS_DOEPCTL_SNAK;
                break;
            case USB_EP_RX_VALID:
                if (0UL != (u32dxepctl & USBFS_DOEPCTL_STALL)) {
                    ep->datax_pid = 0U;
                    usb_clearepstall(USBx, ep);
                    u8RetFlag = 1U;
                }
                u32dxepctl |= (USBFS_DOEPCTL_CNAK | USBFS_DOEPCTL_USBAEP | USBFS_DOEPCTL_EPENA);
                break;
            case USB_EP_RX_DIS:
                u32dxepctl &= (~USBFS_DOEPCTL_USBAEP);
                break;
            default:
                break;
        }
        /* Write register */
        if (1U != u8RetFlag) {
            WRITE_REG32(USBx->OUTEP_REGS[ep->epidx]->DOEPCTL, u32dxepctl);
        }
    }
}

/**
 * @brief  enable the EP0 to be actiove
 * @param  [in] USBx        usb instance
 * @retval None
 */
void  usb_ep0activate(LL_USB_TypeDef *USBx)
{
    uint32_t u32EnumSpeed;
    uint32_t u32DiepctlTmp;

    u32EnumSpeed = READ_REG32(USBx->DREGS->DSTS) & USBFS_DSTS_ENUMSPD;
    u32DiepctlTmp = READ_REG32(USBx->INEP_REGS[0]->DIEPCTL);
    /* Set the MPS of the DIEPCTL0 based on the enumeration speed */
    if ((DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ == u32EnumSpeed)
        || (DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ == u32EnumSpeed)
        || (DSTS_ENUMSPD_FS_PHY_48MHZ == u32EnumSpeed)) {
        u32DiepctlTmp &= (~USBFS_DIEPCTL_MPSIZ);
    } else if (DSTS_ENUMSPD_LS_PHY_6MHZ == u32EnumSpeed) {
        u32DiepctlTmp &= (~USBFS_DIEPCTL_MPSIZ);
        u32DiepctlTmp |= (3UL << USBFS_DIEPCTL_MPSIZ_POS);
    } else {
        ;
    }
    WRITE_REG32(USBx->INEP_REGS[0]->DIEPCTL, u32DiepctlTmp);
    SET_REG32_BIT(USBx->DREGS->DCTL, USBFS_DCTL_CGINAK);
}

/**
 * @brief  enable an EP to be active
 * @param  [in] USBx        usb instance
 * @param  [in] ep          endpoint instance
 * @retval None
 */
void usb_epactive(LL_USB_TypeDef *USBx, USB_DEV_EP *ep)
{
    uint32_t u32Addr;
    uint32_t u32dxepctl;
    uint32_t u32Daintmsk;

    if (ep->ep_dir == 1U) {
        u32Addr = (uint32_t)(&(USBx->INEP_REGS[ep->epidx]->DIEPCTL));
        u32Daintmsk = 1UL << ep->epidx;
    } else {
        u32Addr = (uint32_t)(&(USBx->OUTEP_REGS[ep->epidx]->DOEPCTL));
        u32Daintmsk = 1UL << (USBFS_DAINTMSK_OEPINTM_POS + ep->epidx);
    }
    u32dxepctl = READ_REG32(*(__IO uint32_t *)u32Addr);
    if (0UL == (u32dxepctl & USBFS_DIEPCTL_USBAEP)) {
        u32dxepctl = ((ep->maxpacket << USBFS_DIEPCTL_MPSIZ_POS)
                      | (((uint32_t)ep->trans_type) << USBFS_DIEPCTL_EPTYP_POS)
                      | (((uint32_t)ep->tx_fifo_num) << USBFS_DIEPCTL_TXFNUM_POS)
                      | USBFS_DIEPCTL_SD0PID_SEVNFRM
                      | USBFS_DIEPCTL_USBAEP);

        WRITE_REG32(*(__IO uint32_t *)u32Addr, u32dxepctl);
    }
    SET_REG32_BIT(USBx->DREGS->DAINTMSK, u32Daintmsk);
}

/**
 * @brief  enable an EP to be deactive state if it is active
 * @param  [in] USBx        usb instance
 * @param  [in] ep          endpoint instance
 * @retval None
 */
void usb_epdeactive(LL_USB_TypeDef *USBx, USB_DEV_EP *ep)
{
    uint32_t u32Daintmsk;

    if (ep->ep_dir == 1U) {
        CLR_REG32_BIT(USBx->INEP_REGS[ep->epidx]->DIEPCTL, USBFS_DIEPCTL_USBAEP);
        u32Daintmsk  = 1UL << ep->epidx;
    } else {
        CLR_REG32_BIT(USBx->OUTEP_REGS[ep->epidx]->DOEPCTL, USBFS_DOEPCTL_USBAEP);
        u32Daintmsk = 1UL << (USBFS_DAINTMSK_OEPINTM_POS + ep->epidx);
    }
    CLR_REG32_BIT(USBx->DREGS->DAINTMSK, u32Daintmsk);
}

/**
 * @brief  Setup the data into the EP and begin to transmit data.
 * @param  [in] USBx        usb instance
 * @param  [in] ep          endpoint instance
 * @param  [in] u8DmaEn     USB DMA status
 * @retval None
 */
void usb_epntransbegin(LL_USB_TypeDef *USBx, USB_DEV_EP *ep, uint8_t u8DmaEn)
{
    uint32_t u32depctl;
    uint32_t u32DeptsizTmp;
    uint32_t u32Pktcnt;
    uint32_t u32Xfersize;

    if (ep->ep_dir == 1U) {
        u32depctl = READ_REG32(USBx->INEP_REGS[ep->epidx]->DIEPCTL);
        /* Zero Length Packet? */
        if (ep->xfer_len == 0UL) {
            u32Xfersize = 0UL;
            u32Pktcnt = 1UL;
            u32DeptsizTmp = (u32Xfersize << USBFS_DIEPTSIZ_XFRSIZ_POS) | (u32Pktcnt << USBFS_DIEPTSIZ_PKTCNT_POS);
        } else {
            /* Program the transfer size and packet count
            * as follows: xfersize = N * maxpacket +
            * short_packet pktcnt = N + (short_packet
            * exist ? 1 : 0)
            */
            u32Xfersize = ep->xfer_len;
            u32Pktcnt = (ep->xfer_len - 1U + ep->maxpacket) / ep->maxpacket;
            u32DeptsizTmp = (u32Xfersize << USBFS_DIEPTSIZ_XFRSIZ_POS) | (u32Pktcnt << USBFS_DIEPTSIZ_PKTCNT_POS);
        }
        MODIFY_REG32(USBx->INEP_REGS[ep->epidx]->DIEPTSIZ, USBFS_DIEPTSIZ_XFRSIZ | USBFS_DIEPTSIZ_PKTCNT, u32DeptsizTmp);

        if (u8DmaEn == 1U) {
            WRITE_REG32(USBx->INEP_REGS[ep->epidx]->DIEPDMA, ep->dma_addr);
        } else {
            if (ep->trans_type != EP_TYPE_ISOC) {
                /* Enable the Tx FIFO Empty Interrupt for this EP */
                if (ep->xfer_len > 0U) {
                    SET_REG32_BIT(USBx->DREGS->DIEPEMPMSK, 1UL << ep->epidx);
                }
            }
        }

        if (ep->trans_type == EP_TYPE_ISOC) {
            if (((READ_REG32(USBx->DREGS->DSTS) >> USBFS_DSTS_FNSOF_POS) & 0x1U) == 0U) {
                u32depctl |= USBFS_DIEPCTL_SODDFRM;
            } else {
                u32depctl |= USBFS_DIEPCTL_SD0PID_SEVNFRM;
            }
        }

        u32depctl |= (USBFS_DIEPCTL_CNAK | USBFS_DIEPCTL_EPENA);
        WRITE_REG32(USBx->INEP_REGS[ep->epidx]->DIEPCTL, u32depctl);

        if (ep->trans_type == EP_TYPE_ISOC) {
            usb_wrpkt(USBx, ep->xfer_buff, ep->epidx, (uint16_t)ep->xfer_len, u8DmaEn);
        }
    } else {
        u32depctl = READ_REG32(USBx->OUTEP_REGS[ep->epidx]->DOEPCTL);
        /* Program the transfer size and packet count as follows:
        * pktcnt = N
        * xfersize = N * maxpacket
        */
        if (ep->xfer_len == 0U) {
            u32Xfersize = ep->maxpacket;
            u32Pktcnt = 1UL;
            u32DeptsizTmp = (u32Xfersize << USBFS_DOEPTSIZ_XFRSIZ_POS) | (u32Pktcnt << USBFS_DOEPTSIZ_PKTCNT_POS);
        } else {
            u32Pktcnt   = (ep->xfer_len + (ep->maxpacket - 1U)) / ep->maxpacket;
            u32Xfersize = u32Pktcnt * ep->maxpacket;
            u32DeptsizTmp = (u32Xfersize << USBFS_DOEPTSIZ_XFRSIZ_POS) | (u32Pktcnt << USBFS_DOEPTSIZ_PKTCNT_POS);
            ep->xfer_len = u32Xfersize;
        }
        MODIFY_REG32(USBx->OUTEP_REGS[ep->epidx]->DOEPTSIZ, USBFS_DOEPTSIZ_XFRSIZ | USBFS_DOEPTSIZ_PKTCNT, u32DeptsizTmp);

        if (u8DmaEn == 1U) {
            WRITE_REG32(USBx->OUTEP_REGS[ep->epidx]->DOEPDMA, ep->dma_addr);
        }

        if (ep->trans_type == EP_TYPE_ISOC) {
            if (0U != ep->datax_pid) {
                u32depctl |= USBFS_DOEPCTL_SD1PID;
            } else {
                u32depctl |= USBFS_DOEPCTL_SD0PID;
            }
        }
        u32depctl |= (USBFS_DOEPCTL_CNAK | USBFS_DOEPCTL_EPENA);
        WRITE_REG32(USBx->OUTEP_REGS[ep->epidx]->DOEPCTL, u32depctl);
    }
}

/**
 * @brief  Setup the data into the EP0 and begin to transmit data.
 * @param  [in] USBx        usb instance
 * @param  [in] ep          endpoint instance
 * @param  [in] u8DmaEn     USB DMA status
 * @retval None
 */
void usb_ep0transbegin(LL_USB_TypeDef *USBx, USB_DEV_EP *ep, uint8_t u8DmaEn)
{
    uint32_t u32depctl;
    uint32_t u32DeptsizTmp;
    uint32_t u32Pktcnt;
    uint32_t u32Xfersize;

    if (ep->ep_dir == 1U) {
        u32depctl = READ_REG32(USBx->INEP_REGS[0]->DIEPCTL);
        /* Zero Length Packet? */
        if (ep->xfer_len == 0U) {
            u32Xfersize = 0UL;
            u32Pktcnt = 1UL;
            u32DeptsizTmp = (u32Xfersize << USBFS_DIEPTSIZ_XFRSIZ_POS) | (u32Pktcnt << USBFS_DIEPTSIZ_PKTCNT_POS);
        } else {
            if (ep->xfer_len > ep->maxpacket) {
                ep->xfer_len = ep->maxpacket;
                u32Xfersize = ep->maxpacket;
            } else {
                u32Xfersize = ep->xfer_len;
            }
            u32Pktcnt = 1UL;
            u32DeptsizTmp = (u32Xfersize << USBFS_DIEPTSIZ_XFRSIZ_POS) | (u32Pktcnt << USBFS_DIEPTSIZ_PKTCNT_POS);
        }
        MODIFY_REG32(USBx->INEP_REGS[0]->DIEPTSIZ, USBFS_DIEPTSIZ_XFRSIZ | USBFS_DIEPTSIZ_PKTCNT, u32DeptsizTmp);

        if (u8DmaEn == 1U) {
            WRITE_REG32(USBx->INEP_REGS[ep->epidx]->DIEPDMA, ep->dma_addr);
        }

        u32depctl |= (USBFS_DIEPCTL_CNAK | USBFS_DIEPCTL_EPENA);
        WRITE_REG32(USBx->INEP_REGS[0]->DIEPCTL, u32depctl);

        if (u8DmaEn == 0U) {
            /* Enable the Tx FIFO Empty Interrupt for this EP */
            if (ep->xfer_len > 0U) {
                SET_REG32_BIT(USBx->DREGS->DIEPEMPMSK, 1UL << ep->epidx);
            }
        }
    } else {
        u32depctl  = READ_REG32(USBx->OUTEP_REGS[0]->DOEPCTL);
        /* Program the transfer size and packet count as follows:
        * xfersize = N * (maxpacket + 4 - (maxpacket % 4))
        * pktcnt = N           */
        if (ep->xfer_len == 0U) {
            u32Xfersize = 0UL;
            u32Pktcnt = 1UL;
        } else {
            ep->xfer_len = LL_MIN(ep->rem_data_len, ep->maxpacket);
            u32Xfersize = ep->xfer_len;
            u32Pktcnt   = 1UL;
        }
        u32DeptsizTmp = (u32Xfersize << USBFS_DOEPTSIZ_XFRSIZ_POS) | (u32Pktcnt << USBFS_DOEPTSIZ_PKTCNT_POS);
        MODIFY_REG32(USBx->OUTEP_REGS[ep->epidx]->DOEPTSIZ, USBFS_DOEPTSIZ_XFRSIZ | USBFS_DOEPTSIZ_PKTCNT, u32DeptsizTmp);

        if (u8DmaEn == 1U) {
            WRITE_REG32(USBx->OUTEP_REGS[ep->epidx]->DOEPDMA, ep->dma_addr);
        }
        u32depctl |= (USBFS_DOEPCTL_CNAK | USBFS_DOEPCTL_EPENA);
        WRITE_REG32(USBx->OUTEP_REGS[ep->epidx]->DOEPCTL, u32depctl);
    }
}

/**
 * @brief  Set the EP to be stall status
 * @param  [in] USBx        usb instance
 * @param  [in] ep          endpoint instance
 * @retval None
 */
void usb_setepstall(LL_USB_TypeDef *USBx, USB_DEV_EP *ep)
{
    uint32_t u32depctl;

    if (ep->ep_dir == 1U) {
        u32depctl = READ_REG32(USBx->INEP_REGS[ep->epidx]->DIEPCTL);
        if (0UL != (u32depctl & USBFS_DIEPCTL_EPENA)) {
            u32depctl |= USBFS_DIEPCTL_EPDIS;
        }
        u32depctl |= USBFS_DIEPCTL_STALL;
        WRITE_REG32(USBx->INEP_REGS[ep->epidx]->DIEPCTL, u32depctl);
    } else {
        u32depctl = READ_REG32(USBx->OUTEP_REGS[ep->epidx]->DOEPCTL);
        u32depctl |= USBFS_DOEPCTL_STALL;
        WRITE_REG32(USBx->OUTEP_REGS[ep->epidx]->DOEPCTL, u32depctl);
    }
}

/**
 * @brief  clear the stall status of a EP
 * @param  [in] USBx        usb instance
 * @param  [in] ep          endpoint instance
 * @retval None
 */
void usb_clearepstall(LL_USB_TypeDef *USBx, USB_DEV_EP *ep)
{
    uint32_t tmp_depctl_addr;
    uint32_t u32depctl;
    if (ep->ep_dir == 1U) {
        tmp_depctl_addr = (uint32_t)(&(USBx->INEP_REGS[ep->epidx]->DIEPCTL));
    } else {
        tmp_depctl_addr = (uint32_t)(&(USBx->OUTEP_REGS[ep->epidx]->DOEPCTL));
    }
    u32depctl = READ_REG32(*(__IO uint32_t *)tmp_depctl_addr);

    u32depctl &= (~USBFS_DIEPCTL_STALL);
    if ((ep->trans_type == EP_TYPE_INTR) || (ep->trans_type == EP_TYPE_BULK)) {
        u32depctl |= USBFS_DIEPCTL_SD0PID_SEVNFRM;
    }

    WRITE_REG32(*(__IO uint32_t *)tmp_depctl_addr, u32depctl);
}

/**
 * @brief  configure the EPO to receive data packets
 * @param  [in] USBx        usb instance
 * @param  [in] u8DmaEn     USB DMA status
 * @retval None
 */
void usb_ep0revcfg(LL_USB_TypeDef *USBx, uint8_t u8DmaEn, uint8_t *u8RevBuf)
{
    uint32_t u32deptsize;
    uint32_t u32doepctl;

    u32deptsize = (3UL << USBFS_DOEPTSIZ0_STUPCNT_POS)
                  | (1UL << USBFS_DOEPTSIZ0_PKTCNT_POS)
                  | (64UL << USBFS_DOEPTSIZ0_XFRSIZ_POS);

    WRITE_REG32(USBx->OUTEP_REGS[0]->DOEPTSIZ, u32deptsize);

    if (u8DmaEn == 1U) {
        WRITE_REG32(USBx->OUTEP_REGS[0]->DOEPDMA, (uint32_t)&u8RevBuf[0]);
        u32doepctl = READ_REG32(USBx->OUTEP_REGS[0]->DOEPCTL);
        u32doepctl |= (USBFS_DOEPCTL_EPENA | USBFS_DOEPCTL_USBAEP);
        WRITE_REG32(USBx->OUTEP_REGS[0]->DOEPCTL, u32doepctl);
    }
}

/**
 * @brief  enable remote wakeup active
 * @param  [in] USBx        usb instance
 * @retval None
 */
void usb_remotewakeupen(LL_USB_TypeDef *USBx)
{
    uint32_t u32dsts;

    u32dsts = READ_REG32(USBx->DREGS->DSTS);
    if (0UL != (u32dsts & USBFS_DSTS_SUSPSTS)) {
        /* un-gate USB Core clock */
        CLR_REG32_BIT(*USBx->GCCTL, USBFS_GCCTL_GATEHCLK | USBFS_GCCTL_STPPCLK);
    }

    SET_REG32_BIT(USBx->DREGS->DCTL, USBFS_DCTL_RWUSIG);
    hdl_delay_ms(5UL);
    CLR_REG32_BIT(USBx->DREGS->DCTL, USBFS_DCTL_RWUSIG);
}

/**
 * @brief  control the device to connect or disconnect
 * @param  [in] USBx        usb instance
 * @param  [in] link        0(conn) or 1(disconn)
 * @retval None
 */
void usb_ctrldevconnect(LL_USB_TypeDef *USBx, uint8_t link)
{
    if (0U == link) {
        CLR_REG32_BIT(USBx->DREGS->DCTL, USBFS_DCTL_SDIS);
    } else {
        SET_REG32_BIT(USBx->DREGS->DCTL, USBFS_DCTL_SDIS);
    }
    hdl_delay_ms(3UL);
}
#endif

#ifdef USE_HOST_MODE
/**
 * @brief  Initializes the USB controller when it is host mode
 * @param  [in] USBx        usb instance
 * @param  [in] basic_cfgs  usb core basic cfgs
 * @retval None
 */
void usb_hostmodeinit(LL_USB_TypeDef *USBx, USB_CORE_BASIC_CFGS *basic_cfgs)
{
    __IO uint8_t u8Tmp = 0U;
    WRITE_REG32(*USBx->GCCTL, 0UL);  /* reset the register-GCCTL */
    if (USBHS_PHY_EMBED == basic_cfgs->phy_type) {
        usb_fslspclkselset(USBx, HCFG_6_MHZ);  /* PHY clock is running at 6MHz */
    } else {
        usb_fslspclkselset(USBx, HCFG_30_60_MHZ);  /* PHY clock is running at 6MHz */
    }
    usb_hprtrst(USBx);                     /* reset the port */
    usb_enumspeed(USBx);                   /* FS or LS bases on the maximum speed supported by the connected device */
    usb_sethostfifo(USBx, basic_cfgs->core_type);
    /* Flush all the txFIFO and the whole rxFIFO */
    usb_txfifoflush(USBx, 0x10UL);
    usb_rxfifoflush(USBx);
    /* Clear all HC Interrupt bits that are pending */
    while (u8Tmp < basic_cfgs->host_chnum) {
        WRITE_REG32(USBx->HC_REGS[u8Tmp]->HCINT, 0xFFFFFFFFUL);
        WRITE_REG32(USBx->HC_REGS[u8Tmp]->HCINTMSK, 0UL);
        u8Tmp++;
    }
    usb_hostinten(USBx, basic_cfgs->dmaen);
}

/**
 * @brief  set the vbus if state is 1 or reset the vbus if state is 0.
 * @param  [in] USBx        usb instance
 * @param  [in] u8State       the vbus state it would be.
 * @retval None
 */
void usb_vbusctrl(LL_USB_TypeDef *USBx, uint8_t u8State)
{
    uint32_t u32hprt;

    u32hprt = usb_rdhprt(USBx);
    if ((0UL == (u32hprt & USBFS_HPRT_PWPR)) && (1U == u8State)) {
        u32hprt |= USBFS_HPRT_PWPR;
        WRITE_REG32(*USBx->HPRT, u32hprt);
    }
    if ((0UL != (u32hprt & USBFS_HPRT_PWPR)) && (0U == u8State)) {
        u32hprt &= (~USBFS_HPRT_PWPR);
        WRITE_REG32(*USBx->HPRT, u32hprt);
    }
}

/**
 * @brief  Enables the related interrupts when the core is host mode
 * @param  [in] USBx        usb instance
 * @param  [in] u8DmaEn     USB DMA status
 * @retval None
 */
void usb_hostinten(LL_USB_TypeDef *USBx, uint8_t u8DmaEn)
{
    uint32_t u32gIntmsk = 0UL;
    WRITE_REG32(USBx->GREGS->GINTMSK, 0UL);
    /* Clear the pending interrupt bits */
    WRITE_REG32(USBx->GREGS->GINTSTS, 0xFFFFFFFFUL);

    /* Enable the normal interrupt bits */
    usb_normalinten(USBx);

    if (u8DmaEn == 0U) {
        u32gIntmsk |= USBFS_GINTMSK_RXFNEM;
    }
    u32gIntmsk |= (USBFS_GINTMSK_HPRTIM
                   | USBFS_GINTMSK_HCIM
                   | USBFS_GINTMSK_DISCIM
                   | USBFS_GINTMSK_SOFM
                   | USBFS_GINTMSK_IPXFRM_INCOMPISOOUTM);
    SET_REG32_BIT(USBx->GREGS->GINTMSK, u32gIntmsk);
}

/**
 * @brief  Reset the port, the 1'b0 state must last at lease 10ms.
 * @param  [in] USBx        usb instance
 * @retval None
 */
void usb_hprtrst(LL_USB_TypeDef *USBx)
{
    uint32_t u32hprt;
    u32hprt = usb_rdhprt(USBx);
    u32hprt |= USBFS_HPRT_PRST;
    WRITE_REG32(*USBx->HPRT, u32hprt);
    hdl_delay_ms(10UL);
    u32hprt &= ~USBFS_HPRT_PRST;
    WRITE_REG32(*USBx->HPRT, u32hprt);
    hdl_delay_ms(20UL);
}

/**
 * @brief  Prepares transferring packets on a host channel
 * @param  [in] USBx        usb instance
 * @param  [in] hc_num      channel index
 * @param  [in] pCh         channel structure
 * @param  [in] u8DmaEn     USB DMA status
 * @retval status in byte
 */
uint8_t usb_inithch(LL_USB_TypeDef *USBx, uint8_t hc_num, USB_HOST_CH *pCh, uint8_t u8DmaEn)
{
    uint32_t u32hcintmsk = 0UL;
    uint32_t u32hcchar = 0UL;

    WRITE_REG32(USBx->HC_REGS[hc_num]->HCINT, 0xFFFFFFFFUL);
    switch (pCh->ep_type) {
        case EP_TYPE_CTRL:
        case EP_TYPE_BULK:
            u32hcintmsk |= (USBFS_HCINTMSK_XFRCM
                            | USBFS_HCINTMSK_STALLM
                            | USBFS_HCINTMSK_TXERRM
                            | USBFS_HCINTMSK_DTERRM
                            | USBFS_HCINTMSK_NAKM);
            if (0U != pCh->is_epin) {
                u32hcintmsk |= USBFS_HCINTMSK_BBERRM;
            } else {
                if (0U != pCh->do_ping) {
                    u32hcintmsk |= USBFS_HCINTMSK_ACKM;
                }
            }
            break;
        case EP_TYPE_INTR:
            u32hcintmsk |= (USBFS_HCINTMSK_XFRCM
                            | USBFS_HCINTMSK_NAKM
                            | USBFS_HCINTMSK_STALLM
                            | USBFS_HCINTMSK_TXERRM
                            | USBFS_HCINTMSK_DTERRM
                            | USBFS_HCINTMSK_FRMORM);
            if (0U != pCh->is_epin) {
                u32hcintmsk |= USBFS_HCINTMSK_BBERRM;
            }
            break;
        case EP_TYPE_ISOC:
            u32hcintmsk |= (USBFS_HCINTMSK_XFRCM
                            | USBFS_HCINTMSK_FRMORM
                            | USBFS_HCINTMSK_ACKM);

            if (0U != pCh->is_epin) {
                u32hcintmsk |= (USBFS_HCINTMSK_TXERRM | USBFS_HCINTMSK_BBERRM);
            }
            break;
        default:
            break;
    }

    WRITE_REG32(USBx->HC_REGS[hc_num]->HCINTMSK, u32hcintmsk);
    SET_REG32_BIT(USBx->HREGS->HAINTMSK, 1UL << hc_num);

    /* enable the host channel interrupts */
    SET_REG32_BIT(USBx->GREGS->GINTMSK, USBFS_GINTMSK_HCIM);

    /* modify HCCHAR */
    u32hcchar |= (((uint32_t)pCh->dev_addr) << USBFS_HCCHAR_DAD_POS);
    u32hcchar |= (((uint32_t)pCh->ep_idx) << USBFS_HCCHAR_EPNUM_POS);
    u32hcchar |= (((uint32_t)pCh->is_epin) << USBFS_HCCHAR_EPDIR_POS);
    u32hcchar |= (((uint32_t)pCh->ep_type) << USBFS_HCCHAR_EPTYP_POS);
    u32hcchar |= (((uint32_t)pCh->max_packet) << USBFS_HCCHAR_MPSIZ_POS);
    if (PRTSPD_LOW_SPEED == pCh->ch_speed) {
        u32hcchar |= USBFS_HCCHAR_LSDEV;
    } else {
        u32hcchar &= ~USBFS_HCCHAR_LSDEV;
    }

    if (pCh->ep_type == EP_TYPE_INTR) {
        u32hcchar |= USBFS_HCCHAR_ODDFRM;
    }
    WRITE_REG32(USBx->HC_REGS[hc_num]->HCCHAR, u32hcchar);
    return USB_OK;
}

/**
 * @brief  Start transfer on the channel whose index is hc_num.
 * @param  [in] USBx        usb instance
 * @param  [in] hc_num      channel index
 * @param  [in] pCh         channel structure
 * @param  [in] u8DmaEn     USB DMA status
 * @retval status in 8 bits
 */
uint8_t usb_hchtransbegin(LL_USB_TypeDef *USBx, uint8_t hc_num, USB_HOST_CH *pCh, uint8_t u8DmaEn)
{
    uint32_t u32hcchar;
    uint32_t u32hctsiz = 0UL;
    uint32_t u32hnptxsts;
    uint32_t u32hptxsts;
    uint16_t u16LenWords;
    uint16_t u16NumPacket;
    uint16_t u16MaxHcPktCount = 256U;

    /* Compute the expected number of packets associated to the transfer */
    if (pCh->xfer_len > 0U) {
        u16NumPacket = (uint16_t)((pCh->xfer_len +
                                   (uint32_t)pCh->max_packet - 1UL) / (uint32_t)pCh->max_packet);

        if (u16NumPacket > u16MaxHcPktCount) {
            u16NumPacket = u16MaxHcPktCount;
            pCh->xfer_len = (uint32_t)u16NumPacket * (uint32_t)pCh->max_packet;
        }
    } else {
        u16NumPacket = 1U;
    }
    if (0U != pCh->is_epin) {
        pCh->xfer_len = (uint32_t)u16NumPacket * (uint32_t)pCh->max_packet;
    }

    u32hctsiz |= (((uint32_t)pCh->xfer_len) << USBFS_HCTSIZ_XFRSIZ_POS);
    u32hctsiz |= (((uint32_t)u16NumPacket) << USBFS_HCTSIZ_PKTCNT_POS);
    u32hctsiz |= (((uint32_t)pCh->pid_type) << USBFS_HCTSIZ_DPID_POS);
    WRITE_REG32(USBx->HC_REGS[hc_num]->HCTSIZ, u32hctsiz);

    if (u8DmaEn == 1U) {
        WRITE_REG32(USBx->HC_REGS[hc_num]->HCDMA, pCh->xfer_buff);
    }

    u32hcchar = READ_REG32(USBx->HC_REGS[hc_num]->HCCHAR);
    u32hcchar &= ~USBFS_HCCHAR_ODDFRM;
    u32hcchar |= (usb_ifevenframe(USBx) << USBFS_HCCHAR_ODDFRM_POS);

    /* enable this host channel whose number is hc_num */
    u32hcchar |= USBFS_HCCHAR_CHENA;
    u32hcchar &= ~USBFS_HCCHAR_CHDIS;
    WRITE_REG32(USBx->HC_REGS[hc_num]->HCCHAR, u32hcchar);

    if (u8DmaEn == 0U) {
        if ((pCh->is_epin == 0U) && (pCh->xfer_len > 0U)) {
            switch (pCh->ep_type) {
                /* Non-periodic transmit */
                case EP_TYPE_CTRL:
                case EP_TYPE_BULK:
                    u32hnptxsts = READ_REG32(USBx->GREGS->HNPTXSTS);
                    u16LenWords = (uint16_t)((pCh->xfer_len + 3UL) / 4UL);
                    /* check if the amount of free space available in the non-periodic txFIFO is enough */
                    if (u16LenWords > ((u32hnptxsts & USBFS_HNPTXSTS_NPTXFSAV) >> USBFS_HNPTXSTS_NPTXFSAV_POS)) {
                        /* enable interrrupt of nptxfempty of GINTMSK*/
                        SET_REG32_BIT(USBx->GREGS->GINTMSK, USBFS_GINTMSK_NPTXFEM);
                    }
                    break;
                /* Periodic trnsmit */
                case EP_TYPE_INTR:
                case EP_TYPE_ISOC:
                    u32hptxsts = READ_REG32(USBx->HREGS->HPTXSTS);
                    u16LenWords = (uint16_t)((pCh->xfer_len + 3UL) / 4UL);
                    /* check if the space of periodic TxFIFO is enough */
                    if (u16LenWords > ((u32hptxsts & USBFS_HPTXSTS_PTXFSAVL) >> USBFS_HPTXSTS_PTXFSAVL_POS)) {
                        /* enable interrrupt of ptxfempty of GINTMSK */
                        SET_REG32_BIT(USBx->GREGS->GINTMSK, USBFS_GINTMSK_PTXFEM);
                    }
                    break;
                default:
                    break;
            }

            usb_wrpkt(USBx, pCh->xfer_buff, hc_num, (uint16_t)pCh->xfer_len, u8DmaEn);
        }
    }
    return USB_OK;
}

/**
 * @brief  Stop the host and flush all the txFIFOs and the whole rxFIFO.
 * @param  [in] USBx        usb instance
 * @param  [in] u8ChNum     Host channel number
 * @retval None
 */
void usb_hoststop(LL_USB_TypeDef *USBx, uint8_t u8ChNum)
{
    __IO uint32_t u32Tmp = 0UL;

    WRITE_REG32(USBx->HREGS->HAINTMSK, 0UL);
    WRITE_REG32(USBx->HREGS->HAINT, 0xFFFFFFFFUL);

    do {
        usb_chrst(USBx, (uint8_t)u32Tmp);
        u32Tmp++;
    } while (u32Tmp < u8ChNum);
    /* flush all the txFIFOs and the whole rxFIFO */
    usb_rxfifoflush(USBx);
    usb_txfifoflush(USBx, 0x10UL);
}

/**
 * @brief  make the channel to halt
 * @param  [in] USBx        usb instance
 * @param  [in] hc_num      channel index
 * @retval None
 */
void usb_hchstop(LL_USB_TypeDef *USBx, uint8_t hc_num)
{
    uint32_t u32hcchar;

    u32hcchar = READ_REG32(USBx->HC_REGS[hc_num]->HCCHAR);
    u32hcchar |= USBFS_HCCHAR_CHDIS;
    /* Check for space in the request queue to issue the halt. */
    if ((EP_TYPE_CTRL == ((u32hcchar & USBFS_HCCHAR_EPTYP) >> USBFS_HCCHAR_EPTYP_POS))
        || (EP_TYPE_BULK == ((u32hcchar & USBFS_HCCHAR_EPTYP) >> USBFS_HCCHAR_EPTYP_POS))) {
        if (0UL == (READ_REG32(USBx->GREGS->HNPTXSTS) & USBFS_HNPTXSTS_NPTQXSAV)) {
            u32hcchar &= (~USBFS_HCCHAR_CHENA);
            WRITE_REG32(USBx->HC_REGS[hc_num]->HCCHAR, u32hcchar);
        }
    } else {
        if (0UL == (READ_REG32(USBx->HREGS->HPTXSTS) & USBFS_HPTXSTS_PTXQSAV)) {
            u32hcchar &= (~USBFS_HCCHAR_CHENA);
            WRITE_REG32(USBx->HC_REGS[hc_num]->HCCHAR, u32hcchar);
        }
    }
    u32hcchar |= USBFS_HCCHAR_CHENA;
    WRITE_REG32(USBx->HC_REGS[hc_num]->HCCHAR, u32hcchar);
}

#endif

/**
 * @}
*/

#endif /* LL_USB_ENABLE */

/**
 * @}
*/

/**
 * @}
 */

/******************************************************************************
 * EOF (not truncated)
 *****************************************************************************/
