//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//

//
// This module contains a stub implementation of the battery PDD.  OEMs
// that want to support the battery APIs on their platform can copy this
// file to their platform and link it into their own version of the power
// manager DLL.
//
// If the platform-specific power manager provides its own version of these
// entry points, this module will not be pulled into the link map from
// the pm_battapi library.
//

//--------------------------------------------------------------------------------------------------
// Includes
//--------------------------------------------------------------------------------------------------
#include <battimpl.h>
#include <ceddk.h>
#include <bsp.h>
#include <Drvlib_mem.h>
#include <drvmsg.h>
#include <pm.h>
#include <pmplatform.h>

#define DBGBAT 0

volatile static BSP_ARGS *v_pBSPArgs;

#define BAT_MUTEX_NAME    (TEXT("ADC_MUTEX"))


//-------------------------------------------------------------------------------------------------
// Global Variables
//--------------------------------------------------------------------------------------------------

volatile PMU_MISC_REG			*v_pPMUMISCregs;
volatile GPIO_REG				*v_pGPIOregs;
volatile TSADC_REG 				*v_pTSADC0regs;
volatile TSADC_REG				*v_pTSADC1regs;
volatile VIC_REG				*v_pVIC0regs;
volatile VIC_REG				*v_pVIC2regs;
volatile VIC_REG				*v_pVIC3regs;
volatile PWM_REG				*v_pPWMregs;
volatile CMU_CLK_REG			*v_pCMUCLKregs;


BOOL g_bTSPInitialized			= FALSE;

HANDLE g_hPwrControl			= INVALID_HANDLE_VALUE;
HANDLE g_hADCEvent;

DWORD g_IntrADC;

#define BATMSG(x, y) DBGMSG(x, (TEXT("[BAT] : "))); DBGMSG(x, y);
#define BATERR(x, y) ERRMSG((TEXT("[BAT:ERR] : "))); ERRMSG(y);


// TSADC clock
#define FILCLK							24000000
#define FILCLK_PERIOD_IN_NS				(float)(1000000000 / FILCLK)
#define BATTERY_CONVERSION_DELAY_IN_NS	1000000
#define BATTERY_CONVERSION_DELAY_COUNT	(DWORD)(BATTERY_CONVERSION_DELAY_IN_NS / FILCLK_PERIOD_IN_NS)		// TSDLY


#if defined(BSP_HW_ID) && (BSP_HW_ID == 0 || BSP_HW_ID == 1)
	#define BAT_CH0
#else
	// No TSADC channel is connected for battery level sampling.
#endif




DWORD BAT_CreateADCMutex(void);
DWORD BAT_ADCLock(void);
DWORD BAT_ADCUnlock(void);





// Name : TSP_GetSysIntrVal(DWORD * TouchIntr, DWORD * TouchIntrChanged, UINT32 * IrqTable)
//
// Descriptions : Credit Touch/Timer Interrupt.
//
//      DWORD*    TouchIntr            : Touch Interrupt ID
//      DWORD*    TouchIntrChanged    : Polling(Timer) Interrupt ID
//      UINT32*    IrqTable            : Multiple Interrupt Source Table
//      Return Value    : True/False
//
BOOL
BAT_GetSysIntrVal(DWORD *ADCIntr, UINT32 *IrqTable)
{
#ifdef BAT_CH0
	UINT32 irqBuf[3];
#endif

	BOOL bReturn = TRUE;
	
	BATMSG(BATT_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));
   	
/* TODO : dual channel */

#ifdef BAT_CH0
    irqBuf[0] = IrqTable[0];
    irqBuf[1] = IrqTable[1];
    irqBuf[2] = IRQ_ADC;

    if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &irqBuf, sizeof(irqBuf), ADCIntr, sizeof(UINT32), NULL))
    {
        BATERR(TRUE, (TEXT("IRQ_ADC SYSINTR Request was failed.\r\n")));
        bReturn = FALSE;
        goto CleanUp;
    }
#else
	// No TSADC is used.
#endif


CleanUp:

	BATMSG(BATT_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));

	return bReturn;
	
}




