//
// 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.
//
// Copyright (c) Samsung Electronics. Co. LTD. All rights reserved.

/*++

THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.

Module Name:

   power_control.c   Power Controller Driver

Abstract:

   This module control Power state of system and HW Block Power
   Especially, this module concerns about sleep/wakeup changes
   IOCTL serves HW Block Power on/off

Functions:



Notes:

--*/

#include "precomp.h"
#include <clkinfo.h>
#define OEM_PM

static volatile CMU_CLK_REG *g_pCMUCLKRegs = NULL;
static volatile CMU_MISC_REG *g_pCMUMISCRegs = NULL;
static volatile PMU_PM_REG *g_pPMUPMRegs = NULL;
static volatile PMU_MISC_REG *g_pPMUMISCRegs = NULL;

static HANDLE g_hThreadPowerMon = NULL;
static HANDLE g_hMsgQueue = NULL;
static BOOL g_aIPPowerStatus[PWR_IP_MAX] = {FALSE, };
static BOOL g_aIPClockStatus[CLK_IP_MAX] = {FALSE, }; 
static BOOL g_bExitThread = FALSE;
static CRITICAL_SECTION csPowerCon;

#ifdef BSP_USEDVFS

// System Clock Define Information For BSP_ARGS
// This Define has to be same as DVFS Level

SYSTEM_CLOCK g_SysClockTable[SYS_CLK_DEF_MAX] =
{
 //SYS_CLK_DEF   ARM_CLK      HCLKMSYS   PCLKMSYS    HCLKDSYS    PCLKDSYS   HCLKPSYS     PCLKPSYS     APLLRATIO  A2MRATIO  HCLK_DSYSRATIO  HCLK_PSYSRATIO   DMC0RATIO    ONENANDRATIO ARM_VOLTAGE INT_VOLTAGE                         
  {SYS_CLK_DEF0, CLK_1GHz,   CLK_200MHz, CLK_100MHz, CLK_166MHz, CLK_83MHz, CLK_133MHz,  CLK_66_5MHz,     1,       5,          4,              5,               4,             0,     DVS_L0_ARM, DVS_L0_INT},
  {SYS_CLK_DEF1, CLK_800MHz, CLK_200MHz, CLK_100MHz, CLK_166MHz, CLK_83MHz, CLK_133MHz,  CLK_66_5MHz,     1,       4,          4,              5,               4,             0,     DVS_L1_ARM, DVS_L0_INT},  
  {SYS_CLK_DEF2, CLK_400MHz, CLK_200MHz, CLK_100MHz, CLK_166MHz, CLK_83MHz, CLK_133MHz,  CLK_66_5MHz,     2,       4,          4,              5,               4,             0,     DVS_L1_ARM, DVS_L0_INT},   
  {SYS_CLK_DEF3, CLK_100MHz, CLK_100MHz, CLK_100MHz, CLK_83MHz,  CLK_83MHz, CLK_66_5MHz, CLK_66_5MHz,     4,       8,          8,              10,              8,             0,     DVS_L3_ARM, DVS_L1_INT},   
};  


// ARM/INT Voltage level and CPU Ratio for each Power level
DWORD g_aTransitionTable_VR[DVFS_LEVEL_MAX][6] =
{
    //___________________________________________________________
    //  Current Level  Level-Dn Ratio,
    //                           Down Level,
    //                                    Level-up Ratio,
    //                                              Up Level,
    //___________________________________________________________
#if 1
    #if 0   // Ideal DVFS
/*  SYS_L0      */  {RATIO_PERCENTAGE(70)   ,    SYS_L1 ,   RATIO_PERCENTAGE(100)   ,    SYS_L0 },
/*  SYS_L1      */  {RATIO_PERCENTAGE(40)   ,    SYS_L2 ,   RATIO_PERCENTAGE(95)    ,    SYS_L0 },
/*  SYS_L2      */  {RATIO_PERCENTAGE(30)   ,    SYS_L3 ,   RATIO_PERCENTAGE(90)    ,    SYS_L1 },
/*  SYS_L3      */  {RATIO_PERCENTAGE(0)    ,    SYS_L3 ,   RATIO_PERCENTAGE(80)    ,    SYS_L2 }
    #else   // Current DVFS
    /*  SYS_L0      */  {RATIO_PERCENTAGE(70)   ,    SYS_L1 ,   RATIO_PERCENTAGE(100)   ,    SYS_L0 },
    /*  SYS_L1      */  {RATIO_PERCENTAGE(40)   ,    SYS_L2 ,   RATIO_PERCENTAGE(95)    ,    SYS_L0 },
    /*  SYS_L2      */  {RATIO_PERCENTAGE(15)   ,    SYS_L3 ,   RATIO_PERCENTAGE(90)    ,    SYS_L1 },
    /*  SYS_L3      */  {RATIO_PERCENTAGE(0)    ,    SYS_L3 ,   RATIO_PERCENTAGE(95)    ,    SYS_L2 }    
    #endif
#else       // Previous DVFS
    /*	SYS_L0	    */	{RATIO_PERCENTAGE(25)	,	 SYS_L1	,	RATIO_PERCENTAGE(100)	,	 SYS_L0	},
    /*	SYS_L1	    */	{RATIO_PERCENTAGE(40)	,	 SYS_L2	,	RATIO_PERCENTAGE(95)	,	 SYS_L0	},
    /*	SYS_L2	    */	{RATIO_PERCENTAGE(15)	,	 SYS_L3	,	RATIO_PERCENTAGE(85)	,	 SYS_L1	},
    /*	SYS_L3	    */	{RATIO_PERCENTAGE(0)    ,	 SYS_L3	,	RATIO_PERCENTAGE(95)	,	 SYS_L2	}
#endif
};

#define DemotedRatio(CurLvl)     g_aTransitionTable_VR[(CurLvl)][0]
#define DemotedLevel(CurLvl)      g_aTransitionTable_VR[(CurLvl)][1]
#define PromotedRatio(CurLvl)     g_aTransitionTable_VR[(CurLvl)][2]
#define PromotedLevel(CurLvl)      g_aTransitionTable_VR[(CurLvl)][3]


static BOOL g_bDoEventDVFS   = FALSE;
static BOOL g_bExitDVFSThread;
static DWORD g_dwSysIntrDVFS = SYSINTR_UNDEFINED;

static HANDLE g_hThreadDVFS = NULL;
static HANDLE g_hEventDVFS = NULL;

static HANDLE g_hSentinel  = NULL;
static HANDLE g_hEventSentinel  = NULL;
BOOL bHoldDVFS = FALSE;

static UINT32 gRealMaxPowerLevel = SYS_L0;
static UINT32 gRealMinPowerLevel = SYS_L3;

 // 0. Cnt, 1. Max Level, 2. Min Level
#define PROFILE_STACKCNT    0
#define PROFILE_MAXLVL      1
#define PROFILE_MINLVL      2
int gProfileStack[PERF_MAX][3]  =
{
        {0,SYS_L0,SYS_L0},          // Hyper Performance        : L0
        {0,SYS_L0,SYS_L1},          // High Performance         : L0 - L1
        {0,SYS_L0,SYS_L2},          // High-Norm Performance    : L0 - L2
        {0,SYS_L1,SYS_L2},          // Norm Performance         : L1 - L2
        {0,SYS_L1,SYS_L3},          // Low-Norm Performance     : L1 - L3
        {0,SYS_L2,SYS_L3},          // Low Performance          : L2 - L3
        {0,SYS_L3,SYS_L3}           // Slow Performance         : L3
};

int gProfileMax     = ACTIVE_MAXLEVEL;
int gProfileMin     = ACTIVE_MINLEVEL;

#define SN_ACTIVEAPPLICATION_ROOT HKEY_CURRENT_USER
#define SN_ACTIVEAPPLICATION_PATH TEXT("System\\State\\Shell")
#define SN_ACTIVEAPPLICATION_VALUE TEXT("Active Application")

#define FVC_MUTEX_NAME    (TEXT("FVC_MUTEX"))
#define FVC_MUTEX_TIMEOUT (5000) // 5000 ms

static HANDLE ghFVC_Mutex = NULL;


BSP_ARGS * DVFSBSP_ARGs;
SHORT AppRemapTransitTable[DVFS_LEVEL_MAX];

BOOL g_bDVFS_Disable = FALSE;

volatile VIC_REG *g_pVIC0, *g_pVIC1, *g_pVIC2, *g_pVIC3;
volatile RTC_REG *g_pRTCReg;
static volatile PWM_REG *g_pPWMRegs;
volatile DRAMCON_REG *g_pDMC0Regs, *g_pDMC1Regs;

volatile GPIO_REG *g_pGPIORegs;

volatile CMU_GCTRL_REG *g_pCMUGCTRLRegs;

extern DWORD g_aTransitionTable_DIV[DVFS_LEVEL_MAX][11];

/* DVFS Transition Lock Bit setting */
#define TRANSLOCK_APPS      (0x1)
#define TRANSLOCK_LPMODE    (0x2)

static BOOL GetRegistryString(const HKEY hRoot, const TCHAR* const psKeyName, const TCHAR* const psValueName, TCHAR *ptszValue, DWORD dwSize)
{
	BOOL bSuccess = FALSE;
	HKEY hKey;
        HKEY hSubKey = NULL;
	DWORD dwDisposition;

	if((0 != ptszValue) && (ERROR_SUCCESS == RegCreateKeyEx(hRoot, psKeyName, 0, TEXT(""), 0, KEY_READ, 0, &hKey, &dwDisposition)))
	{
		DWORD dwValueSize = dwSize ;
		bSuccess = (ERROR_SUCCESS == RegQueryValueEx(hKey, psValueName, 0, 0, (BYTE*)ptszValue, &dwValueSize)) ;
		RegCloseKey(hKey);
	}

	return bSuccess;
}

DWORD FVC_CreateVARMutex(void)
{
	DWORD dwStatus = ERROR_SUCCESS;

	ghFVC_Mutex = CreateMutex(NULL, FALSE, FVC_MUTEX_NAME);
	if (ghFVC_Mutex == NULL)
	{
		DBGMSG(PWC_INFO, (TEXT("DVFS#CREATEMUTEX\r\n")));
		dwStatus = GetLastError();
		DEBUGCHK(dwStatus != ERROR_SUCCESS);
	}
	else // if (ghFVC_Mutex != NULL)
	{
		if ( ERROR_ALREADY_EXISTS == GetLastError() )
		{
			DBGMSG(PWC_INFO, (TEXT("[TCH:M] CreateMutex() opened existing mutex.\r\n")));
			dwStatus = ERROR_ALREADY_EXISTS;
		}
		else
		{
			DBGMSG(PWC_INFO, (TEXT("[TCH:M] CreateMutex() created new mutex.\r\n")));
			dwStatus = ERROR_SUCCESS;
		}
		DEBUGCHK(dwStatus != ERROR_SUCCESS);
	}

	DBGMSG(PWC_INFO, (TEXT("[TCH:F] -FVC_CreateADCMutex()\r\n")));
	return dwStatus;
}

DWORD FVC_VARLock(void)
{
	DWORD dwStatus = ERROR_SUCCESS;

	DEBUGCHK(ghFVC_Mutex != NULL);
	DBGMSG(PWC_INFO, (TEXT("[TCH:F] +FVC_ADCLock()\r\n")));

	do	// while(dwStatus !=ERROR_SUCCESS);
	{
		dwStatus = WaitForSingleObject(ghFVC_Mutex, FVC_MUTEX_TIMEOUT);
		if(dwStatus == WAIT_OBJECT_0)
		{
			dwStatus = ERROR_SUCCESS;
		}
		else
		{
			dwStatus = GetLastError();
			DEBUGCHK(dwStatus != ERROR_SUCCESS);
		}

		DBGMSG(PWC_INFO, (TEXT("[FVC] FVC_Receive Mutex\r\n")));

		RETAILMSG(dwStatus != ERROR_SUCCESS, (TEXT("[FVC:M] WaitForSingleObject() failed for %d : %d\r\n"), FVC_MUTEX_TIMEOUT, GetLastError()));

	} while (0);

	DEBUGCHK(dwStatus == ERROR_SUCCESS);

	RETAILMSG(0, (TEXT("[FVC:F] -FVC_ADCLock()\r\n")));
	return dwStatus;
}

DWORD FVC_VARUnlock(void)
{
	DWORD dwStatus = ERROR_SUCCESS;
	BOOL fOk;

	DEBUGCHK(ghFVC_Mutex != NULL);
	DBGMSG(PWC_INFO, (TEXT("[TCH:F] +FVC_ADCUnlock()\r\n")));

	fOk = ReleaseMutex(ghFVC_Mutex);
	if( !fOk )
	{
		dwStatus = GetLastError();
		DEBUGCHK(dwStatus != ERROR_SUCCESS);
	}

	DBGMSG(PWC_INFO, (TEXT("[TCH:M] FVC_Release Mutex\r\n")));

	RETAILMSG(dwStatus != ERROR_SUCCESS, (TEXT("[TCH:M] ReleaseMutex() failed %d\r\n"),  GetLastError()));

	DEBUGCHK(dwStatus == ERROR_SUCCESS);

	DBGMSG(PWC_INFO, (TEXT("[TCH:F] -FVC_ADCUnlock()\r\n")));
	return dwStatus;
}

void PWC_ExecuteDVFS(void)
{
        g_bDoEventDVFS = TRUE;
        SetEvent(g_hEventDVFS);
}

/**************************************
/*
/*  Get the current DVFS Level.
*/
UINT32 PWC_GetDVFS(void)
{
    UINT32 CurrSysLevel=SYS_L0;
    CurrSysLevel = DVFSBSP_ARGs->CurrentPowerLevel;

    DBGMSG(PWC_FUNC,(TEXT("[PWC] GetCurrentSyslevel %d \r\n"), CurrSysLevel));

    return CurrSysLevel;
}

/**************************************
/*
/*  Be forced to set the Power level temporarily.
/*
/*          This function adjust the "PowerLevelReMapTable[]", and it means that
/*          The adjusted Power level remains at one time.
/*          After that, the DVFS Transit range go back to the "AppRemapTransitTable[]" or Normal Transit range.
*/
void PWC_SetDVFS(UINT32 SysLevel)
{
    UINT32 New_SysLevel = SysLevel;

    if(FVC_VARLock() == ERROR_SUCCESS)
    {
        DVFSBSP_ARGs->PowerLevelReMapTable[SYS_L0] = New_SysLevel;
        DVFSBSP_ARGs->PowerLevelReMapTable[SYS_L1] = New_SysLevel;
        DVFSBSP_ARGs->PowerLevelReMapTable[SYS_L2] = New_SysLevel;
        DVFSBSP_ARGs->PowerLevelReMapTable[SYS_L3] = New_SysLevel;
        FVC_VARUnlock();
        PWC_ExecuteDVFS();
    }

   DBGMSG(PWC_FUNC,(TEXT("[PWC] SetCurrentSyslevel %d \r\n"), New_SysLevel));
}

