//
// 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.
//
//------------------------------------------------------------------------------
//
//  Module: DeepIdle.c
//
//  Implementation of Deep Idle for LP Mode.
//
//
//      History :
//              [2009.07.28 JOHN] The sources are moved to new file
//
#include "DeepIdle.h"


#define ON  1
#define RETENTION 0
#define DEEPIDLE_TOP_STATUS RETENTION

#define WAKEUP_PWR_BUTTON   BP_EINT14_WAKEUP_MASK
#define WAKEUP_RESET_BUTTON BP_EINT31_WAKEUP_MASK

#define EINT_POWER_BUTTON   EXT_INT_31
#define EINT_RESET_BUTTON   EXT_INT_14


extern volatile GPIO_REG *g_pGPIORegs;
extern volatile SYSTIMER_REG *g_pSYSTimerRegs;
extern volatile VIC_REG *g_pVIC0Regs;
extern volatile PMU_PM_REG *g_pPMUPMRegs;
extern volatile PMU_MISC_REG *g_pPMUMISCRegs;



static volatile VIC_REG *g_pVIC1Regs = NULL;
static volatile VIC_REG *g_pVIC2Regs = NULL;
static volatile VIC_REG *g_pVIC3Regs = NULL;
static volatile TSADC_REG *g_pADC0Regs = NULL;
static volatile TSADC_REG *g_pADC1Regs = NULL;
static volatile SDMMC_REG *g_pSDMMC0Regs = NULL;
static volatile SDMMC_REG *g_pSDMMC1Regs = NULL;
static volatile SDMMC_REG *g_pSDMMC2Regs = NULL;
static volatile SDMMC_REG *g_pSDMMC3Regs = NULL;
static volatile unsigned int *g_pOTGLinkRegs = NULL;
static volatile DMAC_REG *g_pPDMAC0Regs = NULL;
static volatile DMAC_REG *g_pPDMAC1Regs = NULL;
static volatile DMAC_REG *g_pMDMACRegs = NULL;
static volatile IIC_REG  *g_pIIC0Regs = NULL;
static volatile IIC_REG  *g_pIIC2Regs = NULL;
//static volatile OneNANDReg *g_pONDCReg = NULL;

static WORD wIntSignals[4];


extern void System_EnableBP(void);
extern void CPUDeepIdleMode(void);

static BOOL DeepIdle_CheckDMAUse();
static BOOL DeepIdle_CheckUSBOTGUse();
static BOOL DeepIdle_CheckSDMMCUse();
static BOOL DeepIdle_CheckI2CUse();
//static BOOL DeepIdle_Check_OneNandUse();

//Function For Deep Idle
static void DeepIdle_WakeUpSource_Configure(void);
static void ADC_Configure(void);


void DeepIdle_Init(void)
{
    g_pADC0Regs = (volatile TSADC_REG*)OALPAtoVA(BASE_REG_PA_TSADC0, FALSE);
    g_pADC1Regs = (volatile TSADC_REG*)OALPAtoVA(BASE_REG_PA_TSADC1, FALSE);
    g_pOTGLinkRegs = (unsigned int *)OALPAtoVA(BASE_REG_PA_USBOTG_LINK, FALSE);
    g_pSDMMC0Regs = (volatile SDMMC_REG*)OALPAtoVA(BASE_REG_PA_SDMMC0, FALSE);
    g_pSDMMC1Regs = (volatile SDMMC_REG*)OALPAtoVA(BASE_REG_PA_SDMMC1, FALSE);
    g_pSDMMC2Regs = (volatile SDMMC_REG*)OALPAtoVA(BASE_REG_PA_SDMMC2, FALSE);
    g_pSDMMC3Regs = (volatile SDMMC_REG*)OALPAtoVA(BASE_REG_PA_SDMMC3, FALSE);
    g_pVIC1Regs = (volatile VIC_REG*)OALPAtoVA(BASE_REG_PA_VIC1,FALSE);
    g_pVIC2Regs = (volatile VIC_REG*)OALPAtoVA(BASE_REG_PA_VIC2,FALSE);
    g_pVIC3Regs = (volatile VIC_REG*)OALPAtoVA(BASE_REG_PA_VIC3,FALSE);
    g_pPDMAC0Regs = (volatile DMAC_REG*)OALPAtoVA(BASE_REG_PA_PDMA0,FALSE);
    g_pPDMAC1Regs = (volatile DMAC_REG*)OALPAtoVA(BASE_REG_PA_PDMA1,FALSE);
    g_pMDMACRegs = (volatile DMAC_REG*)OALPAtoVA(BASE_REG_PA_MDMA,FALSE);
    g_pIIC0Regs = (volatile IIC_REG*)OALPAtoVA(BASE_REG_PA_I2C0,FALSE);
    g_pIIC2Regs = (volatile IIC_REG*)OALPAtoVA(BASE_REG_PA_I2C0,FALSE);    
}

