/**
 * @file hv_drv_UsbMusbDrv.c
 * @brief This is musb controller platform driver.
 * @details 
 * @author HiView SoC Software Team
 * @version 0.0.1
 * @date 20230417
 * @copyright Copyright(c),2022-9, Hiview Software. All rights reserved.
 * @par History:
 * <table>
 * <tr><th>Author                         <th>Date             <th>Details
 * <tr><td>HiView SoC Software Team   <td>20230417       <td>Create and first Version.
 * </table>
 **/
 
#include "hv_drv_UsbCoreCommonUsb.h"
#include "hv_drv_UsbCoreUtils.h"
#include "hv_drv_UsbMusbCore.h"
#include "hv_drv_UsbMusbRegs.h"

static u32 drv_musb_fifo_offset(u8 epnum) {
    return (0x20 + epnum * 4);
}

static u32 drv_musb_ep_offset(u8 epnum, u16 offset) {
    return (0x100 + (epnum)*0x10 + (offset) - 0x10);
}

static void    drv_musb_set_vbus(struct musb *musb, int on) {
    if (on) {
        musb->port_mode = MUSB_PORT_MODE_HOST;
        MUSB_HST_MODE(musb);
        musb->is_active = 1;
    } else {
        musb->port_mode = 0;
        musb->is_active = 0;
        MUSB_DEV_MODE(musb);
    }
}


static void drv_musb_phy_reset(void) {
    volatile unsigned int vbusReg = 0;
    
#if HV_CONFIG_USB_SPEED == USBSPEED_LOW
      //USB_LOGI("####LowSpeed.\n");
      USB_W32(MUSB_REGS_PHY_ADDR_BASE + 0x14,0x02400000); 
      USB_W32(MUSB_REGS_PHY_ADDR_BASE + 0x10,0x00000000);

      USB_W32(0x110900f4,0x00000001);
      USB_W32(MUSB_REGS_ADDR_BASE + 0x00,0x00000100);
      USB_W32(MUSB_REGS_ADDR_BASE + 0x60,0x00000001);

      while(vbusReg != 0x20){
          vbusReg = USB_R32(MUSB_REGS_PHY_ADDR_BASE + 0x1c) & 0x20;
      }

      USB_W32(MUSB_REGS_PHY_ADDR_BASE + 0x14,0x01c00000);
      USB_R32(MUSB_REGS_ADDR_BASE + 0x60);

#elif HV_CONFIG_USB_SPEED == USBSPEED_FULL
      //USB_LOGI("####FullSpeed.\n");

      USB_W32(MUSB_REGS_PHY_ADDR_BASE + 0x14,0x02400000);
      USB_W32(MUSB_REGS_PHY_ADDR_BASE + 0x10,0x00000000);
      
      USB_W32(MUSB_REGS_ADDR_BASE + 0x00,0x00000100);
      USB_W32(MUSB_REGS_ADDR_BASE + 0x60,0x00000001);

      while(vbusReg != 0x20){
          vbusReg = USB_R32(MUSB_REGS_PHY_ADDR_BASE + 0x1c) & 0x20;
      }

      USB_W32(MUSB_REGS_PHY_ADDR_BASE + 0x14,0x01c00000);
      USB_R32(MUSB_REGS_ADDR_BASE + 0x60);

#elif HV_CONFIG_USB_SPEED == USBSPEED_HIGH
      //USB_LOGI("####HighSpeed.\n");

      USB_W32(MUSB_REGS_PHY_ADDR_BASE + 0x14,0x02400000);
      USB_W32(MUSB_REGS_PHY_ADDR_BASE + 0x10,0x00000000);
      
      USB_W32(MUSB_REGS_ADDR_BASE + 0x00,0x00002100);
      USB_W32(MUSB_REGS_ADDR_BASE + 0x60,0x00000001);

      while(vbusReg != 0x20){
          vbusReg = USB_R32(MUSB_REGS_PHY_ADDR_BASE + 0x1c) & 0x20;
      }

      USB_W32(MUSB_REGS_PHY_ADDR_BASE + 0x14,0x01c00000);
      USB_R32(MUSB_REGS_ADDR_BASE + 0x60);
    
#else
    USB_LOGI("PHY no support speed.\n");
#endif
}

