| /* |
| * Copyright (c) 2023, MediaTek Inc. All rights reserved. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #include <assert.h> |
| #include <stddef.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #include <common/debug.h> |
| #include <lib/mmio.h> |
| #include <plat/common/platform.h> |
| #include <lib/pm/mtk_pm.h> |
| #include <lpm/mt_lp_rqm.h> |
| #include "mt_spm.h" |
| #include "mt_spm_conservation.h" |
| #include "mt_spm_reg.h" |
| #include <platform_def.h> |
| |
| #define INFRA_EMI_DCM_CFG0 U(0x10002028) |
| |
| static struct wake_status spm_wakesta; /* record last wakesta */ |
| static wake_reason_t spm_wake_reason = WR_NONE; |
| static unsigned int emi_bak; |
| |
| 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; |
| unsigned int cpu = plat_my_core_pos(); |
| |
| pwrctrl = spm_lp->pwrctrl; |
| |
| /* EMI workaround */ |
| emi_bak = mmio_read_32(INFRA_EMI_DCM_CFG0) & BIT(22); |
| mmio_setbits_32(INFRA_EMI_DCM_CFG0, BIT(22)); |
| |
| __spm_set_cpu_status(cpu); |
| __spm_set_power_control(pwrctrl); |
| __spm_set_wakeup_event(pwrctrl); |
| |
| __spm_set_pcm_flags(pwrctrl); |
| |
| __spm_src_req_update(pwrctrl, resource_req); |
| |
| if ((ext_opand & MT_SPM_EX_OP_CLR_26M_RECORD) != 0U) { |
| __spm_clean_before_wfi(); |
| } |
| |
| if ((ext_opand & MT_SPM_EX_OP_SET_WDT) != 0U) { |
| __spm_set_pcm_wdt(1); |
| } |
| |
| if ((ext_opand & MT_SPM_EX_OP_HW_S1_DETECT) != 0U) { |
| spm_hw_s1_state_monitor_resume(); |
| } |
| |
| __spm_send_cpu_wakeup_event(); |
| |
| 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; |
| |
| if ((ext_opand & MT_SPM_EX_OP_SET_WDT) != 0U) { |
| __spm_set_pcm_wdt(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(); |
| spm_wake_reason = __spm_output_wake_reason(&spm_wakesta); |
| |
| /* EMI workaround */ |
| if (emi_bak == 0U) { |
| mmio_clrbits_32(INFRA_EMI_DCM_CFG0, BIT(22)); |
| } |
| } |
| |
| int spm_conservation(int state_id, unsigned int ext_opand, |
| struct spm_lp_scen *spm_lp, |
| unsigned int resource_req) |
| { |
| unsigned int rc_state = resource_req; |
| |
| if (spm_lp == NULL) { |
| return -1; |
| } |
| |
| spin_lock(&spm_lock); |
| go_to_spm_before_wfi(state_id, ext_opand, spm_lp, rc_state); |
| spin_unlock(&spm_lock); |
| |
| return 0; |
| } |
| |
| void spm_conservation_finish(int state_id, unsigned int ext_opand, struct spm_lp_scen *spm_lp, |
| struct wake_status **status) |
| { |
| spin_lock(&spm_lock); |
| go_to_spm_after_wfi(state_id, ext_opand, spm_lp, status); |
| spin_unlock(&spm_lock); |
| } |
| |
| int spm_conservation_get_result(struct wake_status **res) |
| { |
| if (res == NULL) { |
| return -1; |
| } |
| *res = &spm_wakesta; |
| return 0; |
| } |