/*
 * Copyright (c) 2021 HPMicro
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *
 */

#ifndef HPM_USB_DRV_H
#define HPM_USB_DRV_H
/*---------------------------------------------------------------------
 * Includes
 *---------------------------------------------------------------------
 */
#include "hpm_common.h"
#include "hpm_usb_regs.h"
#include "hpm_soc_feature.h"

/**
 * @brief USB driver APIs
 * @defgroup usb_interface USB driver APIs
 * @ingroup communication_interfaces
 * @{
 */

/*---------------------------------------------------------------------
 *  Macro Constant Declarations
 *---------------------------------------------------------------------
 */
#define USB_PHY_INIT_DELAY_COUNT  (100U) /**< a delay count for USB phy initialization */
#define USB_HOST_FRAMELIST_SIZE   (8U)  /**< a frame list size in USB host mode */

/*---------------------------------------------------------------------
 *  Macro Enum Declarations
 *---------------------------------------------------------------------
 */
/**
 * @brief USB transfer direction types
 */
typedef enum {
    usb_dir_out     = 0,
    usb_dir_in      = 1,
    usb_dir_in_mask = 0x80
} usb_dir_t;    /**< usb_dir_t */

/**
 * @brief USB transfer types
 */
typedef enum {
    usb_xfer_control = 0,
    usb_xfer_isochronous,
    usb_xfer_bulk,
    usb_xfer_interrupt
} usb_xfer_type_t;    /**< usb_xfer_type_t */

/**
 * @brief USB controller work modes
 */
typedef enum {
    usb_ctrl_mode_otg    = 0,
    usb_ctrl_mode_device = 2,
    usb_ctrl_mode_host   = 3
} usb_controller_mode_t;    /**< usb_controller_mode_t */

/**
 * @brief USB line state
 */
typedef enum {
    usb_line_state0 = 0,
    usb_line_state1 = 1,
    usb_line_state2 = 2
} usb_line_state_t;    /**< usb_line_state_t */

/**
 * @brief USB transceiver
 */
typedef enum {
    usb_tran_parallel = 0,
    usb_tran_serial = 1
} usb_transceiver_t;    /**< usb_transceiver_t */

/**
 * @brief USB test modes
 */
typedef enum {
    usb_test_mode_disable = 0,
    usb_test_j_state,
    usb_test_k_state,
    usb_test_se0_nak,
    usb_test_packet,
    usb_test_force_hs,
    usb_test_force_fs,
    usb_test_force_ls,
} usb_test_mode_t;    /**< usb_test_mode_t */

/**
 * @brief USB vbus wakeup source
 */
typedef enum {
    usb_vbus_wakeup_vbus_valid = 0,
    usb_vbus_wakeup_session_valid,
} usb_vbus_wakeup_source_t;    /**< usb_vbus_wakeup_source_t */

/*---------------------------------------------------------------------
 * Structure Declarations
 *---------------------------------------------------------------------
 */
/**
 * @brief Control request structure
 */
typedef struct  __attribute__ ((packed)) {
    union {
        struct  __attribute__ ((packed)) {
            uint8_t recipient :  5;
            uint8_t type      :  2;
            uint8_t direction :  1;
        } bmRequestType_bit;

        uint8_t bmRequestType;
    };

    uint8_t  bRequest;
    uint16_t wValue;
    uint16_t wIndex;
    uint16_t wLength;
} usb_control_request_t;

/**
 * @brief Endpoint config structure
 */
typedef struct {
    uint8_t  xfer;
    uint8_t  ep_addr;
    uint16_t max_packet_size;
} usb_endpoint_config_t;