INLINE void drv_musb_set_clock(int flag) {
}

static int drv_musb_reset(struct musb *musb) {
    u16 temp;   /* throw away all interrupts */

    /*ip reset*/
    USB_W32_MASK(MUSB_REGS_IP_RESET_ADDR,0x3<<19,0x1<<19);
    USB_MSleep(200);
    USB_W32_MASK(MUSB_REGS_IP_RESET_ADDR,0x3<<19,0x3<<19);
    USB_MSleep(200);
    musb->ops->phy_reset();
    musb_start(musb);

    USB_MSleep(200);
    temp = musb_readb(MUSB_INTRUSB);
    temp = musb_readw(MUSB_INTRTX);
    temp = musb_readw(MUSB_INTRRX);
    //temp = temp;    /* cheat compiler */
    USB_MSleep(100);

    return 0;
}


static int drv_musb_init(struct musb *musb) {
    /*ip reset*/
    USB_W32_MASK(MUSB_REGS_IP_RESET_ADDR,0x3<<19,0x1<<19);
    USB_W32_MASK(MUSB_REGS_IP_RESET_ADDR,0x3<<19,0x3<<19);

    musb->ops->phy_reset();

    return 0;
}

static int drv_musb_exit(struct musb *musb) {
    return 0;
}

static struct musb_platform_ops drv_usb_ops = {
    0, //u32    quirks;
    drv_musb_init, //int    (*init)(struct musb *musb);
    drv_musb_exit, //int    (*exit)(struct musb *musb);
    NULL, //void (*enable)(struct musb *musb);
    NULL, //void (*disable)(struct musb *musb);
    drv_musb_ep_offset, //u32    (*ep_offset)(u8 epnum, u16 offset);
    NULL, //void (*ep_select)(u8 epnum);
    0, //u16    fifo_mode;
    drv_musb_fifo_offset, //u32    (*fifo_offset)(u8 epnum);
    NULL, //u8    (*readb)(const void *addr, unsigned offset);
    NULL, //void (*writeb)(void *addr, unsigned offset, u8 data);
    NULL, //u16    (*readw)(const void *addr, unsigned offset);
    NULL, //void (*writew)(void *addr, unsigned offset, u16 data);
    NULL, //u32    (*readl)(const void *addr, unsigned offset);
    NULL, //void (*writel)(void *addr, unsigned offset, u32 data);
    NULL, //void (*read_fifo)(struct musb_hw_ep *hw_ep, u16 len, u8 *buf);
    NULL, //void (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf);
    drv_musb_reset, //int    (*reset)(struct musb *musb);
    drv_musb_phy_reset, //void (*phy_reset) (void);
    drv_musb_set_vbus, //void (*set_vbus)(struct musb *musb, int on);
};

static int drv_musb_set_power(int state) {
    return 0;
}

void drv_plat_init(struct platform_device *platdev) {
    struct musb_hdrc_platform_data *hdrc;
    struct musb_platform_ops *ops;

    hdrc = (struct musb_hdrc_platform_data*)USB_MALLOC(sizeof (*hdrc));
    HV_MEMSET((void*)hdrc, 0, sizeof(struct musb_hdrc_platform_data));
    ops = &drv_usb_ops;
    hdrc->platform_ops = ops;
    hdrc->set_power = drv_musb_set_power;

    platdev_set_platdata(platdev, hdrc);
}

void drv_plat_exit(struct platform_device *platdev) {
    struct musb_hdrc_platform_data *hdrc;

    hdrc = (struct musb_hdrc_platform_data*)platdev_get_platdata(platdev);
    if (hdrc) {
        hdrc->platform_ops = NULL;
        if(hdrc != NULL)
        {
            USB_FREE(hdrc);
            hdrc = NULL;
        }
    }
}
