Exemple #1
0
/*
 * cpu_pdn:
 *    true  = CPU dormant
 *    false = CPU standby
 * pwrlevel:
 *    0 = AXI is off
 *    1 = AXI is 26M
 */
wake_reason_t spm_go_to_dpidle(bool cpu_pdn, u16 pwrlevel)
{
    wake_status_t wakesta;
    unsigned long flags;
    struct mtk_irq_mask mask;
    wake_reason_t wr = WR_NONE;
    const pcm_desc_t *pcmdesc = &pcm_dpidle;
    const bool pcmwdt_en = false;

    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_reset_and_init_pcm();

    spm_kick_im_to_fetch(pcmdesc);

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

    spm_init_pcm_register();

    spm_init_event_vector(pcmdesc);

    spm_set_pwrctl_for_dpidle(pwrlevel);

    spm_set_wakeup_event(0, WAKE_SRC_FOR_DPIDLE);

    spm_kick_pcm_to_run(cpu_pdn, false, pcmwdt_en);     /* keep INFRA/DDRPHY power */

    spm_dpidle_before_wfi();

    spm_trigger_wfi_for_dpidle(cpu_pdn);

    spm_dpidle_after_wfi();

    spm_get_wakeup_status(&wakesta);

    spm_clean_after_wakeup(pcmwdt_en);

    wr = spm_output_wake_reason(&wakesta, true);

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

    return wr;
}
static void rgidle_after_wfi(int cpu)
{
#ifdef CCI400_CLOCK_SWITCH
    struct mtk_irq_mask mask;
#ifdef CONFIG_SMP
    if ((atomic_read(&is_in_hotplug) == 0)&&(atomic_read(&hotplug_cpu_count) == 1))
#endif
    {
        mt_irq_mask_all(&mask);
        mt_cirq_clone_gic();
        mt_cirq_enable();
        cci400_clk_restore();
        mt_cirq_flush();
        mt_cirq_disable();
        mt_irq_mask_restore(&mask);
    }
#endif
    rgidle_cnt[cpu]++;
}
static void slidle_before_wfi(int cpu)
{
    //struct mtk_irq_mask mask;
    bus_dcm_enable();
#ifdef CCI400_CLOCK_SWITCH
#ifdef CONFIG_SMP
    if ((atomic_read(&is_in_hotplug) == 0)&&(atomic_read(&hotplug_cpu_count) == 1))
#endif
    {
        mt_irq_mask_all(&mask);
        mt_cirq_clone_gic();
        mt_cirq_enable();

        cci400_clk_26MHz();

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

    }
#endif
}
Exemple #4
0
wake_reason_t spm_go_to_sleep(void)
{
    wake_status_t *wakesta;
    unsigned long flags,i;
    struct mtk_irq_mask mask;
    static wake_reason_t last_wr = WR_NONE;

    if(pcm_config_suspend.reserved & SPM_SUSPEND_GET_FGUAGE)
        pcm_config_suspend.timer_val_ms = spm_get_wake_period(last_wr)*1000;
    
    mtk_wdt_suspend();

    spin_lock_irqsave(&spm_lock, flags);
    mt_irq_mask_all(&mask);
    mt_irq_unmask_for_sleep(MT_SPM0_IRQ_ID);

    if (spm_init_pcm(&pcm_config_suspend)==false)
        goto RESTORE_IRQ;

    spm_kick_pcm(&pcm_config_suspend);

    snapshot_golden_setting(__FUNCTION__, __LINE__);
    spm_trigger_wfi(&pcm_config_suspend);
    
    wakesta = spm_get_wakeup_status(&pcm_config_suspend);
    
    last_wr = wakesta->wake_reason;

    spm_clean_after_wakeup();
    
 RESTORE_IRQ:
    mt_irq_mask_restore(&mask);
    spin_unlock_irqrestore(&spm_lock, flags);
   
    mtk_wdt_resume();
       
    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
}
Exemple #6
0
void APDMA_test_transfer(int testcase)
{
    int i, channel;
    int start_dma;
    unsigned int cnt = 20;
    channel = mt_req_gdma(GDMA_ANY);

    printk("GDMA channel:%d\n",channel);
    if(channel < 0 ){
        printk("ERROR Register DMA\n");
        return;
    }

    mt_reset_gdma_conf(channel);
    
    dma_dst_array_v = dma_alloc_coherent(NULL, TEST_LEN, &dma_dst_array_p, GFP_KERNEL ); // 25 unsinged int
    dma_src_array_v = dma_alloc_coherent(NULL, TEST_LEN, &dma_src_array_p, GFP_KERNEL );
    struct mt_gdma_conf dma_conf = {
        .count = TEST_LEN,
        .src = dma_src_array_p,
        .dst = dma_dst_array_p,
        .iten = (testcase == 2) ? DMA_FALSE : DMA_TRUE,
        .isr_cb = (testcase == 2) ? NULL : irq_dma_handler,
        .data = channel,
        .burst = DMA_CON_BURST_SINGLE,
        .dfix = DMA_FALSE,
        .sfix = DMA_FALSE,
        //.cohen = DMA_TRUE, //enable coherence bus
        .sec = DMA_FALSE, // non-security channel
        .domain = 0, 
        .limiter = (testcase == 3 || testcase == 4) ? 0x3FF : 0,
    };

    for(i = 0; i < LEN; i++) {
        dma_dst_array_v[i] = 0;
        dma_src_array_v[i] = i;
    }
    
    if ( mt_config_gdma(channel, &dma_conf, ALL) != 0) {
        printk("ERROR set DMA\n");
        goto _exit;
        return;
    }
    
    /*
    unsigned int dma_src = readl(DMA_SRC(DMA_BASE_CH(channel)));
    unsigned int dma_dst = readl(DMA_DST);
    unsigned int len = readl(DMA_LEN1);
    printk("start dma channel %d src = 0x%x, dst = 0x%x, len = %d bytes\n", channel, dma_src, dma_dst, len);
    */
    start_dma=mt_start_gdma(channel);
#ifdef CONFIG_OF
    mt_irq_dump_status(cqdma_irq_num);
#else
    mt_irq_dump_status(CQ_DMA_IRQ_BIT_ID);
#endif
    printk("Start %d\n",start_dma);
    switch(testcase)
    {
        case 1:
               while(!DMA_INT_DONE);
#ifdef CONFIG_OF
               mt_irq_dump_status(cqdma_irq_num);
#else
               mt_irq_dump_status(CQ_DMA_IRQ_BIT_ID);
#endif
               DMA_INT_DONE=0;
               break;
        case 2:
            if (mt_polling_gdma(channel, GDMA_WARM_RST_TIMEOUT) != 0)
                printk("Polling transfer failed\n");    
            else
                printk("Polling succeeded\n");
            mt_free_gdma(channel);
            break;
        case 3:
            mt_warm_reset_gdma(channel);
            
            for(i = 0; i < LEN; i++) {
                if(dma_dst_array_v[i] != dma_src_array_v[i]) {
                    printk("Warm reset succeeded\n");
                    break;
                }
                mt_free_gdma(channel);
            }

            if(i == LEN)
                printk("Warm reset failed\n");
            break;
            
        case 4:
            mt_hard_reset_gdma(channel);
            
            for(i = 0; i < LEN; i++) {
                if(dma_dst_array_v[i] != dma_src_array_v[i]) {
                    printk("Hard reset succeeded\n");
                    break;
                }
                mt_free_gdma(channel);
            }
            if(i == LEN)
                printk("Hard reset failed\n");
            break;

        case 5:
               do {
	       	    mdelay(50);
               	    printk("GIC mask test cnt = %d\n", cnt);
		    if (cnt-- == 0) {
			mt_irq_unmask(irq_get_irq_data(cqdma_irq_num));
               		printk("GIC Mask PASS !!!\n");
			break;
		    }
	       }while(!DMA_INT_DONE);
#ifdef CONFIG_OF
               mt_irq_dump_status(cqdma_irq_num);
#else
               mt_irq_dump_status(CQ_DMA_IRQ_BIT_ID);
#endif
               DMA_INT_DONE=0;
               break;

        case 6:
               do {
	       	    mdelay(50);
               	    printk("GIC ack test cnt = %d\n", cnt);
		    if (cnt-- == 0) {
               		mt_irq_mask_restore(&mask);
               		printk("GIC Mask All PASS !!!\n");
			break;
		    }
	       }while(!DMA_INT_DONE);
#ifdef CONFIG_OF
               mt_irq_dump_status(cqdma_irq_num);
#else
               mt_irq_dump_status(CQ_DMA_IRQ_BIT_ID);
#endif
               DMA_INT_DONE=0;
               break;
        
        default:
            break;

    }

_exit:
    if(dma_dst_array_v){
        dma_free_coherent(NULL, TEST_LEN, dma_dst_array_v, dma_dst_array_p);
        dma_dst_array_v = dma_dst_array_p = NULL;
    }

    if(dma_src_array_v){
        dma_free_coherent(NULL, TEST_LEN, dma_src_array_v, dma_src_array_p);
        dma_src_array_v = dma_src_array_p = NULL;
    }

    return;
}