/**************************************
/*
/*  Enable /Disable the DVFS
/*
/*              Handle the "g_bDVFS_Disable"
*/
void PWC_EnableDVFS(BOOL bEnable)
{
    if(FVC_VARLock() == ERROR_SUCCESS)
    {
        if(bEnable)
        {
            g_bDVFS_Disable = FALSE;
            DVFSBSP_ARGs->DVFSTransitionDone = TRUE;
            FVC_VARUnlock();
            PWC_ExecuteDVFS();
        }
        else
        {
            FVC_VARUnlock();
            g_bDVFS_Disable = TRUE;
        }
    }
}

/**************************************
/*
/*  Program the DVFS range. (Set the DVFS Profile)
/*
/*         "DVFSTransitLock[0]" : DVFS range locked by LP Player
/*         "DVFSTransitLock[1]" : DVFS range locked by "Profile"
/*         Locking by LP Player is prior to the others.
/*
*/
void PWC_PofileHandler(PROFILE_LIST Profile, BOOL Action)
{
    int i;
    int minLvl = ACTIVE_MINLEVEL;
    int maxLvl = SYS_LEVEL_MAX;
    int LockCnt = 0;
    // Action : TRUE -> Add Profile, FALSE -> Delete Profile
    DVFSBSP_ARGs->DVFSTransitLock &= ~(TRANSLOCK_APPS);

    if(Profile < HYPER_PERF || Profile >= PERF_MAX) return;

    // Update ProfileStack
    gProfileStack[Profile][PROFILE_STACKCNT]  = (Action) ? (gProfileStack[Profile][PROFILE_STACKCNT] + 1):(gProfileStack[Profile][PROFILE_STACKCNT] - 1);
    if(gProfileStack[Profile][PROFILE_STACKCNT] <= 0) gProfileStack[Profile][PROFILE_STACKCNT] = 0;

    // Update Profile Sum (Min, Max)
    for(i = HYPER_PERF ; i < PERF_MAX ; i++)
    {
        if(gProfileStack[i][PROFILE_STACKCNT] > 0)
        {
            // Check Max performance
            if(maxLvl > gProfileStack[i][PROFILE_MAXLVL]) maxLvl = gProfileStack[i][PROFILE_MAXLVL];
            // Check Min performance
            if(minLvl > gProfileStack[i][PROFILE_MINLVL]) minLvl = gProfileStack[i][PROFILE_MINLVL];

            LockCnt += gProfileStack[i][PROFILE_STACKCNT];

            DVFSBSP_ARGs->DVFSTransitLock |= (TRANSLOCK_APPS);
        }
    }

    if(LockCnt > 0)
    {
        gProfileMax = (maxLvl < ACTIVE_MAXLEVEL)?(ACTIVE_MAXLEVEL):(maxLvl);
        gProfileMin = (minLvl > ACTIVE_MINLEVEL)?(ACTIVE_MINLEVEL):(minLvl);
    }
    else
    {
        gProfileMax = ACTIVE_MAXLEVEL;
        gProfileMin = ACTIVE_MINLEVEL;
    }

    for(i = ACTIVE_MAXLEVEL ; i <= ACTIVE_MINLEVEL ; i++)
    {
        if(AppRemapTransitTable[i] < gProfileMax) AppRemapTransitTable[i] = gProfileMax;
        else if(AppRemapTransitTable[i] > gProfileMin) AppRemapTransitTable[i] = gProfileMin;
    }

    gRealMaxPowerLevel = gProfileMax;
    gRealMinPowerLevel = gProfileMin;

    PWC_ExecuteDVFS();
}
#endif // BSP_USEDVFS

#ifdef OEM_PM
#define OEMPM_URGENT_WAKEUP (TEXT("PWC_URG_WAKEUP"))
#define OEMPM_PM_NOTIFY     (TEXT("PWC_NOTIFY"))
#define OEMPM_DIDLE_REQ     (TEXT("PWC_DIDLEREQ"))
#define OEMPM_DEVICENUMBER     100
#define OEMPM_LPDRVNUMBER       100

typedef enum
{
#if 1
    PMST_NORMAL = 0,                // Not Low Power Mode
#else
    PMST_NORMAL_EXCITED = 0,      // Normal Power Mode, Fully running
    PMST_NORMAL_STEADY,             // Normal Power Mode, Partially running
#endif
    PMST_LP_READY,             // Pre-Low Power mode. Device Power state is met with the LP Condition but the Display is not.
    PMST_LP_CREEP,              // Low Power mode, but the LP support module running
    PMST_LP_ABYSS,              // Low Power mode, and the LP support module does not running
    PMST_LP_SUSPEND,        // Suspend state in augmented Power state
    PMST_MAX
} AUG_PowerState;

typedef enum
{
    PMEVT_DEVSTATE_CHANGE = 0,
/* PMEVT_DVFS_TRANS, */
    PMEVT_LPMODE_REQ,
    PMEVT_NOTIFY,
    PMEVT_MAX
} PM_EVENT;

typedef struct
{
    BOOL                      bRegistered;
    BOOL                      bLPModeSupport;
    DWORD                   dwDeviceID;

    short                       DevicePowerState;

    BOOL                      bLPActive;
    DWORD                   LPModeStatus;	/// DPIDLE_SB/DPIDLE_LB/NORMAL_SB/NORMAL_SB
    DWORD                   LPModeInfo;		// dwMinLPPlayTime
    WCHAR                        DrvQueName[64];
    HANDLE                  hDrvNotiMSGQ;	// PwrCon -> Drv MsgQueue
} DEV_STATE;

AUG_PowerState AugmentedSysPowerState;

DWORD   dwNumOfLevel[5];   // Total number of each device power level
DWORD   dwRegisteredDevice;

HANDLE g_hPWCSemaphore = NULL;
HANDLE g_hThreadPowerManager = NULL;     // AugmentPM_Thread
HANDLE g_hIntrIntercepter = NULL;               // InterruptIntercepter Thread

HANDLE g_hEventPowerManager[PMEVT_MAX]; // Event handler
HANDLE g_hIntrSentinel = NULL;                        // Intercept Event handler

short                    PlatformDeviceStatusMax = D4;
short                    PlatformDeviceStatusMin = D0;
DEV_STATE          DevStateList[OEMPM_DEVICENUMBER];
BSP_ARGS    * SharedMem;
MSG_DEV_STATE   txmqParam;
BOOL                    bAPMDisabled = TRUE;

DWORD PWC_Lock()
{
    if(!g_hPWCSemaphore) return -1;

    return WaitForSingleObject(g_hPWCSemaphore, 1000);
}

DWORD PWC_Unlock()
{
    if(!g_hPWCSemaphore) return -1;

    return ReleaseSemaphore(g_hPWCSemaphore, 1, NULL);
}


AUG_PowerState CheckAugPowerState()
{
    AUG_PowerState dwRet;
    if(dwRegisteredDevice > 0 && dwNumOfLevel[0] == 0 && dwNumOfLevel[1] == 0)
    {
        if(SharedMem->bLPDisplayOff == TRUE)
        {
            if(dwNumOfLevel[2] == 0) dwRet = PMST_LP_SUSPEND;
            else dwRet = PMST_LP_ABYSS; // or PMST_LP_CREEP
        }
        else
        {
            dwRet = PMST_LP_READY;
        }
    }
    else
    {
        dwRet = PMST_NORMAL;
    }

    return dwRet;
}


INT
WINAPI
LPWakeupThread(void)
{
    int i;
    DWORD dwRet;
    DWORD dwWakeup_IRQ;
    DWORD dwWakeup_SysIntr = SYSINTR_UNDEFINED;

    CeSetThreadPriority(g_hIntrIntercepter, POWER_MONITOR_THREAD_PRIODITY);

    dwWakeup_IRQ = IRQ_DIDLEWU;

    if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &dwWakeup_IRQ, sizeof(DWORD), &dwWakeup_SysIntr, sizeof(DWORD), NULL))
    {
        DBGMSG(PWC_INFO,(TEXT("[PWC_PM:ERR] KernelIoControl REQUEST_SYSINTR fail\r\n")));
        goto CleanUp_Interceptor;
    }

    g_hIntrSentinel = CreateEvent(NULL, FALSE, FALSE, OEMPM_URGENT_WAKEUP);

    if (!g_hIntrSentinel)
    {
        DBGMSG(PWC_INFO,(TEXT("[PWC_PM:ERR] Handler Creation fail\r\n")));
        goto CleanUp_Interceptor;
    }

    if (!InterruptInitialize(dwWakeup_SysIntr, g_hIntrSentinel, NULL, 0))
    {
        DBGMSG(PWC_INFO,(TEXT("[PWC_PM:ERR] InterruptInitialization fail\r\n")));
        goto CleanUp_Interceptor;
    }

    DBGMSG(PWC_FUNC,(_T("Wakeup DIDLE SysIntr 0x%x and then enter into infinite loop\r\n"), dwWakeup_SysIntr));

    while(1)
    {
        dwRet = WaitForSingleObject(g_hIntrSentinel, INFINITE);

        InterruptDone(dwWakeup_SysIntr);
        PWC_Lock();

        if(dwRet == WAIT_OBJECT_0)
        {
            if(AugmentedSysPowerState == PMST_LP_ABYSS || AugmentedSysPowerState == PMST_LP_CREEP)
            {
#ifdef BSP_USEDVFS
                PWC_EnableDVFS(TRUE);
#endif
                SharedMem->bAugStateLPMode = FALSE;     // Aug Power state : NORMAL

                DBGMSG(PWC_INFO,(_T("[PWC_PM:INFO] Receive Intercepted Interrupt : Farewell to the LowPower Mode by Intr. r\n")));
                for(i = 0 ; i < OEMPM_DEVICENUMBER ; i++)
                {
                    if(DevStateList[i].bRegistered == FALSE) break;

                    if(DevStateList[i].bLPModeSupport)// && DevStateList[i].bLPActive)
                    {   // Temporarily, we assume that there are only one LP Driver : Audio
#if 0
                     // 1. Fill in Tx Msg
                        DBGMSG(PWC_INFO,(_T("[PWC_PM:INFO] WAV_NORMAL_SB (%s)\r\n"), DevStateList[i].DeviceName));
                        RETAILMSG(1,(_T("[PWC_PM:INFO] WAV_NORMAL_SB (%s)\r\n"), DevStateList[i].DeviceName));
                        txmqParam.bLPModeSupport = TRUE;
                        strcpy(txmqParam.DeviceName,DevStateList[i].DeviceName);
                        // Send "Not OK" Info to the LP Driver
                        // Send NorMal SB
                        txmqParam.dwMsg = PTOD_LP_MODE;
                        txmqParam.dwLParam = WAV_NORMAL_SB;
                        txmqParam.dwRParam = 0;
                        DevStateList[i].LPModeStatus = WAV_NORMAL_SB;
                        // Send LB
                        if(!WriteMsgQueue(DevStateList[i].hDrvNotiMSGQ, &txmqParam, sizeof(MSG_DEV_STATE), INFINITE, 0))
                        {
                            RETAILMSG(1,(TEXT(" WriteMsgQueue Error(0x%x)\r\n"),GetLastError()));
                        }
#endif
                    }
                }

                SharedMem->bLPAudioEn = FALSE;

                dwRet = CheckAugPowerState();

                if(dwRet == PMST_LP_READY && AugmentedSysPowerState != PMST_LP_READY)
                {
                    AugmentedSysPowerState = PMST_LP_READY;
                    RETAILMSG(1,(_T("(INTR)Entering LP_READY\r\n\r\n")));
                    DBGMSG(PWC_INFO,(_T("[PWC_PM:INFO] Entering LP_READY\r\n\r\n")));
                }
                else if(dwRet == PMST_NORMAL && AugmentedSysPowerState != PMST_NORMAL)
                {
                    AugmentedSysPowerState = PMST_NORMAL;
                    RETAILMSG(1,(_T("(INTR)Entering LP_NORMAL\r\n\r\n")));
                    DBGMSG(PWC_INFO,(_T("[PWC_PM:INFO] Entering LP_NORMAL\r\n\r\n")));
                }
            }
        }

        PWC_Unlock();
    }


CleanUp_Interceptor:
    CloseHandle(g_hIntrSentinel);

    return -1;
}

