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> 19539061b8Skenny 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> 31539061b8Skenny liang #include <sspm.h> 32e977b4dbSkenny liang #include <rtc.h> 333fa9dec4Skenny liang 34539061b8Skenny liang /* Local power state for power domains in Run state. */ 35539061b8Skenny liang #define MTK_LOCAL_STATE_RUN 0 36539061b8Skenny liang /* Local power state for retention. */ 37539061b8Skenny liang #define MTK_LOCAL_STATE_RET 1 38539061b8Skenny liang /* Local power state for OFF/power-down. */ 397352f329Skenny liang #define MTK_LOCAL_STATE_OFF 2 407352f329Skenny liang 41539061b8Skenny liang #if PSCI_EXTENDED_STATE_ID 42539061b8Skenny liang /* 43539061b8Skenny liang * Macros used to parse state information from State-ID if it is using the 44539061b8Skenny liang * recommended encoding for State-ID. 45539061b8Skenny liang */ 46539061b8Skenny liang #define MTK_LOCAL_PSTATE_WIDTH 4 47539061b8Skenny liang #define MTK_LOCAL_PSTATE_MASK ((1 << MTK_LOCAL_PSTATE_WIDTH) - 1) 48539061b8Skenny liang 49539061b8Skenny liang /* Macros to construct the composite power state */ 50539061b8Skenny liang 51539061b8Skenny liang /* Make composite power state parameter till power level 0 */ 52539061b8Skenny liang 53539061b8Skenny liang #define mtk_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type) \ 54539061b8Skenny liang (((lvl0_state) << PSTATE_ID_SHIFT) | ((type) << PSTATE_TYPE_SHIFT)) 55539061b8Skenny liang 56539061b8Skenny liang #else /* !PSCI_EXTENDED_STATE_ID */ 57539061b8Skenny liang 58539061b8Skenny liang #define mtk_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type) \ 59539061b8Skenny liang (((lvl0_state) << PSTATE_ID_SHIFT) | \ 60539061b8Skenny liang ((pwr_lvl) << PSTATE_PWR_LVL_SHIFT) | \ 61539061b8Skenny liang ((type) << PSTATE_TYPE_SHIFT)) 62539061b8Skenny liang 63539061b8Skenny liang #endif /* PSCI_EXTENDED_STATE_ID */ 64539061b8Skenny liang 65539061b8Skenny liang /* Make composite power state parameter till power level 1 */ 66539061b8Skenny liang #define mtk_make_pwrstate_lvl1(lvl1_state, lvl0_state, pwr_lvl, type) \ 67539061b8Skenny liang (((lvl1_state) << MTK_LOCAL_PSTATE_WIDTH) | \ 68539061b8Skenny liang mtk_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type)) 69539061b8Skenny liang 70539061b8Skenny liang /* Make composite power state parameter till power level 2 */ 71539061b8Skenny liang #define mtk_make_pwrstate_lvl2( \ 72539061b8Skenny liang lvl2_state, lvl1_state, lvl0_state, pwr_lvl, type) \ 73539061b8Skenny liang (((lvl2_state) << (MTK_LOCAL_PSTATE_WIDTH * 2)) | \ 74539061b8Skenny liang mtk_make_pwrstate_lvl1(lvl1_state, lvl0_state, pwr_lvl, type)) 75539061b8Skenny liang 76539061b8Skenny liang #define MTK_PWR_LVL0 0 77539061b8Skenny liang #define MTK_PWR_LVL1 1 78539061b8Skenny liang #define MTK_PWR_LVL2 2 79539061b8Skenny liang 80539061b8Skenny liang /* Macros to read the MTK power domain state */ 81539061b8Skenny liang #define MTK_CORE_PWR_STATE(state) (state)->pwr_domain_state[MTK_PWR_LVL0] 82539061b8Skenny liang #define MTK_CLUSTER_PWR_STATE(state) (state)->pwr_domain_state[MTK_PWR_LVL1] 83539061b8Skenny liang #define MTK_SYSTEM_PWR_STATE(state) ((PLAT_MAX_PWR_LVL > MTK_PWR_LVL1) ? \ 84539061b8Skenny liang (state)->pwr_domain_state[MTK_PWR_LVL2] : 0) 85539061b8Skenny liang 86539061b8Skenny liang #if PSCI_EXTENDED_STATE_ID 87539061b8Skenny liang /* 88539061b8Skenny liang * The table storing the valid idle power states. Ensure that the 89539061b8Skenny liang * array entries are populated in ascending order of state-id to 90539061b8Skenny liang * enable us to use binary search during power state validation. 91539061b8Skenny liang * The table must be terminated by a NULL entry. 92539061b8Skenny liang */ 93539061b8Skenny liang const unsigned int mtk_pm_idle_states[] = { 94539061b8Skenny liang /* State-id - 0x001 */ 95539061b8Skenny liang mtk_make_pwrstate_lvl2(MTK_LOCAL_STATE_RUN, MTK_LOCAL_STATE_RUN, 96539061b8Skenny liang MTK_LOCAL_STATE_RET, MTK_PWR_LVL0, PSTATE_TYPE_STANDBY), 97539061b8Skenny liang /* State-id - 0x002 */ 98539061b8Skenny liang mtk_make_pwrstate_lvl2(MTK_LOCAL_STATE_RUN, MTK_LOCAL_STATE_RUN, 99539061b8Skenny liang MTK_LOCAL_STATE_OFF, MTK_PWR_LVL0, PSTATE_TYPE_POWERDOWN), 100539061b8Skenny liang /* State-id - 0x022 */ 101539061b8Skenny liang mtk_make_pwrstate_lvl2(MTK_LOCAL_STATE_RUN, MTK_LOCAL_STATE_OFF, 102539061b8Skenny liang MTK_LOCAL_STATE_OFF, MTK_PWR_LVL1, PSTATE_TYPE_POWERDOWN), 103539061b8Skenny liang #if PLAT_MAX_PWR_LVL > MTK_PWR_LVL1 104539061b8Skenny liang /* State-id - 0x222 */ 105539061b8Skenny liang mtk_make_pwrstate_lvl2(MTK_LOCAL_STATE_OFF, MTK_LOCAL_STATE_OFF, 106539061b8Skenny liang MTK_LOCAL_STATE_OFF, MTK_PWR_LVL2, PSTATE_TYPE_POWERDOWN), 107539061b8Skenny liang #endif 108539061b8Skenny liang 0, 109539061b8Skenny liang }; 110539061b8Skenny liang #endif 111539061b8Skenny liang 112539061b8Skenny liang #define CPU_IDX(cluster, cpu) ((cluster << 2) + cpu) 113539061b8Skenny liang #define ON true 114539061b8Skenny liang #define OFF false 115539061b8Skenny liang 116539061b8Skenny liang /* Pause MCDI when CPU hotplug */ 117539061b8Skenny liang static bool HP_SSPM_PAUSE; 118539061b8Skenny liang /* CPU Hotplug by SSPM */ 119539061b8Skenny liang static bool HP_SSPM_CTRL = true; 120539061b8Skenny liang /* Turn off cluster when CPU hotplug off */ 121539061b8Skenny liang static bool HP_CLUSTER_OFF = true; 122539061b8Skenny liang /* Turn off cluster when CPU MCDI off */ 123539061b8Skenny liang static bool MCDI_C2 = true; 124539061b8Skenny liang /* Enable MCDI */ 125539061b8Skenny liang static bool MCDI_SSPM = true; 126539061b8Skenny 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 136539061b8Skenny liang static bool clst_single_pwr(int cluster, int cpu) 1377352f329Skenny liang { 138539061b8Skenny liang uint32_t cpu_mask[2] = {0x00001e00, 0x000f0000}; 139539061b8Skenny liang uint32_t cpu_pwr_bit[] = {9, 10, 11, 12, 16, 17, 18, 19}; 140539061b8Skenny liang int my_idx = (cluster << 2) + cpu; 141539061b8Skenny liang uint32_t pwr_stat = mmio_read_32(0x10006180); 1427352f329Skenny liang 143539061b8Skenny liang return !(pwr_stat & (cpu_mask[cluster] & ~BIT(cpu_pwr_bit[my_idx]))); 144539061b8Skenny liang } 1457352f329Skenny liang 146539061b8Skenny liang static bool clst_single_on(int cluster, int cpu) 147539061b8Skenny liang { 148539061b8Skenny liang uint32_t cpu_mask[2] = {0x0f, 0xf0}; 149539061b8Skenny liang int my_idx = (cluster << 2) + cpu; 150539061b8Skenny liang uint32_t on_stat = mcdi_avail_cpu_mask_read(); 151539061b8Skenny liang 152539061b8Skenny liang return !(on_stat & (cpu_mask[cluster] & ~BIT(my_idx))); 153539061b8Skenny liang } 154539061b8Skenny liang 1554450a518Skenny liang static void plat_cpu_pwrdwn_common(void) 1564450a518Skenny liang { 1574450a518Skenny liang /* Prevent interrupts from spuriously waking up this cpu */ 1584450a518Skenny liang mt_gic_rdistif_save(); 1594450a518Skenny liang mt_gic_cpuif_disable(); 1604450a518Skenny liang } 1614450a518Skenny liang 1624450a518Skenny liang static void plat_cpu_pwron_common(void) 1634450a518Skenny liang { 1644450a518Skenny liang /* Enable the gic cpu interface */ 1654450a518Skenny liang mt_gic_cpuif_enable(); 1664450a518Skenny liang mt_gic_rdistif_init(); 1674450a518Skenny liang mt_gic_rdistif_restore(); 1684450a518Skenny liang } 1694450a518Skenny liang 170539061b8Skenny liang static void plat_cluster_pwrdwn_common(uint64_t mpidr, int cluster) 171539061b8Skenny liang { 172539061b8Skenny liang if (cluster > 0) 173539061b8Skenny liang mt_gic_sync_dcm_enable(); 174539061b8Skenny liang 175539061b8Skenny liang /* Disable coherency */ 176539061b8Skenny liang plat_mtk_cci_disable(); 177539061b8Skenny liang disable_scu(mpidr); 178539061b8Skenny liang } 179539061b8Skenny liang 180539061b8Skenny liang static void plat_cluster_pwron_common(uint64_t mpidr, int cluster) 181539061b8Skenny liang { 182539061b8Skenny liang if (cluster > 0) { 1837352f329Skenny liang l2c_parity_check_setup(); 1847352f329Skenny liang circular_buffer_setup(); 1857352f329Skenny liang mp1_L2_desel_config(); 1867352f329Skenny liang mt_gic_sync_dcm_disable(); 1877352f329Skenny liang } 188539061b8Skenny liang 189539061b8Skenny liang /* Enable coherency */ 190539061b8Skenny liang enable_scu(mpidr); 191539061b8Skenny liang plat_mtk_cci_enable(); 192539061b8Skenny liang /* Enable big core dcm */ 193539061b8Skenny liang plat_dcm_restore_cluster_on(mpidr); 194539061b8Skenny liang /* Enable rgu dcm */ 195539061b8Skenny liang plat_dcm_rgu_enable(); 1967352f329Skenny liang } 1977352f329Skenny liang 198539061b8Skenny liang static void plat_cpu_standby(plat_local_state_t cpu_state) 199539061b8Skenny liang { 200539061b8Skenny liang unsigned int scr; 201539061b8Skenny liang 202539061b8Skenny liang scr = read_scr_el3(); 203539061b8Skenny liang write_scr_el3(scr | SCR_IRQ_BIT | SCR_FIQ_BIT); 204539061b8Skenny liang 205539061b8Skenny liang isb(); 206539061b8Skenny liang dsb(); 207539061b8Skenny liang wfi(); 208539061b8Skenny liang 209539061b8Skenny liang write_scr_el3(scr); 210539061b8Skenny liang } 211539061b8Skenny liang 212539061b8Skenny liang static void mcdi_ctrl_before_hotplug_on(int cluster, int cpu) 213539061b8Skenny liang { 214539061b8Skenny liang if (!HP_SSPM_CTRL && HP_SSPM_PAUSE && MCDI_SSPM) { 215539061b8Skenny liang mcdi_pause_clr(cluster, CPU_IDX(cluster, cpu), OFF); 216539061b8Skenny liang mcdi_pause_set(cluster, CPU_IDX(cluster, cpu), ON); 217539061b8Skenny liang } 218539061b8Skenny liang } 219539061b8Skenny liang 220539061b8Skenny liang static void mcdi_ctrl_before_hotplug_off(int cluster, int cpu, bool cluster_off) 221539061b8Skenny liang { 222539061b8Skenny liang if (!HP_SSPM_CTRL && HP_SSPM_PAUSE && MCDI_SSPM) 223539061b8Skenny liang mcdi_pause_set(cluster_off ? cluster : -1, 224539061b8Skenny liang CPU_IDX(cluster, cpu), OFF); 225539061b8Skenny liang } 226539061b8Skenny liang 227539061b8Skenny liang static void mcdi_ctrl_cluster_cpu_off(int cluster, int cpu, bool cluster_off) 228539061b8Skenny liang { 229539061b8Skenny liang if (MCDI_SSPM) { 230539061b8Skenny liang sspm_set_bootaddr(secure_entrypoint); 231539061b8Skenny liang 232539061b8Skenny liang sspm_standbywfi_irq_enable(CPU_IDX(cluster, cpu)); 233539061b8Skenny liang 234539061b8Skenny liang if (cluster_off) 235539061b8Skenny liang sspm_cluster_pwr_off_notify(cluster); 236539061b8Skenny liang else 237539061b8Skenny liang sspm_cluster_pwr_on_notify(cluster); 238539061b8Skenny liang } 239539061b8Skenny liang } 240539061b8Skenny liang 241539061b8Skenny liang static void mcdi_ctrl_suspend(void) 242539061b8Skenny liang { 243539061b8Skenny liang if (MCDI_SSPM) 244539061b8Skenny liang mcdi_pause(); 245539061b8Skenny liang } 246539061b8Skenny liang 247539061b8Skenny liang static void mcdi_ctrl_resume(void) 248539061b8Skenny liang { 249539061b8Skenny liang if (MCDI_SSPM) 250539061b8Skenny liang mcdi_unpause(); 251539061b8Skenny liang } 252539061b8Skenny liang 253539061b8Skenny liang static void hotplug_ctrl_cluster_on(int cluster, int cpu) 254539061b8Skenny liang { 255539061b8Skenny liang if (HP_SSPM_CTRL && MCDI_SSPM) { 256539061b8Skenny liang mcdi_hotplug_clr(cluster, CPU_IDX(cluster, cpu), OFF); 257539061b8Skenny liang mcdi_hotplug_set(cluster, -1, ON); 258539061b8Skenny liang mcdi_hotplug_wait_ack(cluster, -1, ON); 259539061b8Skenny liang } else { 260539061b8Skenny liang /* power on cluster */ 261539061b8Skenny liang if (!spm_get_cluster_powerstate(cluster)) 262539061b8Skenny liang spm_poweron_cluster(cluster); 263539061b8Skenny liang } 264539061b8Skenny liang } 265539061b8Skenny liang 266539061b8Skenny liang static void hotplug_ctrl_cpu_on(int cluster, int cpu) 267539061b8Skenny liang { 268539061b8Skenny liang if (HP_SSPM_CTRL && MCDI_SSPM) 269539061b8Skenny liang mcdi_hotplug_set(cluster, CPU_IDX(cluster, cpu), ON); 270539061b8Skenny liang else 271539061b8Skenny liang spm_poweron_cpu(cluster, cpu); 272539061b8Skenny liang } 273539061b8Skenny liang 274539061b8Skenny liang static void hotplug_ctrl_cpu_on_finish(int cluster, int cpu) 275539061b8Skenny liang { 276539061b8Skenny liang spm_disable_cpu_auto_off(cluster, cpu); 277539061b8Skenny liang 278539061b8Skenny liang if (HP_SSPM_CTRL && MCDI_SSPM) 279539061b8Skenny liang mcdi_hotplug_clr(cluster, CPU_IDX(cluster, cpu), ON); 280539061b8Skenny liang else if (HP_SSPM_PAUSE && MCDI_SSPM) 281539061b8Skenny liang mcdi_pause_clr(cluster, CPU_IDX(cluster, cpu), ON); 282539061b8Skenny liang 283539061b8Skenny liang mcdi_avail_cpu_mask_set(BIT(CPU_IDX(cluster, cpu))); 284539061b8Skenny liang } 285539061b8Skenny liang 286539061b8Skenny liang static void hotplug_ctrl_cluster_cpu_off(int cluster, int cpu, bool cluster_off) 287539061b8Skenny liang { 288539061b8Skenny liang mcdi_avail_cpu_mask_clr(BIT(CPU_IDX(cluster, cpu))); 289539061b8Skenny liang 290539061b8Skenny liang if (HP_SSPM_CTRL && MCDI_SSPM) { 291539061b8Skenny liang mcdi_hotplug_set(cluster_off ? cluster : -1, 292539061b8Skenny liang CPU_IDX(cluster, cpu), OFF); 293539061b8Skenny liang } else { 294539061b8Skenny liang spm_enable_cpu_auto_off(cluster, cpu); 295539061b8Skenny liang 296539061b8Skenny liang if (cluster_off) 297539061b8Skenny liang spm_enable_cluster_auto_off(cluster); 298539061b8Skenny liang 299539061b8Skenny liang spm_set_cpu_power_off(cluster, cpu); 300539061b8Skenny liang } 301539061b8Skenny liang } 302539061b8Skenny liang 303539061b8Skenny liang static int plat_mtk_power_domain_on(unsigned long mpidr) 304539061b8Skenny liang { 305539061b8Skenny liang int cpu = MPIDR_AFFLVL0_VAL(mpidr); 306539061b8Skenny liang int cluster = MPIDR_AFFLVL1_VAL(mpidr); 307*c6e0a64dSJames Liao int clst_pwr = spm_get_cluster_powerstate(cluster); 308*c6e0a64dSJames Liao unsigned int i; 309539061b8Skenny liang 310539061b8Skenny liang mcdi_ctrl_before_hotplug_on(cluster, cpu); 311539061b8Skenny liang hotplug_ctrl_cluster_on(cluster, cpu); 312539061b8Skenny liang 313*c6e0a64dSJames Liao if (clst_pwr == 0) { 314*c6e0a64dSJames Liao /* init cpu reset arch as AARCH64 of cluster */ 315*c6e0a64dSJames Liao for (i = 0; i < PLATFORM_MAX_CPUS_PER_CLUSTER; i++) { 316*c6e0a64dSJames Liao mcucfg_init_archstate(cluster, i, 1); 317*c6e0a64dSJames Liao mcucfg_set_bootaddr(cluster, i, secure_entrypoint); 318*c6e0a64dSJames Liao } 319*c6e0a64dSJames Liao } 3207352f329Skenny liang 321539061b8Skenny liang hotplug_ctrl_cpu_on(cluster, cpu); 3227352f329Skenny liang 3237352f329Skenny liang return PSCI_E_SUCCESS; 3247352f329Skenny liang } 3257352f329Skenny liang 3267352f329Skenny liang static void plat_mtk_power_domain_off(const psci_power_state_t *state) 3277352f329Skenny liang { 3287352f329Skenny liang uint64_t mpidr = read_mpidr(); 3297352f329Skenny liang int cpu = MPIDR_AFFLVL0_VAL(mpidr); 3307352f329Skenny liang int cluster = MPIDR_AFFLVL1_VAL(mpidr); 331539061b8Skenny liang const plat_local_state_t *pds = state->pwr_domain_state; 332539061b8Skenny liang bool afflvl1 = (pds[MPIDR_AFFLVL1] == MTK_LOCAL_STATE_OFF); 333539061b8Skenny liang bool cluster_off = (HP_CLUSTER_OFF && afflvl1 && 334539061b8Skenny liang clst_single_on(cluster, cpu)); 3357352f329Skenny liang 3364450a518Skenny liang plat_cpu_pwrdwn_common(); 3377352f329Skenny liang 338539061b8Skenny liang if (cluster_off) 339539061b8Skenny liang plat_cluster_pwrdwn_common(mpidr, cluster); 3407352f329Skenny liang 341539061b8Skenny liang mcdi_ctrl_before_hotplug_off(cluster, cpu, cluster_off); 342539061b8Skenny liang hotplug_ctrl_cluster_cpu_off(cluster, cpu, cluster_off); 3437352f329Skenny liang } 3447352f329Skenny liang 3457352f329Skenny liang static void plat_mtk_power_domain_on_finish(const psci_power_state_t *state) 3467352f329Skenny liang { 3477352f329Skenny liang uint64_t mpidr = read_mpidr(); 3487352f329Skenny liang int cpu = MPIDR_AFFLVL0_VAL(mpidr); 3497352f329Skenny liang int cluster = MPIDR_AFFLVL1_VAL(mpidr); 350539061b8Skenny liang const plat_local_state_t *pds = state->pwr_domain_state; 351539061b8Skenny liang bool afflvl1 = (pds[MPIDR_AFFLVL1] == MTK_LOCAL_STATE_OFF); 3527352f329Skenny liang 353539061b8Skenny liang if (afflvl1) 354539061b8Skenny liang plat_cluster_pwron_common(mpidr, cluster); 3557352f329Skenny liang 3564450a518Skenny liang plat_cpu_pwron_common(); 357539061b8Skenny liang 358539061b8Skenny liang hotplug_ctrl_cpu_on_finish(cluster, cpu); 3597352f329Skenny liang } 3607352f329Skenny liang 361539061b8Skenny liang static void plat_mtk_power_domain_suspend(const psci_power_state_t *state) 362539061b8Skenny liang { 363539061b8Skenny liang uint64_t mpidr = read_mpidr(); 364539061b8Skenny liang int cpu = MPIDR_AFFLVL0_VAL(mpidr); 365539061b8Skenny liang int cluster = MPIDR_AFFLVL1_VAL(mpidr); 366539061b8Skenny liang const plat_local_state_t *pds = state->pwr_domain_state; 367539061b8Skenny liang bool afflvl1 = (pds[MPIDR_AFFLVL1] == MTK_LOCAL_STATE_OFF); 368539061b8Skenny liang bool afflvl2 = (pds[MPIDR_AFFLVL2] == MTK_LOCAL_STATE_OFF); 369539061b8Skenny liang bool cluster_off = MCDI_C2 && afflvl1 && clst_single_pwr(cluster, cpu); 370539061b8Skenny liang 3714450a518Skenny liang plat_cpu_pwrdwn_common(); 3724450a518Skenny liang 373539061b8Skenny liang plat_dcm_mcsi_a_backup(); 374539061b8Skenny liang 375539061b8Skenny liang if (cluster_off || afflvl2) 376539061b8Skenny liang plat_cluster_pwrdwn_common(mpidr, cluster); 377539061b8Skenny liang 378539061b8Skenny liang if (afflvl2) { 379539061b8Skenny liang spm_data_t spm_d = { .cmd = SPM_SUSPEND }; 380539061b8Skenny liang uint32_t *d = (uint32_t *)&spm_d; 381539061b8Skenny liang uint32_t l = sizeof(spm_d) / sizeof(uint32_t); 382539061b8Skenny liang 383539061b8Skenny liang mcdi_ctrl_suspend(); 384539061b8Skenny liang 385539061b8Skenny liang spm_set_bootaddr(secure_entrypoint); 386539061b8Skenny liang 387539061b8Skenny liang if (MCDI_SSPM) 388539061b8Skenny liang sspm_ipi_send_non_blocking(IPI_ID_SUSPEND, d); 389539061b8Skenny liang 390539061b8Skenny liang spm_system_suspend(); 391539061b8Skenny liang 392539061b8Skenny liang if (MCDI_SSPM) 393539061b8Skenny liang while (sspm_ipi_recv_non_blocking(IPI_ID_SUSPEND, d, l)) 394539061b8Skenny liang ; 3954450a518Skenny liang 3964450a518Skenny liang mt_gic_distif_save(); 397539061b8Skenny liang } else { 398539061b8Skenny liang mcdi_ctrl_cluster_cpu_off(cluster, cpu, cluster_off); 399539061b8Skenny liang } 400539061b8Skenny liang } 401539061b8Skenny liang 402539061b8Skenny liang static void plat_mtk_power_domain_suspend_finish(const psci_power_state_t *state) 403539061b8Skenny liang { 404539061b8Skenny liang uint64_t mpidr = read_mpidr(); 405539061b8Skenny liang int cluster = MPIDR_AFFLVL1_VAL(mpidr); 406539061b8Skenny liang const plat_local_state_t *pds = state->pwr_domain_state; 407539061b8Skenny liang bool afflvl2 = (pds[MPIDR_AFFLVL2] == MTK_LOCAL_STATE_OFF); 408539061b8Skenny liang 409539061b8Skenny liang if (afflvl2) { 410539061b8Skenny liang spm_data_t spm_d = { .cmd = SPM_RESUME }; 411539061b8Skenny liang uint32_t *d = (uint32_t *)&spm_d; 412539061b8Skenny liang uint32_t l = sizeof(spm_d) / sizeof(uint32_t); 413539061b8Skenny liang 414539061b8Skenny liang mt_gic_init(); 4154450a518Skenny liang mt_gic_distif_restore(); 4164450a518Skenny liang mt_gic_rdistif_restore(); 4174450a518Skenny liang 418539061b8Skenny liang mmio_write_32(EMI_WFIFO, 0xf); 419539061b8Skenny liang 420539061b8Skenny liang if (MCDI_SSPM) 421539061b8Skenny liang sspm_ipi_send_non_blocking(IPI_ID_SUSPEND, d); 422539061b8Skenny liang 423539061b8Skenny liang spm_system_suspend_finish(); 424539061b8Skenny liang 425539061b8Skenny liang if (MCDI_SSPM) 426539061b8Skenny liang while (sspm_ipi_recv_non_blocking(IPI_ID_SUSPEND, d, l)) 427539061b8Skenny liang ; 428539061b8Skenny liang 429539061b8Skenny liang mcdi_ctrl_resume(); 4304450a518Skenny liang } else { 4314450a518Skenny liang plat_cpu_pwron_common(); 432539061b8Skenny liang } 433539061b8Skenny liang 434539061b8Skenny liang plat_cluster_pwron_common(mpidr, cluster); 435539061b8Skenny liang 436539061b8Skenny liang plat_dcm_mcsi_a_restore(); 437539061b8Skenny liang } 438539061b8Skenny liang 439539061b8Skenny liang #if PSCI_EXTENDED_STATE_ID 440539061b8Skenny liang 441539061b8Skenny liang static int plat_mtk_validate_power_state(unsigned int power_state, 442539061b8Skenny liang psci_power_state_t *req_state) 443539061b8Skenny liang { 444539061b8Skenny liang unsigned int state_id; 445539061b8Skenny liang int i; 446539061b8Skenny liang 447539061b8Skenny liang assert(req_state); 448539061b8Skenny liang 449539061b8Skenny liang if (!MCDI_SSPM) 450539061b8Skenny liang return PSCI_E_INVALID_PARAMS; 451539061b8Skenny liang 452539061b8Skenny liang /* 453539061b8Skenny liang * Currently we are using a linear search for finding the matching 454539061b8Skenny liang * entry in the idle power state array. This can be made a binary 455539061b8Skenny liang * search if the number of entries justify the additional complexity. 456539061b8Skenny liang */ 457539061b8Skenny liang for (i = 0; !!mtk_pm_idle_states[i]; i++) { 458539061b8Skenny liang if (power_state == mtk_pm_idle_states[i]) 459539061b8Skenny liang break; 460539061b8Skenny liang } 461539061b8Skenny liang 462539061b8Skenny liang /* Return error if entry not found in the idle state array */ 463539061b8Skenny liang if (!mtk_pm_idle_states[i]) 464539061b8Skenny liang return PSCI_E_INVALID_PARAMS; 465539061b8Skenny liang 466539061b8Skenny liang i = 0; 467539061b8Skenny liang state_id = psci_get_pstate_id(power_state); 468539061b8Skenny liang 469539061b8Skenny liang /* Parse the State ID and populate the state info parameter */ 470539061b8Skenny liang while (state_id) { 471539061b8Skenny liang req_state->pwr_domain_state[i++] = state_id & 472539061b8Skenny liang MTK_LOCAL_PSTATE_MASK; 473539061b8Skenny liang state_id >>= MTK_LOCAL_PSTATE_WIDTH; 474539061b8Skenny liang } 475539061b8Skenny liang 476539061b8Skenny liang return PSCI_E_SUCCESS; 477539061b8Skenny liang } 478539061b8Skenny liang 479539061b8Skenny liang #else /* if !PSCI_EXTENDED_STATE_ID */ 480539061b8Skenny liang 481539061b8Skenny liang static int plat_mtk_validate_power_state(unsigned int power_state, 482539061b8Skenny liang psci_power_state_t *req_state) 483539061b8Skenny liang { 484539061b8Skenny liang int pstate = psci_get_pstate_type(power_state); 485539061b8Skenny liang int pwr_lvl = psci_get_pstate_pwrlvl(power_state); 486539061b8Skenny liang int i; 487539061b8Skenny liang 488539061b8Skenny liang assert(req_state); 489539061b8Skenny liang 490539061b8Skenny liang if (pwr_lvl > PLAT_MAX_PWR_LVL) 491539061b8Skenny liang return PSCI_E_INVALID_PARAMS; 492539061b8Skenny liang 493539061b8Skenny liang /* Sanity check the requested state */ 494539061b8Skenny liang if (pstate == PSTATE_TYPE_STANDBY) { 495539061b8Skenny liang /* 496539061b8Skenny liang * It's possible to enter standby only on power level 0 497539061b8Skenny liang * Ignore any other power level. 498539061b8Skenny liang */ 499539061b8Skenny liang if (pwr_lvl != 0) 500539061b8Skenny liang return PSCI_E_INVALID_PARAMS; 501539061b8Skenny liang 502539061b8Skenny liang req_state->pwr_domain_state[MTK_PWR_LVL0] = MTK_LOCAL_STATE_RET; 503539061b8Skenny liang } else if (!MCDI_SSPM) { 504539061b8Skenny liang return PSCI_E_INVALID_PARAMS; 505539061b8Skenny liang } else { 506539061b8Skenny liang for (i = 0; i <= pwr_lvl; i++) 507539061b8Skenny liang req_state->pwr_domain_state[i] = MTK_LOCAL_STATE_OFF; 508539061b8Skenny liang } 509539061b8Skenny liang 510539061b8Skenny liang return PSCI_E_SUCCESS; 511539061b8Skenny liang } 512539061b8Skenny liang 513539061b8Skenny liang #endif /* PSCI_EXTENDED_STATE_ID */ 514539061b8Skenny liang 5153fa9dec4Skenny liang /******************************************************************************* 516e977b4dbSkenny liang * MTK handlers to shutdown/reboot the system 517e977b4dbSkenny liang ******************************************************************************/ 518e977b4dbSkenny liang static void __dead2 plat_mtk_system_off(void) 519e977b4dbSkenny liang { 520e977b4dbSkenny liang INFO("MTK System Off\n"); 521e977b4dbSkenny liang 522e977b4dbSkenny liang rtc_power_off_sequence(); 523e977b4dbSkenny liang wk_pmic_enable_sdn_delay(); 524e977b4dbSkenny liang pmic_power_off(); 525e977b4dbSkenny liang 526e977b4dbSkenny liang wfi(); 527e977b4dbSkenny liang ERROR("MTK System Off: operation not handled.\n"); 528e977b4dbSkenny liang panic(); 529e977b4dbSkenny liang } 530e977b4dbSkenny liang 5313d91c9c3Skenny liang static void __dead2 plat_mtk_system_reset(void) 5323d91c9c3Skenny liang { 5333d91c9c3Skenny liang struct bl_aux_gpio_info *gpio_reset = plat_get_mtk_gpio_reset(); 5343d91c9c3Skenny liang 5353d91c9c3Skenny liang INFO("MTK System Reset\n"); 5363d91c9c3Skenny liang 5373d91c9c3Skenny liang mt_set_gpio_out(gpio_reset->index, gpio_reset->polarity); 5383d91c9c3Skenny liang 5393d91c9c3Skenny liang wfi(); 5403d91c9c3Skenny liang ERROR("MTK System Reset: operation not handled.\n"); 5413d91c9c3Skenny liang panic(); 5423d91c9c3Skenny liang } 5433d91c9c3Skenny liang 5443c25ba44Skenny liang static void plat_mtk_get_sys_suspend_power_state(psci_power_state_t *req_state) 5453c25ba44Skenny liang { 5463c25ba44Skenny liang assert(PLAT_MAX_PWR_LVL >= 2); 5473c25ba44Skenny liang 5483c25ba44Skenny liang for (int i = MPIDR_AFFLVL0; i <= PLAT_MAX_PWR_LVL; i++) 5493c25ba44Skenny liang req_state->pwr_domain_state[i] = MTK_LOCAL_STATE_OFF; 5503c25ba44Skenny liang } 5513c25ba44Skenny liang 552e977b4dbSkenny liang /******************************************************************************* 5533fa9dec4Skenny liang * MTK_platform handler called when an affinity instance is about to be turned 5543fa9dec4Skenny liang * on. The level and mpidr determine the affinity instance. 5553fa9dec4Skenny liang ******************************************************************************/ 5563fa9dec4Skenny liang static const plat_psci_ops_t plat_plat_pm_ops = { 557539061b8Skenny liang .cpu_standby = plat_cpu_standby, 5587352f329Skenny liang .pwr_domain_on = plat_mtk_power_domain_on, 5597352f329Skenny liang .pwr_domain_on_finish = plat_mtk_power_domain_on_finish, 5607352f329Skenny liang .pwr_domain_off = plat_mtk_power_domain_off, 5613c25ba44Skenny liang .pwr_domain_suspend = plat_mtk_power_domain_suspend, 5623c25ba44Skenny liang .pwr_domain_suspend_finish = plat_mtk_power_domain_suspend_finish, 563e977b4dbSkenny liang .system_off = plat_mtk_system_off, 5643d91c9c3Skenny liang .system_reset = plat_mtk_system_reset, 565539061b8Skenny liang .validate_power_state = plat_mtk_validate_power_state, 566*c6e0a64dSJames Liao .get_sys_suspend_power_state = plat_mtk_get_sys_suspend_power_state 5673fa9dec4Skenny liang }; 5683fa9dec4Skenny liang 5693fa9dec4Skenny liang int plat_setup_psci_ops(uintptr_t sec_entrypoint, 5703fa9dec4Skenny liang const plat_psci_ops_t **psci_ops) 5713fa9dec4Skenny liang { 572*c6e0a64dSJames Liao unsigned int i; 573*c6e0a64dSJames Liao 5743fa9dec4Skenny liang *psci_ops = &plat_plat_pm_ops; 5753fa9dec4Skenny liang secure_entrypoint = sec_entrypoint; 576539061b8Skenny liang 577*c6e0a64dSJames Liao /* Init cpu reset arch as AARCH64 of cluster 0 */ 578*c6e0a64dSJames Liao for (i = 0; i < PLATFORM_MAX_CPUS_PER_CLUSTER; i++) { 579*c6e0a64dSJames Liao mcucfg_init_archstate(0, i, 1); 580*c6e0a64dSJames Liao mcucfg_set_bootaddr(0, i, secure_entrypoint); 581*c6e0a64dSJames Liao } 582*c6e0a64dSJames Liao 583539061b8Skenny liang if (!check_mcdi_ctl_stat()) { 584539061b8Skenny liang HP_SSPM_CTRL = false; 585539061b8Skenny liang MCDI_SSPM = false; 586539061b8Skenny liang } 587539061b8Skenny liang 5883fa9dec4Skenny liang return 0; 5893fa9dec4Skenny liang } 590