/**
 * @file hv_drv_TypecTimer.c
 * @brief  PD Generic Timer source file. This file contains the function definitions that make up the 
 *         interface to the Power delivery modules.
 * @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_Gpio.h"
#include "hv_vos_Comm.h"
#include "hv_comm_Assert.h"
#include "hv_drv_Timer.h"

#include "hv_drv_TypecStdInc.h"
#include "hv_drv_TypecTimer.h"

#if TYPEC_CC_VOLCHG_USE_POLLING
static UINT8 u8PreCC1AdcIn = 0;
static UINT8 u8PreCC2AdcIn = 0;
static UINT8 u8LoopCnt = 0;
#endif
static UINT16 u16SrcUnAttachCnt[CONFIG_PD_PORT_COUNT] = {0};
HwTmr* gpstPDTimer  = NULL;
static UCHAR8 gucPDTimerIsStart = HV_FALSE;
UINT64 uiPDTimeTick = 0;
#define SRC_UNATTACHSTATE_EXPIRED_TIME  (2000) //ms

#include "FreeRTOS.h"
#include "task.h"

#if (HV_PROJECT_CONFIG_TYPEC_TASK_DYN == HV_CONFIG_ON)
#define TYPEC_DYNAMIC_TASK_PRIORITY_EN    0
#else
#define TYPEC_DYNAMIC_TASK_PRIORITY_EN    1
#endif

#define URGENT_TASK_TIMEOUT_THRESHOLD    30  //ms

static UCHAR8 s_ucPDTimerUrgentTaskFlag = 0;

void PDTimer_UrgentTaskInit(void)
{

    if(s_ucPDTimerUrgentTaskFlag){
        s_ucPDTimerUrgentTaskFlag = 0;
        vTaskPrioritySet(NULL, TYPEC_NORMAL_TASK_PRIORITY);
    }    

}

void PDTimer_UrgentTaskStart(void)
{

    s_ucPDTimerUrgentTaskFlag++;
    vTaskPrioritySet(NULL, TYPEC_URGENT_TASK_PRIORITY);
    Hv_Vos_TaskYield(); 

}

void PDTimer_UrgentTaskStop(void)
{

    if(!(--s_ucPDTimerUrgentTaskFlag))
    {
        vTaskPrioritySet(NULL, TYPEC_NORMAL_TASK_PRIORITY);
    }

}

static HwTmr* PDTimer_HWTimerInit(VOID)
{
    HwTmrConfig stTmrInit = {0};
    HwTmr *self = NULL;
    HV_MEMSET(&stTmrInit, 0, sizeof(HwTmrConfig));
    /* initialize as interval timer */
    stTmrInit.enChannel = TMR_CH_1;
    stTmrInit.enClockDividedEnable = TMRC_CCDE_DIV_ENABLE;
    stTmrInit.enDivMode = TMR_DIV_CCD_2;
    stTmrInit.enClrEnable = TMRC_CLR_ENABLE;
    stTmrInit.enIntEnable = TMRIVL_TIIE_INT_ENABLE;
    stTmrInit.enZeroClrEnable = TMRIVL_TZCE_ZERO_CLR_ENABLE;
    stTmrInit.pfTmrCb = PDTimer_InterruptHandler;

    self = Hv_Drv_Tmr_Init(&stTmrInit);
    HV_ASSERT_VALID_PTR_RET(self, NULL);
    Hv_Drv_Tmr_SetMsCounter(self, 1); // 1ms loop timer
    Hv_Drv_Tmr_SetInterrupt(self);

    return self;
}

/*******************************************************************************/
VOID Hv_Drv_Typec_Timer_Init(VOID)
{
    gpstPDTimer = PDTimer_HWTimerInit();

    /*Setting all the PD Software timer's state to Non Active during PD stack initialization*/

    for (UINT8 u8TimerID = SET_TO_ZERO; u8TimerID < MAX_CONCURRENT_TIMERS; u8TimerID++)
    {
        gasPDTimers[u8TimerID].u8TimerStPortNum = RESET_TO_ZERO;
#if TYPEC_DYNAMIC_TASK_PRIORITY_EN
        gasPDTimers[u8TimerID].ucTaskPriorityFlag = PDTIMER_NORMAL_TASK_FLAG;
#endif
    }

    if (HV_FALSE == gucPDTimerIsStart)
    {
        Hv_Drv_Tmr_Start(gpstPDTimer);
        gucPDTimerIsStart = HV_TRUE;
    }

    return;
}
/*******************************************************************************/

