/* SPDX-License-Identifier: LGPL-2.1-only OR BSD-3-Clause */
/******************************************************************************
 *
 * This file is provided under a dual license.  When you use or
 * distribute this software, you may choose to be licensed under
 * version 2.1 of the GNU Lesser General Public License
 * ("LGPLv2.1 License") or BSD License.
 *
 * LGPLv2.1 License
 *
 * Copyright(C) 2019 MediaTek Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of version 2.1 of the GNU Lesser General Public
 * License as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html for more
 * details.
 *
 * BSD LICENSE
 *
 * Copyright(C) 2019 MediaTek Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *  * Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *****************************************************************************/

//-----------------------------------------------------------------------------
// Include files
//-----------------------------------------------------------------------------
#include "x_pdwnc.h"
#include "x_bim.h"
#include "x_ckgen.h"
#include "x_os.h"
#include "drvcust_if.h"
#include "x_lint.h"
#include "x_assert.h"
#include "x_pinmux.h"
#include "x_dram.h"
#include "pdwnc_config.h"
#include "pdwnc_debug.h"
#include "pdwnc_entry.h"
#include "pdwnc_t8032.h"
#include "pdwnc_gpio.h"
#include "pdwnc_drvif.h"
#include "ir_if.h"

#include "x_timer.h"

//#undef PDWNC_WRITE32
//#undef PDWNC_READ32

//-----------------------------------------------------------------------------
// Configurations
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
// Constant definitions
//-----------------------------------------------------------------------------


#define T8032_ACK_TIMEOUT 2000
#define T8032_ALIVE_CHECK_RETRY 3// 100
#define T8032_ALIVE_CHECK_INTERVAL 10 // ms
#define T8032_MSGQ_CAPACITY 16

//-----------------------------------------------------------------------------
// Type definitions
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
// Macro definitions
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
// Imported functions
//-----------------------------------------------------------------------------
//extern void HAL_Delay_us(UINT32 u4Micros);
//-----------------------------------------------------------------------------
// Prototypes
//-----------------------------------------------------------------------------
//INT32 _PDWNC_SetT8032(UINT32 u4State);
//INT32 PDWNC_InitT8032(UINT32 u4Addr, UINT32 u4Size);
//INT32 PDWNC_T8032Cmd(PDWNC_T8032_CMD_T *prCmd, PDWNC_T8032_RESPONSE_T *prResponse);
//INT32 PDWNC_ReadSwVersion(UINT32* pu4SwVersion);

//-----------------------------------------------------------------------------
// Static variables
//-----------------------------------------------------------------------------
#ifndef CC_MTK_LOADER
static HANDLE_T _hT8032IsrMsgq;
/*static*/ HANDLE_T _hT8032CmdMutex;
#endif
///*static*/ UINT8 _au1CecData[CEC_XDATA_SIZE];


//-----------------------------------------------------------------------------
// Static functions
//-----------------------------------------------------------------------------
static INT32 _CopyToT8032(UINT32 u4XAddr, UINT32 u4Size, UINT32 u4RiscAddr)
{//for instruction ram copy
    UINT32 i;
  
    volatile UINT32* pu1Ptr = (volatile UINT32*)u4RiscAddr;
    vIO32Write4B(PDWNC_UP_ADDR, u4XAddr);
    for(i = 0; i < u4Size; i+=4)
    {
        vIO32Write4B(PDWNC_UP_DATA, *pu1Ptr);
        pu1Ptr++;
    }    
    return 0;
}

/*static*/ INT32 _CopyToT8032_XDATA(UINT32 u4XAddr, UINT32 u4Size, UINT32 u4RiscAddr)
{//for xdata copy
//though we can only write one byte data into xdata with bus operation, we still need to use DW access for the bus operation! 
    UINT32 i;
    volatile UINT8* pu4Ptr = (volatile UINT8*)u4RiscAddr;
    vIO32Write4B(PDWNC_UP_ADDR, u4XAddr);
    for(i = 0; i < u4Size; i++)
    {
        vIO32Write4B(PDWNC_UP_DATA, *pu4Ptr);
        pu4Ptr++;
    }
    return 0;
}


static INT32 _CopyFromT8032(UINT32 u4XAddr, UINT32 u4Size, UINT32 u4RiscAddr)
{
    UINT32 i;
    volatile UINT32* pu1Ptr = (volatile UINT32*)u4RiscAddr;

    vIO32Write4B(PDWNC_UP_ADDR, u4XAddr);

    for(i = 0; i < u4Size; i+=4)
    {
        *pu1Ptr = u4IO32Read4B(PDWNC_UP_DATA);
        pu1Ptr++;  
    }   
    return 0;
}


