/**
 * @file  hv_drv_TypecHardware.c
 * @brief    UPD350 hardware source file 
 *          This file has UPD350 Hardware related GPIO, UPD350 Idle, PIO override related 
 *          API definitions.
 * @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_drv_TypecStdInc.h>
#include "hv_comm_Assert.h"

/*******************************************************************************************/
/**************************UPD Register Read/Write APIs*************************************/
/*******************************************************************************************/

void Hv_Typec_RegWriteByte (UINT8 u8PortNum, UINT16 u16RegOffset, UINT8 u8WriteValue)
{
    Hv_Typec_RegisterWrite (u8PortNum, u16RegOffset, &u8WriteValue, BYTE_LEN_1);
}

void Hv_Typec_RegWriteWord (UINT8 u8PortNum, UINT16 u16RegOffset, UINT32 u16WriteValue)
{
    Hv_Typec_RegisterWrite (u8PortNum, u16RegOffset, (UINT8 *)&u16WriteValue, BYTE_LEN_4);
}

/******************************************************************************************************/
UINT8 Hv_Typec_RegReadByte (UINT8 u8PortNum, UINT16 u16RegOffset)
{
    UINT8 u8ReadData;
    Hv_Typec_RegisterRead (u8PortNum, u16RegOffset, &u8ReadData, BYTE_LEN_1);
    return u8ReadData;
}

UINT16 Hv_Typec_RegReadWord (UINT8 u8PortNum, UINT16 u16RegOffset)
{
    UINT8 u8ReadData[2];
    Hv_Typec_RegisterRead (u8PortNum, u16RegOffset, u8ReadData, BYTE_LEN_2);
    return MAKE_UINT16(u8ReadData[1], u8ReadData[0]);
}

/******************************************************************************************************/

void Hv_Typec_RegByteSetBit (UINT8 u8PortNum, UINT16 u16RegOffset, UINT8 u8BitMsk)
{
    UINT8 u8Data = SET_TO_ZERO;
    Hv_Typec_RegisterRead (u8PortNum, u16RegOffset, &u8Data, BYTE_LEN_1);
    u8Data |= u8BitMsk; 
    Hv_Typec_RegisterWrite (u8PortNum, u16RegOffset, &u8Data, BYTE_LEN_1);
}

void Hv_Typec_RegByteClearBit (UINT8 u8PortNum, UINT16 u16RegOffset, UINT8 u8BitMsk)
{
    UINT8 u8Data = SET_TO_ZERO;
    Hv_Typec_RegisterRead (u8PortNum, u16RegOffset, &u8Data, BYTE_LEN_1);
    u8Data &= ~u8BitMsk;
    Hv_Typec_RegisterWrite (u8PortNum, u16RegOffset, &u8Data, BYTE_LEN_1);
}