static ssize_t cqdma_dvt_show(struct device_driver *driver, char *buf)
{
   return snprintf(buf, PAGE_SIZE, "==CQDMA test==\n"
                                   "1.CQDMA transfer (interrupt mode)\n"
                                   "2.CQDMA transfer (polling mode)\n"
                                   "3.CQDMA warm reset\n"
                                   "4.CQDMA hard reset\n"
                                   "5.Mask CQDMA interrupt test\n"
                                   "6.Mask all CQDMA interrupt test\n"
   ); 
}

static ssize_t cqdma_dvt_store(struct device_driver *driver, const char *buf, size_t count)
{
	char *p = (char *)buf;
	unsigned int num;

	num = simple_strtoul(p, &p, 10);
        switch(num){
            /* Test APDMA Normal Function */
            case 1:
                APDMA_test_transfer(1);
                break;
            case 2:
                APDMA_test_transfer(2);
                break;
            case 3:
                APDMA_test_transfer(3);
                break;
            case 4:
                APDMA_test_transfer(4);
                break;
            case 5:
                mt_irq_mask(irq_get_irq_data(cqdma_irq_num));
                APDMA_test_transfer(5);
                break;
            case 6:
                mt_irq_mask_all(&mask);
                APDMA_test_transfer(6);
                break;
            default:
                break;
        }

	return count;
}
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;
}
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;
}
/*
    status
    RESERVED[0] :  mcusys dormant 
    RESERVED[1] :  APSRCSLEEP - (pass CHECK_APSRCWAKE)
    RESERVED[2] :  EMI self-refresh & mem ck off
     
    Options
    RESERVED[16] :  skip arm pll cg/pd 
    RESERVED[17] :  skip arm pll pd 
    RESERVED[18] :  skip AXI 26 SWITCH 
    RESERVED[19] :  skip EMI self-refresh & mem ck off                         
    RESERVED[20] :  skip mem ck off
                                                                    
    RESERVED[24] :  stop after cpu power down / cpu pll off
    RESERVED[25] :  stop after emi  down / mem ck off

*/
wake_reason_t spm_go_to_sodi(bool sodi_en,int cpu)
{
    wake_status_t *wakesta;
    unsigned long flags;
    struct mtk_irq_mask mask;
    wake_reason_t wr = WR_NONE;
    u32 con0;

    spin_lock_irqsave(&spm_lock, flags);
    spm_stop_normal();    

    mt_irq_mask_all(&mask);
    mt_irq_unmask_for_sleep(MT_SPM0_IRQ_ID);    
    mt_cirq_clone_gic();
    mt_cirq_enable();
    spm_direct_enable_sodi();

    spm_write(SPM_PCM_RESERVE,pcm_config_sodi.pcm_reserved);

#ifdef SPM_SODI_SECONDARY_KICK_IMPROVE
    if( spm_last_senario != pcm_config_sodi.scenario)//MCDI SPM Initialize
#endif
    {
        //spm_direct_enable_sodi();
        if (spm_init_pcm(&pcm_config_sodi)==false)
            goto  RESTORE_IRQ;
        
        spm_write(SPM_SLEEP_CPU_IRQ_MASK,0x1); 

        spm_write(SPM_PCM_CON1,(spm_read(SPM_PCM_CON1)| CON1_CFG_KEY )&0xffffffdf);//temporary disable pcm timer

        spm_wfi_sel(pcm_config_sodi.wfi_sel, SPM_CORE0_WFI_SEL_SW_MASK );
        spm_wfi_sel(pcm_config_sodi.wfi_sel, SPM_CORE1_WFI_SEL_SW_MASK ); 

        spm_kick_pcm(&pcm_config_sodi);

        spm_change_fw = 1;
    }
#ifdef SPM_SODI_SECONDARY_KICK_IMPROVE
    else
    {
        spm_write(SPM_SLEEP_CPU_IRQ_MASK,0x1);
        spm_wfi_sel(pcm_config_sodi.wfi_sel, SPM_CORE0_WFI_SEL_SW_MASK );
        spm_wfi_sel(pcm_config_sodi.wfi_sel, SPM_CORE1_WFI_SEL_SW_MASK ); 
        if( spm_secondary_kick(&pcm_config_sodi)==false)
            goto RESTORE_IRQ;

        spm_change_fw = 0;
    }
#endif    

    //gpiodbg_infra_dbg_out();//measure bus clock to LPD15
    soidle_before_wfi();

    spm_trigger_wfi_for_dpidle(1);//SODI with cpu dormant

    soidle_after_wfi(sodi_en);

    wakesta = spm_get_wakeup_status(&pcm_config_sodi);

    wr = wakesta->wake_reason;

    spm_clean_after_wakeup();
      
    spm_write(SPM_PCM_CON1,(spm_read(SPM_PCM_CON1)| CON1_CFG_KEY )|(CON1_PCM_TIMER_EN)); 

RESTORE_IRQ:
    mt_cirq_flush();
    mt_cirq_disable();
    mt_irq_mask_restore(&mask);
    spm_go_to_normal();
    spin_unlock_irqrestore(&spm_lock, flags);
    return wr;

}
wake_reason_t spm_go_to_mcdi_ipi_test(int cpu)
{
    wake_status_t *wakesta;
    unsigned long flags;
    struct mtk_irq_mask mask;
    wake_reason_t wr = WR_NONE;
    u32 con0;
    bool sodi_en = 0;

    spin_lock_irqsave(&spm_lock, flags);
    //mt_irq_mask_all(&mask);
    //mt_irq_unmask_for_sleep(MT_SPM0_IRQ_ID);       
    
    if(cpu==0)//except mcdi
    {
        mt_irq_mask_all(&mask);
        mt_irq_unmask_for_sleep(MT_SPM0_IRQ_ID);
        
        //spm_wdt_config(false);
        
        mt_cirq_clone_gic();
        mt_cirq_enable();
    }

    pcm_config_sodi.spm_request_uart_sleep = true;
    pcm_config_sodi.wake_src=WAKE_SRC_FOR_SODI;


    if((sodi_en==1)&&(cpu==0))//SODI
    {
        spm_direct_enable_sodi();
        pcm_config_sodi.wfi_sel[0]=true;
        pcm_config_sodi.wfi_sel[1]=true;        
        pcm_config_sodi.wfi_scu_mask=false;
        pcm_config_sodi.wfi_l2c_mask=false;

        
        spm_write(SPM_PCM_RESERVE,spm_read(SPM_PCM_RESERVE)&~0x1);
    }
    else if((sodi_en==0)&&(cpu==0))//CPU Dormant
    {
        pcm_config_sodi.wfi_sel[0]=true;
        pcm_config_sodi.wfi_sel[1]=true;    
        pcm_config_sodi.wfi_scu_mask=false;
        pcm_config_sodi.wfi_l2c_mask=false;        
        spm_direct_disable_sodi();
        spm_write(SPM_PCM_RESERVE,spm_read(SPM_PCM_RESERVE)&~0x1);
    }
    else if((sodi_en==0)&&(cpu!=0))//MCDI
    {
        //pcm_config_sodi.wake_src=WAKE_SRC_FOR_MCDI;
        pcm_config_sodi.wake_src=0x0;//for test IPI only
        pcm_config_sodi.wfi_sel[0]=false;
        pcm_config_sodi.wfi_sel[1]=true;
        pcm_config_sodi.wfi_scu_mask=true;
        pcm_config_sodi.wfi_l2c_mask=true;        
        pcm_config_sodi.spm_request_uart_sleep = false;
        spm_direct_disable_sodi();
        spm_write(SPM_PCM_RESERVE,spm_read(SPM_PCM_RESERVE)|0x1);
    }
    else
    {
        printk("[SPM]Wrong para!!\n");
        goto RESTORE_IRQ;
    }

    if( spm_last_senario != pcm_config_sodi.scenario)//MCDI SPM Initialize
    {
        //spm_direct_enable_sodi();
        if (spm_init_pcm(&pcm_config_sodi)==false)
            goto  RESTORE_IRQ;

        if(cpu==1)//except mcdi
        {
            spm_write(SPM_SLEEP_CPU_IRQ_MASK,0x2); 

        spm_mcdi_init_core_mux();
        }
        else
            spm_write(SPM_SLEEP_CPU_IRQ_MASK,0x1);


        spm_write(SPM_PCM_CON1,(spm_read(SPM_PCM_CON1)| CON1_CFG_KEY )&0xffffffdf);//temporary disable pcm timer
        
        spm_wfi_sel(pcm_config_sodi.wfi_sel, SPM_CORE0_WFI_SEL_SW_MASK );
        spm_wfi_sel(pcm_config_sodi.wfi_sel, SPM_CORE1_WFI_SEL_SW_MASK ); 


        spm_kick_pcm(&pcm_config_sodi);

        //spm_wfi_sel(pcm_config_sodi.wfi_sel, SPM_CORE0_WFI_SEL_SW_MASK );
        //spm_wfi_sel(pcm_config_sodi.wfi_sel, SPM_CORE1_WFI_SEL_SW_MASK );
        spm_change_fw = 1;
    }
    else
    {
        if(cpu==1)//except mcdi
        {
            spm_write(SPM_SLEEP_CPU_IRQ_MASK,0x2); 

            //spm_mcdi_init_core_mux();
        }
        else
            spm_write(SPM_SLEEP_CPU_IRQ_MASK,0x1);

        spm_wfi_sel(pcm_config_sodi.wfi_sel, SPM_CORE0_WFI_SEL_SW_MASK );
        spm_wfi_sel(pcm_config_sodi.wfi_sel, SPM_CORE1_WFI_SEL_SW_MASK );     
        if( spm_secondary_kick(&pcm_config_sodi)==false)
            goto RESTORE_IRQ;

        spm_change_fw = 0;
    }
   

    if(cpu==0)
    {
        soidle_before_wfi();

        spm_trigger_wfi_for_dpidle(1);//SODI with cpu dormant

        soidle_after_wfi(sodi_en);
    }
    else
    {
    spm_pre_SPM_PCM_SW_INT_SET=spm_read(SPM_PCM_SW_INT_SET);

    spm_pre_SPM_SLEEP_ISR_STATUS=spm_read(SPM_SLEEP_ISR_STATUS);
    spm_write(SPM_PCM_SW_INT_CLEAR,0xf);

        //mcidle_before_wfi(cpu);
        if (!cpu_power_down(DORMANT_MODE)) 
        {
            switch_to_amp();  
            
            /* do not add code here */
            wfi_with_sync();
        }
        switch_to_smp(); 
        cpu_check_dormant_abort();
        //mcidle_after_wfi(cpu);

    spm_post_SPM_PCM_SW_INT_SET=spm_read(SPM_PCM_SW_INT_SET);

    spm_post_SPM_SLEEP_ISR_STATUS=spm_read(SPM_SLEEP_ISR_STATUS);  
    spm_post_SPM_PCM_RESERVE = spm_read(SPM_PCM_RESERVE );
    } 

#if 1
    wakesta = spm_get_wakeup_status(&pcm_config_sodi);

    wr = wakesta->wake_reason;
#endif
    spm_clean_after_wakeup();

    spm_write(SPM_PCM_CON1,(spm_read(SPM_PCM_CON1)| CON1_CFG_KEY )|(CON1_PCM_TIMER_EN)); 

RESTORE_IRQ:
    if(cpu==0)//except mcdi
    {
        mt_cirq_flush();
        mt_cirq_disable();
        mt_irq_mask_restore(&mask);
    }

    //mt_irq_mask_restore(&mask);
    spin_unlock_irqrestore(&spm_lock, flags);
    return wr;

}
wake_reason_t spm_go_to_dpidle(bool cpu_pdn, u8 pwrlevel)
{
    wake_status_t *wakesta;
    unsigned long flags;
    struct mtk_irq_mask mask;
    wake_reason_t wr = WR_NONE;
    u32 con0;
    u32 top_ckgen_val = 0;

    spin_lock_irqsave(&spm_lock, flags);
    spm_stop_normal();
    mt_irq_mask_all(&mask);
    mt_irq_unmask_for_sleep(MT_SPM0_IRQ_ID);
    mt_cirq_clone_gic();
    mt_cirq_enable();

#ifdef SPM_DPIDLE_SECONDARY_KICK_IMPROVE
    if( spm_last_senario != pcm_config_dpidle.scenario)//dpidle SPM Initialize
#endif    
    {   
        if(pwrlevel>2) 
        {
            spm_crit2("Hey!! wrong PWR Level: %x",pwrlevel);//ASSERT Wrong Para!!
            goto RESTORE_IRQ;
        }
        pcm_config_dpidle.pcm_pwrlevel = 1 << pwrlevel ;
        pcm_config_dpidle.spm_request_uart_sleep = (pwrlevel == 0 ? true : false);
        pcm_config_dpidle.cpu_pdn = cpu_pdn;

        if (spm_init_pcm(&pcm_config_dpidle)==false)
            goto  RESTORE_IRQ;

#ifdef SPM_CLOCK_INIT
        enable_clock(MT_CG_MEMSLP_DLYER_SW_CG, "SPM_DPIDLE");
        enable_clock(MT_CG_SPM_SW_CG, "SPM_DPIDLE");
#endif

#ifdef SPM_DPIDLE_CLK_DBG_OUT
        //gpiodbg_monitor();
        gpiodbg_emi_dbg_out();
        gpiodbg_armcore_dbg_out();
#endif

        spm_kick_pcm(&pcm_config_dpidle);
        //spm_change_fw = 1;

    }
#ifdef SPM_DPIDLE_SECONDARY_KICK_IMPROVE    
    else       
    {
        if( spm_secondary_kick(&pcm_config_dpidle)==false)
            goto RESTORE_IRQ;

        //spm_change_fw = 0;
    }  
#endif     
        
    spm_dpidle_before_wfi();

    //hopping 33M-->fixed 26M, 20130902 HG.Wei
    top_ckgen_val = spm_read(TOPCKGEN_BASE);
    spm_write(TOPCKGEN_BASE,top_ckgen_val&(~(1<<26)));
        
    snapshot_golden_setting(__FUNCTION__, __LINE__);

    spm_trigger_wfi(&pcm_config_dpidle);

    //hopping fixed 26M-->hopping 33M, 20130902 HG.Wei
    spm_write(TOPCKGEN_BASE,top_ckgen_val);

    spm_dpidle_after_wfi();

    wakesta = spm_get_wakeup_status(&pcm_config_dpidle);

    wr = wakesta->wake_reason;

    spm_clean_after_wakeup();

#ifdef SPM_CLOCK_INIT
    disable_clock(MT_CG_SPM_SW_CG, "SPM_DPIDLE");
    disable_clock(MT_CG_MEMSLP_DLYER_SW_CG, "SPM_DPIDLE");
#endif

RESTORE_IRQ:
    mt_cirq_flush();
    mt_cirq_disable();
    mt_irq_mask_restore(&mask);
    spm_go_to_normal();
    spin_unlock_irqrestore(&spm_lock, flags);
    return wr;
}
Exemple #12
0
/*
 * pwrlevel:
 *     0: CPU standby + AP sleep (SRCLKENA high) => legacy deep idle
 *     1: CPU dormant (L1/L2 sleep) + AP sleep (SRCLKENA high)
 *     2: CPU shutdown (L1/L2 power down) + AP sleep (SRCLKENA high)
 */
