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 275bd9c17dSSaurabh Gorecha /* Make composite power state parameter till level 0 */ 285bd9c17dSSaurabh Gorecha #define qti_make_pwrstate_lvl0(lvl0_state, type) \ 295bd9c17dSSaurabh Gorecha (((lvl0_state) << PSTATE_ID_SHIFT) | ((type) << PSTATE_TYPE_SHIFT)) 305bd9c17dSSaurabh Gorecha 315bd9c17dSSaurabh Gorecha /* Make composite power state parameter till level 1 */ 325bd9c17dSSaurabh Gorecha #define qti_make_pwrstate_lvl1(lvl1_state, lvl0_state, type) \ 335bd9c17dSSaurabh Gorecha (((lvl1_state) << QTI_LOCAL_PSTATE_WIDTH) | \ 345bd9c17dSSaurabh Gorecha qti_make_pwrstate_lvl0(lvl0_state, type)) 355bd9c17dSSaurabh Gorecha 365bd9c17dSSaurabh Gorecha /* Make composite power state parameter till level 2 */ 375bd9c17dSSaurabh Gorecha #define qti_make_pwrstate_lvl2(lvl2_state, lvl1_state, lvl0_state, type) \ 385bd9c17dSSaurabh Gorecha (((lvl2_state) << (QTI_LOCAL_PSTATE_WIDTH * 2)) | \ 395bd9c17dSSaurabh Gorecha qti_make_pwrstate_lvl1(lvl1_state, lvl0_state, type)) 405bd9c17dSSaurabh Gorecha 415bd9c17dSSaurabh Gorecha /* Make composite power state parameter till level 3 */ 425bd9c17dSSaurabh Gorecha #define qti_make_pwrstate_lvl3(lvl3_state, lvl2_state, lvl1_state, lvl0_state, type) \ 435bd9c17dSSaurabh Gorecha (((lvl3_state) << (QTI_LOCAL_PSTATE_WIDTH * 3)) | \ 445bd9c17dSSaurabh Gorecha qti_make_pwrstate_lvl2(lvl2_state, lvl1_state, lvl0_state, type)) 455bd9c17dSSaurabh Gorecha 465bd9c17dSSaurabh Gorecha /* QTI_CORE_PWRDN_EN_MASK happens to be same across all CPUs */ 475bd9c17dSSaurabh Gorecha #define QTI_CORE_PWRDN_EN_MASK 1 485bd9c17dSSaurabh Gorecha 495bd9c17dSSaurabh Gorecha /* cpu power control happens to be same across all CPUs */ 50*c2fb8ef6SAndre Przywara DEFINE_RENAME_SYSREG_RW_FUNCS(cpu_pwrctrl_val, S3_0_C15_C2_7) 515bd9c17dSSaurabh Gorecha 525bd9c17dSSaurabh Gorecha const unsigned int qti_pm_idle_states[] = { 535bd9c17dSSaurabh Gorecha qti_make_pwrstate_lvl0(QTI_LOCAL_STATE_OFF, 545bd9c17dSSaurabh Gorecha PSTATE_TYPE_POWERDOWN), 555bd9c17dSSaurabh Gorecha qti_make_pwrstate_lvl0(QTI_LOCAL_STATE_DEEPOFF, 565bd9c17dSSaurabh Gorecha PSTATE_TYPE_POWERDOWN), 575bd9c17dSSaurabh Gorecha qti_make_pwrstate_lvl1(QTI_LOCAL_STATE_DEEPOFF, 585bd9c17dSSaurabh Gorecha QTI_LOCAL_STATE_DEEPOFF, 595bd9c17dSSaurabh Gorecha PSTATE_TYPE_POWERDOWN), 605bd9c17dSSaurabh Gorecha qti_make_pwrstate_lvl2(QTI_LOCAL_STATE_OFF, 615bd9c17dSSaurabh Gorecha QTI_LOCAL_STATE_DEEPOFF, 625bd9c17dSSaurabh Gorecha QTI_LOCAL_STATE_DEEPOFF, 635bd9c17dSSaurabh Gorecha PSTATE_TYPE_POWERDOWN), 645bd9c17dSSaurabh Gorecha qti_make_pwrstate_lvl3(QTI_LOCAL_STATE_OFF, 655bd9c17dSSaurabh Gorecha QTI_LOCAL_STATE_DEEPOFF, 665bd9c17dSSaurabh Gorecha QTI_LOCAL_STATE_DEEPOFF, 675bd9c17dSSaurabh Gorecha QTI_LOCAL_STATE_DEEPOFF, 685bd9c17dSSaurabh Gorecha PSTATE_TYPE_POWERDOWN), 695bd9c17dSSaurabh Gorecha 0, 705bd9c17dSSaurabh Gorecha }; 715bd9c17dSSaurabh Gorecha 725bd9c17dSSaurabh Gorecha /******************************************************************************* 735bd9c17dSSaurabh Gorecha * QTI standard platform handler called to check the validity of the power 745bd9c17dSSaurabh Gorecha * state parameter. The power state parameter has to be a composite power 755bd9c17dSSaurabh Gorecha * state. 765bd9c17dSSaurabh Gorecha ******************************************************************************/ 775bd9c17dSSaurabh Gorecha int qti_validate_power_state(unsigned int power_state, 785bd9c17dSSaurabh Gorecha psci_power_state_t *req_state) 795bd9c17dSSaurabh Gorecha { 805bd9c17dSSaurabh Gorecha unsigned int state_id; 815bd9c17dSSaurabh Gorecha int i; 825bd9c17dSSaurabh Gorecha 835bd9c17dSSaurabh Gorecha assert(req_state); 845bd9c17dSSaurabh Gorecha 855bd9c17dSSaurabh Gorecha /* 865bd9c17dSSaurabh Gorecha * Currently we are using a linear search for finding the matching 875bd9c17dSSaurabh Gorecha * entry in the idle power state array. This can be made a binary 885bd9c17dSSaurabh Gorecha * search if the number of entries justify the additional complexity. 895bd9c17dSSaurabh Gorecha */ 905bd9c17dSSaurabh Gorecha for (i = 0; !!qti_pm_idle_states[i]; i++) { 915bd9c17dSSaurabh Gorecha if (power_state == qti_pm_idle_states[i]) 925bd9c17dSSaurabh Gorecha break; 935bd9c17dSSaurabh Gorecha } 945bd9c17dSSaurabh Gorecha 955bd9c17dSSaurabh Gorecha /* Return error if entry not found in the idle state array */ 965bd9c17dSSaurabh Gorecha if (!qti_pm_idle_states[i]) 975bd9c17dSSaurabh Gorecha return PSCI_E_INVALID_PARAMS; 985bd9c17dSSaurabh Gorecha 995bd9c17dSSaurabh Gorecha i = 0; 1005bd9c17dSSaurabh Gorecha state_id = psci_get_pstate_id(power_state); 1015bd9c17dSSaurabh Gorecha 1025bd9c17dSSaurabh Gorecha /* Parse the State ID and populate the state info parameter */ 1035bd9c17dSSaurabh Gorecha while (state_id) { 1045bd9c17dSSaurabh Gorecha req_state->pwr_domain_state[i++] = state_id & 1055bd9c17dSSaurabh Gorecha QTI_LOCAL_PSTATE_MASK; 1065bd9c17dSSaurabh Gorecha state_id >>= QTI_LOCAL_PSTATE_WIDTH; 1075bd9c17dSSaurabh Gorecha } 1085bd9c17dSSaurabh Gorecha 1095bd9c17dSSaurabh Gorecha return PSCI_E_SUCCESS; 1105bd9c17dSSaurabh Gorecha } 1115bd9c17dSSaurabh Gorecha 1125bd9c17dSSaurabh Gorecha /******************************************************************************* 1135bd9c17dSSaurabh Gorecha * PLATFORM FUNCTIONS 1145bd9c17dSSaurabh Gorecha ******************************************************************************/ 1155bd9c17dSSaurabh Gorecha 1165bd9c17dSSaurabh Gorecha static void qti_set_cpupwrctlr_val(void) 1175bd9c17dSSaurabh Gorecha { 1185bd9c17dSSaurabh Gorecha unsigned long val; 1195bd9c17dSSaurabh Gorecha 1205bd9c17dSSaurabh Gorecha val = read_cpu_pwrctrl_val(); 1215bd9c17dSSaurabh Gorecha val |= QTI_CORE_PWRDN_EN_MASK; 1225bd9c17dSSaurabh Gorecha write_cpu_pwrctrl_val(val); 1235bd9c17dSSaurabh Gorecha 1245bd9c17dSSaurabh Gorecha isb(); 1255bd9c17dSSaurabh Gorecha } 1265bd9c17dSSaurabh Gorecha 1275bd9c17dSSaurabh Gorecha /** 1285bd9c17dSSaurabh Gorecha * CPU power on function - ideally we want a wrapper since this function is 1295bd9c17dSSaurabh Gorecha * target specific. But to unblock teams. 1305bd9c17dSSaurabh Gorecha */ 1315bd9c17dSSaurabh Gorecha static int qti_cpu_power_on(u_register_t mpidr) 1325bd9c17dSSaurabh Gorecha { 1335bd9c17dSSaurabh Gorecha int core_pos = plat_core_pos_by_mpidr(mpidr); 1345bd9c17dSSaurabh Gorecha 1355bd9c17dSSaurabh Gorecha /* If not valid mpidr, return error */ 1365bd9c17dSSaurabh Gorecha if (core_pos < 0 || core_pos >= QTISECLIB_PLAT_CORE_COUNT) { 1375bd9c17dSSaurabh Gorecha return PSCI_E_INVALID_PARAMS; 1385bd9c17dSSaurabh Gorecha } 1395bd9c17dSSaurabh Gorecha 1405bd9c17dSSaurabh Gorecha return qtiseclib_psci_node_power_on(mpidr); 1415bd9c17dSSaurabh Gorecha } 1425bd9c17dSSaurabh Gorecha 1435bd9c17dSSaurabh Gorecha static bool is_cpu_off(const psci_power_state_t *target_state) 1445bd9c17dSSaurabh Gorecha { 1455bd9c17dSSaurabh Gorecha if ((target_state->pwr_domain_state[QTI_PWR_LVL0] == 1465bd9c17dSSaurabh Gorecha QTI_LOCAL_STATE_OFF) || 1475bd9c17dSSaurabh Gorecha (target_state->pwr_domain_state[QTI_PWR_LVL0] == 1485bd9c17dSSaurabh Gorecha QTI_LOCAL_STATE_DEEPOFF)) { 1495bd9c17dSSaurabh Gorecha return true; 1505bd9c17dSSaurabh Gorecha } else { 1515bd9c17dSSaurabh Gorecha return false; 1525bd9c17dSSaurabh Gorecha } 1535bd9c17dSSaurabh Gorecha } 1545bd9c17dSSaurabh Gorecha 1555bd9c17dSSaurabh Gorecha static void qti_cpu_power_on_finish(const psci_power_state_t *target_state) 1565bd9c17dSSaurabh Gorecha { 1575bd9c17dSSaurabh Gorecha const uint8_t *pwr_states = 1585bd9c17dSSaurabh Gorecha (const uint8_t *)target_state->pwr_domain_state; 1595bd9c17dSSaurabh Gorecha qtiseclib_psci_node_on_finish(pwr_states); 1605bd9c17dSSaurabh Gorecha 1615bd9c17dSSaurabh Gorecha if (is_cpu_off(target_state)) { 1625bd9c17dSSaurabh Gorecha plat_qti_gic_cpuif_enable(); 1635bd9c17dSSaurabh Gorecha } 1645bd9c17dSSaurabh Gorecha } 1655bd9c17dSSaurabh Gorecha 1665bd9c17dSSaurabh Gorecha static void qti_cpu_standby(plat_local_state_t cpu_state) 1675bd9c17dSSaurabh Gorecha { 1685bd9c17dSSaurabh Gorecha } 1695bd9c17dSSaurabh Gorecha 1705bd9c17dSSaurabh Gorecha static void qti_node_power_off(const psci_power_state_t *target_state) 1715bd9c17dSSaurabh Gorecha { 1725bd9c17dSSaurabh Gorecha qtiseclib_psci_node_power_off((const uint8_t *) 1735bd9c17dSSaurabh Gorecha target_state->pwr_domain_state); 1745bd9c17dSSaurabh Gorecha if (is_cpu_off(target_state)) { 1755bd9c17dSSaurabh Gorecha plat_qti_gic_cpuif_disable(); 1765bd9c17dSSaurabh Gorecha qti_set_cpupwrctlr_val(); 1775bd9c17dSSaurabh Gorecha } 1785bd9c17dSSaurabh Gorecha } 1795bd9c17dSSaurabh Gorecha 1805bd9c17dSSaurabh Gorecha static void qti_node_suspend(const psci_power_state_t *target_state) 1815bd9c17dSSaurabh Gorecha { 1825bd9c17dSSaurabh Gorecha qtiseclib_psci_node_suspend((const uint8_t *)target_state-> 1835bd9c17dSSaurabh Gorecha pwr_domain_state); 1845bd9c17dSSaurabh Gorecha if (is_cpu_off(target_state)) { 1855bd9c17dSSaurabh Gorecha plat_qti_gic_cpuif_disable(); 1865bd9c17dSSaurabh Gorecha qti_set_cpupwrctlr_val(); 1875bd9c17dSSaurabh Gorecha } 1885bd9c17dSSaurabh Gorecha } 1895bd9c17dSSaurabh Gorecha 1905bd9c17dSSaurabh Gorecha static void qti_node_suspend_finish(const psci_power_state_t *target_state) 1915bd9c17dSSaurabh Gorecha { 1925bd9c17dSSaurabh Gorecha const uint8_t *pwr_states = 1935bd9c17dSSaurabh Gorecha (const uint8_t *)target_state->pwr_domain_state; 1945bd9c17dSSaurabh Gorecha qtiseclib_psci_node_suspend_finish(pwr_states); 1955bd9c17dSSaurabh Gorecha if (is_cpu_off(target_state)) { 1965bd9c17dSSaurabh Gorecha plat_qti_gic_cpuif_enable(); 1975bd9c17dSSaurabh Gorecha } 1985bd9c17dSSaurabh Gorecha } 1995bd9c17dSSaurabh Gorecha 2005bd9c17dSSaurabh Gorecha __dead2 void qti_domain_power_down_wfi(const psci_power_state_t *target_state) 2015bd9c17dSSaurabh Gorecha { 2025bd9c17dSSaurabh Gorecha 2035bd9c17dSSaurabh Gorecha /* For now just do WFI - add any target specific handling if needed */ 2045bd9c17dSSaurabh Gorecha psci_power_down_wfi(); 2055bd9c17dSSaurabh Gorecha /* We should never reach here */ 2065bd9c17dSSaurabh Gorecha } 2075bd9c17dSSaurabh Gorecha 208522a2277SJulius Werner static __dead2 void assert_ps_hold(void) 209522a2277SJulius Werner { 210522a2277SJulius Werner mmio_write_32(QTI_PS_HOLD_REG, 0); 211522a2277SJulius Werner mdelay(1000); 212522a2277SJulius Werner 213522a2277SJulius Werner /* Should be dead before reaching this. */ 214522a2277SJulius Werner panic(); 215522a2277SJulius Werner } 216522a2277SJulius Werner 2175bd9c17dSSaurabh Gorecha __dead2 void qti_system_off(void) 2185bd9c17dSSaurabh Gorecha { 219522a2277SJulius Werner qti_pmic_prepare_shutdown(); 220522a2277SJulius Werner assert_ps_hold(); 2215bd9c17dSSaurabh Gorecha } 2225bd9c17dSSaurabh Gorecha 2235bd9c17dSSaurabh Gorecha __dead2 void qti_system_reset(void) 2245bd9c17dSSaurabh Gorecha { 225522a2277SJulius Werner qti_pmic_prepare_reset(); 226522a2277SJulius Werner assert_ps_hold(); 2275bd9c17dSSaurabh Gorecha } 2285bd9c17dSSaurabh Gorecha 2295bd9c17dSSaurabh Gorecha void qti_get_sys_suspend_power_state(psci_power_state_t *req_state) 2305bd9c17dSSaurabh Gorecha { 2315bd9c17dSSaurabh Gorecha int i = 0; 2325bd9c17dSSaurabh Gorecha unsigned int state_id, power_state; 2335bd9c17dSSaurabh Gorecha int size = ARRAY_SIZE(qti_pm_idle_states); 2345bd9c17dSSaurabh Gorecha 2355bd9c17dSSaurabh Gorecha /* 2365bd9c17dSSaurabh Gorecha * Find deepest state. 2375bd9c17dSSaurabh Gorecha * The arm_pm_idle_states[] array has last element by default 0, 2385bd9c17dSSaurabh Gorecha * so the real deepest state is second last element of that array. 2395bd9c17dSSaurabh Gorecha */ 2405bd9c17dSSaurabh Gorecha power_state = qti_pm_idle_states[size - 2]; 2415bd9c17dSSaurabh Gorecha state_id = psci_get_pstate_id(power_state); 2425bd9c17dSSaurabh Gorecha 2435bd9c17dSSaurabh Gorecha /* Parse the State ID and populate the state info parameter */ 2445bd9c17dSSaurabh Gorecha while (state_id) { 2455bd9c17dSSaurabh Gorecha req_state->pwr_domain_state[i++] = 2465bd9c17dSSaurabh Gorecha state_id & QTI_LOCAL_PSTATE_MASK; 2475bd9c17dSSaurabh Gorecha state_id >>= QTI_LOCAL_PSTATE_WIDTH; 2485bd9c17dSSaurabh Gorecha } 2495bd9c17dSSaurabh Gorecha } 2505bd9c17dSSaurabh Gorecha 2515bd9c17dSSaurabh Gorecha /* 2525bd9c17dSSaurabh Gorecha * Structure containing platform specific PSCI operations. Common 2535bd9c17dSSaurabh Gorecha * PSCI layer will use this. 2545bd9c17dSSaurabh Gorecha */ 2555bd9c17dSSaurabh Gorecha const plat_psci_ops_t plat_qti_psci_pm_ops = { 2565bd9c17dSSaurabh Gorecha .pwr_domain_on = qti_cpu_power_on, 2575bd9c17dSSaurabh Gorecha .pwr_domain_on_finish = qti_cpu_power_on_finish, 2585bd9c17dSSaurabh Gorecha .cpu_standby = qti_cpu_standby, 2595bd9c17dSSaurabh Gorecha .pwr_domain_off = qti_node_power_off, 2605bd9c17dSSaurabh Gorecha .pwr_domain_suspend = qti_node_suspend, 2615bd9c17dSSaurabh Gorecha .pwr_domain_suspend_finish = qti_node_suspend_finish, 2625bd9c17dSSaurabh Gorecha .pwr_domain_pwr_down_wfi = qti_domain_power_down_wfi, 2635bd9c17dSSaurabh Gorecha .system_off = qti_system_off, 2645bd9c17dSSaurabh Gorecha .system_reset = qti_system_reset, 2655bd9c17dSSaurabh Gorecha .get_node_hw_state = NULL, 2665bd9c17dSSaurabh Gorecha .translate_power_state_by_mpidr = NULL, 2675bd9c17dSSaurabh Gorecha .get_sys_suspend_power_state = qti_get_sys_suspend_power_state, 2685bd9c17dSSaurabh Gorecha .validate_power_state = qti_validate_power_state, 2695bd9c17dSSaurabh Gorecha }; 2705bd9c17dSSaurabh Gorecha 2715bd9c17dSSaurabh Gorecha /** 2725bd9c17dSSaurabh Gorecha * The QTI Standard platform definition of platform porting API 2735bd9c17dSSaurabh Gorecha * `plat_setup_psci_ops`. 2745bd9c17dSSaurabh Gorecha */ 2755bd9c17dSSaurabh Gorecha int plat_setup_psci_ops(uintptr_t sec_entrypoint, 2765bd9c17dSSaurabh Gorecha const plat_psci_ops_t **psci_ops) 2775bd9c17dSSaurabh Gorecha { 2785bd9c17dSSaurabh Gorecha int err; 2795bd9c17dSSaurabh Gorecha 2805bd9c17dSSaurabh Gorecha err = qtiseclib_psci_init((uintptr_t)bl31_warm_entrypoint); 2815bd9c17dSSaurabh Gorecha if (err == PSCI_E_SUCCESS) { 2825bd9c17dSSaurabh Gorecha *psci_ops = &plat_qti_psci_pm_ops; 2835bd9c17dSSaurabh Gorecha } 2845bd9c17dSSaurabh Gorecha 2855bd9c17dSSaurabh Gorecha return err; 2865bd9c17dSSaurabh Gorecha } 287