/*static*/ INT32 _CopyFromT8032_XDATA(UINT32 u4XAddr, UINT32 u4Size, UINT32 u4RiscAddr)
{
    UINT32 i;
    volatile UINT8* pu1Ptr = (volatile UINT8*)u4RiscAddr;

    vIO32Write4B(PDWNC_UP_ADDR, u4XAddr);

    for(i = 0; i < u4Size; i++)
    {
//        *pu1Ptr = u4IO32Read4B(PDWNC_UP_DATA);
        *pu1Ptr = u1IO32Read1B(PDWNC_UP_DATA);
        pu1Ptr++;  
    }   
    return 0;
}

static INT32 _LoadCode(UINT32 u4Addr, UINT32 u4Size)
{
    if(u4Addr == 0)
    {
        return -1;
    }
    return _CopyToT8032(0x0, u4Size, u4Addr);
}

static INT32 _T8032Cmd(PDWNC_T8032_CMD_T* prCmd, PDWNC_T8032_RESPONSE_T* prResponse)
{
    INT32 i4Ret = 0;
    UINT8 u1Cmd, u1SubCmd;
    UINT32 u4Data;
#ifndef CC_MTK_LOADER  
	UINT16 i2MsgNum;  
    UINT16 u2Idx = 0;
    SIZE_T u4Size = sizeof(UINT32);
    UINT32 u4Msg;
#endif    

    u4Data = DRVCUST_InitGet(eT8032uPOffset);
    if (u4Data == 0)  // here needs to setup 8032uP.
    {
        Printf("Warning! T8032 is not active!");
        return 0;
    }

    VERIFY(prCmd != NULL);
	
    u1Cmd = prCmd->u1Cmd;
    u1SubCmd = prCmd->u1SubCmd;
    u4Data = prCmd->au1Data[0] | (prCmd->au1Data[1] << 8) | (prCmd->au1Data[2] << 16) | (prCmd->au1Data[3] << 24);    
     vIO32WriteFldAlign(PDWNC_INTCLR, 0x1, FLD_AUX_INTCLR);
#ifndef CC_MTK_LOADER
    PDWNC_T8032CMD_SET_CMD(u1Cmd | (u1SubCmd << 8));
    PDWNC_T8032CMD_SET_DATA(u4Data);
	do{
		VERIFY(x_msg_q_num_msgs(_hT8032IsrMsgq, &i2MsgNum) == OSR_OK);
		if(i2MsgNum != 0)
		{
			Printf("Warning! _hT8032IsrMsgq is not empty");
			VERIFY(x_msg_q_receive_timeout(&u2Idx, &u4Msg, &u4Size, &_hT8032IsrMsgq, 1, T8032_ACK_TIMEOUT) == OSR_OK);
			Printf("...flush Msg(%d)\n", u4Msg);				
		}
	}while(i2MsgNum != 0);        
	vIO32WriteFldAlign(PDWNC_ARM_INT, 0x1, FLD_ARM_INT);


    // If the command is not ENTER_STANDBY, we need ACK 
    if((u1Cmd != PDWNC_CMD_ARMT8032_ENTER_STANDBY))
    {
        if (x_msg_q_receive_timeout(&u2Idx, &u4Msg, &u4Size, &_hT8032IsrMsgq, 1, T8032_ACK_TIMEOUT) != OSR_OK)
        {
            Printf("T8032 interrupt error: ack timeout!(Cmd = 0x%x, SubCmd = 0x%x)\n", u1Cmd, u1SubCmd);
            Printf("%s:%d 0xf0008034 Value is 0x%x\n", __FUNCTION__, __LINE__, *((volatile UINT32*)(0xf0008034)));
            // Ack timeout
            return -2;
        }
        if((u4Msg & 0xff) == u1Cmd)
        {
            if(prResponse != NULL)
            {
                prResponse->u1Ack = (UINT8)(u4Msg & 0xff);
                prResponse->au1Data[0] = (UINT8)((u4Msg >> 8) & 0xff);
                prResponse->au1Data[1] = (UINT8)((u4Msg >> 16) & 0xff);
                prResponse->au1Data[2] = (UINT8)((u4Msg >> 24) & 0xff);            
            }
        }
        else
        {      
            Printf("T8032 interrupt error: ack mismatch!(Cmd = 0x%x, SubCmd = 0x%x, Ack = %d)\n", u1Cmd, u1SubCmd, (UINT8)(u4Msg & 0xff));
            // Ack mismatch                    
            i4Ret = -1;                
        }
        
    }
#else    
    PDWNC_T8032CMD_SET_CMD(u1Cmd | ((UINT32)u1SubCmd << 8));
    PDWNC_T8032CMD_SET_DATA(u4Data);
    // If the command is not ENTER_STANDBY, we need ACK 
    if(u1Cmd != PDWNC_CMD_ARMT8032_ENTER_STANDBY)
    {           
        // Wait until T8032 enter non-xdata mode
        UINT32 u4Val = 0;        
        UINT32 i = 0, j = 0;
        
        while(1)
        {          
            vIO32WriteFldAlign(PDWNC_ARM_INT, 0x1, FLD_ARM_INT);
            
            while(i < 10000)
            {
                HAL_Delay_us(100);                  
                if(IO32ReadFldAlign(PDWNC_INTSTA, FLD_AUX_INT_STA))
                {
                    u4Val = PDWNC_T8032CMD_GET_RESPONSE();
                    if((u4Val & 0xff) == u1Cmd)
                    {
                        if(prResponse != NULL)
                        {
                            prResponse->u1Ack = (UINT8)(u4Val & 0xff);
                            prResponse->au1Data[0] = (UINT8)((u4Val >> 8) & 0xff);
                            prResponse->au1Data[1] = (UINT8)((u4Val >> 16) & 0xff);
                            prResponse->au1Data[2] = (UINT8)((u4Val >> 24) & 0xff);            
                        }                            
                    }
                    else
                    {      
                        Printf("T8032 polling error: ack mismatch!(Cmd = 0x%x, SubCmd = 0x%x, Ack = %d)\n", u1Cmd, u1SubCmd,(UINT8)(u4Val & 0xff));
                        // Ack mismatch
                        i4Ret = -1;                
                    }
                                       
                    break;                
                }      
                i++;
            }
                                 
            if(i < 10000)
            {
                	vIO32WriteFldAlign(PDWNC_INTCLR, 0x1, FLD_AUX_INTCLR);
                break;
            }            
            else
            {
                	vIO32WriteFldAlign(PDWNC_INTCLR, 0x1, FLD_AUX_INTCLR);
                i = 0;
            }
            
            if(++j > 2 /*T8032_ACK_TIMEOUT*/)
            {
                Printf("T8032 polling error: ack timeout!(Cmd = 0x%x, SubCmd = 0x%x, Ack = %d)\n", u1Cmd, u1SubCmd,(UINT8)(u4Val & 0xff));
                // Ack timeout
                i4Ret = -2;
                break;
            }
          
        }
    }
    else
    {
        vIO32WriteFldAlign(PDWNC_ARM_INT, 0x1, FLD_ARM_INT);
    }
#endif    
    
    return i4Ret;    

}

