/**
 * @file hv_drv_Eeprom.c
 * @brief eeprom driver layer file.
 * @details This file provides the following functions: \n
 *          (1) eeprom write\n
 *          (2) eeprom read\n
 *          (3) eeprom protect \n
 *
 * @author HiView SoC Software Team
 * @version 1.0.0
 * @date 2023-05-08
 * @copyright Copyright(c),2023-5, Hiview Software. All rights reserved.
 * @par History:
 * <table>
 * <tr><th>Author       <th>Date            <th>Change Description
 * <tr><td>HiView SoC Software Team     <td>2023-05-08      <td>init
 * <tr><td>HiView SoC Software Team     <td>2023-06-27      <td>add simi2c、mi2c、wp control
 * </table>
 */
#include "Common/hv_comm_DataType.h"
#include "hv_comm_Define.h"
#include "hv_cal_I2c.h"
#include "hv_drv_I2c.h"
#include "hv_chip_Config.h"
#include "hv_vos_Comm.h"
#include "hv_drv_Eeprom.h"
#include "hv_comm_Define.h"
#include "hv_drv_GpioSimI2c.h"
#include "hv_cal_MI2c.h"
#include "hv_drv_Gpio.h"

static HvCalI2c *apSelf[I2C_PORT_MAX] = {NULL, NULL};


typedef enum _EepromType
{
    E2P_TYPE_1K_16PAGES_8BYTE = 0,
    E2P_TYPE_2K_32PAGES_8BYTE,
    E2P_TYPE_2K_16PAGES_16BYTE,
    E2P_TYPE_4K_32PAGES_16BYTE,
    E2P_TYPE_8K_64PAGES_16BYTE,
    E2P_TYPE_16K_128PAGES_16BYTE,
    E2P_TYPE_32K_128PAGES_32BYTE,
    E2P_TYPE_64K_256PAGES_32BYTE,
} EepromType;

static void I2C_E2pType(UCHAR8 ucBus, USHORT16 *pAddrByteSize, USHORT16 *pBytesPerPage, USHORT16 *pTotalPageNum)
{
    USHORT16 usAddrByteSize = 1;
    USHORT16 usBytesPerPage = 8;
    USHORT16 usTotalPageNum = 16;
    EepromType enEepromType = E2P_TYPE_1K_16PAGES_8BYTE;

    switch (ucBus)
    {
#ifdef HV_BOARD_CONFIG_E2P_USE_I2CS0
        case I2CS0:
        {
            enEepromType = HV_BOARD_CONFIG_E2P_TYPE_1;
            break;
        }
#endif
#ifdef HV_BOARD_CONFIG_E2P_USE_MI2C
        case MI2C:
        {
            enEepromType = HV_BOARD_CONFIG_E2P_TYPE_2;
            break;
        }
#endif
#ifdef HV_BOARD_CONFIG_E2P_USE_SIMI2C0
        case SIMI2C0:
        {
            enEepromType = HV_BOARD_CONFIG_E2P_TYPE_3;
            break;
        }
#endif
        default:
        {
            break;
        }
    }

    switch (enEepromType)
    {
        case E2P_TYPE_1K_16PAGES_8BYTE:
        {
            usAddrByteSize = 1;
            usBytesPerPage = 8;
            usTotalPageNum = 16;
            break;
        }
        case E2P_TYPE_2K_32PAGES_8BYTE:
        {
            usAddrByteSize = 1;
            usBytesPerPage = 8;
            usTotalPageNum = 32;
            break;
        }
        case E2P_TYPE_2K_16PAGES_16BYTE:
        {
            usAddrByteSize = 1;
            usBytesPerPage = 16;
            usTotalPageNum = 16;
            break;
        }
        case E2P_TYPE_4K_32PAGES_16BYTE:
        {
            usAddrByteSize = 1;
            usBytesPerPage = 16;
            usTotalPageNum = 32;
            break;
        }
        case E2P_TYPE_8K_64PAGES_16BYTE:
        {
            usAddrByteSize = 1;
            usBytesPerPage = 16;
            usTotalPageNum = 64;
            break;
        }
        case E2P_TYPE_16K_128PAGES_16BYTE:
        {
            usAddrByteSize = 1;
            usBytesPerPage = 16;
            usTotalPageNum = 128;
            break;
        }
        case E2P_TYPE_32K_128PAGES_32BYTE:
        {
            usAddrByteSize = 2;
            usBytesPerPage = 32;
            usTotalPageNum = 128;
            break;
        }
        case E2P_TYPE_64K_256PAGES_32BYTE:
        {
            usAddrByteSize = 2;
            usBytesPerPage = 32;
            usTotalPageNum = 256;
            break;
        }
        default:
        {
            HV_LOGI("No such e2p type .\n");
            break;
        }
    }

    *pAddrByteSize = usAddrByteSize;
    *pBytesPerPage = usBytesPerPage;
    *pTotalPageNum = usTotalPageNum;
    return;
}