UINT8 PDTimer_Start (UINT32 u32TimeoutTicks, PDTimerCallback pfnTimerCallback, \
                                UINT8 u8PortNum, UINT8 u8PDState)
{
    UINT8 u8TimerID;

    if (HV_FALSE == gucPDTimerIsStart)
    {
        Hv_Drv_Tmr_Start(gpstPDTimer);
        gucPDTimerIsStart = HV_TRUE;
    }

    /*u32TimeoutTicks is incremented by 1 to make sure guaranteed timer wait is provided*/
    u32TimeoutTicks++;

    /*Find the unused PD Software timer and start the given timeout value with the found timer*/
    for (u8TimerID = SET_TO_ZERO; u8TimerID < MAX_CONCURRENT_TIMERS; u8TimerID++)
    {
        if (((gasPDTimers[u8TimerID].u8TimerStPortNum & PDTIMER_STATE ) == PDTIMER_NON_ACTIVE) || \
                ((gasPDTimers[u8TimerID].u8TimerStPortNum & PDTIMER_STATE)== PDTIMER_EXPIRED))
        {

            #if (HV_TRUE == HV_PSF_CONFIG_16BIT_PDTIMER_COUNTER)
                gasPDTimers[u8TimerID].u16TimeoutTickCnt = (UINT16)u32TimeoutTicks;
            #else
                gasPDTimers[u8TimerID].u32TimeoutTickCnt = u32TimeoutTicks;
            #endif

            /*Store the callback function address and arguments to be passed in the PD Software 
            timer structure*/
            gasPDTimers[u8TimerID].pfnTimerCallback = pfnTimerCallback;
            gasPDTimers[u8TimerID].u8TimerStPortNum = (u8PortNum & PDTIMER_PORT_NUM);
            gasPDTimers[u8TimerID].u8PDState = u8PDState;
            
            /*Setting the PD Software timer state to "Active"*/
            gasPDTimers[u8TimerID].u8TimerStPortNum &= ~PDTIMER_STATE;
            gasPDTimers[u8TimerID].u8TimerStPortNum |= PDTIMER_ACTIVE;
#if TYPEC_DYNAMIC_TASK_PRIORITY_EN
            if(u32TimeoutTicks < URGENT_TASK_TIMEOUT_THRESHOLD)
            {
                gasPDTimers[u8TimerID].ucTaskPriorityFlag = PDTIMER_URGENT_TASK_FLAG;
                PDTimer_UrgentTaskStart();
            }
#endif            
            break;
        }
    }
    return u8TimerID;
}
/**************************************************************************************/
void PDTimer_WaitforTicks (UINT32 u32TimeoutTicks)
{
    UINT8 u8TimerID;
    
    /*u32TimeoutTicks is incremented by 1 to make sure guaranteed timer wait is provided*/
    ++u32TimeoutTicks;
    
    /*Find the unused PD Software timer and start the given timeout value with the found timer*/    
    for (u8TimerID = SET_TO_ZERO; u8TimerID < MAX_CONCURRENT_TIMERS; u8TimerID++)
    {

        if (((gasPDTimers[u8TimerID].u8TimerStPortNum & PDTIMER_STATE ) == PDTIMER_NON_ACTIVE) || ((gasPDTimers[u8TimerID].u8TimerStPortNum & PDTIMER_STATE)== PDTIMER_EXPIRED))
        {

#if (HV_TRUE == HV_PSF_CONFIG_16BIT_PDTIMER_COUNTER)

            gasPDTimers[u8TimerID].u16TimeoutTickCnt = (UINT16)u32TimeoutTicks;
#else
            gasPDTimers[u8TimerID].u32TimeoutTickCnt = u32TimeoutTicks;
#endif
            /*Setting the PDTimer Call back function as NULL*/
            gasPDTimers[u8TimerID].pfnTimerCallback = NULL;
            
            /*Setting the PD Software timer state to "Active"*/
            gasPDTimers[u8TimerID].u8TimerStPortNum &= ~PDTIMER_STATE;
            gasPDTimers[u8TimerID].u8TimerStPortNum |= PDTIMER_ACTIVE;
            
            do
            {
                /*Wait here until the Timer state changes to Timer Expired*/
              
            }while((gasPDTimers[u8TimerID].u8TimerStPortNum & PDTIMER_STATE) == PDTIMER_ACTIVE);
          
            break;
        }        
    }
    
}

