/**
 * @file hv_drv_TypecPortable.c
 * @brief  This source file contains soc driver globals and functions.
 * @details This file provides the following functions: \n
 *          (1) \n
 *          (2) \n
 *          (3) \n
 *
 * @author HiView SoC Software Team
 * @version 1.0.0
 * @date 2022-08-25
 * @copyright Copyright(c),2022-8, Hiview Software. All rights reserved.
 * @par History:
 * <table>
 * <tr><th>Author                         <th>Date         <th>Change Description
 * <tr><td>Hiview SoC Software Team       <td>2022-08-25   <td>init
 * </table>
*/

#include "hv_comm_DataType.h"
#include "hv_comm_Event.h"
#include "hv_comm_Assert.h"
#include "hv_vos_Comm.h"
#include "hv_vos_Irq.h"
#include "hv_mw_SystemManager.h"
#include "hv_chip_Irq.h"
#include "hv_chip_Debug.h"
#include "hv_drv_Gpio.h"
#include "hv_cal_I2c.h"
#include "hv_drv_GpioSimI2c.h"
#include "hv_vos_Cache.h"
#include "HV_REGS_HT7315.h"
#include "FreeRTOS.h"
#include "task.h"
#include "BoardConfig.h"
#include "hv_drv_TypecStdInc.h"

#define TYPEC_DYNAMIC_MEMORY_ALLOCATION 0

#define PDBG_BUFFER_LEN                 0x8000
#define PDBG_BUFFER_ADDR_REG            SW_DUMMY_TYPEC_DEBUG 
extern UINT64 uiPDTimeTick;

#define TYPEC                           "typec"

#define VBUS_POLLING_TIME                30 //100

/*Make UINT32 from Bytes*/
#define SOC_MAKE_UINT32_FROM_BYTES(u8LoWordLoByte, u8LoWordHiByte, u8HiWordLoByte, u8HiWordHiByte)\
                                            (((UINT32)u8LoWordLoByte)                |\
                                            (((UINT32)u8LoWordHiByte) << 8)          |\
                                            (((UINT32)u8HiWordLoByte) << 16)         |\
                                            (((UINT32)u8HiWordHiByte) << 24u))
#if CONFIG_HOOK_DEBUG_MSG

#if TYPEC_DYNAMIC_MEMORY_ALLOCATION
CHAR8 *pdbg_buffer = NULL;
#else
CHAR8 pdbg_buffer[PDBG_BUFFER_LEN];
#endif
CHAR8 *pdbg_mem = NULL;
UINT32 dbg_mem_addr;
#endif

static UCHAR8 TypecPorts[CONFIG_PD_PORT_COUNT];
static UCHAR8 su8LastReadVal[2] = { 0 };
static TaskHandle_t stCurTaskHandle;
static TypecPlatformCB *s_pstTypecPlatformCB = NULL;

/*****************************************************************************/
/*****************************************************************************/
/*********************************SPI Driver APIs*****************************/
/*****************************************************************************/
#if CONFIG_HOOK_DEBUG_MSG

void Hv_Drv_Typec_DebugClearDbgBuff(void)
{
    INT32 i = 0;

    for (i = 0; i < PDBG_BUFFER_LEN; i++)
    {
        pdbg_buffer[i] = '\0';
    }
}

void Hv_Drv_Typec_DebugMemInit(void)
{
#if TYPEC_DYNAMIC_MEMORY_ALLOCATION
    pdbg_buffer = (CHAR8*)Hv_Vos_Malloc(PDBG_BUFFER_LEN);
#endif
    Hv_Drv_Typec_DebugClearDbgBuff();
    pdbg_mem = pdbg_buffer;
    dbg_mem_addr = (UINT32)pdbg_mem;
    HV_W32(PDBG_BUFFER_ADDR_REG, ((UINT32)pdbg_mem&0x7FFFFFFF));
}

void Hv_Drv_Typec_DebugPrintChar(CHAR8 ch)
{
    if ((UINT32)pdbg_mem > (dbg_mem_addr+PDBG_BUFFER_LEN))
    {
        pdbg_mem = (CHAR8*)dbg_mem_addr;
        Hv_Drv_Typec_DebugClearDbgBuff();
    }
    
    HV_ASSERT(pdbg_mem != NULL);
    
    *pdbg_mem++ = ch;
}