static Status _Eeprom_Write(UCHAR8 ucBus, USHORT16 usCmdAddr, USHORT16 usAddrByteSize, UCHAR8 *pucWtData, USHORT16 usWtDataSize)
{
    int iLoop               = 0;
    UCHAR8 aucCmdAddr[2]    = { 0 };
    USHORT16 usDevAddress   = 0;
    BOOL bSub2AddrEn        = HV_FALSE;
    I2cFuncParam   enI2cParam;
    UCHAR8 ucDevAddr        = 0;
    UCHAR8 *pucCmdAddr      = NULL;

    aucCmdAddr[0] = usCmdAddr >> 8;
    aucCmdAddr[1] = usCmdAddr & 0xff;
    if (1 == usAddrByteSize)
    {
        pucCmdAddr = &aucCmdAddr[1];
    }
    else
    {
        pucCmdAddr = aucCmdAddr;
    }

    switch (ucBus)
    {
#ifdef HV_BOARD_CONFIG_E2P_USE_I2CS0
        case I2CS0:
        {
            enI2cParam.usDevAddress     = HV_BOARD_CONFIG_E2P_ADDR_1;
            enI2cParam.pucCmdAddr       = pucCmdAddr;
            enI2cParam.usCmdAddrSize    = usAddrByteSize;
            enI2cParam.pucData          = pucWtData;
            enI2cParam.usDataSize       = usWtDataSize;
            Hv_Drv_Gpio_SetPinLevel(HV_BOARD_CONFIG_E2P_WP_GPIO_1, HV_BOARD_CONFIG_E2P_WP_PROTECT_LEVEL_1?GPIO_LEVEL_LOW:GPIO_LEVEL_HIGH);
            Hv_Drv_I2C_MasterPollingTransmit(apSelf[ucBus], &enI2cParam, 0);
            Hv_Drv_Gpio_SetPinLevel(HV_BOARD_CONFIG_E2P_WP_GPIO_1, HV_BOARD_CONFIG_E2P_WP_PROTECT_LEVEL_1?GPIO_LEVEL_HIGH:GPIO_LEVEL_LOW);
            break;
        }
#endif
#ifdef HV_BOARD_CONFIG_E2P_USE_MI2C
        case MI2C:
        {
            HV_LOGV("write: ucDevAddr %x, usCmdAddr %x\n", ucDevAddr, usCmdAddr);
            ucDevAddr = HV_BOARD_CONFIG_E2P_ADDR_2;
            if (usAddrByteSize == 1)
            {
                ucDevAddr |= (usCmdAddr >> 8) & 0x7;
                usCmdAddr &= 0xff;
                bSub2AddrEn = HV_FALSE;
            }
            else if (usAddrByteSize == 2)
            {
                bSub2AddrEn = HV_TRUE;
            }
            else
            {
                return HV_FAILURE;
            }
            HV_LOGV("write: ucDevAddr %x, usCmdAddr %x\n", ucDevAddr, usCmdAddr);
            Hv_Drv_Gpio_SetPinLevel(HV_BOARD_CONFIG_E2P_WP_GPIO_2, HV_BOARD_CONFIG_E2P_WP_PROTECT_LEVEL_2?GPIO_LEVEL_LOW:GPIO_LEVEL_HIGH);
            HV_LOGV("write: addr %x, addsize %d size %d\n", usCmdAddr, bSub2AddrEn, usWtDataSize);
            #if 0
            for (iLoop = 0; iLoop < usWtDataSize; iLoop++)
            {
                HV_LOGI("write:%d %x\n", iLoop,*(pucWtData+iLoop));
                Hv_Cal_MI2C_E2PWriteByte(HV_BOARD_CONFIG_E2P_ADDR_2, usCmdAddr++, bSub2AddrEn, *(pucWtData + iLoop));
            }
            #else
            Hv_Cal_MI2C_E2PWriteBytes(ucDevAddr, usCmdAddr, bSub2AddrEn, usWtDataSize, pucWtData);
            for (iLoop = 0; iLoop < usWtDataSize; iLoop++)
            {
                HV_LOGV("write:%d %x\n", iLoop,*(pucWtData+iLoop));
            }
            #endif
            Hv_Drv_Gpio_SetPinLevel(HV_BOARD_CONFIG_E2P_WP_GPIO_2, HV_BOARD_CONFIG_E2P_WP_PROTECT_LEVEL_2?GPIO_LEVEL_HIGH:GPIO_LEVEL_LOW);
            break;
        }
#endif
#ifdef HV_BOARD_CONFIG_E2P_USE_SIMI2C0
        case SIMI2C0:
        {
            Hv_Drv_Gpio_SetPinLevel(HV_BOARD_CONFIG_E2P_WP_GPIO_3, HV_BOARD_CONFIG_E2P_WP_PROTECT_LEVEL_3?GPIO_LEVEL_LOW:GPIO_LEVEL_HIGH);
            Hv_Drv_GpioSimI2c_SendData(SIMI2C0, HV_BOARD_CONFIG_E2P_ADDR_3, pucCmdAddr, usAddrByteSize,
                                       pucWtData, usWtDataSize);
            Hv_Drv_Gpio_SetPinLevel(HV_BOARD_CONFIG_E2P_WP_GPIO_3, HV_BOARD_CONFIG_E2P_WP_PROTECT_LEVEL_3?GPIO_LEVEL_HIGH:GPIO_LEVEL_LOW);
            break;
        }
#endif
        default:
        {
            return HV_FAILURE;
        }
    }
    return HV_SUCCESS;
}


