/* 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.
 *
 *****************************************************************************/

/*-----------------------------------------------------------------------------
 *
 * $Author: weibo.li $
 * $Date: 2016/01/12 $
 * $RCSfile: pdwnc_srvad.c,v $
 * $Revision: #11 $
 *
 *---------------------------------------------------------------------------*/

/** @file pdwnc_srvad.c
 *  pdwnc_srvad.c The PDWNC (Power DoWN Controller) interface
 */


//-----------------------------------------------------------------------------
// Include files
//-----------------------------------------------------------------------------
#include "x_pinmux.h"
#include "x_pdwnc.h"
#include "x_bim.h"
#include "x_assert.h"
#include "x_os.h"
#include "x_ckgen.h"
#include "x_timer.h"
#include "x_hal_arm.h"
#include "x_gpio.h"
#include "drvcust_if.h"
#include "pdwnc_config.h"
#include "pdwnc_debug.h"
#include "pdwnc_entry.h"
#include "pdwnc_gpio.h"
#include "pdwnc_t8032.h"
#include "pdwnc_drvif.h"
#include "sif_if.h"
#include "eeprom_if.h"

#include "ir_if.h"
#include "rtc_if.h"

//#undef PDWNC_WRITE32
//#undef PDWNC_READ32

//-----------------------------------------------------------------------------
// Configuration
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Constant definitions
//-----------------------------------------------------------------------------
#define PDWNC_WAKTOP(X) ((X==0)?PDWNC_WAKTOP0:((X==1)?PDWNC_WAKTOP1:((X==2)?PDWNC_WAKTOP2:(X==3)?PDWNC_WAKTOP3:\
						((X==4)?PDWNC_WAKTOP4:((X==5)?PDWNC_WAKTOP5:((X==6)?PDWNC_WAKTOP6:((X==7)?PDWNC_WAKTOP7:\
						((X==8)?PDWNC_WAKTOP8:PDWNC_WAKTOP9))))))))
#define PDWNC_WAKBTM(X) ((X==0)?PDWNC_WAKBTM0:((X==1)?PDWNC_WAKBTM1:((X==2)?PDWNC_WAKBTM2:(X==3)?PDWNC_WAKBTM3:\
						((X==4)?PDWNC_WAKBTM4:((X==5)?PDWNC_WAKBTM5:((X==6)?PDWNC_WAKBTM6:((X==7)?PDWNC_WAKBTM7:\
						((X==8)?PDWNC_WAKBTM8:PDWNC_WAKBTM9))))))))
#define PDWNC_CHCFG(X) ((X==0)?PDWNC_CHCFG0:((X==1)?PDWNC_CHCFG1:((X==2)?PDWNC_CHCFG2:(X==3)?PDWNC_CHCFG3:\
						((X==4)?PDWNC_CHCFG4:((X==5)?PDWNC_CHCFG5:((X==6)?PDWNC_CHCFG6:((X==7)?PDWNC_CHCFG7:\
						((X==8)?PDWNC_CHCFG8:PDWNC_CHCFG9))))))))


//-----------------------------------------------------------------------------
// Static variables
//-----------------------------------------------------------------------------
static BOOL _fgServoADCInit = 0;
#if 0//#ifndef CC_MTK_LOADER
static const INT8 _ai1PdwncServoGpio2Int[TOTAL_SERVOADC_NUM] =
{
    -1, -1, 20, 21, 22, 23
};
#endif


//-----------------------------------------------------------------------------
// Prototypes 
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Static functions
//-----------------------------------------------------------------------------
#ifndef CC_MTK_LOADER
#ifndef CC_ENABLE_HW_SERVO_ADC  
static HANDLE_T _hServoADMutex;
#endif
#endif /* CC_MTK_LOADER */
//-----------------------------------------------------------------------------
// Inter-file functions
//-----------------------------------------------------------------------------
void _PDWNC_ReadServoADCWakeupStatus(UINT32* pu4Val)
{
     VERIFY(pu4Val != NULL);
    *pu4Val = (_PDWNC_ReadWakeupStatus(0) & (WAK_ALLSRVAD));
}