//-----------------------------------------------------------------------------------------------------
//
//  Function:   PreConfigureDeepIdle()
//
//  This PreConfigureDeepIdle function configure registers for prefaring to enter deep idle power modd
//
//

VOID PreConfigureDeepIdle()
{
#if (DEEPIDLE_TOP_STATUS == ON)
    {
        g_pPMUPMRegs->PWR_CONF.IDLE_CFG = (g_pPMUPMRegs->PWR_CONF.IDLE_CFG & ~((BW_CFG_DIDLE<<BP_CFG_DIDLE)     |
                                                                                (BW_ARM_L2CACHE<<BP_ARM_L2CACHE) |
                                                                                (BW_TOP_MEMORY<<BP_TOP_MEMORY)   |
                                                                                (BW_TOP_LOGIC<<BP_TOP_LOGIC)))  |
                                          (CFG_DEEP_IDLE<<BP_CFG_DIDLE)     |
                                          (ARM_L2CACHE_OFF<<BP_ARM_L2CACHE) |
                                          (TOP_MEMORY_ON<<BP_TOP_MEMORY)    |
                                          (TOP_LOGIC_ON<<BP_TOP_LOGIC);

    }
#else
    {
        g_pPMUPMRegs->PWR_CONF.IDLE_CFG = (g_pPMUPMRegs->PWR_CONF.IDLE_CFG & ~((BW_CFG_DIDLE<<BP_CFG_DIDLE)     |
                                                                               (BW_ARM_L2CACHE<<BP_ARM_L2CACHE)|
                                                                               (BW_TOP_MEMORY<<BP_TOP_MEMORY)  |
                                                                               (BW_TOP_LOGIC<<BP_TOP_LOGIC)))|
                                          (CFG_DEEP_IDLE<<BP_CFG_DIDLE)     |
                                          (ARM_L2CACHE_RET<<BP_ARM_L2CACHE) |
                                          (TOP_MEMORY_RET<<BP_TOP_MEMORY)    |
                                          (TOP_LOGIC_RET<<BP_TOP_LOGIC);
    }
#endif
}