static Status _Eeprom_Read(UCHAR8 ucBus, USHORT16 usCmdAddr, USHORT16 usAddrByteSize, UCHAR8 *pucRDData, USHORT16 usRDDataSize)
{
    int iLoop               = 0;
    USHORT16 usDevAddress   = 0;
    UCHAR8 aucCmdAddr[2]    = { 0 };
    BOOL bSub2AddrEn        = HV_FALSE;
    I2cFuncParam   enI2cParam;
    UCHAR8 ucDevAddr        = 0;
    UCHAR8 *pucCmdAddr      = NULL;

    aucCmdAddr[0] = usCmdAddr >> 8;
    aucCmdAddr[1] = usCmdAddr & 0xff;
    if (1 == usAddrByteSize)
    {
        pucCmdAddr = &aucCmdAddr[1];
    }
    else
    {
        pucCmdAddr = aucCmdAddr;
    }

    switch (ucBus)
    {
#ifdef HV_BOARD_CONFIG_E2P_USE_I2CS0
        case I2CS0:
        {
            enI2cParam.usDevAddress     = HV_BOARD_CONFIG_E2P_ADDR_1;
            enI2cParam.pucCmdAddr       = pucCmdAddr;
            enI2cParam.usCmdAddrSize    = usAddrByteSize;
            enI2cParam.pucData          = pucRDData;
            enI2cParam.usDataSize       = usRDDataSize;
            return Hv_Drv_I2C_MasterPollingReceive(apSelf[ucBus], &enI2cParam, 0);
            break;
        }
#endif
#ifdef HV_BOARD_CONFIG_E2P_USE_MI2C
        case MI2C:
        {
            HV_LOGV("write: ucDevAddr %x, usCmdAddr %x\n", ucDevAddr, usCmdAddr);
            ucDevAddr = HV_BOARD_CONFIG_E2P_ADDR_2;
            if (usAddrByteSize == 1)
            {
                ucDevAddr |= (usCmdAddr >> 8) & 0x7;
                usCmdAddr &= 0xff;
                bSub2AddrEn = HV_FALSE;
            }
            else if (usAddrByteSize == 2)
            {
                bSub2AddrEn = HV_TRUE;
            }
            else
            {
                return HV_FAILURE;
            }
            if (usRDDataSize < 4)
            {
                HV_LOGV("read: addr %x, addsize %d\n", usCmdAddr, bSub2AddrEn);
                for (iLoop = 0; iLoop < usRDDataSize; iLoop++)
                {
                    *(pucRDData + iLoop) = Hv_Cal_MI2C_E2PReadByte(HV_BOARD_CONFIG_E2P_ADDR_2, usCmdAddr++, bSub2AddrEn);
                    HV_LOGV("read: %d:%x\n", iLoop,*(pucRDData+iLoop));
                }
            }
            else
            {
                HV_LOGV("write: ucDevAddr %x, usCmdAddr %x\n", ucDevAddr, usCmdAddr);
                HV_LOGV("read: addr %x, addsize %d, size %d\n", usCmdAddr, bSub2AddrEn, usRDDataSize);
                Hv_Cal_MI2C_E2PReadBytes(ucDevAddr, usCmdAddr, bSub2AddrEn, usRDDataSize, pucRDData);
                for (iLoop = 0; iLoop < usRDDataSize; iLoop++)
                {
                    HV_LOGV("read: %d:%x\n", iLoop,*(pucRDData+iLoop));
                }
            }
            break;
        }
#endif
#ifdef HV_BOARD_CONFIG_E2P_USE_SIMI2C0
        case SIMI2C0:
        {
            return Hv_Drv_GpioSimI2c_RecvData(SIMI2C0, HV_BOARD_CONFIG_E2P_ADDR_3, pucCmdAddr, usAddrByteSize,
                                       pucRDData, usRDDataSize);
        }
#endif
        default:
        {
            return HV_FAILURE;
        }
    }
    return HV_SUCCESS;
}