void sc_go_to_dpidle(unsigned int pwrlevel)
{
	u32 wakesrc, dvfscon, clksettle;
	unsigned long flags;
	struct mtk_irq_mask mask;

	wakesrc = WAKE_SRC_KP | WAKE_SRC_MSDC1 | WAKE_SRC_EINT | WAKE_SRC_RTC |
	          WAKE_SRC_CCIF_MD | WAKE_SRC_ACCDET | WAKE_SRC_GPT |
	          WAKE_SRC_AFE;

	spin_lock_irqsave(&sc_lock, flags);
	mt_irq_mask_all(&mask);

	mt_irq_unmask_for_sleep(MT_SLEEP_IRQ_ID);

	/* to workaround DVFS issue due to CKDIV1 failure */
	dvfscon = sc_read(SC_AP_DVFS_CON);
	//ckdiv1 = sc_read(TOP_CKDIV1);
	sc_write(SC_AP_DVFS_CON, dvfscon | 0x3);
	sc_write_sync();

	sc_set_apmcu_pwrctl(pwrlevel);

	/* keep SRCLKENA high (26M on) when in sleep mode */
	sc_write(SC_CLK_CON, sc_read(SC_CLK_CON) | SC_CLK_CON_SRCLKEN);

	/* set SYSCLK settle time to 1T 32K */
	clksettle = sc_read(SC_CLK_SETTLE);
	sc_write(SC_CLK_SETTLE, SC_CLK_SETTLE_26MON);

	sc_write(SC_PAUSE, 0xffffffff);

	/* unmask wakeup sources */
	sc_write(SC_WAKEUP_EVENT_MASK, ~wakesrc);

	/* unmask Pause Interrupt and Pause Abort */
	sc_write(SC_ISR_MASK, 0x0001);

	sc_write(SC_TIMER_CON, SC_TIMER_CON_EN);
	sc_write(SC_TIMER_CMD, SC_TIMER_CMD_KEY | SC_TIMER_CMD_CONWR |
	                       SC_TIMER_CMD_CNTWR | SC_TIMER_CMD_WR);
	while (!(sc_read(SC_TIMER_STA) & SC_TIMER_STA_CMDCPL));

	if (sc_dpidle_can_enter()) {
		sc_write(SC_TIMER_CMD, SC_TIMER_CMD_KEY | SC_TIMER_CMD_PAUSE);
		while (!(sc_read(SC_TIMER_STA) & SC_TIMER_STA_CMDCPL));

		sc_dpidle_before_wfi();

		sc_wait_for_interrupt(pwrlevel, true);

		sc_dpidle_after_wfi();
	}

	/* delay to make sure ISR_STATUS can be cleared later */
	udelay(100);

	/* for cleaning wakeup source status */
	sc_write(SC_DBG_WAKEUP, 0);

	sc_write(SC_TIMER_CON, SC_TIMER_CON_DBG | SC_TIMER_CON_EN);
	sc_write(SC_TIMER_CMD, SC_TIMER_CMD_KEY | SC_TIMER_CMD_CONWR |
	                       SC_TIMER_CMD_WR);
	while (!(sc_read(SC_TIMER_STA) & SC_TIMER_STA_CMDCPL));

	sc_write(SC_ISR_MASK, 0x0007);
	sc_write(SC_ISR_STATUS, 0x0007);	/* write 1 clear */

	/* restore SC_CLK_SETTLE and SC_CLK_CON */
	sc_write(SC_CLK_SETTLE, clksettle);
	sc_write(SC_CLK_CON, sc_read(SC_CLK_CON) & ~SC_CLK_CON_SRCLKEN);
	sc_write_sync();

	/* to workaround DVFS issue due to CKDIV1 failure */
	//sc_write(TOP_CKDIV1, 0x00);
	//sc_write(TOP_CKDIV1, ckdiv1);
	sc_write(SC_AP_DVFS_CON, dvfscon);
	sc_write_sync();

	mt_irq_mask_restore(&mask);
	spin_unlock_irqrestore(&sc_lock, flags);
}
Exemple #13
0
/*
 * pwrlevel:
 *     0: CPU standby + AP sleep => legacy sleep mode
 *     1: CPU dormant (L1/L2 sleep) + AP sleep
 *     2: CPU shutdown (L1/L2 power down) + AP sleep
 */