//-----------------------------------------------------------------------------
// Inter-file functions
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/**  is the interrupt routine for GPIO pins of PDWNC module.
 *  @param u2Gpio is the gpio number in PDWNC module to trigger interrupt.
 */
//-----------------------------------------------------------------------------
#ifndef CC_MTK_LOADER  
void _PDWNC_T8032Isr(UINT16 u2Vector)
{
    UINT32 u4Val;
    UINT32 u4Size = sizeof(UINT32); 
    UINT16 u2Num;
    	
    x_msg_q_num_msgs (_hT8032IsrMsgq, &u2Num);
    if(u2Num >= T8032_MSGQ_CAPACITY)
    {
        return;
    }
    
    u4Val = PDWNC_T8032CMD_GET_RESPONSE();//u4IO32Read4B(PDWNC_SHREG0);
    x_msg_q_send(_hT8032IsrMsgq, &u4Val, u4Size, 100);    
    
}
//-----------------------------------------------------------------------------
/**  PDWNC_SetT8032 set T8032 to the specified state.
 *  @param u4State T8032 state to set
 */
//-----------------------------------------------------------------------------
INT32 _PDWNC_RegT8032Isr(void)
{
    void (* pfnOldIsr)(UINT16);
    VERIFY(x_msg_q_create(&_hT8032IsrMsgq, "t8032msg", sizeof(UINT32), T8032_MSGQ_CAPACITY) == OSR_OK);
	VERIFY(x_sema_create(&_hT8032CmdMutex, X_SEMA_TYPE_MUTEX, X_SEMA_STATE_UNLOCK) == OSR_OK);    
    // Hook T8032 ISR
    VERIFY(PDWNC_RegIsr((UINT16)PDWNC_INTNO_AUX_INT, _PDWNC_T8032Isr, &pfnOldIsr) == PDWNC_OK);    
    return 0;
}
#endif


//-----------------------------------------------------------------------------
/**  PDWNC_SetupCEC to setup CEC wakeup before entering standby.
 *  @param void
 */
