1*5bd9c17dSSaurabh Gorecha /* 2*5bd9c17dSSaurabh Gorecha * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. 3*5bd9c17dSSaurabh Gorecha * Copyright (c) 2018, 2020, The Linux Foundation. All rights reserved. 4*5bd9c17dSSaurabh Gorecha * 5*5bd9c17dSSaurabh Gorecha * SPDX-License-Identifier: BSD-3-Clause 6*5bd9c17dSSaurabh Gorecha */ 7*5bd9c17dSSaurabh Gorecha #include <assert.h> 8*5bd9c17dSSaurabh Gorecha 9*5bd9c17dSSaurabh Gorecha #include <arch_helpers.h> 10*5bd9c17dSSaurabh Gorecha #include <bl31/bl31.h> 11*5bd9c17dSSaurabh Gorecha #include <common/debug.h> 12*5bd9c17dSSaurabh Gorecha #include <lib/psci/psci.h> 13*5bd9c17dSSaurabh Gorecha 14*5bd9c17dSSaurabh Gorecha #include <platform.h> 15*5bd9c17dSSaurabh Gorecha #include <platform_def.h> 16*5bd9c17dSSaurabh Gorecha #include <qti_cpu.h> 17*5bd9c17dSSaurabh Gorecha #include <qti_plat.h> 18*5bd9c17dSSaurabh Gorecha #include <qtiseclib_cb_interface.h> 19*5bd9c17dSSaurabh Gorecha #include <qtiseclib_defs_plat.h> 20*5bd9c17dSSaurabh Gorecha #include <qtiseclib_interface.h> 21*5bd9c17dSSaurabh Gorecha 22*5bd9c17dSSaurabh Gorecha #define QTI_LOCAL_PSTATE_WIDTH 4 23*5bd9c17dSSaurabh Gorecha #define QTI_LOCAL_PSTATE_MASK ((1 << QTI_LOCAL_PSTATE_WIDTH) - 1) 24*5bd9c17dSSaurabh Gorecha 25*5bd9c17dSSaurabh Gorecha /* Make composite power state parameter till level 0 */ 26*5bd9c17dSSaurabh Gorecha #define qti_make_pwrstate_lvl0(lvl0_state, type) \ 27*5bd9c17dSSaurabh Gorecha (((lvl0_state) << PSTATE_ID_SHIFT) | ((type) << PSTATE_TYPE_SHIFT)) 28*5bd9c17dSSaurabh Gorecha 29*5bd9c17dSSaurabh Gorecha /* Make composite power state parameter till level 1 */ 30*5bd9c17dSSaurabh Gorecha #define qti_make_pwrstate_lvl1(lvl1_state, lvl0_state, type) \ 31*5bd9c17dSSaurabh Gorecha (((lvl1_state) << QTI_LOCAL_PSTATE_WIDTH) | \ 32*5bd9c17dSSaurabh Gorecha qti_make_pwrstate_lvl0(lvl0_state, type)) 33*5bd9c17dSSaurabh Gorecha 34*5bd9c17dSSaurabh Gorecha /* Make composite power state parameter till level 2 */ 35*5bd9c17dSSaurabh Gorecha #define qti_make_pwrstate_lvl2(lvl2_state, lvl1_state, lvl0_state, type) \ 36*5bd9c17dSSaurabh Gorecha (((lvl2_state) << (QTI_LOCAL_PSTATE_WIDTH * 2)) | \ 37*5bd9c17dSSaurabh Gorecha qti_make_pwrstate_lvl1(lvl1_state, lvl0_state, type)) 38*5bd9c17dSSaurabh Gorecha 39*5bd9c17dSSaurabh Gorecha /* Make composite power state parameter till level 3 */ 40*5bd9c17dSSaurabh Gorecha #define qti_make_pwrstate_lvl3(lvl3_state, lvl2_state, lvl1_state, lvl0_state, type) \ 41*5bd9c17dSSaurabh Gorecha (((lvl3_state) << (QTI_LOCAL_PSTATE_WIDTH * 3)) | \ 42*5bd9c17dSSaurabh Gorecha qti_make_pwrstate_lvl2(lvl2_state, lvl1_state, lvl0_state, type)) 43*5bd9c17dSSaurabh Gorecha 44*5bd9c17dSSaurabh Gorecha /* QTI_CORE_PWRDN_EN_MASK happens to be same across all CPUs */ 45*5bd9c17dSSaurabh Gorecha #define QTI_CORE_PWRDN_EN_MASK 1 46*5bd9c17dSSaurabh Gorecha 47*5bd9c17dSSaurabh Gorecha /* cpu power control happens to be same across all CPUs */ 48*5bd9c17dSSaurabh Gorecha _DEFINE_SYSREG_WRITE_FUNC(cpu_pwrctrl_val, S3_0_C15_C2_7) 49*5bd9c17dSSaurabh Gorecha _DEFINE_SYSREG_READ_FUNC(cpu_pwrctrl_val, S3_0_C15_C2_7) 50*5bd9c17dSSaurabh Gorecha 51*5bd9c17dSSaurabh Gorecha const unsigned int qti_pm_idle_states[] = { 52*5bd9c17dSSaurabh Gorecha qti_make_pwrstate_lvl0(QTI_LOCAL_STATE_OFF, 53*5bd9c17dSSaurabh Gorecha PSTATE_TYPE_POWERDOWN), 54*5bd9c17dSSaurabh Gorecha qti_make_pwrstate_lvl0(QTI_LOCAL_STATE_DEEPOFF, 55*5bd9c17dSSaurabh Gorecha PSTATE_TYPE_POWERDOWN), 56*5bd9c17dSSaurabh Gorecha qti_make_pwrstate_lvl1(QTI_LOCAL_STATE_DEEPOFF, 57*5bd9c17dSSaurabh Gorecha QTI_LOCAL_STATE_DEEPOFF, 58*5bd9c17dSSaurabh Gorecha PSTATE_TYPE_POWERDOWN), 59*5bd9c17dSSaurabh Gorecha qti_make_pwrstate_lvl2(QTI_LOCAL_STATE_OFF, 60*5bd9c17dSSaurabh Gorecha QTI_LOCAL_STATE_DEEPOFF, 61*5bd9c17dSSaurabh Gorecha QTI_LOCAL_STATE_DEEPOFF, 62*5bd9c17dSSaurabh Gorecha PSTATE_TYPE_POWERDOWN), 63*5bd9c17dSSaurabh Gorecha qti_make_pwrstate_lvl3(QTI_LOCAL_STATE_OFF, 64*5bd9c17dSSaurabh Gorecha QTI_LOCAL_STATE_DEEPOFF, 65*5bd9c17dSSaurabh Gorecha QTI_LOCAL_STATE_DEEPOFF, 66*5bd9c17dSSaurabh Gorecha QTI_LOCAL_STATE_DEEPOFF, 67*5bd9c17dSSaurabh Gorecha PSTATE_TYPE_POWERDOWN), 68*5bd9c17dSSaurabh Gorecha 0, 69*5bd9c17dSSaurabh Gorecha }; 70*5bd9c17dSSaurabh Gorecha 71*5bd9c17dSSaurabh Gorecha /******************************************************************************* 72*5bd9c17dSSaurabh Gorecha * QTI standard platform handler called to check the validity of the power 73*5bd9c17dSSaurabh Gorecha * state parameter. The power state parameter has to be a composite power 74*5bd9c17dSSaurabh Gorecha * state. 75*5bd9c17dSSaurabh Gorecha ******************************************************************************/ 76*5bd9c17dSSaurabh Gorecha int qti_validate_power_state(unsigned int power_state, 77*5bd9c17dSSaurabh Gorecha psci_power_state_t *req_state) 78*5bd9c17dSSaurabh Gorecha { 79*5bd9c17dSSaurabh Gorecha unsigned int state_id; 80*5bd9c17dSSaurabh Gorecha int i; 81*5bd9c17dSSaurabh Gorecha 82*5bd9c17dSSaurabh Gorecha assert(req_state); 83*5bd9c17dSSaurabh Gorecha 84*5bd9c17dSSaurabh Gorecha /* 85*5bd9c17dSSaurabh Gorecha * Currently we are using a linear search for finding the matching 86*5bd9c17dSSaurabh Gorecha * entry in the idle power state array. This can be made a binary 87*5bd9c17dSSaurabh Gorecha * search if the number of entries justify the additional complexity. 88*5bd9c17dSSaurabh Gorecha */ 89*5bd9c17dSSaurabh Gorecha for (i = 0; !!qti_pm_idle_states[i]; i++) { 90*5bd9c17dSSaurabh Gorecha if (power_state == qti_pm_idle_states[i]) 91*5bd9c17dSSaurabh Gorecha break; 92*5bd9c17dSSaurabh Gorecha } 93*5bd9c17dSSaurabh Gorecha 94*5bd9c17dSSaurabh Gorecha /* Return error if entry not found in the idle state array */ 95*5bd9c17dSSaurabh Gorecha if (!qti_pm_idle_states[i]) 96*5bd9c17dSSaurabh Gorecha return PSCI_E_INVALID_PARAMS; 97*5bd9c17dSSaurabh Gorecha 98*5bd9c17dSSaurabh Gorecha i = 0; 99*5bd9c17dSSaurabh Gorecha state_id = psci_get_pstate_id(power_state); 100*5bd9c17dSSaurabh Gorecha 101*5bd9c17dSSaurabh Gorecha /* Parse the State ID and populate the state info parameter */ 102*5bd9c17dSSaurabh Gorecha while (state_id) { 103*5bd9c17dSSaurabh Gorecha req_state->pwr_domain_state[i++] = state_id & 104*5bd9c17dSSaurabh Gorecha QTI_LOCAL_PSTATE_MASK; 105*5bd9c17dSSaurabh Gorecha state_id >>= QTI_LOCAL_PSTATE_WIDTH; 106*5bd9c17dSSaurabh Gorecha } 107*5bd9c17dSSaurabh Gorecha 108*5bd9c17dSSaurabh Gorecha return PSCI_E_SUCCESS; 109*5bd9c17dSSaurabh Gorecha } 110*5bd9c17dSSaurabh Gorecha 111*5bd9c17dSSaurabh Gorecha /******************************************************************************* 112*5bd9c17dSSaurabh Gorecha * PLATFORM FUNCTIONS 113*5bd9c17dSSaurabh Gorecha ******************************************************************************/ 114*5bd9c17dSSaurabh Gorecha 115*5bd9c17dSSaurabh Gorecha static void qti_set_cpupwrctlr_val(void) 116*5bd9c17dSSaurabh Gorecha { 117*5bd9c17dSSaurabh Gorecha unsigned long val; 118*5bd9c17dSSaurabh Gorecha 119*5bd9c17dSSaurabh Gorecha val = read_cpu_pwrctrl_val(); 120*5bd9c17dSSaurabh Gorecha val |= QTI_CORE_PWRDN_EN_MASK; 121*5bd9c17dSSaurabh Gorecha write_cpu_pwrctrl_val(val); 122*5bd9c17dSSaurabh Gorecha 123*5bd9c17dSSaurabh Gorecha isb(); 124*5bd9c17dSSaurabh Gorecha } 125*5bd9c17dSSaurabh Gorecha 126*5bd9c17dSSaurabh Gorecha /** 127*5bd9c17dSSaurabh Gorecha * CPU power on function - ideally we want a wrapper since this function is 128*5bd9c17dSSaurabh Gorecha * target specific. But to unblock teams. 129*5bd9c17dSSaurabh Gorecha */ 130*5bd9c17dSSaurabh Gorecha static int qti_cpu_power_on(u_register_t mpidr) 131*5bd9c17dSSaurabh Gorecha { 132*5bd9c17dSSaurabh Gorecha int core_pos = plat_core_pos_by_mpidr(mpidr); 133*5bd9c17dSSaurabh Gorecha 134*5bd9c17dSSaurabh Gorecha /* If not valid mpidr, return error */ 135*5bd9c17dSSaurabh Gorecha if (core_pos < 0 || core_pos >= QTISECLIB_PLAT_CORE_COUNT) { 136*5bd9c17dSSaurabh Gorecha return PSCI_E_INVALID_PARAMS; 137*5bd9c17dSSaurabh Gorecha } 138*5bd9c17dSSaurabh Gorecha 139*5bd9c17dSSaurabh Gorecha return qtiseclib_psci_node_power_on(mpidr); 140*5bd9c17dSSaurabh Gorecha } 141*5bd9c17dSSaurabh Gorecha 142*5bd9c17dSSaurabh Gorecha static bool is_cpu_off(const psci_power_state_t *target_state) 143*5bd9c17dSSaurabh Gorecha { 144*5bd9c17dSSaurabh Gorecha if ((target_state->pwr_domain_state[QTI_PWR_LVL0] == 145*5bd9c17dSSaurabh Gorecha QTI_LOCAL_STATE_OFF) || 146*5bd9c17dSSaurabh Gorecha (target_state->pwr_domain_state[QTI_PWR_LVL0] == 147*5bd9c17dSSaurabh Gorecha QTI_LOCAL_STATE_DEEPOFF)) { 148*5bd9c17dSSaurabh Gorecha return true; 149*5bd9c17dSSaurabh Gorecha } else { 150*5bd9c17dSSaurabh Gorecha return false; 151*5bd9c17dSSaurabh Gorecha } 152*5bd9c17dSSaurabh Gorecha } 153*5bd9c17dSSaurabh Gorecha 154*5bd9c17dSSaurabh Gorecha static void qti_cpu_power_on_finish(const psci_power_state_t *target_state) 155*5bd9c17dSSaurabh Gorecha { 156*5bd9c17dSSaurabh Gorecha const uint8_t *pwr_states = 157*5bd9c17dSSaurabh Gorecha (const uint8_t *)target_state->pwr_domain_state; 158*5bd9c17dSSaurabh Gorecha qtiseclib_psci_node_on_finish(pwr_states); 159*5bd9c17dSSaurabh Gorecha 160*5bd9c17dSSaurabh Gorecha if (is_cpu_off(target_state)) { 161*5bd9c17dSSaurabh Gorecha plat_qti_gic_cpuif_enable(); 162*5bd9c17dSSaurabh Gorecha } 163*5bd9c17dSSaurabh Gorecha } 164*5bd9c17dSSaurabh Gorecha 165*5bd9c17dSSaurabh Gorecha static void qti_cpu_standby(plat_local_state_t cpu_state) 166*5bd9c17dSSaurabh Gorecha { 167*5bd9c17dSSaurabh Gorecha } 168*5bd9c17dSSaurabh Gorecha 169*5bd9c17dSSaurabh Gorecha static void qti_node_power_off(const psci_power_state_t *target_state) 170*5bd9c17dSSaurabh Gorecha { 171*5bd9c17dSSaurabh Gorecha qtiseclib_psci_node_power_off((const uint8_t *) 172*5bd9c17dSSaurabh Gorecha target_state->pwr_domain_state); 173*5bd9c17dSSaurabh Gorecha if (is_cpu_off(target_state)) { 174*5bd9c17dSSaurabh Gorecha plat_qti_gic_cpuif_disable(); 175*5bd9c17dSSaurabh Gorecha qti_set_cpupwrctlr_val(); 176*5bd9c17dSSaurabh Gorecha } 177*5bd9c17dSSaurabh Gorecha } 178*5bd9c17dSSaurabh Gorecha 179*5bd9c17dSSaurabh Gorecha static void qti_node_suspend(const psci_power_state_t *target_state) 180*5bd9c17dSSaurabh Gorecha { 181*5bd9c17dSSaurabh Gorecha qtiseclib_psci_node_suspend((const uint8_t *)target_state-> 182*5bd9c17dSSaurabh Gorecha pwr_domain_state); 183*5bd9c17dSSaurabh Gorecha if (is_cpu_off(target_state)) { 184*5bd9c17dSSaurabh Gorecha plat_qti_gic_cpuif_disable(); 185*5bd9c17dSSaurabh Gorecha qti_set_cpupwrctlr_val(); 186*5bd9c17dSSaurabh Gorecha } 187*5bd9c17dSSaurabh Gorecha } 188*5bd9c17dSSaurabh Gorecha 189*5bd9c17dSSaurabh Gorecha static void qti_node_suspend_finish(const psci_power_state_t *target_state) 190*5bd9c17dSSaurabh Gorecha { 191*5bd9c17dSSaurabh Gorecha const uint8_t *pwr_states = 192*5bd9c17dSSaurabh Gorecha (const uint8_t *)target_state->pwr_domain_state; 193*5bd9c17dSSaurabh Gorecha qtiseclib_psci_node_suspend_finish(pwr_states); 194*5bd9c17dSSaurabh Gorecha if (is_cpu_off(target_state)) { 195*5bd9c17dSSaurabh Gorecha plat_qti_gic_cpuif_enable(); 196*5bd9c17dSSaurabh Gorecha } 197*5bd9c17dSSaurabh Gorecha } 198*5bd9c17dSSaurabh Gorecha 199*5bd9c17dSSaurabh Gorecha __dead2 void qti_domain_power_down_wfi(const psci_power_state_t *target_state) 200*5bd9c17dSSaurabh Gorecha { 201*5bd9c17dSSaurabh Gorecha 202*5bd9c17dSSaurabh Gorecha /* For now just do WFI - add any target specific handling if needed */ 203*5bd9c17dSSaurabh Gorecha psci_power_down_wfi(); 204*5bd9c17dSSaurabh Gorecha /* We should never reach here */ 205*5bd9c17dSSaurabh Gorecha } 206*5bd9c17dSSaurabh Gorecha 207*5bd9c17dSSaurabh Gorecha __dead2 void qti_system_off(void) 208*5bd9c17dSSaurabh Gorecha { 209*5bd9c17dSSaurabh Gorecha qtiseclib_psci_system_off(); 210*5bd9c17dSSaurabh Gorecha } 211*5bd9c17dSSaurabh Gorecha 212*5bd9c17dSSaurabh Gorecha __dead2 void qti_system_reset(void) 213*5bd9c17dSSaurabh Gorecha { 214*5bd9c17dSSaurabh Gorecha qtiseclib_psci_system_reset(); 215*5bd9c17dSSaurabh Gorecha } 216*5bd9c17dSSaurabh Gorecha 217*5bd9c17dSSaurabh Gorecha void qti_get_sys_suspend_power_state(psci_power_state_t *req_state) 218*5bd9c17dSSaurabh Gorecha { 219*5bd9c17dSSaurabh Gorecha int i = 0; 220*5bd9c17dSSaurabh Gorecha unsigned int state_id, power_state; 221*5bd9c17dSSaurabh Gorecha int size = ARRAY_SIZE(qti_pm_idle_states); 222*5bd9c17dSSaurabh Gorecha 223*5bd9c17dSSaurabh Gorecha /* 224*5bd9c17dSSaurabh Gorecha * Find deepest state. 225*5bd9c17dSSaurabh Gorecha * The arm_pm_idle_states[] array has last element by default 0, 226*5bd9c17dSSaurabh Gorecha * so the real deepest state is second last element of that array. 227*5bd9c17dSSaurabh Gorecha */ 228*5bd9c17dSSaurabh Gorecha power_state = qti_pm_idle_states[size - 2]; 229*5bd9c17dSSaurabh Gorecha state_id = psci_get_pstate_id(power_state); 230*5bd9c17dSSaurabh Gorecha 231*5bd9c17dSSaurabh Gorecha /* Parse the State ID and populate the state info parameter */ 232*5bd9c17dSSaurabh Gorecha while (state_id) { 233*5bd9c17dSSaurabh Gorecha req_state->pwr_domain_state[i++] = 234*5bd9c17dSSaurabh Gorecha state_id & QTI_LOCAL_PSTATE_MASK; 235*5bd9c17dSSaurabh Gorecha state_id >>= QTI_LOCAL_PSTATE_WIDTH; 236*5bd9c17dSSaurabh Gorecha } 237*5bd9c17dSSaurabh Gorecha } 238*5bd9c17dSSaurabh Gorecha 239*5bd9c17dSSaurabh Gorecha /* 240*5bd9c17dSSaurabh Gorecha * Structure containing platform specific PSCI operations. Common 241*5bd9c17dSSaurabh Gorecha * PSCI layer will use this. 242*5bd9c17dSSaurabh Gorecha */ 243*5bd9c17dSSaurabh Gorecha const plat_psci_ops_t plat_qti_psci_pm_ops = { 244*5bd9c17dSSaurabh Gorecha .pwr_domain_on = qti_cpu_power_on, 245*5bd9c17dSSaurabh Gorecha .pwr_domain_on_finish = qti_cpu_power_on_finish, 246*5bd9c17dSSaurabh Gorecha .cpu_standby = qti_cpu_standby, 247*5bd9c17dSSaurabh Gorecha .pwr_domain_off = qti_node_power_off, 248*5bd9c17dSSaurabh Gorecha .pwr_domain_suspend = qti_node_suspend, 249*5bd9c17dSSaurabh Gorecha .pwr_domain_suspend_finish = qti_node_suspend_finish, 250*5bd9c17dSSaurabh Gorecha .pwr_domain_pwr_down_wfi = qti_domain_power_down_wfi, 251*5bd9c17dSSaurabh Gorecha .system_off = qti_system_off, 252*5bd9c17dSSaurabh Gorecha .system_reset = qti_system_reset, 253*5bd9c17dSSaurabh Gorecha .get_node_hw_state = NULL, 254*5bd9c17dSSaurabh Gorecha .translate_power_state_by_mpidr = NULL, 255*5bd9c17dSSaurabh Gorecha .get_sys_suspend_power_state = qti_get_sys_suspend_power_state, 256*5bd9c17dSSaurabh Gorecha .validate_power_state = qti_validate_power_state, 257*5bd9c17dSSaurabh Gorecha }; 258*5bd9c17dSSaurabh Gorecha 259*5bd9c17dSSaurabh Gorecha /** 260*5bd9c17dSSaurabh Gorecha * The QTI Standard platform definition of platform porting API 261*5bd9c17dSSaurabh Gorecha * `plat_setup_psci_ops`. 262*5bd9c17dSSaurabh Gorecha */ 263*5bd9c17dSSaurabh Gorecha int plat_setup_psci_ops(uintptr_t sec_entrypoint, 264*5bd9c17dSSaurabh Gorecha const plat_psci_ops_t **psci_ops) 265*5bd9c17dSSaurabh Gorecha { 266*5bd9c17dSSaurabh Gorecha int err; 267*5bd9c17dSSaurabh Gorecha 268*5bd9c17dSSaurabh Gorecha err = qtiseclib_psci_init((uintptr_t)bl31_warm_entrypoint); 269*5bd9c17dSSaurabh Gorecha if (err == PSCI_E_SUCCESS) { 270*5bd9c17dSSaurabh Gorecha *psci_ops = &plat_qti_psci_pm_ops; 271*5bd9c17dSSaurabh Gorecha } 272*5bd9c17dSSaurabh Gorecha 273*5bd9c17dSSaurabh Gorecha return err; 274*5bd9c17dSSaurabh Gorecha } 275