INT
WINAPI
AugmentPM_Thread(void)
{
    // 0. Power Manger Thread Local variable declaration
    int i;
    DWORD dwRet;
    DWORD tempVal;
    DWORD dwDVFS_SysIntr = SYSINTR_UNDEFINED;
    DWORD dwReadBytes = 0;
    DWORD dwFlags;
    BOOL bTransitAPMState = TRUE;
    MSG_DEV_STATE   mqParam;
    AUG_PowerState    apsParam;
    MSGQUEUEOPTIONS msgOptions;
    PHYSICAL_ADDRESS    ioPhysicalBase = {0,0};
    WCHAR TempString[100];

    DBGMSG(PWC_FUNC,(_T("[PMT] Power Manager Thread created\r\n")));
    //  1. OEM Power Manger Initialization
    AugmentedSysPowerState = PMST_NORMAL;

    ioPhysicalBase.LowPart = IMAGE_SHARE_ARGS_PA_START;
    SharedMem = (BSP_ARGS *)DrvLib_MapIoSpace(ioPhysicalBase.LowPart, sizeof(BSP_ARGS), FALSE);
    if (SharedMem == NULL)
    {
        return FALSE;
    }

    dwRegisteredDevice = 0;
    for(i = 0 ; i < 5 ; i++) dwNumOfLevel[i] = 0;
    for(i = 0 ; i < PMEVT_MAX ; i++)
    {
        g_hEventPowerManager[i] = NULL;
    }

    for(i = 0 ; i < OEMPM_DEVICENUMBER ; i++)
    {
        DevStateList[i].bRegistered = FALSE;
        DevStateList[i].bLPModeSupport = FALSE;
        DevStateList[i].dwDeviceID = 0xffffffff;
        DevStateList[i].DevicePowerState = D4;
        DevStateList[i].bLPActive = FALSE;
        DevStateList[i].LPModeStatus = 0;       // DPIDLE_SB/DPIDLE_LB/NORMAL_SB/NORMAL_SB
        DevStateList[i].LPModeInfo = 0;             // dwMinLPPlayTime
        wcscpy(DevStateList[i].DrvQueName, L"DRV_MSGQUE_");
        DevStateList[i].hDrvNotiMSGQ = NULL;    // PwrCon -> Drv MsgQueue
    }

    SharedMem->LPWakeUpSrc[0] = (0x1ffff)|(1<<PHYIRQ_RTC_ALARM);// VIC0 : all EINT Source
    SharedMem->LPWakeUpSrc[1] = (0x0);
    SharedMem->LPWakeUpSrc[2] = (1<<(PHYIRQ_KEYPAD-VIC2_BIT_OFFSET));
    SharedMem->LPWakeUpSrc[3] = (0x0);        

    // 2. Message Queue initialization for Device Power State transition
    memset((void *)&msgOptions, 0x0, sizeof(msgOptions));
    msgOptions.dwSize = sizeof(MSGQUEUEOPTIONS);
    msgOptions.dwFlags = 0;
    msgOptions.dwMaxMessages = 1024;        // Max number of MSG queue entries
    msgOptions.cbMaxMessage = sizeof(MSG_DEV_STATE);
    msgOptions.bReadAccess = TRUE;

    g_hEventPowerManager[PMEVT_DEVSTATE_CHANGE] = CreateMsgQueue(OEMPM_MSGQ_NAME, &msgOptions);
    if (g_hEventPowerManager[PMEVT_DEVSTATE_CHANGE] == NULL )
    {
        DBGMSG(PWC_INFO,(TEXT("[PWC_PM:INFO]  %s->CreateMsgQueue() Failed : Err %d\n"), _T(__FUNCTION__), GetLastError()));
        goto CleanUp_PowerManger;
    }

    // 5. Power Manager DeepIdle Request Event.
    g_hEventPowerManager[PMEVT_LPMODE_REQ] = CreateEvent(NULL, FALSE, FALSE, OEMPM_DIDLE_REQ);
    if(g_hEventPowerManager[PMEVT_LPMODE_REQ] == NULL) DBGMSG(PWC_INFO,(_T("DeepIdle request Handler Fail\r\n")));
    // 6. Power Manager notification Initialization
    g_hEventPowerManager[PMEVT_NOTIFY] = CreateEvent(NULL, FALSE, FALSE, OEMPM_PM_NOTIFY);
    if(g_hEventPowerManager[PMEVT_NOTIFY] == NULL) DBGMSG(PWC_INFO,(_T("NOTIRY Handler Fail\r\n")));

    bAPMDisabled = TRUE;

    DBGMSG(PWC_INFO,(_T("Enter into Infinite Loop\r\n")));
    PWC_Unlock();
    // 7. Infinite Power Manager Loop
    while(1)
    {
        dwRet = WaitForMultipleObjects(PMEVT_MAX, g_hEventPowerManager, FALSE, INFINITE);

        PWC_Lock();
        DBGMSG(PWC_INFO,(_T("[PWC_PM:INFO] Power Manager Thread receive EVENT\r\n")));
        switch(dwRet)
        {
        // CASE(DEVSTATE_CHANGE) : Device Power State Changed
        case (WAIT_OBJECT_0 + PMEVT_DEVSTATE_CHANGE):
            {
                DBGMSG(PWC_INFO,(TEXT("ReadMsgQueue\r\n")));
                if(!ReadMsgQueue(g_hEventPowerManager[PMEVT_DEVSTATE_CHANGE], &mqParam, sizeof(MSG_DEV_STATE), &dwReadBytes, INFINITE, &dwFlags))
                {
                    DBGMSG(PWC_INFO,(TEXT(": Message Queue Read Fail : (0x%x)\r\n"), GetLastError()));
                    break;
                }

                DBGMSG(PWC_FUNC,(TEXT("[PWC_PM:INFO] DEVICE POWER STATE Changed (%d : %d,%d)\r\n"), mqParam.dwDeviceID, mqParam.dwMsg, mqParam.dwLParam));

                // This routine assumes that DeviceStateList only increses, but not shrinks.
                for(i = 0 ; i < OEMPM_DEVICENUMBER ; i++)
                {
                    if(DevStateList[i].bRegistered && (DevStateList[i].dwDeviceID == mqParam.dwDeviceID)) // Find the registered slot
                    {
                        break;
                    }

                    if(DevStateList[i].bRegistered == FALSE)
                    {
                        break;
                    }
                }

				if(i == OEMPM_DEVICENUMBER)
				{
					RETAILMSG(1, (TEXT("[PWC] ERROR : Could not find a matched DevStateList item\r\n")));
					continue;		// skip this turn.
				}

                if(DevStateList[i].bRegistered == FALSE)    // Register the Device
                {
                    dwRegisteredDevice++;
                    DevStateList[i].bRegistered = TRUE;
                    DevStateList[i].bLPModeSupport = mqParam.bLPModeSupport;
                    DevStateList[i].dwDeviceID = mqParam.dwDeviceID;
                    DBGMSG(PWC_FUNC,(_T("[PWC_PM:INFO] Register the Device %d.(%d)\r\n"), i, mqParam.dwDeviceID));
                    if(DevStateList[i].bLPModeSupport) // If LP Mode Driver
                    {
                        _itow(DevStateList[i].dwDeviceID, TempString, 16);
                        wcscat(DevStateList[i].DrvQueName, TempString);
                        msgOptions.bReadAccess = FALSE; // Write Only
#if 0
                        DevStateList[i].hDrvNotiMSGQ = CreateMsgQueue((TEXT("WAV1_MSGQUE")), &msgOptions);
#endif
                    }
                }

                bTransitAPMState = TRUE;

                switch(mqParam.dwMsg)
                {
                case DTOP_LP_WKUP:
                    {   // LP Wakeup source
                        bTransitAPMState = FALSE;
                        
                        if(mqParam.dwLParam < 32)                                   // VIC 0
                        {
                            if(mqParam.dwRParam == 1) SharedMem->LPWakeUpSrc[0] |= (0x1 << mqParam.dwLParam);
                            else if(mqParam.dwRParam == 0) SharedMem->LPWakeUpSrc[0] &= ~(0x1 << mqParam.dwLParam);
                        }
                        else if(mqParam.dwLParam >= 32 && mqParam.dwLParam < 64)                           // VIC 1
                        {
                            if(mqParam.dwRParam == 1) SharedMem->LPWakeUpSrc[1] |= (0x1 << (mqParam.dwLParam-32));
                            else if(mqParam.dwRParam == 0) SharedMem->LPWakeUpSrc[1] &= ~(0x1 << (mqParam.dwLParam-32));
                            
                        }
                        else if(mqParam.dwLParam >= 64 && mqParam.dwLParam < 96)                           // VIC 2
                        {
                            if(mqParam.dwRParam == 1) SharedMem->LPWakeUpSrc[2] |= (0x1 << (mqParam.dwLParam-64));
                            else if(mqParam.dwRParam == 0) SharedMem->LPWakeUpSrc[2] &= ~(0x1 << (mqParam.dwLParam-64));
                            
                        }
                        else if(mqParam.dwLParam >= 96 && mqParam.dwLParam < 128)                           // VIC 3
                        {
                            if(mqParam.dwRParam == 1) SharedMem->LPWakeUpSrc[3] |= (0x1 << (mqParam.dwLParam-96));
                            else if(mqParam.dwRParam == 0) SharedMem->LPWakeUpSrc[3] &= ~(0x1 << (mqParam.dwLParam-96));
                            
                        }    
                        else
                        {
                        }
                    }
                    break;
                case DTOP_DVFS_EMGR:
                    {   // DVFS Emergency level up source
                        bTransitAPMState = FALSE;
                    }
                    break;
                case DTOP_POWER_SET:
                case DTOP_LP_INFO:
                    {
                        DBGMSG(PWC_FUNC,(_T("[PWC_PM:INFO] DTOP_POWER_SET/LP_INFO (%d):D%d -> D%d"), mqParam.dwDeviceID, DevStateList[i].DevicePowerState, mqParam.dwLParam));
                        tempVal = DevStateList[i].DevicePowerState;
                        DevStateList[i].DevicePowerState = (short)mqParam.dwLParam;
                        if(dwNumOfLevel[tempVal] > 0) dwNumOfLevel[tempVal]--;
                         dwNumOfLevel[mqParam.dwLParam]++;

                        DBGMSG(PWC_INFO,(_T("[PWC_PM:INFO] NumOfLevel[%d]=%d, NumOfLevel[%d]=%d\r\n"), tempVal, dwNumOfLevel[tempVal], \
                            mqParam.dwLParam, dwNumOfLevel[mqParam.dwLParam]));

                         DevStateList[i].LPModeInfo = mqParam.dwRParam;  // Read dwMinLPPlayTime
                     }
                    break;
                case DTOP_LP_ACTIVE:
                    {
                        DBGMSG(PWC_INFO,(_T("[PWC_PM:INFO] ACTIVE\r\n")));
                        DevStateList[i].bLPActive = TRUE;
                    }
                    break;
                case DTOP_LP_DEACTIVE:
                    {
                        DBGMSG(PWC_INFO,(_T("[PWC_PM:INFO] DEACTIVE\r\n")));
                        DevStateList[i].bLPActive = FALSE;
                    }
                    break;
#if 0
                case DTOP_LP_INFO:
                    {
                        DevStateList[i].LPModeInfo = mqParam.dwRParam;  // Read dwMinLPPlayTime
                    }
                    break
#endif
                default :
                    DBGMSG(PWC_INFO, (TEXT("Unavailable PWR MSG\r\n")));
                }

                if(!bTransitAPMState)
                {
                    // return to the "WaitForMultipleObjects"
                    break;
                }

                if(bAPMDisabled)
                {
                    // return to the "WaitForMultipleObjects"
                    //
                    RETAILMSG(0,(_T("APM canceled for System power state\r\n")));
                    break;
                }

                DBGMSG(PWC_INFO,(_T("[PWC_PM:INFO] Check LP Mode %d, %d, %d, %d, %d, %d, %d\r\n"), dwRegisteredDevice, \
                    dwNumOfLevel[0], dwNumOfLevel[1], dwNumOfLevel[2], dwNumOfLevel[3], dwNumOfLevel[4],\
                    SharedMem->bLPDisplayOff));

                RETAILMSG(0,(_T("[PWC_PM:INFO] Check LP Mode %d, %d, %d, %d, %d, %d, %d\r\n"), dwRegisteredDevice, \
                    dwNumOfLevel[0], dwNumOfLevel[1], dwNumOfLevel[2], dwNumOfLevel[3], dwNumOfLevel[4],\
                    SharedMem->bLPDisplayOff));

                apsParam = CheckAugPowerState();

                if(apsParam == PMST_LP_SUSPEND)
                {
                    DBGMSG(PWC_INFO,(_T("[PWC_PM:INFO] Entering LP_SUSPEND\r\n\r\n")));
                    RETAILMSG(1,(_T("Entering LP_SUSPEND\r\n\r\n")));
                    AugmentedSysPowerState = PMST_LP_SUSPEND;
                }
                else if(apsParam == PMST_LP_ABYSS ||apsParam == PMST_LP_CREEP)
                {
                    DBGMSG(PWC_INFO,(_T("[PWC_PM:INFO] Entering LP_ABYSS\r\n\r\n")));
                    RETAILMSG(1,(_T("Entering LP_ABYSS\r\n\r\n")));
                    AugmentedSysPowerState = PMST_LP_ABYSS;
                    SharedMem->bAugStateLPMode = TRUE;     // Aug Power state : LP_CREEP or LP_ABYSS

#ifdef BSP_USEDVFS
                    PWC_EnableDVFS(FALSE);
                    PWC_SetDVFS(SYS_L1);
#endif
                    // Send "OK" Info to the LP Driver
                    for(i = 0 ; i < OEMPM_DEVICENUMBER ; i++)
                    {
                        if(DevStateList[i].bRegistered == FALSE) break;

                        if(DevStateList[i].bLPModeSupport)// && DevStateList[i].bLPActive)
                        {   // Temporarily, we assume that there are only one LP Driver : Audio
#if 0
                         // 1. Fill in Tx Msg
                            txmqParam.bLPModeSupport = TRUE;
                            strcpy(txmqParam.DeviceName,DevStateList[i].DeviceName);

                         // Dedicated to LP Audio Player
                            if(10 < SharedMem->RescheduleTimeGap &&  SharedMem->RescheduleTimeGap < DevStateList[i].LPModeInfo)
                            {
                                DBGMSG(PWC_INFO,(_T("[PWC_PM:INFO] WAV_DPIDLE_SB (%s)\r\n"), DevStateList[i].DeviceName));
                                RETAILMSG(1,(_T("[PWC_PM:INFO] WAV_DPIDLE_SB (%s)\r\n"), DevStateList[i].DeviceName));
                                txmqParam.dwMsg = PTOD_LP_MODE;
                                txmqParam.dwLParam = WAV_DPIDLE_SB;
                                txmqParam.dwRParam = 0;
                                DevStateList[i].LPModeStatus = WAV_DPIDLE_SB;
                                // Send SB
                                if(!WriteMsgQueue(DevStateList[i].hDrvNotiMSGQ, &txmqParam, sizeof(MSG_DEV_STATE), INFINITE, 0))
                                {
                                    RETAILMSG(1,(TEXT(" WriteMsgQueue Error(0x%x)\r\n"),GetLastError()));
                                }
                            }
                            else if(DevStateList[i].LPModeInfo <= SharedMem->RescheduleTimeGap)
                            {
                                DBGMSG(PWC_INFO,(_T("[PWC_PM:INFO] WAV_DPIDLE_LB (%s)\r\n"), DevStateList[i].DeviceName));
                                RETAILMSG(1,(_T("[PWC_PM:INFO] WAV_DPIDLE_LB (%s)\r\n"), DevStateList[i].DeviceName));
                                txmqParam.dwMsg = PTOD_LP_MODE;
                                txmqParam.dwLParam = WAV_DPIDLE_LB;
                                txmqParam.dwRParam = 0;
                                DevStateList[i].LPModeStatus = WAV_DPIDLE_LB;
                                // Send LB
                                if(!WriteMsgQueue(DevStateList[i].hDrvNotiMSGQ, &txmqParam, sizeof(MSG_DEV_STATE), INFINITE, 0))
                                {
                                    RETAILMSG(1,(TEXT(" WriteMsgQueue Error(0x%x)\r\n"),GetLastError()));
                                }
                            }
                            else
                            {
                            }
#endif
                            if(DevStateList[i].DevicePowerState == D2) AugmentedSysPowerState = PMST_LP_CREEP;
                            DBGMSG(PWC_INFO,(_T("[PWC_PM:INFO] Entering LP_CREEP\r\n\r\n")));
                            RETAILMSG(1,(_T("Entering LP_CREEP\r\n\r\n")));

                        }
                    }

                    SharedMem->bLPAudioEn = TRUE;
                }
                else
                {
                    if(AugmentedSysPowerState == PMST_LP_ABYSS || AugmentedSysPowerState == PMST_LP_CREEP)
                    {
                        SharedMem->bAugStateLPMode = FALSE;     // Aug Power state : NORMAL
#ifdef BSP_USEDVFS
                        PWC_EnableDVFS(TRUE);
#endif

                        DBGMSG(PWC_INFO,(_T("[PWC_PM:INFO] Farewell to the LowPower Mode by Device Power Mode or Display r\n")));
                        for(i = 0 ; i < OEMPM_DEVICENUMBER ; i++)
                        {
                            if(DevStateList[i].bRegistered == FALSE) break;

                            if(DevStateList[i].bLPModeSupport)// && DevStateList[i].bLPActive)
                            {   // Temporarily, we assume that there are only one LP Driver : Audio
#if 1
#else
                             // 1. Fill in Tx Msg
                                DBGMSG(PWC_INFO,(_T("[PWC_PM:INFO] PWC:WAV_NORMAL_SB (%s)\r\n"), DevStateList[i].DeviceName));
                                RETAILMSG(1,(_T("[PWC_PM:INFO] PWC:WAV_NORMAL_SB (%s)\r\n"), DevStateList[i].DeviceName));
                                txmqParam.bLPModeSupport = TRUE;
                                strcpy(txmqParam.DeviceName,DevStateList[i].DeviceName);
                                // Send "Not OK" Info to the LP Driver
                                // Send NorMal SB
                                txmqParam.dwMsg = PTOD_LP_MODE;
                                txmqParam.dwLParam = WAV_NORMAL_SB;
                                txmqParam.dwRParam = 0;
                                DevStateList[i].LPModeStatus = WAV_NORMAL_SB;
                                // Send LB
                                if(!WriteMsgQueue(DevStateList[i].hDrvNotiMSGQ, &txmqParam, sizeof(MSG_DEV_STATE), INFINITE, 0))
                                {
                                    RETAILMSG(1,(TEXT(" WriteMsgQueue Error(0x%x)\r\n"),GetLastError()));
                                }
#endif
                            }
                        }

                        SharedMem->bLPAudioEn = FALSE;
                    }

                    if(apsParam == PMST_LP_READY && AugmentedSysPowerState != PMST_LP_READY)
                    {
                        AugmentedSysPowerState = PMST_LP_READY;
                        RETAILMSG(1,(_T("Entering LP_READY\r\n\r\n")));
                        DBGMSG(PWC_INFO,(_T("[PWC_PM:INFO] Entering LP_READY\r\n\r\n")));
                    }
                    else if(apsParam == PMST_NORMAL && AugmentedSysPowerState != PMST_NORMAL)
                    {
                        AugmentedSysPowerState = PMST_NORMAL;
                        RETAILMSG(1,(_T("Entering LP_NORMAL\r\n\r\n")));
                        DBGMSG(PWC_INFO,(_T("[PWC_PM:INFO] Entering LP_NORMAL\r\n\r\n")));
                    }

                }
            }
            break;
#if 0
        case (WAIT_OBJECT_0 + PMEVT_DVFS_TRANS):
            {
                DBGMSG(PWC_INFO,(TEXT("[PWC_PM:INFO] DVFS Transition")));
                InterruptDone(dwDVFS_SysIntr);
                DBGMSG(PWC_INFO,(TEXT("\r\n")));
            }
            break;
#endif
        case (WAIT_OBJECT_0 + PMEVT_LPMODE_REQ):
            {
                DBGMSG(PWC_INFO,(TEXT("[PWC_PM:INFO] DeepIDLE Request")));

                if(!ReadMsgQueue(g_hEventPowerManager[PMEVT_DEVSTATE_CHANGE], &mqParam, sizeof(MSG_DEV_STATE), &dwReadBytes, INFINITE, &dwFlags))
                {
                    DBGMSG(PWC_FUNC,(TEXT(": Message Queue Read Fail : (0x%x)\r\n"), GetLastError()));
                    break;
                }
                DBGMSG(PWC_INFO,(TEXT("\r\n")));
            }
            break;
        case (WAIT_OBJECT_0 + PMEVT_NOTIFY):
            {
                DBGMSG(PWC_INFO,(TEXT("[PWC_PM:INFO] PowerManger Notification for")));
                DBGMSG(PWC_INFO,(TEXT("\r\n")));
            }
            break;
        case (WAIT_TIMEOUT):
            {
                DBGMSG(PWC_INFO,(TEXT("[PWC_PM:INFO] PowerManger Timeouts")));
                DBGMSG(PWC_INFO,(TEXT("\r\n")));
            }
            break;
        case (WAIT_FAILED):
            {
                DBGMSG(PWC_INFO,(TEXT("[PWC_PM:INFO] PowerManger Thread msg fail (0x%x)"), GetLastError()));
                DBGMSG(PWC_INFO,(TEXT("\r\n")));
            }
            break;
        }

        PWC_Unlock();
    }


    // Termination
CleanUp_PowerManger:
    DBGMSG(PWC_FUNC,(TEXT("[PWC_PM:INFO] --%s\n"), _T(__FUNCTION__)));

    CloseMsgQueue(g_hEventPowerManager[PMEVT_DEVSTATE_CHANGE]);
#if 0
    CloseHandle(g_hEventPowerManager[PMEVT_DVFS_TRANS]);
#endif
    CloseHandle(g_hEventPowerManager[PMEVT_LPMODE_REQ]);
    CloseHandle(g_hEventPowerManager[PMEVT_NOTIFY]);

    for(i = 0 ; i < OEMPM_DEVICENUMBER ; i++)
    {
        if(DevStateList[i].hDrvNotiMSGQ != NULL) CloseMsgQueue(DevStateList[i].hDrvNotiMSGQ);
    }

    return 0;
}
#endif