/**************************************************************************************/
void PDTimer_Kill (UINT8 u8TimerID)
{  
   /*Disabling Global interrupts, So that Timer Variable will not be corrupted by the 
   PDTimer ISR Handler*/       
   HV_PSF_HOOK_DISABLE_GLOBAL_INTERRUPT();
   
    if (u8TimerID < MAX_CONCURRENT_TIMERS)
    {       
        /*Setting the PD Software timer to "Non Active"  state will disable the PDTimer 
        ISR Handler from calling the callback function*/
        gasPDTimers[u8TimerID].u8TimerStPortNum &= ~PDTIMER_STATE;
        gasPDTimers[u8TimerID].pfnTimerCallback = NULL; 
#if TYPEC_DYNAMIC_TASK_PRIORITY_EN        
        if(gasPDTimers[u8TimerID].ucTaskPriorityFlag == PDTIMER_URGENT_TASK_FLAG)
        {
            gasPDTimers[u8TimerID].ucTaskPriorityFlag = PDTIMER_NORMAL_TASK_FLAG;
            PDTimer_UrgentTaskStop();            
        } 
#endif              
    }
    /*Enabling the Global interrupts*/
    HV_PSF_HOOK_ENABLE_GLOBAL_INTERRUPT();    
}
/**************************************************************************************/
void PDTimer_KillPortTimers (UINT8 u8PortNum)
{
    /*Disabling Global interrupts, So that Timer Variable will not be 
    corrupted by the PDTimer ISR Handler*/
    HV_PSF_HOOK_DISABLE_GLOBAL_INTERRUPT();
#if TYPEC_DYNAMIC_TASK_PRIORITY_EN
    PDTimer_UrgentTaskInit();
#endif    
    /* Resetting all the module's TimerID globals */
    gasPolicyEngine[u8PortNum].u8PETimerID = MAX_CONCURRENT_TIMERS;
    gasTypeCcontrol[u8PortNum].u8TypeCTimerID = MAX_CONCURRENT_TIMERS;
    gasTypeCcontrol[u8PortNum].u8VBUSCheckTmrID = MAX_CONCURRENT_TIMERS;
#if (HV_TRUE == INCLUDE_PD_3_0)
    gasChunkSM [u8PortNum].u8CAorChunkSMTimerID = MAX_CONCURRENT_TIMERS;
#endif
    gasPolicyEngine[u8PortNum].u8PENoResponseTimerID = MAX_CONCURRENT_TIMERS;

#if (HV_TRUE == INCLUDE_POWER_FAULT_HANDLING)
    gasDPM[u8PortNum].u8VBUSPowerGoodTmrID = MAX_CONCURRENT_TIMERS;
    gasDPM[u8PortNum].u8VCONNPowerGoodTmrID = MAX_CONCURRENT_TIMERS;
#endif
    
    #if (HV_TRUE == INCLUDE_PD_SOURCE_PPS || HV_TRUE == INCLUDE_PD_SOURCE_EPR)
    gasDPM[u8PortNum].u8PPSFaultPersistTmrID = MAX_CONCURRENT_TIMERS;
    #endif

    #if (HV_TRUE == INCLUDE_PD_VCONN_SWAP)
    gasDPM[u8PortNum].u8VCONNSwapWaitTmrID = MAX_CONCURRENT_TIMERS;
    gasDPM[u8PortNum].u8DiscoverIdentityTmrID = MAX_CONCURRENT_TIMERS;
    #endif /*INCLUDE_PD_VCONN_SWAP*/

    #if (HV_TRUE == INCLUDE_PD_PR_SWAP)
    gasDPM[u8PortNum].u8PRSwapWaitTmrID = MAX_CONCURRENT_TIMERS;
    #endif 
    
    #if (HV_TRUE == INCLUDE_PD_DR_SWAP)
    gasDPM[u8PortNum].u8DRSwapWaitTmrID = MAX_CONCURRENT_TIMERS;
    #endif

    #if (HV_TRUE == INCLUDE_PD_VDM)
    gasDPM[u8PortNum].u8VDMBusyTmrID = MAX_CONCURRENT_TIMERS;
    #endif 
    
    #if (HV_TRUE == INCLUDE_PD_ALT_MODE)
    gasDPM[u8PortNum].u8AMETmrID = MAX_CONCURRENT_TIMERS;
    #endif 

    gasDPM[u8PortNum].u8CCADCRecheckTmrID = MAX_CONCURRENT_TIMERS;
    /*Setting the PD Software timers of a given port number to "Non Active"  state will disable 
    the PDTimer ISR Handler from calling the callback function*/
    for (UINT8 u8TimerID = SET_TO_ZERO; u8TimerID < MAX_CONCURRENT_TIMERS; u8TimerID++)
    {
        if (((gasPDTimers[u8TimerID].u8TimerStPortNum & PDTIMER_STATE) == PDTIMER_ACTIVE)\
          && ((gasPDTimers[u8TimerID].u8TimerStPortNum & PDTIMER_PORT_NUM) == u8PortNum)
            
#if (HV_TRUE == INCLUDE_POWER_MANAGEMENT_CTRL)
            /*If power management is active don't kill IDLE Timer*/
            && (u8TimerID != gau8PortIdleTimerID[u8PortNum])
#endif
              )
        {
            /*Setting the Timer state to inactive*/
            gasPDTimers[u8TimerID].u8TimerStPortNum &= ~PDTIMER_STATE;
            gasPDTimers[u8TimerID].pfnTimerCallback = NULL;
        }
#if TYPEC_DYNAMIC_TASK_PRIORITY_EN        
        gasPDTimers[u8TimerID].ucTaskPriorityFlag = PDTIMER_NORMAL_TASK_FLAG;
#endif        
    }
    /*Enabling the Global interrupts*/
    HV_PSF_HOOK_ENABLE_GLOBAL_INTERRUPT();
}
/**************************************************************************************/