/** Transmit an amount of data to Eeprom
 *  @param ucBus i2c bus
 *  @param usWtAddr  address to write data
 *  @param pucWtData pointer to write data
 *  @param usWtDataSize size to write data.
 *  @retval Status
 */
Status Hv_Drv_Eeprom_Write(UCHAR8 ucBus, USHORT16 usWtAddr, UCHAR8 *pucWtData, USHORT16 usWtDataSize)
{

    USHORT16 usPageNum          = 0;
    USHORT16 usStartDataSize    = 0;
    USHORT16 usEndDataSize      = 0;
    USHORT16 usAddrByteSize     = 0;
    USHORT16 usBytesPerPage     = 0;
    USHORT16 usTotalPageNum     = 0;
    USHORT16 usLoop             = 0;

    Status enStatus =  HV_SUCCESS;

    UCHAR8 *pData = NULL;

    HvCalI2c *pstSelf = NULL;

    pData = pucWtData;


    I2C_E2pType(ucBus, &usAddrByteSize, &usBytesPerPage, &usTotalPageNum);

    usStartDataSize = usBytesPerPage - (usWtAddr % usBytesPerPage);
    if (usStartDataSize != usBytesPerPage)
    {
        if (usWtDataSize < usStartDataSize)
        {
            usStartDataSize = 0;
        }
        else
        {
            usWtDataSize -= usStartDataSize;
        }
    }
    else
    {
        usStartDataSize = 0;
    }
    usPageNum = usWtDataSize / usBytesPerPage;
    usEndDataSize = usWtDataSize % usBytesPerPage;

    if (usStartDataSize != 0)
    {
        enStatus = _Eeprom_Write(ucBus, usWtAddr, usAddrByteSize, pData, usStartDataSize);
        pData += usStartDataSize;
        usWtAddr += usStartDataSize;
        Hv_Vos_Delayus(5000);
    }
    for (usLoop = 0; usLoop < usPageNum; usLoop++)
    {
        enStatus = _Eeprom_Write(ucBus, usWtAddr, usAddrByteSize, pData, usBytesPerPage);
        pData += usBytesPerPage;
        usWtAddr += usBytesPerPage;
        Hv_Vos_Delayus(5000);
    }
    if (usEndDataSize != 0)
    {
        enStatus = _Eeprom_Write(ucBus, usWtAddr, usAddrByteSize, pData, usEndDataSize);
        Hv_Vos_Delayus(5000);
    }

    return enStatus;
}

/** receive an amount of data from eeprom
 *  @param ucBus i2c bus
 *  @param usRdAddr  address to write data
 *  @param pucRdData pointer to write data
 *  @param usRdDataSize size to read data.
 *  @retval Status
 */
Status Hv_Drv_Eeprom_Read(UCHAR8 ucBus, USHORT16 usRdAddr, UCHAR8 *pucRdData, USHORT16 usRdDataSize)
{
    USHORT16 usPageNum          = 0;
    USHORT16 usStartDataSize    = 0;
    USHORT16 usEndDataSize      = 0;
    USHORT16 usAddrByteSize     = 0;
    USHORT16 usBytesPerPage     = 0;
    USHORT16 usTotalPageNum     = 0;
    USHORT16 usLoop             = 0;

    Status enStatus =  HV_SUCCESS;

    UCHAR8 *pData = NULL;

    HvCalI2c *pstSelf = NULL;

    I2C_E2pType(ucBus, &usAddrByteSize, &usBytesPerPage, &usTotalPageNum);

    pData = pucRdData;

    usStartDataSize = usBytesPerPage - (usRdAddr % usBytesPerPage);
    if (usStartDataSize != usBytesPerPage)
    {
        if (usRdDataSize < usStartDataSize)
        {
            usStartDataSize = 0;
        }
        else
        {
            usRdDataSize -= usStartDataSize;
        }
    }
    else
    {
        usStartDataSize = 0;
    }
    usPageNum = usRdDataSize / usBytesPerPage;
    usEndDataSize = usRdDataSize % usBytesPerPage;

    if (usStartDataSize != 0)
    {
        enStatus = _Eeprom_Read(ucBus, usRdAddr, usAddrByteSize, pData, usStartDataSize);
        pData += usStartDataSize;
        usRdAddr += usStartDataSize;
        Hv_Vos_Delayus(5000);
    }
    for (usLoop = 0; usLoop < usPageNum; usLoop++)
    {
        enStatus = _Eeprom_Read(ucBus, usRdAddr, usAddrByteSize, pData, usBytesPerPage);
        pData += usBytesPerPage;
        usRdAddr += usBytesPerPage;
        Hv_Vos_Delayus(5000);
    }
    if (usEndDataSize != 0)
    {
        enStatus = _Eeprom_Read(ucBus, usRdAddr, usAddrByteSize, pData, usEndDataSize);
        Hv_Vos_Delayus(5000);
    }

    return HV_SUCCESS;
}