#ifdef BSP_USEDVFS
VOID IFVC_Changer(int CurrentLevel, int NextLevel)
{
    int idx;

    if(NextLevel > CurrentLevel)                           //2 Level down execute
    {
       // DisableVIC(RSD0, RSD1, RSD2,RSD3, g_pVIC0, g_pVIC1, g_pVIC2, g_pVIC3);
        for(idx = CurrentLevel + 1 ; idx <= (int)NextLevel ; idx++)
        {
            if(idx == SYS_L1)  
            {
                DVFS_L0toL1ClockChange((void *)g_pCMUCLKRegs, (void *)g_pDMC0Regs, (void *)g_pDMC1Regs, SYS_L1);
#if (S5PV210_EVT==0)
                DBGMSG(PWC_INFO,(_T("D(L1). 0x%x, 0x%x\r\n"),g_pCMUCLKRegs->PLL_CON.APLL_CON, g_pCMUCLKRegs->CLK_DIV.CLK_DIV0));
#else
                DBGMSG(PWC_INFO,(_T("D(L1). 0x%x, 0x%x, 0x%x\r\n"),g_pCMUCLKRegs->PLL_CON.APLL_CON0, g_pCMUCLKRegs->PLL_CON.APLL_CON1, g_pCMUCLKRegs->CLK_DIV.CLK_DIV0));
#endif
            }
            else
            {    
                DVFS_L1_L3ClockChange((void *)g_pCMUCLKRegs, (void *)g_pDMC0Regs, (void *)g_pDMC1Regs, idx);
#if (S5PV210_EVT==0)                
                DBGMSG(PWC_INFO,(_T("D(L%d). 0x%x, 0x%x\r\n"), idx, g_pCMUCLKRegs->PLL_CON.APLL_CON, g_pCMUCLKRegs->CLK_DIV.CLK_DIV0));
#else
                DBGMSG(PWC_INFO,(_T("D(L%d). 0x%x, 0x%x, 0x%x\r\n"), idx, g_pCMUCLKRegs->PLL_CON.APLL_CON0, g_pCMUCLKRegs->PLL_CON.APLL_CON1, g_pCMUCLKRegs->CLK_DIV.CLK_DIV0));
#endif
            }
#ifdef ENABLE_VOLTAGE_CONTROL
            PMIC_VoltageSet((void*)g_pCMUGCTRLRegs, (void *)g_pGPIORegs, g_aTransitionTable_DIV[idx][9], g_aTransitionTable_DIV[idx][10]);
#endif            
        }
    }
    else if(NextLevel < CurrentLevel)                  //2 Level up execute
    {
        // Level down the Internal Clock/Voltage
        for(idx = CurrentLevel - 1 ; idx >= (int)NextLevel ; idx--)
        {
#ifdef ENABLE_VOLTAGE_CONTROL        
            PMIC_VoltageSet((void*)g_pCMUGCTRLRegs, (void *)g_pGPIORegs, g_aTransitionTable_DIV[idx][9], g_aTransitionTable_DIV[idx][10]);
#endif
            if(idx == SYS_L0)   
            {
                DVFS_L1toL0ClockChange((void *)g_pCMUCLKRegs, (void *)g_pDMC0Regs, (void *)g_pDMC1Regs, SYS_L0);
#if (S5PV210_EVT==0)
                DBGMSG(PWC_INFO,(_T("U(L0). 0x%x, 0x%x\r\n"),g_pCMUCLKRegs->PLL_CON.APLL_CON, g_pCMUCLKRegs->CLK_DIV.CLK_DIV0));
#else
                DBGMSG(PWC_INFO,(_T("U(L0). 0x%x, 0x%x, 0x%x\r\n"),g_pCMUCLKRegs->PLL_CON.APLL_CON0, g_pCMUCLKRegs->PLL_CON.APLL_CON1, g_pCMUCLKRegs->CLK_DIV.CLK_DIV0));
#endif
            }
            else
            {
                DVFS_L1_L3ClockChange((void *)g_pCMUCLKRegs, (void *)g_pDMC0Regs, (void *)g_pDMC1Regs, idx);
#if (S5PV210_EVT==0)                
                DBGMSG(PWC_INFO,(_T("U(L%d). 0x%x, 0x%x\r\n"), idx, g_pCMUCLKRegs->PLL_CON.APLL_CON, g_pCMUCLKRegs->CLK_DIV.CLK_DIV0));
#else
                DBGMSG(PWC_INFO,(_T("U(L%d). 0x%x, 0x%x, 0x%x\r\n"), idx, g_pCMUCLKRegs->PLL_CON.APLL_CON0, g_pCMUCLKRegs->PLL_CON.APLL_CON1, g_pCMUCLKRegs->CLK_DIV.CLK_DIV0));
#endif
            }
        }

        //EnableVIC(RSD0, RSD1, RSD2,RSD3, g_pVIC0, g_pVIC1, g_pVIC2, g_pVIC3);
    }
    else
    {
        // Remain steady, do nothing.
    }
}

void PWC_WaitForFullBoot(void)
{
    HANDLE hBootCompleteEvt = NULL;
    hBootCompleteEvt = OpenEvent(EVENT_ALL_ACCESS, FALSE, TEXT("OEM/BootCompleteEvent"));
    if (hBootCompleteEvt == NULL)
    {
    	RETAILMSG(PWC_FUNC,(_T("Fail in create/open BootComplete event\r\n")));
    	CloseHandle(hBootCompleteEvt);
    	hBootCompleteEvt = NULL;

    Sleep(20000);
#ifdef OEM_PM
       bAPMDisabled = FALSE;
#endif
       return;
    }

    WaitForSingleObject(hBootCompleteEvt, 40000);
#ifdef OEM_PM
    bAPMDisabled = FALSE;
#endif
}

INT WINAPI DVFS_Thread(void)
{
    DWORD dwRet;
    DWORD nxtLevel, runRatio;
    DWORD i = 0;

#if 0   // Unused variables, this is useful for future. 
    DWORD nBtnCount = 0;
    BOOL Toggleing = TRUE;    
    HANDLE hSnapshot = INVALID_HANDLE_VALUE;
    HANDLE hSnapshotModule = INVALID_HANDLE_VALUE;
    PROCESSENTRY32  ActiveProcess = {0};
    MODULEENTRY32  ActiveModule = {0};

    BOOL L0Triggered = FALSE;

    TCHAR m_szApp[128]={0,0};
    TCHAR m_szCurActiveApp[128]={0,0};
    TCHAR m_szPrevActiveApp[128]={0,0};
    BOOL   m_bWMPlayerLock = FALSE;
#endif

    CeSetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
    DBGMSG(PWC_INFO,(_T("[DM.DSC] DVFSThread++\r\n")));

    
    //1 0. Registered the Emergency Level up interrupt source
    DVFSBSP_ARGs->EmergencyLevelUpMsk[0] = (0x1ffff)|(1<<PHYIRQ_RTC_ALARM)|(1<<PHYIRQ_GPIO);// VIC0 : all EINT Source
    DVFSBSP_ARGs->EmergencyLevelUpMsk[1] = (0x0);
    DVFSBSP_ARGs->EmergencyLevelUpMsk[2] = (1<<(PHYIRQ_PENDN-VIC2_BIT_OFFSET))|(1<<(PHYIRQ_KEYPAD-VIC2_BIT_OFFSET));
    DVFSBSP_ARGs->EmergencyLevelUpMsk[3] =  0x0;
    for(i = SYS_L0 ; i < DVFS_LEVEL_MAX ; i++)
        AppRemapTransitTable[i] = (SHORT)i;

    FVC_CreateVARMutex();

    DVFSBSP_ARGs->DVFSTransitionDone  = TRUE;

    PWC_WaitForFullBoot();
    DBGMSG(PWC_INFO,(_T("[DM.DPC] DVFS_Thread Initialization done\r\n")));
    while(!g_bExitDVFSThread)
    {
        dwRet = WaitForSingleObject(g_hEventDVFS, INFINITE);
        InterruptDone(g_dwSysIntrDVFS);  // DO NOT CLEAR THE PENDING REGISTER, TIMER ISR MUST BE RE_INVOKED.

        if(g_bDVFS_Disable && !g_bDoEventDVFS) continue;

        g_bDoEventDVFS = FALSE;

        if(g_bExitDVFSThread)
        {
            break;
        }

		__try
		{
        if( WAIT_OBJECT_0 == dwRet)
        {
            FVC_VARLock();
            //1 1. Read CurrentPowerLevel, CPU RunTime, CPU_Idle Time

            nxtLevel = DVFSBSP_ARGs->CurrentPowerLevel;
            //4 // Calculate Running Ratio(128 based), and Clear CPU/IDLE Run time
			if (DVFSBSP_ARGs->CPU_RunTime == 0)
                {
                DBGMSG(PWC_INFO,(_T("[DM.DPC]Inforced to DVFS by event or invalid utilization data : CPU Ratio 50%\r\n")));
                runRatio = MAX_CPU_RATIO/2;
            }
			else if(DVFSBSP_ARGs->CPU_RunTime < DVFSBSP_ARGs->CPU_IdleTime)
            {
				DBGMSG(PWC_INFO,(_T("[DM.DPC]Invalid utilization data, abandoned\r\n")));
                
                runRatio = MAX_CPU_RATIO/2;
            }
            else
            {
                runRatio =((DVFSBSP_ARGs->CPU_RunTime - DVFSBSP_ARGs->CPU_IdleTime) << 7) / DVFSBSP_ARGs->CPU_RunTime;
            }

            //1 2. Check current Application
            {
            }

            //1 3. Check DVFS, Adjust the Monitoring time
            if(runRatio > PromotedRatio(nxtLevel))                                    //2 Level up check
            {
                //4 //Determine PowerLevel
                nxtLevel = PromotedLevel(nxtLevel);

            }
            else if(runRatio < DemotedRatio(nxtLevel))                            //2        Level down check
            {
                //4 // Determine PowerLevel
                nxtLevel = DemotedLevel(nxtLevel);
            }

            //1 4. Remap the Power level and Save the Current Status
            {
                // Remap the Power level.
                nxtLevel = DVFSBSP_ARGs->PowerLevelReMapTable[nxtLevel];

				// Limit of operation Power level is determined, RealXXX, ProfileXXX is same value, and ProfileXXX is used for future
                {
                    gRealMaxPowerLevel  = gProfileMax;
                    gRealMinPowerLevel = gProfileMin;
                }

                // Save LevelUpMargin, LevelDnMargin

                if(nxtLevel <= gRealMaxPowerLevel)
                {
                    DVFSBSP_ARGs->LevelUpMargin = MAX_CPU_RATIO;
                    nxtLevel = gRealMaxPowerLevel;
                }
                else
                {
                    DVFSBSP_ARGs->LevelUpMargin = PromotedRatio(nxtLevel);
                }

                if(nxtLevel >= gRealMinPowerLevel)
                {
                    DVFSBSP_ARGs->LevelDnMargin = 0;
                    nxtLevel = gRealMinPowerLevel;
                }
                else
                {
                    DVFSBSP_ARGs->LevelDnMargin = DemotedRatio(nxtLevel);
                }

                DVFSBSP_ARGs->CPU_RunTime = 0;
                DVFSBSP_ARGs->CPU_IdleTime = 0;
            }

            DBGMSG(0,(_T("%d -> %d \r\n"), DVFSBSP_ARGs->CurrentPowerLevel, nxtLevel));

            //1 5. Execute DVFS step by step
            IFVC_Changer(DVFSBSP_ARGs->CurrentPowerLevel, nxtLevel);

            //1 6. Restore/Update DVFS Remap table
            if(!g_bDVFS_Disable)
            {
                if(DVFSBSP_ARGs->DVFSTransitLock == TRANSLOCK_APPS)   // DVFS Transition table locked
                {
                    RETAILMSG(0,(_T("Remapped for the Application %d ~ %d\r\n"), gProfileMax, gProfileMin));
                DVFSBSP_ARGs->PowerLevelReMapTable[SYS_L0] = AppRemapTransitTable[SYS_L0];
                DVFSBSP_ARGs->PowerLevelReMapTable[SYS_L1] = AppRemapTransitTable[SYS_L1];
                DVFSBSP_ARGs->PowerLevelReMapTable[SYS_L2] = AppRemapTransitTable[SYS_L2];
                DVFSBSP_ARGs->PowerLevelReMapTable[SYS_L3] = AppRemapTransitTable[SYS_L3];
            }
            else                                                                // DVFS Transition table unlocked, Emergency LU is available
            {
                    RETAILMSG(0,(_T("Remapped for the Normal Range %d ~ %d\r\n"), gProfileMax, gProfileMin));
                DVFSBSP_ARGs->PowerLevelReMapTable[SYS_L0] = SYS_L0;
                DVFSBSP_ARGs->PowerLevelReMapTable[SYS_L1] = SYS_L1;
                DVFSBSP_ARGs->PowerLevelReMapTable[SYS_L2] = SYS_L2;
                DVFSBSP_ARGs->PowerLevelReMapTable[SYS_L3] = SYS_L3;
            }
            }


            //1 7. DVFS done
            DVFSBSP_ARGs->CurrentPowerLevel = nxtLevel;
            DVFSBSP_ARGs->DVFSTransitionDone  = TRUE;

            switch(nxtLevel)
            {
            case SYS_L0:
                memcpy(&DVFSBSP_ARGs->SystemClocks, &g_SysClockTable[SYS_CLK_DEF0], sizeof(g_pBSPArgs->SystemClocks));
                break;
                
            case SYS_L1:
                memcpy(&DVFSBSP_ARGs->SystemClocks, &g_SysClockTable[SYS_CLK_DEF1], sizeof(g_pBSPArgs->SystemClocks));
                break;
                
            case SYS_L2:
                memcpy(&DVFSBSP_ARGs->SystemClocks, &g_SysClockTable[SYS_CLK_DEF2], sizeof(g_pBSPArgs->SystemClocks));
                break;
                
            case SYS_L3:
                memcpy(&DVFSBSP_ARGs->SystemClocks, &g_SysClockTable[SYS_CLK_DEF3], sizeof(g_pBSPArgs->SystemClocks));
                break;
            }

            FVC_VARUnlock();
        }
    }
		__except(EXCEPTION_EXECUTE_HANDLER)
		{
			DBGMSG(1,(_T("DVFS Thread is terminated by unexpected fault (0x%x)\r\n"), GetExceptionCode()));
			DBGMSG(1,(_T("Sentinel program would restore the DVFS thread immediately\r\n")));
			FVC_VARUnlock();
			DVFSBSP_ARGs->DVFSTransitionDone  = TRUE;
			SetEvent(g_hEventSentinel);
			return 0;
		}
    }

    return 0;
}

