Home | History | Annotate | Download | only in scp
      1 /*
      2  * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
      3  *
      4  * SPDX-License-Identifier: BSD-3-Clause
      5  */
      6 
      7 #include <arch_helpers.h>
      8 #include <assert.h>
      9 #include <css_def.h>
     10 #include <css_pm.h>
     11 #include <debug.h>
     12 #include <plat_arm.h>
     13 #include <platform.h>
     14 #include <string.h>
     15 #include "../scmi/scmi.h"
     16 #include "css_scp.h"
     17 
     18 /*
     19  * This file implements the SCP helper functions using SCMI protocol.
     20  */
     21 
     22 /*
     23  * SCMI power state parameter bit field encoding for ARM CSS platforms.
     24  *
     25  * 31  20 19       16 15      12 11       8 7        4 3         0
     26  * +-------------------------------------------------------------+
     27  * | SBZ | Max level |  Level 3 |  Level 2 |  Level 1 |  Level 0 |
     28  * |     |           |   state  |   state  |   state  |   state  |
     29  * +-------------------------------------------------------------+
     30  *
     31  * `Max level` encodes the highest level that has a valid power state
     32  * encoded in the power state.
     33  */
     34 #define SCMI_PWR_STATE_MAX_PWR_LVL_SHIFT	16
     35 #define SCMI_PWR_STATE_MAX_PWR_LVL_WIDTH	4
     36 #define SCMI_PWR_STATE_MAX_PWR_LVL_MASK		\
     37 				((1 << SCMI_PWR_STATE_MAX_PWR_LVL_WIDTH) - 1)
     38 #define SCMI_SET_PWR_STATE_MAX_PWR_LVL(pwr_state, max_lvl)		\
     39 		(pwr_state) |= ((max_lvl) & SCMI_PWR_STATE_MAX_PWR_LVL_MASK)	\
     40 				<< SCMI_PWR_STATE_MAX_PWR_LVL_SHIFT
     41 #define SCMI_GET_PWR_STATE_MAX_PWR_LVL(pwr_state)		\
     42 		(((pwr_state) >> SCMI_PWR_STATE_MAX_PWR_LVL_SHIFT)	\
     43 				& SCMI_PWR_STATE_MAX_PWR_LVL_MASK)
     44 
     45 #define SCMI_PWR_STATE_LVL_WIDTH		4
     46 #define SCMI_PWR_STATE_LVL_MASK			\
     47 				((1 << SCMI_PWR_STATE_LVL_WIDTH) - 1)
     48 #define SCMI_SET_PWR_STATE_LVL(pwr_state, lvl, lvl_state)		\
     49 		(pwr_state) |= ((lvl_state) & SCMI_PWR_STATE_LVL_MASK)	\
     50 				<< (SCMI_PWR_STATE_LVL_WIDTH * (lvl))
     51 #define SCMI_GET_PWR_STATE_LVL(pwr_state, lvl)		\
     52 		(((pwr_state) >> (SCMI_PWR_STATE_LVL_WIDTH * (lvl))) &	\
     53 				SCMI_PWR_STATE_LVL_MASK)
     54 
     55 /*
     56  * The SCMI power state enumeration for a power domain level
     57  */
     58 typedef enum {
     59 	scmi_power_state_off = 0,
     60 	scmi_power_state_on = 1,
     61 	scmi_power_state_sleep = 2,
     62 } scmi_power_state_t;
     63 
     64 /*
     65  * This mapping array has to be exported by the platform. Each element at
     66  * a given index maps that core to an SCMI power domain.
     67  */
     68 extern uint32_t plat_css_core_pos_to_scmi_dmn_id_map[];
     69 
     70 /*
     71  * The global handle for invoking the SCMI driver APIs after the driver
     72  * has been initialized.
     73  */
     74 void *scmi_handle;
     75 
     76 /* The SCMI channel global object */
     77 static scmi_channel_t scmi_channel;
     78 
     79 ARM_INSTANTIATE_LOCK;
     80 
     81 /*
     82  * Helper function to suspend a CPU power domain and its parent power domains
     83  * if applicable.
     84  */
     85 void css_scp_suspend(const psci_power_state_t *target_state)
     86 {
     87 	int lvl, ret;
     88 	uint32_t scmi_pwr_state = 0;
     89 
     90 	/* At least power domain level 0 should be specified to be suspended */
     91 	assert(target_state->pwr_domain_state[ARM_PWR_LVL0] ==
     92 						ARM_LOCAL_STATE_OFF);
     93 
     94 	/* Check if power down at system power domain level is requested */
     95 	if (CSS_SYSTEM_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF) {
     96 		/* Issue SCMI command for SYSTEM_SUSPEND */
     97 		ret = scmi_sys_pwr_state_set(scmi_handle,
     98 				SCMI_SYS_PWR_FORCEFUL_REQ,
     99 				SCMI_SYS_PWR_SUSPEND);
    100 		if (ret != SCMI_E_SUCCESS) {
    101 			ERROR("SCMI system power domain suspend return 0x%x unexpected\n",
    102 					ret);
    103 			panic();
    104 		}
    105 		return;
    106 	}
    107 
    108 	/*
    109 	 * If we reach here, then assert that power down at system power domain
    110 	 * level is running.
    111 	 */
    112 	assert(target_state->pwr_domain_state[CSS_SYSTEM_PWR_DMN_LVL] ==
    113 							ARM_LOCAL_STATE_RUN);
    114 
    115 	/* For level 0, specify `scmi_power_state_sleep` as the power state */
    116 	SCMI_SET_PWR_STATE_LVL(scmi_pwr_state, ARM_PWR_LVL0,
    117 						scmi_power_state_sleep);
    118 
    119 	for (lvl = ARM_PWR_LVL1; lvl <= PLAT_MAX_PWR_LVL; lvl++) {
    120 		if (target_state->pwr_domain_state[lvl] == ARM_LOCAL_STATE_RUN)
    121 			break;
    122 
    123 		assert(target_state->pwr_domain_state[lvl] ==
    124 							ARM_LOCAL_STATE_OFF);
    125 		/*
    126 		 * Specify `scmi_power_state_off` as power state for higher
    127 		 * levels.
    128 		 */
    129 		SCMI_SET_PWR_STATE_LVL(scmi_pwr_state, lvl,
    130 						scmi_power_state_off);
    131 	}
    132 
    133 	SCMI_SET_PWR_STATE_MAX_PWR_LVL(scmi_pwr_state, lvl - 1);
    134 
    135 	ret = scmi_pwr_state_set(scmi_handle,
    136 		plat_css_core_pos_to_scmi_dmn_id_map[plat_my_core_pos()],
    137 		scmi_pwr_state);
    138 
    139 	if (ret != SCMI_E_SUCCESS) {
    140 		ERROR("SCMI set power state command return 0x%x unexpected\n",
    141 				ret);
    142 		panic();
    143 	}
    144 }
    145 
    146 /*
    147  * Helper function to turn off a CPU power domain and its parent power domains
    148  * if applicable.
    149  */
    150 void css_scp_off(const psci_power_state_t *target_state)
    151 {
    152 	int lvl = 0, ret;
    153 	uint32_t scmi_pwr_state = 0;
    154 
    155 	/* At-least the CPU level should be specified to be OFF */
    156 	assert(target_state->pwr_domain_state[ARM_PWR_LVL0] ==
    157 							ARM_LOCAL_STATE_OFF);
    158 
    159 	/* PSCI CPU OFF cannot be used to turn OFF system power domain */
    160 	assert(target_state->pwr_domain_state[CSS_SYSTEM_PWR_DMN_LVL] ==
    161 							ARM_LOCAL_STATE_RUN);
    162 
    163 	for (; lvl <= PLAT_MAX_PWR_LVL; lvl++) {
    164 		if (target_state->pwr_domain_state[lvl] == ARM_LOCAL_STATE_RUN)
    165 			break;
    166 
    167 		assert(target_state->pwr_domain_state[lvl] ==
    168 							ARM_LOCAL_STATE_OFF);
    169 		SCMI_SET_PWR_STATE_LVL(scmi_pwr_state, lvl,
    170 				scmi_power_state_off);
    171 	}
    172 
    173 	SCMI_SET_PWR_STATE_MAX_PWR_LVL(scmi_pwr_state, lvl - 1);
    174 
    175 	ret = scmi_pwr_state_set(scmi_handle,
    176 		plat_css_core_pos_to_scmi_dmn_id_map[plat_my_core_pos()],
    177 		scmi_pwr_state);
    178 
    179 	if (ret != SCMI_E_QUEUED && ret != SCMI_E_SUCCESS) {
    180 		ERROR("SCMI set power state command return 0x%x unexpected\n",
    181 				ret);
    182 		panic();
    183 	}
    184 }
    185 
    186 /*
    187  * Helper function to turn ON a CPU power domain and its parent power domains
    188  * if applicable.
    189  */
    190 void css_scp_on(u_register_t mpidr)
    191 {
    192 	int lvl = 0, ret, core_pos;
    193 	uint32_t scmi_pwr_state = 0;
    194 
    195 	for (; lvl <= PLAT_MAX_PWR_LVL; lvl++)
    196 		SCMI_SET_PWR_STATE_LVL(scmi_pwr_state, lvl,
    197 				scmi_power_state_on);
    198 
    199 	SCMI_SET_PWR_STATE_MAX_PWR_LVL(scmi_pwr_state, lvl - 1);
    200 
    201 	core_pos = plat_core_pos_by_mpidr(mpidr);
    202 	assert(core_pos >= 0 && core_pos < PLATFORM_CORE_COUNT);
    203 
    204 	ret = scmi_pwr_state_set(scmi_handle,
    205 		plat_css_core_pos_to_scmi_dmn_id_map[core_pos],
    206 		scmi_pwr_state);
    207 
    208 	if (ret != SCMI_E_QUEUED && ret != SCMI_E_SUCCESS) {
    209 		ERROR("SCMI set power state command return 0x%x unexpected\n",
    210 				ret);
    211 		panic();
    212 	}
    213 }
    214 
    215 /*
    216  * Helper function to get the power state of a power domain node as reported
    217  * by the SCP.
    218  */
    219 int css_scp_get_power_state(u_register_t mpidr, unsigned int power_level)
    220 {
    221 	int ret, cpu_idx;
    222 	uint32_t scmi_pwr_state = 0, lvl_state;
    223 
    224 	/* We don't support get power state at the system power domain level */
    225 	if ((power_level > PLAT_MAX_PWR_LVL) ||
    226 			(power_level == CSS_SYSTEM_PWR_DMN_LVL)) {
    227 		WARN("Invalid power level %u specified for SCMI get power state\n",
    228 				power_level);
    229 		return PSCI_E_INVALID_PARAMS;
    230 	}
    231 
    232 	cpu_idx = plat_core_pos_by_mpidr(mpidr);
    233 	assert(cpu_idx > -1);
    234 
    235 	ret = scmi_pwr_state_get(scmi_handle,
    236 		plat_css_core_pos_to_scmi_dmn_id_map[cpu_idx],
    237 		&scmi_pwr_state);
    238 
    239 	if (ret != SCMI_E_SUCCESS) {
    240 		WARN("SCMI get power state command return 0x%x unexpected\n",
    241 				ret);
    242 		return PSCI_E_INVALID_PARAMS;
    243 	}
    244 
    245 	/*
    246 	 * Find the maximum power level described in the get power state
    247 	 * command. If it is less than the requested power level, then assume
    248 	 * the requested power level is ON.
    249 	 */
    250 	if (SCMI_GET_PWR_STATE_MAX_PWR_LVL(scmi_pwr_state) < power_level)
    251 		return HW_ON;
    252 
    253 	lvl_state = SCMI_GET_PWR_STATE_LVL(scmi_pwr_state, power_level);
    254 	if (lvl_state == scmi_power_state_on)
    255 		return HW_ON;
    256 
    257 	assert((lvl_state == scmi_power_state_off) ||
    258 				(lvl_state == scmi_power_state_sleep));
    259 	return HW_OFF;
    260 }
    261 
    262 void __dead2 css_scp_system_off(int state)
    263 {
    264 	int ret;
    265 
    266 	/*
    267 	 * Disable GIC CPU interface to prevent pending interrupt from waking
    268 	 * up the AP from WFI.
    269 	 */
    270 	plat_arm_gic_cpuif_disable();
    271 
    272 	/*
    273 	 * Issue SCMI command. First issue a graceful
    274 	 * request and if that fails force the request.
    275 	 */
    276 	ret = scmi_sys_pwr_state_set(scmi_handle,
    277 			SCMI_SYS_PWR_FORCEFUL_REQ,
    278 			state);
    279 
    280 	if (ret != SCMI_E_SUCCESS) {
    281 		ERROR("SCMI system power state set 0x%x returns unexpected 0x%x\n",
    282 			state, ret);
    283 		panic();
    284 	}
    285 	wfi();
    286 	ERROR("CSS set power state: operation not handled.\n");
    287 	panic();
    288 }
    289 
    290 /*
    291  * Helper function to shutdown the system via SCMI.
    292  */
    293 void __dead2 css_scp_sys_shutdown(void)
    294 {
    295 	css_scp_system_off(SCMI_SYS_PWR_SHUTDOWN);
    296 }
    297 
    298 /*
    299  * Helper function to reset the system via SCMI.
    300  */
    301 void __dead2 css_scp_sys_reboot(void)
    302 {
    303 	css_scp_system_off(SCMI_SYS_PWR_COLD_RESET);
    304 }
    305 
    306 scmi_channel_plat_info_t plat_css_scmi_plat_info = {
    307 		.scmi_mbx_mem = CSS_SCMI_PAYLOAD_BASE,
    308 		.db_reg_addr = PLAT_CSS_MHU_BASE + CSS_SCMI_MHU_DB_REG_OFF,
    309 		.db_preserve_mask = 0xfffffffe,
    310 		.db_modify_mask = 0x1,
    311 };
    312 
    313 void plat_arm_pwrc_setup(void)
    314 {
    315 	scmi_channel.info = &plat_css_scmi_plat_info;
    316 	scmi_channel.lock = ARM_LOCK_GET_INSTANCE;
    317 	scmi_handle = scmi_init(&scmi_channel);
    318 	if (scmi_handle == NULL) {
    319 		ERROR("SCMI Initialization failed\n");
    320 		panic();
    321 	}
    322 }
    323 
    324 /******************************************************************************
    325  * This function overrides the default definition for ARM platforms. Initialize
    326  * the SCMI driver, query capability via SCMI and modify the PSCI capability
    327  * based on that.
    328  *****************************************************************************/
    329 const plat_psci_ops_t *plat_arm_psci_override_pm_ops(plat_psci_ops_t *ops)
    330 {
    331 	uint32_t msg_attr;
    332 	int ret;
    333 
    334 	assert(scmi_handle);
    335 
    336 	/* Check that power domain POWER_STATE_SET message is supported */
    337 	ret = scmi_proto_msg_attr(scmi_handle, SCMI_PWR_DMN_PROTO_ID,
    338 				SCMI_PWR_STATE_SET_MSG, &msg_attr);
    339 	if (ret != SCMI_E_SUCCESS) {
    340 		ERROR("Set power state command is not supported by SCMI\n");
    341 		panic();
    342 	}
    343 
    344 	/*
    345 	 * Don't support PSCI NODE_HW_STATE call if SCMI doesn't support
    346 	 * POWER_STATE_GET message.
    347 	 */
    348 	ret = scmi_proto_msg_attr(scmi_handle, SCMI_PWR_DMN_PROTO_ID,
    349 				SCMI_PWR_STATE_GET_MSG, &msg_attr);
    350 	if (ret != SCMI_E_SUCCESS)
    351 		ops->get_node_hw_state = NULL;
    352 
    353 	/* Check if the SCMI SYSTEM_POWER_STATE_SET message is supported */
    354 	ret = scmi_proto_msg_attr(scmi_handle, SCMI_SYS_PWR_PROTO_ID,
    355 				SCMI_SYS_PWR_STATE_SET_MSG, &msg_attr);
    356 	if (ret != SCMI_E_SUCCESS) {
    357 		/* System power management operations are not supported */
    358 		ops->system_off = NULL;
    359 		ops->system_reset = NULL;
    360 		ops->get_sys_suspend_power_state = NULL;
    361 	} else {
    362 		if (!(msg_attr & SCMI_SYS_PWR_SUSPEND_SUPPORTED)) {
    363 			/*
    364 			 * System power management protocol is available, but
    365 			 * it does not support SYSTEM SUSPEND.
    366 			 */
    367 			ops->get_sys_suspend_power_state = NULL;
    368 		}
    369 		if (!(msg_attr & SCMI_SYS_PWR_WARM_RESET_SUPPORTED)) {
    370 			/*
    371 			 * WARM reset is not available.
    372 			 */
    373 			ops->system_reset2 = NULL;
    374 		}
    375 	}
    376 
    377 	return ops;
    378 }
    379 
    380 int css_system_reset2(int is_vendor, int reset_type, u_register_t cookie)
    381 {
    382 	if (is_vendor || (reset_type != PSCI_RESET2_SYSTEM_WARM_RESET))
    383 		return PSCI_E_INVALID_PARAMS;
    384 
    385 	css_scp_system_off(SCMI_SYS_PWR_WARM_RESET);
    386 	/*
    387 	 * css_scp_system_off cannot return (it is a __dead function),
    388 	 * but css_system_reset2 has to return some value, even in
    389 	 * this case.
    390 	 */
    391 	return 0;
    392 }
    393