/** init i2c for eeprom
 *  @retval Status
 */
Status Hv_Drv_Eeprom_Init(void)
{
    I2cInitParam stInitParam;
    GpioSimI2c stGpioSimi2c = {INVALID_PARAM_UHCAR8, INVALID_PARAM_UHCAR8};

#ifdef HV_BOARD_CONFIG_E2P_USE_I2CS0
    stInitParam.enPort = I2C_PORT_1;
    stInitParam.enMode = I2C_MODE_MASTER;
    stInitParam.enSpeed = I2C_SPEED_100K;
    stInitParam.enAddressingMode = I2C_ADDR_7BIT;
    Hv_Drv_Gpio_SetPinLevel(HV_BOARD_CONFIG_E2P_WP_GPIO_1, HV_BOARD_CONFIG_E2P_WP_PROTECT_LEVEL_1?GPIO_LEVEL_HIGH:GPIO_LEVEL_LOW);
    Hv_Drv_Gpio_SetDirection(HV_BOARD_CONFIG_E2P_WP_GPIO_1, GPIO_DIR_OUTPUT);
    apSelf[I2CS0] = Hv_Drv_I2C_Init(&stInitParam);
#endif

#ifdef HV_BOARD_CONFIG_E2P_USE_I2CM0
    stInitParam.enPort = I2C_PORT_0;
    stInitParam.enMode = I2C_MODE_MASTER;
    stInitParam.enSpeed = I2C_SPEED_100K;
    stInitParam.enAddressingMode = I2C_ADDR_7BIT;
    Hv_Drv_Gpio_SetPinLevel(HV_BOARD_CONFIG_E2P_WP_GPIO_0, HV_BOARD_CONFIG_E2P_WP_PROTECT_LEVEL_0?GPIO_LEVEL_HIGH:GPIO_LEVEL_LOW);
    Hv_Drv_Gpio_SetDirection(HV_BOARD_CONFIG_E2P_WP_GPIO_0, GPIO_DIR_OUTPUT);
    apSelf[I2CM0] = Hv_Drv_I2C_Init(&stInitParam);
#endif

#ifdef HV_BOARD_CONFIG_E2P_USE_SIMI2C0
    stGpioSimi2c.ucSdaPin = SIMI2C_SDA;
    stGpioSimi2c.ucSclPin = SIMI2C_SCL;
    Hv_Drv_Gpio_SetPinLevel(HV_BOARD_CONFIG_E2P_WP_GPIO_3, HV_BOARD_CONFIG_E2P_WP_PROTECT_LEVEL_3?GPIO_LEVEL_HIGH:GPIO_LEVEL_LOW);
    Hv_Drv_Gpio_SetDirection(HV_BOARD_CONFIG_E2P_WP_GPIO_3, GPIO_DIR_OUTPUT);
    Hv_Drv_GpioSimI2c_Init(SIMI2C0, &stGpioSimi2c);
#endif

#ifdef HV_BOARD_CONFIG_E2P_USE_MI2C
    Hv_Drv_Gpio_SetPinLevel(HV_BOARD_CONFIG_E2P_WP_GPIO_2, HV_BOARD_CONFIG_E2P_WP_PROTECT_LEVEL_2?GPIO_LEVEL_HIGH:GPIO_LEVEL_LOW);
    Hv_Drv_Gpio_SetDirection(HV_BOARD_CONFIG_E2P_WP_GPIO_2, GPIO_DIR_OUTPUT);
    Hv_Cal_MI2C_Init();
#endif

    return HV_SUCCESS;
}