INT WINAPI Sentinel_Thread(void)
{
	DBGMSG(PWC_INFO,(_T("[DM.DSC] Sentinel_Thread++\r\n")));
	while(1)
	{
		WaitForSingleObject(g_hEventSentinel, INFINITE);

		DBGMSG(1,(_T("[DM.DSC] DVFS Thread is terminated by unexpected reason\r\n")));
		DBGMSG(1,(_T("[DM.DSC] It would be restored++\r\n")));

		CloseHandle(g_hEventDVFS);
		CloseHandle(g_hThreadDVFS);

		g_hEventDVFS = NULL;
		g_hThreadDVFS = NULL;

	    g_hEventDVFS = CreateEvent(NULL, FALSE, FALSE, NULL);
	    if(NULL == g_hEventDVFS)
	    {
	        DEBUGMSG(1,(L"[PWC:ERR] Sentinel : Create DVFS Failed \n\r"));
	        continue;
	    }

	    if (!(InterruptInitialize(g_dwSysIntrDVFS, g_hEventDVFS, 0, 0)))
	    {
	        DEBUGMSG(1,(L"[PWC:ERR] Sentinel : InterruptInitialize() DVFS Failed \n\r"));
	        continue;
	    }

	    // Create PM Thread
	    g_hThreadDVFS = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) DVFS_Thread, NULL, 0, NULL);
	    if (g_hThreadDVFS == NULL )
	    {
	        DEBUGMSG(1,(L"[PWC:ERR] Sentinel : fail in loading DVFS Driver ()\r\n"));
	        continue;
	    }				
	}

	return 1;
}
#endif // BSP_USEDVFS

/**
*    @fn    PowerMonitorThread(void)
*    @note  This thread handle Power notification message
*/
INT
WINAPI
PowerMonitorThread(void)
{
    HANDLE hPowerNotification = NULL;
    MSGQUEUEOPTIONS msgOptions;
    PPOWER_BROADCAST pB;
    //RESET_STATUS eRstStat;
    UCHAR msgBuf[QUEUE_SIZE];
    DWORD iBytesInQueue = 0;
    DWORD dwFlags;
    int iSleepCount = 0;
    BOOL bRet;

    DBGMSG(PWC_FUNC,(TEXT("[PWRCON:INF] ++%s\n"), _T(__FUNCTION__)));

   // eRstStat = PwrCon_get_reset_status();

    //-------------------------------------------------
    // Set Power Monitor Thread Priority
    //-------------------------------------------------
    CeSetThreadPriority(g_hThreadPowerMon, POWER_MONITOR_THREAD_PRIODITY);

    //-------------------------------------------------
    // Create a message queue for Power Manager notifications.
    //-------------------------------------------------
    memset((void *)&msgOptions, 0x0, sizeof(msgOptions));
    msgOptions.dwSize = sizeof(MSGQUEUEOPTIONS);
    msgOptions.dwFlags = 0;
    msgOptions.dwMaxMessages = QUEUE_ENTRIES;
    msgOptions.cbMaxMessage = sizeof(POWER_BROADCAST) + MAX_NAMELEN;
    msgOptions.bReadAccess = TRUE;

    g_hMsgQueue = CreateMsgQueue(NULL, &msgOptions);
    if (g_hMsgQueue == NULL )
    {
        DBGMSG(PWC_INFO,(TEXT("[PWRCON:ERR] %s->CreateMsgQueue() Failed : Err %d\n"), _T(__FUNCTION__), GetLastError()));
        goto Thread_CleanUp;
    }

    // Request Power notifications
    hPowerNotification = RequestPowerNotifications(g_hMsgQueue, POWER_NOTIFY_ALL);
    if (!hPowerNotification)
    {
        DBGMSG(PWC_INFO,(TEXT("[PWRCON:ERR] %s->RequestPowerNotifications() Failed : Err %d\n"), _T(__FUNCTION__), GetLastError()));
        goto Thread_CleanUp;
    }

    memset(msgBuf, 0x0, QUEUE_SIZE);
    pB = (PPOWER_BROADCAST)msgBuf;

    while(!g_bExitThread)
    {
        DBGMSG(PWC_INFO, (TEXT("[PWR:INF] PowerMonitorThread() : Wait for PM Notification\n")));

        // Read message from queue.
        bRet = ReadMsgQueue(g_hMsgQueue, msgBuf, QUEUE_SIZE, &iBytesInQueue, INFINITE, &dwFlags);

        DBGMSG(PWC_INFO, (TEXT("[PWR:INF] %s() : Read Size:%d\r\n"), _T(__FUNCTION__), iBytesInQueue));

        if (!bRet)
        {
            if (g_bExitThread)
            {
                break;
            }

            DBGMSG(PWC_INFO,(TEXT("[PWRCON:ERR] %s->ReadMsgQueue() Failed : Err %d\n"), _T(__FUNCTION__), GetLastError()));
        }
        else if (iBytesInQueue < sizeof(POWER_BROADCAST))
        {
            DBGMSG(PWC_INFO,(TEXT("[PWRCON:ERR] %s->Receive Insufficient Message (Size is %d, Expected %d)\n"), _T(__FUNCTION__), iBytesInQueue, sizeof(POWER_BROADCAST)));
        }
        else
        {
            switch (pB->Message)
            {
            //-----------------------
            // Notified State Transition
            //-----------------------
            case PBT_TRANSITION:

                DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] Notified [PBT_TRANSITION : %s (0x%08x)]\n"), pB->SystemPowerState, pB->Flags));
#ifdef  OEM_PM
                {
                    DWORD SysPMStates = POWER_STATE_ON|POWER_STATE_IDLE|POWER_STATE_UNATTENDED|POWER_STATE_USERIDLE|POWER_STATE_BACKLIGHTON;
                    if(pB->Flags & SysPMStates)
                    {
                        RETAILMSG(0,(_T("APM Enabled\r\n")));
                        bAPMDisabled = FALSE;
                    }
                    else
                    {
                        RETAILMSG(0,(_T("APM Disabled\r\n")));
                        bAPMDisabled = TRUE;
                    }
                }
#endif
                break;

            //-----------------------
            // Notified Resume State
            //-----------------------
            case PBT_RESUME:

                DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] Notified [PBT_RESUME]\n")));
                {
                    DWORD dwWakeSrc = SYSWAKE_UNKNOWN;
                    DWORD dwBytesRet = 0;

                    if (KernelIoControl(IOCTL_HAL_GET_WAKE_SOURCE, NULL, 0, &dwWakeSrc, sizeof(dwWakeSrc), &dwBytesRet)
                        && (dwBytesRet == sizeof(dwWakeSrc)))
                    {
                        switch(dwWakeSrc)
                        {
                        case SYSWAKE_POWER_BUTTON:    // Power Button
                            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] Wake Up by Power Button\n")));
                            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] SetSystemPowerState(POWER_STATE_ON)\n")));
                            SetSystemPowerState(NULL, POWER_STATE_ON, POWER_FORCE);
                            break;
                        case OEMWAKE_RTC_ALARM:
                            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] Wake Up by RTC Alarm\n")));
                            //PWRCON_INF((_T("[PWRCON:INF] SetSystemPowerState(POWER_STATE_ON)\r\n")));
                            // Do not change Power State to POWER_STATE_ON
                            break;
                        case OEMWAKE_RTC_TICK:
                            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] Wake Up by RTC Tick\n")));
                            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] SetSystemPowerState(POWER_STATE_ON)\n")));
                            SetSystemPowerState(NULL, POWER_STATE_ON, POWER_FORCE);
                            break;
                        case OEMWAKE_KEYPAD:
                            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] Wake Up by Keypad\n")));
                            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] SetSystemPowerState(POWER_STATE_ON)\n")));
                            SetSystemPowerState(NULL, POWER_STATE_ON, POWER_FORCE);
                            break;
                        case OEMWAKE_MSM:
                            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] Wake Up by MSM I/F\n")));
                            //PWRCON_INF((_T("[PWRCON:INF] SetSystemPowerState(POWER_STATE_ON)\r\n")));
                            //SetSystemPowerState(NULL, POWER_STATE_ON, POWER_FORCE);
                            break;

                        case OEMWAKE_HDMICEC:
                            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] Wake Up by HDMI CEC\n")));
                            //PWRCON_INF((_T("[PWRCON:INF] SetSystemPowerState(POWER_STATE_ON)\r\n")));
                            SetSystemPowerState(NULL, POWER_STATE_ON, POWER_FORCE);
                            break;
                        case OEMWAKE_BYPASS:
                            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] Bypass entering sleep mode \n")));
                            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] SetSystemPowerState(POWER_STATE_ON)\n")));
                            SetSystemPowerState(NULL, POWER_STATE_ON, POWER_FORCE);
                            break;
/*                        case OEMWAKE_HSI:
                            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] Wake Up by HSI I/F\n")));
                            //PWRCON_INF((_T("[PWRCON:INF] SetSystemPowerState(POWER_STATE_ON)\r\n")));
                            //SetSystemPowerState(NULL, POWER_STATE_ON, POWER_FORCE);
                            break;
*/
                        default:
                            DBGMSG(PWC_INFO,(TEXT("[PWRCON:ERR] WakeUp Source = 0x%08x\n"), dwWakeSrc));
                            break;
                        }


                    }
                    else
                    {
                        NKDbgPrintfW(L"PWRCON: Error getting wake source\n");
                        DBGMSG(PWC_INFO,(TEXT("[PWRCON:ERR] IOCTL_HAL_GET_WAKE_SOURCE Failed\n")));
                        DBGMSG(PWC_INFO,(TEXT("[PWRCON:ERR] System is Still in [PBT_RESUME] State\n")));
                    }
                }
                break;

            //-----------------------------------
            // Notified Power Supply changed (AC/DC)
            //-----------------------------------
            case PBT_POWERSTATUSCHANGE:
                DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] Notified [PBT_POWERSTATUSCHANGE]\n")));
                break;

            //---------------------------------
            // Notified Power Information changed
            //---------------------------------
            case PBT_POWERINFOCHANGE:
            {
                PPOWER_BROADCAST_POWER_INFO ppbpi;

                ppbpi = (PPOWER_BROADCAST_POWER_INFO)pB->SystemPowerState;

                DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] Notified [PBT_POWERINFOCHANGE]\n")));
                DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF]     ACLine Status : %d\n"), ppbpi->bACLineStatus));
                DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF]     Battery Flag  : %d\n"), ppbpi->bBatteryFlag));
                DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF]     Backup Flag   : %d\n"), ppbpi->bBackupBatteryFlag));
                DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF]     Level         : %d\n"), ppbpi->dwNumLevels));
                break;
            }

            default:
                DBGMSG(PWC_INFO,(TEXT("[PWRCON:ERR] Notified Unknown Message [0x%08x]\n"), pB->Message));
                break;
            }
        }
    }

