Home | History | Annotate | Download | only in compat
      1 /*
      2  * Copyright (c) 2015, 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 <errno.h>
     10 #include <platform.h>
     11 #include <psci.h>
     12 
     13 /*
     14  * The platform hooks exported by the platform using the earlier version of
     15  * platform interface
     16  */
     17 const plat_pm_ops_t *pm_ops;
     18 
     19 /*
     20  * The hooks exported by the compatibility layer
     21  */
     22 static plat_psci_ops_t compat_psci_ops;
     23 
     24 /*
     25  * The secure entry point to be used on warm reset.
     26  */
     27 static unsigned long secure_entrypoint;
     28 
     29 /*
     30  * This array stores the 'power_state' requests of each CPU during
     31  * CPU_SUSPEND and SYSTEM_SUSPEND to support querying of state-ID
     32  * by the platform.
     33  */
     34 unsigned int psci_power_state_compat[PLATFORM_CORE_COUNT];
     35 
     36 /*******************************************************************************
     37  * The PSCI compatibility helper to parse the power state and populate the
     38  * 'pwr_domain_state' for each power level. It is assumed that, when in
     39  * compatibility mode, the PSCI generic layer need to know only whether the
     40  * affinity level will be OFF or in RETENTION and if the platform supports
     41  * multiple power down and retention states, it will be taken care within
     42  * the platform layer.
     43  ******************************************************************************/
     44 static int parse_power_state(unsigned int power_state,
     45 		    psci_power_state_t *req_state)
     46 {
     47 	int i;
     48 	int pstate = psci_get_pstate_type(power_state);
     49 	int aff_lvl = psci_get_pstate_pwrlvl(power_state);
     50 
     51 	if (aff_lvl > PLATFORM_MAX_AFFLVL)
     52 		return PSCI_E_INVALID_PARAMS;
     53 
     54 	/* Sanity check the requested state */
     55 	if (pstate == PSTATE_TYPE_STANDBY) {
     56 		/*
     57 		 * Set the CPU local state as retention and ignore the higher
     58 		 * levels. This allows the generic PSCI layer to invoke
     59 		 * plat_psci_ops 'cpu_standby' hook and the compatibility
     60 		 * layer invokes the 'affinst_standby' handler with the
     61 		 * correct power_state parameter thus preserving the correct
     62 		 * behavior.
     63 		 */
     64 		req_state->pwr_domain_state[0] =
     65 					PLAT_MAX_RET_STATE;
     66 	} else {
     67 		for (i = 0; i <= aff_lvl; i++)
     68 			req_state->pwr_domain_state[i] =
     69 					PLAT_MAX_OFF_STATE;
     70 	}
     71 
     72 	return PSCI_E_SUCCESS;
     73 }
     74 
     75 /*******************************************************************************
     76  * The PSCI compatibility helper to set the 'power_state' in
     77  * psci_power_state_compat[] at index corresponding to the current core.
     78  ******************************************************************************/
     79 static void set_psci_power_state_compat(unsigned int power_state)
     80 {
     81 	unsigned int my_core_pos = plat_my_core_pos();
     82 
     83 	psci_power_state_compat[my_core_pos] = power_state;
     84 	flush_dcache_range((uintptr_t) &psci_power_state_compat[my_core_pos],
     85 			sizeof(psci_power_state_compat[my_core_pos]));
     86 }
     87 
     88 /*******************************************************************************
     89  * The PSCI compatibility helper for plat_pm_ops_t 'validate_power_state'
     90  * hook.
     91  ******************************************************************************/
     92 static int validate_power_state_compat(unsigned int power_state,
     93 			    psci_power_state_t *req_state)
     94 {
     95 	int rc;
     96 	assert(req_state);
     97 
     98 	if (pm_ops->validate_power_state) {
     99 		rc = pm_ops->validate_power_state(power_state);
    100 		if (rc != PSCI_E_SUCCESS)
    101 			return rc;
    102 	}
    103 
    104 	/* Store the 'power_state' parameter for the current CPU. */
    105 	set_psci_power_state_compat(power_state);
    106 
    107 	return parse_power_state(power_state, req_state);
    108 }
    109 
    110 /*******************************************************************************
    111  * The PSCI compatibility helper for plat_pm_ops_t
    112  * 'get_sys_suspend_power_state' hook.
    113  ******************************************************************************/
    114 void get_sys_suspend_power_state_compat(psci_power_state_t *req_state)
    115 {
    116 	unsigned int power_state;
    117 	assert(req_state);
    118 
    119 	power_state = pm_ops->get_sys_suspend_power_state();
    120 
    121 	/* Store the 'power_state' parameter for the current CPU. */
    122 	set_psci_power_state_compat(power_state);
    123 
    124 	if (parse_power_state(power_state, req_state) != PSCI_E_SUCCESS)
    125 		assert(0);
    126 }
    127 
    128 /*******************************************************************************
    129  * The PSCI compatibility helper for plat_pm_ops_t 'validate_ns_entrypoint'
    130  * hook.
    131  ******************************************************************************/
    132 static int validate_ns_entrypoint_compat(uintptr_t ns_entrypoint)
    133 {
    134 	return pm_ops->validate_ns_entrypoint(ns_entrypoint);
    135 }
    136 
    137 /*******************************************************************************
    138  * The PSCI compatibility helper for plat_pm_ops_t 'affinst_standby' hook.
    139  ******************************************************************************/
    140 static void cpu_standby_compat(plat_local_state_t cpu_state)
    141 {
    142 	unsigned int powerstate = psci_get_suspend_powerstate();
    143 
    144 	assert(powerstate != PSCI_INVALID_DATA);
    145 
    146 	pm_ops->affinst_standby(powerstate);
    147 }
    148 
    149 /*******************************************************************************
    150  * The PSCI compatibility helper for plat_pm_ops_t 'affinst_on' hook.
    151  ******************************************************************************/
    152 static int pwr_domain_on_compat(u_register_t mpidr)
    153 {
    154 	int level, rc;
    155 
    156 	/*
    157 	 * The new PSCI framework does not hold the locks for higher level
    158 	 * power domain nodes when this hook is invoked. Hence figuring out the
    159 	 * target state of the parent power domains does not make much sense.
    160 	 * Hence we hard-code the state as PSCI_STATE_OFF for all the levels.
    161 	 * We expect the platform to perform the necessary CPU_ON operations
    162 	 * when the 'affinst_on' is invoked only for level 0.
    163 	 */
    164 	for (level = PLATFORM_MAX_AFFLVL; level >= 0; level--) {
    165 		rc = pm_ops->affinst_on((unsigned long)mpidr, secure_entrypoint,
    166 					level, PSCI_STATE_OFF);
    167 		if (rc != PSCI_E_SUCCESS)
    168 			break;
    169 	}
    170 
    171 	return rc;
    172 }
    173 
    174 /*******************************************************************************
    175  * The PSCI compatibility helper for plat_pm_ops_t 'affinst_off' hook.
    176  ******************************************************************************/
    177 static void pwr_domain_off_compat(const psci_power_state_t *target_state)
    178 {
    179 	int level;
    180 	unsigned int plat_state;
    181 
    182 	for (level = 0; level <= PLATFORM_MAX_AFFLVL; level++) {
    183 		plat_state = (is_local_state_run(
    184 				target_state->pwr_domain_state[level]) ?
    185 				PSCI_STATE_ON : PSCI_STATE_OFF);
    186 		pm_ops->affinst_off(level, plat_state);
    187 	}
    188 }
    189 
    190 /*******************************************************************************
    191  * The PSCI compatibility helper for plat_pm_ops_t 'affinst_suspend' hook.
    192  ******************************************************************************/
    193 static void pwr_domain_suspend_compat(const psci_power_state_t *target_state)
    194 {
    195 	int level;
    196 	unsigned int plat_state;
    197 
    198 	for (level = 0; level <= psci_get_suspend_afflvl(); level++) {
    199 		plat_state = (is_local_state_run(
    200 				target_state->pwr_domain_state[level]) ?
    201 				PSCI_STATE_ON : PSCI_STATE_OFF);
    202 		pm_ops->affinst_suspend(secure_entrypoint, level, plat_state);
    203 	}
    204 }
    205 
    206 /*******************************************************************************
    207  * The PSCI compatibility helper for plat_pm_ops_t 'affinst_on_finish'
    208  * hook.
    209  ******************************************************************************/
    210 static void pwr_domain_on_finish_compat(const psci_power_state_t *target_state)
    211 {
    212 	int level;
    213 	unsigned int plat_state;
    214 
    215 	for (level = PLATFORM_MAX_AFFLVL; level >= 0; level--) {
    216 		plat_state = (is_local_state_run(
    217 				target_state->pwr_domain_state[level]) ?
    218 				PSCI_STATE_ON : PSCI_STATE_OFF);
    219 		pm_ops->affinst_on_finish(level, plat_state);
    220 	}
    221 }
    222 
    223 /*******************************************************************************
    224  * The PSCI compatibility helper for plat_pm_ops_t
    225  * 'affinst_suspend_finish' hook.
    226  ******************************************************************************/
    227 static void pwr_domain_suspend_finish_compat(
    228 				const psci_power_state_t *target_state)
    229 {
    230 	int level;
    231 	unsigned int plat_state;
    232 
    233 	for (level = psci_get_suspend_afflvl(); level >= 0; level--) {
    234 		plat_state = (is_local_state_run(
    235 				target_state->pwr_domain_state[level]) ?
    236 				PSCI_STATE_ON : PSCI_STATE_OFF);
    237 		pm_ops->affinst_suspend_finish(level, plat_state);
    238 	}
    239 }
    240 
    241 /*******************************************************************************
    242  * The PSCI compatibility helper for plat_pm_ops_t 'system_off' hook.
    243  ******************************************************************************/
    244 static void __dead2 system_off_compat(void)
    245 {
    246 	pm_ops->system_off();
    247 }
    248 
    249 /*******************************************************************************
    250  * The PSCI compatibility helper for plat_pm_ops_t 'system_reset' hook.
    251  ******************************************************************************/
    252 static void __dead2 system_reset_compat(void)
    253 {
    254 	pm_ops->system_reset();
    255 }
    256 
    257 /*******************************************************************************
    258  * Export the compatibility compat_psci_ops. The assumption made is that the
    259  * power domains correspond to affinity instances on the platform.
    260  ******************************************************************************/
    261 int plat_setup_psci_ops(uintptr_t sec_entrypoint,
    262 				const plat_psci_ops_t **psci_ops)
    263 {
    264 	platform_setup_pm(&pm_ops);
    265 
    266 	secure_entrypoint = (unsigned long) sec_entrypoint;
    267 
    268 	/*
    269 	 * It is compulsory for the platform ports using the new porting
    270 	 * interface to export a hook to validate the power state parameter
    271 	 */
    272 	compat_psci_ops.validate_power_state = validate_power_state_compat;
    273 
    274 	/*
    275 	 * Populate the compatibility plat_psci_ops_t hooks if available
    276 	 */
    277 	if (pm_ops->validate_ns_entrypoint)
    278 		compat_psci_ops.validate_ns_entrypoint =
    279 				validate_ns_entrypoint_compat;
    280 
    281 	if (pm_ops->affinst_standby)
    282 		compat_psci_ops.cpu_standby = cpu_standby_compat;
    283 
    284 	if (pm_ops->affinst_on)
    285 		compat_psci_ops.pwr_domain_on = pwr_domain_on_compat;
    286 
    287 	if (pm_ops->affinst_off)
    288 		compat_psci_ops.pwr_domain_off = pwr_domain_off_compat;
    289 
    290 	if (pm_ops->affinst_suspend)
    291 		compat_psci_ops.pwr_domain_suspend = pwr_domain_suspend_compat;
    292 
    293 	if (pm_ops->affinst_on_finish)
    294 		compat_psci_ops.pwr_domain_on_finish =
    295 				pwr_domain_on_finish_compat;
    296 
    297 	if (pm_ops->affinst_suspend_finish)
    298 		compat_psci_ops.pwr_domain_suspend_finish =
    299 				pwr_domain_suspend_finish_compat;
    300 
    301 	if (pm_ops->system_off)
    302 		compat_psci_ops.system_off = system_off_compat;
    303 
    304 	if (pm_ops->system_reset)
    305 		compat_psci_ops.system_reset = system_reset_compat;
    306 
    307 	if (pm_ops->get_sys_suspend_power_state)
    308 		compat_psci_ops.get_sys_suspend_power_state =
    309 				get_sys_suspend_power_state_compat;
    310 
    311 	*psci_ops = &compat_psci_ops;
    312 	return 0;
    313 }
    314