void Hv_Drv_Typec_DebugPrintf(CHAR8 *pstr)
{
    if (pstr == NULL)
    {
        return;
    }

    Hv_Drv_Typec_DebugPrintChar(' ');
    
    while(*pstr)
    {
        if (*pstr == '\r')
        {
            pstr++;
            continue;
        }

        if (*pstr == '\n')
        {
            Hv_Drv_Typec_DebugPrintChar('\r');
        }

        Hv_Drv_Typec_DebugPrintChar(*pstr);

        pstr++;
    }
    Hv_Vos_InvalidDCache(dbg_mem_addr, PDBG_BUFFER_LEN);
}


/**
 * @brief This function show input value in hex
 * @param [in] uiVal             value will be show on console
*/
void Hv_Drv_Typec_DebugPrintHex(UINT32 uiVal)
{
    char scBuf[32] = "0x        -0x";
    char *psc = (char *)&scBuf[0];
    int32_t    i = 0;

    HV_ASSERT(pdbg_mem != NULL);
    
    // print uiVal and Timeticks
    for (i = 0; i < 8; i++)
    {
        psc[i+2] = (uiVal << (i * 4)) >> 28;
        if (psc[i+2] >= 0 && psc[i+2] <= 9)
            psc[i+2] += 0x30;
        else
            psc[i+2] += 0x37;
    }

    for (i = 0; i < 8; i++)
    {
        psc[i+13] = (((uint32_t)uiPDTimeTick) << (i * 4)) >> 28;
        if (psc[i+13] >= 0 && psc[i+13] <= 9)
            psc[i+13] += 0x30;
        else
            psc[i+13] += 0x37;
    }
    psc[22] = 0;

    psc = scBuf;


    while (*psc != 0)
    {
        if ((UINT32)pdbg_mem > (dbg_mem_addr+PDBG_BUFFER_LEN))
        {           
            pdbg_mem = (CHAR8*)dbg_mem_addr;
            Hv_Drv_Typec_DebugClearDbgBuff();
        }
        
        HV_ASSERT(pdbg_mem != NULL);
        *pdbg_mem++ = *psc;
        psc++;
    }
}

/**
 * @brief This function show input value in dec
 * @param [in] uiVal             value will be show on console
*/
void Hv_Drv_Typec_DebugPrintDec(UINT32 uiVal)
{
    CHAR8 scBuf[16];
    CHAR8 *psc = NULL;
    INT32 iPos = 0;

    HV_ASSERT(pdbg_mem != NULL);

    if (uiVal > 0)
    {
        while (uiVal)
        {
            scBuf[iPos] = (CHAR8)(uiVal % 10) + 0x30;
            uiVal /= 10;
            iPos++;
        }
    }
    else
    {
        scBuf[0] = '0';
        iPos = 1;
    }

    scBuf[iPos] = 0;

    psc = &(scBuf[iPos - 1]);

    while (psc >= scBuf)
    {
        //IRQPutChar(*psc);
        if ((UINT32)pdbg_mem > (dbg_mem_addr+PDBG_BUFFER_LEN))
        {
            pdbg_mem = (CHAR8*)dbg_mem_addr;
            Hv_Drv_Typec_DebugClearDbgBuff();
        }

        HV_ASSERT(pdbg_mem != NULL);
        *pdbg_mem++ = *psc;
        psc--;
    }
}

void Hv_Drv_Typec_UartPrintChar(UCHAR8 byData)
{
    Hv_Drv_Typec_DebugPrintChar((CHAR8)byData);
}

void Hv_Drv_Typec_UartPrintDec(UINT32 dwWriteInt)
{
    Hv_Drv_Typec_DebugPrintHex(dwWriteInt);
}

void Hv_Drv_Typec_UartPrint(CHAR *       pbyMessage)
{
    Hv_Drv_Typec_DebugPrintf(pbyMessage);
}

#endif