#if defined __cplusplus
extern "C" {
#endif /* __cplusplus */

/*---------------------------------------------------------------------
 * Common API
 *---------------------------------------------------------------------
 */

/**
 * @brief Get the mask of all enabled interrupts
 *
 * @param[in] ptr A USB peripheral base address.
 * @retval Mask of all enabled interrupts.
 */
static inline uint32_t usb_get_interrupts(USB_Type *ptr)
{
    return ptr->USBINTR;
}

/**
 * @brief Enable interrupts
 *
 * @param[in] ptr A USB peripheral base address
 * @param[in] mask Mask value for interrupt events
 */
static inline void usb_enable_interrupts(USB_Type *ptr, uint32_t mask)
{
    ptr->USBINTR |= mask;
}

/**
 * @brief Disable interrupts
 *
 * @param[in] ptr A USB peripheral base address
 * @param[in] mask Mask value for interrupt events
 */
static inline void usb_disable_interrupts(USB_Type *ptr, uint32_t mask)
{
    ptr->USBINTR &= ~mask;
}

/**
 * @brief Get all USB status flags
 *
 * @param[in] ptr A USB peripheral base address
 * @retval The USB interrupt status flags
 */
static inline uint32_t usb_get_status_flags(USB_Type *ptr)
{
    return ptr->USBSTS;
}

/**
 * @brief Clear status flags
 *
 * Only the specified flags can be cleared by writing USBSTS register.
 *
 * @param[in] ptr A USB peripheral base address
 * @param[in] mask Mask value for flags to be cleared.
 */

static inline void usb_clear_status_flags(USB_Type *ptr, uint32_t mask)
{
    ptr->USBSTS = mask;
}

/**
 * @brief Enable otg dp/dm change wakeup
 *
 * @param[in] ptr A USB peripheral base address
 */
static inline void usb_otg_enable_dpdm_wakeup(USB_Type *ptr)
{
    ptr->OTG_CTRL0 |= USB_OTG_CTRL0_OTG_WKDPDMCHG_EN_MASK;
}

/**
 * @brief Disbable otg dp/dm change  wakeup
 *
 * @param[in] ptr A USB peripheral base address
 */
static inline void usb_otg_disable_dpdm_wakeup(USB_Type *ptr)
{
    ptr->OTG_CTRL0 &= ~USB_OTG_CTRL0_OTG_WKDPDMCHG_EN_MASK;
}

/**
 * @brief Enable otg id change wakeup
 *
 * @param[in] ptr A USB peripheral base address
 */
static inline void usb_otg_enable_id_wakeup(USB_Type *ptr)
{
    ptr->OTG_CTRL0 |= USB_OTG_CTRL0_OTG_ID_WAKEUP_EN_MASK;
}

/**
 * @brief Disbable otg id change  wakeup
 *
 * @param[in] ptr A USB peripheral base address
 */
static inline void usb_otg_disable_id_wakeup(USB_Type *ptr)
{
    ptr->OTG_CTRL0 &= ~USB_OTG_CTRL0_OTG_ID_WAKEUP_EN_MASK;
}

/**
 * @brief Enable otg vbus change wakeup
 *
 * @param[in] ptr A USB peripheral base address
 */
static inline void usb_otg_enable_vbus_wakeup(USB_Type *ptr)
{
    ptr->OTG_CTRL0 |= USB_OTG_CTRL0_OTG_VBUS_WAKEUP_EN_MASK;
}

/**
 * @brief Disbable otg vbus change wakeup
 *
 * @param[in] ptr A USB peripheral base address
 */
static inline void usb_otg_disable_vbus_wakeup(USB_Type *ptr)
{
    ptr->OTG_CTRL0 &= ~USB_OTG_CTRL0_OTG_VBUS_WAKEUP_EN_MASK;
}

/**
 * @brief Set otg vbus wakeup source
 *
 * @param[in] ptr A USB peripheral base address
 * @param[in] src wakeup source, @ref usb_vbus_wakeup_source_t
 */
static inline void usb_otg_set_vbus_wakeup_source(USB_Type *ptr, usb_vbus_wakeup_source_t src)
{
    ptr->OTG_CTRL0 = (ptr->OTG_CTRL0 & ~USB_OTG_CTRL0_OTG_VBUS_SOURCE_SEL_MASK) | USB_OTG_CTRL0_OTG_VBUS_SOURCE_SEL_SET(src);
}

/**
 * @brief Enable otg wakeup interrupt
 *
 * @param[in] ptr A USB peripheral base address
 */
static inline void usb_otg_enable_wakeup_int(USB_Type *ptr)
{
    ptr->OTG_CTRL0 |=  USB_OTG_CTRL0_OTG_WAKEUP_INT_ENABLE_MASK;
}

/**
 * @brief Disable otg wakeup interrupt
 *
 * @param[in] ptr A USB peripheral base address
 */
static inline void usb_otg_disable_wakeup_int(USB_Type *ptr)
{
    ptr->OTG_CTRL0 &= ~USB_OTG_CTRL0_OTG_WAKEUP_INT_ENABLE_MASK;
}

/**
 * @brief Get otg wakeup status flags
 *
 * @param[in] ptr A USB peripheral base address
 * @retval The USB otg wakeup interrupt status flag
 */
static inline bool usb_get_otg_wakeup_int_flag(USB_Type *ptr)
{
    return (USB_TOP_STATUS_WAKEUP_INT_STATUS_GET(ptr->TOP_STATUS) != 0) ? true : false;
}

/**
 * @brief Get USB suspend status
 *
 * @param[in] ptr A USB peripheral base address
 * @retval The USB controller suspend status
 */
static inline uint8_t usb_get_suspend_status(USB_Type *ptr)
{
    return USB_PORTSC1_SUSP_GET(ptr->PORTSC1);
}

/**
 * @brief Get USB reset status
 *
 * @param[in] ptr A USB peripheral base address
 * @retval The USB controller reset status
 */
static inline bool usb_get_port_reset_status(USB_Type *ptr)
{
    return USB_PORTSC1_PR_GET(ptr->PORTSC1);
}

/**
 * @brief Get USB current connect status
 *
 * @param[in] ptr A USB peripheral base address
 * @retval The USB controller reset status
 */
static inline bool usb_get_port_ccs(USB_Type *ptr)
{
    return USB_PORTSC1_CCS_GET(ptr->PORTSC1);
}

/**
 * @brief Get USB port speed status
 *
 * @param[in] ptr A USB peripheral base address
 * @retval The USB controller port speed status
 */
static inline uint8_t usb_get_port_speed(USB_Type *ptr)
{
    return USB_PORTSC1_PSPD_GET(ptr->PORTSC1);
}

/**
 * @brief Set port test control mode
 *
 * @param[in] ptr A USB peripheral base address
 * @param[in] test_mode usb test mode, @ref usb_test_mode_t
 */
static inline void usb_set_port_test_mode(USB_Type *ptr, usb_test_mode_t test_mode)
{
    ptr->PORTSC1 = (ptr->PORTSC1 & ~USB_PORTSC1_PTC_MASK) | USB_PORTSC1_PTC_SET(test_mode);
}

/**
 * @brief USB set port suspend
 *
 * @param[in] ptr A USB peripheral base address
 */
static inline void usb_set_port_suspend(USB_Type *ptr)
{
    ptr->PORTSC1 |= USB_PORTSC1_SUSP_MASK;
}

/**
 * @brief USB force port resume
 *
 * @param[in] ptr A USB peripheral base address
 */
static inline void usb_force_port_resume(USB_Type *ptr)
{
    ptr->PORTSC1 |= USB_PORTSC1_FPR_MASK;
    while ((ptr->PORTSC1 & USB_PORTSC1_FPR_MASK) != 0) {
    }
}

/**
 * @brief USB phy enter low power suspend
 *
 * @param[in] ptr A USB peripheral base address
 */
static inline void usb_phy_enter_low_power_suspend(USB_Type *ptr)
{
    ptr->PORTSC1 |= USB_PORTSC1_PHCD_MASK;
}

/**
 * @brief USB phy exit low power suspend
 *
 * @param[in] ptr A USB peripheral base address
 */
static inline void usb_phy_exit_low_power_suspend(USB_Type *ptr)
{
    ptr->PORTSC1 &= ~USB_PORTSC1_PHCD_MASK;
    /* otg utmi clock detection */
    ptr->PHY_STATUS |= USB_PHY_STATUS_UTMI_CLK_VALID_MASK;                 /* write 1 to clear valid status */
    while (USB_PHY_STATUS_UTMI_CLK_VALID_GET(ptr->PHY_STATUS) == 0) {      /* get utmi clock status */
        ;
    }
}

/**
 * @brief Get phy session valid flag
 *
 * @param[in] ptr A USB peripheral base address
 * @retval The phy session valid flag
 */
static inline bool usb_phy_get_session_valid_flag(USB_Type *ptr)
{
    return (USB_PHY_STATUS_UTMI_SESS_VALID_GET(ptr->PHY_STATUS) != 0) ? true : false;
}

/**
 * @brief enable otgsc session valid change interrupt
 *
 * @param[in] ptr A USB peripheral base address
 */
static inline void usb_otgsc_enable_session_valid_chg_int(USB_Type *ptr)
{
    ptr->OTGSC |= USB_OTGSC_ASVIE_MASK;
}

/**
 * @brief disable otgsc session valid change interrupt
 *
 * @param[in] ptr A USB peripheral base address
 */
static inline void usb_otgsc_disable_session_valid_chg_int(USB_Type *ptr)
{
    ptr->OTGSC &= ~USB_OTGSC_ASVIE_MASK;
}

/**
 * @brief check otgsc session valid change interrupt is enable
 *
 * @param[in] ptr A USB peripheral base address
 * @retval true interrupt is enable, false interrupt is disable
 */
static inline bool usb_otgsc_session_valid_chg_int_is_enable(USB_Type *ptr)
{
    return ((ptr->OTGSC & USB_OTGSC_ASVIE_MASK) != 0);
}

/**
 * @brief get otgsc session valid change flag
 *
 * @param[in] ptr A USB peripheral base address
 * @retval The otgsc session valid flag
 */
static inline bool usb_otgsc_get_session_valid_chg_flag(USB_Type *ptr)
{
    return (USB_OTGSC_ASVIS_SET(ptr->OTGSC) != 0) ? true : false;
}

/**
 * @brief clear otgsc session valid change flag
 *
 * @param[in] ptr A USB peripheral base address
 */
static inline void usb_otgsc_clear_session_valid_chg_flag(USB_Type *ptr)
{
    ptr->OTGSC |= USB_OTGSC_ASVIS_MASK;
}

/**
 * @brief Get otgsc session valid flag
 *
 * @param[in] ptr A USB peripheral base address
 * @retval The otgsc session valid flag
 */
static inline bool usb_otgsc_get_session_valid_flag(USB_Type *ptr)
{
    return (USB_OTGSC_ASV_GET(ptr->OTGSC) != 0) ? true : false;
}

/**
 * @brief Initialize USB phy
 *
 * @param[in] ptr A USB peripheral base address
 */
void usb_phy_init(USB_Type *ptr, bool host);

/**
 * @brief USB phy get line status
 *
 * @param[in] ptr A USB peripheral base address
 */
static inline uint8_t usb_phy_get_line_state(USB_Type *ptr)
{
    return USB_PHY_STATUS_LINE_STATE_GET(ptr->PHY_STATUS);
}

/**
 * @brief USB phy using internal vbus
 *
 * @param[in] ptr A USB peripheral base address
 */
static inline void usb_phy_using_internal_vbus(USB_Type *ptr)
{
    ptr->PHY_CTRL0 |= (USB_PHY_CTRL0_VBUS_VALID_OVERRIDE_MASK | USB_PHY_CTRL0_SESS_VALID_OVERRIDE_MASK)
                    | (USB_PHY_CTRL0_VBUS_VALID_OVERRIDE_EN_MASK | USB_PHY_CTRL0_SESS_VALID_OVERRIDE_EN_MASK);
}

/**
 * @brief USB phy using external vbus
 *
 * @param[in] ptr A USB peripheral base address
 */
static inline void usb_phy_using_external_vbus(USB_Type *ptr)
{
    ptr->PHY_CTRL0 &= ~((USB_PHY_CTRL0_VBUS_VALID_OVERRIDE_MASK | USB_PHY_CTRL0_SESS_VALID_OVERRIDE_MASK)
                      | (USB_PHY_CTRL0_VBUS_VALID_OVERRIDE_EN_MASK | USB_PHY_CTRL0_SESS_VALID_OVERRIDE_EN_MASK));
}

/**
 * @brief USB phy disconnect dp/dm pins pulldown resistance
 *
 * @param[in] ptr A USB peripheral base address
 */
static inline void usb_phy_disable_dp_dm_pulldown(USB_Type *ptr)
{
    ptr->PHY_CTRL0 |= 0x001000E0u;
}

/**
 * @brief USB phy connect dp/dm pins pulldown resistance
 *
 * @param[in] ptr A USB peripheral base address
 */
static inline void usb_phy_enable_dp_dm_pulldown(USB_Type *ptr)
{
    ptr->PHY_CTRL0 &= ~0x001000E0u;
}

/**
 * @brief Set phyctrl1 not utmi suspend
 *
 * @param[in] ptr A USB peripheral base address
 */
static inline void usb_phyctrl1_set_not_utmi_suspend(USB_Type *ptr)
{
    ptr->PHY_CTRL1 |= USB_PHY_CTRL1_UTMI_OTG_SUSPENDM_MASK;
}

/*---------------------------------------------------------------------
 * Device API
 *---------------------------------------------------------------------
 */

/**
 * @brief USB device bus reset
 *
 * @param[in] ptr A USB peripheral base address
 * @param[in] ep0_max_packet_size The maximum packet size of endpoint 0
 */
void usb_dcd_bus_reset(USB_Type *ptr, uint16_t ep0_max_packet_size);

/**
 * @brief Initialize controller to device mode
 *
 * @param[in] ptr A USB peripheral base address
 */
void usb_dcd_init(USB_Type *ptr);

/**
 * @brief Deinitialize controller to device
 *
 * @param[in] ptr A USB peripheral base address
 */
void usb_dcd_deinit(USB_Type *ptr);

/**
 * @brief Wakeup from host
 *
 * @param[in] ptr A USB peripheral base address
 */
void usb_dcd_remote_wakeup(USB_Type *ptr);

/**
 * @brief Open an endpoint
 *
 * @param[in] ptr A USB peripheral base address
 * @param[in] config A pointer to the specified endpoint config struct
 */
void usb_dcd_edpt_open(USB_Type *ptr, usb_endpoint_config_t *config);

/**
 * @brief get a specified endpoint type
 *
 * @param[in] ptr A USB peripheral base address
 * @param[in] ep_addr Endpoint address
 */
uint8_t usb_dcd_edpt_get_type(USB_Type *ptr, uint8_t ep_addr);

/**
 * @brief Submit a transfer
 *
 * @param[in] ptr A USB peripheral base address
 * @param[in] ep_idx An index of the specified endpoint
 */
void usb_dcd_edpt_xfer(USB_Type *ptr, uint8_t ep_idx);

/**
 * @brief Stall endpoint
 *
 * @param[in] ptr A USB peripheral base address
 * @param[in] ep_addr An address of the specified endpoint
 */
void usb_dcd_edpt_stall(USB_Type *ptr, uint8_t ep_addr);

/**
 * @brief Clear stall
 *
 * @param[in] ptr A USB peripheral base address
 * @param[in] ep_addr An address of the specified endpoint
 */
void usb_dcd_edpt_clear_stall(USB_Type *ptr, uint8_t ep_addr);

/**
 * @brief Clear stall
 *
 * @param[in] ptr A USB peripheral base address
 * @param[in] ep_addr An address of the specified endpoint
 * @retval The status of endpoint stall, true is stall, false is not stall
 */
bool usb_dcd_edpt_check_stall(USB_Type *ptr, uint8_t ep_addr);

/**
 * @brief Close a specified endpoint
 *
 * @param[in] ptr A USB peripheral base address
 * @param[in] ep_addr An address of the specified endpoint
 */
void usb_dcd_edpt_close(USB_Type *ptr, uint8_t ep_addr);

/**
 * @brief Connect by enabling internal pull-up resistor on D+/D-
 *
 * @param[in] ptr A USB peripheral base address
 */
void usb_dcd_connect(USB_Type *ptr);

/**
 * @brief Disconnect by disabling internal pull-up resistor on D+/D-
 *
 * @param[in] ptr A USB peripheral base address
 */
void usb_dcd_disconnect(USB_Type *ptr);

/**
 * @brief Get setup status of endpoint
 *
 * @param[in] ptr A USB peripheral base address
 * @retval The status of setup endpoint
 */
static inline uint32_t usb_dcd_get_edpt_setup_status(USB_Type *ptr)
{
    return ptr->ENDPTSETUPSTAT;
}

/**
 * @brief Clear the setup status of all specified endpoints
 *
 * @param[in] ptr A USB peripheral base address
 * @param[in] mask A mask of all specified endpoints
 */
static inline void usb_dcd_clear_edpt_setup_status(USB_Type *ptr, uint32_t mask)
{
    ptr->ENDPTSETUPSTAT = mask;
}

/**
 * @brief Set address
 *
 * @param[in] ptr A USB peripheral base address
 * @param[in] dev_addr An assigned endpoint address from USB host
 */
static inline void usb_dcd_set_address(USB_Type *ptr, uint8_t dev_addr)
{
    ptr->DEVICEADDR = USB_DEVICEADDR_USBADR_SET(dev_addr) | USB_DEVICEADDR_USBADRA_MASK;
}

/**
 * @brief Set endpoint list address
 *
 * @param[in] ptr A USB peripheral base address
 * @param[in] addr A start address of the endpoint qtd list
 */
static inline void usb_dcd_set_edpt_list_addr(USB_Type *ptr, uint32_t addr)
{
    ptr->ENDPTLISTADDR = addr & USB_ENDPTLISTADDR_EPBASE_MASK;
}

/**
 * @brief Get device address
 *
 * @param[in] ptr A USB peripheral base address
 * @retval The endpoint address
 */
static inline uint8_t usb_dcd_get_device_addr(USB_Type *ptr)
{
    return USB_DEVICEADDR_USBADR_GET(ptr->DEVICEADDR);
}

/**
 * @brief Get complete status of endpoint
 *
 * @param[in] ptr A USB peripheral base address
 * @retval The complete status od endpoint
 */
static inline uint32_t usb_dcd_get_edpt_complete_status(USB_Type *ptr)
{
    return ptr->ENDPTCOMPLETE;
}

/**
 * @brief Clear complete status of endpoint
 *
 * @param[in] ptr A USB peripheral base address
 * @param[in] mask A mask of the specified endpoints
 */
static inline void usb_dcd_clear_edpt_complete_status(USB_Type *ptr, uint32_t mask)
{
    ptr->ENDPTCOMPLETE = mask;
}

/*---------------------------------------------------------------------
 * Host API
 *---------------------------------------------------------------------
 */
/**
 * @brief Initialize controller to host mode
 *
 * @param[in] ptr A USB peripheral base address
 * @param[in] int_mask A mask of all required interrupts
 * @param[in] framelist_size A size of the frame list
 */
bool usb_hcd_init(USB_Type *ptr, uint32_t int_mask, uint16_t framelist_size);

/**
 * @brief Initialize controller to host modeHost Reset port
 *
 * @param[in] ptr A USB peripheral base address
 */
void usb_hcd_port_reset(USB_Type *ptr);

/**
 * @brief Initialize controller to host modeHost set command register
 *
 * @param[in] ptr A USB peripheral base address
 * @param[in] mask A mask of all required commands
 */
static inline void usb_hcd_set_command(USB_Type *ptr, uint32_t mask)
{
    ptr->USBCMD |= mask;
}

/**
 * @brief Get frame index
 *
 * @param[in] ptr A USB peripheral base address
 * @retval A index of the current frame list
 */
static inline uint32_t usb_hcd_get_frame_index(USB_Type *ptr)
{
    return ptr->FRINDEX;
}

/**
 * @brief Get port connect status change
 *
 * @param[in] ptr A USB peripheral base address
 * @retval A connect status change
 */
static inline bool usb_hcd_get_port_csc(USB_Type *ptr)
{
    return USB_PORTSC1_CSC_GET(ptr->PORTSC1);
}

/**
 * @brief Set power ctrl polarity
 *
 * @param[in] ptr A USB peripheral base address
 * @param[in] high true - vbus high level enable, false - vbus low level enable
 */
static inline void usb_hcd_set_power_ctrl_polarity(USB_Type *ptr, bool high)
{
    if (high) {
        ptr->OTG_CTRL0 |= USB_OTG_CTRL0_OTG_POWER_MASK_MASK;
    } else {
        ptr->OTG_CTRL0 &= ~USB_OTG_CTRL0_OTG_POWER_MASK_MASK;
    }
}

/**
 * @brief Enable port power
 *
 * @param[in] ptr A USB peripheral base address
 */
static inline void usb_hcd_enable_port_power(USB_Type *ptr)
{
    ptr->PORTSC1 |= USB_PORTSC1_PP_MASK;
}

/**
 * @brief Get port connect status changeSet async list address
 *
 * @param[in] ptr A USB peripheral base address
 * @param[in] addr An the start address of the async endpoint list
 */
static inline void usb_hcd_set_async_list_addr(USB_Type *ptr, uint32_t addr)
{
    ptr->ASYNCLISTADDR = addr & USB_ASYNCLISTADDR_ASYBASE_MASK;
}

/**
 * @brief Set periodic list address
 *
 * @param[in] ptr A USB peripheral base address
 * @param[in] addr An start address of the periodic endpoint list
 */
static inline void usb_hcd_set_periodic_list_addr(USB_Type *ptr, uint32_t addr)
{
    ptr->PERIODICLISTBASE = addr & USB_PERIODICLISTBASE_BASEADR_MASK;
}

/**
 * @brief Start hcd controller
 *
 * @param[in] ptr A USB peripheral base address
 */
static inline void usb_hcd_run(USB_Type *ptr)
{
    ptr->USBCMD |= USB_USBCMD_RS_MASK;
}

/**
 * @brief Stop hcd controller
 *
 * @param[in] ptr A USB peripheral base address
 */
static inline void usb_hcd_stop(USB_Type *ptr)
{
    ptr->USBCMD &= ~USB_USBCMD_RS_MASK;
}

/**
 * @brief Disable hcd controller Asynchronous Schedule and Periodic Schedule
 *
 * @param[in] ptr A USB peripheral base address
 */
static inline void usb_hcd_disable_ase_pse(USB_Type *ptr)
{
    ptr->USBCMD &= ~(USB_USBCMD_PSE_MASK | USB_USBCMD_ASE_MASK);
    while (ptr->USBCMD & (USB_USBCMD_PSE_MASK | USB_USBCMD_ASE_MASK)) {
        ;
    }
}

/**
 * @brief Enable hcd controller Asynchronous Schedule and Periodic Schedule
 *
 * @param[in] ptr A USB peripheral base address
 */
static inline void usb_hcd_enable_ase_pse(USB_Type *ptr)
{
    ptr->USBCMD |= (USB_USBCMD_PSE_MASK | USB_USBCMD_ASE_MASK);
}

#if defined __cplusplus
}
#endif /* __cplusplus */

/** @} */
#endif /* HPM_USB_DRV_H */