Thread_CleanUp:
    if (hPowerNotification)
    {
        StopPowerNotifications(hPowerNotification);
        hPowerNotification = NULL;
    }

    if (g_hMsgQueue)
    {
        CloseMsgQueue(g_hMsgQueue);
        g_hMsgQueue = NULL;
    }

    DBGMSG(PWC_FUNC,(TEXT("[PWRCON:INF] --%s\n"), _T(__FUNCTION__)));

    return 0;
}

/**
*    @fn    AllocResource(void)
*    @note  This function maps System Controller Register Block to virtual address space.
*/
static BOOL
AllocResources(void)
{
    PHYSICAL_ADDRESS    ioPhysicalBase = {0,0};
    DBGMSG(PWC_FUNC, (TEXT("[PWRCON] ++%s\n"), _T(__FUNCTION__)));

    //--------------------
    // PMU/CMU SFR
    //--------------------
    ioPhysicalBase.LowPart = BASE_REG_PA_CMU_CLK;
    g_pCMUCLKRegs = (CMU_CLK_REG *)DrvLib_MapIoSpace(ioPhysicalBase.LowPart, sizeof(CMU_CLK_REG), FALSE);
    if (g_pCMUCLKRegs == NULL)
    {
        DBGMSG(PWC_INFO,(TEXT("[PWRCON:ERR] %s->g_pCMUCLKRegs MmMapIoSpace() Failed \n"), _T(__FUNCTION__)));
        return FALSE;
    }

    ioPhysicalBase.LowPart = BASE_REG_PA_CMU_MISC;
    g_pCMUMISCRegs = (CMU_MISC_REG *)DrvLib_MapIoSpace(ioPhysicalBase.LowPart, sizeof(CMU_MISC_REG), FALSE);
    if (g_pCMUMISCRegs == NULL)
    {
        DBGMSG(PWC_INFO,(TEXT("[PWRCON:ERR] %s->g_pCMUMISCRegs MmMapIoSpace() Failed \n"), _T(__FUNCTION__)));
        return FALSE;
    }
    
    ioPhysicalBase.LowPart = BASE_REG_PA_PMU_PM;
    g_pPMUPMRegs = (PMU_PM_REG *)DrvLib_MapIoSpace(ioPhysicalBase.LowPart, sizeof(PMU_PM_REG), FALSE);
    if (g_pPMUPMRegs == NULL)
    {
        DBGMSG(PWC_INFO,(TEXT("[PWRCON:ERR] %s->g_pPMUPMRegs MmMapIoSpace() Failed \n"), _T(__FUNCTION__)));
        return FALSE;
    }    


    ioPhysicalBase.LowPart = BASE_REG_PA_PMU_MISC;
    g_pPMUMISCRegs = (PMU_MISC_REG *)DrvLib_MapIoSpace(ioPhysicalBase.LowPart, sizeof(PMU_MISC_REG), FALSE);
    if (g_pPMUMISCRegs == NULL)
    {
        DBGMSG(PWC_INFO,(TEXT("[PWRCON:ERR] %s->g_pPMUMISCRegs MmMapIoSpace() Failed \n"), _T(__FUNCTION__)));
        return FALSE;
    }

#ifdef BSP_USEDVFS
    ioPhysicalBase.LowPart = BASE_REG_PA_GPIO;
    g_pGPIORegs = (GPIO_REG *)DrvLib_MapIoSpace(ioPhysicalBase.LowPart, sizeof(GPIO_REG), FALSE);
    if (g_pGPIORegs == NULL)
    {
        DBGMSG(PWC_INFO,(TEXT("[PWRCON:ERR] %s->g_pGPIORegs MmMapIoSpace() Failed \n"), _T(__FUNCTION__)));
        return FALSE;
    }   

    ioPhysicalBase.LowPart = BASE_REG_PA_VIC0;
    g_pVIC0 = (VIC_REG *)DrvLib_MapIoSpace(ioPhysicalBase.LowPart, sizeof(VIC_REG), FALSE);
    if (g_pVIC0 == NULL)
    {
        DBGMSG(PWC_INFO,(TEXT("[PWRCON:ERR] %s->g_pVIC0 MmMapIoSpace() Failed \n"), _T(__FUNCTION__)));
        return FALSE;
    }

    ioPhysicalBase.LowPart = BASE_REG_PA_VIC1;
    g_pVIC1 = (VIC_REG *)DrvLib_MapIoSpace(ioPhysicalBase.LowPart, sizeof(VIC_REG), FALSE);
    if (g_pVIC1 == NULL)
    {
        DBGMSG(PWC_INFO,(TEXT("[PWRCON:ERR] %s->g_pVIC1 MmMapIoSpace() Failed \n"), _T(__FUNCTION__)));
        return FALSE;
    }

    ioPhysicalBase.LowPart = BASE_REG_PA_VIC2;
    g_pVIC2 = (VIC_REG *)DrvLib_MapIoSpace(ioPhysicalBase.LowPart, sizeof(VIC_REG), FALSE);
    if (g_pVIC2 == NULL)
    {
        DBGMSG(PWC_INFO,(TEXT("[PWRCON:ERR] %s->g_pVIC2 MmMapIoSpace() Failed \n"), _T(__FUNCTION__)));
        return FALSE;
    }

    ioPhysicalBase.LowPart = BASE_REG_PA_VIC3;
    g_pVIC3 = (VIC_REG *)DrvLib_MapIoSpace(ioPhysicalBase.LowPart, sizeof(VIC_REG), FALSE);
    if (g_pVIC3 == NULL)
    {
        DBGMSG(PWC_INFO,(TEXT("[PWRCON:ERR] %s->g_pVIC3 MmMapIoSpace() Failed \n"), _T(__FUNCTION__)));
        return FALSE;
    }

    ioPhysicalBase.LowPart = BASE_REG_PA_PWMTIMER;
    g_pPWMRegs = (PWM_REG *)DrvLib_MapIoSpace(ioPhysicalBase.LowPart, sizeof(PWM_REG), FALSE);
    if (g_pPWMRegs == NULL)
    {
        DBGMSG(PWC_INFO, (TEXT("[PWRCON:ERR] %s->g_pPWMRegs MmMapIoSpace() Failed \n"), _T(__FUNCTION__)));
        return FALSE;
    }

    ioPhysicalBase.LowPart = BASE_REG_PA_RTC;
    g_pRTCReg =(RTC_REG *)DrvLib_MapIoSpace(ioPhysicalBase.LowPart, sizeof(RTC_REG), FALSE);
    if (g_pRTCReg == NULL)
    {
        DBGMSG(PWC_INFO, (TEXT("[PWRCON:ERR] %s->g_pRTCReg MmMapIoSpace() Failed \n"), _T(__FUNCTION__)));
        return FALSE;
    }

    ioPhysicalBase.LowPart = IMAGE_SHARE_ARGS_PA_START;
    DVFSBSP_ARGs = (BSP_ARGS *)DrvLib_MapIoSpace(ioPhysicalBase.LowPart, sizeof(BSP_ARGS), FALSE);
    if (DVFSBSP_ARGs == NULL)
    {
        DBGMSG(PWC_INFO, (TEXT("[PWRCON:ERR] %s->DVFSBSP_ARGs MmMapIoSpace() Failed \n"), _T(__FUNCTION__)));
        return FALSE;
    }

    ioPhysicalBase.LowPart = BASE_REG_PA_DMC0;
    g_pDMC0Regs = (DRAMCON_REG *)DrvLib_MapIoSpace(ioPhysicalBase.LowPart, sizeof(DRAMCON_REG), FALSE);
    if (g_pDMC0Regs == NULL)
    {
        DBGMSG(PWC_INFO, (TEXT("[PWRCON:ERR] %s->g_pDMC0 MmMapIoSpace() Failed \n"), _T(__FUNCTION__)));
        return FALSE;
    }
    
    ioPhysicalBase.LowPart = BASE_REG_PA_DMC1;
    g_pDMC1Regs = (DRAMCON_REG *)DrvLib_MapIoSpace(ioPhysicalBase.LowPart, sizeof(DRAMCON_REG), FALSE);
    if (g_pDMC1Regs == NULL)
    {
        DBGMSG(PWC_INFO, (TEXT("[PWRCON:ERR] %s->g_pDMC1 MmMapIoSpace() Failed \n"), _T(__FUNCTION__)));
        return FALSE;
    }
    
    ioPhysicalBase.LowPart = BASE_REG_PA_CMU_GCTRL;
    g_pCMUGCTRLRegs = (CMU_GCTRL_REG *)DrvLib_MapIoSpace(ioPhysicalBase.LowPart, sizeof(CMU_GCTRL_REG), FALSE);
    if (g_pCMUGCTRLRegs == NULL)
    {
        DBGMSG(PWC_INFO, (TEXT("[PWRCON:ERR] %s->g_pCMUGCTRLRegs MmMapIoSpace() Failed \n"), _T(__FUNCTION__)));
        return FALSE;
    }    
#endif

    //--------------------
    // Critical Section
    //--------------------
    InitializeCriticalSection(&csPowerCon);

    DBGMSG(PWC_FUNC, (TEXT("[PWRCON] --%s\n"), _T(__FUNCTION__)));

    return TRUE;
}

/**
*    @fn    ReleaseResource(void)
*    @note  This function unmaps System Controller Register Block' virtual address space.
*/
static void
ReleaseResources(void)
{
    DBGMSG(PWC_FUNC, (TEXT("[PWRCON] ++%s\n"), _T(__FUNCTION__)));

    if (g_pCMUCLKRegs != NULL)
    {
        DrvLib_UnmapIoSpace((PVOID)g_pCMUCLKRegs);
        g_pCMUCLKRegs = NULL;
    }

    if (g_pCMUMISCRegs != NULL)
    {
        DrvLib_UnmapIoSpace((PVOID)g_pCMUMISCRegs);
        g_pCMUMISCRegs = NULL;
    }

    if (g_pPMUPMRegs != NULL)
    {
        DrvLib_UnmapIoSpace((PVOID)g_pPMUPMRegs);
        g_pPMUPMRegs = NULL;
    }

    if (g_pPMUMISCRegs != NULL)
    {
        DrvLib_UnmapIoSpace((PVOID)g_pPMUMISCRegs);
        g_pPMUMISCRegs = NULL;
    }
#ifdef BSP_USEDVFS
    if (g_pVIC0 != NULL)
    {
        DrvLib_UnmapIoSpace((PVOID)g_pVIC0);
        g_pVIC0 = NULL;
    }

    if (g_pVIC1 != NULL)
    {
        DrvLib_UnmapIoSpace((PVOID)g_pVIC1);
        g_pVIC1 = NULL;
    }

    if (g_pVIC2 != NULL)
    {
        DrvLib_UnmapIoSpace((PVOID)g_pVIC2);
        g_pVIC2 = NULL;
    }

    if (g_pVIC3 != NULL)
    {
        DrvLib_UnmapIoSpace((PVOID)g_pVIC3);
        g_pVIC3 = NULL;
    }

    if (g_pPWMRegs != NULL)
    {
        DrvLib_UnmapIoSpace((PVOID)g_pPWMRegs);
        g_pPWMRegs = NULL;
    }

    if (g_pRTCReg != NULL)
    {
        DrvLib_UnmapIoSpace((PVOID)g_pRTCReg);
        g_pRTCReg = NULL;
    }

    if (g_pDMC0Regs != NULL)
    {
        DrvLib_UnmapIoSpace((PVOID)g_pDMC0Regs);
        g_pDMC0Regs = NULL;
    }

    if (g_pDMC1Regs != NULL)
    {
        DrvLib_UnmapIoSpace((PVOID)g_pDMC1Regs);
        g_pDMC1Regs = NULL;
    } 

    if (g_pCMUGCTRLRegs != NULL)
    {
        DrvLib_UnmapIoSpace((PVOID)g_pCMUGCTRLRegs);
        g_pCMUGCTRLRegs = NULL;
    }      
#endif
    
    DeleteCriticalSection(&csPowerCon);

    DBGMSG(PWC_FUNC, (TEXT("[PWRCON] --%s\n"), _T(__FUNCTION__)));
}

BOOL
WINAPI
DllEntry(HINSTANCE hinstDll, DWORD dwReason, LPVOID lpReserved)
{
    if (dwReason == DLL_PROCESS_ATTACH)
    {
        DEBUGREGISTER(hinstDll);
        DisableThreadLibraryCalls ((HMODULE)hinstDll);
        DBGMSG(PWC_FUNC,(TEXT("[PWRCON] %s : Process Attach\n"), _T(__FUNCTION__)));
    }
    else if (dwReason == DLL_PROCESS_DETACH)
    {
        DBGMSG(PWC_FUNC,(TEXT("[PWRCON] %s : Process Detach\n"), _T(__FUNCTION__)));
    }

    return TRUE;
}

BOOL PWC_Deinit(PDWORD pContext)
{
    DBGMSG(PWC_FUNC,(TEXT("[PWRCON] ++%s(0x%08x)\n"), _T(__FUNCTION__), pContext));

    g_bExitThread = TRUE;

    if(g_hThreadPowerMon)        // Make Sure if thread is exist
    {
        // Signal Thread to Finish
        if (g_hMsgQueue)
        {
            CloseMsgQueue(g_hMsgQueue);    // Closing the MsgQueue will force ReadMsgQueue to return
            g_hMsgQueue = NULL;
        }

        // Wait for Thread to Finish
        WaitForSingleObject(g_hThreadPowerMon, INFINITE);
        CloseHandle(g_hThreadPowerMon);
        g_hThreadPowerMon = NULL;
    }

#ifdef BSP_USEDVFS
    g_bExitDVFSThread = TRUE;

    InterruptDisable(g_dwSysIntrDVFS);
    SetEvent(g_hEventDVFS);
    WaitForSingleObject(g_hThreadDVFS, INFINITE);
    CloseHandle(g_hEventDVFS);
    CloseHandle(g_hThreadDVFS);
    g_hThreadDVFS = NULL;
    g_hEventDVFS = NULL;
#endif // BSP_USEDVFS
    
    ReleaseResources();

    LocalFree(pContext);

    DBGMSG(PWC_FUNC,(TEXT("[PWRCON] --%s\n"), _T(__FUNCTION__) ));

    return TRUE;
}

DWORD
PWC_Init(DWORD dwContext)
{
#ifdef BSP_USEDVFS
    DWORD dwIRQ;
#endif // BSP_USEDVFS

//added by terry for PM 2012.06.28
	HANDLE hevReloadActivityTimeouts = OpenEvent(EVENT_ALL_ACCESS, FALSE, _T("PowerManager/ReloadActivityTimeouts"));
	if (hevReloadActivityTimeouts) {
		SetEvent(hevReloadActivityTimeouts);
		CloseHandle(hevReloadActivityTimeouts);
	} 


    DBGMSG(PWC_FUNC,(TEXT("[PWRCON:INF] ++%s(0x%08x)\n"), _T(__FUNCTION__), dwContext));

    if (AllocResources() == FALSE)
    {
        DBGMSG(PWC_INFO,(TEXT("[PWRCON:ERR] %s->AllocResources() Failed \n"), _T(__FUNCTION__)));
        goto CleanUp;
    }

    PwrCon_initialize_register_address((void *)g_pCMUCLKRegs, (void *)g_pCMUMISCRegs, (void *)g_pPMUPMRegs, (void *)g_pPMUMISCRegs);

    // Create power Monitor Thread, Singleton
    g_hThreadPowerMon = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) PowerMonitorThread, NULL, 0, NULL);
    if (g_hThreadPowerMon == NULL )
    {
        DBGMSG(PWC_INFO,(TEXT("[PWRCON:ERR] %s->CreateThread() Power Monitor Failed \n"), _T(__FUNCTION__)));
        goto CleanUp;
    }
    DBGMSG(PWC_FUNC,(TEXT("[PWRCON:INF] --%s()\n"), _T(__FUNCTION__)));

