/*
 * cpu_pdn:
 *    true  = CPU dormant
 *    false = CPU standby
 * pwrlevel:
 *    0 = AXI is off
 *    1 = AXI is 26M
 * pwake_time:
 *    >= 0  = specific wakeup period
 */
wake_reason_t spm_go_to_sleep_dpidle(u32 spm_flags, u32 spm_data)
{
    u32 sec = 0;
    u32 dpidle_timer_val = 0;
    u32 dpidle_wake_src = 0;
    int wd_ret;
    struct wake_status wakesta;
    unsigned long flags;
    struct mtk_irq_mask mask;
    struct wd_api *wd_api;
    static wake_reason_t last_wr = WR_NONE;
    struct pcm_desc *pcmdesc = __spm_dpidle.pcmdesc;
    struct pwr_ctrl *pwrctrl = __spm_dpidle.pwrctrl;

	/* backup original dpidle setting */
	dpidle_timer_val = pwrctrl->timer_val;
	dpidle_wake_src = pwrctrl->wake_src;

    set_pwrctrl_pcm_flags(pwrctrl, spm_flags);

#if SPM_PWAKE_EN
    sec = spm_get_wake_period(-1 /* FIXME */, last_wr);
#endif
    pwrctrl->timer_val = sec * 32768;

    pwrctrl->wake_src = spm_get_sleep_wakesrc();

    wd_ret = get_wd_api(&wd_api);
    if (!wd_ret)
        wd_api->wd_suspend_notify();

    spin_lock_irqsave(&__spm_lock, flags);

    mt_irq_mask_all(&mask);
    mt_irq_unmask_for_sleep(SPM_IRQ0_ID);
	
    mt_cirq_clone_gic();
    mt_cirq_enable();

    /* set PMIC WRAP table for deepidle power control */
    mt_cpufreq_set_pmic_phase(PMIC_WRAP_PHASE_DEEPIDLE);

    spm_crit2("sleep_deepidle, sec = %u, wakesrc = 0x%x [%u]\n",
              sec, pwrctrl->wake_src, is_cpu_pdn(pwrctrl->pcm_flags));

    __spm_reset_and_init_pcm(pcmdesc);

    __spm_kick_im_to_fetch(pcmdesc);

    if (request_uart_to_sleep()) {
        last_wr = WR_UART_BUSY;
        goto RESTORE_IRQ;
    }

    __spm_init_pcm_register();

    __spm_init_event_vector(pcmdesc);

    __spm_set_power_control(pwrctrl);

    __spm_set_wakeup_event(pwrctrl);

    __spm_kick_pcm_to_run(pwrctrl);

    spm_dpidle_pre_process();

    spm_trigger_wfi_for_dpidle(pwrctrl);

    spm_dpidle_post_process();    

    __spm_get_wakeup_status(&wakesta);

    __spm_clean_after_wakeup();

    request_uart_to_wakeup();

    last_wr = __spm_output_wake_reason(&wakesta, pcmdesc, true);

RESTORE_IRQ:

    /* set PMIC WRAP table for normal power control */
    mt_cpufreq_set_pmic_phase(PMIC_WRAP_PHASE_NORMAL);

    mt_cirq_flush();
    mt_cirq_disable();

    mt_irq_mask_restore(&mask);

    spin_unlock_irqrestore(&__spm_lock, flags);

    if (!wd_ret)
        wd_api->wd_resume_notify();

	/* restore original dpidle setting */
	pwrctrl->timer_val = dpidle_timer_val;
	pwrctrl->wake_src = dpidle_wake_src;

    return last_wr;  
}
wake_reason_t spm_go_to_dpidle(u32 spm_flags, u32 spm_data)
{
    struct wake_status wakesta;
    unsigned long flags;
    struct mtk_irq_mask mask;
    wake_reason_t wr = WR_NONE;
    struct pcm_desc *pcmdesc = __spm_dpidle.pcmdesc;
    struct pwr_ctrl *pwrctrl = __spm_dpidle.pwrctrl;

#if SPM_AEE_RR_REC
    aee_rr_rec_deepidle_val(1<<SPM_DEEPIDLE_ENTER);
#endif 

    set_pwrctrl_pcm_flags(pwrctrl, spm_flags);
    
    //pwrctrl->timer_val = 1 * 32768;

    spm_dpidle_before_wfi();

    lockdep_off();
    spin_lock_irqsave(&__spm_lock, flags);    
    mt_irq_mask_all(&mask);
    mt_irq_unmask_for_sleep(SPM_IRQ0_ID);
    mt_cirq_clone_gic();
    mt_cirq_enable();

#if SPM_AEE_RR_REC
    aee_rr_rec_deepidle_val(aee_rr_curr_deepidle_val()|(1<<SPM_DEEPIDLE_ENTER_UART_SLEEP));
#endif     

    if (request_uart_to_sleep()) {
        wr = WR_UART_BUSY;
        goto RESTORE_IRQ;
    }

    __spm_reset_and_init_pcm(pcmdesc);

    __spm_kick_im_to_fetch(pcmdesc);
	
    __spm_init_pcm_register();

    __spm_init_event_vector(pcmdesc);

    __spm_set_power_control(pwrctrl);

    __spm_set_wakeup_event(pwrctrl);

    spm_dpidle_pre_process();

    __spm_kick_pcm_to_run(pwrctrl);

#if SPM_AEE_RR_REC
    aee_rr_rec_deepidle_val(aee_rr_curr_deepidle_val()|(1<<SPM_DEEPIDLE_ENTER_WFI));
#endif

#ifdef SPM_DEEPIDLE_PROFILE_TIME
    gpt_get_cnt(SPM_PROFILE_APXGPT,&dpidle_profile[1]);
#endif   
    spm_trigger_wfi_for_dpidle(pwrctrl);
#ifdef SPM_DEEPIDLE_PROFILE_TIME
    gpt_get_cnt(SPM_PROFILE_APXGPT,&dpidle_profile[2]);
#endif

#if SPM_AEE_RR_REC
    aee_rr_rec_deepidle_val(aee_rr_curr_deepidle_val()|(1<<SPM_DEEPIDLE_LEAVE_WFI));
#endif 

    spm_dpidle_post_process();

    __spm_get_wakeup_status(&wakesta);

    __spm_clean_after_wakeup();

#if SPM_AEE_RR_REC
    aee_rr_rec_deepidle_val(aee_rr_curr_deepidle_val()|(1<<SPM_DEEPIDLE_ENTER_UART_AWAKE));
#endif

    request_uart_to_wakeup();

    wr = __spm_output_wake_reason(&wakesta, pcmdesc, false);

RESTORE_IRQ:
    mt_cirq_flush();
    mt_cirq_disable();
    mt_irq_mask_restore(&mask);  
    spin_unlock_irqrestore(&__spm_lock, flags);
    lockdep_on();
    spm_dpidle_after_wfi();

#if SPM_AEE_RR_REC
    aee_rr_rec_deepidle_val(0);
#endif 
    return wr;
}
wake_reason_t spm_go_to_sleep(u32 spm_flags, u32 spm_data)
{
    u32 sec = 0;
    int wd_ret;
    struct wake_status wakesta;
    unsigned long flags;
    struct mtk_irq_mask mask;
    struct wd_api *wd_api;
    static wake_reason_t last_wr = WR_NONE;
    struct pcm_desc *pcmdesc = __spm_suspend.pcmdesc;
    struct pwr_ctrl *pwrctrl = __spm_suspend.pwrctrl;
    struct spm_lp_scen *lpscen;

    lpscen = spm_check_talking_get_lpscen(&__spm_suspend, &spm_flags);
    pcmdesc = lpscen->pcmdesc;
    pwrctrl = lpscen->pwrctrl;

    set_pwrctrl_pcm_flags(pwrctrl, spm_flags);
    set_pwrctrl_pcm_data(pwrctrl, spm_data);

#if SPM_PWAKE_EN
    sec = spm_get_wake_period(-1 /* FIXME */, last_wr);
#endif
    pwrctrl->timer_val = sec * 32768;

    wd_ret = get_wd_api(&wd_api);
    if (!wd_ret)
        wd_api->wd_suspend_notify();

    spm_suspend_pre_process(pwrctrl);

    spin_lock_irqsave(&__spm_lock, flags);
    mt_irq_mask_all(&mask);
    mt_irq_unmask_for_sleep(MT_SPM_IRQ_ID);
    mt_cirq_clone_gic();
    mt_cirq_enable();

    spm_set_sysclk_settle();

    spm_crit2("sec = %u, wakesrc = 0x%x (%u)(%u)\n",
              sec, pwrctrl->wake_src, is_cpu_pdn(pwrctrl->pcm_flags), is_infra_pdn(pwrctrl->pcm_flags));

    if (request_uart_to_sleep()) {
        last_wr = WR_UART_BUSY;
        goto RESTORE_IRQ;
    }

    __spm_reset_and_init_pcm(pcmdesc);

    __spm_kick_im_to_fetch(pcmdesc);

    __spm_init_pcm_register();

    __spm_init_event_vector(pcmdesc);

    __spm_set_power_control(pwrctrl);

    __spm_set_wakeup_event(pwrctrl);

    spm_kick_pcm_to_run(pwrctrl);

#if 0
    if (1 == spm_snapshot_golden_setting)
    {
        snapshot_golden_setting(__FUNCTION__, __LINE__);
        spm_snapshot_golden_setting = 2;
    }
#endif

    spm_trigger_wfi_for_sleep(pwrctrl);

    __spm_get_wakeup_status(&wakesta);

    spm_clean_after_wakeup();

    request_uart_to_wakeup();

    last_wr = spm_output_wake_reason(&wakesta, pcmdesc);

RESTORE_IRQ:
    mt_cirq_flush();
    mt_cirq_disable();
    mt_irq_mask_restore(&mask);
    spin_unlock_irqrestore(&__spm_lock, flags);

    spm_suspend_post_process(pwrctrl);

    if (!wd_ret)
        wd_api->wd_resume_notify();

    return last_wr;
}
void spm_go_to_sodi(u32 spm_flags, u32 spm_data)
{
    struct wake_status wakesta;
    unsigned long flags;
    struct mtk_irq_mask mask;
    wake_reason_t wr = WR_NONE;
    struct pcm_desc *pcmdesc = __spm_sodi.pcmdesc;
    struct pwr_ctrl *pwrctrl = __spm_sodi.pwrctrl;
    int vcore_status = 0; //0:disable, 1:HPM, 2:LPM

#if SPM_AEE_RR_REC
    aee_rr_rec_sodi_val(1<<SPM_SODI_ENTER);
#endif

#if defined (CONFIG_ARM_PSCI)||defined(CONFIG_MTK_PSCI)
    spm_flags &= ~SPM_DISABLE_ATF_ABORT;
#else
    spm_flags |= SPM_DISABLE_ATF_ABORT;
#endif

	if(gSpm_SODI_mempll_pwr_mode == 1)
	{
		spm_flags |= SPM_MEMPLL_CG_EN; //MEMPLL CG mode
	}
	else
	{
		spm_flags &= ~SPM_MEMPLL_CG_EN; //DDRPHY power down mode
	}

    set_pwrctrl_pcm_flags(pwrctrl, spm_flags);

    //If Vcore DVFS is disable, force to disable SODI internal Vcore DVS
    if (pwrctrl->pcm_flags_cust == 0)
    {
        if ((pwrctrl->pcm_flags & SPM_VCORE_DVFS_EN) == 0) 
        {
            pwrctrl->pcm_flags |= SPM_VCORE_DVS_EVENT_DIS;
        }
    }

    //SODI will not decrease Vcore voltage in HPM mode.
    if ((pwrctrl->pcm_flags & SPM_VCORE_DVS_EVENT_DIS) == 0)
    {
        if (get_ddr_khz() != FDDR_S1_KHZ)
        {
		#if SPM_AEE_RR_REC
		aee_rr_rec_sodi_val(aee_rr_curr_sodi_val()|(1<<SPM_SODI_VCORE_HPM));
		#endif 
/*            	//modify by mtk
		//if DRAM freq is high,SPM will not enter event_vector to enter EMI self-refresh
		if(pwrctrl->pcm_flags_cust == 0)
		{
			pwrctrl->pcm_flags|=0x80;		
		}
*/
            	vcore_status = 1;
            	//printk("SODI: get_ddr_khz() = %d\n", get_ddr_khz());
        }
        else
        {
			#if SPM_AEE_RR_REC
            aee_rr_rec_sodi_val(aee_rr_curr_sodi_val()|(1<<SPM_SODI_VCORE_LPM));
			#endif            
            
            vcore_status = 2;
        }
    }

    //enable APxGPT timer
	soidle_before_wfi(0);

	lockdep_off();
    spin_lock_irqsave(&__spm_lock, flags);

    mt_irq_mask_all(&mask);
    mt_irq_unmask_for_sleep(SPM_IRQ0_ID);
    mt_cirq_clone_gic();
    mt_cirq_enable();

#if SPM_AEE_RR_REC
    aee_rr_rec_sodi_val(aee_rr_curr_sodi_val()|(1<<SPM_SODI_ENTER_UART_SLEEP));
#endif     

    if (request_uart_to_sleep()) {
        wr = WR_UART_BUSY;
        goto RESTORE_IRQ;
    }
    
#if SPM_AEE_RR_REC
    aee_rr_rec_sodi_val(aee_rr_curr_sodi_val()|(1<<SPM_SODI_ENTER_SPM_FLOW));
#endif

    __spm_reset_and_init_pcm(pcmdesc);

	/*
	 * When commond-queue is in shut-down mode, SPM will hang if it tries to access commond-queue status.
     * Follwoing patch is to let SODI driver to notify SPM that commond-queue is in shut-down mode or not to avoid above SPM hang issue.
     * But, now display can automatically notify SPM that command-queue is shut-down or not, so following code is not needed anymore.
	 */
	#if 0
    //check GCE
	if(clock_is_on(MT_CG_INFRA_GCE))
	{
		pwrctrl->pcm_flags &= ~SPM_DDR_HIGH_SPEED;
	}
	else
	{
		pwrctrl->pcm_flags |= SPM_DDR_HIGH_SPEED;
	}
	#endif

    __spm_kick_im_to_fetch(pcmdesc);

    __spm_init_pcm_register();

    __spm_init_event_vector(pcmdesc);

#if 0 //In D2, can not set apsrc_req bit in SODI. It is used by Vcore DVS for GPU 550M in HPM mode
    //Display set SPM_PCM_SRC_REQ[0]=1'b1 to force DRAM not enter self-refresh mode
   	if((spm_read(SPM_PCM_SRC_REQ)&0x00000001))
    {
    	pwrctrl->pcm_apsrc_req = 1;
    }
    else
    {
    	pwrctrl->pcm_apsrc_req = 0;
    }
#endif

    __spm_set_power_control(pwrctrl);

    __spm_set_wakeup_event(pwrctrl);

#if SODI_DVT_PCM_TIMER_DISABLE
	//PCM_Timer is enable in above '__spm_set_wakeup_event(pwrctrl);', disable PCM Timer here
	spm_write(SPM_PCM_CON1 ,spm_read(SPM_PCM_CON1)&(~CON1_PCM_TIMER_EN));
#endif

    spm_sodi_pre_process();

    __spm_kick_pcm_to_run(pwrctrl);

#if SPM_SODI_DUMP_REGS
    printk("============SODI Before============\n");
    spm_sodi_dump_regs(); //dump debug info
#endif

#if SPM_AEE_RR_REC
    aee_rr_rec_sodi_val(aee_rr_curr_sodi_val()|(1<<SPM_SODI_ENTER_WFI));
#endif

#ifdef SPM_SODI_PROFILE_TIME
    gpt_get_cnt(SPM_SODI_PROFILE_APXGPT,&soidle_profile[1]);
#endif

    spm_trigger_wfi_for_sodi(pwrctrl);

#ifdef SPM_SODI_PROFILE_TIME
    gpt_get_cnt(SPM_SODI_PROFILE_APXGPT,&soidle_profile[2]);
#endif

#if SPM_AEE_RR_REC
    aee_rr_rec_sodi_val(aee_rr_curr_sodi_val()|(1<<SPM_SODI_LEAVE_WFI));
#endif

#if SPM_SODI_DUMP_REGS
    printk("============SODI After=============\n");
    spm_sodi_dump_regs();//dump debug info
#endif

    spm_sodi_post_process();

    __spm_get_wakeup_status(&wakesta);

    sodi_debug("emi-selfrefrsh cnt = %d, pcm_flag = 0x%x, SPM_PCM_RESERVE2 = 0x%x, vcore_status = %d, %s\n", 
                spm_read(SPM_PCM_PASR_DPD_3), spm_read(SPM_PCM_FLAGS), spm_read(SPM_PCM_RESERVE2), vcore_status, pcmdesc->version);

    __spm_clean_after_wakeup();

#if SPM_AEE_RR_REC
    aee_rr_rec_sodi_val(aee_rr_curr_sodi_val()|(1<<SPM_SODI_ENTER_UART_AWAKE));
#endif

    request_uart_to_wakeup();

    wr = __spm_output_wake_reason(&wakesta, pcmdesc, false);
    if (wr == WR_PCM_ASSERT)
    {
        sodi_err("PCM ASSERT AT %u (%s), r13 = 0x%x, debug_flag = 0x%x\n", wakesta.assert_pc, pcmdesc->version, wakesta.r13, wakesta.debug_flag);
    }

#if SPM_AEE_RR_REC
    aee_rr_rec_sodi_val(aee_rr_curr_sodi_val()|(1<<SPM_SODI_LEAVE_SPM_FLOW));
#endif

RESTORE_IRQ:
    mt_cirq_flush();
    mt_cirq_disable();
    mt_irq_mask_restore(&mask);

    spin_unlock_irqrestore(&__spm_lock, flags);
    lockdep_on();

    //stop APxGPT timer and enable caore0 local timer
    soidle_after_wfi(0);

#if SODI_DVT_SPM_MEM_RW_TEST
    {
        static u32 magic_init = 0;
        int i =0;

        if(magic_init == 0){
		    magic_init++;
		    printk("magicNumArray:0x%p",magicArray);
	    }

    	for(i=0;i<16;i++)
    	{
    		if(magicArray[i]!=SODI_DVT_MAGIC_NUM)
    		{
    			printk("Error: sodi magic number no match!!!");
    			ASSERT(0);
    		}
    	}

    	if (i>=16)
    	    printk("SODI_DVT_SPM_MEM_RW_TEST pass (count = %d)\n", magic_init);
    }
#endif

#if SPM_AEE_RR_REC
    aee_rr_rec_sodi_val(0);
#endif
}
wake_reason_t spm_go_to_dpidle(u32 spm_flags, u32 spm_data)
{
    struct wake_status wakesta;
    unsigned long flags;
    struct mtk_irq_mask mask;
    wake_reason_t wr = WR_NONE;
    struct pcm_desc *pcmdesc = __spm_dpidle.pcmdesc;
    struct pwr_ctrl *pwrctrl = __spm_dpidle.pwrctrl;
    struct spm_lp_scen *lpscen;

    lpscen = spm_check_talking_get_lpscen(&__spm_dpidle, &spm_flags);
    pcmdesc = lpscen->pcmdesc;
    pwrctrl = lpscen->pwrctrl;

    set_pwrctrl_pcm_flags(pwrctrl, spm_flags);
	
    /* set PMIC WRAP table for deepidle power control */
    mt_cpufreq_set_pmic_phase(PMIC_WRAP_PHASE_DEEPIDLE);

    spin_lock_irqsave(&__spm_lock, flags);

    mt_irq_mask_all(&mask);
    mt_irq_unmask_for_sleep(MT_SPM_IRQ_ID);
    mt_cirq_clone_gic();
    mt_cirq_enable();

    if (request_uart_to_sleep()) {
        wr = WR_UART_BUSY;
        goto RESTORE_IRQ;
    }
	
    __spm_reset_and_init_pcm(pcmdesc);

    __spm_kick_im_to_fetch(pcmdesc);
	
    __spm_init_pcm_register();

    __spm_init_event_vector(pcmdesc);

    __spm_set_power_control(pwrctrl);

    __spm_set_wakeup_event(pwrctrl);

    __spm_kick_pcm_to_run(pwrctrl);

    spm_dpidle_before_wfi();

    spm_trigger_wfi_for_dpidle(pwrctrl);

    spm_dpidle_after_wfi();

    __spm_get_wakeup_status(&wakesta);

    __spm_clean_after_wakeup();

    request_uart_to_wakeup();

    wr = __spm_output_wake_reason(&wakesta, pcmdesc, false);

RESTORE_IRQ:
    mt_cirq_flush();
    mt_cirq_disable();
    mt_irq_mask_restore(&mask);

    spin_unlock_irqrestore(&__spm_lock, flags);
    /* set PMIC WRAP table for normal power control */
    mt_cpufreq_set_pmic_phase(PMIC_WRAP_PHASE_NORMAL);

    return wr;
}