//-----------------------------------------------------------------------------

INT32 _PDWNC_SetupCEC(UINT32 u4XAddr, UINT32 u4Size, UINT8* pu1Data)
{

    INT32 i4Ret = 0;

#ifndef CC_MTK_LOADER
    x_sema_lock(_hT8032CmdMutex, X_SEMA_OPTION_WAIT); //make sure no other thread can interrupt T8032 in this critical time
#endif

    i4Ret |= _PDWNC_SetT8032(PDWNC_T8032_SET_XDATA_ARM);

    // Copy CEC data to XData
    i4Ret |= _CopyToT8032_XDATA(u4XAddr, u4Size, (UINT32)pu1Data);//_PDWNC_TransferCECData(PDWNC_CEC_DATA_TO_T8032);

    i4Ret |= _PDWNC_SetT8032(PDWNC_T8032_SET_XDATA_T8032);


#ifndef CC_MTK_LOADER
    x_sema_unlock(_hT8032CmdMutex);
#endif
    
    return i4Ret; 
}

INT32 _PDWNC_GetT8302CECData(UINT32 u4XAddr, UINT32 u4Size, UINT8* pu1Data)
{

    INT32 i4Ret = 0;

#ifndef CC_MTK_LOADER
    x_sema_lock(_hT8032CmdMutex, X_SEMA_OPTION_WAIT); //make sure no other thread can interrupt T8032 in this critical time
#endif
    i4Ret |= _PDWNC_SetT8032(PDWNC_T8032_SET_XDATA_ARM);

    // Copy CEC data to XData
    i4Ret |= _CopyFromT8032_XDATA(u4XAddr, u4Size, (UINT32)pu1Data);//_PDWNC_TransferCECData(PDWNC_CEC_DATA_TO_ARM);

    i4Ret |= _PDWNC_SetT8032(PDWNC_T8032_SET_XDATA_T8032);

#ifndef CC_MTK_LOADER
    x_sema_unlock(_hT8032CmdMutex);
#endif
    
    return i4Ret; 
}


//-----------------------------------------------------------------------------
/**   _PDWNC_RequestT8032TurnOffSys request T8032 to entern standby mode
 */
//-----------------------------------------------------------------------------
static INT32 _PDWNC_RequestT8032TurnOffSys(void)
{
    INT32 i4Ret = 0;
    PDWNC_T8032_CMD_T rCmd;
	UINT32 i;
   // x_memset(&rCmd, 0, sizeoof(PDWNC_T8032_CMD_T));
   
#if defined(CC_MT5891) && defined(CC_DRAM_POWER_CONTROL_BY_T8032)
	UINT32 u4Gpio,u4Pol;
	if(!((DRVCUST_InitQuery(eSuspendDramPowGpio, (UINT32 *)&u4Gpio) < 0) || (DRVCUST_InitQuery(eSuspendDramPowPol, (UINT32 *)&u4Pol) < 0)))
    {
        rCmd.au1Data[0] = u4Gpio - OPCTRL0;
		rCmd.au1Data[1] = u4Pol;
		vIO32WriteFldAlign(PDWNC_T8032_INTEN, 0x1, FLD_T8032_WAKEUP_INT_EN); 
    }
	else
	{
		rCmd.au1Data[0] = 0;
		rCmd.au1Data[1] = 0;
	}
	rCmd.au1Data[2] = rCmd.au1Data[3] = 0;
#else
   rCmd.au1Data[0] = rCmd.au1Data[1]= rCmd.au1Data[2] = rCmd.au1Data[3] = 0;
#endif
   if(IO32ReadFldAlign(PDWNC_UP_CFG, FLD_XDATA_ACC))
   {
      vIO32WriteFldAlign(PDWNC_UP_CFG, 0x0, FLD_XDATA_ACC); //let T8032 control xdata
   }
   //david : Switch operation of  CEC, UART, ENGINE are not necessary in MT5387/63
   vIO32WriteFldAlign(PDWNC_UP_CFG, 0x1, FLD_VIR_EN);

		
#if defined(CC_SUPPORT_STR_CORE_OFF)
	if(PDWNC_IsSuspend())
	{		
		rCmd.u1Cmd = PDWNC_CMD_ARM8032_ENTER_SUSPEND;		
		#ifdef NDEBUG
		if((IO32ReadFldAlign(PDWNC_CONCFG1,FLD_RG_DIS_SUSPEND_WDT) == 0))
		{
			rCmd.au1Data[0] = (UINT8)DRVCUST_InitGet(eSuspendWdtTimer);
		}
		#endif
		#if defined(CC_MT5891)
		if(IS_IC_5891_SM())
		{
			rCmd.au1Data[2] = 0x1; //wukong SM.
		}
		#endif
	}
	else
#endif
	{
    	rCmd.u1Cmd = PDWNC_CMD_ARMT8032_ENTER_STANDBY;
	}
#ifdef CC_MTK_LOADER
    rCmd.u1SubCmd = 0x1; // AC_ON->Standby
#else
    rCmd.u1SubCmd = 0x2; // AC_ON->Normal->Standby
#endif
    // Signal 8032 to enter standby
    i4Ret = PDWNC_T8032Cmd(&rCmd, NULL);

    LOG(7, "Signal T8032 to write power down entry code!\n"); 
	
	#if defined(CC_SUPPORT_STR_CORE_OFF)
	if(!PDWNC_IsSuspend())
	#endif
	for(i=0;i<T8032_ACK_TIMEOUT;i++)
	{
   		 HAL_Delay_us(1000);
	}

    return i4Ret;
}