void _PDWNC_SetupServoADC(UINT32 *pu4PowerDownEn)
{
    INT32 i, i4LastCh = -1;
    UINT32 u4Val, u4DkBit;
    UINT32 u4Max, u4Min;
    SGL_SERVO_T   *prSingleServoKeyList = NULL;
    
    ASSERT(pu4PowerDownEn != NULL);
    if (!PDWNC_SetupFpWakeup(NULL))
    {
        Printf("FP wakeup disable\n");
        return;
    }
#ifdef CC_NO_ACD_STANDBY_SETTING
    // if ServoADC is enabled, skip this setting, because this is set.
    {        
        Printf("Power down Enable:0x%08x\n", *pu4PowerDownEn);
        return;
    }
#endif
    u4DkBit = DRVCUST_InitGet(eKeyPadWakeupBit);
    i = DRVCUST_InitQuery(eSingleServoKeyList, (UINT32 *)(void *)&prSingleServoKeyList);
    if (i < 0)
    {
        return;
    }

    if (!((*pu4PowerDownEn) & WAK_IRRX))
    {
        u4DkBit = DKBIT_POWER;
    }

    // disable ServoADC working. --- mustang channel b,c,d,e,f for power detect
	vIO32WriteFldAlign(PDWNC_SRVCFG1, u4IO32Read4B(PDWNC_SRVCFG1) & (~0x3FF), FLD_SRVCH_EN);
	// Clear ServoADC output data and channel status.
	vIO32WriteFldAlign(PDWNC_SRVCLR, 0x1, FLD_ADCLR);
	//Printf("The total servoadc num = %d .\n",MAX_SERVOADC_NUM);
	for (i=0; i<MAX_SERVOADC_NUM; i++)//MAX_SERVOADC_NUM
	{
		vIO32WriteFldAlign(PDWNC_WAKTOP(i), 0x0, FLD_CH0TOP);
		vIO32WriteFldAlign(PDWNC_WAKBTM(i), 0x0, FLD_CH0BTM);
		vIO32WriteFldAlign(PDWNC_CHCFG(i), 0x0, FLD_CH0WKC);
	}

    // Do the Servo wakeup initialization
    i = 0;
    u4Max = 0;    
    u4Min = 0xff;//bit [9:2] 
    while ((prSingleServoKeyList) &&
            (prSingleServoKeyList[i].u4Key != 0) &&
            (prSingleServoKeyList[i].u4Key != DKBIT_NONE))
    {
        if (prSingleServoKeyList[i].u4Key & u4DkBit)
        {
            if ((prSingleServoKeyList[i].u4Top > 0x3ff) ||
                (prSingleServoKeyList[i].u4Servo >= (ADIN0 + TOTAL_SERVOADC_NUMBER)) ||
                (prSingleServoKeyList[i].u4Btm > 0x3ff) ||
                (prSingleServoKeyList[i].u4Btm >= prSingleServoKeyList[i].u4Top))
            {
                LOG(1, "Error Power button setting\n");
                i++;
                continue;
            }
            // In case of old style servo ADC numbering
            if(prSingleServoKeyList[i].u4Servo >= ADIN0)
            {
               prSingleServoKeyList[i].u4Servo -= ADIN0; 
            }

            // Make sure enable PAD
            u4Val = PDWNC_ReadServoADCChannelValue(prSingleServoKeyList[i].u4Servo);

			//Get the range
            if(prSingleServoKeyList[i].u4Servo != i4LastCh)
            {//reset init value while setting another channel...
                u4Max = 0;    
                u4Min = 0x3ff;    
            }
            else        
            {            
                u4Max = IO32ReadFldAlign(PDWNC_WAKTOP(prSingleServoKeyList[i].u4Servo), FLD_CH0TOP);
                u4Min = IO32ReadFldAlign(PDWNC_WAKBTM(prSingleServoKeyList[i].u4Servo), FLD_CH0BTM);
            }
            i4LastCh = prSingleServoKeyList[i].u4Servo;                    
            // Set Servo ADC value            
            u4Val = ((u4Max < prSingleServoKeyList[i].u4Top) ? (prSingleServoKeyList[i].u4Top) : u4Max) & 0x3FF;
			vIO32WriteFldAlign(PDWNC_WAKTOP(prSingleServoKeyList[i].u4Servo), u4Val, FLD_CH0TOP);	           
            u4Val = ((u4Min > prSingleServoKeyList[i].u4Btm) ? (prSingleServoKeyList[i].u4Btm) : u4Min) & 0x3FF; 
			vIO32WriteFldAlign(PDWNC_WAKBTM(prSingleServoKeyList[i].u4Servo), u4Val, FLD_CH0BTM);   
			
            if(u4Val)
                u4Val = CH_WAK_MID_EN & CH_MASK;
            else
                u4Val = (CH_WAK_MID_EN | CH_WAK_BTM_EN) & CH_MASK;
			vIO32WriteFldAlign(PDWNC_CHCFG(prSingleServoKeyList[i].u4Servo), u4Val, FLD_CH0WKC);
            u4Val = 0;
            *pu4PowerDownEn |= PDWNC_SRVAD_WAKEN(PDWNC_WAK_SRVADX(prSingleServoKeyList[i].u4Servo));
        }
        i++;
    }
}

