/*
* @file hv_drv_UsbMusbCore.h
* @brief Header file of musb core.
*
* @author HiView SoC Software Team
* @version 1.0.0
* @date 2022-06-15
*/

#ifndef __HV_DRV_USB_MUSB_CORE_H 
#define __HV_DRV_USB_MUSB_CORE_H

#include "hv_drv_UsbCh9.h"
#include "hv_drv_UsbCoreDevice.h"
#include "hv_drv_UsbErrNo.h"
#include "hv_drv_UsbBitWise.h"

struct musb;
struct musb_hw_ep;

enum {
    MUSB_PORT_MODE_HOST    = 1,
    MUSB_PORT_MODE_GADGET,
    MUSB_PORT_MODE_DUAL_ROLE,
};

#define MUSB_HST_MODE(_musb)\
        { (_musb)->is_host = true; }
#define MUSB_DEV_MODE(_musb) \
        { (_musb)->is_host = false; }

#include "hv_drv_UsbMusbRegs.h"
#include "hv_drv_UsbMusbDma.h"
#include "hv_drv_UsbMusbIO.h"

#define dev_dbg
#define IRQ_NONE 0
#define IRQ_HANDLED 1

#define MUSB_EP0_FIFOSIZE   64  /* This is non-configurable */
#define MUSB_HWVERS_2000    0x800

#define MUSB_TX_DMA_USED
#define MUSB_RX_DMA_USED

enum musb_mode {
    MUSB_UNDEFINED = 0,
    MUSB_HOST,          /* A or Mini-A connector */
    MUSB_PERIPHERAL,    /* B or Mini-B connector */
    MUSB_OTG            /* Mini-AB connector */
};
#pragma pack(1)
enum musb_fifo_style {
    FIFO_RXTX,
    FIFO_TX,
    FIFO_RX
};

enum musb_buf_mode {
    BUF_SINGLE,
    BUF_DOUBLE
};
#pragma pack()

struct musb_fifo_cfg {
    u8 hw_ep_num;
    enum musb_fifo_style    style;
    enum musb_buf_mode  mode;
    u16 maxpacket;
};

#define MUSB_EP_FIFO(ep, st, m, pkt)        \
{                       \
    .hw_ep_num  = ep,           \
    .style = st,            \
    .mode = m,          \
    .maxpacket = pkt,           \
}

/* host side ep0 states */
#pragma pack(1)
enum musb_h_ep0_state {
    MUSB_EP0_IDLE,
    MUSB_EP0_START,         /* expect ack of setup */
    MUSB_EP0_IN,            /* expect IN DATA */
    MUSB_EP0_OUT,           /* expect ack of OUT DATA */
    MUSB_EP0_STATUS,        /* expect ack of STATUS */
};
#pragma pack()
/*
 * struct musb_hw_ep - endpoint hardware (bidirectional)
 *
 * Ordered slightly for better cacheline locality.
 */
struct musb_hw_ep {
    void *fifo;

    /* index in musb->endpoints[]  */
    u8  epnum;

    struct musb *musb;

    /* hardware configuration, possibly dynamic */
    boolean is_shared_fifo;
    boolean tx_double_buffered;
    boolean rx_double_buffered;
    u16 max_packet_sz_tx;
    u16 max_packet_sz_rx;

    struct dma_channel *tx_channel;
    struct dma_channel *rx_channel;

    struct musb_xfer    *xfer;

    u8  rx_reinit:1;
    u8  tx_reinit:1;
    u8  bundled:1;
};


/**
 * struct musb_platform_ops - Operations passed to musb_core by HW glue layer
 * @quirks:    flags for platform specific quirks
 * @enable:    enable device
 * @disable:    disable device
 * @ep_offset:    returns the end point offset
 * @ep_select:    selects the specified end point
 * @fifo_mode:    sets the fifo mode
 * @fifo_offset: returns the fifo offset
 * @readb:    read 8 bits
 * @writeb:    write 8 bits
 * @readw:    read 16 bits
 * @writew:    write 16 bits
 * @readl:    read 32 bits
 * @writel:    write 32 bits
 * @read_fifo:    reads the fifo
 * @write_fifo:    writes to fifo
 * @init:    turns on clocks, sets up platform-specific registers, etc
 * @exit:    undoes @init
 * @set_mode:    forcefully changes operating mode
 * @try_ilde:    tries to idle the IP
 * @set_vbus:    forces vbus status
 * @adjust_channel_params: pre check for standard dma channel_program func
 */