void Hv_Drv_Typec_EnableDisableIntr(UCHAR8 u8En)
{
    UCHAR8 u8PortNum;

    if (u8En)
    {        
        for (u8PortNum = SET_TO_ZERO; u8PortNum < CONFIG_PD_PORT_COUNT; u8PortNum++)
          {
            if (UPD_PORT_ENABLED == DPM_GET_CONFIGURED_PORT_EN(u8PortNum))
            {                                
                Hv_Vos_UnmaskIrq(HV_CHIP_IRQ_TYPC0+u8PortNum);              
            }
        }           
    }
    else
    {
        for (u8PortNum = SET_TO_ZERO; u8PortNum < CONFIG_PD_PORT_COUNT; u8PortNum++)
          {
            if (UPD_PORT_ENABLED == DPM_GET_CONFIGURED_PORT_EN(u8PortNum))
            {                                
                Hv_Vos_MaskIrq(HV_CHIP_IRQ_TYPC0+u8PortNum);
            }
        }
    }    
}

static HV_VOS_ISR_RESULT_E Hv_Drv_Typec_ISRHandler(UINT32 irqNum, void *arg)
{
#if TYPEC_DEBUG_TRACE_EN
    static UINT32 ISRCounts = 0;
#endif
    UCHAR8 u8PortNum = 0;

    HV_ASSERT(arg != NULL);
    HV_ASSERT(irqNum >= HV_CHIP_IRQ_BASE);

    u8PortNum = *((UCHAR8 *)arg);

#if TYPEC_DEBUG_TRACE_EN
    HV_W32(0x11008034, ++ISRCounts);
#endif

    Hv_Drv_Typec_IrqHandler (u8PortNum);
    
    Hv_Vos_WClrMiscInt(irqNum - HV_CHIP_IRQ_BASE);
    
    return HV_VOS_ISR_HANDLED;
}

void Hv_Drv_Typec_IntInit(void)
{
    UCHAR8 u8PortNum;
    
    for (u8PortNum = SET_TO_ZERO; u8PortNum < CONFIG_PD_PORT_COUNT; u8PortNum++)
      {
        if (UPD_PORT_ENABLED == DPM_GET_CONFIGURED_PORT_EN(u8PortNum))
        {
           TypecPorts[u8PortNum] = u8PortNum;
           Hv_Vos_ConfigIrq(HV_CHIP_IRQ_TYPC0+u8PortNum, HV_TRUE, 0);
           Hv_Vos_AttachIsr(HV_CHIP_IRQ_TYPC0+u8PortNum, Hv_Drv_Typec_ISRHandler, &TypecPorts[u8PortNum]);
        }
    }

    stCurTaskHandle = xTaskGetCurrentTaskHandle();
}

INT32 Hv_Drv_Typec_MemCmp(void *pc1, void *pc2, INT32 iLen)
{
    INT32 si, siRet = 0;
    const UCHAR8 *pbc1, *pbc2;

    HV_ASSERT(pc1 != NULL);
    HV_ASSERT(pc2 != NULL);
    HV_ASSERT(iLen != 0);

    pbc1 = (UCHAR8 *)pc1;
    pbc2 = (UCHAR8 *)pc2;

    for (si = 0; si < iLen; si++)
    {
        siRet = (INT32)(pbc1[si] - pbc2[si]);
        if (siRet != 0)
        {
            break;
        }
    }

    return siRet;
}

void Hv_Drv_Typec_MemCopy(void *pDest, const void *pSrc, INT32 iLen)
{
    INT32    si, i;
    const UINT32    *puiSource;
    UCHAR8    *pbyDest;
    const UCHAR8    *pbySource;
    
    HV_ASSERT(pDest != NULL);
    HV_ASSERT(pSrc != NULL);

    // count double word length
    si = iLen / sizeof(UINT32);
    if (si > 0)
    {
        UINT32    *puiDest;
        
        puiDest = (UINT32 *)pDest;
        puiSource = (UINT32 *)pSrc;
        
        for (i = 0; i < si; i++)
        {
            puiDest[i] = puiSource[i];
        }

        si = iLen % sizeof(UINT32);

        if (si > 0)
        {
            pbyDest = (UCHAR8 *)pDest + i * sizeof(UINT32);
            pbySource = (UCHAR8 *)pSrc + i * sizeof(UINT32);
        }
        else
        {
            return;
        }
    }
    else
    {
        si = iLen % sizeof(UINT32);
        pbyDest = (UCHAR8 *)pDest;
        pbySource = (UCHAR8 *)pSrc;
    }

    // copy in byte mode
    for (i = 0; i < si; i++)
    {
        pbyDest[i] = pbySource[i];
    }

    return;
}