//-----------------------------------------------------------------------------
// Public functions
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void PDWNC_InitServoADC(void)
{
    if(_fgServoADCInit == 0)
    {        
    	vIO32WriteFldAlign(PDWNC_CLKPDN, 0x0,FLD_SRVPD);
    	vIO32WriteFldAlign(PDWNC_SADCCFG0, 0x0, FLD_RG_SRV_MODE_SEL);
		vIO32WriteFldAlign(PDWNC_SADCCFG0, 0x1, FLD_RG_SRV_BIAS_OPTION);
    	vIO32WriteFldAlign(PDWNC_SRVCFG0, 0x0, FLD_SWEN);
    	vIO32WriteFldAlign(PDWNC_SRVCFG0, 0x1, FLD_HWEN);
		
    #ifndef CC_MTK_LOADER    
        VERIFY(x_sema_create(&_hServoADMutex, X_SEMA_TYPE_MUTEX, X_SEMA_STATE_UNLOCK) == OSR_OK);    
    #endif    
    	//UNUSED(PDWNC_InitServoGpio());
    	_fgServoADCInit = 1;
    }
}

#if defined(CC_MT5891)
#define EFUSE_TS1_ADDR 0xF00086C8
#define EFUSE_TS2_ADDR 0xF00086CC
UINT32 PDWNC_ServoADCCalibration(UINT32 u4Val)
{
	UINT32 u2GeTemp_num;
	UINT32 u2OeTemp_num;
	UINT32 temp0,temp1;
	UINT32 u4adc_ge, u4adc_oe, u4adc_cali_en;
	UINT32 u4CaliVal;
	
	temp0 = u4IO32Read4B(EFUSE_TS1_ADDR);
	temp1 = u4IO32Read4B(EFUSE_TS2_ADDR);
	#ifndef CC_MTK_LOADER
	LOG(2,"[PDWNC ServoADC calibration] Reg(0xF00086C8)=0x%x, Reg(0xF00086CC)=0x%x\n", temp0, temp1);
	#endif
	u4adc_ge = (temp1 & 0xFFC00000)>>22;
	u4adc_oe = (temp1 & 0x003FF000)>>12;
	u4adc_cali_en = (temp0 & 0x00000001);

	if(u4adc_cali_en == 0)
	{
		return u4Val;
	}
	
	if(u4adc_oe < 512)
	{
		u2OeTemp_num = u4Val*4096*4 + ((512 - u4adc_oe)*40960)/28;
	}	
	else
	{
		if(u4Val*4096*4 < (((u4adc_oe - 512)*40960)/28))
		{
			u2OeTemp_num = 0;
		}
		else
		{
			u2OeTemp_num = u4Val*4096*4 - ((u4adc_oe - 512)*40960)/28;
		}
	}		

	if(u4adc_ge < 512)
	{
		u2GeTemp_num = (512 - u4adc_ge);
		u4CaliVal = u2OeTemp_num /(4096 - u2GeTemp_num);
	}	
	else
	{
		u2GeTemp_num = (u4adc_ge - 512);
		u4CaliVal = u2OeTemp_num/(4096 + u2GeTemp_num);
	}

	if(u4CaliVal > 0xfff)
	{
		u4CaliVal = 0xfff;
	}
	u4CaliVal = (u4CaliVal & 0xfff) >> 2;	

	#ifndef CC_MTK_LOADER
	LOG(2,"after Calibration Input voltage is %d mV\n", ((u4CaliVal+1)*2800)/1024);	
    #endif
    
	return u4CaliVal;
}
#endif
//-----------------------------------------------------------------------------
/** PDWNC_ReadServoADCChannelValue() Read the ServoADC Channel Value
 *  @param u4Channel the channel number
 *  @retval the channel adc value.
 */
