/*******************************************************************************
 * Copyright 2019-2022 Microchip FPGA Embedded Systems Solutions.
 *
 * SPDX-License-Identifier: MIT
 *
 * Microchip PolarFire SoC MSS USB Driver Stack
 *      USB Core Interface Layer (USB-CIFL)
 *          USB-CIF driver
 *
 * USB-CIF driver implementation:
 * This source file implements MSS USB Interrupt handler functions. This file
 * also implements core interface function for the logical layer to control
 * the MSS USB core. These interface functions are independent of the USB mode.
 *
 *
 * SVN $Revision$
 * SVN $Date$
 */

#include "mss_usb_common_cif.h"

#include "mss_assert.h"
#include "mss_usb_common_reg_io.h"
#include "mss_usb_core_regs.h"
#include "mss_plic.h"

#ifdef MSS_USB_HOST_ENABLED
#include "mss_usb_host_cif.h"
#include "mss_usb_host_reg_io.h"
#endif  /* MSS_USB_HOST_ENABLED */

#ifdef __cplusplus
extern "C" {
#endif

#ifdef MSS_USB_HOST_ENABLED
static uint8_t MSS_USB_CIF_host_rx_errchk(mss_usb_ep_num_t ep_num);
static uint8_t MSS_USB_CIF_host_tx_errchk(mss_usb_ep_num_t ep_num);
#endif  /* MSS_USB_HOST_ENABLED */

#ifdef MSS_USB_DEVICE_ENABLED
static uint8_t MSS_USB_CIF_device_rx_errchk(mss_usb_ep_num_t ep_num);
static uint8_t MSS_USB_CIF_device_tx_errchk(mss_usb_ep_num_t ep_num);
#endif  /* MSS_USB_HOST_ENABLED */

/***************************************************************************//**
* Global variables shared by mss_usb_device_cif.c and mss_usb_common_cif.c
*/
#ifdef MSS_USB_DEVICE_ENABLED
    extern mss_usbd_cb_t g_mss_usbd_cb;
    extern volatile mss_usbd_cep_state_t cep_state;
#endif  /* MSS_USB_HOST_ENABLED */

#ifdef MSS_USB_HOST_ENABLED
    extern mss_usbh_cb_t g_mss_usbh_cb;
#endif /* MSS_USB_HOST_ENABLED */

/***************************************************************************//**
 * Private functions declarations of USB-CIF.
 ******************************************************************************/
static void
MSS_USB_CIF_handle_cep_irq
(
    void
);

static void
MSS_USB_CIF_handle_tx_ep_irq
(
    uint16_t irq_num
);

static void
MSS_USB_CIF_handle_rx_ep_irq
(
    uint16_t irq_num
);

/***************************************************************************//**
 * Main USB interrupt handler. It checks for TX/RX endpoint interrupts and USB
 * system level interrupts and calls the appropriate routine.
 */
uint8_t PLIC_usb_mc_IRQHandler
(
    void
)
{
    volatile uint8_t usb_irq;
    volatile uint16_t tx_ep_irq;
    volatile uint16_t rx_ep_irq;
    volatile uint8_t role;

    usb_irq = MSS_USB_CIF_read_irq_reg();
    tx_ep_irq = MSS_USB_CIF_read_tx_ep_irq_reg();
    rx_ep_irq = MSS_USB_CIF_read_rx_ep_irq_reg();

    /*
     When operating in Host mode, on detecting Disconnect event, Disconnect
     Interrupt occurs but the HostMode bit in DevCtl is also cleared.
     Hence moving Disconnect handling out of get_mode condition.
     In the event of Disconnection, The decision is made based on the B-Device
     bit(DevCtl-D7).
     */
    if (usb_irq & DISCONNECT_IRQ_MASK)
    {
        role = MSS_USB_CIF_get_role();

#ifdef MSS_USB_DEVICE_ENABLED
        if (MSS_USB_DEVICE_ROLE_DEVICE_B == role)
        {
            MSS_USB_CIF_enable_usbirq(RESET_IRQ_MASK);
            g_mss_usbd_cb.usbd_disconnect();
        }
#endif  /* MSS_USB_DEVICE_ENABLED */

#ifdef MSS_USB_HOST_ENABLED
        if (MSS_USB_DEVICE_ROLE_DEVICE_A == role)
        {
            /*In Host mode, On removing the attached B-device the session bit
              somehow remains set.
              This bit need to be cleared otherwise RESET interrupt is not occurring
              when B-Device is connected after removing A-Device*/
            if (MSS_USB_CIF_is_session_on())
            {
                MSS_USB_CIF_stop_session();
            }

            MSS_USBH_CIF_read_vbus_level();
            g_mss_usbh_cb.usbh_disconnect();
        }
#endif /* MSS_USB_HOST_ENABLED */
    }

#ifdef MSS_USB_DEVICE_ENABLED
    if (MSS_USB_CORE_MODE_DEVICE == MSS_USB_CIF_get_mode())
    {
        if (usb_irq & RESUME_IRQ_MASK)
        {
            g_mss_usbd_cb.usbd_resume();
        }
        if (usb_irq & SUSPEND_IRQ_MASK)
        {
            g_mss_usbd_cb.usbd_suspend();
        }
        if (usb_irq & RESET_IRQ_MASK)
        {
            MSS_USB_CIF_set_index_reg(MSS_USB_CEP);
            MSS_USB_CIF_enable_usbirq(DISCONNECT_IRQ_MASK | SUSPEND_IRQ_MASK);
            cep_state = MSS_USB_CTRL_EP_IDLE;
            MSS_USB_CIF_clr_usb_irq_reg();
            MSS_USB_CIF_cep_clr_setupend();
            MSS_USB_CIF_cep_clr_stall_sent();
            g_mss_usbd_cb.usbd_reset();
        }
    }
#endif /* MSS_USB_DEVICE_ENABLED */

#ifdef MSS_USB_HOST_ENABLED
    if (MSS_USB_CORE_MODE_HOST == MSS_USB_CIF_get_mode())
    {
        role = MSS_USB_CIF_get_role();
        if (usb_irq & RESUME_IRQ_MASK)
        {
            /* Resume interrupt in Host mode means Remote Wakeup request */
        }

        /* Vbus_err and session request interrupts are valid only in A device */
        if (MSS_USB_DEVICE_ROLE_DEVICE_A == role)
        {
            if (usb_irq & SESSION_REQUEST_IRQ_MASK)
            {
                /* This means SRP initiated by Target device. */
            }
            if (usb_irq & VBUS_ERROR_IRQ_MASK)
            {
                /* Power management */
                MSS_USBH_CIF_read_vbus_level();
            }
        }

        if (usb_irq & CONNECT_IRQ_MASK)
        {
            MSS_USBH_CIF_handle_connect_irq();
        }

        if (usb_irq & BABBLE_IRQ_MASK)
        {
            /* Not supported yet */
        }

#if 0   /* SOF interrupt is not processed */
        if (usb_irq & SOF_IRQ_MASK)
        {
            g_mss_usbd_cb.usb_device_sof(0);
        }
#endif /* SOF interrupt is not processed */

    }
#endif    /* MSS_USB_HOST_ENABLED */

    if (tx_ep_irq & 0x0001u)
    {
        /* handle EP0 IRQ */
        MSS_USB_CIF_handle_cep_irq();
    }

    if (tx_ep_irq & 0xFFFEu)    /* EP0 is handled above */
    {
        /* Handle TX EP here, pass on the EP numbers.Mask EP0 bit */
        tx_ep_irq &= 0xFFFEu;
        MSS_USB_CIF_handle_tx_ep_irq(tx_ep_irq);
    }

    if (rx_ep_irq & 0xFFFEu)    /* bit0 is not defined */
    {
        /* Handle RX EP here, pass on the EP numbers */
        MSS_USB_CIF_handle_rx_ep_irq(rx_ep_irq);
    }

    return EXT_IRQ_KEEP_ENABLED;
}

/***************************************************************************//**
 * Routine to handle the interrupt on Control Endpoint.(EP0)
 */
static void
MSS_USB_CIF_handle_cep_irq
(
    void
)
{
    uint8_t status = 0u;

    MSS_USB_CIF_set_index_reg(MSS_USB_CEP);

#ifdef MSS_USB_DEVICE_ENABLED
    if (MSS_USB_CORE_MODE_DEVICE == MSS_USB_CIF_get_mode())
    {
        if (MSS_USB_CIF_cep_is_stall_sent())
        {
            status |= CTRL_EP_STALL_ERROR;
            MSS_USB_CIF_cep_clr_stall_sent();
            g_mss_usbd_cb.usbd_cep_setup(status);
        }
        else
        {
            if (MSS_USB_CIF_cep_is_setupend())
            {
                MSS_USB_CIF_cep_clr_setupend();

                if (!MSS_USB_CIF_cep_is_rxpktrdy())
                {
                    status |= CTRL_EP_SETUP_END_ERROR;
                }
                else
                {
                    status &= ~CTRL_EP_SETUP_END_ERROR;
                }

                g_mss_usbd_cb.usbd_cep_setup(status);
            }
            else
            {
                if (cep_state == MSS_USB_CTRL_EP_IDLE)
                {
                    if (MSS_USB_CIF_cep_is_rxpktrdy())
                    {
                        g_mss_usbd_cb.usbd_cep_setup(status);
                    }
                }
                else if (cep_state == MSS_USB_CTRL_EP_TX)
                {
                    g_mss_usbd_cb.usbd_cep_tx_complete(status);
                }
                else if (cep_state == MSS_USB_CTRL_EP_RX)
                {
                    if (MSS_USB_CIF_cep_is_rxpktrdy())
                    {
                        g_mss_usbd_cb.usbd_cep_rx(status);
                    }
                }
                else
                {
                    ASSERT(0);
                }
            }
        }
    }
#endif  /* MSS_USB_DEVICE_ENABLED */

#ifdef MSS_USB_HOST_ENABLED
    if (MSS_USB_CORE_MODE_HOST == MSS_USB_CIF_get_mode())
    {
        if (MSS_USBH_CIF_cep_is_rxstall_err())
        {
            status |= MSS_USB_EP_STALL_RCVD;
            MSS_USBH_CIF_cep_clr_rxstall_err();
        }
        if (MSS_USBH_CIF_cep_is_retry_err())
        {
            status |= MSS_USB_EP_NO_RESPONSE;
            MSS_USBH_CIF_cep_clr_retry_err();
        }
        if (MSS_USBH_CIF_cep_is_naktimeout_err())
        {
            status |= MSS_USB_EP_NAK_TOUT;
            MSS_USBH_CIF_cep_clr_naktimeout_err();
        }
        if (status == 0u)    /* No error was found */
        {
            status = MSS_USB_EP_TXN_SUCCESS;
        }
        g_mss_usbh_cb.usbh_cep(status);
    }
#endif /* MSS_USB_HOST_ENABLED */

}

/***************************************************************************//**
 * Routine to handle the interrupt on TX_EP
 */
static void MSS_USB_CIF_handle_tx_ep_irq
(
    uint16_t irq_num
)
{
    mss_usb_ep_num_t ep_num = MSS_USB_TX_EP_1;
    uint8_t status = 0u;

    while (irq_num)
    {
        irq_num >>= 1u;                                  /*EP1 starts from D1*/

        if (irq_num & MSS_USB_WORD_BIT_0_MASK)
        {
            MSS_USB_CIF_tx_ep_disable_irq(ep_num);
#ifdef MSS_USB_DEVICE_ENABLED
            if (MSS_USB_CORE_MODE_DEVICE == MSS_USB_CIF_get_mode())
            {
                status = MSS_USB_CIF_device_tx_errchk(ep_num);
                g_mss_usbd_cb.usbd_ep_tx_complete(ep_num,status);
            }
#endif  /* MSS_USB_DEVICE_ENABLED */

#ifdef MSS_USB_HOST_ENABLED
            if (MSS_USB_CORE_MODE_HOST == MSS_USB_CIF_get_mode())
            {
                status = MSS_USB_CIF_host_tx_errchk(ep_num);
                g_mss_usbh_cb.usbh_tx_complete((uint8_t)ep_num, status);
            }
#endif  /* MSS_USB_HOST_ENABLED */

            MSS_USB_CIF_tx_ep_enable_irq(ep_num);
        }
        status = 0u; /*resetting for next EP status*/
        ++ep_num;
    }
}

/***************************************************************************//**
 * Routine to handle the interrupt on RX EP
 */
static void MSS_USB_CIF_handle_rx_ep_irq
(
    uint16_t irq_num
)
{
    mss_usb_ep_num_t ep_num = MSS_USB_RX_EP_1;
    uint8_t status = 0u;

    while (irq_num)
    {
        irq_num >>= 1u;  /*EP1 starts from D1*/

        if (irq_num & MSS_USB_WORD_BIT_0_MASK)
        {
            MSS_USB_CIF_rx_ep_disable_irq(ep_num);
#ifdef MSS_USB_DEVICE_ENABLED
            if (MSS_USB_CORE_MODE_DEVICE == MSS_USB_CIF_get_mode())
            {
                status = MSS_USB_CIF_device_rx_errchk(ep_num);
                g_mss_usbd_cb.usbd_ep_rx(ep_num, status);
            }
#endif /* MSS_USB_DEVICE_ENABLED */

#ifdef MSS_USB_HOST_ENABLED
            if (MSS_USB_CORE_MODE_HOST == MSS_USB_CIF_get_mode())
            {
                status = MSS_USB_CIF_host_rx_errchk(ep_num);
                g_mss_usbh_cb.usbh_rx((uint8_t)ep_num, status);
            }
#endif /* MSS_USB_HOST_ENABLED */
            MSS_USB_CIF_rx_ep_enable_irq(ep_num);
        }
        status = 0u; /*resetting for next EP status*/
        ++ep_num;
    }
}

/***************************************************************************//**
 * Handler for DMA interrupt. Checks for the DMA channel on which interrupt has
 * Occurred and corresponding EP number then calls-back to upper layer to indicate
 * the event.
 */
uint8_t PLIC_usb_dma_IRQHandler(void)
{
    mss_usb_dma_channel_t dma_channel= MSS_USB_DMA_CHANNEL1;
    uint8_t status = 0;
    mss_usb_dma_dir_t dma_dir;
    mss_usb_ep_num_t ep_num;
    uint8_t dma_irq;
    uint32_t increamented_addr=0;

    dma_irq = MSS_USB_CIF_dma_read_irq();

    while (dma_irq)
    {
        if (dma_irq & MSS_USB_BYTE_BIT_0_MASK)
        {
            /* DMA Transfer for this channel is complete.Clear Start_transfer
               bit
             */
            MSS_USB_CIF_dma_stop_xfr(dma_channel);

            ep_num = (mss_usb_ep_num_t)MSS_USB_CIF_dma_get_epnum(dma_channel);
            dma_dir = (mss_usb_dma_dir_t)MSS_USB_CIF_dma_get_dir(dma_channel);

            if (MSS_USB_CIF_dma_is_bus_err(dma_channel))
            {
                status |=DMA_XFR_ERROR;
                MSS_USB_CIF_dma_clr_bus_err(dma_channel);
            }
            else
            {
                increamented_addr = MSS_USB_CIF_dma_read_addr(dma_channel);
                if (MSS_USB_DMA_READ == dma_dir)    /*TX EP*/
                {
                    if (MSS_USB_CIF_tx_ep_is_dma_enabled(ep_num))
                    {
                        MSS_USB_CIF_tx_ep_disable_dma(ep_num);
                    }
                }
#ifdef MSS_USB_HOST_ENABLED
                if (MSS_USB_CORE_MODE_HOST == MSS_USB_CIF_get_mode())
                {
                    /* Call the host mode logical layer driver callback
                       function
                     */
                    g_mss_usbh_cb.usbh_dma_handler(ep_num, dma_dir, status,
                                                             increamented_addr);
                }
#endif /* MSS_USB_HOST_ENABLED */
#ifdef MSS_USB_DEVICE_ENABLED
                if (MSS_USB_CORE_MODE_DEVICE == MSS_USB_CIF_get_mode())
                {
                    /* Call the device mode logical layer driver callback
                       function
                     */
                    g_mss_usbd_cb.usbd_dma_handler(ep_num, dma_dir, status,
                                                             increamented_addr);
                }
#endif /* MSS_USB_DEVICE_ENABLED */

            }
        }
        dma_channel++;
        dma_irq >>= 1u;
    }

    return EXT_IRQ_KEEP_ENABLED;
}

/***************************************************************************//**
 * Prepares the RX EP for receiving data as per parameters provided by upper layer
 */
void
MSS_USB_CIF_rx_ep_read_prepare
(
    mss_usb_ep_num_t ep_num,
    uint8_t* buf_addr,
    uint8_t dma_enable,
    mss_usb_dma_channel_t dma_channel,
    mss_usb_xfr_type_t xfr_type,
    uint32_t xfr_length
)
{
    /*
     * Fixed Buffer overwriting issue found with printer driver and issue with
     *  interrupt transfer with DMA by moving the location of interrupt enable
     *  function
     */
    if (DMA_ENABLE == dma_enable)
    {
        /*Make sure that address is Modulo-4.Bits D0-D1 are read only.*/
        ASSERT(!(((ptrdiff_t)buf_addr) & 0x00000002U));

        MSS_USB_CIF_dma_write_addr(dma_channel, (ptrdiff_t)buf_addr);

        /*
         * DMA Count register will be loaded after receive interrupt occurs.
         * Mode need to be set every time since M1 to M0 transition might have
         * happened for "short packet".
         */
         if (MSS_USB_XFR_BULK == xfr_type)
        {
            MSS_USB_CIF_rx_ep_set_dma_mode1(ep_num);
            MSS_USB_CIF_rx_ep_set_autoclr(ep_num);
            MSS_USB_CIF_rx_ep_enable_dma(ep_num);

            MSS_USB_CIF_dma_write_count(dma_channel,
                                        xfr_length);

            /* Handling single NULL packet reception */
            if (0u != xfr_length )
            {
                MSS_USB_CIF_dma_start_xfr(dma_channel);
            }
        }
        else
        {
            MSS_USB_CIF_rx_ep_clr_autoclr(ep_num);
            MSS_USB_CIF_rx_ep_set_dma_mode0(ep_num);
            MSS_USB_CIF_rx_ep_disable_dma(ep_num);
        }
    }
}

/***************************************************************************//**
 * Writes packet on TX EP
 */
void
MSS_USB_CIF_ep_write_pkt
(
    mss_usb_ep_num_t ep_num,
    uint8_t* buf_addr,
    uint8_t dma_enable,
    mss_usb_dma_channel_t dma_channel,
    mss_usb_xfr_type_t xfr_type,
    uint32_t xfr_length,
    uint32_t txn_length
)
{
    if (ep_num && (buf_addr != 0))
    {
        if (DMA_ENABLE == dma_enable)
        {
            /* Make sure that address is Modulo-4.Bits D0-D1 are read only.*/
            ASSERT(!(((ptrdiff_t)buf_addr) & 0x00000002u));

            MSS_USB_CIF_dma_write_addr(dma_channel,(ptrdiff_t)(buf_addr));

            if (MSS_USB_XFR_BULK == xfr_type)
            {
                MSS_USB_CIF_tx_ep_enable_dma(ep_num);

                 /*
                  * DMA-m1 will take care of transferring 'xfr_length' data
                  * as IN packets arrive.
                  * DMA interrupt will occur when all TxMaxPkt size packets
                  * are transferred.
                  */
                MSS_USB_CIF_dma_write_count(dma_channel, xfr_length);
            }
            else
            {
                /*
                 * DMA Enable bit in TxCSR is not needed. If set,TX EP
                 * Interrupt will not occur.
                 */
                MSS_USB_CIF_tx_ep_disable_dma(ep_num);

                /* Transfer only one packet with DMA-m0 */
                MSS_USB_CIF_dma_write_count(dma_channel, txn_length);
           }

             /*
              * This will start DMA transfer.
              * TODO: For Null transfer DMA is not needed, but not setting
              * TxPktRdy bit here, is not invoking EP interrupt.
              * EP interrupt does get called when Null DMA transfer is done.
              */
            MSS_USB_CIF_dma_start_xfr(dma_channel);


            /*
             * DMA interrupt will occur when all bytes are written to FIFO
             * TxPktRdy should be set when the DMA interrupt occurs.
             */
        }
        else    /* no DMA */
        {
            MSS_USB_CIF_load_tx_fifo(ep_num,
                                     buf_addr,
                                     txn_length);
            MSS_USB_CIF_tx_ep_set_txpktrdy(ep_num);
        }
    }
}

/***************************************************************************//**
 * Configures DMA for data transfer operations.
 */
void
MSS_USB_CIF_configure_ep_dma
(
    mss_usb_dma_channel_t dma_channel,
    mss_usb_dma_dir_t dma_dir,
    mss_usb_dma_mode_t dma_mode,
    mss_usb_dma_burst_mode_t burst_mode,
    mss_usb_ep_num_t ep_num,
    uint32_t buf_addr
)
{
    MSS_USB_CIF_dma_assign_to_epnum(dma_channel, ep_num);
    MSS_USB_CIF_dma_set_dir(dma_channel, dma_dir);
    MSS_USB_CIF_dma_set_mode(dma_channel, dma_mode);
    MSS_USB_CIF_dma_set_burst_mode(dma_channel, burst_mode);
    MSS_USB_CIF_dma_write_addr(dma_channel, buf_addr);
    MSS_USB_CIF_dma_enable_irq(dma_channel);
}

/***************************************************************************//**
 * Configures the TX EP for data transfer operations as per the parameters
 * provided by upper layer.
 */
void
MSS_USB_CIF_tx_ep_configure

(
    mss_usb_ep_t* core_ep
)
{
    uint8_t dpb = 1u;
    mss_usb_dma_mode_t mode;

    if (DPB_ENABLE == core_ep->dpb_enable)
    {
        dpb = 2u;
    }

    MSS_USB_CIF_tx_ep_set_fifo_size(core_ep->num,
                                    ((core_ep->fifo_size) / dpb),
                                    core_ep->dpb_enable);

    MSS_USB_CIF_tx_ep_set_fifo_addr(core_ep->num, core_ep->fifo_addr);

    if (DPB_ENABLE == core_ep->dpb_enable)
    {
        MSS_USB_enable_tx_ep_dpb(core_ep->num);
    }
    else if (DPB_DISABLE == core_ep->dpb_enable)
    {
        MSS_USB_disable_tx_ep_dpb(core_ep->num);
    }
    else
    {
        ASSERT(0);
    }

    MSS_USB_CIF_tx_ep_set_max_pkt(core_ep->num,
                                  core_ep->xfr_type,
                                  core_ep->max_pkt_size,
                                  core_ep->num_usb_pkt);

    MSS_USB_CIF_tx_ep_clr_data_tog(core_ep->num);

    if (DMA_ENABLE == core_ep->dma_enable)
    {
        if (MSS_USB_XFR_BULK == core_ep->xfr_type )
        {
            MSS_USB_CIF_tx_ep_set_dma_mode1(core_ep->num);
            MSS_USB_CIF_tx_ep_enable_dma(core_ep->num);
            mode = MSS_USB_DMA_MODE1;
        }
        else
        {
            /*
             * DMA_ENable bit in TXCSRL is not required to be set for m0. if it
             * is set TX interrupt would not occur.
             */
            MSS_USB_CIF_tx_ep_set_dma_mode0(core_ep->num);
            MSS_USB_CIF_tx_ep_disable_dma(core_ep->num);
            mode = MSS_USB_DMA_MODE0;
        }

        MSS_USB_CIF_configure_ep_dma(core_ep->dma_channel,
                                     MSS_USB_DMA_READ,
                                     mode,
                                     MSS_USB_DMA_BURST_MODE3,
                                     core_ep->num,
                                     (ptrdiff_t)(core_ep->buf_addr));
    }

    MSS_USB_CIF_tx_ep_enable_irq(core_ep->num);
}

/***************************************************************************//**
 * Configures the RX EP for data transfer operations as per the parameters
 * provided by upper layer.
 */
void
MSS_USB_CIF_rx_ep_configure

(
    mss_usb_ep_t* core_ep
)
{
    uint8_t dpb = 1u;
    mss_usb_dma_mode_t mode;
    if (DPB_ENABLE == core_ep->dpb_enable)
    {
        dpb = 2u;
    }

    MSS_USB_CIF_rx_ep_set_fifo_size(core_ep->num,
                                     ((core_ep->fifo_size) / dpb),
                                     core_ep->dpb_enable);

    MSS_USB_CIF_rx_ep_set_fifo_addr(core_ep->num,
                                     core_ep->fifo_addr);

    if (DPB_ENABLE == core_ep->dpb_enable)
    {
        MSS_USB_CIF_enable_rx_ep_dpb(core_ep->num);
    }
    else if (DPB_DISABLE == core_ep->dpb_enable)
    {
        MSS_USB_CIF_disable_rx_ep_dpb(core_ep->num);
    }
    else
    {
        ASSERT(0);
    }

    MSS_USB_CIF_rx_ep_set_max_pkt(core_ep->num,
                                  core_ep->xfr_type,
                                  core_ep->max_pkt_size,
                                  core_ep->num_usb_pkt);

    MSS_USB_CIF_rx_ep_clr_data_tog(core_ep->num);
    MSS_USB_CIF_rx_ep_clr_rxpktrdy(core_ep->num);

    if (DMA_ENABLE == core_ep->dma_enable)
    {
        if (MSS_USB_XFR_BULK == core_ep->xfr_type)
        {
            MSS_USB_CIF_rx_ep_set_dma_mode1(core_ep->num);
            MSS_USB_CIF_rx_ep_enable_dma(core_ep->num);
            mode = MSS_USB_DMA_MODE1;
        }
        else
        {
            /*
             * DMA_ENable bit in RXCSRL is not required to be set in m0. if it is
             * set RX interrupt would not occur.
             */
            MSS_USB_CIF_rx_ep_set_dma_mode0(core_ep->num);
            MSS_USB_CIF_rx_ep_disable_dma(core_ep->num);
            mode = MSS_USB_DMA_MODE0;
        }

        MSS_USB_CIF_configure_ep_dma(core_ep->dma_channel,
                                     MSS_USB_DMA_WRITE,
                                     mode,
                                     MSS_USB_DMA_BURST_MODE3,
                                     core_ep->num,
                                     (ptrdiff_t)(core_ep->buf_addr));
    }

    MSS_USB_CIF_rx_ep_enable_irq(core_ep->num);
}

/***************************************************************************//**
 * Starts sending Test packet as specified in the USB2.0
 * This is USB-IF certification requirement.
 */
void MSS_USB_CIF_start_testpacket(void)
{
    uint8_t test_pkt[53] =
    {
    0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
    0xAAU, 0xAAU, 0xAAU, 0xAAU, 0xAAU, 0xAAU, 0xAAU, 0xAAU, 0xEEU,
    0xEEU, 0xEEU, 0xEEU, 0xEEU, 0xEEU, 0xEEU, 0xEEU, 0xFEU, 0xFFU,
    0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU,
    0xFFU, 0x7FU, 0xBFU, 0xDFU, 0xEFU, 0xF7U, 0xFBU, 0xFDU, 0xFCU,
    0x7EU, 0xBFU, 0xDFU, 0xEFU, 0xF7U, 0xFBU, 0xFDU, 0x7EU
    };
    MSS_USB_CIF_load_tx_fifo(MSS_USB_CEP, test_pkt, 53u);
    MSS_USB_CIF_start_testpacket_bit();
    MSS_USB_CIF_cep_set_txpktrdy();
    MSS_USB_CIF_cep_disable_irq();
}

#ifdef MSS_USB_HOST_ENABLED
static uint8_t MSS_USB_CIF_host_rx_errchk(mss_usb_ep_num_t ep_num)
{
    uint8_t status = 0u;

    if (MSS_USBH_CIF_rx_ep_is_rxpktrdy(ep_num))
    {
        status = 0u;
    }
    if (MSS_USBH_CIF_rx_ep_is_rxstall_err(ep_num))
    {
        status |= MSS_USB_EP_STALL_RCVD;
        MSS_USBH_CIF_rx_ep_clr_rxstall_err(ep_num);
    }
    if (MSS_USBH_CIF_rx_ep_is_naktimeout_err(ep_num))
    {
        status |= MSS_USB_EP_NAK_TOUT;
        /* Not clearing NAKTIMEOUT error here. Application may want to abort
         * transfer. Clearing it here makes Scheduler keep trying the transfer
         */
    }
    if (MSS_USBH_CIF_rx_ep_is_retry_err(ep_num))
    {
        status |= MSS_USB_EP_NO_RESPONSE;
        MSS_USBH_CIF_rx_ep_clr_retry_err(ep_num);
    }

    return (status);
}

static uint8_t MSS_USB_CIF_host_tx_errchk(mss_usb_ep_num_t ep_num)
{
    uint8_t status = 0;

    if (MSS_USBH_CIF_tx_ep_is_retry_err(ep_num))
    {
        status |= MSS_USB_EP_NO_RESPONSE;
        MSS_USBH_CIF_tx_ep_clr_retry_err(ep_num);
    }
    if (MSS_USBH_CIF_tx_ep_is_rxstall_err(ep_num))
    {
        status |= MSS_USB_EP_STALL_RCVD;
        MSS_USBH_CIF_tx_ep_clr_rxstall_err(ep_num);
    }
    if (MSS_USBH_CIF_tx_ep_is_naktimeout_err(ep_num))
    {
        status |= MSS_USB_EP_NAK_TOUT;
        /* Not clearing NAKTIMEOUT error here. Application may want to abort
         * transfer. Clearing it here makes Scheduler keep trying the transfer
         */
    }

    return(status);
}
#endif /* MSS_USB_HOST_ENABLED */

#ifdef MSS_USB_DEVICE_ENABLED
static uint8_t MSS_USB_CIF_device_rx_errchk(mss_usb_ep_num_t ep_num)
{
    uint8_t status = 0u;

    if (MSS_USB_CIF_rx_ep_is_overrun(ep_num))
    {
        status |= RX_EP_OVER_RUN_ERROR;
        MSS_USB_CIF_rx_ep_clr_overrun(ep_num);
    }
    if (MSS_USB_CIF_rx_ep_is_stall_sent_bit(ep_num))
    {
        status |= RX_EP_STALL_ERROR;
        /*
         * "sent stall" bit should be cleared."Send Stall" bit is still set.
         * it should be cleared via Clear feature command or reset"
         */
        MSS_USB_CIF_rx_ep_clr_stall_sent_bit(ep_num);
    }
    if (MSS_USB_CIF_rx_ep_is_dataerr(ep_num))
    {
        /* This error will be cleared when RxPktRdy bit is cleared.
         */
        status |= RX_EP_DATA_ERROR;

    }
#if 0
    /*
     * PID error and INCOMP error should be checked only in ISO transfers.
     * This should be moved to logical layer
     */
    if (MSS_USB_CIF_rx_ep_is_piderr(ep_num))
    {
        status |= RX_EP_PID_ERROR;
        /* Data sheet doesn't mention about how this error bit is cleared
         * Assuming that this will be cleared when RxPKTRdy is cleared.*/
    }
    if (MSS_USB_CIF_rx_ep_is_isoincomp(ep_num))
    {
        status |= RX_EP_ISO_INCOMP_ERROR;
        /* This error will be cleared when RxPktRdy bit is cleared.*/
    }
#endif /* if 0 */

    return(status);
}

static uint8_t MSS_USB_CIF_device_tx_errchk(mss_usb_ep_num_t ep_num)
{
    uint8_t status = 0u;

    if (MSS_USB_CIF_tx_ep_is_underrun(ep_num))
    {
        /* Under-run errors should happen only for ISO endpoints.*/
        status |= TX_EP_UNDER_RUN_ERROR;
        MSS_USB_CIF_tx_ep_clr_underrun(ep_num);
    }
    if (MSS_USB_CIF_tx_ep_is_stall_sent_bit(ep_num))
    {
        status |= TX_EP_STALL_ERROR;
        MSS_USB_CIF_tx_ep_clr_stall_sent_bit(ep_num);
    }

    return(status);
}
#endif /* MSS_USB_DEVICE_ENABLED */

#ifdef __cplusplus
}
#endif