//-----------------------------------------------------------------------------
/**  PDWNC_SetT8032 set T8032 to the specified state.
 *  @param u4State T8032 state to set
 */
//-----------------------------------------------------------------------------
INT32 _PDWNC_SetT8032(UINT32 u4Set)
{
    INT32 i4Ret = 0;
    UINT32 u4Val;   
    PDWNC_T8032_CMD_T rCmd;
	
	x_memset((void *)&rCmd, 0, sizeof(PDWNC_T8032_CMD_T));
	
    // PDWNC_T8032_RESPONSE_T rResponse;    
    if(u4Set == PDWNC_T8032_SET_STANDBY)
    {//david: this path will not go through OS API (x_sema_lock)
        return _PDWNC_RequestT8032TurnOffSys();
    }    
#ifndef CC_MTK_LOADER
    x_sema_lock(_hT8032CmdMutex, X_SEMA_OPTION_WAIT);
#endif
    
    if(u4Set == PDWNC_T8032_SET_XDATA_ARM)    
    {       
        u4Val = IO32ReadFldAlign(PDWNC_UP_CFG,FLD_T8032_RST);
        
        // If T8032 is inactive (reset)             
        if(u4Val)
        {
            LOG(7, "XDATA belongs to ARM already!\n");                        
#ifndef CC_MTK_LOADER
            x_sema_unlock(_hT8032CmdMutex);
#endif             
            return 0;
        }
        // If T8032 is active (reset released)    
        else
        {        
          rCmd.u1Cmd = PDWNC_CMD_ARMT8032_XDATA_SWITCH;
          rCmd.u1SubCmd = 0x1;

          MSET_XDATA_TO_ARM_FLAG();

          //Printf("Set XDATA to ARM!\n");
          // Signal 8032 to change to non-xdata mode
          _T8032Cmd(&rCmd, NULL);
            // Really set xdata access right to ARM
          vIO32WriteFldAlign(PDWNC_UP_CFG, 0x1, FLD_XDATA_ACC);//switch xdata to ARM
          LOG(7, "Switch XDATA to ARM!\n");            
        }

    }
    else if(u4Set == PDWNC_T8032_SET_XDATA_T8032)
    {

        vIO32WriteFldAlign(PDWNC_UP_CFG, 0x0, FLD_XDATA_ACC);//switch xdata to T8032
        MCLR_XDATA_TO_ARM_FLAG();
        LOG(7, "Switch XDATA to T8032!\n");    

        
    }    	
    else if(u4Set == PDWNC_T8032_SET_ENGINE_ARM)//useless in MT5387
    {
        rCmd.u1Cmd = PDWNC_CMD_ARMT8032_ENGINE_SWITCH;
        rCmd.u1SubCmd = 0x1;

        // Signal 8032 to release other engine control right
        if(_T8032Cmd(&rCmd, NULL) != PDWNC_OK)
        {
            LOG(5, "Switch other engine to ARM error: T8032 not response!\n");
            i4Ret = -1;
        }   
   	  // Disable 8032 engine access   	  

        u4Val = u4IO32Read4B(PDWNC_UP_CFG);
        u4Val &= ~(1U << 16);   	    	 
        vIO32Write4B(PDWNC_UP_CFG, u4Val);
        LOG(7, "Switch other engine to ARM successfully!\n");        
    }    
    else if(u4Set == PDWNC_T8032_SET_ENGINE_T8032)////useless in MT5387
    {
   	  // Disable 8032 engine access   	  
        u4Val = u4IO32Read4B(PDWNC_UP_CFG);
        u4Val |= (1U << 16);   	    	 
        vIO32Write4B(PDWNC_UP_CFG, u4Val);
        
        rCmd.u1Cmd = PDWNC_CMD_ARMT8032_ENGINE_SWITCH;
        rCmd.u1SubCmd = 0x2;    
        
        // Signal 8032 to get other engine control right        
        if(_T8032Cmd(&rCmd, NULL) != PDWNC_OK)
        {
            LOG(5, "Switch other engine to T8032 error: T8032 not response!\n");
            i4Ret = -1;
        }

        LOG(7, "Switch other engine to T8032 successfully!\n");        
    }        
    else if(u4Set == PDWNC_T8032_SET_NORMAL)
    {
        rCmd.u1Cmd = PDWNC_CMD_ARMT8032_ENTER_NORMAL;
        rCmd.u1SubCmd = 0x2;
        
        // Signal 8032 to enter normal mode
        _T8032Cmd(&rCmd, NULL);
        
        LOG(7, "Signal T8032 to switch to normal mode!\n"); 
    }
    else
    {
        i4Ret = -1;
    }        

#ifndef CC_MTK_LOADER
    x_sema_unlock(_hT8032CmdMutex);
#endif
    
    return i4Ret;
}

