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