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
27e528bbecSMaulik Shah #if PSCI_OS_INIT_MODE
28e528bbecSMaulik Shah #define QTI_LAST_AT_PLVL_MASK (QTI_LOCAL_PSTATE_MASK << \
29e528bbecSMaulik Shah (QTI_LOCAL_PSTATE_WIDTH * \
30e528bbecSMaulik Shah (PLAT_MAX_PWR_LVL + 1)))
31e528bbecSMaulik Shah #endif
32e528bbecSMaulik 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 ******************************************************************************/
qti_validate_power_state(unsigned int power_state,psci_power_state_t * req_state)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++) {
97e528bbecSMaulik Shah #if PSCI_OS_INIT_MODE
98e528bbecSMaulik Shah if ((power_state & ~QTI_LAST_AT_PLVL_MASK) ==
99e528bbecSMaulik Shah qti_pm_idle_states[i])
100e528bbecSMaulik Shah #else
1015bd9c17dSSaurabh Gorecha if (power_state == qti_pm_idle_states[i])
102e528bbecSMaulik 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 */
114e528bbecSMaulik Shah for (i = QTI_PWR_LVL0; i <= PLAT_MAX_PWR_LVL; i++) {
115e528bbecSMaulik 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 }
119e528bbecSMaulik Shah #if PSCI_OS_INIT_MODE
120e528bbecSMaulik Shah req_state->last_at_pwrlvl = state_id & QTI_LOCAL_PSTATE_MASK;
121e528bbecSMaulik Shah #endif
1225bd9c17dSSaurabh Gorecha
1235bd9c17dSSaurabh Gorecha return PSCI_E_SUCCESS;
1245bd9c17dSSaurabh Gorecha }
1255bd9c17dSSaurabh Gorecha
1265bd9c17dSSaurabh Gorecha /*******************************************************************************
1275bd9c17dSSaurabh Gorecha * PLATFORM FUNCTIONS
1285bd9c17dSSaurabh Gorecha ******************************************************************************/
1295bd9c17dSSaurabh Gorecha
qti_set_cpupwrctlr_val(void)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 */
qti_cpu_power_on(u_register_t mpidr)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
is_cpu_off(const psci_power_state_t * target_state)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
qti_cpu_power_on_finish(const psci_power_state_t * target_state)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
qti_cpu_standby(plat_local_state_t cpu_state)1805bd9c17dSSaurabh Gorecha static void qti_cpu_standby(plat_local_state_t cpu_state)
1815bd9c17dSSaurabh Gorecha {
1825bd9c17dSSaurabh Gorecha }
1835bd9c17dSSaurabh Gorecha
qti_node_power_off(const psci_power_state_t * target_state)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
qti_node_suspend(const psci_power_state_t * target_state)1945bd9c17dSSaurabh Gorecha static void qti_node_suspend(const psci_power_state_t *target_state)
1955bd9c17dSSaurabh Gorecha {
1965bd9c17dSSaurabh Gorecha qtiseclib_psci_node_suspend((const uint8_t *)target_state->
1975bd9c17dSSaurabh Gorecha pwr_domain_state);
1985bd9c17dSSaurabh Gorecha if (is_cpu_off(target_state)) {
1995bd9c17dSSaurabh Gorecha plat_qti_gic_cpuif_disable();
2005bd9c17dSSaurabh Gorecha qti_set_cpupwrctlr_val();
2015bd9c17dSSaurabh Gorecha }
2025bd9c17dSSaurabh Gorecha }
2035bd9c17dSSaurabh Gorecha
qti_node_suspend_finish(const psci_power_state_t * target_state)2045bd9c17dSSaurabh Gorecha static void qti_node_suspend_finish(const psci_power_state_t *target_state)
2055bd9c17dSSaurabh Gorecha {
2065bd9c17dSSaurabh Gorecha const uint8_t *pwr_states =
2075bd9c17dSSaurabh Gorecha (const uint8_t *)target_state->pwr_domain_state;
2085bd9c17dSSaurabh Gorecha qtiseclib_psci_node_suspend_finish(pwr_states);
2095bd9c17dSSaurabh Gorecha if (is_cpu_off(target_state)) {
2105bd9c17dSSaurabh Gorecha plat_qti_gic_cpuif_enable();
2115bd9c17dSSaurabh Gorecha }
2125bd9c17dSSaurabh Gorecha }
2135bd9c17dSSaurabh Gorecha
assert_ps_hold(void)214522a2277SJulius Werner static __dead2 void assert_ps_hold(void)
215522a2277SJulius Werner {
216522a2277SJulius Werner mmio_write_32(QTI_PS_HOLD_REG, 0);
217522a2277SJulius Werner mdelay(1000);
218522a2277SJulius Werner
219522a2277SJulius Werner /* Should be dead before reaching this. */
220522a2277SJulius Werner panic();
221522a2277SJulius Werner }
222522a2277SJulius Werner
qti_system_off(void)2235bd9c17dSSaurabh Gorecha __dead2 void qti_system_off(void)
2245bd9c17dSSaurabh Gorecha {
225522a2277SJulius Werner qti_pmic_prepare_shutdown();
226522a2277SJulius Werner assert_ps_hold();
2275bd9c17dSSaurabh Gorecha }
2285bd9c17dSSaurabh Gorecha
qti_system_reset(void)2295bd9c17dSSaurabh Gorecha __dead2 void qti_system_reset(void)
2305bd9c17dSSaurabh Gorecha {
231522a2277SJulius Werner qti_pmic_prepare_reset();
232522a2277SJulius Werner assert_ps_hold();
2335bd9c17dSSaurabh Gorecha }
2345bd9c17dSSaurabh Gorecha
qti_get_sys_suspend_power_state(psci_power_state_t * req_state)2355bd9c17dSSaurabh Gorecha void qti_get_sys_suspend_power_state(psci_power_state_t *req_state)
2365bd9c17dSSaurabh Gorecha {
2375bd9c17dSSaurabh Gorecha int i = 0;
2385bd9c17dSSaurabh Gorecha unsigned int state_id, power_state;
2395bd9c17dSSaurabh Gorecha int size = ARRAY_SIZE(qti_pm_idle_states);
2405bd9c17dSSaurabh Gorecha
2415bd9c17dSSaurabh Gorecha /*
2425bd9c17dSSaurabh Gorecha * Find deepest state.
2435bd9c17dSSaurabh Gorecha * The arm_pm_idle_states[] array has last element by default 0,
2445bd9c17dSSaurabh Gorecha * so the real deepest state is second last element of that array.
2455bd9c17dSSaurabh Gorecha */
2465bd9c17dSSaurabh Gorecha power_state = qti_pm_idle_states[size - 2];
2475bd9c17dSSaurabh Gorecha state_id = psci_get_pstate_id(power_state);
2485bd9c17dSSaurabh Gorecha
2495bd9c17dSSaurabh Gorecha /* Parse the State ID and populate the state info parameter */
2505bd9c17dSSaurabh Gorecha while (state_id) {
2515bd9c17dSSaurabh Gorecha req_state->pwr_domain_state[i++] =
2525bd9c17dSSaurabh Gorecha state_id & QTI_LOCAL_PSTATE_MASK;
2535bd9c17dSSaurabh Gorecha state_id >>= QTI_LOCAL_PSTATE_WIDTH;
2545bd9c17dSSaurabh Gorecha }
255*0a9270abSWing Li
256*0a9270abSWing Li #if PSCI_OS_INIT_MODE
257*0a9270abSWing Li req_state->last_at_pwrlvl = PLAT_MAX_PWR_LVL;
258*0a9270abSWing Li #endif
2595bd9c17dSSaurabh Gorecha }
2605bd9c17dSSaurabh Gorecha
2615bd9c17dSSaurabh Gorecha /*
2625bd9c17dSSaurabh Gorecha * Structure containing platform specific PSCI operations. Common
2635bd9c17dSSaurabh Gorecha * PSCI layer will use this.
2645bd9c17dSSaurabh Gorecha */
2655bd9c17dSSaurabh Gorecha const plat_psci_ops_t plat_qti_psci_pm_ops = {
2665bd9c17dSSaurabh Gorecha .pwr_domain_on = qti_cpu_power_on,
2675bd9c17dSSaurabh Gorecha .pwr_domain_on_finish = qti_cpu_power_on_finish,
2685bd9c17dSSaurabh Gorecha .cpu_standby = qti_cpu_standby,
2695bd9c17dSSaurabh Gorecha .pwr_domain_off = qti_node_power_off,
2705bd9c17dSSaurabh Gorecha .pwr_domain_suspend = qti_node_suspend,
2715bd9c17dSSaurabh Gorecha .pwr_domain_suspend_finish = qti_node_suspend_finish,
2725bd9c17dSSaurabh Gorecha .system_off = qti_system_off,
2735bd9c17dSSaurabh Gorecha .system_reset = qti_system_reset,
2745bd9c17dSSaurabh Gorecha .get_node_hw_state = NULL,
2755bd9c17dSSaurabh Gorecha .translate_power_state_by_mpidr = NULL,
2765bd9c17dSSaurabh Gorecha .get_sys_suspend_power_state = qti_get_sys_suspend_power_state,
2775bd9c17dSSaurabh Gorecha .validate_power_state = qti_validate_power_state,
2785bd9c17dSSaurabh Gorecha };
2795bd9c17dSSaurabh Gorecha
2805bd9c17dSSaurabh Gorecha /**
2815bd9c17dSSaurabh Gorecha * The QTI Standard platform definition of platform porting API
2825bd9c17dSSaurabh Gorecha * `plat_setup_psci_ops`.
2835bd9c17dSSaurabh Gorecha */
plat_setup_psci_ops(uintptr_t sec_entrypoint,const plat_psci_ops_t ** psci_ops)2845bd9c17dSSaurabh Gorecha int plat_setup_psci_ops(uintptr_t sec_entrypoint,
2855bd9c17dSSaurabh Gorecha const plat_psci_ops_t **psci_ops)
2865bd9c17dSSaurabh Gorecha {
2875bd9c17dSSaurabh Gorecha int err;
2885bd9c17dSSaurabh Gorecha
2895bd9c17dSSaurabh Gorecha err = qtiseclib_psci_init((uintptr_t)bl31_warm_entrypoint);
2905bd9c17dSSaurabh Gorecha if (err == PSCI_E_SUCCESS) {
2915bd9c17dSSaurabh Gorecha *psci_ops = &plat_qti_psci_pm_ops;
2925bd9c17dSSaurabh Gorecha }
2935bd9c17dSSaurabh Gorecha
2945bd9c17dSSaurabh Gorecha return err;
2955bd9c17dSSaurabh Gorecha }
296