#ifdef BSP_USEDVFS
    //--------------------
    // DVFS
    //--------------------
    if(DVFSBSP_ARGs->bDVFSDisable)
    {    
        RETAILMSG(1,(TEXT("*************************************************\r\n")));
        RETAILMSG(1,(TEXT("* !!CHECK!! DVFS is Disabled By EBOOT !!CHECK!! *\r\n")));  
        RETAILMSG(1,(TEXT("*************************************************\r\n")));
    }
    g_bExitDVFSThread = DVFSBSP_ARGs->bDVFSDisable;

    
    dwIRQ = IRQ_DVFS;
    g_dwSysIntrDVFS = SYSINTR_UNDEFINED;
    g_hEventDVFS = NULL;

    if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &dwIRQ, sizeof(DWORD), &g_dwSysIntrDVFS, sizeof(DWORD), NULL))
    {
        DEBUGMSG(PWC_ZONE_ERROR,(L"[PWC:ERR] PWC_Init : IOCTL_HAL_REQUEST_SYSINTR DVFS Failed \n\r"));
        g_dwSysIntrDVFS = SYSINTR_UNDEFINED;
        goto CleanUp;
    }

    g_hEventDVFS = CreateEvent(NULL, FALSE, FALSE, NULL);
    if(NULL == g_hEventDVFS)
    {
        DEBUGMSG(PWC_ZONE_ERROR,(L"[PWC:ERR] PWC_Init : Create DVFS Failed \n\r"));
        goto CleanUp;
    }

    if (!(InterruptInitialize(g_dwSysIntrDVFS, g_hEventDVFS, 0, 0)))
    {
        DEBUGMSG(PWC_ZONE_ERROR,(L"[PWC:ERR] PWC_Init : InterruptInitialize() DVFS Failed \n\r"));
        goto CleanUp;
    }

    // Create PM Thread
    g_hThreadDVFS = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) DVFS_Thread, NULL, 0, NULL);
    if (g_hThreadDVFS == NULL )
    {
        DEBUGMSG(PWC_ZONE_ERROR,(L"[PWC:ERR] PWC_Init : fail in loading DVFS Driver ()\r\n"));
        goto CleanUp;
    }
	else
	{
		g_hEventSentinel = CreateEvent(NULL, FALSE, FALSE, NULL);

	    if(NULL == g_hEventSentinel)
	    {
	        DEBUGMSG(PWC_ZONE_ERROR,(L"[PWC:ERR] PWC_Init : Create Sentinel Event \n\r"));
	    }	
		else
		{		
			g_hSentinel = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) Sentinel_Thread, NULL, 0, NULL);
			if(g_hSentinel == NULL)
			{
				DEBUGMSG(PWC_ZONE_ERROR,(L"[PWC:ERR] PWC_Init : fail in loading Sentinel driver for DVFS ()\r\n"));
				CloseHandle(g_hEventSentinel);
			}
		}
	}	
#endif // BSP_USEDVFS

#ifdef OEM_PM
    // Create Semaphore
    g_hPWCSemaphore = CreateSemaphore(NULL, 1, 1, (TEXT("PWC_SEMAPHORE")));
    if(!g_hPWCSemaphore)
    {
        DBGMSG(PWC_INFO,(TEXT("[PWRCON:ERR] %s->CreateSemaphore() Failed \n"), _T(__FUNCTION__)));
        goto CleanUp;
    }
    // Create OEM Power Manager Thread, Singleton
    g_hThreadPowerManager= CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) AugmentPM_Thread, NULL, 0, NULL);
    if (g_hThreadPowerManager == NULL )
    {
        DBGMSG(PWC_INFO,(TEXT("[PWRCON:ERR] %s->CreateThread() Power Monitor Failed \n"), _T(__FUNCTION__)));
        goto CleanUp;
    }
    DBGMSG(PWC_FUNC,(TEXT("[PWRCON:INF] --%s()\n"), _T(__FUNCTION__)));

    g_hIntrIntercepter= CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) LPWakeupThread, NULL, 0, NULL);
    if (g_hIntrIntercepter == NULL )
    {
        DBGMSG(PWC_INFO,(TEXT("[PWRCON:ERR] %s->CreateThread() Power Monitor Failed \n"), _T(__FUNCTION__)));
        goto CleanUp;
    }
    DBGMSG(PWC_FUNC,(TEXT("[PWRCON:INF] --%s()\n"), _T(__FUNCTION__)));

#endif

    return TRUE;

CleanUp:

    DBGMSG(PWC_INFO,(TEXT("[PWRCON:ERR] --%s : Failed\n"), _T(__FUNCTION__)));

    PWC_Deinit(0);

    return FALSE;
}

DWORD
PWC_Open(DWORD pContext, DWORD dwAccess, DWORD dwShareMode)
{
    DBGMSG(PWC_FUNC,(TEXT("[PWRCON] %s(0x%08x, 0x%08x, 0x%08x)\n"), _T(__FUNCTION__), pContext, dwAccess, dwShareMode));
    return TRUE;
}

BOOL
PWC_Close(DWORD pContext)
{
    DBGMSG(PWC_FUNC,(TEXT("[PWRCON] %s(0x%08x)\n"), _T(__FUNCTION__), pContext));
    return TRUE;
}

DWORD
PWC_Read (DWORD pContext,  LPVOID pBuf, DWORD Len)
{
    DBGMSG(PWC_FUNC,(TEXT("[PWRCON] %s(0x%08x, 0x%08x, 0x%08x)\n"), _T(__FUNCTION__), pContext, pBuf, Len));
    return (0);    // End of File
}

DWORD
PWC_Write(DWORD pContext, LPCVOID pBuf, DWORD Len)
{
    DBGMSG(PWC_FUNC,(TEXT("[PWRCON] %s(0x%08x, 0x%08x, 0x%08x)\n"), _T(__FUNCTION__), pContext, pBuf, Len));
    return (0);    // Number of Byte
}

DWORD
PWC_Seek (DWORD pContext, long pos, DWORD type)
{
    DBGMSG(PWC_FUNC,(TEXT("[PWRCON] %s(0x%08x, 0x%08x, 0x%08x)\n"), _T(__FUNCTION__), pContext, pos, type));
    return (DWORD)-1;    // Failure
}

BOOL
PWC_PowerUp(DWORD pContext)
{
    DBGMSG(PWC_FUNC,(TEXT("[PWRCON] %s(0x%08x)\n"), _T(__FUNCTION__), pContext));
#ifdef BSP_USEDVFS
        g_bDVFS_Disable = FALSE;
#endif
    return TRUE;
}

BOOL
PWC_PowerDown(DWORD pContext)
{
    DBGMSG(PWC_FUNC,(TEXT("[PWRCON] %s(0x%08x)\n"), _T(__FUNCTION__), pContext));
#ifdef BSP_USEDVFS
    if(!DVFSBSP_ARGs->bDVFSDisable)
    {
        g_bDVFS_Disable = TRUE;
        IFVC_Changer(DVFSBSP_ARGs->CurrentPowerLevel, SYS_L1);
        DVFSBSP_ARGs->CurrentPowerLevel = SYS_L1;
        DVFSBSP_ARGs->DVFSTransitionDone  = TRUE;
    }    
#endif // BSP_USEDVFS

#ifdef OEM_PM
        AugmentedSysPowerState = PMST_LP_SUSPEND;

        if(SharedMem != NULL)
        {
            SharedMem->bAugStateLPMode = FALSE;     // Aug Power state : NORMAL
            SharedMem->bLPAudioEn = FALSE;
        }
#endif
    return TRUE;
}