wake_reason_t sc_go_to_sleep(unsigned int pwrlevel)
{
	u32 wakesrc, dvfscon, sec = 0;
	int i, r;
	unsigned long flags;
	struct mtk_irq_mask mask;
	static wake_reason_t last_wr = WR_NONE;

	sc_info2("pwrlevel = %u\n", pwrlevel);

	if (sc_pwake_en)
		sec = sc_get_wake_period(!!(last_wr != WR_SLP_TMR));

	mtk_wdt_suspend();

	spin_lock_irqsave(&sc_lock, flags);
	wakesrc = sc_wake_src;
	for (i = 0; i < NUM_WAKE_SRC; i++) {
		if (wakesrc & (1U << i)) {
			r = sc_enable_wake_irq(sc_wake_irq[i]);
			if (r)
				wakesrc &= ~(1U << i);
		}
	}

	mt_irq_mask_all(&mask);

	mt_irq_unmask_for_sleep(MT_SLEEP_IRQ_ID);

	/* to workaround DVFS issue due to CKDIV1 failure */
	dvfscon = sc_read(SC_AP_DVFS_CON);
	//ckdiv1 = sc_read(TOP_CKDIV1);
	sc_write(SC_AP_DVFS_CON, dvfscon | 0x3);
	sc_write_sync();

	sc_set_apmcu_pwrctl(pwrlevel);

#ifdef CONFIG_MTK_SC_DEBUG
	/* bypass CSYSPWRUPREQ to avoid keeping SRCLKENA high */
	sc_write(SC_CLK_CON, sc_read(SC_CLK_CON) | SC_CLK_CON_BYPASSDBG);
	sc_write_sync();
#endif

	last_wr = sc_enter_pause_mode(pwrlevel, sec, wakesrc);

#ifdef CONFIG_MTK_SC_DEBUG
	/* restore SC_CLK_CON */
	sc_write(SC_CLK_CON, sc_read(SC_CLK_CON) & ~SC_CLK_CON_BYPASSDBG);
	sc_write_sync();
#endif

	/* to workaround DVFS issue due to CKDIV1 failure */
	//sc_write(TOP_CKDIV1, 0x00);
	//sc_write(TOP_CKDIV1, ckdiv1);
	sc_write(SC_AP_DVFS_CON, dvfscon);
	sc_write_sync();

	mt_irq_mask_restore(&mask);
	spin_unlock_irqrestore(&sc_lock, flags);

	mtk_wdt_resume();

	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;
}
Exemple #15
0
/*
 * cpu_pdn:
 *    true  = CPU shutdown
 *    false = CPU standby
 * infra_pdn:
 *    true  = INFRA/DDRPHY power down
 *    false = keep INFRA/DDRPHY power
 * pwake_time:
 *    >= 0  = specific wakeup period
 */