struct musb_platform_ops {
#define MUSB_DMA_UX500        BIT(6)
#define MUSB_DMA_CPPI41        BIT(5)
#define MUSB_DMA_CPPI        BIT(4)
#define MUSB_DMA_TUSB_OMAP    BIT(3)
#define MUSB_DMA_INVENTRA    BIT(2)
#define MUSB_IN_TUSB        BIT(1)
#define MUSB_INDEXED_EP        BIT(0)
    u32    quirks;

    int    (*init)(struct musb *musb);
    int    (*exit)(struct musb *musb);

    void   (*enable)(struct musb *musb);
    void   (*disable)(struct musb *musb);

    u32    (*ep_offset)(u8 epnum, u16 offset);
    void   (*ep_select)(u8 epnum);
    u16    fifo_mode;
    u32    (*fifo_offset)(u8 epnum);
    u8     (*readb)(const void *addr, unsigned offset);
    void   (*writeb)(void *addr, unsigned offset, u8 data);
    u16    (*readw)(const void *addr, unsigned offset);
    void   (*writew)(void *addr, unsigned offset, u16 data);
    u32    (*readl)(const void *addr, unsigned offset);
    void   (*writel)(void *addr, unsigned offset, u32 data);
    void   (*read_fifo)(struct musb_hw_ep *hw_ep, u16 len, u8 *buf);
    void   (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf);
    int    (*reset)(struct musb *musb);

    void   (*phy_reset) (void);
    void   (*set_vbus)(struct musb *musb, int on);
};

struct musb_hdrc_platform_data {
    /* MUSB_HOST, MUSB_PERIPHERAL, or MUSB_OTG */
    u8        mode;

    /* for clk_get() */
    const char    *clock;

    /* (HOST or OTG) switch VBUS on/off */
    int        (*set_vbus)(struct device *dev, int is_on);

    /* (HOST or OTG) mA/2 power supplied on (default = 8mA) */
    u8        power;

    /* (PERIPHERAL) mA/2 max power consumed (default = 100mA) */
    u8        min_power;

    /* (HOST or OTG) msec/2 after VBUS on till power good */
    u8        potpgt;

    /* (HOST or OTG) program PHY for external Vbus */
    unsigned    extvbus:1;

    /* Power the device on or off */
    int        (*set_power)(int state);

    /* MUSB configuration-specific details */
    struct musb_hdrc_config    *config;

    /* Architecture specific board data    */
    void        *board_data;

    /* Platform specific struct musb_ops pointer */
    const void    *platform_ops;
};


/*
 * struct musb - Driver instance data.
 */
struct musb {
    struct device *controller;
    u16 hwvers;

    void        *mregs;
    u8          is_multipoint;
    u16         intrrxe;
    u16         intrtxe;

    const struct musb_platform_ops *ops;

    void        (*isr)(void *);

    /* this hub status bit is reserved by USB 2.0 and not seen by usbcore */
#define MUSB_PORT_STAT_RESUME   (1 << 31)

    u32         port1_status;
    int            port_mode;    /* MUSB_PORT_MODE_* */

    enum musb_h_ep0_state ep0_stage;

    /* bulk traffic normally dedicates endpoint hardware, and each
    * direction has its own ring of host side endpoints.
    * we try to progress the transfer at the head of each endpoint's
    * queue until it completes or NAKs too much; then we try the next
    * endpoint.
    */
    struct musb_hw_ep *bulk_ep;

    struct musb_dma_controller *musb_dma_controller;
    struct dma_controller   *dma_controller;

    /* passed down from chip/board specific irq handlers */
    u8 int_usb;
    u16 int_rx;
    u16 int_tx;
    u8 int_hsdma;

    struct usb_hcd *hcd;    /* the usb hcd */

    struct musb_hw_ep    endpoints[16];
#define control_ep endpoints    /* &endpoints[0] */