//------------------------------------------------------------------------------------------------------
//
//  Function:   OALCPUDeepIdle
//
//  This DeepIdle function implements DeepIdle Power State of CPU instead of Idle function.
//  It is intended to save more power in case of Idle function. This function must be used
//  in the limited conditions. So, This function must supported by S5PV210 CPU.
//
//
VOID OALCPUDeepIdle()
{
	static UINT32	dwSaveIntEnVIC0, dwSaveIntEnVIC1,dwSaveIntEnVIC2, dwSaveIntEnVIC3;

#if (DEEPIDLE_TOP_STATUS == ON)
    {
        dwSaveIntEnVIC0 = g_pVIC0Regs->VICINTENABLE;
        dwSaveIntEnVIC1 = g_pVIC1Regs->VICINTENABLE;
        dwSaveIntEnVIC2 = g_pVIC2Regs->VICINTENABLE;
        dwSaveIntEnVIC3 = g_pVIC3Regs->VICINTENABLE;

        g_pVIC0Regs->VICINTENCLEAR = 0xffffffff;
        g_pVIC1Regs->VICINTENCLEAR = 0xffffffff;
        g_pVIC2Regs->VICINTENCLEAR = 0xffffffff;
        g_pVIC3Regs->VICINTENCLEAR = 0xffffffff;

        DeepIdle_WakeUpSource_Configure();
        
        Set_PinData(g_pGPIORegs, LED7, 1);
        CPUDeepIdleMode();
        Set_PinData(g_pGPIORegs, LED7, 0);

        g_pPMUPMRegs->PWR_CONF.IDLE_CFG = (g_pPMUPMRegs->PWR_CONF.IDLE_CFG & ~((BW_CFG_DIDLE<<BP_CFG_DIDLE)));
                                                                     
        System_EnableBP();


        g_pVIC0Regs->VICINTENABLE = dwSaveIntEnVIC0;
	    g_pVIC1Regs->VICINTENABLE = dwSaveIntEnVIC1;
	    g_pVIC2Regs->VICINTENABLE = dwSaveIntEnVIC2;
        g_pVIC3Regs->VICINTENABLE = dwSaveIntEnVIC3;
    }
#else
    {
       
        dwSaveIntEnVIC0 = g_pVIC0Regs->VICINTENABLE;
        dwSaveIntEnVIC1 = g_pVIC1Regs->VICINTENABLE;
        dwSaveIntEnVIC2 = g_pVIC2Regs->VICINTENABLE;
        dwSaveIntEnVIC3 = g_pVIC3Regs->VICINTENABLE;

        g_pVIC0Regs->VICINTENCLEAR = 0xffffffff;
        g_pVIC1Regs->VICINTENCLEAR = 0xffffffff;
        g_pVIC2Regs->VICINTENCLEAR = 0xffffffff;
        g_pVIC3Regs->VICINTENCLEAR = 0xffffffff;

        DeepIdle_WakeUpSource_Configure();

        Set_PinData(g_pGPIORegs, LED7, 1);
        CPUDeepIdleMode();
        Set_PinData(g_pGPIORegs, LED7, 0);

        g_pPMUPMRegs->PWR_CONF.IDLE_CFG = (g_pPMUPMRegs->PWR_CONF.IDLE_CFG & ~((BW_CFG_DIDLE<<BP_CFG_DIDLE)));

        System_EnableBP();

        g_pPMUMISCRegs->SYS_CON.OTHERS |= (1<<BP_RET_RELEAE_GPIO);   //ENABLE GPIO
        g_pPMUMISCRegs->SYS_CON.OTHERS |= (1<<BP_RET_RELEAE_MMC_IO); //ENABLE MMC IO
        g_pPMUMISCRegs->SYS_CON.OTHERS |= (1<<BP_RET_RELEAE_UART_IO); //ENABLE UART IO

        g_pVIC0Regs->VICINTENABLE = dwSaveIntEnVIC0;
	    g_pVIC1Regs->VICINTENABLE = dwSaveIntEnVIC1;
	    g_pVIC2Regs->VICINTENABLE = dwSaveIntEnVIC2;
        g_pVIC3Regs->VICINTENABLE = dwSaveIntEnVIC3;

    }
#endif
}

BOOL DeepIdle_CheckEnterDeepIdle()
{
  if(!(g_pBSPArgs->bLPAudioEn))
  {
   return FALSE;  //Can't enter into deep idle because Custom player is not played
  }
  else
  {
    if(!g_pBSPArgs->bLPDisplayOff)
    {
        return FALSE; //Can't enter into deep idle becacuse display is not off
    }
    else
    {
        if((DeepIdle_CheckUSBOTGUse())||(DeepIdle_CheckDMAUse())||(DeepIdle_CheckSDMMCUse())||(DeepIdle_CheckI2CUse()))
        {
            return FALSE;
        }
        else
        {
            if(g_pVIC2Regs->VICIRQSTATUS & 1<<16)
            {    
                // For early WakeUp
                return FALSE;
            }
            else
                return TRUE;   // Accept to enter into deep idle
        }
    }
  }
    return TRUE;
}



BOOL DeepIdle_CheckI2CUse()
{
   if((g_pIIC0Regs->IICSTAT & 1<<5)||(g_pIIC2Regs->IICSTAT & 1<<5))
   {
        //OALMSG(1,(TEXT("IIC Use\r\n")));
        return TRUE;
   }
   else
        return FALSE;
}


BOOL DeepIdle_CheckSDMMCUse()
{

   if((g_pSDMMC0Regs->CLKCON & 1<<2) || (g_pSDMMC1Regs->CLKCON & 1<<2) || (g_pSDMMC2Regs->CLKCON & 1<<2)|| (g_pSDMMC3Regs->CLKCON & 1<<2))
    {
	    //OALMSG(1,(TEXT("SDMMC Clock Enable\r\n")));
	    return TRUE;
	}
   else  
   return FALSE;

}