/******************************************************************************************************/
void Hv_Typec_RegisterWrite (UINT8 u8PortNum, UINT16 u16RegOffset, UINT8 *pu8WriteData, UINT8 u8WriteDataLen)
{
    INT32    j;
    UINT32    u32RegAddr;
    UINT32    u32RegVal = 0;
    UINT32    u32DivAddr, u32Offset = 0;
    volatile UINT32    *pu32Data = SET_TO_ZERO;

    u32RegAddr = (u8PortNum == SET_TO_ZERO ? UPD_PORT0_BASE_ADDR : SET_TO_ZERO) + (UINT32)u16RegOffset;        //now only one port support
    u32DivAddr = ((u32RegAddr >> 2) << 2);
    for (j = 0; j < u8WriteDataLen; j++) {
        u32DivAddr = (((u32RegAddr + j) >> 2) << 2);
        u32Offset = (u32RegAddr + j) % 4;

        if ((UINT32)pu32Data != u32DivAddr) {
            pu32Data = (volatile UINT32*)u32DivAddr;            //to confirm the big endian and little endian use case.
            u32RegVal = *pu32Data;
        }

        u32RegVal &= ~((UINT32)((UINT32)0xFF << (u32Offset * 8)));                //clear the reg val
        u32RegVal |= (UINT32)((UINT32)pu8WriteData[j] << (u32Offset * 8));

        if (u32Offset == 3) {
            *pu32Data = u32RegVal;
            u32RegVal = 0;
        }
    }

    if (u32Offset != 3) {
        HV_ASSERT_VALID_PTR_VOID(pu32Data);
        *pu32Data = u32RegVal;
    }
}
/******************************************************************************************************/
void Hv_Typec_RegisterRead (UINT8 u8PortNum, UINT16 u16RegOffset, UINT8 *pu8ReadData, UINT8 u8Readlen)
{
    INT32    j;
    UINT32    u32RegAddr;
    UINT32    u32RegVal = 0;
    UINT32    u32DivAddr, u32Offset;
    volatile UINT32    *pu32Data = SET_TO_ZERO;

    u32RegAddr = (u8PortNum == SET_TO_ZERO ? UPD_PORT0_BASE_ADDR : SET_TO_ZERO) + (UINT32)u16RegOffset;        //now only one port support
    u32DivAddr = ((u32RegAddr >> 2) << 2);
    for (j = 0; j < u8Readlen; j++) {
        u32DivAddr = (((u32RegAddr + j) >> 2) << 2);
        u32Offset = (u32RegAddr + j) % 4;

        if ((UINT32)pu32Data != u32DivAddr) {
            pu32Data = (volatile UINT32*)u32DivAddr;            //to confirm the big endian and little endian use case.
            u32RegVal = *pu32Data;
        }
        
        pu8ReadData[j] = (UINT8)((u32RegVal >> (u32Offset * 8)) & 0xFF);
    }
}

/*******************************************************************************************/
/********************UPD Register Read/Write ISR APIs***************************************/
/*******************************************************************************************/

void Hv_Typec_RegisterWriteISR (UINT8 u8PortNum, UINT16 u16RegOffset, 
        UINT8 *pu8WriteData, UINT8 u8WriteDataLen)
{
    Hv_Typec_RegisterWrite(u8PortNum, u16RegOffset, pu8WriteData, u8WriteDataLen);
}

/******************************************************************************************************/
void Hv_Typec_RegisterReadISR (UINT8 u8PortNum, UINT16 u16RegOffset, UINT8 *pu8ReadData, UINT8 u8Readlen)
{
    Hv_Typec_RegisterRead(u8PortNum, u16RegOffset, pu8ReadData, u8Readlen);
}

/*******************************************************************************************/
/************************ Power Management Control APIs ************************************/
/*******************************************************************************************/

#if (HV_TRUE == INCLUDE_POWER_MANAGEMENT_CTRL)

UINT8 UPD_ReturnUPDsActiveSts ()
{
    UINT8 u8IsAllUPDsActive = HV_FALSE;
    
    for (UINT8 u8PortNum = SET_TO_ZERO; u8PortNum < CONFIG_PD_PORT_COUNT; u8PortNum++)
      {
        /*Ignore if port is disabled, so consider only for enabled ports*/
        if (UPD_PORT_ENABLED == DPM_GET_CONFIGURED_PORT_EN(u8PortNum))
        {
            /*UPD_STATE_ACTIVE will be set frequently by respective Alert ISR.
              It means that the appropriate port is active, so skip MCU IDLE*/
            if ((UPD_STATE_ACTIVE == gau8ISRPortState[u8PortNum]) ||
            /*Verify any other IDLE timer is running for all other ports.
            if its running, then lets handle in that timer expire event, so skip MCU
            IDLE for now*/
             (((gau8PortIdleTimerID[u8PortNum]< MAX_CONCURRENT_TIMERS) && \
             (gasPDTimers[gau8PortIdleTimerID[u8PortNum]].u8TimerStPortNum & PDTIMER_STATE ) == PDTIMER_ACTIVE)))
            {
                u8IsAllUPDsActive = HV_TRUE;
                break;
            }
        }
    }
  
    return u8IsAllUPDsActive;
}