//    Name : BAT_VirtualFree(VOID)
//
//    Descriptions : Deallocation memory of the TOUCH, ADC, Interrupt registers.
//
//    Return Value : True/Fail
//
VOID
BAT_VirtualFree(VOID)
{
	BATMSG(BATT_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

    if (v_pPMUMISCregs)
    {
        DrvLib_UnmapIoSpace((PVOID)v_pPMUMISCregs);
        v_pPMUMISCregs = NULL;
    }
    if (v_pGPIOregs)
    {
        DrvLib_UnmapIoSpace((PVOID)v_pGPIOregs);
        v_pGPIOregs = NULL;
    }

    if (v_pTSADC0regs)
    {
        DrvLib_UnmapIoSpace((PVOID)v_pTSADC0regs);
        v_pTSADC0regs = NULL;
    }

    if (v_pTSADC1regs)
    {
        DrvLib_UnmapIoSpace((PVOID)v_pTSADC1regs);
        v_pTSADC1regs = NULL;
    }

    if (v_pVIC0regs)
    {
        DrvLib_UnmapIoSpace((PVOID)v_pVIC0regs);
        v_pVIC0regs = NULL;
    }

    if (v_pVIC2regs)
    {
        DrvLib_UnmapIoSpace((PVOID)v_pVIC2regs);
        v_pVIC2regs = NULL;
    }

    if (v_pVIC3regs)
    {
        DrvLib_UnmapIoSpace((PVOID)v_pVIC3regs);
        v_pVIC3regs = NULL;
    }

    if (v_pPWMregs)
    {
        DrvLib_UnmapIoSpace((PVOID)v_pPWMregs);
        v_pPWMregs = NULL;
    }

    if (v_pCMUCLKregs)
    {
    	DrvLib_UnmapIoSpace((PVOID)v_pCMUCLKregs);
    	v_pCMUCLKregs = NULL;
    };
    
    g_bTSPInitialized = FALSE;

	BATMSG(BATT_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
}


BOOL
BAT_VirtualAlloc(VOID)
{
    BOOL ReturnValue = TRUE;
    PHYSICAL_ADDRESS ioPhysicalBase = {0, 0};

	BATMSG(BATT_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

     v_pPMUMISCregs = (PMU_MISC_REG *)DrvLib_MapIoSpace(BASE_REG_PA_PMU_MISC, sizeof(PMU_MISC_REG), FALSE);
    if (v_pPMUMISCregs == NULL)
    {
        BATERR(TRUE, (TEXT("v_pPMUMISCregs Allocation Fail\r\n")));
        ReturnValue = FALSE;
        goto CleanUp;
    }

     v_pGPIOregs = (GPIO_REG *)DrvLib_MapIoSpace(BASE_REG_PA_GPIO, sizeof(GPIO_REG), FALSE);
    if (v_pGPIOregs == NULL)
    {
        BATERR(TRUE, (TEXT("v_pGPIOregs Allocation Fail\r\n")));
        ReturnValue = FALSE;
        goto CleanUp;
    }

	// TSADCCON0 is always used to select an appropriate channel.
    v_pTSADC0regs = (TSADC_REG *)DrvLib_MapIoSpace(BASE_REG_PA_TSADC0, sizeof(TSADC_REG), FALSE);
    if (v_pTSADC0regs == NULL)
    {
        BATERR(TRUE, (TEXT("BAT_VirtualAlloc() : v_pTSADC0regs Allocation Fail\r\n")));
        ReturnValue = FALSE;
        goto CleanUp;
    }

    v_pTSADC1regs = (TSADC_REG *)DrvLib_MapIoSpace(BASE_REG_PA_TSADC1, sizeof(TSADC_REG), FALSE);
    if (v_pTSADC1regs == NULL)
    {
        BATERR(TRUE, (TEXT("BAT_VirtualAlloc() : v_pTSADC1regs Allocation Fail\r\n")));
        ReturnValue = FALSE;
        goto CleanUp;
    }


    v_pVIC0regs = (VIC_REG *)DrvLib_MapIoSpace(BASE_REG_PA_VIC0, sizeof(VIC_REG), FALSE);
    if (v_pVIC0regs == NULL)
    {
        BATERR(TRUE, (TEXT("BAT_VirtualAlloc() : v_pVIC0regs Allocation Fail\r\n")));
        ReturnValue = FALSE;
        goto CleanUp;
    }

    v_pVIC2regs = (VIC_REG *)DrvLib_MapIoSpace(BASE_REG_PA_VIC2, sizeof(VIC_REG), FALSE);
    if (v_pVIC2regs == NULL)
    {
        BATERR(TRUE, (TEXT("BAT_VirtualAlloc() : v_pVIC2regs Allocation Fail\r\n")));
        ReturnValue = FALSE;
        goto CleanUp;
    }

    v_pVIC3regs = (VIC_REG *)DrvLib_MapIoSpace(BASE_REG_PA_VIC3, sizeof(VIC_REG), FALSE);
    if (v_pVIC3regs == NULL)
    {
        BATERR(TRUE, (TEXT("BAT_VirtualAlloc() : v_pVIC3regs Allocation Fail\r\n")));
        ReturnValue = FALSE;
        goto CleanUp;
    }
    
    v_pPWMregs = (PWM_REG *)DrvLib_MapIoSpace(BASE_REG_PA_PWMTIMER, sizeof(PWM_REG), FALSE);
    if (v_pPWMregs == NULL)
    {
        BATERR(TRUE, (TEXT("BAT_VirtualAlloc() : v_pPWMregs Allocation Fail\r\n")));
        ReturnValue = FALSE;
        goto CleanUp;
    }

    v_pCMUCLKregs = (CMU_CLK_REG *)DrvLib_MapIoSpace(BASE_REG_PA_CMU_CLK, sizeof(CMU_CLK_REG), FALSE);
    if (v_pCMUCLKregs == NULL)
    {
        BATERR(TRUE, (TEXT("BAT_VirtualAlloc() : v_pCMUCLKregs Allocation Fail\r\n")));
        ReturnValue = FALSE;
        goto CleanUp;
    }
    
CleanUp:

    if (!ReturnValue)
    {
        BAT_VirtualFree();
	    g_bTSPInitialized = FALSE;
    }
    else
    {
	    g_bTSPInitialized = TRUE;
    }

	BATMSG(BATT_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));

    return ReturnValue;
}





// Name : BAT_CreateADCMutex(void)
//
// Descriptions : Create Mutex for ADC resource.
//
// Return Value : GetLastError()
//
#define BAT_MUTEX_NAME    (TEXT("ADC_MUTEX"))
#define BAT_MUTEX_TIMEOUT (5000) // 5000 ms

static HANDLE ghBAT_Mutex = NULL;

DWORD
BAT_CreateADCMutex(void)
{
    DWORD dwStatus = ERROR_SUCCESS;

	BATMSG(BATT_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

    ghBAT_Mutex = CreateMutex(NULL, FALSE, BAT_MUTEX_NAME);
    if (ghBAT_Mutex == NULL)
    {
		dwStatus = GetLastError();
        BATERR(TRUE, (TEXT("Failed to CreateMutex, error = %d\r\n"), dwStatus));
        DEBUGCHK(dwStatus != ERROR_SUCCESS);
    }
    else // if (ghBAT_Mutex != NULL)
    {
        if ( ERROR_ALREADY_EXISTS == GetLastError() )
        {
            BATMSG(BATT_DBG, (TEXT("CreateMutex() opened existing mutex.\r\n")));
            dwStatus = ERROR_ALREADY_EXISTS;
        }
        else
        {
            BATMSG(BATT_DBG, (TEXT("CreateMutex() created new mutex.\r\n")));
            dwStatus = ERROR_SUCCESS;
        }
        DEBUGCHK(dwStatus != ERROR_SUCCESS);
    }

	BATMSG(BATT_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
	
    return dwStatus;
}


// Name : BAT_ADCLock(void)
//
// Descriptions : Obtain the Mutex.
//
//            Return Value    : GetLastError()
//
DWORD
BAT_ADCLock(void)
{
    DWORD dwStatus = ERROR_SUCCESS;

	BATMSG(BATT_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

    DEBUGCHK(ghBAT_Mutex != NULL);
    
    do    // while(dwStatus !=ERROR_SUCCESS);
    {
        dwStatus = WaitForSingleObject(ghBAT_Mutex, BAT_MUTEX_TIMEOUT);
        if(dwStatus == WAIT_OBJECT_0)
        {
            dwStatus = ERROR_SUCCESS;
        }
        else
        {
            dwStatus = GetLastError();
            DEBUGCHK(dwStatus != ERROR_SUCCESS);
            BATMSG(BATT_DBG, (TEXT("WaitForSingleObject() failed for %d : %d\r\n"), BAT_MUTEX_TIMEOUT, GetLastError()));
        }

        BATMSG(BATT_DBG, (TEXT("BAT_Receive Mutex\r\n")));
    } while ( dwStatus != ERROR_SUCCESS );

    DEBUGCHK(dwStatus == ERROR_SUCCESS);


	BATMSG(BATT_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));

    return dwStatus;
}


// Name : BAT_ADCUnlock(void)
//
// Descriptions : Release the Mutex.
//
// Return Value : GetLastError()
//
DWORD
BAT_ADCUnlock(void)
{
    DWORD dwStatus = ERROR_SUCCESS;
    BOOL fOk;

	BATMSG(BATT_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

    DEBUGCHK(ghBAT_Mutex != NULL);

    fOk = ReleaseMutex(ghBAT_Mutex);
    if( !fOk )
    {
        dwStatus = GetLastError();
        DEBUGCHK(dwStatus != ERROR_SUCCESS);
        BATMSG(BATT_DBG, (TEXT("ReleaseMutex() failed %d\r\n"), GetLastError()));
    }

    BATMSG(BATT_DBG, (TEXT("BAT_Release Mutex\r\n")));

    DEBUGCHK(dwStatus == ERROR_SUCCESS);

	BATMSG(BATT_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
    return dwStatus;
}


//------------------------------------------------------------------------------------------------------------
// Function: GetChargePercent
// 
// Purpose:  Get Charge capacity from battery check hardware
// Returns:  unsigned __int8
//
//-------------------------------------------------------------------------------------------------------------
BOOL GetChargePercent(BYTE *pBatteryLifePercent, DWORD *pBatteryVoltage)
{
	BOOL bReturn = TRUE;

#ifdef BAT_CH0
	DWORD dwTSCON;
	DWORD dwTSADCCON;
	DWORD dwTSDLY;

	DWORD dwBatteryLevel;
#endif	

	
	BATMSG(BATT_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));


#ifdef BAT_CH0

	BAT_ADCLock();

	// Clear Interrupt
	v_pTSADC0regs->CLRINTADC = 1;

	dwTSCON = v_pTSADC0regs->TSCON;
	dwTSADCCON = v_pTSADC0regs->TSADCCON;
	dwTSDLY = v_pTSADC0regs->TSDLY;
	
	v_pTSADC0regs->TSDLY = BATTERY_CONVERSION_DELAY_COUNT;

	v_pTSADC0regs->ADCMUX = 0x1;			// AIN1
	v_pTSADC0regs->TSCON = 0x58;			// turn off all switches.

	v_pTSADC0regs->TSADCCON = (0x1<<16)|(0x1<<14)|(14 << 6)|(0<<2);	
	v_pTSADC0regs->TSADCCON &= ~(1 << 17);	// select TSADC0, TSADC1 is useless in normal conversion mode.
	v_pTSADC0regs->TSADCCON |= 0x1;


	if(WaitForSingleObject(g_hADCEvent, INFINITE) == WAIT_OBJECT_0)
	{
		BATMSG(BATT_DBG, (TEXT("A/D conversion completed.\r\n")));
		dwBatteryLevel = v_pTSADC0regs->TSDATX & 0xFFF;
		BATMSG(BATT_INFO, (TEXT("Battery ADC level = %04d\r\n"), dwBatteryLevel));
	}
	else
	{
		BATERR(BATT_DBG, (TEXT("Failed to wait for ADC conversion complete.\r\n")));
	}

#if 0
	// polling
/*	
	while(1)
	{
		if(v_pTSADC0regs->TSADCCON & (1 << 15))
		{
			dwBatteryLevel = v_pTSADC0regs->TSDATX & 0xFFF;
			//BATMSG(1, (TEXT("\r\nADC finished : %04d\r\n"), dwBatteryLevel));
			break;
		}
		else
		{
			// Timeout should be added.
			// On timeout, bReturn = FALSE;
		}
	}
				
*/
#endif


	v_pTSADC0regs->TSDLY = dwTSDLY;
	v_pTSADC0regs->TSCON = dwTSCON;
	v_pTSADC0regs->TSADCCON = dwTSADCCON;


	// Clear Interrupt
	v_pTSADC0regs->CLRINTADC = 1;
	InterruptDone(g_IntrADC);
	
    BAT_ADCUnlock();


	// Sample battery level

	// TSADC VREF = 3.3V
	// VIN = 1V ~ 2.46V
	if(dwBatteryLevel > 2470)			// 4.0V
	{
		*pBatteryLifePercent = 100;
	}
	else if(dwBatteryLevel >= 1550)
	{
		// 2470 = 4.0V (100%)
		// 1550 = 3.3V (0%)
		
		*pBatteryLifePercent = 100 - (INT8)(((float)(2470 - dwBatteryLevel) / (float)(2470 - 1550)) * 100);
		*pBatteryVoltage = 3300 + (DWORD)((float)(dwBatteryLevel - 1550) * 0.76);
}
	else
	{
		*pBatteryLifePercent = 0;
		*pBatteryVoltage = 3300;
	}

	if(dwBatteryLevel >= 1550)
	{
		*pBatteryVoltage = 3300 + (DWORD)((float)(dwBatteryLevel - 1550) * 0.76);
	}
	else
	{
		*pBatteryVoltage = 3300;
	}

#else

	// No battery monitoring
    BAT_ADCLock();
	
	*pBatteryLifePercent = 80;
	*pBatteryVoltage = 3700;
	
	BAT_ADCUnlock();

#endif // BAT_CH0

	BATMSG(BATT_INFO, (TEXT("Battery level percentage = %d\r\n"), *pBatteryLifePercent));
	BATMSG(BATT_INFO, (TEXT("Battery voltage = %d mV\r\n"), *pBatteryVoltage));
   
	BATMSG(BATT_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
    return bReturn;
}



//------------------------------------------------------------------------------------------------------------
// Function: InitBatteryDriver
// 
// Purpose:  Initialises the battery driver and allocates the necessary memory.
// Returns:  void
//
//-------------------------------------------------------------------------------------------------------------

void InitBatteryDriver()
{
	DWORD dwStatus = ERROR_SUCCESS;

    UINT32 Irq[3] = {-1, OAL_INTR_FORCE_STATIC, 0};

	
	BATMSG(BATT_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

	//Now, SMDKV210 have no Battery Check hardware
	//Implement initialized code for Battery driver. 

	dwStatus = BAT_CreateADCMutex();

#ifdef BAT_CH0
	if(BAT_GetSysIntrVal(&g_IntrADC, Irq) == FALSE)
	{
		BATERR(TRUE, (TEXT("Failed to get a SYSINTR for IRQ_ADC.\r\n")));
		goto CleanUp;
	}

	g_hADCEvent = CreateEvent(NULL,
							FALSE,     //  Not manual reset
							FALSE,     //  Not signalled
							NULL
							);
	if(g_hADCEvent == NULL)
	{
		BATERR(TRUE, (TEXT("Failed to create an event for IRQ_ADC, error = %d\r\n"), GetLastError()));
		goto CleanUp;
	}
	
	if(InterruptInitialize(g_IntrADC, g_hADCEvent, NULL, 0) == FALSE)
	{
		BATERR(TRUE, (TEXT("Failed to initialize an interrupt for IRQ_ADC.\r\n")));
		goto CleanUp;
	}

CleanUp:

#endif

	
	BATMSG(BATT_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
}

//------------------------------------------------------------------------------------------------------------
// Function: IsACOnline
// 
// Purpose:  Simply indicates whether we are running out of AC or out of Battery.
// Returns:  TRUE indicates we are running out of the AC wall power supply.
//           FALSE indicates we are running out of Battery.
//
//-------------------------------------------------------------------------------------------------------------
BOOL WINAPI IsACOnline(void)
{
    BOOL AcOnlineStatus = TRUE;  //TRUE:AC FALSE:DC
	BATMSG(BATT_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));
	
    // Implement Check code for whether AC or Battery. 
    v_pBSPArgs->bPowerStatus = AcOnlineStatus;
    
	BATMSG(BATT_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
    return AcOnlineStatus;
}

BOOL WINAPI 
BatteryPDDInitialize(LPCTSTR pszRegistryContext)
{
    BOOL bReturn = TRUE;
	BATMSG(BATT_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));
    
    v_pBSPArgs = (volatile BSP_ARGS *)DrvLib_MapIoSpace(IMAGE_SHARE_ARGS_PA_START, sizeof(BSP_ARGS), FALSE);
    if (v_pBSPArgs == NULL)
    {
        BATERR(TRUE, (TEXT("Failed to allocate BSP_ARGS\r\n")));
		bReturn = FALSE;
		goto CLEAN_UP;
	}
    UNREFERENCED_PARAMETER(pszRegistryContext);


	if(BAT_VirtualAlloc() == FALSE)
	{
		bReturn = FALSE;
		goto CLEAN_UP;
	}

	InitBatteryDriver();
	
CLEAN_UP:

	BATMSG(BATT_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));


    return bReturn;
}

void WINAPI 
BatteryPDDDeinitialize(void)
{
	BATMSG(BATT_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

	BAT_VirtualFree();

	if(g_hPwrControl != INVALID_HANDLE_VALUE)
	{
		CloseHandle(g_hPwrControl);
		g_hPwrControl = INVALID_HANDLE_VALUE;
	}
    
	BATMSG(BATT_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
}

void WINAPI 
BatteryPDDResume(void)
{
	BATMSG(BATT_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));
    
	BATMSG(BATT_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
}

void WINAPI 
BatteryPDDPowerHandler(BOOL bOff)
{
#ifdef BAT_CH0
	DWORD dwIPIndex;
	DWORD dwBytes;
#endif
	
	BATMSG(BATT_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

#ifdef BAT_CH0

	if(bOff == FALSE)
	{

		if(g_hPwrControl == INVALID_HANDLE_VALUE)
		{
			// Open a POWERCONTROL device
		    g_hPwrControl = CreateFile( L"PWC0:", GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
		    if (INVALID_HANDLE_VALUE == g_hPwrControl)
		    {
		        BATERR(TRUE, (TEXT("Failed to open PWC0!\r\n")));
		        goto CLEAN_UP;
		    }
		}
	
		// turn on
		dwIPIndex = PWR_IP_BATT_ADC;
		if(!DeviceIoControl(g_hPwrControl, IOCTL_PWRCON_SET_POWER_ON, &dwIPIndex, sizeof(DWORD), NULL, 0, &dwBytes, NULL))
		{
			BATERR(TRUE, (TEXT("Failed to turn on TSADC!\r\n")));
		}

		dwIPIndex = CLK_IP3_TSADC;
		if(!DeviceIoControl(g_hPwrControl, IOCTL_PWRCON_SET_CLOCK_ON, &dwIPIndex, sizeof(DWORD), NULL, 0, &dwBytes, NULL))
		{
			BATERR(TRUE, (TEXT("Failed to enable TSADC clock!\r\n")));
		}

		// A/D conversion delay (D)
		// T1(Delay)        : D * (1 / FILCLK)
		// T2(X-conversion) : (D + 5) * (1 / PCLK)
		// T3(Y-conversion) : (D + 5) * (1 / PCLK)
		// A/D conversion time = T1 + T2 + T3
	}
	else
	{
		// power is automatically turned off on sleep mode
		dwIPIndex = PWR_IP_BATT_ADC;
		if(!DeviceIoControl(g_hPwrControl, IOCTL_PWRCON_SET_POWER_OFF, &dwIPIndex, sizeof(DWORD), NULL, 0, &dwBytes, NULL))
		{
			BATMSG(BATT_INFO, (TEXT("Failed to turn off PWR_IP_BATT_ADC!\r\n")));
			BATERR(BATT_INFO, (TEXT("Another driver may be using the ADC resource.\r\n")));
		}

#if 0 // clock is automatically disabled on sleep mode
		dwIPIndex = CLK_IP3_TSADC;
		if(!DeviceIoControl(g_hPwrControl, IOCTL_PWRCON_SET_CLOCK_OFF, &dwIPIndex, sizeof(DWORD), NULL, 0, &dwBytes, NULL))
		{
			BATERR(TRUE, (TEXT("Failed to disable TSADC clock!\r\n")));
		}
#endif
	}
	
#endif // BAT_CH0

CLEAN_UP:


	BATMSG(BATT_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
}

// This routine obtains the most current battery/power status available
// on the platform.  It fills in the structures pointed to by its parameters
// and returns TRUE if successful.  If there's an error, it returns FALSE.
BOOL WINAPI
BatteryPDDGetStatus(
                    PSYSTEM_POWER_STATUS_EX2 pstatus,
                    PBOOL pfBatteriesChangedSinceLastCall
                    )
{

    // this function is used to report the battery status
    // now, temperature, and terminal voltage
    BYTE ucBatteryLifePercent;
    DWORD dwBatteryVoltage;
    BOOL bACOnline;
    static BOOL first_time = TRUE;
    BYTE ucBatteryFlag = 0;


	BATMSG(BATT_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));
	

    bACOnline = IsACOnline();
    
    if(GetChargePercent(&ucBatteryLifePercent, &dwBatteryVoltage) == FALSE)
    {
    	BATMSG(BATT_DBG, (TEXT("Baterry level sampling was failed.\r\n")));

		ucBatteryLifePercent = 80;
	}
	else
	{
		if(ucBatteryLifePercent > 100)
		{
			ucBatteryLifePercent = 100;
		}
	}

    if (bACOnline)
    {
        pstatus->ACLineStatus = AC_LINE_ONLINE;
        ucBatteryFlag |= BATTERY_FLAG_CHARGING;
    } 
    else
    {
        pstatus->ACLineStatus = AC_LINE_OFFLINE;
    }
	

    // Level Indicator 
    if(ucBatteryLifePercent >= 65)
        ucBatteryFlag |= BATTERY_FLAG_HIGH;
    else if ((ucBatteryLifePercent < 65) && (ucBatteryLifePercent >= 20))
        ucBatteryFlag |= BATTERY_FLAG_LOW;
    else
        ucBatteryFlag |= BATTERY_FLAG_CRITICAL;

    pstatus->BatteryFlag                = ucBatteryFlag;
    pstatus->BatteryLifePercent         = ucBatteryLifePercent;
    pstatus->Reserved1                  = 0;
    pstatus->BatteryLifeTime            = BATTERY_LIFE_UNKNOWN;
    pstatus->BatteryFullLifeTime        = BATTERY_LIFE_UNKNOWN;

    pstatus->Reserved2                  = 0;
    pstatus->BackupBatteryFlag          = BATTERY_FLAG_UNKNOWN;
    pstatus->BackupBatteryLifePercent   = 0;
    pstatus->Reserved3                  = 0;
    pstatus->BackupBatteryLifeTime      = BATTERY_LIFE_UNKNOWN;
    pstatus->BackupBatteryFullLifeTime  = BATTERY_LIFE_UNKNOWN;

    pstatus->BatteryChemistry           = BATTERY_CHEMISTRY_LION;
    pstatus->BatteryVoltage             = dwBatteryVoltage;   //Sample Data
    pstatus->BatteryCurrent             = 0;
    pstatus->BatteryAverageCurrent      = 0;
    pstatus->BatteryAverageInterval     = 0;
    pstatus->BatterymAHourConsumed      = 0;
    pstatus->BatteryTemperature         = 0;
    pstatus->BackupBatteryVoltage       = 0;
   
   *pfBatteriesChangedSinceLastCall = FALSE;
   
	BATMSG(BATT_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
    
    return (TRUE);
}



// This routine indicates how many battery levels will be reported
// in the BatteryFlag and BackupBatteryFlag fields of the PSYSTEM_POWER_STATUS_EX2
// filed in by BatteryPDDGetStatus().  This number ranges from 0 through 3 --
// see the Platform Builder documentation for details.  The main battery
// level count is reported in the low word of the return value; the count 
// for the backup battery is in the high word.
LONG
BatteryPDDGetLevels(
    void
        )
{
    LONG lLevels = MAKELONG (3 /* main battery levels   */,  
                             3 /* backup battery levels */);
    
	BATMSG(BATT_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

    BATMSG(BATT_INFO, (TEXT("(%d main levels, %d backup levels)\r\n"), LOWORD(lLevels), HIWORD(lLevels)));

	BATMSG(BATT_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
    return lLevels;
}



// This routine returns TRUE to indicate that the pfBatteriesChangedSinceLastCall
// value filled in by BatteryPDDGetStatus() is valid.  If there is no way to
// tell that the platform's batteries have been changed this routine should
// return FALSE.
BOOL
BatteryPDDSupportsChangeNotification(
    void
        )
{
    BOOL fSupportsChange = FALSE;
	BATMSG(BATT_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

    BATMSG(BATT_INFO, (TEXT("BatteryPDDSupportsChangeNotification = %d\r\n"), fSupportsChange));

    BATMSG(BATT_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
    return fSupportsChange;
}