//-----------------------------------------------------------------------------
/**  PDWNC_InitT8032 to initialize T8032.
 *  @param void.
 */
//-----------------------------------------------------------------------------
INT32 _PDWNC_InitT8032(UINT32 u4Addr, UINT32 u4Size)
{
    INT32 i4Ret = 0;
    UINT32 u4Val;    
    UINT32 u4IrrxType;
    UINT32 i = 0;
    vIO32WriteFldAlign(PDWNC_ARM_INTEN, 0x1, FLD_AUX_INTEN);
    vIO32WriteFldAlign(PDWNC_RESRV5,0x0,FLD_RG_WDT_DRAMSR);
    // Enable IR interrupt & wakeup

    i4Ret = DRVCUST_InitQuery(eIrrxType, &u4IrrxType);   
    i4Ret = IRRX_InitMtkIrRegSetup(u4IrrxType);   

    vIO32WriteFldAlign(PDWNC_ARM_INTEN, 0x1, FLD_IR_INTEN);
	#ifndef __MODEL_slt__
    vIO32Write4B(PDWNC_RESRV0,BSP_GetIcVersion());//Record IC version.
    #endif
    //vIO32WriteFldAlign(PDWNC_WAKEN, 0x1, FLD_IR_WAKEN);
    // Set reset count from 16/27 seconds to 5/27 seconds
    vIO32Write4B(PDWNC_WKRSC, DRVCUST_InitGet(eSysResetTimer));
	Printf("IR DATA register : 0x%8x\n",u4IO32Read4B(PDWNC_IRDATA0));
    // If A/C on, load uCode to T8032 except for mt8223 
    u4Val = PDWNC_ReadWakeupReason();
#if defined(CC_CMDLINE_SUPPORT_BOOTREASON)
	if(u4Val == PDWNC_WAKE_UP_REASON_WATCHDOG)
	{
		switch(u1IO32Read1B(PDWNC_RESRV5))
		{
			case 1:
			case 2:
				break;
			default:
				vIO32Write1B(PDWNC_RESRV5,0x03);
				break;
		}
	}
	else
	{
		vIO32Write1B(PDWNC_RESRV5,0x00);
	}
#endif
    if((u4Val == PDWNC_WAKE_UP_REASON_AC_POWER) || (u4Val == PDWNC_WAKE_UP_REASON_WATCHDOG))

    {    
        UINT32 u4Val;
        UINT8* pu1Buf = x_mem_alloc(u4Size);
        if(NULL == pu1Buf)
		{
			return 1;
		}
        Printf("T8032 init A/C on case loader stage...\n");      
        vIO32WriteFldAlign(PDWNC_UP_CFG, 0x0, FLD_VIR_EN);
        vIO32WriteFldAlign(PDWNC_UP_CFG, 0x1, FLD_T8032_RST);	
        vIO32WriteFld(PDWNC_UP_CFG, 0x1, FLD_XDATA_ACC);//switch xdata to ARM
        // Load uP code              
        VERIFY(_LoadCode(u4Addr, u4Size) == 0);        
        
        _CopyFromT8032(0x0, u4Size, (UINT32)pu1Buf);
        if(x_memcmp((void*)u4Addr, (void*)pu1Buf, u4Size) != 0)
        {
            Printf("Load T8032 FW fail!!\n");
            return 1;// keep vir_en = 0 && t8032_rst = 1 for T8032_ALIVE macro in pdwnc_drvif.h
        }
        
        Printf("Load T8032 FW (addr: 0x%8x, size: %d)success!!\n", u4Addr, u4Size);

#if PDWNC_T8032_UART_PD_ENABLE
		vIO32Write4B(PDWNC_PINMUX0, (u4IO32Read4B(PDWNC_PINMUX0) & 0xffff8fff) | 0x00003000);
		
        //Set UART transparent mode
        vIO32Write4B(PDWNC_STAB, 0xe2);//david: need to confirm...     
#endif        
        vIO32WriteFldAlign(PDWNC_UP_CFG, 0x0, FLD_XDATA_ACC);
        vIO32WriteFldAlign(PDWNC_UP_CFG, 0x1, FLD_VIR_EN);
	
        vIO32WriteFldAlign(PDWNC_UP_CFG, 0x0, FLD_T8032_RST);
        vIO32WriteFldAlign(PDWNC_INTCLR, 0x1, FLD_AUX_INTCLR);
        HAL_Delay_us(500); //wait T8032 init ready
        for(i = 0; i < T8032_ALIVE_CHECK_RETRY; i++)
        {
            if((i4Ret = PDWNC_ReadSwVersion(&u4Val)) == PDWNC_OK)
            {
                Printf("T8032 FW version: %d\n", u4Val);
                break;
            }
            Printf("T8032 FW version read error!\n");                        
            HAL_Delay_us(T8032_ALIVE_CHECK_INTERVAL);                                        
        }                     
    }

    if( i == T8032_ALIVE_CHECK_RETRY)
    {
    	vIO32WriteFldAlign(PDWNC_UP_CFG, 0x0, FLD_VIR_EN);
    	vIO32WriteFldAlign(PDWNC_UP_CFG, 0x1, FLD_T8032_RST);	
    	vIO32WriteFld(PDWNC_UP_CFG, 0x1, FLD_XDATA_ACC);//switch xdata to ARM
        Printf("Read veriosn fail, set t8032 as reset state!\n");                        	
	    return 1;// keep vir_en = 0 && t8032_rst = 1 for T8032_ALIVE macro in pdwnc_drvif.h
    }
#ifndef CC_IR_POWER_BUTTON_RECEIVE_PRELOADER      
    else
    {

#if PDWNC_T8032_UART_PD_ENABLE
		vIO32Write4B(PDWNC_PINMUX0, (u4IO32Read4B(PDWNC_PINMUX0) & 0xffff8fff) | 0x00003000);
		
        //Set UART transparent mode
        vIO32Write4B(PDWNC_STAB, 0xe2);//david: need to confirm...     
#endif            
        PDWNC_T8032_CMD_T rCmd;
        rCmd.u1Cmd = PDWNC_CMD_ARMT8032_ENTER_NORMAL;
        rCmd.u1SubCmd = 1; // loader stage
        rCmd.au1Data[0] = rCmd.au1Data[1]= rCmd.au1Data[2] = rCmd.au1Data[3] = 0;  
        if(_T8032Cmd(&rCmd, NULL))
        {
            vIO32WriteFldAlign(PDWNC_UP_CFG, 0x0, FLD_VIR_EN);
            vIO32WriteFldAlign(PDWNC_UP_CFG, 0x1, FLD_T8032_RST);	
            vIO32WriteFld(PDWNC_UP_CFG, 0x1, FLD_XDATA_ACC);//switch xdata to ARM
            Printf("Enter normal state fail, set t8032 as reset state!\n");                        	
            return 1;// keep vir_en = 0 && t8032_rst = 1 for T8032_ALIVE macro in pdwnc_drvif.h
        }
        else
        {
            Printf("T8032 change to loader stage...\n");             
         }    
    }       
#endif

    return i4Ret;
}