void Hv_Drv_Typec_MemCopyWordFromByte(UINT32 * pu32Dst, const UCHAR8 *pucSrc, INT32 iLength)
{
    INT32 i;

    HV_ASSERT(pu32Dst != NULL);
    HV_ASSERT(pucSrc != NULL);

    if (iLength == 0)
        return;

    for (i = 0; i < iLength; i++) {
        *pu32Dst++ = SOC_MAKE_UINT32_FROM_BYTES(pucSrc[i*4], pucSrc[i*4+1], pucSrc[i*4+2], pucSrc[i*4+3]);
    }
}

void Hv_Drv_Typec_MemCopyByteFromWord(UCHAR8 *pu8Dst, const UINT32 *pu32Src, INT32 iLength)
{
    INT32 i, j;
    UINT32 u32SrcVal;

    HV_ASSERT(pu8Dst != NULL);
    HV_ASSERT(pu32Src != NULL);
    HV_ASSERT(iLength != 0);

    for (i = 0; i < iLength / 4; i++) {
        u32SrcVal = *pu32Src++;
        for (j = 0; j < 4; j++) {
            *pu8Dst++ = (UCHAR8)((u32SrcVal>>(j*8))&0xFF);
        }
    }
}

void Hv_Drv_Typec_DpSignalLoss(void)
{
    return;
}

void Hv_Drv_Typec_SimulateUnplugPlug(void)
{
#if (HV_TRUE == INCLUDE_PD_DRP)
    HV_W32_FIELD_EX(STANDBY_USB_TYPEC_TOP_00_04, 0x00, reg_00_sw_ctrl_rd_en, 1);
    HV_W32_FIELD_EX(STANDBY_USB_TYPEC_TOP_00_04, 0x00, reg_00_sw_ctrl_rp_en, 1);
#endif
    /* Simulate Typec Unplug*/
    HV_W32_FIELD_EX(STANDBY_USB_TYPEC_TOP_00_04, 0x00, reg_00_cc1_rd_en, 0);
    HV_W32_FIELD_EX(STANDBY_USB_TYPEC_TOP_00_04, 0x00, reg_00_cc2_rd_en, 0);
    HV_W32_FIELD_EX(STANDBY_USB_TYPEC_TOP_00_04, 0x00, reg_00_cc1_rp_en, 0);
    HV_W32_FIELD_EX(STANDBY_USB_TYPEC_TOP_00_04, 0x00, reg_00_cc2_rp_en, 0);

    Hv_Vos_MSleep(20);
    
#if (HV_TRUE == INCLUDE_PD_DRP)
    HV_W32_FIELD_EX(STANDBY_USB_TYPEC_TOP_00_04, 0x00, reg_00_cc1_rd_en, 1);
    HV_W32_FIELD_EX(STANDBY_USB_TYPEC_TOP_00_04, 0x00, reg_00_cc1_rd_en, 1);
#elif (HV_TRUE == INCLUDE_PD_SOURCE)
    HV_W32_FIELD_EX(STANDBY_USB_TYPEC_TOP_00_04, 0x00, reg_00_cc1_rp_en, 1);
    HV_W32_FIELD_EX(STANDBY_USB_TYPEC_TOP_00_04, 0x00, reg_00_cc2_rp_en, 1);
#elif (HV_TRUE == INCLUDE_PD_SINK)
    HV_W32_FIELD_EX(STANDBY_USB_TYPEC_TOP_00_04, 0x00, reg_00_cc1_rd_en, 1);
    HV_W32_FIELD_EX(STANDBY_USB_TYPEC_TOP_00_04, 0x00, reg_00_cc2_rd_en, 1);
#else
    HV_W32_FIELD_EX(STANDBY_USB_TYPEC_TOP_00_04, 0x00, reg_00_cc1_rp_en, 1);
    HV_W32_FIELD_EX(STANDBY_USB_TYPEC_TOP_00_04, 0x00, reg_00_cc2_rp_en, 1);
#endif

#if (HV_TRUE == INCLUDE_PD_DRP)
    HV_W32_FIELD_EX(STANDBY_USB_TYPEC_TOP_00_04, 0x00, reg_00_sw_ctrl_rd_en, 0);
    HV_W32_FIELD_EX(STANDBY_USB_TYPEC_TOP_00_04, 0x00, reg_00_sw_ctrl_rp_en, 0);
#endif
}