void PDTimer_InterruptHandler (UINT32 arg)
{
    UINT8 u8TimerID;
    UINT8 u8FSM_State = 0;
    UINT8 DRP_Toggle_State = 0;

#if TYPEC_CC_VOLCHG_USE_POLLING
    UINT8 u8CC1AdcIn;
    UINT8 u8CC2AdcIn;

    if (gasTypeCcontrol[0].u8TypeC_CCCompSta & TYPEC_CC_COMP_CTL_CC1_CC2) 
    {
        u8CC1AdcIn = Hv_Typec_RegReadByte(0, UPD_REG_RW_CC1_ADC_IN);
        u8CC2AdcIn = Hv_Typec_RegReadByte(0, UPD_REG_RW_CC2_ADC_IN);
        
        if (u8CC1AdcIn > (u8PreCC1AdcIn - 4) && u8CC1AdcIn < (u8PreCC1AdcIn + 4) 
            && u8CC2AdcIn > (u8PreCC2AdcIn - 4) && u8CC2AdcIn < (u8PreCC2AdcIn + 4)) {
            u8LoopCnt++;
            if (u8LoopCnt == 5) {
                gasTypeCcontrol[0].u8TypeC_CC1AdcIn = u8CC1AdcIn;
                gasTypeCcontrol[0].u8TypeC_CC2AdcIn = u8CC2AdcIn;
                u8LoopCnt = 0;
            }
        }
        else {
            u8PreCC1AdcIn = u8CC1AdcIn;
            u8PreCC2AdcIn = u8CC2AdcIn;
            u8LoopCnt = 0;
        }
        
    }
#endif

    uiPDTimeTick++;

#if TYPEC_DEBUG_TRACE_EN
    HV_W32(0x11008038, (UINT32)uiPDTimeTick); /* reg_stby_reg7  */
    if((uiPDTimeTick % 10) == 0)
    {
        HV_W32(0x110080A0, (UINT32)Hv_Drv_Typec_GetTaskPriority()); /* reg_stby_reg5  */ 
    }
#endif

    /**
     * This code addresses a hardware bug in Type-C devices where, under certain conditions,
     * the FSM (Finite State Machine) remains in state 2 while the toggle state is 0.
     * This issue has been observed, for example, when shutting down a MacBook Pro M1
     * while connected to a Type-C device. Although this situation theoretically should not occur,
     * hardware engineers have not identified the root cause. Therefore, the following code
     * implements a workaround to mitigate this issue.
     */
    u8FSM_State = Hv_Typec_RegReadByte(0, UPD_REG_RO_FSM_STATE);
    DRP_Toggle_State = Hv_Typec_RegReadByte(0, UPD_REG_RW_DRP_CONFIG) & UPD_DRP_CONFIG_TOGGLE_EN;
    if ((u8FSM_State == TYPEC_FSM_STATE_UNATTACHED_SINK) && (DRP_Toggle_State == 0))
    {
        DEBUG_PRINT_PORT_STR (uiPDTimeTick,"------Force to drp toggle ------\r\n"); 
        // HV_LOG_INFO(TYPEC, "srcunattached state, uiPDTimeTick = %d.", (UINT32)uiPDTimeTick);
        TypeC_DRPEnDisToggle(0, HV_TRUE);
    }

    HV_PSF_HOOK_DISABLE_GLOBAL_INTERRUPT();
    
    for (u8TimerID = SET_TO_ZERO; u8TimerID < MAX_CONCURRENT_TIMERS; u8TimerID++)
    {
         
      /*Checking for a "Active" State PD Software timer*/
      
        if (((gasPDTimers[u8TimerID].u8TimerStPortNum & PDTIMER_STATE) == PDTIMER_ACTIVE))
        {
            
#if (HV_TRUE == HV_PSF_CONFIG_16BIT_PDTIMER_COUNTER)

       /*If the Timeout_Tickcnt value is 0, then the timer has expired*/
          
            if (--gasPDTimers[u8TimerID].u16TimeoutTickCnt == SET_TO_ZERO)
            {
#else
            
            if (--gasPDTimers[u8TimerID].u32TimeoutTickCnt == SET_TO_ZERO)
            {
#endif
                
                if (gasPDTimers[u8TimerID].pfnTimerCallback != NULL)
                {
                    /*Calling the callback function with a set of arguments 
                    namely port number and PD state*/
                    gasPDTimers[u8TimerID].pfnTimerCallback((gasPDTimers[u8TimerID].u8TimerStPortNum & PDTIMER_PORT_NUM), gasPDTimers[u8TimerID].u8PDState);
                    /* Hook to notify that PSF stack is not idle */
                    HV_PSF_HOOK_PDTIMER_EVENT();
                }
                
                /*Setting the timer state as "Timer Expired"*/
                gasPDTimers[u8TimerID].u8TimerStPortNum &= ~PDTIMER_STATE;
                gasPDTimers[u8TimerID].u8TimerStPortNum |= PDTIMER_EXPIRED;                   
#if TYPEC_DYNAMIC_TASK_PRIORITY_EN                
                if(gasPDTimers[u8TimerID].ucTaskPriorityFlag == PDTIMER_URGENT_TASK_FLAG)
                {
                    gasPDTimers[u8TimerID].ucTaskPriorityFlag = PDTIMER_NORMAL_TASK_FLAG;
                    PDTimer_UrgentTaskStop();            
                } 
#endif                                
            }
        }
    }
    
    HV_PSF_HOOK_ENABLE_GLOBAL_INTERRUPT();

}
