| /* |
| * Copyright (c) 2020, MediaTek Inc. All rights reserved. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #include <common/debug.h> |
| #include <lib/mmio.h> |
| |
| #include <mt_spm.h> |
| #include <mt_spm_conservation.h> |
| #include <mt_spm_internal.h> |
| #include <mt_spm_reg.h> |
| #include <mt_spm_vcorefs.h> |
| #include <plat_mtk_lpm.h> |
| #include <plat_pm.h> |
| #include <plat/common/platform.h> |
| #include <platform_def.h> |
| |
| struct wake_status spm_wakesta; /* record last wakesta */ |
| |
| static int go_to_spm_before_wfi(int state_id, unsigned int ext_opand, |
| struct spm_lp_scen *spm_lp, |
| unsigned int resource_req) |
| { |
| int ret = 0; |
| struct pwr_ctrl *pwrctrl; |
| uint32_t cpu = plat_my_core_pos(); |
| |
| pwrctrl = spm_lp->pwrctrl; |
| |
| __spm_set_cpu_status(cpu); |
| __spm_set_power_control(pwrctrl); |
| __spm_set_wakeup_event(pwrctrl); |
| __spm_sync_vcore_dvfs_power_control(pwrctrl, __spm_vcorefs.pwrctrl); |
| __spm_set_pcm_flags(pwrctrl); |
| __spm_src_req_update(pwrctrl, resource_req); |
| |
| if ((ext_opand & MT_SPM_EX_OP_SET_WDT) != 0U) { |
| __spm_set_pcm_wdt(1); |
| } |
| |
| if ((ext_opand & MT_SPM_EX_OP_SRCLKEN_RC_BBLPM) != 0U) { |
| __spm_xo_soc_bblpm(1); |
| } |
| |
| if ((ext_opand & MT_SPM_EX_OP_HW_S1_DETECT) != 0U) { |
| spm_hw_s1_state_monitor_resume(); |
| } |
| |
| /* Disable auto resume by PCM in system suspend stage */ |
| if (IS_PLAT_SUSPEND_ID(state_id)) { |
| __spm_disable_pcm_timer(); |
| __spm_set_pcm_wdt(0); |
| } |
| |
| __spm_send_cpu_wakeup_event(); |
| |
| INFO("cpu%d: wakesrc = 0x%x, settle = 0x%x, sec = %u\n", |
| cpu, pwrctrl->wake_src, mmio_read_32(SPM_CLK_SETTLE), |
| mmio_read_32(PCM_TIMER_VAL) / 32768); |
| INFO("sw_flag = 0x%x 0x%x, req = 0x%x, pwr = 0x%x 0x%x\n", |
| pwrctrl->pcm_flags, pwrctrl->pcm_flags1, |
| mmio_read_32(SPM_SRC_REQ), mmio_read_32(PWR_STATUS), |
| mmio_read_32(PWR_STATUS_2ND)); |
| |
| return ret; |
| } |
| |
| static void go_to_spm_after_wfi(int state_id, unsigned int ext_opand, |
| struct spm_lp_scen *spm_lp, |
| struct wake_status **status) |
| { |
| unsigned int ext_status = 0U; |
| |
| /* system watchdog will be resumed at kernel stage */ |
| if ((ext_opand & MT_SPM_EX_OP_SET_WDT) != 0U) { |
| __spm_set_pcm_wdt(0); |
| } |
| |
| if ((ext_opand & MT_SPM_EX_OP_SRCLKEN_RC_BBLPM) != 0U) { |
| __spm_xo_soc_bblpm(0); |
| } |
| |
| if ((ext_opand & MT_SPM_EX_OP_HW_S1_DETECT) != 0U) { |
| spm_hw_s1_state_monitor_pause(&ext_status); |
| } |
| |
| __spm_ext_int_wakeup_req_clr(); |
| __spm_get_wakeup_status(&spm_wakesta, ext_status); |
| |
| if (status != NULL) { |
| *status = &spm_wakesta; |
| } |
| |
| __spm_clean_after_wakeup(); |
| |
| if (IS_PLAT_SUSPEND_ID(state_id)) { |
| __spm_output_wake_reason(state_id, &spm_wakesta); |
| } |
| } |
| |
| int spm_conservation(int state_id, unsigned int ext_opand, |
| struct spm_lp_scen *spm_lp, unsigned int resource_req) |
| { |
| if (spm_lp == NULL) { |
| return -1; |
| } |
| |
| spm_lock_get(); |
| go_to_spm_before_wfi(state_id, ext_opand, spm_lp, resource_req); |
| spm_lock_release(); |
| |
| return 0; |
| } |
| |
| void spm_conservation_finish(int state_id, unsigned int ext_opand, |
| struct spm_lp_scen *spm_lp, |
| struct wake_status **status) |
| { |
| spm_lock_get(); |
| go_to_spm_after_wfi(state_id, ext_opand, spm_lp, status); |
| spm_lock_release(); |
| } |
| |
| int spm_conservation_get_result(struct wake_status **res) |
| { |
| if (res == NULL) { |
| return -1; |
| } |
| |
| *res = &spm_wakesta; |
| |
| return 0; |
| } |
| |
| #define GPIO_BANK (GPIO_BASE + 0x6F0) |
| #define TRAP_UFS_FIRST BIT(11) /* bit 11, 0: UFS, 1: eMMC */ |
| |
| void spm_conservation_pwrctrl_init(struct pwr_ctrl *pwrctrl) |
| { |
| if (pwrctrl == NULL) { |
| return; |
| } |
| |
| /* For ufs, emmc storage type */ |
| if ((mmio_read_32(GPIO_BANK) & TRAP_UFS_FIRST) != 0U) { |
| /* If eMMC is used, mask UFS req */ |
| pwrctrl->reg_ufs_srcclkena_mask_b = 0; |
| pwrctrl->reg_ufs_infra_req_mask_b = 0; |
| pwrctrl->reg_ufs_apsrc_req_mask_b = 0; |
| pwrctrl->reg_ufs_vrf18_req_mask_b = 0; |
| pwrctrl->reg_ufs_ddr_en_mask_b = 0; |
| } |
| } |