//-----------------------------------------------------------------------------
UINT32 PDWNC_ReadServoADCChannelValue(UINT32 u4Channel)
{
	UINT32 u4Val, u4Ret, i, u1Frist;
	#ifndef CC_MTK_LOADER
	CRIT_STATE_T rCrit;
	#endif
	// support 2 styles of servo adc numbering: 0 ~ 4 and 400 ~ 404
	if(u4Channel >= ADIN0)
	{
		u4Channel -= ADIN0;
	}
	// Maximum is channel 9.(0~9)
	VERIFY(u4Channel < TOTAL_SERVOADC_NUM);
	PDWNC_InitServoADC();
//#ifndef CC_MTK_LOADER
//   VERIFY(x_sema_lock(_hServoADMutex, X_SEMA_OPTION_WAIT) == OSR_OK);
//#endif
	#ifndef CC_MTK_LOADER
	rCrit = x_crit_start();
	#endif /* CC_MTK_LOADER */

	u4Val = IO32ReadFld(PDWNC_SRVCFG1, FLD_SRVCH_EN);
	i = 0;
	u1Frist = 0;
	if((u4Val & PDWNC_SRVCH_EN_CH(u4Channel)) == 0)
	{
		u4Val |= PDWNC_SRVCH_EN_CH(u4Channel);
		vIO32Write4B(PDWNC_SRVCFG1, u4Val);
		//wait for servoadc update, each channel parsing need 24us, Total is 32 channel. 
		HAL_Delay_us(1000);
		while(i<3000)
		{
			if((IO32ReadFldAlign(PDWNC_SRVCST, FLD_SRVCST)) == u4Channel)
			{
				if(u1Frist > 0)
				{
					u4Ret = IO32ReadFldAlign(PDWNC_ADOUT0 + (u4Channel * 4), FLD_ADOUT0);	
					Printf("Servoadc Channel%d finish update!!!, value is %d\n", u4Channel, u4Ret);
					break;
				}
				else
				{
					u4Ret = IO32ReadFldAlign(PDWNC_ADOUT0 + (u4Channel * 4), FLD_ADOUT0);	
					Printf("Servoadc Channel%d frist update!!!, value is %d\n", u4Channel, u4Ret);
					i = 0;
					u1Frist++;
					HAL_Delay_us(50);
				}
			}
			i++;	//use i for timeout
		}
		if(i >= 3000)
		{
			Printf("Wait Servoadc Channel%d update timeout!!!\n", u4Channel);
		}
	}
	
	u4Ret = IO32ReadFldAlign(PDWNC_ADOUT0 + (u4Channel * 4), FLD_ADOUT0);	
        	
//#ifndef CC_MTK_LOADER
//	VERIFY(x_sema_unlock(_hServoADMutex) == OSR_OK);
//#endif
	#ifndef CC_MTK_LOADER
	x_crit_end(rCrit);		   
	#endif /* CC_MTK_LOADER */ 

#if defined(CC_MT5891)
	u4Val = u4Ret & 0x3ff;
	#ifndef CC_MTK_LOADER
	LOG(2,"Channel %d befor Calibration value is %d\n", u4Channel, u4Val);
	LOG(2,"Input voltage is %d mV\n", ((u4Val+1) * 2800)/1024);
	#endif
	u4Ret = PDWNC_ServoADCCalibration(u4Val);
#endif

	return (u4Ret & 0x3ff); //Oryx servoADC bit2~11.
}