//-----------------------------------------------------------------------------
// Public functions
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/* PDWNC_InitT8032() To initial T8032 uP with 8032 binary info.
 *
 * @param u4Offset  8032 binary pointer offset.
 * @param u4Size    8032 binary size.
 * @retval 0        SUCCESS.
 *  @retval   1		    FAIL.
 */
//-----------------------------------------------------------------------------
INT32 PDWNC_InitT8032(UINT32 u4Offset, UINT32 u4Size)
{
    return _PDWNC_InitT8032(u4Offset, u4Size);

}

//-----------------------------------------------------------------------------
/** PDWNC_T8032Cmd to issue cmd to T8032.
 *  @param u1Flag the power down flag to set.
 */
//-----------------------------------------------------------------------------
INT32 PDWNC_T8032Cmd(PDWNC_T8032_CMD_T* prCmd, PDWNC_T8032_RESPONSE_T* prResponse)
{
    INT32 i4Ret = 0;
#ifndef CC_MTK_LOADER
    x_sema_lock(_hT8032CmdMutex, X_SEMA_OPTION_WAIT);
#endif
    
    i4Ret = _T8032Cmd(prCmd, prResponse);

#ifndef CC_MTK_LOADER
    x_sema_unlock(_hT8032CmdMutex);
#endif

    return i4Ret;
}