wake_reason_t spm_go_to_sleep(bool cpu_pdn, bool infra_pdn, int pwake_time)
{
    u32 sec = 0;
    int wd_ret;
    wake_status_t wakesta;
    unsigned long flags;
    struct mtk_irq_mask mask;
    struct wd_api *wd_api;
    static wake_reason_t last_wr = WR_NONE;
    const pcm_desc_t *pcmdesc = &pcm_suspend;
    const bool pcmwdt_en = true;

#if SPM_PWAKE_EN
    sec = spm_get_wake_period(pwake_time, last_wr);
#endif

    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(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, spm_sleep_wakesrc, cpu_pdn, infra_pdn);

    spm_reset_and_init_pcm();

    spm_kick_im_to_fetch(pcmdesc);

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

    spm_init_pcm_register();

    spm_init_event_vector(pcmdesc);

    spm_set_pwrctl_for_sleep();

    spm_set_wakeup_event(sec * 32768, spm_sleep_wakesrc);

    spm_kick_pcm_to_run(cpu_pdn, infra_pdn, pcmwdt_en);

    spm_trigger_wfi_for_sleep(cpu_pdn, infra_pdn);

    spm_get_wakeup_status(&wakesta);

    spm_clean_after_wakeup(pcmwdt_en);

    last_wr = spm_output_wake_reason(&wakesta, false);

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

    //spm_go_to_normal();   /* included in pcm_suspend */

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

    return last_wr;
}
/*
 * 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;  
}
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;

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

    set_pwrctrl_pcm_flags(pwrctrl, spm_flags);

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

    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_SPM_IRQ_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_SPM_FLOW));
#endif   

    __spm_reset_and_init_pcm(pcmdesc);
#if 0
    /* 0: mempll shutdown mode; 1: cg mode */
    gSpm_SODI_mempll_pwr_mode ? (pwrctrl->pcm_flags |= SPM_MEMPLL_CPU) :
				(pwrctrl->pcm_flags &= ~SPM_MEMPLL_CPU);