void Hv_Drv_Typec_SetTaskPriority(UCHAR8 u8Priority)
{
    if (stCurTaskHandle != NULL) 
    {
        HV_LOG_DEBUG(TYPEC, "---------- Hv_Drv_Typec_SetTaskPriority = %d ----------", u8Priority);
        vTaskPrioritySet(stCurTaskHandle, u8Priority);
    } 
}

UCHAR8 Hv_Drv_Typec_GetTaskPriority(void)
{
    return uxTaskPriorityGet(stCurTaskHandle);
}

Status Hv_Drv_Typec_SendEventToTypecQueue(USHORT16 usEventId, USHORT16 usValue)
{
    HV_VOS_QUEUE_S* pTypecQueue = NULL;
    HV_ASSERT_VALID_PTR(s_pstTypecPlatformCB);
    pTypecQueue = s_pstTypecPlatformCB->GetTypecQueue();
    HV_ASSERT_VALID_PTR(pTypecQueue);

    TypecEvent stTypecEvent;
    HV_MEMSET(&stTypecEvent, 0, sizeof(TypecEvent));
    stTypecEvent.usEventId = usEventId;
    stTypecEvent.usValue = usValue;

    HV_ASSERT_TRUE(Hv_Vos_QueueSend(pTypecQueue, &stTypecEvent));

    return HV_SUCCESS;
}

Status Hv_Drv_Typec_SendEventToTypecQueueFromISR(USHORT16 usEventId, USHORT16 usValue)
{
    HV_VOS_QUEUE_S* pTypecQueue = NULL;
    HV_ASSERT_VALID_PTR(s_pstTypecPlatformCB);
    pTypecQueue = s_pstTypecPlatformCB->GetTypecQueue();
    HV_ASSERT_VALID_PTR(pTypecQueue);

    TypecEvent stTypecEvent;
    HV_MEMSET(&stTypecEvent, 0, sizeof(TypecEvent));
    stTypecEvent.usEventId = usEventId;
    stTypecEvent.usValue = usValue;

    HV_ASSERT_TRUE(Hv_Vos_QueueSendFromISR(pTypecQueue, &stTypecEvent));

    return HV_SUCCESS;
}

ULONG32 Hv_Drv_Typec_QueueMessagesWaiting(void)
{
    HV_VOS_QUEUE_S* pTypecQueue = NULL;
    HV_ASSERT_VALID_PTR(s_pstTypecPlatformCB);
    pTypecQueue = s_pstTypecPlatformCB->GetTypecQueue();
    HV_ASSERT_VALID_PTR(pTypecQueue);

    return Hv_Vos_QueueMessagesWaiting(pTypecQueue);
}

ULONG32 Hv_Drv_Typec_QueueMessagesWaitingFromISR(void)
{
    HV_VOS_QUEUE_S* pTypecQueue = NULL;
    HV_ASSERT_VALID_PTR(s_pstTypecPlatformCB);
    pTypecQueue = s_pstTypecPlatformCB->GetTypecQueue();
    HV_ASSERT_VALID_PTR(pTypecQueue);

    return Hv_Vos_QueueMessagesWaitingFromISR(pTypecQueue);
}

Status Hv_Drv_Typec_FlatformCbInit(TypecPlatformCB *pstTypecPlatformCB)
{
    HV_ASSERT_VALID_PTR(pstTypecPlatformCB);
    s_pstTypecPlatformCB = pstTypecPlatformCB;

    return HV_SUCCESS;
}

TypecPlatformCB* Hv_Drv_Typec_GetFlatformCbPointer(void)
{
    return s_pstTypecPlatformCB;
}