//-----------------------------------------------------------------------------
/** PDWNC_ReadSwVersion reads SW version from T8032.
 *  @param u2Gpio is the gpio number in PDWNC module to trigger interrupt.
 */
//-----------------------------------------------------------------------------
INT32 PDWNC_ReadSwVersion(UINT32* pu4SwVersion)
{
    INT32 i4Ret = 0;
    PDWNC_T8032_CMD_T rCmd;
    PDWNC_T8032_RESPONSE_T rResponse;

    rCmd.u1Cmd = PDWNC_CMD_ARMT8032_QUERY_SW_VERSION;
    rCmd.u1SubCmd = 0x0;
    rCmd.au1Data[0] = rCmd.au1Data[1]= rCmd.au1Data[2] = rCmd.au1Data[3] = 0;    
    // Signal 8032 to enter normal mode
    i4Ret = PDWNC_T8032Cmd(&rCmd, &rResponse);

    *pu4SwVersion = (UINT32)rResponse.au1Data[0];

    return i4Ret;
}
//-----------------------------------------------------------------------------
/** PDWNC_ReadT8032WakeupReason reads wakeup reason & subreason from T8032.
 */
//-----------------------------------------------------------------------------
INT32 PDWNC_ReadT8032WakeupReason(UINT32* pu4WakReason , UINT32* pu4SubReason)
{
    INT32 i4Ret = 0;
    PDWNC_T8032_CMD_T rCmd;
    PDWNC_T8032_RESPONSE_T rResponse;

    rCmd.u1Cmd = PDWNC_CMD_ARMT8032_READ_WAKEUP_REASON;
    rCmd.u1SubCmd = 0x0;
    rCmd.au1Data[0] = rCmd.au1Data[1]= rCmd.au1Data[2] = rCmd.au1Data[3] = 0;    
    // Signal 8032 to enter normal mode
    i4Ret = PDWNC_T8032Cmd(&rCmd, &rResponse);

    *pu4WakReason = (UINT32)rResponse.au1Data[0];
	if(pu4SubReason != NULL)
	{
		*pu4SubReason = (UINT32)rResponse.au1Data[1];
	}
    return i4Ret;
}


//-----------------------------------------------------------------------------
/** PDWNC_SetUartPDBaudrate to set system error code.
 *  @param u1ErrorCode the error code.
 */
//-----------------------------------------------------------------------------

INT32 PDWNC_SetUartPDBaudrate(UINT8 u1baudrate)
{
    PDWNC_T8032_CMD_T rCmd;
    rCmd.u1Cmd = PDWNC_CMD_ARMT8032_UART_PD_BAUDRATE;
    rCmd.u1SubCmd = u1baudrate;
    rCmd.au1Data[0] = rCmd.au1Data[1]= rCmd.au1Data[2] = rCmd.au1Data[3] = 0;    
    return PDWNC_T8032Cmd(&rCmd, NULL);
}

#if defined(CC_ENABLE_REBOOT_REASON) && defined(ANDROID) && defined(CC_EMMC_BOOT)

INT32 PDWNC_Write_T8032_XData(UINT32 u4XAddr, UINT32 u4Size, const void *u4RiscAddr)
{
    int ret = 1;

    if(_PDWNC_SetT8032(PDWNC_T8032_SET_XDATA_ARM))
    {
            return 0;
    }

    // Copy data to XData
    if(_CopyToT8032_XDATA(u4XAddr, u4Size, (UINT32)u4RiscAddr))
    {
            ret = 0;
    }

    if(_PDWNC_SetT8032(PDWNC_T8032_SET_XDATA_T8032))
    {
            ret = 0;
    }

    return ret;
}

INT32 PDWNC_Read_T8032_XData(UINT32 u4XAddr, UINT32 u4Size, const void *u4RiscAddr)
{
    int ret = 1;

    if(_PDWNC_SetT8032(PDWNC_T8032_SET_XDATA_ARM))
    {
            return 0;
    }

    // Copy data to XData
    if(_CopyFromT8032_XDATA(u4XAddr, u4Size, (UINT32)u4RiscAddr))
    {
            ret = 0;
    }

    if(_PDWNC_SetT8032(PDWNC_T8032_SET_XDATA_T8032))
    {
            ret = 0;
    }

    return ret;
}
#endif