/******************************************************************************************************/
void UPD_SetIdleCB (UINT8 u8PortNum, UINT8 u8DummyVariable)
{
    /*Invalidate this Timer ID since it is stored in global*/
    gau8PortIdleTimerID[u8PortNum] = MAX_CONCURRENT_TIMERS;
    
    /*Set gau8PortState to UPD_STATE_IDLE*/
    gau8ISRPortState[u8PortNum] = UPD_STATE_IDLE;

    /*Enable RX_AFE: In order to receive a PD message the RX AFE shall be enabled by SW and the
      trip point set via CC RX DAC Control Register (CC_RX_DAC_CTL) and  CC RX DAC Filter Register
      (CC_RX_DAC_FILT)*/
    
    /*Disable Ring , 48 MHz Oscillator and System clock to put UPD350 to idle*/
    //Hv_Typec_RegByteClearBit (u8PortNum, UPD_CLK_CTL, 
    //        (UPD_RING_OSC_ENABLE | UPD_48MHZ_OSC_ENABLE | UPD_SYS_CLK_ENABLE));
    
    DEBUG_PRINT_PORT_STR (u8PortNum,"UPDHW: Set Port Idle\r\n");
    
    /* Put MCU into IDLE */

    if (HV_FALSE == UPD_ReturnUPDsActiveSts ())
    {
        DEBUG_PRINT_PORT_STR (u8PortNum,"UPDHW: Set MCU IDLE\r\n");
                
        gu8SetMCUIdle = UPD_MCU_IDLE;
    }    
}

/******************************************************************************************************/
void PD_StartIdleTimer (UINT8 u8PortNum)
{
    HV_PSF_HOOK_DISABLE_GLOBAL_INTERRUPT();
                
    /*if UPD350 is active; Restart UPD IDLE Timer*/
    if (UPD_STATE_ACTIVE == gau8ISRPortState[u8PortNum])
    {        
        PDTimer_Kill (gau8PortIdleTimerID[u8PortNum]);
        
        gau8PortIdleTimerID[u8PortNum] = MAX_CONCURRENT_TIMERS;
        
        gau8PortIdleTimerID[u8PortNum] = PDTimer_Start (\
                    UPD_IDLE_TIMEOUT_MS,\
                    UPD_SetIdleCB,\
                    u8PortNum, \
                    (UINT8)SET_TO_ZERO);
                    
        gau8ISRPortState[u8PortNum] = UPD_STATE_STARTED_IDLE_TIMER;
    }
    
    HV_PSF_HOOK_ENABLE_GLOBAL_INTERRUPT();
} 

/******************************************************************************************************/
void UPD_PwrManagementCtrl (UINT8 u8PortNum)
{
   /*Restart IDLE Timer if UPD350 is Active*/
    PD_StartIdleTimer (u8PortNum);

    if (UPD_MCU_IDLE == gu8SetMCUIdle)
    {
        /*Invalidate MCU Idle, Before keeping MCU in Idle clear this set 
         gu8SetMCUidle to Active state for next run */
        gu8SetMCUIdle = UPD_MCU_ACTIVE;

        if (HV_FALSE == UPD_ReturnUPDsActiveSts())
        {
            /* Notification CallBack to the Client, to indicate all the
             UPD350s are in idle state */
            (void)DPM_NotifyClient(u8PortNum, eHV_PSF_UPDS_IN_IDLE);
        }
    } 
}

/********************************************************************************************/
void UPD_InitPwrManagement (UINT8 u8PortNum)
{
    /*Set UPD state as Active in the initialization*/
    gau8ISRPortState[u8PortNum] = UPD_STATE_ACTIVE;

    /*Start idle timer */
    gau8PortIdleTimerID[u8PortNum] = PDTimer_Start(\
                                        UPD_IDLE_TIMEOUT_MS,\
                                        UPD_SetIdleCB,\
                                        u8PortNum, \
                                        (UINT8)SET_TO_ZERO);
    
    /*Set MCU Idle flag as UPD_MCU_ACTIVE*/
    gu8SetMCUIdle = UPD_MCU_ACTIVE;
}

/********************************************************************************************/
#endif /* INCLUDE_POWER_MANAGEMENT_CTRL*/