UINT32 PDWNC_ReadServoADCChannelValue_MoreLog(UINT32 u4Channel)
{
	UINT32 u4Val, u4Ret, i;
	#ifndef CC_MTK_LOADER
	CRIT_STATE_T rCrit;
	#endif
	// support 2 styles of servo adc numbering: 0 ~ 4 and 400 ~ 404
	if(u4Channel >= ADIN0)
	{
		u4Channel -= ADIN0;
	}
	// Maximum is channel 9.(0~9)
	VERIFY(u4Channel < TOTAL_SERVOADC_NUM);
	PDWNC_InitServoADC();
//#ifndef CC_MTK_LOADER
//   VERIFY(x_sema_lock(_hServoADMutex, X_SEMA_OPTION_WAIT) == OSR_OK);
//#endif
	#ifndef CC_MTK_LOADER
	rCrit = x_crit_start();
	#endif /* CC_MTK_LOADER */

	u4Val = IO32ReadFld(PDWNC_SRVCFG1, FLD_SRVCH_EN);
	i = 0;
	if((u4Val & PDWNC_SRVCH_EN_CH(u4Channel)) == 0)
	{
		u4Val |= PDWNC_SRVCH_EN_CH(u4Channel);
		vIO32Write4B(PDWNC_SRVCFG1, u4Val);
		//wait for servoadc update
		HAL_Delay_us(50);
		while(((IO32ReadFldAlign(PDWNC_SRVCST, FLD_SRVCST)) != u4Channel) && (i<3000))
		{
			i++;	//use i for timeout
		}
		if(i >= 3000)
		{
			Printf("Wait Servoadc Channel%d update timeout!!!", u4Channel);
		}
	}
	
	u4Ret = IO32ReadFldAlign(PDWNC_ADOUT0 + (u4Channel * 4), FLD_ADOUT0);	
        	
//#ifndef CC_MTK_LOADER
//	VERIFY(x_sema_unlock(_hServoADMutex) == OSR_OK);
//#endif
	#ifndef CC_MTK_LOADER
	x_crit_end(rCrit);		   
	#endif /* CC_MTK_LOADER */ 

#if defined(CC_MT5891)	
	u4Val = u4IO32Read4B(EFUSE_TS1_ADDR) & 0x00000001;

	if(u4Val == 0)
	{
		LOG(0,"ServoADC FT Calibration e-Fuse is Off!\n");
		u4Val = u4Ret & 0x3ff;
		LOG(0,"Channel %d value is %d\n", u4Channel, u4Val);
		LOG(0,"Input voltage is %d mV\n", ((u4Val+1) * 2800)/1024);
	}
	else
	{
		LOG(0,"ServoADC FT Calibration e-Fuse is On!\n");
		u4Val = u4Ret & 0x3ff;
		LOG(0,"Channel %d befor Calibration value is %d\n", u4Channel, u4Val);
		LOG(0,"Input voltage is %d mV\n", ((u4Val+1) * 2800)/1024);
		u4Ret = PDWNC_ServoADCCalibration(u4Val);
		LOG(0,"after Calibration value is %d\n", u4Ret); 
		LOG(0,"Input voltage is %d mV\n", ((u4Ret+1)*2800)/1024);	
	}
#endif

	return (u4Ret & 0x3ff); //Oryx servoADC bit2~11.
}

//todo: this function is used or not ? and check define ADC2GPIO_CH_ID_MIN  ADC2GPIO_CH_ID_MAX
INT32 PDWNC_ServoGpioRangeCheck(INT32 i4Gpio)
{
    if((i4Gpio >= (ADC2GPIO_CH_ID_MIN + SERVO_0_ALIAS)) && (i4Gpio <= (ADC2GPIO_CH_ID_MAX + SERVO_0_ALIAS)))//only srvad4~7 can be configured as gpio for 5368/96
    {
        return 1;
    }
    else
    {   
        return 0;
    }   
}


