/**
 * @file hv_drv_GpioSimSWire.c
 * @brief gpio smulitaion single wire driver  layer file.
 * @details This file provides the following functions: \n
 *          (1) gpio simulate swire tx/rx \n
 *          (2) gpio simulate swire init \n
 *
 * @author HiView SoC Software Team
 * @version 1.0.0
 * @date 2023-12-16
 * @copyright Copyright(c),2023-12, Hiview Software. All rights reserved.
 * @par History:
 * <table>
 * <tr><th>Author       <th>Date            <th>Change Description
 * </table>
 */
#include "Common/hv_comm_DataType.h"
#include "Common/Assert/hv_comm_Assert.h"
#include "hv_comm_Define.h"
#include "hv_cal_Gpio.h"
#include "hv_vos_Comm.h"
#include "hv_drv_GpioSimSWire.h"

#define GPIOSIMSWIRE_DELAY     1U

static GpioSimSWire s_stgpiosimswire[SIMSWIRE_ID_MAX] = 
{
    {INVALID_PARAM_UHCAR8, INVALID_PARAM_UHCAR8, 1, 1, 1},
};

#define SET_SCK_OUT(uiIndex)          Hv_Cal_Gpio_SetDirection(s_stgpiosimswire[uiIndex].ucSckPin, GPIO_DIR_OUTPUT)
#define SET_SCK_IN(uiIndex)           Hv_Cal_Gpio_SetDirection(s_stgpiosimswire[uiIndex].ucSckPin, GPIO_DIR_INPUT)
#define SET_SDT_OUT(uiIndex)          Hv_Cal_Gpio_SetDirection(s_stgpiosimswire[uiIndex].ucSdtPin, GPIO_DIR_OUTPUT)
#define SET_SDT_IN(uiIndex)           Hv_Cal_Gpio_SetDirection(s_stgpiosimswire[uiIndex].ucSdtPin, GPIO_DIR_INPUT)

#define SET_SCK_HIGH(uiIndex)         Hv_Cal_Gpio_SetPinLevel(s_stgpiosimswire[uiIndex].ucSckPin, GPIO_LEVEL_HIGH)
#define SET_SCK_LOW(uiIndex)          Hv_Cal_Gpio_SetPinLevel(s_stgpiosimswire[uiIndex].ucSckPin, GPIO_LEVEL_LOW)
#define SET_SDT_HIGH(uiIndex)         Hv_Cal_Gpio_SetPinLevel(s_stgpiosimswire[uiIndex].ucSdtPin, GPIO_LEVEL_HIGH)
#define SET_SDT_LOW(uiIndex)          Hv_Cal_Gpio_SetPinLevel(s_stgpiosimswire[uiIndex].ucSdtPin, GPIO_LEVEL_LOW)
#define GET_SDT_LEVEL(uiIndex)        Hv_Cal_Gpio_GetPinLevel(s_stgpiosimswire[uiIndex].ucSdtPin)
#define GET_SCK_TWHL(uiIndex)         (s_stgpiosimswire[uiIndex].ucTWHL)
#define GET_SDT_SCK_TSU(uiIndex)      (s_stgpiosimswire[uiIndex].ucTSU)
#define GET_SDT_SCK_THL(uiIndex)      (s_stgpiosimswire[uiIndex].ucTHL)

/* 1nop about 13 ns */
static inline VOID GpioSimSWire_Delay_Nop(UINT32 uiNop)
{
    HV_ASSERT_TRUE_VOID(uiNop > 0);
    while(uiNop--)
    {
        asm("nop");
    }
    return;
}

/** Initialize gpio simulate single wire
 *  @param pstGpioSimSwire pointer to swire configuration parameters
 *  @param enBus select sginle wire bus id
 *  @return SUCCESS/FAILURE
 */
Status Hv_Drv_GpioSimSWire_Init(const GpioSimSWire *pstGpioSimSwire)
{
    HV_ASSERT_VALID_PTR_RET(pstGpioSimSwire, HV_FAILURE);
    HV_ASSERT_TRUE(pstGpioSimSwire->enBus < SIMSWIRE_ID_MAX);

    memcpy(&s_stgpiosimswire[pstGpioSimSwire->enBus], pstGpioSimSwire, sizeof(GpioSimSWire));
    SET_SCK_OUT(pstGpioSimSwire->enBus);
    SET_SDT_OUT(pstGpioSimSwire->enBus);
    SET_SCK_LOW(pstGpioSimSwire->enBus);
    SET_SDT_LOW(pstGpioSimSwire->enBus);
    return HV_SUCCESS;
}

/** Swire start signal
 *@SCK and SDT keep low
 */
VOID Hv_GpioSimSWire_Start(UINT32 uiIndex)
{
    SET_SCK_OUT(uiIndex);
    SET_SDT_OUT(uiIndex);
    SET_SDT_LOW(uiIndex);
    SET_SCK_LOW(uiIndex);
    return;
}

/** Swire stop  signal
 *@SCK and SDT keep Low
 */
VOID Hv_GpioSimSWire_Stop(UINT32 uiIndex)
{
    SET_SCK_OUT(uiIndex);
    SET_SDT_OUT(uiIndex);
    SET_SDT_LOW(uiIndex);
    SET_SCK_LOW(uiIndex);
    return;
}

/** send data 1 byte
 *@ Send data bit by bit and MSB first;
 *@ When SCK keep high, data is valid;
 */