#endif

    __spm_kick_im_to_fetch(pcmdesc);

    __spm_init_pcm_register();

    __spm_init_event_vector(pcmdesc);

    /* set pcm_apsrc_req to be 1 if 10006b0c[0] is 1 */
    if ((spm_read(SPM_PCM_SRC_REQ) & 1) || pwrctrl->pcm_apsrc_req)
        pwrctrl->pcm_apsrc_req = 1;
    else
        pwrctrl->pcm_apsrc_req = 0;

    __spm_set_power_control(pwrctrl);

    __spm_set_wakeup_event(pwrctrl);

    /* set pcm_flags[18] to be 1 if 10006b08[7] is 1 */
    if ((spm_read(SPM_PCM_FLAGS) & SPM_MEMPLL_RESET) ||
        gSpm_SODI_mempll_pwr_mode ||
        (pwrctrl->pcm_flags_cust & SPM_MEMPLL_CPU))
        pwrctrl->pcm_flags |= SPM_MEMPLL_CPU;
    else
        pwrctrl->pcm_flags &= ~SPM_MEMPLL_CPU;

    __spm_kick_pcm_to_run(pwrctrl);

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

    spm_trigger_wfi_for_sodi(pwrctrl);

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

    __spm_get_wakeup_status(&wakesta);

    __spm_clean_after_wakeup();	

    wr = __spm_output_wake_reason(&wakesta, pcmdesc, false);
    /* for test */
    /* wr = __spm_output_wake_reason(&wakesta, pcmdesc, true); */

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

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

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

#if SPM_AEE_RR_REC
    aee_rr_rec_sodi_val(0);
#endif 
    //return wr;

}