13fa9dec4Skenny liang /* 27352f329Skenny liang * Copyright (c) 2019, MediaTek Inc. All rights reserved. 33fa9dec4Skenny liang * 43fa9dec4Skenny liang * SPDX-License-Identifier: BSD-3-Clause 53fa9dec4Skenny liang */ 63fa9dec4Skenny liang 73fa9dec4Skenny liang /* common headers */ 83fa9dec4Skenny liang #include <arch_helpers.h> 93fa9dec4Skenny liang #include <assert.h> 103fa9dec4Skenny liang #include <common/debug.h> 113fa9dec4Skenny liang #include <lib/mmio.h> 123fa9dec4Skenny liang #include <lib/psci/psci.h> 133fa9dec4Skenny liang #include <errno.h> 143fa9dec4Skenny liang 153fa9dec4Skenny liang /* mediatek platform specific headers */ 163fa9dec4Skenny liang #include <platform_def.h> 173fa9dec4Skenny liang #include <scu.h> 187352f329Skenny liang #include <mt_gic_v3.h> 19*539061b8Skenny liang #include <mtk_mcdi.h> 203fa9dec4Skenny liang #include <mtk_plat_common.h> 213d91c9c3Skenny liang #include <mtgpio.h> 227352f329Skenny liang #include <mtspmc.h> 237352f329Skenny liang #include <plat_dcm.h> 247352f329Skenny liang #include <plat_debug.h> 253d91c9c3Skenny liang #include <plat_params.h> 263fa9dec4Skenny liang #include <plat_private.h> 273d91c9c3Skenny liang #include <power_tracer.h> 28e977b4dbSkenny liang #include <pmic.h> 293c25ba44Skenny liang #include <spm.h> 303c25ba44Skenny liang #include <spm_suspend.h> 31*539061b8Skenny liang #include <sspm.h> 32e977b4dbSkenny liang #include <rtc.h> 333fa9dec4Skenny liang 34*539061b8Skenny liang /* Local power state for power domains in Run state. */ 35*539061b8Skenny liang #define MTK_LOCAL_STATE_RUN 0 36*539061b8Skenny liang /* Local power state for retention. */ 37*539061b8Skenny liang #define MTK_LOCAL_STATE_RET 1 38*539061b8Skenny liang /* Local power state for OFF/power-down. */ 397352f329Skenny liang #define MTK_LOCAL_STATE_OFF 2 407352f329Skenny liang 41*539061b8Skenny liang #if PSCI_EXTENDED_STATE_ID 42*539061b8Skenny liang /* 43*539061b8Skenny liang * Macros used to parse state information from State-ID if it is using the 44*539061b8Skenny liang * recommended encoding for State-ID. 45*539061b8Skenny liang */ 46*539061b8Skenny liang #define MTK_LOCAL_PSTATE_WIDTH 4 47*539061b8Skenny liang #define MTK_LOCAL_PSTATE_MASK ((1 << MTK_LOCAL_PSTATE_WIDTH) - 1) 48*539061b8Skenny liang 49*539061b8Skenny liang /* Macros to construct the composite power state */ 50*539061b8Skenny liang 51*539061b8Skenny liang /* Make composite power state parameter till power level 0 */ 52*539061b8Skenny liang 53*539061b8Skenny liang #define mtk_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type) \ 54*539061b8Skenny liang (((lvl0_state) << PSTATE_ID_SHIFT) | ((type) << PSTATE_TYPE_SHIFT)) 55*539061b8Skenny liang 56*539061b8Skenny liang #else /* !PSCI_EXTENDED_STATE_ID */ 57*539061b8Skenny liang 58*539061b8Skenny liang #define mtk_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type) \ 59*539061b8Skenny liang (((lvl0_state) << PSTATE_ID_SHIFT) | \ 60*539061b8Skenny liang ((pwr_lvl) << PSTATE_PWR_LVL_SHIFT) | \ 61*539061b8Skenny liang ((type) << PSTATE_TYPE_SHIFT)) 62*539061b8Skenny liang 63*539061b8Skenny liang #endif /* PSCI_EXTENDED_STATE_ID */ 64*539061b8Skenny liang 65*539061b8Skenny liang /* Make composite power state parameter till power level 1 */ 66*539061b8Skenny liang #define mtk_make_pwrstate_lvl1(lvl1_state, lvl0_state, pwr_lvl, type) \ 67*539061b8Skenny liang (((lvl1_state) << MTK_LOCAL_PSTATE_WIDTH) | \ 68*539061b8Skenny liang mtk_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type)) 69*539061b8Skenny liang 70*539061b8Skenny liang /* Make composite power state parameter till power level 2 */ 71*539061b8Skenny liang #define mtk_make_pwrstate_lvl2( \ 72*539061b8Skenny liang lvl2_state, lvl1_state, lvl0_state, pwr_lvl, type) \ 73*539061b8Skenny liang (((lvl2_state) << (MTK_LOCAL_PSTATE_WIDTH * 2)) | \ 74*539061b8Skenny liang mtk_make_pwrstate_lvl1(lvl1_state, lvl0_state, pwr_lvl, type)) 75*539061b8Skenny liang 76*539061b8Skenny liang #define MTK_PWR_LVL0 0 77*539061b8Skenny liang #define MTK_PWR_LVL1 1 78*539061b8Skenny liang #define MTK_PWR_LVL2 2 79*539061b8Skenny liang 80*539061b8Skenny liang /* Macros to read the MTK power domain state */ 81*539061b8Skenny liang #define MTK_CORE_PWR_STATE(state) (state)->pwr_domain_state[MTK_PWR_LVL0] 82*539061b8Skenny liang #define MTK_CLUSTER_PWR_STATE(state) (state)->pwr_domain_state[MTK_PWR_LVL1] 83*539061b8Skenny liang #define MTK_SYSTEM_PWR_STATE(state) ((PLAT_MAX_PWR_LVL > MTK_PWR_LVL1) ? \ 84*539061b8Skenny liang (state)->pwr_domain_state[MTK_PWR_LVL2] : 0) 85*539061b8Skenny liang 86*539061b8Skenny liang #if PSCI_EXTENDED_STATE_ID 87*539061b8Skenny liang /* 88*539061b8Skenny liang * The table storing the valid idle power states. Ensure that the 89*539061b8Skenny liang * array entries are populated in ascending order of state-id to 90*539061b8Skenny liang * enable us to use binary search during power state validation. 91*539061b8Skenny liang * The table must be terminated by a NULL entry. 92*539061b8Skenny liang */ 93*539061b8Skenny liang const unsigned int mtk_pm_idle_states[] = { 94*539061b8Skenny liang /* State-id - 0x001 */ 95*539061b8Skenny liang mtk_make_pwrstate_lvl2(MTK_LOCAL_STATE_RUN, MTK_LOCAL_STATE_RUN, 96*539061b8Skenny liang MTK_LOCAL_STATE_RET, MTK_PWR_LVL0, PSTATE_TYPE_STANDBY), 97*539061b8Skenny liang /* State-id - 0x002 */ 98*539061b8Skenny liang mtk_make_pwrstate_lvl2(MTK_LOCAL_STATE_RUN, MTK_LOCAL_STATE_RUN, 99*539061b8Skenny liang MTK_LOCAL_STATE_OFF, MTK_PWR_LVL0, PSTATE_TYPE_POWERDOWN), 100*539061b8Skenny liang /* State-id - 0x022 */ 101*539061b8Skenny liang mtk_make_pwrstate_lvl2(MTK_LOCAL_STATE_RUN, MTK_LOCAL_STATE_OFF, 102*539061b8Skenny liang MTK_LOCAL_STATE_OFF, MTK_PWR_LVL1, PSTATE_TYPE_POWERDOWN), 103*539061b8Skenny liang #if PLAT_MAX_PWR_LVL > MTK_PWR_LVL1 104*539061b8Skenny liang /* State-id - 0x222 */ 105*539061b8Skenny liang mtk_make_pwrstate_lvl2(MTK_LOCAL_STATE_OFF, MTK_LOCAL_STATE_OFF, 106*539061b8Skenny liang MTK_LOCAL_STATE_OFF, MTK_PWR_LVL2, PSTATE_TYPE_POWERDOWN), 107*539061b8Skenny liang #endif 108*539061b8Skenny liang 0, 109*539061b8Skenny liang }; 110*539061b8Skenny liang #endif 111*539061b8Skenny liang 112*539061b8Skenny liang #define CPU_IDX(cluster, cpu) ((cluster << 2) + cpu) 113*539061b8Skenny liang #define ON true 114*539061b8Skenny liang #define OFF false 115*539061b8Skenny liang 116*539061b8Skenny liang /* Pause MCDI when CPU hotplug */ 117*539061b8Skenny liang static bool HP_SSPM_PAUSE; 118*539061b8Skenny liang /* CPU Hotplug by SSPM */ 119*539061b8Skenny liang static bool HP_SSPM_CTRL = true; 120*539061b8Skenny liang /* Turn off cluster when CPU hotplug off */ 121*539061b8Skenny liang static bool HP_CLUSTER_OFF = true; 122*539061b8Skenny liang /* Turn off cluster when CPU MCDI off */ 123*539061b8Skenny liang static bool MCDI_C2 = true; 124*539061b8Skenny liang /* Enable MCDI */ 125*539061b8Skenny liang static bool MCDI_SSPM = true; 126*539061b8Skenny liang 1277352f329Skenny liang static uintptr_t secure_entrypoint; 1287352f329Skenny liang 1297352f329Skenny liang static void mp1_L2_desel_config(void) 1307352f329Skenny liang { 1317352f329Skenny liang mmio_write_64(MCUCFG_BASE + 0x2200, 0x2092c820); 1327352f329Skenny liang 1337352f329Skenny liang dsb(); 1347352f329Skenny liang } 1357352f329Skenny liang 136*539061b8Skenny liang static bool clst_single_pwr(int cluster, int cpu) 1377352f329Skenny liang { 138*539061b8Skenny liang uint32_t cpu_mask[2] = {0x00001e00, 0x000f0000}; 139*539061b8Skenny liang uint32_t cpu_pwr_bit[] = {9, 10, 11, 12, 16, 17, 18, 19}; 140*539061b8Skenny liang int my_idx = (cluster << 2) + cpu; 141*539061b8Skenny liang uint32_t pwr_stat = mmio_read_32(0x10006180); 1427352f329Skenny liang 143*539061b8Skenny liang return !(pwr_stat & (cpu_mask[cluster] & ~BIT(cpu_pwr_bit[my_idx]))); 144*539061b8Skenny liang } 1457352f329Skenny liang 146*539061b8Skenny liang static bool clst_single_on(int cluster, int cpu) 147*539061b8Skenny liang { 148*539061b8Skenny liang uint32_t cpu_mask[2] = {0x0f, 0xf0}; 149*539061b8Skenny liang int my_idx = (cluster << 2) + cpu; 150*539061b8Skenny liang uint32_t on_stat = mcdi_avail_cpu_mask_read(); 151*539061b8Skenny liang 152*539061b8Skenny liang return !(on_stat & (cpu_mask[cluster] & ~BIT(my_idx))); 153*539061b8Skenny liang } 154*539061b8Skenny liang 155*539061b8Skenny liang static void plat_cluster_pwrdwn_common(uint64_t mpidr, int cluster) 156*539061b8Skenny liang { 157*539061b8Skenny liang if (cluster > 0) 158*539061b8Skenny liang mt_gic_sync_dcm_enable(); 159*539061b8Skenny liang 160*539061b8Skenny liang /* Disable coherency */ 161*539061b8Skenny liang plat_mtk_cci_disable(); 162*539061b8Skenny liang disable_scu(mpidr); 163*539061b8Skenny liang } 164*539061b8Skenny liang 165*539061b8Skenny liang static void plat_cluster_pwron_common(uint64_t mpidr, int cluster) 166*539061b8Skenny liang { 167*539061b8Skenny liang if (cluster > 0) { 1687352f329Skenny liang l2c_parity_check_setup(); 1697352f329Skenny liang circular_buffer_setup(); 1707352f329Skenny liang mp1_L2_desel_config(); 1717352f329Skenny liang mt_gic_sync_dcm_disable(); 1727352f329Skenny liang } 173*539061b8Skenny liang 174*539061b8Skenny liang /* Enable coherency */ 175*539061b8Skenny liang enable_scu(mpidr); 176*539061b8Skenny liang plat_mtk_cci_enable(); 177*539061b8Skenny liang /* Enable big core dcm */ 178*539061b8Skenny liang plat_dcm_restore_cluster_on(mpidr); 179*539061b8Skenny liang /* Enable rgu dcm */ 180*539061b8Skenny liang plat_dcm_rgu_enable(); 1817352f329Skenny liang } 1827352f329Skenny liang 183*539061b8Skenny liang static void plat_cpu_standby(plat_local_state_t cpu_state) 184*539061b8Skenny liang { 185*539061b8Skenny liang unsigned int scr; 186*539061b8Skenny liang 187*539061b8Skenny liang scr = read_scr_el3(); 188*539061b8Skenny liang write_scr_el3(scr | SCR_IRQ_BIT | SCR_FIQ_BIT); 189*539061b8Skenny liang 190*539061b8Skenny liang isb(); 191*539061b8Skenny liang dsb(); 192*539061b8Skenny liang wfi(); 193*539061b8Skenny liang 194*539061b8Skenny liang write_scr_el3(scr); 195*539061b8Skenny liang } 196*539061b8Skenny liang 197*539061b8Skenny liang static void mcdi_ctrl_before_hotplug_on(int cluster, int cpu) 198*539061b8Skenny liang { 199*539061b8Skenny liang if (!HP_SSPM_CTRL && HP_SSPM_PAUSE && MCDI_SSPM) { 200*539061b8Skenny liang mcdi_pause_clr(cluster, CPU_IDX(cluster, cpu), OFF); 201*539061b8Skenny liang mcdi_pause_set(cluster, CPU_IDX(cluster, cpu), ON); 202*539061b8Skenny liang } 203*539061b8Skenny liang } 204*539061b8Skenny liang 205*539061b8Skenny liang static void mcdi_ctrl_before_hotplug_off(int cluster, int cpu, bool cluster_off) 206*539061b8Skenny liang { 207*539061b8Skenny liang if (!HP_SSPM_CTRL && HP_SSPM_PAUSE && MCDI_SSPM) 208*539061b8Skenny liang mcdi_pause_set(cluster_off ? cluster : -1, 209*539061b8Skenny liang CPU_IDX(cluster, cpu), OFF); 210*539061b8Skenny liang } 211*539061b8Skenny liang 212*539061b8Skenny liang static void mcdi_ctrl_cluster_cpu_off(int cluster, int cpu, bool cluster_off) 213*539061b8Skenny liang { 214*539061b8Skenny liang if (MCDI_SSPM) { 215*539061b8Skenny liang sspm_set_bootaddr(secure_entrypoint); 216*539061b8Skenny liang 217*539061b8Skenny liang sspm_standbywfi_irq_enable(CPU_IDX(cluster, cpu)); 218*539061b8Skenny liang 219*539061b8Skenny liang if (cluster_off) 220*539061b8Skenny liang sspm_cluster_pwr_off_notify(cluster); 221*539061b8Skenny liang else 222*539061b8Skenny liang sspm_cluster_pwr_on_notify(cluster); 223*539061b8Skenny liang } 224*539061b8Skenny liang } 225*539061b8Skenny liang 226*539061b8Skenny liang static void mcdi_ctrl_suspend(void) 227*539061b8Skenny liang { 228*539061b8Skenny liang if (MCDI_SSPM) 229*539061b8Skenny liang mcdi_pause(); 230*539061b8Skenny liang } 231*539061b8Skenny liang 232*539061b8Skenny liang static void mcdi_ctrl_resume(void) 233*539061b8Skenny liang { 234*539061b8Skenny liang if (MCDI_SSPM) 235*539061b8Skenny liang mcdi_unpause(); 236*539061b8Skenny liang } 237*539061b8Skenny liang 238*539061b8Skenny liang static void hotplug_ctrl_cluster_on(int cluster, int cpu) 239*539061b8Skenny liang { 240*539061b8Skenny liang if (HP_SSPM_CTRL && MCDI_SSPM) { 241*539061b8Skenny liang mcdi_hotplug_clr(cluster, CPU_IDX(cluster, cpu), OFF); 242*539061b8Skenny liang mcdi_hotplug_set(cluster, -1, ON); 243*539061b8Skenny liang mcdi_hotplug_wait_ack(cluster, -1, ON); 244*539061b8Skenny liang } else { 245*539061b8Skenny liang /* power on cluster */ 246*539061b8Skenny liang if (!spm_get_cluster_powerstate(cluster)) 247*539061b8Skenny liang spm_poweron_cluster(cluster); 248*539061b8Skenny liang } 249*539061b8Skenny liang } 250*539061b8Skenny liang 251*539061b8Skenny liang static void hotplug_ctrl_cpu_on(int cluster, int cpu) 252*539061b8Skenny liang { 253*539061b8Skenny liang if (HP_SSPM_CTRL && MCDI_SSPM) 254*539061b8Skenny liang mcdi_hotplug_set(cluster, CPU_IDX(cluster, cpu), ON); 255*539061b8Skenny liang else 256*539061b8Skenny liang spm_poweron_cpu(cluster, cpu); 257*539061b8Skenny liang } 258*539061b8Skenny liang 259*539061b8Skenny liang static void hotplug_ctrl_cpu_on_finish(int cluster, int cpu) 260*539061b8Skenny liang { 261*539061b8Skenny liang spm_disable_cpu_auto_off(cluster, cpu); 262*539061b8Skenny liang 263*539061b8Skenny liang if (HP_SSPM_CTRL && MCDI_SSPM) 264*539061b8Skenny liang mcdi_hotplug_clr(cluster, CPU_IDX(cluster, cpu), ON); 265*539061b8Skenny liang else if (HP_SSPM_PAUSE && MCDI_SSPM) 266*539061b8Skenny liang mcdi_pause_clr(cluster, CPU_IDX(cluster, cpu), ON); 267*539061b8Skenny liang 268*539061b8Skenny liang mcdi_avail_cpu_mask_set(BIT(CPU_IDX(cluster, cpu))); 269*539061b8Skenny liang } 270*539061b8Skenny liang 271*539061b8Skenny liang static void hotplug_ctrl_cluster_cpu_off(int cluster, int cpu, bool cluster_off) 272*539061b8Skenny liang { 273*539061b8Skenny liang mcdi_avail_cpu_mask_clr(BIT(CPU_IDX(cluster, cpu))); 274*539061b8Skenny liang 275*539061b8Skenny liang if (HP_SSPM_CTRL && MCDI_SSPM) { 276*539061b8Skenny liang mcdi_hotplug_set(cluster_off ? cluster : -1, 277*539061b8Skenny liang CPU_IDX(cluster, cpu), OFF); 278*539061b8Skenny liang } else { 279*539061b8Skenny liang spm_enable_cpu_auto_off(cluster, cpu); 280*539061b8Skenny liang 281*539061b8Skenny liang if (cluster_off) 282*539061b8Skenny liang spm_enable_cluster_auto_off(cluster); 283*539061b8Skenny liang 284*539061b8Skenny liang spm_set_cpu_power_off(cluster, cpu); 285*539061b8Skenny liang } 286*539061b8Skenny liang } 287*539061b8Skenny liang 288*539061b8Skenny liang static int plat_mtk_power_domain_on(unsigned long mpidr) 289*539061b8Skenny liang { 290*539061b8Skenny liang int cpu = MPIDR_AFFLVL0_VAL(mpidr); 291*539061b8Skenny liang int cluster = MPIDR_AFFLVL1_VAL(mpidr); 292*539061b8Skenny liang 293*539061b8Skenny liang mcdi_ctrl_before_hotplug_on(cluster, cpu); 294*539061b8Skenny liang hotplug_ctrl_cluster_on(cluster, cpu); 295*539061b8Skenny liang 2967352f329Skenny liang /* init cpu reset arch as AARCH64 */ 2977352f329Skenny liang mcucfg_init_archstate(cluster, cpu, 1); 2987352f329Skenny liang mcucfg_set_bootaddr(cluster, cpu, secure_entrypoint); 2997352f329Skenny liang 300*539061b8Skenny liang hotplug_ctrl_cpu_on(cluster, cpu); 3017352f329Skenny liang 3027352f329Skenny liang return PSCI_E_SUCCESS; 3037352f329Skenny liang } 3047352f329Skenny liang 3057352f329Skenny liang static void plat_mtk_power_domain_off(const psci_power_state_t *state) 3067352f329Skenny liang { 3077352f329Skenny liang uint64_t mpidr = read_mpidr(); 3087352f329Skenny liang int cpu = MPIDR_AFFLVL0_VAL(mpidr); 3097352f329Skenny liang int cluster = MPIDR_AFFLVL1_VAL(mpidr); 310*539061b8Skenny liang const plat_local_state_t *pds = state->pwr_domain_state; 311*539061b8Skenny liang bool afflvl1 = (pds[MPIDR_AFFLVL1] == MTK_LOCAL_STATE_OFF); 312*539061b8Skenny liang bool cluster_off = (HP_CLUSTER_OFF && afflvl1 && 313*539061b8Skenny liang clst_single_on(cluster, cpu)); 3147352f329Skenny liang 3157352f329Skenny liang mt_gic_cpuif_disable(); 3167352f329Skenny liang 317*539061b8Skenny liang if (cluster_off) 318*539061b8Skenny liang plat_cluster_pwrdwn_common(mpidr, cluster); 3197352f329Skenny liang 320*539061b8Skenny liang mcdi_ctrl_before_hotplug_off(cluster, cpu, cluster_off); 321*539061b8Skenny liang hotplug_ctrl_cluster_cpu_off(cluster, cpu, cluster_off); 3227352f329Skenny liang } 3237352f329Skenny liang 3247352f329Skenny liang static void plat_mtk_power_domain_on_finish(const psci_power_state_t *state) 3257352f329Skenny liang { 3267352f329Skenny liang uint64_t mpidr = read_mpidr(); 3277352f329Skenny liang int cpu = MPIDR_AFFLVL0_VAL(mpidr); 3287352f329Skenny liang int cluster = MPIDR_AFFLVL1_VAL(mpidr); 329*539061b8Skenny liang const plat_local_state_t *pds = state->pwr_domain_state; 330*539061b8Skenny liang bool afflvl1 = (pds[MPIDR_AFFLVL1] == MTK_LOCAL_STATE_OFF); 3317352f329Skenny liang 332*539061b8Skenny liang if (afflvl1) 333*539061b8Skenny liang plat_cluster_pwron_common(mpidr, cluster); 3347352f329Skenny liang 3357352f329Skenny liang mt_gic_pcpu_init(); 3367352f329Skenny liang mt_gic_cpuif_enable(); 337*539061b8Skenny liang 338*539061b8Skenny liang hotplug_ctrl_cpu_on_finish(cluster, cpu); 3397352f329Skenny liang } 3407352f329Skenny liang 341*539061b8Skenny liang static void plat_mtk_power_domain_suspend(const psci_power_state_t *state) 342*539061b8Skenny liang { 343*539061b8Skenny liang uint64_t mpidr = read_mpidr(); 344*539061b8Skenny liang int cpu = MPIDR_AFFLVL0_VAL(mpidr); 345*539061b8Skenny liang int cluster = MPIDR_AFFLVL1_VAL(mpidr); 346*539061b8Skenny liang const plat_local_state_t *pds = state->pwr_domain_state; 347*539061b8Skenny liang bool afflvl1 = (pds[MPIDR_AFFLVL1] == MTK_LOCAL_STATE_OFF); 348*539061b8Skenny liang bool afflvl2 = (pds[MPIDR_AFFLVL2] == MTK_LOCAL_STATE_OFF); 349*539061b8Skenny liang bool cluster_off = MCDI_C2 && afflvl1 && clst_single_pwr(cluster, cpu); 350*539061b8Skenny liang 351*539061b8Skenny liang /* init cpu reset arch as AARCH64 */ 352*539061b8Skenny liang mcucfg_init_archstate(cluster, cpu, 1); 353*539061b8Skenny liang mcucfg_set_bootaddr(cluster, cpu, secure_entrypoint); 354*539061b8Skenny liang 355*539061b8Skenny liang mt_gic_cpuif_disable(); 356*539061b8Skenny liang mt_gic_irq_save(); 357*539061b8Skenny liang plat_dcm_mcsi_a_backup(); 358*539061b8Skenny liang 359*539061b8Skenny liang if (cluster_off || afflvl2) 360*539061b8Skenny liang plat_cluster_pwrdwn_common(mpidr, cluster); 361*539061b8Skenny liang 362*539061b8Skenny liang if (afflvl2) { 363*539061b8Skenny liang spm_data_t spm_d = { .cmd = SPM_SUSPEND }; 364*539061b8Skenny liang uint32_t *d = (uint32_t *)&spm_d; 365*539061b8Skenny liang uint32_t l = sizeof(spm_d) / sizeof(uint32_t); 366*539061b8Skenny liang 367*539061b8Skenny liang mcdi_ctrl_suspend(); 368*539061b8Skenny liang 369*539061b8Skenny liang spm_set_bootaddr(secure_entrypoint); 370*539061b8Skenny liang 371*539061b8Skenny liang if (MCDI_SSPM) 372*539061b8Skenny liang sspm_ipi_send_non_blocking(IPI_ID_SUSPEND, d); 373*539061b8Skenny liang 374*539061b8Skenny liang spm_system_suspend(); 375*539061b8Skenny liang 376*539061b8Skenny liang if (MCDI_SSPM) 377*539061b8Skenny liang while (sspm_ipi_recv_non_blocking(IPI_ID_SUSPEND, d, l)) 378*539061b8Skenny liang ; 379*539061b8Skenny liang } else { 380*539061b8Skenny liang mcdi_ctrl_cluster_cpu_off(cluster, cpu, cluster_off); 381*539061b8Skenny liang } 382*539061b8Skenny liang } 383*539061b8Skenny liang 384*539061b8Skenny liang static void plat_mtk_power_domain_suspend_finish(const psci_power_state_t *state) 385*539061b8Skenny liang { 386*539061b8Skenny liang uint64_t mpidr = read_mpidr(); 387*539061b8Skenny liang int cluster = MPIDR_AFFLVL1_VAL(mpidr); 388*539061b8Skenny liang const plat_local_state_t *pds = state->pwr_domain_state; 389*539061b8Skenny liang bool afflvl2 = (pds[MPIDR_AFFLVL2] == MTK_LOCAL_STATE_OFF); 390*539061b8Skenny liang 391*539061b8Skenny liang if (afflvl2) { 392*539061b8Skenny liang spm_data_t spm_d = { .cmd = SPM_RESUME }; 393*539061b8Skenny liang uint32_t *d = (uint32_t *)&spm_d; 394*539061b8Skenny liang uint32_t l = sizeof(spm_d) / sizeof(uint32_t); 395*539061b8Skenny liang 396*539061b8Skenny liang mt_gic_init(); 397*539061b8Skenny liang mt_gic_irq_restore(); 398*539061b8Skenny liang mmio_write_32(EMI_WFIFO, 0xf); 399*539061b8Skenny liang 400*539061b8Skenny liang if (MCDI_SSPM) 401*539061b8Skenny liang sspm_ipi_send_non_blocking(IPI_ID_SUSPEND, d); 402*539061b8Skenny liang 403*539061b8Skenny liang spm_system_suspend_finish(); 404*539061b8Skenny liang 405*539061b8Skenny liang if (MCDI_SSPM) 406*539061b8Skenny liang while (sspm_ipi_recv_non_blocking(IPI_ID_SUSPEND, d, l)) 407*539061b8Skenny liang ; 408*539061b8Skenny liang 409*539061b8Skenny liang mcdi_ctrl_resume(); 410*539061b8Skenny liang } 411*539061b8Skenny liang 412*539061b8Skenny liang plat_cluster_pwron_common(mpidr, cluster); 413*539061b8Skenny liang 414*539061b8Skenny liang plat_dcm_mcsi_a_restore(); 415*539061b8Skenny liang } 416*539061b8Skenny liang 417*539061b8Skenny liang #if PSCI_EXTENDED_STATE_ID 418*539061b8Skenny liang 419*539061b8Skenny liang static int plat_mtk_validate_power_state(unsigned int power_state, 420*539061b8Skenny liang psci_power_state_t *req_state) 421*539061b8Skenny liang { 422*539061b8Skenny liang unsigned int state_id; 423*539061b8Skenny liang int i; 424*539061b8Skenny liang 425*539061b8Skenny liang assert(req_state); 426*539061b8Skenny liang 427*539061b8Skenny liang if (!MCDI_SSPM) 428*539061b8Skenny liang return PSCI_E_INVALID_PARAMS; 429*539061b8Skenny liang 430*539061b8Skenny liang /* 431*539061b8Skenny liang * Currently we are using a linear search for finding the matching 432*539061b8Skenny liang * entry in the idle power state array. This can be made a binary 433*539061b8Skenny liang * search if the number of entries justify the additional complexity. 434*539061b8Skenny liang */ 435*539061b8Skenny liang for (i = 0; !!mtk_pm_idle_states[i]; i++) { 436*539061b8Skenny liang if (power_state == mtk_pm_idle_states[i]) 437*539061b8Skenny liang break; 438*539061b8Skenny liang } 439*539061b8Skenny liang 440*539061b8Skenny liang /* Return error if entry not found in the idle state array */ 441*539061b8Skenny liang if (!mtk_pm_idle_states[i]) 442*539061b8Skenny liang return PSCI_E_INVALID_PARAMS; 443*539061b8Skenny liang 444*539061b8Skenny liang i = 0; 445*539061b8Skenny liang state_id = psci_get_pstate_id(power_state); 446*539061b8Skenny liang 447*539061b8Skenny liang /* Parse the State ID and populate the state info parameter */ 448*539061b8Skenny liang while (state_id) { 449*539061b8Skenny liang req_state->pwr_domain_state[i++] = state_id & 450*539061b8Skenny liang MTK_LOCAL_PSTATE_MASK; 451*539061b8Skenny liang state_id >>= MTK_LOCAL_PSTATE_WIDTH; 452*539061b8Skenny liang } 453*539061b8Skenny liang 454*539061b8Skenny liang return PSCI_E_SUCCESS; 455*539061b8Skenny liang } 456*539061b8Skenny liang 457*539061b8Skenny liang #else /* if !PSCI_EXTENDED_STATE_ID */ 458*539061b8Skenny liang 459*539061b8Skenny liang static int plat_mtk_validate_power_state(unsigned int power_state, 460*539061b8Skenny liang psci_power_state_t *req_state) 461*539061b8Skenny liang { 462*539061b8Skenny liang int pstate = psci_get_pstate_type(power_state); 463*539061b8Skenny liang int pwr_lvl = psci_get_pstate_pwrlvl(power_state); 464*539061b8Skenny liang int i; 465*539061b8Skenny liang 466*539061b8Skenny liang assert(req_state); 467*539061b8Skenny liang 468*539061b8Skenny liang if (pwr_lvl > PLAT_MAX_PWR_LVL) 469*539061b8Skenny liang return PSCI_E_INVALID_PARAMS; 470*539061b8Skenny liang 471*539061b8Skenny liang /* Sanity check the requested state */ 472*539061b8Skenny liang if (pstate == PSTATE_TYPE_STANDBY) { 473*539061b8Skenny liang /* 474*539061b8Skenny liang * It's possible to enter standby only on power level 0 475*539061b8Skenny liang * Ignore any other power level. 476*539061b8Skenny liang */ 477*539061b8Skenny liang if (pwr_lvl != 0) 478*539061b8Skenny liang return PSCI_E_INVALID_PARAMS; 479*539061b8Skenny liang 480*539061b8Skenny liang req_state->pwr_domain_state[MTK_PWR_LVL0] = MTK_LOCAL_STATE_RET; 481*539061b8Skenny liang } else if (!MCDI_SSPM) { 482*539061b8Skenny liang return PSCI_E_INVALID_PARAMS; 483*539061b8Skenny liang } else { 484*539061b8Skenny liang for (i = 0; i <= pwr_lvl; i++) 485*539061b8Skenny liang req_state->pwr_domain_state[i] = MTK_LOCAL_STATE_OFF; 486*539061b8Skenny liang } 487*539061b8Skenny liang 488*539061b8Skenny liang return PSCI_E_SUCCESS; 489*539061b8Skenny liang } 490*539061b8Skenny liang 491*539061b8Skenny liang #endif /* PSCI_EXTENDED_STATE_ID */ 492*539061b8Skenny liang 4933fa9dec4Skenny liang /******************************************************************************* 494e977b4dbSkenny liang * MTK handlers to shutdown/reboot the system 495e977b4dbSkenny liang ******************************************************************************/ 496e977b4dbSkenny liang static void __dead2 plat_mtk_system_off(void) 497e977b4dbSkenny liang { 498e977b4dbSkenny liang INFO("MTK System Off\n"); 499e977b4dbSkenny liang 500e977b4dbSkenny liang rtc_power_off_sequence(); 501e977b4dbSkenny liang wk_pmic_enable_sdn_delay(); 502e977b4dbSkenny liang pmic_power_off(); 503e977b4dbSkenny liang 504e977b4dbSkenny liang wfi(); 505e977b4dbSkenny liang ERROR("MTK System Off: operation not handled.\n"); 506e977b4dbSkenny liang panic(); 507e977b4dbSkenny liang } 508e977b4dbSkenny liang 5093d91c9c3Skenny liang static void __dead2 plat_mtk_system_reset(void) 5103d91c9c3Skenny liang { 5113d91c9c3Skenny liang struct bl_aux_gpio_info *gpio_reset = plat_get_mtk_gpio_reset(); 5123d91c9c3Skenny liang 5133d91c9c3Skenny liang INFO("MTK System Reset\n"); 5143d91c9c3Skenny liang 5153d91c9c3Skenny liang mt_set_gpio_out(gpio_reset->index, gpio_reset->polarity); 5163d91c9c3Skenny liang 5173d91c9c3Skenny liang wfi(); 5183d91c9c3Skenny liang ERROR("MTK System Reset: operation not handled.\n"); 5193d91c9c3Skenny liang panic(); 5203d91c9c3Skenny liang } 5213d91c9c3Skenny liang 5223c25ba44Skenny liang static void plat_mtk_get_sys_suspend_power_state(psci_power_state_t *req_state) 5233c25ba44Skenny liang { 5243c25ba44Skenny liang assert(PLAT_MAX_PWR_LVL >= 2); 5253c25ba44Skenny liang 5263c25ba44Skenny liang for (int i = MPIDR_AFFLVL0; i <= PLAT_MAX_PWR_LVL; i++) 5273c25ba44Skenny liang req_state->pwr_domain_state[i] = MTK_LOCAL_STATE_OFF; 5283c25ba44Skenny liang } 5293c25ba44Skenny liang 530e977b4dbSkenny liang /******************************************************************************* 5313fa9dec4Skenny liang * MTK_platform handler called when an affinity instance is about to be turned 5323fa9dec4Skenny liang * on. The level and mpidr determine the affinity instance. 5333fa9dec4Skenny liang ******************************************************************************/ 5343fa9dec4Skenny liang static const plat_psci_ops_t plat_plat_pm_ops = { 535*539061b8Skenny liang .cpu_standby = plat_cpu_standby, 5367352f329Skenny liang .pwr_domain_on = plat_mtk_power_domain_on, 5377352f329Skenny liang .pwr_domain_on_finish = plat_mtk_power_domain_on_finish, 5387352f329Skenny liang .pwr_domain_off = plat_mtk_power_domain_off, 5393c25ba44Skenny liang .pwr_domain_suspend = plat_mtk_power_domain_suspend, 5403c25ba44Skenny liang .pwr_domain_suspend_finish = plat_mtk_power_domain_suspend_finish, 541e977b4dbSkenny liang .system_off = plat_mtk_system_off, 5423d91c9c3Skenny liang .system_reset = plat_mtk_system_reset, 543*539061b8Skenny liang .validate_power_state = plat_mtk_validate_power_state, 5443c25ba44Skenny liang .get_sys_suspend_power_state = plat_mtk_get_sys_suspend_power_state, 5453fa9dec4Skenny liang }; 5463fa9dec4Skenny liang 5473fa9dec4Skenny liang int plat_setup_psci_ops(uintptr_t sec_entrypoint, 5483fa9dec4Skenny liang const plat_psci_ops_t **psci_ops) 5493fa9dec4Skenny liang { 5503fa9dec4Skenny liang *psci_ops = &plat_plat_pm_ops; 5513fa9dec4Skenny liang secure_entrypoint = sec_entrypoint; 552*539061b8Skenny liang 553*539061b8Skenny liang if (!check_mcdi_ctl_stat()) { 554*539061b8Skenny liang HP_SSPM_CTRL = false; 555*539061b8Skenny liang MCDI_SSPM = false; 556*539061b8Skenny liang } 557*539061b8Skenny liang 5583fa9dec4Skenny liang return 0; 5593fa9dec4Skenny liang } 560