BOOL
PWC_IOControl(DWORD pContext, DWORD dwCode, PBYTE pBufIn, DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut, PDWORD pdwActualOut)
{
    BOOL bRet = TRUE;
    DWORD dwIndex;

    if ( !( (dwCode == IOCTL_PWRCON_SET_POWER_ON)
        || (dwCode == IOCTL_PWRCON_SET_POWER_OFF)
        || (dwCode == IOCTL_PWRCON_SET_CLOCK_ON)
        || (dwCode == IOCTL_PWRCON_SET_CLOCK_OFF)
#ifdef BSP_USEDVFS  //_______________________________________________________________________________________________________________________
        || (dwCode == IOCTL_DVFS_SET_LEVEL_FIX)
        || (dwCode == IOCTL_DVFS_CLEAR_LEVEL_FIX)
        || (dwCode == IOCTL_DVFS_SET_PROFILE)
        || (dwCode == IOCTL_DVFS_CLEAR_PROFILE)
        || (dwCode == IOCTL_DVFS_GET_STATUS)
        #endif
        ))

    {
        DBGMSG(PWC_INFO,(TEXT("[PWRCON:ERR] %s : Unknown IOCTL [0x%08x]\n"), _T(__FUNCTION__), dwCode));
        SetLastError (ERROR_INVALID_ACCESS);
        return FALSE;
    }

    switch(dwCode)
    {
    case IOCTL_PWRCON_SET_POWER_ON:
        if ((dwLenIn < sizeof(DWORD)) || (NULL == pBufIn))
        {
            DBGMSG(PWC_INFO,(TEXT("[PWRCON:ERR] %s(IOCTL_PWRCON_SET_POWER_ON) : Invalid Parameter\n"),_T(__FUNCTION__)));
            SetLastError (ERROR_INVALID_PARAMETER);
            bRet = FALSE;
            break;
        }

        dwIndex = (DWORD)(*pBufIn);

        EnterCriticalSection(&csPowerCon);

        switch(dwIndex)
        {
        case PWR_IP_DISPCON:    // LCD Sub system
        case PWR_IP_MIE:
        case PWR_IP_MIPI_DSI:
#if (S5PV210_EVT>0)            
        case PWR_IP_G2D:
#endif            
            g_aIPPowerStatus[dwIndex] = TRUE;
            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] %s(SET_POWER_ON, %d) : LCD\n"), _T(__FUNCTION__), dwIndex));
            PwrCon_set_block_power_on(BLKPWR_DOMAIN_LCD);
            break;
        case PWR_IP_VP:         //TV Sub System
        case PWR_IP_HDMI:
        case PWR_IP_MIXER:
        case PWR_IP_TVENC:
            g_aIPPowerStatus[dwIndex] = TRUE;
            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] %s(SET_POWER_ON, %d) : TV\n"), _T(__FUNCTION__), dwIndex));
            PwrCon_set_block_power_on(BLKPWR_DOMAIN_TV);
            break;
        case PWR_IP_CAMIF:      // CAM Sub System
        case PWR_IP_CAMIF1:
        case PWR_IP_CAMIF2:    
        case PWR_IP_MIPI_CSI:
        case PWR_IP_JPEG:
        case PWR_IP_ROTATOR:
        case PWR_IP_IPC:    
            g_aIPPowerStatus[dwIndex] = TRUE;
            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] %s(SET_POWER_ON, %d) : CAM\n"), _T(__FUNCTION__), dwIndex));
            PwrCon_set_block_power_on(BLKPWR_DOMAIN_CAM);
            break;
        case PWR_IP_MFC:        // Domain MFC
            g_aIPPowerStatus[dwIndex] = TRUE;
            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] %s(SET_POWER_ON, %d) : MFC\n"), _T(__FUNCTION__), dwIndex));
            PwrCon_set_block_power_on(BLKPWR_DOMAIN_MFC);
            break;
        case PWR_IP_G3D:        // Domain G3D
            g_aIPPowerStatus[dwIndex] = TRUE;
            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] %s(SET_POWER_ON, %d) : 3D\n"), _T(__FUNCTION__), dwIndex));
            PwrCon_set_block_power_on(BLKPWR_DOMAIN_G3D);
            break;
        case PWR_IP_AUDIOSS:
            g_aIPPowerStatus[dwIndex] = TRUE;
            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] %s(SET_POWER_ON, %d) : AUDIOSS\n"), _T(__FUNCTION__), dwIndex));
            PwrCon_set_block_power_on(BLKPWR_DOMAIN_AUDIOSS);
            break;
        case PWR_IP_HDMI_PHY:
            g_aIPPowerStatus[dwIndex] = TRUE; //HDMI_PHY
            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] %s(SET_POWER_ON, %d) : HDMI_PHY\n"), _T(__FUNCTION__), dwIndex));
            PwrCon_set_analog_power_on(PWR_IP_HDMI_PHY);
            break;
        case PWR_IP_USB_PHY0:
            g_aIPPowerStatus[dwIndex] = TRUE; //USBPHY0
            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] %s(SET_POWER_ON, %d) : USB_PHY0\n"), _T(__FUNCTION__), dwIndex));
            PwrCon_set_analog_power_on(PWR_IP_USB_PHY0);
            break;
        case PWR_IP_USB_PHY1:
            g_aIPPowerStatus[dwIndex] = TRUE; //USBPHY1
            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] %s(SET_POWER_ON, %d) : USBPHY1\n"), _T(__FUNCTION__), dwIndex));
            PwrCon_set_analog_power_on(PWR_IP_USB_PHY1);
            break;            
        case PWR_IP_DAC:
            g_aIPPowerStatus[dwIndex] = TRUE; //DAC
            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] %s(SET_POWER_ON, %d) : DAC\n"), _T(__FUNCTION__), dwIndex));
            PwrCon_set_analog_power_on(PWR_IP_DAC);
            break;   
        case PWR_IP_MIPI_DSI_DPHY:
            g_aIPPowerStatus[dwIndex] = TRUE; //MIPI_DPHY for DSI
            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] %s(SET_POWER_ON, %d) : MIPI_DSI_DPHY\n"), _T(__FUNCTION__), dwIndex));
            PwrCon_set_analog_power_on(PWR_IP_MIPI_DSI_DPHY);
            break;            
        case PWR_IP_MIPI_CSI_DPHY:
            g_aIPPowerStatus[dwIndex] = TRUE; //MIPI_DPHY for DSI
            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] %s(SET_POWER_ON, %d) : MIPI_CSI_DPHY\n"), _T(__FUNCTION__), dwIndex));
            PwrCon_set_analog_power_on(PWR_IP_MIPI_CSI_DPHY);
            break;             
        case PWR_IP_BATT_ADC:
        case PWR_IP_TS_ADC:
            g_aIPPowerStatus[dwIndex] = TRUE; 
            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] %s(SET_POWER_ON, %d) : PWR_IP_ADC\n"), _T(__FUNCTION__), dwIndex));
            PwrCon_set_analog_power_on(PWR_IP_TS_ADC);
            break;             
        default:
            DBGMSG(PWC_INFO,(TEXT("[PWRCON:ERR] %s(IOCTL_PWRCON_SET_POWER_ON, %d) : Invalid Parameter\n"), _T(__FUNCTION__), dwIndex));
            SetLastError (ERROR_INVALID_PARAMETER);
            bRet = FALSE;
        }

        LeaveCriticalSection(&csPowerCon);

        break;

    case IOCTL_PWRCON_SET_POWER_OFF:

        if ((dwLenIn < sizeof(DWORD)) || (NULL == pBufIn))
        {
            DBGMSG(PWC_INFO,(TEXT("[PWRCON:ERR] %s(IOCTL_PWRCON_SET_POWER_OFF) : Invalid Parameter\n"), _T(__FUNCTION__)));
            SetLastError (ERROR_INVALID_PARAMETER);
            bRet = FALSE;
            break;
        }

        dwIndex = (DWORD)(*pBufIn);

        EnterCriticalSection(&csPowerCon);

        switch(dwIndex)
        {
        case PWR_IP_DISPCON:    // LCD Sub system
        case PWR_IP_MIE:
        case PWR_IP_MIPI_DSI:
#if (S5PV210_EVT>0)            
        case PWR_IP_G2D:
#endif              
            g_aIPPowerStatus[dwIndex] = FALSE;
            if ( (g_aIPPowerStatus[PWR_IP_DISPCON] == FALSE)
                && (g_aIPPowerStatus[PWR_IP_MIE] == FALSE)
                && (g_aIPPowerStatus[PWR_IP_MIPI_DSI] == FALSE)
#if (S5PV210_EVT>0)                
                && (g_aIPPowerStatus[PWR_IP_G2D] == FALSE)
#endif                
                )
            {
                DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] %s(SET_POWER_OFF, %d) : LCD\n"), _T(__FUNCTION__), dwIndex));
                PwrCon_set_block_power_off(BLKPWR_DOMAIN_LCD);

            }
            else
            {
              DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] No LCD Power OFF(PWR_IP_DISPCON(%d), PWR_IP_MIE(%d),PWR_IP_MIPI_DSI(%d))"), g_aIPPowerStatus[PWR_IP_DISPCON],g_aIPPowerStatus[PWR_IP_MIE],g_aIPPowerStatus[PWR_IP_MIPI_DSI]));  
            }
            break;
        case PWR_IP_VP:        // TV Sub System
        case PWR_IP_HDMI:
        case PWR_IP_MIXER:
        case PWR_IP_TVENC:
            g_aIPPowerStatus[dwIndex] = FALSE;
            if ( (g_aIPPowerStatus[PWR_IP_VP] == FALSE)
                && (g_aIPPowerStatus[PWR_IP_HDMI] == FALSE)
                && (g_aIPPowerStatus[PWR_IP_MIXER] == FALSE)
                && (g_aIPPowerStatus[PWR_IP_TVENC] == FALSE)
                )
            {
                DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] %s(SET_POWER_OFF, %d) : TV\n"), _T(__FUNCTION__), dwIndex));
                PwrCon_set_block_power_off(BLKPWR_DOMAIN_TV);

            }
            else
            {
               DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] No TV Power OFF(PWR_IP_VP(%d), PWR_IP_HDMI(%d),PWR_IP_MIXER(%d),PWR_IP_TVENC(%d))"), g_aIPPowerStatus[PWR_IP_VP],g_aIPPowerStatus[PWR_IP_HDMI],g_aIPPowerStatus[PWR_IP_MIXER],g_aIPPowerStatus[PWR_IP_TVENC]));  
            }
            break;
        case PWR_IP_CAMIF:        // CAMIF Sub System
        case PWR_IP_CAMIF1:
        case PWR_IP_CAMIF2:   
        case PWR_IP_MIPI_CSI:
        case PWR_IP_JPEG:
        case PWR_IP_ROTATOR:
        case PWR_IP_IPC:             
            g_aIPPowerStatus[dwIndex] = FALSE;
            if ( (g_aIPPowerStatus[PWR_IP_CAMIF] == FALSE)
                && (g_aIPPowerStatus[PWR_IP_CAMIF1] == FALSE)
                && (g_aIPPowerStatus[PWR_IP_CAMIF2] == FALSE)
                && (g_aIPPowerStatus[PWR_IP_MIPI_CSI] == FALSE)
                && (g_aIPPowerStatus[PWR_IP_JPEG] == FALSE)
                && (g_aIPPowerStatus[PWR_IP_ROTATOR] == FALSE)
                && (g_aIPPowerStatus[PWR_IP_IPC] == FALSE)
                )
            {
                DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] %s(SET_POWER_OFF, %d) : CAM\n"), _T(__FUNCTION__), dwIndex));
                PwrCon_set_block_power_off(BLKPWR_DOMAIN_CAM);

            }
            else
            {
              DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] No CAMIF Power OFF(PWR_IP_CAMIF(%d), PWR_IP_MIPI_CSI(%d),PWR_IP_JPEG(%d),PWR_IP_ROTATOR(%d),PWR_IP_IPC(%d))"), g_aIPPowerStatus[PWR_IP_CAMIF],g_aIPPowerStatus[PWR_IP_MIPI_CSI],g_aIPPowerStatus[PWR_IP_JPEG],g_aIPPowerStatus[PWR_IP_ROTATOR],g_aIPPowerStatus[PWR_IP_IPC]));  
            }
            break;            
        case PWR_IP_MFC:        // Domain MFC
            g_aIPPowerStatus[dwIndex] = FALSE;
            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] %s(SET_POWER_OFF, %d) : MFC\n"), _T(__FUNCTION__), dwIndex));
            PwrCon_set_block_power_off(BLKPWR_DOMAIN_MFC);

            break;
        case PWR_IP_G3D:        // Domain G3D
            g_aIPPowerStatus[dwIndex] = FALSE;
            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] %s(SET_POWER_OFF, %d) : 3D\n"), _T(__FUNCTION__), dwIndex));
            PwrCon_set_block_power_off(BLKPWR_DOMAIN_G3D);

            break;
        case PWR_IP_AUDIOSS:    //Audio SS
            g_aIPPowerStatus[dwIndex] = FALSE;
            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] %s(SET_POWER_OFF, %d) : AUDIOSS\n"), _T(__FUNCTION__), dwIndex));
            PwrCon_set_block_power_off(BLKPWR_DOMAIN_AUDIOSS);
            break;            
        case PWR_IP_HDMI_PHY:
            g_aIPPowerStatus[dwIndex] = FALSE; //HDMIPHY
            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] %s(SET_POWER_OFF, %d) : HDMI_PHY\n"), _T(__FUNCTION__), dwIndex));
            PwrCon_set_analog_power_off(PWR_IP_HDMI_PHY);
            break;
        case PWR_IP_USB_PHY0:
            g_aIPPowerStatus[dwIndex] = FALSE; //USBPHY0
            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] %s(SET_POWER_OFF, %d) : USB_PHY0\n"), _T(__FUNCTION__), dwIndex));
            PwrCon_set_analog_power_off(PWR_IP_USB_PHY0);
            break;
        case PWR_IP_USB_PHY1:
            g_aIPPowerStatus[dwIndex] = FALSE; //USBPHY1
            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] %s(SET_POWER_OFF, %d) : USB_PHY1\n"), _T(__FUNCTION__), dwIndex));
            PwrCon_set_analog_power_off(PWR_IP_USB_PHY1);
            break;            
        case PWR_IP_DAC:
            g_aIPPowerStatus[dwIndex] = FALSE; //DAC
            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] %s(SET_POWER_OFF, %d) : DAC\n"), _T(__FUNCTION__), dwIndex));
            PwrCon_set_analog_power_off(PWR_IP_DAC);
            break;   
        case PWR_IP_MIPI_DSI_DPHY:
            g_aIPPowerStatus[dwIndex] = FALSE; //MIPI DPHY for DSI
            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] %s(SET_POWER_OFF, %d) : MIPI_DSI_DPHY\n"), _T(__FUNCTION__), dwIndex));
            PwrCon_set_analog_power_off(PWR_IP_MIPI_DSI_DPHY);   
            break;
        case PWR_IP_MIPI_CSI_DPHY:
            g_aIPPowerStatus[dwIndex] = FALSE; //MIPI DPHY for CSI
            DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] %s(SET_POWER_OFF, %d) : MIPI_CSI_DPHY\n"), _T(__FUNCTION__), dwIndex));
            PwrCon_set_analog_power_off(PWR_IP_MIPI_CSI_DPHY);  
            break;
        case PWR_IP_BATT_ADC:
        case PWR_IP_TS_ADC:
            g_aIPPowerStatus[dwIndex] = FALSE; 
            if ( (g_aIPPowerStatus[PWR_IP_BATT_ADC] == FALSE)
                && (g_aIPPowerStatus[PWR_IP_TS_ADC] == FALSE)
                )
            {    
                DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] %s(SET_POWER_ON, %d) : PWR_IP_ADC\n"), _T(__FUNCTION__), dwIndex));
                PwrCon_set_analog_power_off(PWR_IP_TS_ADC);
            }    
            break;                       
        default:
            DBGMSG(PWC_INFO,(TEXT("[PWRCON:ERR] %s(SET_POWER_OFF, %d) : Invalid Parameter\n"), _T(__FUNCTION__), dwIndex));
            SetLastError (ERROR_INVALID_PARAMETER);
            bRet = FALSE;
        }

        LeaveCriticalSection(&csPowerCon);

        break;
        
    case IOCTL_PWRCON_SET_CLOCK_ON:
        if ((dwLenIn < sizeof(DWORD)) || (NULL == pBufIn))
        {
            DBGMSG(PWC_INFO,(TEXT("[PWRCON:ERR] %s(SET_CLOCK_ON) : Invalid Parameter\n"),_T(__FUNCTION__)));
            SetLastError (ERROR_INVALID_PARAMETER);
            bRet = FALSE;
            break;
        }

        dwIndex = (DWORD)(*pBufIn);

        EnterCriticalSection(&csPowerCon);

        g_aIPClockStatus[dwIndex] = TRUE;
        DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] %s(SET_CLOCK_ON, %d) \n"), _T(__FUNCTION__), dwIndex));

        PwrCon_set_IP_clock_on(dwIndex);

        LeaveCriticalSection(&csPowerCon);

        break;

    case IOCTL_PWRCON_SET_CLOCK_OFF:
        if ((dwLenIn < sizeof(DWORD)) || (NULL == pBufIn))
        {
            DBGMSG(PWC_INFO,(TEXT("[PWRCON:ERR] %s(SET_CLOCK_OFF) : Invalid Parameter\n"),_T(__FUNCTION__)));
            SetLastError (ERROR_INVALID_PARAMETER);
            bRet = FALSE;
            break;
        }

        dwIndex = (DWORD)(*pBufIn);

        EnterCriticalSection(&csPowerCon);

        g_aIPClockStatus[dwIndex] = FALSE;
        DBGMSG(PWC_INFO,(TEXT("[PWRCON:INF] %s(SET_CLOCK_OFF, %d) \n"), _T(__FUNCTION__), dwIndex));

        PwrCon_set_IP_clock_off(dwIndex);

        LeaveCriticalSection(&csPowerCon);

        break;
        
#ifdef BSP_USEDVFS  
    case IOCTL_DVFS_SET_LEVEL_FIX:
        if ((dwLenIn < sizeof(DWORD)) || (NULL == pBufIn))
        {
            DBGMSG(PWC_INFO, (TEXT("[PWRCON:ERR] %s(IOCTL_DVFS_SET_LEVEL_FIX) : Invalid Parameter\n"),_T(__FUNCTION__)));
            SetLastError (ERROR_INVALID_PARAMETER);
            bRet = FALSE;
        break;
        }

        dwIndex = (DWORD)(*pBufIn);

        EnterCriticalSection(&csPowerCon);

        if(dwIndex <=  SYS_L3)
        {
            if(dwIndex >= SYS_L0)
            {
                PWC_EnableDVFS(FALSE);
                PWC_SetDVFS(dwIndex);
            }
        }

        LeaveCriticalSection(&csPowerCon);

        break;

    case IOCTL_DVFS_CLEAR_LEVEL_FIX:
        EnterCriticalSection(&csPowerCon);

        PWC_EnableDVFS(TRUE);

        LeaveCriticalSection(&csPowerCon);

        break;

    case IOCTL_DVFS_SET_PROFILE:
        if ((dwLenIn < sizeof(DWORD)) || (NULL == pBufIn))
        {
            DBGMSG(PWC_INFO, (TEXT("[PWRCON:ERR] %s(IOCTL_PWRCON_SET_POWER_ON) : Invalid Parameter\n"),_T(__FUNCTION__)));
            SetLastError (ERROR_INVALID_PARAMETER);
            bRet = FALSE;
            break;
        }

        dwIndex = (DWORD)(*pBufIn);

        EnterCriticalSection(&csPowerCon);

        PWC_PofileHandler(dwIndex, TRUE);

        LeaveCriticalSection(&csPowerCon);

        break;

    case IOCTL_DVFS_CLEAR_PROFILE:
        if ((dwLenIn < sizeof(DWORD)) || (NULL == pBufIn))
        {
            DBGMSG(PWC_INFO, (TEXT("[PWRCON:ERR] %s(IOCTL_PWRCON_SET_POWER_ON) : Invalid Parameter\n"),_T(__FUNCTION__)));
            SetLastError (ERROR_INVALID_PARAMETER);
            bRet = FALSE;
        break;
        }

        dwIndex = (DWORD)(*pBufIn);

        EnterCriticalSection(&csPowerCon);

        PWC_PofileHandler(dwIndex, FALSE);

        LeaveCriticalSection(&csPowerCon);

        break;
	case IOCTL_DVFS_GET_STATUS:

        EnterCriticalSection(&csPowerCon); 

        if(dwLenOut >= sizeof(INFO_DVFS_STATUS))
        {
            PINFO_DVFS_STATUS DVFSStatus    = (PINFO_DVFS_STATUS)pBufOut;
			DVFSStatus->dwCurrentPowerLevel = DVFSBSP_ARGs->CurrentPowerLevel;
			DVFSStatus->dwRunningTime		= DVFSBSP_ARGs->CPU_RunTime;
			DVFSStatus->dwIdleTime			= DVFSBSP_ARGs->CPU_IdleTime;
			DVFSStatus->dwDurationTime		= DVFSBSP_ARGs->MonitoringTime;
			DVFSStatus->dwSupremumRatio		= DVFSBSP_ARGs->LevelUpMargin;
			DVFSStatus->dwInfimumRatio		= DVFSBSP_ARGs->LevelDnMargin;
			DVFSStatus->dwMaxLevel			= gRealMaxPowerLevel;
			DVFSStatus->dwMinLevel			= gRealMinPowerLevel;
			DVFSStatus->bDVFSDisabled		= g_bDVFS_Disable;			

            *pdwActualOut               = sizeof(INFO_DVFS_STATUS);
        }		
		
        LeaveCriticalSection(&csPowerCon);		
		break;
#endif // BSP_USEDVFS
    }

    return bRet;
}

// EOF
