| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516 |
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/spinlock.h>
- #include <linux/delay.h>
- #include <linux/slab.h>
- #include <mach/irqs.h>
- #include <mach/mt_gpt.h>
- #ifdef CONFIG_MTK_WD_KICKER
- #include <mach/wd_api.h>
- #endif
- #include <mt-plat/mt_boot.h>
- #if defined(CONFIG_MTK_SYS_CIRQ)
- #include <mt-plat/mt_cirq.h>
- #endif
- #include <mt-plat/upmu_common.h>
- #include <mt-plat/mt_io.h>
- #include <mt_spm_sodi3.h>
- /**************************************
- * only for internal debug
- **************************************/
- #define SODI3_TAG "[SODI3] "
- #define sodi3_err(fmt, args...) pr_err(SODI3_TAG fmt, ##args)
- #define sodi3_warn(fmt, args...) pr_warn(SODI3_TAG fmt, ##args)
- #define sodi3_debug(fmt, args...) pr_debug(SODI3_TAG fmt, ##args)
- #define SPM_BYPASS_SYSPWREQ 0
- #define LOG_BUF_SIZE (256)
- #define SODI3_LOGOUT_TIMEOUT_CRITERIA (20)
- #define SODI3_LOGOUT_INTERVAL_CRITERIA (5000U) /* unit:ms */
- static struct pwr_ctrl sodi3_ctrl = {
- .wake_src = WAKE_SRC_FOR_SODI3,
- .wake_src_md32 = WAKE_SRC_FOR_MD32,
- .r0_ctrl_en = 1,
- .r7_ctrl_en = 1,
- .infra_dcm_lock = 1, /* set to be 1 if SODI 3.0 */
- .wfi_op = WFI_OP_AND,
- /* SPM_AP_STANDBY_CON */
- .mp0top_idle_mask = 0,
- .mp1top_idle_mask = 0,
- .mcusys_idle_mask = 0,
- .md_ddr_dbc_en = 0,
- .md1_req_mask_b = 1,
- .md2_req_mask_b = 0, /* bit 20 */
- #if defined(CONFIG_ARCH_MT6755)
- .scp_req_mask_b = 0, /* bit 21 */
- #elif defined(CONFIG_ARCH_MT6797)
- .scp_req_mask_b = 1, /* bit 21 */
- #endif
- .lte_mask_b = 0,
- .md_apsrc1_sel = 0, /* bit 24, set to be 1 for SODI CG mode */
- .md_apsrc0_sel = 0, /* bit 25, set to be 1 for SODI CG mode */
- .conn_mask_b = 1,
- .conn_apsrc_sel = 0, /* bit 27, set to be 1 for SODI CG mode */
- /* SPM_SRC_REQ */
- .spm_apsrc_req = 0,
- .spm_f26m_req = 0,
- .spm_lte_req = 0,
- .spm_infra_req = 0,
- .spm_vrf18_req = 0,
- .spm_dvfs_req = 0,
- .spm_dvfs_force_down = 0,
- .spm_ddren_req = 0,
- .cpu_md_dvfs_sop_force_on = 0,
- /* SPM_SRC_MASK */
- .ccif0_to_md_mask_b = 1,
- .ccif0_to_ap_mask_b = 1,
- .ccif1_to_md_mask_b = 1,
- .ccif1_to_ap_mask_b = 1,
- .ccifmd_md1_event_mask_b = 1,
- .ccifmd_md2_event_mask_b = 1,
- .vsync_mask_b = 0, /* 5-bit */
- .md_srcclkena_0_infra_mask_b = 0, /* bit 12 */
- .md_srcclkena_1_infra_mask_b = 0, /* bit 13 */
- .conn_srcclkena_infra_mask_b = 0, /* bit 14 */
- .md32_srcclkena_infra_mask_b = 0, /* bit 15 */
- .srcclkeni_infra_mask_b = 0, /* bit 16 */
- .md_apsrcreq_0_infra_mask_b = 1,
- .md_apsrcreq_1_infra_mask_b = 0,
- .conn_apsrcreq_infra_mask_b = 1,
- #if defined(CONFIG_ARCH_MT6755)
- .md32_apsrcreq_infra_mask_b = 0,
- #elif defined(CONFIG_ARCH_MT6797)
- .md32_apsrcreq_infra_mask_b = 1,
- #endif
- .md_ddr_en_0_mask_b = 1,
- .md_ddr_en_1_mask_b = 0, /* bit 22 */
- .md_vrf18_req_0_mask_b = 1,
- .md_vrf18_req_1_mask_b = 0, /* bit 24 */
- .emi_bw_dvfs_req_mask = 1,
- .md_srcclkena_0_dvfs_req_mask_b = 0,
- .md_srcclkena_1_dvfs_req_mask_b = 0,
- .conn_srcclkena_dvfs_req_mask_b = 0,
- /* SPM_SRC2_MASK */
- .dvfs_halt_mask_b = 0x1f, /* 5-bit */
- .vdec_req_mask_b = 0, /* bit 6 */
- .gce_req_mask_b = 1, /* bit 7, set to be 1 for SODI */
- .cpu_md_dvfs_erq_merge_mask_b = 0,
- .md1_ddr_en_dvfs_halt_mask_b = 0,
- .md2_ddr_en_dvfs_halt_mask_b = 0,
- .vsync_dvfs_halt_mask_b = 0, /* 5-bit, bit 11 ~ 15 */
- .conn_ddr_en_mask_b = 1,
- .disp_req_mask_b = 1, /* bit 17, set to be 1 for SODI */
- .disp1_req_mask_b = 1, /* bit 18, set to be 1 for SODI */
- #if defined(CONFIG_ARCH_MT6755)
- .mfg_req_mask_b = 0, /* bit 19 */
- #elif defined(CONFIG_ARCH_MT6797)
- .mfg_req_mask_b = 1, /* bit 19, set to be 1 for SODI */
- #endif
- .c2k_ps_rccif_wake_mask_b = 1,
- .c2k_l1_rccif_wake_mask_b = 1,
- .ps_c2k_rccif_wake_mask_b = 1,
- .l1_c2k_rccif_wake_mask_b = 1,
- .sdio_on_dvfs_req_mask_b = 0,
- .emi_boost_dvfs_req_mask_b = 0,
- .cpu_md_emi_dvfs_req_prot_dis = 0,
- #if defined(CONFIG_ARCH_MT6797)
- .disp_od_req_mask_b = 1, /* bit 27, set to be 1 for SODI */
- #endif
- /* SPM_CLK_CON */
- .srclkenai_mask = 1,
- .mp1_cpu0_wfi_en = 1,
- .mp1_cpu1_wfi_en = 1,
- .mp1_cpu2_wfi_en = 1,
- .mp1_cpu3_wfi_en = 1,
- .mp0_cpu0_wfi_en = 1,
- .mp0_cpu1_wfi_en = 1,
- .mp0_cpu2_wfi_en = 1,
- .mp0_cpu3_wfi_en = 1,
- #if SPM_BYPASS_SYSPWREQ
- .syspwreq_mask = 1,
- #endif
- };
- struct spm_lp_scen __spm_sodi3 = {
- .pwrctrl = &sodi3_ctrl,
- };
- static bool gSpm_sodi3_en;
- static unsigned long int sodi3_logout_prev_time;
- static int pre_emi_refresh_cnt;
- static int memPllCG_prev_status = 1; /* 1:CG, 0:pwrdn */
- static unsigned int logout_sodi3_cnt;
- static unsigned int logout_selfrefresh_cnt;
- static void spm_sodi3_pre_process(void)
- {
- #if defined(CONFIG_ARCH_MT6755)
- u32 val;
- #endif
- __spm_pmic_pg_force_on();
- spm_disable_mmu_smi_async();
- spm_pmic_power_mode(PMIC_PWR_SODI3, 0, 0);
- spm_bypass_boost_gpio_set();
- #if defined(CONFIG_ARCH_MT6755)
- pmic_read_interface_nolock(MT6351_PMIC_BUCK_VSRAM_PROC_VOSEL_ON_ADDR,
- &val,
- MT6351_PMIC_BUCK_VSRAM_PROC_VOSEL_ON_MASK,
- MT6351_PMIC_BUCK_VSRAM_PROC_VOSEL_ON_SHIFT);
- mt_spm_pmic_wrap_set_cmd(PMIC_WRAP_PHASE_DEEPIDLE, IDX_DI_VSRAM_NORMAL, val);
- pmic_read_interface_nolock(MT6351_TOP_CON, &val, 0x037F, 0);
- mt_spm_pmic_wrap_set_cmd(PMIC_WRAP_PHASE_DEEPIDLE,
- IDX_DI_SRCCLKEN_IN2_NORMAL,
- val | (1 << MT6351_PMIC_RG_SRCLKEN_IN2_EN_SHIFT));
- mt_spm_pmic_wrap_set_cmd(PMIC_WRAP_PHASE_DEEPIDLE,
- IDX_DI_SRCCLKEN_IN2_SLEEP,
- val & ~(1 << MT6351_PMIC_RG_SRCLKEN_IN2_EN_SHIFT));
- #endif
- /* set PMIC WRAP table for deepidle power control */
- mt_spm_pmic_wrap_set_phase(PMIC_WRAP_PHASE_DEEPIDLE);
- /* Do more low power setting when MD1/C2K/CONN off */
- if (is_md_c2k_conn_power_off())
- __spm_bsi_top_init_setting();
- }
- static void spm_sodi3_post_process(void)
- {
- /* set PMIC WRAP table for normal power control */
- mt_spm_pmic_wrap_set_phase(PMIC_WRAP_PHASE_NORMAL);
- spm_enable_mmu_smi_async();
- __spm_pmic_pg_force_off();
- }
- static wake_reason_t
- spm_sodi3_output_log(struct wake_status *wakesta, struct pcm_desc *pcmdesc, int vcore_status, u32 sodi3_flags)
- {
- wake_reason_t wr = WR_NONE;
- unsigned long int sodi3_logout_curr_time = 0;
- int need_log_out = 0;
- if (sodi3_flags&SODI_FLAG_NO_LOG) {
- if (wakesta->assert_pc != 0) {
- sodi3_err("PCM ASSERT AT %u (%s), r13 = 0x%x, debug_flag = 0x%x\n",
- wakesta->assert_pc, pcmdesc->version, wakesta->r13, wakesta->debug_flag);
- wr = WR_PCM_ASSERT;
- }
- } else if (!(sodi3_flags&SODI_FLAG_REDUCE_LOG) || (sodi3_flags & SODI_FLAG_RESIDENCY)) {
- sodi3_warn("vcore_status = %d, self_refresh = 0x%x, sw_flag = 0x%x, 0x%x, %s\n",
- vcore_status, spm_read(SPM_PASR_DPD_0), spm_read(SPM_SW_FLAG),
- spm_read(DUMMY1_PWR_CON), pcmdesc->version);
- wr = __spm_output_wake_reason(wakesta, pcmdesc, false);
- } else {
- sodi3_logout_curr_time = spm_get_current_time_ms();
- if (wakesta->assert_pc != 0) {
- need_log_out = 1;
- } else if ((wakesta->r12 & (0x1 << 4)) == 0) {
- /* not wakeup by GPT */
- need_log_out = 1;
- } else if (wakesta->timer_out <= SODI3_LOGOUT_TIMEOUT_CRITERIA) {
- need_log_out = 1;
- } else if ((spm_read(SPM_PASR_DPD_0) == 0 && pre_emi_refresh_cnt > 0) ||
- (spm_read(SPM_PASR_DPD_0) > 0 && pre_emi_refresh_cnt == 0)) {
- need_log_out = 1;
- } else if ((sodi3_logout_curr_time - sodi3_logout_prev_time) > SODI3_LOGOUT_INTERVAL_CRITERIA) {
- /* previous logout time > SODI3_LOGOUT_INTERVAL_CRITERIA */
- need_log_out = 1;
- } else {
- /* check CG/pwrdn status is changed */
- int mem_status = 0;
- /* check mempll CG/pwrdn status change */
- if (((spm_read(SPM_SW_FLAG) & SPM_FLAG_SODI_CG_MODE) != 0) ||
- ((spm_read(DUMMY1_PWR_CON) & DUMMY1_PWR_ISO_LSB) != 0))
- mem_status = 1;
- if (memPllCG_prev_status != mem_status) {
- memPllCG_prev_status = mem_status;
- need_log_out = 1;
- }
- }
- logout_sodi3_cnt++;
- logout_selfrefresh_cnt += spm_read(SPM_PASR_DPD_0);
- pre_emi_refresh_cnt = spm_read(SPM_PASR_DPD_0);
- if (need_log_out == 1) {
- sodi3_logout_prev_time = sodi3_logout_curr_time;
- if (wakesta->assert_pc != 0) {
- sodi3_err("wake up by SPM assert, vcore_status = %d, self_refresh = 0x%x, sw_flag = 0x%x, 0x%x, %s\n",
- vcore_status, spm_read(SPM_PASR_DPD_0), spm_read(SPM_SW_FLAG),
- spm_read(DUMMY1_PWR_CON), pcmdesc->version);
- sodi3_err("sodi3_cnt = %d, self_refresh_cnt = 0x%x, spm_pc = 0x%0x, r13 = 0x%x, debug_flag = 0x%x\n",
- logout_sodi3_cnt, logout_selfrefresh_cnt,
- wakesta->assert_pc, wakesta->r13, wakesta->debug_flag);
- sodi3_err("r12 = 0x%x, r12_e = 0x%x, raw_sta = 0x%x, idle_sta = 0x%x, event_reg = 0x%x, isr = 0x%x\n",
- wakesta->r12, wakesta->r12_ext, wakesta->raw_sta, wakesta->idle_sta,
- wakesta->event_reg, wakesta->isr);
- } else {
- char buf[LOG_BUF_SIZE] = { 0 };
- int i;
- if (wakesta->r12 & WAKE_SRC_R12_PCM_TIMER) {
- if (wakesta->wake_misc & WAKE_MISC_PCM_TIMER)
- strcat(buf, " PCM_TIMER");
- if (wakesta->wake_misc & WAKE_MISC_TWAM)
- strcat(buf, " TWAM");
- if (wakesta->wake_misc & WAKE_MISC_CPU_WAKE)
- strcat(buf, " CPU");
- }
- for (i = 1; i < 32; i++) {
- if (wakesta->r12 & (1U << i)) {
- strcat(buf, wakesrc_str[i]);
- wr = WR_WAKE_SRC;
- }
- }
- BUG_ON(strlen(buf) >= LOG_BUF_SIZE);
- sodi3_warn("wake up by %s, vcore_status = %d, self_refresh = 0x%x, sw_flag = 0x%x, 0x%x, %s\n",
- buf, vcore_status, spm_read(SPM_PASR_DPD_0), spm_read(SPM_SW_FLAG),
- spm_read(DUMMY1_PWR_CON), pcmdesc->version);
- sodi3_warn("sodi3_cnt = %d, self_refresh_cnt = 0x%x, timer_out = %u, r13 = 0x%x, debug_flag = 0x%x\n",
- logout_sodi3_cnt, logout_selfrefresh_cnt,
- wakesta->timer_out, wakesta->r13, wakesta->debug_flag);
- sodi3_warn("r12 = 0x%x, r12_e = 0x%x, raw_sta = 0x%x, idle_sta = 0x%x, event_reg = 0x%x, isr = 0x%x\n",
- wakesta->r12, wakesta->r12_ext, wakesta->raw_sta, wakesta->idle_sta,
- wakesta->event_reg, wakesta->isr);
- }
- logout_sodi3_cnt = 0;
- logout_selfrefresh_cnt = 0;
- }
- }
- return wr;
- }
- wake_reason_t spm_go_to_sodi3(u32 spm_flags, u32 spm_data, u32 sodi3_flags)
- {
- u32 sec = 2;
- #ifdef CONFIG_MTK_WD_KICKER
- int wd_ret;
- struct wd_api *wd_api;
- #endif
- u32 con1;
- struct wake_status wakesta;
- unsigned long flags;
- struct mtk_irq_mask *mask;
- wake_reason_t wr = WR_NONE;
- struct pcm_desc *pcmdesc;
- struct pwr_ctrl *pwrctrl = __spm_sodi3.pwrctrl;
- int vcore_status = vcorefs_get_curr_ddr();
- u32 cpu = spm_data;
- u32 sodi_idx;
- #if defined(CONFIG_ARCH_MT6797)
- sodi_idx = spm_get_sodi_pcm_index() + cpu / 4;
- #else
- sodi_idx = DYNA_LOAD_PCM_SODI + cpu / 4;
- #endif
- if (!dyna_load_pcm[sodi_idx].ready) {
- sodi3_err("error: load firmware fail\n");
- BUG();
- }
- pcmdesc = &(dyna_load_pcm[sodi_idx].desc);
- spm_sodi3_footprint(SPM_SODI3_ENTER);
- if (spm_get_sodi_mempll() == 1)
- spm_flags |= SPM_FLAG_SODI_CG_MODE; /* CG mode */
- else
- spm_flags &= ~SPM_FLAG_SODI_CG_MODE; /* PDN mode */
- update_pwrctrl_pcm_flags(&spm_flags);
- set_pwrctrl_pcm_flags(pwrctrl, spm_flags);
- pwrctrl->timer_val = sec * 32768;
- #ifdef CONFIG_MTK_WD_KICKER
- wd_ret = get_wd_api(&wd_api);
- if (!wd_ret)
- wd_api->wd_suspend_notify();
- #endif
- /* enable APxGPT timer */
- soidle3_before_wfi(cpu);
- lockdep_off();
- spin_lock_irqsave(&__spm_lock, flags);
- mask = kmalloc(sizeof(struct mtk_irq_mask), GFP_ATOMIC);
- if (!mask) {
- wr = -ENOMEM;
- goto UNLOCK_SPM;
- }
- mt_irq_mask_all(mask);
- mt_irq_unmask_for_sleep(SPM_IRQ0_ID);
- #if defined(CONFIG_MTK_SYS_CIRQ)
- mt_cirq_clone_gic();
- mt_cirq_enable();
- #endif
- spm_sodi3_footprint(SPM_SODI3_ENTER_UART_SLEEP);
- if (request_uart_to_sleep()) {
- wr = WR_UART_BUSY;
- goto RESTORE_IRQ;
- }
- spm_sodi3_footprint(SPM_SODI3_ENTER_SPM_FLOW);
- __spm_reset_and_init_pcm(pcmdesc);
- __spm_kick_im_to_fetch(pcmdesc);
- __spm_init_pcm_register();
- __spm_init_event_vector(pcmdesc);
- #if defined(CONFIG_ARCH_MT6755)
- __spm_check_md_pdn_power_control(pwrctrl);
- #endif
- __spm_sync_vcore_dvfs_power_control(pwrctrl, __spm_vcore_dvfs.pwrctrl);
- if (spm_read(SPM_SW_FLAG) & SPM_FLAG_SODI_CG_MODE) {
- /* the following masks set to be 1 only for SODI CG mode */
- pwrctrl->md_apsrc1_sel = 1;
- pwrctrl->md_apsrc0_sel = 1;
- pwrctrl->conn_apsrc_sel = 1;
- } else {
- /* the following masks set to be 0 which dynamic switch by FW */
- pwrctrl->md_apsrc1_sel = 0;
- pwrctrl->md_apsrc0_sel = 0;
- pwrctrl->conn_apsrc_sel = 0;
- }
- __spm_set_power_control(pwrctrl);
- __spm_set_wakeup_event(pwrctrl);
- /* PCM WDT WAKE MODE for lastPC */
- con1 = spm_read(PCM_CON1) & ~(PCM_WDT_WAKE_MODE_LSB | PCM_WDT_EN_LSB);
- spm_write(PCM_CON1, SPM_REGWR_CFG_KEY | con1);
- if (spm_read(PCM_TIMER_VAL) > PCM_TIMER_MAX)
- spm_write(PCM_TIMER_VAL, PCM_TIMER_MAX);
- spm_write(PCM_WDT_VAL, spm_read(PCM_TIMER_VAL) + PCM_WDT_TIMEOUT);
- if (pwrctrl->timer_val_cust == 0)
- spm_write(PCM_CON1, con1 | SPM_REGWR_CFG_KEY | PCM_WDT_EN_LSB | PCM_TIMER_EN_LSB);
- else
- spm_write(PCM_CON1, con1 | SPM_REGWR_CFG_KEY | PCM_WDT_EN_LSB);
- spm_sodi3_pre_process();
- __spm_kick_pcm_to_run(pwrctrl);
- spm_sodi3_footprint_val((1 << SPM_SODI3_ENTER_WFI) |
- (1 << SPM_SODI3_B3) | (1 << SPM_SODI3_B4) |
- (1 << SPM_SODI3_B5) | (1 << SPM_SODI3_B6));
- #ifdef SPM_SODI3_PROFILE_TIME
- gpt_get_cnt(SPM_SODI3_PROFILE_APXGPT, &soidle3_profile[1]);
- #endif
- spm_trigger_wfi_for_sodi(pwrctrl);
- #ifdef SPM_SODI3_PROFILE_TIME
- gpt_get_cnt(SPM_SODI3_PROFILE_APXGPT, &soidle3_profile[2]);
- #endif
- spm_sodi3_footprint(SPM_SODI3_LEAVE_WFI);
- spm_sodi3_post_process();
- __spm_get_wakeup_status(&wakesta);
- /* disable PCM WDT to stop count if needed */
- spm_write(PCM_CON1, SPM_REGWR_CFG_KEY | (spm_read(PCM_CON1) & ~PCM_WDT_EN_LSB));
- __spm_clean_after_wakeup();
- spm_sodi3_footprint(SPM_SODI3_ENTER_UART_AWAKE);
- request_uart_to_wakeup();
- wr = spm_sodi3_output_log(&wakesta, pcmdesc, vcore_status, sodi3_flags);
- spm_sodi3_footprint(SPM_SODI3_LEAVE_SPM_FLOW);
- RESTORE_IRQ:
- #if defined(CONFIG_MTK_SYS_CIRQ)
- mt_cirq_flush();
- mt_cirq_disable();
- #endif
- mt_irq_mask_restore(mask);
- kfree(mask);
- UNLOCK_SPM:
- spin_unlock_irqrestore(&__spm_lock, flags);
- lockdep_on();
- /* stop APxGPT timer and enable caore0 local timer */
- soidle3_after_wfi(cpu);
- #ifdef CONFIG_MTK_WD_KICKER
- if (!wd_ret)
- wd_api->wd_resume_notify();
- #endif
- spm_sodi3_reset_footprint();
- return wr;
- }
- void spm_enable_sodi3(bool en)
- {
- gSpm_sodi3_en = en;
- }
- bool spm_get_sodi3_en(void)
- {
- return gSpm_sodi3_en;
- }
- void spm_sodi3_init(void)
- {
- sodi3_debug("spm_sodi3_init\n");
- spm_sodi3_aee_init();
- }
- MODULE_DESCRIPTION("SPM-SODI3 Driver v0.1");
|