15bd9c17dSSaurabh Gorecha /* 25bd9c17dSSaurabh Gorecha * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. 35bd9c17dSSaurabh Gorecha * Copyright (c) 2018, 2020, The Linux Foundation. All rights reserved. 45bd9c17dSSaurabh Gorecha * 55bd9c17dSSaurabh Gorecha * SPDX-License-Identifier: BSD-3-Clause 65bd9c17dSSaurabh Gorecha */ 75bd9c17dSSaurabh Gorecha #include <assert.h> 85bd9c17dSSaurabh Gorecha 95bd9c17dSSaurabh Gorecha #include <arch_helpers.h> 105bd9c17dSSaurabh Gorecha #include <bl31/bl31.h> 115bd9c17dSSaurabh Gorecha #include <common/debug.h> 12522a2277SJulius Werner #include <drivers/delay_timer.h> 13522a2277SJulius Werner #include <lib/mmio.h> 145bd9c17dSSaurabh Gorecha #include <lib/psci/psci.h> 155bd9c17dSSaurabh Gorecha 165bd9c17dSSaurabh Gorecha #include <platform.h> 175bd9c17dSSaurabh Gorecha #include <platform_def.h> 185bd9c17dSSaurabh Gorecha #include <qti_cpu.h> 195bd9c17dSSaurabh Gorecha #include <qti_plat.h> 205bd9c17dSSaurabh Gorecha #include <qtiseclib_cb_interface.h> 215bd9c17dSSaurabh Gorecha #include <qtiseclib_defs_plat.h> 225bd9c17dSSaurabh Gorecha #include <qtiseclib_interface.h> 235bd9c17dSSaurabh Gorecha 245bd9c17dSSaurabh Gorecha #define QTI_LOCAL_PSTATE_WIDTH 4 255bd9c17dSSaurabh Gorecha #define QTI_LOCAL_PSTATE_MASK ((1 << QTI_LOCAL_PSTATE_WIDTH) - 1) 265bd9c17dSSaurabh Gorecha 27*e528bbecSMaulik Shah #if PSCI_OS_INIT_MODE 28*e528bbecSMaulik Shah #define QTI_LAST_AT_PLVL_MASK (QTI_LOCAL_PSTATE_MASK << \ 29*e528bbecSMaulik Shah (QTI_LOCAL_PSTATE_WIDTH * \ 30*e528bbecSMaulik Shah (PLAT_MAX_PWR_LVL + 1))) 31*e528bbecSMaulik Shah #endif 32*e528bbecSMaulik Shah 335bd9c17dSSaurabh Gorecha /* Make composite power state parameter till level 0 */ 345bd9c17dSSaurabh Gorecha #define qti_make_pwrstate_lvl0(lvl0_state, type) \ 355bd9c17dSSaurabh Gorecha (((lvl0_state) << PSTATE_ID_SHIFT) | ((type) << PSTATE_TYPE_SHIFT)) 365bd9c17dSSaurabh Gorecha 375bd9c17dSSaurabh Gorecha /* Make composite power state parameter till level 1 */ 385bd9c17dSSaurabh Gorecha #define qti_make_pwrstate_lvl1(lvl1_state, lvl0_state, type) \ 395bd9c17dSSaurabh Gorecha (((lvl1_state) << QTI_LOCAL_PSTATE_WIDTH) | \ 405bd9c17dSSaurabh Gorecha qti_make_pwrstate_lvl0(lvl0_state, type)) 415bd9c17dSSaurabh Gorecha 425bd9c17dSSaurabh Gorecha /* Make composite power state parameter till level 2 */ 435bd9c17dSSaurabh Gorecha #define qti_make_pwrstate_lvl2(lvl2_state, lvl1_state, lvl0_state, type) \ 445bd9c17dSSaurabh Gorecha (((lvl2_state) << (QTI_LOCAL_PSTATE_WIDTH * 2)) | \ 455bd9c17dSSaurabh Gorecha qti_make_pwrstate_lvl1(lvl1_state, lvl0_state, type)) 465bd9c17dSSaurabh Gorecha 475bd9c17dSSaurabh Gorecha /* Make composite power state parameter till level 3 */ 485bd9c17dSSaurabh Gorecha #define qti_make_pwrstate_lvl3(lvl3_state, lvl2_state, lvl1_state, lvl0_state, type) \ 495bd9c17dSSaurabh Gorecha (((lvl3_state) << (QTI_LOCAL_PSTATE_WIDTH * 3)) | \ 505bd9c17dSSaurabh Gorecha qti_make_pwrstate_lvl2(lvl2_state, lvl1_state, lvl0_state, type)) 515bd9c17dSSaurabh Gorecha 525bd9c17dSSaurabh Gorecha /* QTI_CORE_PWRDN_EN_MASK happens to be same across all CPUs */ 535bd9c17dSSaurabh Gorecha #define QTI_CORE_PWRDN_EN_MASK 1 545bd9c17dSSaurabh Gorecha 555bd9c17dSSaurabh Gorecha /* cpu power control happens to be same across all CPUs */ 56c2fb8ef6SAndre Przywara DEFINE_RENAME_SYSREG_RW_FUNCS(cpu_pwrctrl_val, S3_0_C15_C2_7) 575bd9c17dSSaurabh Gorecha 585bd9c17dSSaurabh Gorecha const unsigned int qti_pm_idle_states[] = { 595bd9c17dSSaurabh Gorecha qti_make_pwrstate_lvl0(QTI_LOCAL_STATE_OFF, 605bd9c17dSSaurabh Gorecha PSTATE_TYPE_POWERDOWN), 615bd9c17dSSaurabh Gorecha qti_make_pwrstate_lvl0(QTI_LOCAL_STATE_DEEPOFF, 625bd9c17dSSaurabh Gorecha PSTATE_TYPE_POWERDOWN), 635bd9c17dSSaurabh Gorecha qti_make_pwrstate_lvl1(QTI_LOCAL_STATE_DEEPOFF, 645bd9c17dSSaurabh Gorecha QTI_LOCAL_STATE_DEEPOFF, 655bd9c17dSSaurabh Gorecha PSTATE_TYPE_POWERDOWN), 665bd9c17dSSaurabh Gorecha qti_make_pwrstate_lvl2(QTI_LOCAL_STATE_OFF, 675bd9c17dSSaurabh Gorecha QTI_LOCAL_STATE_DEEPOFF, 685bd9c17dSSaurabh Gorecha QTI_LOCAL_STATE_DEEPOFF, 695bd9c17dSSaurabh Gorecha PSTATE_TYPE_POWERDOWN), 705bd9c17dSSaurabh Gorecha qti_make_pwrstate_lvl3(QTI_LOCAL_STATE_OFF, 715bd9c17dSSaurabh Gorecha QTI_LOCAL_STATE_DEEPOFF, 725bd9c17dSSaurabh Gorecha QTI_LOCAL_STATE_DEEPOFF, 735bd9c17dSSaurabh Gorecha QTI_LOCAL_STATE_DEEPOFF, 745bd9c17dSSaurabh Gorecha PSTATE_TYPE_POWERDOWN), 755bd9c17dSSaurabh Gorecha 0, 765bd9c17dSSaurabh Gorecha }; 775bd9c17dSSaurabh Gorecha 785bd9c17dSSaurabh Gorecha /******************************************************************************* 795bd9c17dSSaurabh Gorecha * QTI standard platform handler called to check the validity of the power 805bd9c17dSSaurabh Gorecha * state parameter. The power state parameter has to be a composite power 815bd9c17dSSaurabh Gorecha * state. 825bd9c17dSSaurabh Gorecha ******************************************************************************/ 835bd9c17dSSaurabh Gorecha int qti_validate_power_state(unsigned int power_state, 845bd9c17dSSaurabh Gorecha psci_power_state_t *req_state) 855bd9c17dSSaurabh Gorecha { 865bd9c17dSSaurabh Gorecha unsigned int state_id; 875bd9c17dSSaurabh Gorecha int i; 885bd9c17dSSaurabh Gorecha 895bd9c17dSSaurabh Gorecha assert(req_state); 905bd9c17dSSaurabh Gorecha 915bd9c17dSSaurabh Gorecha /* 925bd9c17dSSaurabh Gorecha * Currently we are using a linear search for finding the matching 935bd9c17dSSaurabh Gorecha * entry in the idle power state array. This can be made a binary 945bd9c17dSSaurabh Gorecha * search if the number of entries justify the additional complexity. 955bd9c17dSSaurabh Gorecha */ 965bd9c17dSSaurabh Gorecha for (i = 0; !!qti_pm_idle_states[i]; i++) { 97*e528bbecSMaulik Shah #if PSCI_OS_INIT_MODE 98*e528bbecSMaulik Shah if ((power_state & ~QTI_LAST_AT_PLVL_MASK) == 99*e528bbecSMaulik Shah qti_pm_idle_states[i]) 100*e528bbecSMaulik Shah #else 1015bd9c17dSSaurabh Gorecha if (power_state == qti_pm_idle_states[i]) 102*e528bbecSMaulik Shah #endif 1035bd9c17dSSaurabh Gorecha break; 1045bd9c17dSSaurabh Gorecha } 1055bd9c17dSSaurabh Gorecha 1065bd9c17dSSaurabh Gorecha /* Return error if entry not found in the idle state array */ 1075bd9c17dSSaurabh Gorecha if (!qti_pm_idle_states[i]) 1085bd9c17dSSaurabh Gorecha return PSCI_E_INVALID_PARAMS; 1095bd9c17dSSaurabh Gorecha 1105bd9c17dSSaurabh Gorecha i = 0; 1115bd9c17dSSaurabh Gorecha state_id = psci_get_pstate_id(power_state); 1125bd9c17dSSaurabh Gorecha 1135bd9c17dSSaurabh Gorecha /* Parse the State ID and populate the state info parameter */ 114*e528bbecSMaulik Shah for (i = QTI_PWR_LVL0; i <= PLAT_MAX_PWR_LVL; i++) { 115*e528bbecSMaulik Shah req_state->pwr_domain_state[i] = state_id & 1165bd9c17dSSaurabh Gorecha QTI_LOCAL_PSTATE_MASK; 1175bd9c17dSSaurabh Gorecha state_id >>= QTI_LOCAL_PSTATE_WIDTH; 1185bd9c17dSSaurabh Gorecha } 119*e528bbecSMaulik Shah #if PSCI_OS_INIT_MODE 120*e528bbecSMaulik Shah req_state->last_at_pwrlvl = state_id & QTI_LOCAL_PSTATE_MASK; 121*e528bbecSMaulik Shah #endif 1225bd9c17dSSaurabh Gorecha 1235bd9c17dSSaurabh Gorecha return PSCI_E_SUCCESS; 1245bd9c17dSSaurabh Gorecha } 1255bd9c17dSSaurabh Gorecha 1265bd9c17dSSaurabh Gorecha /******************************************************************************* 1275bd9c17dSSaurabh Gorecha * PLATFORM FUNCTIONS 1285bd9c17dSSaurabh Gorecha ******************************************************************************/ 1295bd9c17dSSaurabh Gorecha 1305bd9c17dSSaurabh Gorecha static void qti_set_cpupwrctlr_val(void) 1315bd9c17dSSaurabh Gorecha { 1325bd9c17dSSaurabh Gorecha unsigned long val; 1335bd9c17dSSaurabh Gorecha 1345bd9c17dSSaurabh Gorecha val = read_cpu_pwrctrl_val(); 1355bd9c17dSSaurabh Gorecha val |= QTI_CORE_PWRDN_EN_MASK; 1365bd9c17dSSaurabh Gorecha write_cpu_pwrctrl_val(val); 1375bd9c17dSSaurabh Gorecha 1385bd9c17dSSaurabh Gorecha isb(); 1395bd9c17dSSaurabh Gorecha } 1405bd9c17dSSaurabh Gorecha 1415bd9c17dSSaurabh Gorecha /** 1425bd9c17dSSaurabh Gorecha * CPU power on function - ideally we want a wrapper since this function is 1435bd9c17dSSaurabh Gorecha * target specific. But to unblock teams. 1445bd9c17dSSaurabh Gorecha */ 1455bd9c17dSSaurabh Gorecha static int qti_cpu_power_on(u_register_t mpidr) 1465bd9c17dSSaurabh Gorecha { 1475bd9c17dSSaurabh Gorecha int core_pos = plat_core_pos_by_mpidr(mpidr); 1485bd9c17dSSaurabh Gorecha 1495bd9c17dSSaurabh Gorecha /* If not valid mpidr, return error */ 1505bd9c17dSSaurabh Gorecha if (core_pos < 0 || core_pos >= QTISECLIB_PLAT_CORE_COUNT) { 1515bd9c17dSSaurabh Gorecha return PSCI_E_INVALID_PARAMS; 1525bd9c17dSSaurabh Gorecha } 1535bd9c17dSSaurabh Gorecha 1545bd9c17dSSaurabh Gorecha return qtiseclib_psci_node_power_on(mpidr); 1555bd9c17dSSaurabh Gorecha } 1565bd9c17dSSaurabh Gorecha 1575bd9c17dSSaurabh Gorecha static bool is_cpu_off(const psci_power_state_t *target_state) 1585bd9c17dSSaurabh Gorecha { 1595bd9c17dSSaurabh Gorecha if ((target_state->pwr_domain_state[QTI_PWR_LVL0] == 1605bd9c17dSSaurabh Gorecha QTI_LOCAL_STATE_OFF) || 1615bd9c17dSSaurabh Gorecha (target_state->pwr_domain_state[QTI_PWR_LVL0] == 1625bd9c17dSSaurabh Gorecha QTI_LOCAL_STATE_DEEPOFF)) { 1635bd9c17dSSaurabh Gorecha return true; 1645bd9c17dSSaurabh Gorecha } else { 1655bd9c17dSSaurabh Gorecha return false; 1665bd9c17dSSaurabh Gorecha } 1675bd9c17dSSaurabh Gorecha } 1685bd9c17dSSaurabh Gorecha 1695bd9c17dSSaurabh Gorecha static void qti_cpu_power_on_finish(const psci_power_state_t *target_state) 1705bd9c17dSSaurabh Gorecha { 1715bd9c17dSSaurabh Gorecha const uint8_t *pwr_states = 1725bd9c17dSSaurabh Gorecha (const uint8_t *)target_state->pwr_domain_state; 1735bd9c17dSSaurabh Gorecha qtiseclib_psci_node_on_finish(pwr_states); 1745bd9c17dSSaurabh Gorecha 1755bd9c17dSSaurabh Gorecha if (is_cpu_off(target_state)) { 1765bd9c17dSSaurabh Gorecha plat_qti_gic_cpuif_enable(); 1775bd9c17dSSaurabh Gorecha } 1785bd9c17dSSaurabh Gorecha } 1795bd9c17dSSaurabh Gorecha 1805bd9c17dSSaurabh Gorecha static void qti_cpu_standby(plat_local_state_t cpu_state) 1815bd9c17dSSaurabh Gorecha { 1825bd9c17dSSaurabh Gorecha } 1835bd9c17dSSaurabh Gorecha 1845bd9c17dSSaurabh Gorecha static void qti_node_power_off(const psci_power_state_t *target_state) 1855bd9c17dSSaurabh Gorecha { 1865bd9c17dSSaurabh Gorecha qtiseclib_psci_node_power_off((const uint8_t *) 1875bd9c17dSSaurabh Gorecha target_state->pwr_domain_state); 1885bd9c17dSSaurabh Gorecha if (is_cpu_off(target_state)) { 1895bd9c17dSSaurabh Gorecha plat_qti_gic_cpuif_disable(); 1905bd9c17dSSaurabh Gorecha qti_set_cpupwrctlr_val(); 1915bd9c17dSSaurabh Gorecha } 1925bd9c17dSSaurabh Gorecha } 1935bd9c17dSSaurabh Gorecha 194*e528bbecSMaulik Shah #if PSCI_OS_INIT_MODE 195*e528bbecSMaulik Shah static int qti_node_suspend(const psci_power_state_t *target_state) 196*e528bbecSMaulik Shah { 197*e528bbecSMaulik Shah qtiseclib_psci_node_suspend((const uint8_t *)target_state-> 198*e528bbecSMaulik Shah pwr_domain_state); 199*e528bbecSMaulik Shah if (is_cpu_off(target_state)) { 200*e528bbecSMaulik Shah plat_qti_gic_cpuif_disable(); 201*e528bbecSMaulik Shah qti_set_cpupwrctlr_val(); 202*e528bbecSMaulik Shah } 203*e528bbecSMaulik Shah return PSCI_E_SUCCESS; 204*e528bbecSMaulik Shah } 205*e528bbecSMaulik Shah #else 2065bd9c17dSSaurabh Gorecha static void qti_node_suspend(const psci_power_state_t *target_state) 2075bd9c17dSSaurabh Gorecha { 2085bd9c17dSSaurabh Gorecha qtiseclib_psci_node_suspend((const uint8_t *)target_state-> 2095bd9c17dSSaurabh Gorecha pwr_domain_state); 2105bd9c17dSSaurabh Gorecha if (is_cpu_off(target_state)) { 2115bd9c17dSSaurabh Gorecha plat_qti_gic_cpuif_disable(); 2125bd9c17dSSaurabh Gorecha qti_set_cpupwrctlr_val(); 2135bd9c17dSSaurabh Gorecha } 2145bd9c17dSSaurabh Gorecha } 215*e528bbecSMaulik Shah #endif 2165bd9c17dSSaurabh Gorecha 2175bd9c17dSSaurabh Gorecha static void qti_node_suspend_finish(const psci_power_state_t *target_state) 2185bd9c17dSSaurabh Gorecha { 2195bd9c17dSSaurabh Gorecha const uint8_t *pwr_states = 2205bd9c17dSSaurabh Gorecha (const uint8_t *)target_state->pwr_domain_state; 2215bd9c17dSSaurabh Gorecha qtiseclib_psci_node_suspend_finish(pwr_states); 2225bd9c17dSSaurabh Gorecha if (is_cpu_off(target_state)) { 2235bd9c17dSSaurabh Gorecha plat_qti_gic_cpuif_enable(); 2245bd9c17dSSaurabh Gorecha } 2255bd9c17dSSaurabh Gorecha } 2265bd9c17dSSaurabh Gorecha 2275bd9c17dSSaurabh Gorecha __dead2 void qti_domain_power_down_wfi(const psci_power_state_t *target_state) 2285bd9c17dSSaurabh Gorecha { 2295bd9c17dSSaurabh Gorecha 2305bd9c17dSSaurabh Gorecha /* For now just do WFI - add any target specific handling if needed */ 2315bd9c17dSSaurabh Gorecha psci_power_down_wfi(); 2325bd9c17dSSaurabh Gorecha /* We should never reach here */ 2335bd9c17dSSaurabh Gorecha } 2345bd9c17dSSaurabh Gorecha 235522a2277SJulius Werner static __dead2 void assert_ps_hold(void) 236522a2277SJulius Werner { 237522a2277SJulius Werner mmio_write_32(QTI_PS_HOLD_REG, 0); 238522a2277SJulius Werner mdelay(1000); 239522a2277SJulius Werner 240522a2277SJulius Werner /* Should be dead before reaching this. */ 241522a2277SJulius Werner panic(); 242522a2277SJulius Werner } 243522a2277SJulius Werner 2445bd9c17dSSaurabh Gorecha __dead2 void qti_system_off(void) 2455bd9c17dSSaurabh Gorecha { 246522a2277SJulius Werner qti_pmic_prepare_shutdown(); 247522a2277SJulius Werner assert_ps_hold(); 2485bd9c17dSSaurabh Gorecha } 2495bd9c17dSSaurabh Gorecha 2505bd9c17dSSaurabh Gorecha __dead2 void qti_system_reset(void) 2515bd9c17dSSaurabh Gorecha { 252522a2277SJulius Werner qti_pmic_prepare_reset(); 253522a2277SJulius Werner assert_ps_hold(); 2545bd9c17dSSaurabh Gorecha } 2555bd9c17dSSaurabh Gorecha 2565bd9c17dSSaurabh Gorecha void qti_get_sys_suspend_power_state(psci_power_state_t *req_state) 2575bd9c17dSSaurabh Gorecha { 2585bd9c17dSSaurabh Gorecha int i = 0; 2595bd9c17dSSaurabh Gorecha unsigned int state_id, power_state; 2605bd9c17dSSaurabh Gorecha int size = ARRAY_SIZE(qti_pm_idle_states); 2615bd9c17dSSaurabh Gorecha 2625bd9c17dSSaurabh Gorecha /* 2635bd9c17dSSaurabh Gorecha * Find deepest state. 2645bd9c17dSSaurabh Gorecha * The arm_pm_idle_states[] array has last element by default 0, 2655bd9c17dSSaurabh Gorecha * so the real deepest state is second last element of that array. 2665bd9c17dSSaurabh Gorecha */ 2675bd9c17dSSaurabh Gorecha power_state = qti_pm_idle_states[size - 2]; 2685bd9c17dSSaurabh Gorecha state_id = psci_get_pstate_id(power_state); 2695bd9c17dSSaurabh Gorecha 2705bd9c17dSSaurabh Gorecha /* Parse the State ID and populate the state info parameter */ 2715bd9c17dSSaurabh Gorecha while (state_id) { 2725bd9c17dSSaurabh Gorecha req_state->pwr_domain_state[i++] = 2735bd9c17dSSaurabh Gorecha state_id & QTI_LOCAL_PSTATE_MASK; 2745bd9c17dSSaurabh Gorecha state_id >>= QTI_LOCAL_PSTATE_WIDTH; 2755bd9c17dSSaurabh Gorecha } 2765bd9c17dSSaurabh Gorecha } 2775bd9c17dSSaurabh Gorecha 2785bd9c17dSSaurabh Gorecha /* 2795bd9c17dSSaurabh Gorecha * Structure containing platform specific PSCI operations. Common 2805bd9c17dSSaurabh Gorecha * PSCI layer will use this. 2815bd9c17dSSaurabh Gorecha */ 2825bd9c17dSSaurabh Gorecha const plat_psci_ops_t plat_qti_psci_pm_ops = { 2835bd9c17dSSaurabh Gorecha .pwr_domain_on = qti_cpu_power_on, 2845bd9c17dSSaurabh Gorecha .pwr_domain_on_finish = qti_cpu_power_on_finish, 2855bd9c17dSSaurabh Gorecha .cpu_standby = qti_cpu_standby, 2865bd9c17dSSaurabh Gorecha .pwr_domain_off = qti_node_power_off, 2875bd9c17dSSaurabh Gorecha .pwr_domain_suspend = qti_node_suspend, 2885bd9c17dSSaurabh Gorecha .pwr_domain_suspend_finish = qti_node_suspend_finish, 2895bd9c17dSSaurabh Gorecha .pwr_domain_pwr_down_wfi = qti_domain_power_down_wfi, 2905bd9c17dSSaurabh Gorecha .system_off = qti_system_off, 2915bd9c17dSSaurabh Gorecha .system_reset = qti_system_reset, 2925bd9c17dSSaurabh Gorecha .get_node_hw_state = NULL, 2935bd9c17dSSaurabh Gorecha .translate_power_state_by_mpidr = NULL, 2945bd9c17dSSaurabh Gorecha .get_sys_suspend_power_state = qti_get_sys_suspend_power_state, 2955bd9c17dSSaurabh Gorecha .validate_power_state = qti_validate_power_state, 2965bd9c17dSSaurabh Gorecha }; 2975bd9c17dSSaurabh Gorecha 2985bd9c17dSSaurabh Gorecha /** 2995bd9c17dSSaurabh Gorecha * The QTI Standard platform definition of platform porting API 3005bd9c17dSSaurabh Gorecha * `plat_setup_psci_ops`. 3015bd9c17dSSaurabh Gorecha */ 3025bd9c17dSSaurabh Gorecha int plat_setup_psci_ops(uintptr_t sec_entrypoint, 3035bd9c17dSSaurabh Gorecha const plat_psci_ops_t **psci_ops) 3045bd9c17dSSaurabh Gorecha { 3055bd9c17dSSaurabh Gorecha int err; 3065bd9c17dSSaurabh Gorecha 3075bd9c17dSSaurabh Gorecha err = qtiseclib_psci_init((uintptr_t)bl31_warm_entrypoint); 3085bd9c17dSSaurabh Gorecha if (err == PSCI_E_SUCCESS) { 3095bd9c17dSSaurabh Gorecha *psci_ops = &plat_qti_psci_pm_ops; 3105bd9c17dSSaurabh Gorecha } 3115bd9c17dSSaurabh Gorecha 3125bd9c17dSSaurabh Gorecha return err; 3135bd9c17dSSaurabh Gorecha } 314