VOID Hv_GpioSimSWire_Send_Bit(UINT32 uiIndex, UCHAR8 ucBit)
{
    SET_SDT_OUT(uiIndex);
    SET_SCK_LOW(uiIndex);
    if (ucBit)
    {
        SET_SDT_HIGH(uiIndex);
    }
    else
    {
        SET_SDT_LOW(uiIndex);
    }

    GpioSimSWire_Delay_Nop(GET_SDT_SCK_TSU(uiIndex));
    SET_SCK_HIGH(uiIndex);
    GpioSimSWire_Delay_Nop(GET_SCK_TWHL(uiIndex));
    SET_SCK_LOW(uiIndex);
    GpioSimSWire_Delay_Nop(GET_SDT_SCK_THL(uiIndex));
    SET_SDT_LOW(uiIndex);
    return;
}

/** receive 1 bit
 *@Pull SCK high, to read SDT data from slave device  bit by bit
 */
UCHAR8 Hv_GpioSimSWire_Recv_Bit(UINT32 uiIndex)
{
    UCHAR8 ucBit = 0x0;
    UCHAR8 ucStep = 0;
    SET_SDT_IN(uiIndex);
    SET_SCK_LOW(uiIndex);
    GpioSimSWire_Delay_Nop(GPIOSIMSWIRE_DELAY);
    SET_SCK_HIGH(uiIndex);
    if (GET_SDT_LEVEL(uiIndex))
    {
        ucBit = ucBit | 0x01;
    }
    GpioSimSWire_Delay_Nop(GPIOSIMSWIRE_DELAY);
    SET_SCK_LOW(uiIndex);
    return ucBit;
}

/** send data 1 byte
 *@ Send data bit by bit and MSB first;
 *@ When SCK keep high, data is valid;
 */
VOID Hv_GpioSimSWire_Send_Byte(UINT32 uiIndex, UCHAR8 ucByte)
{
    UCHAR8 ucStep = 0;
    SET_SDT_OUT(uiIndex);
    SET_SCK_LOW(uiIndex);
    for (ucStep = 0;ucStep < 8; ucStep++)
    {
        if ((ucByte<<ucStep)&0x80)
        {
            SET_SDT_HIGH(uiIndex);
        }
        else
        {
            SET_SDT_LOW(uiIndex);
        }

        GpioSimSWire_Delay_Nop(GET_SDT_SCK_TSU(uiIndex));
        SET_SCK_HIGH(uiIndex);
        GpioSimSWire_Delay_Nop(GET_SCK_TWHL(uiIndex));
        SET_SCK_LOW(uiIndex);
        GpioSimSWire_Delay_Nop(GET_SDT_SCK_THL(uiIndex));
        SET_SDT_LOW(uiIndex);
    }
}

/** receive 1 byte
 *@Pull SCK high, to read SDT data from slave device  bit by bit
 */
UCHAR8 Hv_GpioSimSWire_Recv_Byte(UINT32 uiIndex)
{
    UCHAR8 ucByte = 0x00;
    UCHAR8 ucStep = 0;
    SET_SDT_IN(uiIndex);
    for (ucStep = 0; ucStep < 8; ucStep++)
    {
        SET_SCK_LOW(uiIndex);
        GpioSimSWire_Delay_Nop(GPIOSIMSWIRE_DELAY);
        SET_SCK_HIGH(uiIndex);
        ucByte = ucByte<<1;
        if (GET_SDT_LEVEL(uiIndex))
        {
            ucByte = ucByte | 0x01;
        }
        GpioSimSWire_Delay_Nop(GPIOSIMSWIRE_DELAY);
    }
    SET_SCK_LOW(uiIndex);

    return ucByte;
}

/** Send in master mode an amount of data
 *  @param pucData pointer to data buffer to be transmited
 *  @param usDataSize amount of data to be sent
 *  @retval Status SUCESS/FAILURE
*/
Status Hv_Drv_GpioSimSWire_SendData(SWireID enBus, UCHAR8 *pucData, UINT32 uiDatasize)
{
    HV_ASSERT_TRUE(enBus < SIMSWIRE_ID_MAX);
    HV_ASSERT_VALID_PTR_RET(pucData, HV_FAILURE);
    UINT32 uiSize = 0;

    //Start
    Hv_GpioSimSWire_Start(enBus);

    //Write Data
    for (uiSize = 0; uiSize < uiDatasize; uiSize++)
    {
        Hv_GpioSimSWire_Send_Byte(enBus, pucData[uiSize]);
    }

    //Stop
    Hv_GpioSimSWire_Stop(enBus);
    return HV_SUCCESS;
}

/** Receive in master mode an amount of data
*  @param ucDevAddr target device address
*  @param pucData pointer to data buffer to be transmited
*  @param usDataSize amount of data to be receive
*  @retval Status SUCESS/FAILURE
*/
Status Hv_Drv_GpioSimSWire_RecvData(SWireID enBus, UCHAR8 *pucData, USHORT16 usDataSize)
{
    HV_ASSERT_TRUE(enBus < SIMSWIRE_ID_MAX);
    HV_ASSERT_VALID_PTR_RET(pucData, HV_FAILURE);
    UINT32 uiSize = 0;
    //Start
    Hv_GpioSimSWire_Start(enBus);
    for (uiSize = 0; uiSize < usDataSize; uiSize++)
    {
        pucData[uiSize] = Hv_GpioSimSWire_Recv_Byte(enBus);
    }

    //Stop
    Hv_GpioSimSWire_Stop(enBus);
    return HV_SUCCESS;
}