BOOL DeepIdle_CheckDMAUse()
{

 if((g_pPDMAC0Regs->CHStatusCH0&0xf)||(g_pPDMAC0Regs->CHStatusCH1&0xf)||(g_pPDMAC0Regs->CHStatusCH2&0xf)||(g_pPDMAC0Regs->CHStatusCH3&0xf)||(g_pPDMAC0Regs->CHStatusCH4&0xf)||(g_pPDMAC0Regs->CHStatusCH5&0xf)||(g_pPDMAC0Regs->CHStatusCH6&0xf)||(g_pPDMAC0Regs->CHStatusCH7&0xf))
 {
    //OALMSG(1,(TEXT("PDMA0 USE\r\n")));
    return TRUE;
 }
 else
 {
    if((g_pPDMAC1Regs->CHStatusCH0&0xf)||(g_pPDMAC1Regs->CHStatusCH1&0xf)||(g_pPDMAC1Regs->CHStatusCH2&0xf)||(g_pPDMAC1Regs->CHStatusCH3&0xf)||(g_pPDMAC1Regs->CHStatusCH4&0xf)||(g_pPDMAC1Regs->CHStatusCH5&0xf)||(g_pPDMAC1Regs->CHStatusCH6&0xf)||(g_pPDMAC1Regs->CHStatusCH7&0xf))
    {
      //OALMSG(1,(TEXT("PDMA1 USE\r\n")));
      return TRUE;
    }
    else
    {
        if((g_pMDMACRegs->CHStatusCH0&0xf)||(g_pMDMACRegs->CHStatusCH1&0xf)||(g_pMDMACRegs->CHStatusCH2&0xf)||(g_pMDMACRegs->CHStatusCH3&0xf)||(g_pMDMACRegs->CHStatusCH4&0xf)||(g_pMDMACRegs->CHStatusCH5&0xf)||(g_pMDMACRegs->CHStatusCH6&0xf)||(g_pMDMACRegs->CHStatusCH7&0xf))
        {
            //OALMSG(1,(TEXT("MDMA USE\r\n")));
            return TRUE;
        }
        else
            return FALSE;
    }
 }
    
}

BOOL DeepIdle_CheckUSBOTGUse()
{

 if(*g_pOTGLinkRegs & (0x3<<18))
 {
    //OALMSG(1,(TEXT("USB OTG Use\r\n")));
    return TRUE;
 }
 else
    return FALSE;
 
}

BOOL DeepIdle_WakeupSrc(void * SharedArgs, DWORD PHYIRQ_ID)
{
    BOOL retVal = FALSE;
    BSP_ARGS * pBSP_ARGs;

    pBSP_ARGs = (BSP_ARGS *)SharedArgs;

    if(pBSP_ARGs == NULL) return retVal;

    if(PHYIRQ_ID >= 0  && PHYIRQ_ID < 32)                                   // VIC 0
    {
        if(pBSP_ARGs->LPWakeUpSrc[0] & (0x1 << PHYIRQ_ID))
        {
            retVal = TRUE;
        }
    }
    else if(PHYIRQ_ID >= 32 && PHYIRQ_ID < 64)                           // VIC 1
    {
        if(pBSP_ARGs->LPWakeUpSrc[1] & (0x1 << (PHYIRQ_ID - 32)))
        {
            retVal = TRUE;
        }
    }
    else if(PHYIRQ_ID >= 64 && PHYIRQ_ID < 96)                           // VIC 2
    {
        if(pBSP_ARGs->LPWakeUpSrc[2] & (0x1 << (PHYIRQ_ID - 64)))
        {
            retVal = TRUE;
        }
    }
    else if(PHYIRQ_ID >= 96 && PHYIRQ_ID < 128)                           // VIC 3
    {
        if(pBSP_ARGs->LPWakeUpSrc[3] & (0x1 << (PHYIRQ_ID - 96)))
        {
            retVal = TRUE;
        }
    }    
    else
    {
        OALMSG(1,(_T("Invalid Physical Interrupt occured %d\r\n"), PHYIRQ_ID));
    }

    return retVal;

}