    u16 epmask;
    u8 nr_endpoints;

    int (*board_set_power) (int state);

    u16 is_tx_dma_used:1;
    u16 is_rx_dma_used:1;
    u16 is_host:1;

    /* active means connected and not suspended */
    u16 is_active:1;
    u16 dma_mask:1;

    u16 ignore_disconnect:1;  /* during bus resets */

    u16 bulk_split:1;
#define can_bulk_split(musb,type) \
    (((type) == USB_ENDPOINT_XFER_BULK) && (musb)->bulk_split)

    u16 bulk_combine:1;
#define can_bulk_combine(musb,type) \
    (((type) == USB_ENDPOINT_XFER_BULK) && (musb)->bulk_combine)
};

#define MUSB_EP_FIFO_SINGLE(ep, st, pkt) \
    MUSB_EP_FIFO(ep, st, BUF_SINGLE, pkt)

#define MUSB_EP_FIFO_DOUBLE(ep, st, pkt) \
    MUSB_EP_FIFO(ep, st, BUF_DOUBLE, pkt)


#define is_host_enabled(musb)       (1)
#define is_peripheral_active(musb)  (0)
#define is_host_active(musb)        (musb->is_active||musb->ignore_disconnect)
#define is_cppi_enabled()           (0)

#define USB_URB_COUNT 4
#define MUSB_EP_OFFSET(epnum,offset)    (0x100 + (epnum)*0x10 + (offset) - 0x10)


INLINE boolean musb_hw_detect(void) {
    return (musb_readb(MUSB_DEVCTL) & MUSB_DEVCTL_HM);
}

INLINE boolean musb_host_mode_detect(void) {
    return (!(musb_readb(MUSB_DEVCTL) & MUSB_DEVCTL_BDEVICE));
}

int musb_read_fifosize(struct musb *musb, struct musb_hw_ep *hw_ep, u8 epnum);

INLINE void musb_configure_ep0(struct musb *musb) {
    musb->endpoints[0].max_packet_sz_tx = MUSB_EP0_FIFOSIZE;
    musb->endpoints[0].max_packet_sz_rx = MUSB_EP0_FIFOSIZE;
    musb->endpoints[0].is_shared_fifo = true;
}

INLINE struct musb *dev_to_musb(struct device *dev) {
    return (struct musb *)dev_get_drvdata(dev);
}

extern boolean musb_gpio_toggle(void);
extern void musb_reactive(void);
extern void musb_interrupt(struct musb * musb);
extern void musb_create(void);
extern void musb_start(struct musb *musb);
extern void musb_stop(struct musb *musb);
extern void musb_recovery_host_mode(void);
extern void musb_connect_handle(struct musb *musb);
extern void musb_disconnect_handle(struct musb * musb);
extern void musb_controller_suspend(boolean suspend);

// disable/enable USBC interrupt. They shall be used to
// protect data access between task and IRQ.
extern void musb_interrput_switch(u8 enable);

INLINE u32 musb_ep_offset(struct musb *musb, u8 epnum, u16 offset) {
    USB_ASSERT(musb->ops->ep_offset);
    return musb->ops->ep_offset(epnum, offset);
}

INLINE void musb_platform_set_vbus(struct musb *musb, int is_on) {
    if (musb->ops->set_vbus)
        musb->ops->set_vbus(musb, is_on);
}

INLINE void musb_platform_enable(struct musb *musb) {
    if (musb->ops->enable)
        musb->ops->enable(musb);
}

INLINE void musb_platform_disable(struct musb *musb) {
    if (musb->ops->disable)
        musb->ops->disable(musb);
}

INLINE int musb_platform_reset(struct musb *musb) {
    if (!musb->ops->reset)
        return -EINVAL;

    return musb->ops->reset(musb);
}

INLINE int musb_platform_init(struct musb *musb) {
    if (!musb->ops->init)
        return -EINVAL;

    return musb->ops->init(musb);
}

INLINE int musb_platform_exit(struct musb *musb) {
    if (!musb->ops->exit)
        return -EINVAL;

    return musb->ops->exit(musb);
}

#endif    /* __MUSB_CORE_H__ */