static void DeepIdle_WakeUpSource_Configure()
{
    
#if (DEEPIDLE_TOP_STATUS == ON)

        g_pPMUPMRegs->PWR_CONF.WAKEUP_MASK &= ~(BP_WAKEUP_ALL_MASK);
        g_pPMUPMRegs->PWR_CONF.WAKEUP_MASK |= ((ENABLE_WAKEUP_SRC << BP_RTC_ALARM_WAKEUP_MASK) |
                                             (ENABLE_WAKEUP_SRC << BP_RTC_TICK_WAKEUP_MASK)  |
                                             (DISABLE_WAKEUP_SRC << BP_TS0_WAKEUP_MASK)       |
                                             (DISABLE_WAKEUP_SRC << BP_TS1_WAKEUP_MASK)    |
                                             (DISABLE_WAKEUP_SRC << BP_KEYIF_WAKEUP_MASK)    |
                                             (DISABLE_WAKEUP_SRC << BP_MMC0_WAKEUP_MASK)     |
                                             (DISABLE_WAKEUP_SRC << BP_MMC1_WAKEUP_MASK)     |
                                             (DISABLE_WAKEUP_SRC << BP_MMC2_WAKEUP_MASK)     |
                                             (DISABLE_WAKEUP_SRC << BP_MMC3_WAKEUP_MASK)     |
                                             (ENABLE_WAKEUP_SRC << BP_I2S_WAKEUP_MASK)       |
                                             (ENABLE_WAKEUP_SRC << BP_ST_WAKEUP_MASK)        |
                                             (DISABLE_WAKEUP_SRC << BP_HDMICEC_WAKEUP_MASK));


    //Power/Reset Button EINT[31]/EINT[14]
        g_pPMUPMRegs->PWR_CONF.EINT_WAKEUP_MASK = BP_EINT_WAKEUP_ALL_MASK;
        g_pPMUPMRegs->PWR_CONF.EINT_WAKEUP_MASK &= ~(1<<WAKEUP_PWR_BUTTON | 1<<WAKEUP_RESET_BUTTON);    // enable Power/Reset Button

        // Clear All Wake Up Status bits
        g_pPMUPMRegs->STATUS_REG.WAKEUP_STAT = (g_pPMUPMRegs->STATUS_REG.WAKEUP_STAT & ~(BP_WAKEUP_STAT_ALL_MASK))|BP_WAKEUP_STAT_ALL_MASK;

#else
        g_pPMUPMRegs->PWR_CONF.WAKEUP_MASK &= ~(BP_WAKEUP_ALL_MASK);
        g_pPMUPMRegs->PWR_CONF.WAKEUP_MASK |= ((ENABLE_WAKEUP_SRC << BP_RTC_ALARM_WAKEUP_MASK) |
                                             (ENABLE_WAKEUP_SRC  << BP_RTC_TICK_WAKEUP_MASK) |
                                             (DISABLE_WAKEUP_SRC << BP_TS0_WAKEUP_MASK)       |
                                             (DISABLE_WAKEUP_SRC << BP_TS1_WAKEUP_MASK)    |
                                             (DISABLE_WAKEUP_SRC << BP_KEYIF_WAKEUP_MASK)    |
                                             (ENABLE_WAKEUP_SRC << BP_MMC0_WAKEUP_MASK)     |
                                             (ENABLE_WAKEUP_SRC << BP_MMC1_WAKEUP_MASK)     |
                                             (ENABLE_WAKEUP_SRC << BP_MMC2_WAKEUP_MASK)     |
                                             (ENABLE_WAKEUP_SRC << BP_MMC3_WAKEUP_MASK)     |
                                             (ENABLE_WAKEUP_SRC  << BP_I2S_WAKEUP_MASK)      |
                                             (ENABLE_WAKEUP_SRC  << BP_ST_WAKEUP_MASK)       |
                                             (DISABLE_WAKEUP_SRC << BP_HDMICEC_WAKEUP_MASK));


        //Power/Reset Button EINT[31]/EINT[14]
        g_pPMUPMRegs->PWR_CONF.EINT_WAKEUP_MASK = BP_EINT_WAKEUP_ALL_MASK;
        g_pPMUPMRegs->PWR_CONF.EINT_WAKEUP_MASK &= ~(1<<WAKEUP_PWR_BUTTON | 1<<WAKEUP_RESET_BUTTON);    // enable Power/Reset Button

        Unmask_EXTINT(g_pGPIORegs, EINT_POWER_BUTTON);
        Unmask_EXTINT(g_pGPIORegs, EINT_RESET_BUTTON);

        // Clear All Wake Up Status bits
        g_pPMUPMRegs->STATUS_REG.WAKEUP_STAT = (g_pPMUPMRegs->STATUS_REG.WAKEUP_STAT & ~(BP_WAKEUP_STAT_ALL_MASK))|BP_WAKEUP_STAT_ALL_MASK;

#endif
}


static void ADC_Configure(void)
{
    // ADC Standby Mode
    g_pADC0Regs->TSADCCON |= (1<<2);        // ADC StanbyMode
    g_pADC1Regs->TSADCCON |= (1<<2);        // ADC StanbyMode
}


