blob: ce43ba4a21767a5d1b96c423dacbd4647bda417b [file] [log] [blame]
/*
* Copyright (c) 2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <debug.h>
#include <platform.h>
#include <platform_def.h>
#include <psci.h>
#include <tftf.h>
static unsigned int pstate_initialized;
/*
* Stores pointer to the state_prop_t for all implemented levels
*/
static const plat_state_prop_t *plat_state_ptr[PLAT_MAX_PWR_LEVEL + 1];
/*
* Saves number of implemented power states per level
*/
static unsigned int power_states_per_level[PLAT_MAX_PWR_LEVEL + 1];
void tftf_init_pstate_framework(void)
{
int i, j;
if (pstate_initialized)
return;
/* Detect the PSCI power state format used. */
tftf_detect_psci_pstate_format();
/*
* Get and save the pointers to plat_state_prop_t values for all
* levels. Also, store the max number of local states possible for
* each level in power_states_per_level.
*/
for (i = 0; i <= PLAT_MAX_PWR_LEVEL; i++) {
plat_state_ptr[i] = plat_get_state_prop(i);
assert(plat_state_ptr[i]);
for (j = 0; (plat_state_ptr[i]+j)->state_ID != 0; j++)
;
power_states_per_level[i] = j;
}
pstate_initialized = 1;
}
void tftf_set_next_state_id_idx(unsigned int power_level,
unsigned int pstate_id_idx[])
{
unsigned int i;
#if ENABLE_ASSERTIONS
/* Verify that this is a valid power level. */
assert(power_level <= PLAT_MAX_PWR_LEVEL);
/*
* Verify if a level has PWR_STATE_INIT_INDEX index, all higher levels
* have to be in PWR_STATE_INIT_INDEX. Not needed to check the top
* power level in the outer loop.
*/
for (i = 0; i < power_level; i++) {
if (pstate_id_idx[i] == PWR_STATE_INIT_INDEX) {
for ( ; i <= power_level; i++)
assert(pstate_id_idx[i] == PWR_STATE_INIT_INDEX);
}
}
#endif
/* Increment the pstate_id_idx starting from the lowest power level */
for (i = 0; i <= power_level; i++) {
pstate_id_idx[i]++;
/*
* Wraparound index if the maximum power states available for
* that level is reached and proceed to next level.
*/
if (pstate_id_idx[i] == power_states_per_level[i])
pstate_id_idx[i] = 0;
else
break;
}
/*
* Check if the requested power level has wrapped around. If it has,
* reset pstate_id_idx.
*/
if (i > power_level) {
for (i = 0; i <= power_level; i++)
pstate_id_idx[i] = PWR_STATE_INIT_INDEX;
}
}
void tftf_set_deepest_pstate_idx(unsigned int power_level,
unsigned int pstate_id_idx[])
{
int i;
/* Verify that this is a valid power level. */
assert(power_level <= PLAT_MAX_PWR_LEVEL);
/*
* Assign the highest pstate_id_idx starting from the lowest power
* level
*/
for (i = 0; i <= power_level; i++)
pstate_id_idx[i] = power_states_per_level[i] - 1;
}
int tftf_get_pstate_vars(unsigned int *test_power_level,
unsigned int *test_suspend_type,
unsigned int *suspend_state_id,
unsigned int pstate_id_idx[])
{
unsigned int i;
int state_id = 0;
int suspend_type;
int suspend_depth;
int psci_ret = PSCI_E_SUCCESS;
const plat_state_prop_t *local_state;
/* Atleast one entry should be valid to generate correct power state params */
assert(pstate_id_idx[0] != PWR_STATE_INIT_INDEX &&
pstate_id_idx[0] <= power_states_per_level[0]);
suspend_depth = (plat_state_ptr[0] + pstate_id_idx[0])->suspend_depth;
suspend_type = (plat_state_ptr[0] + pstate_id_idx[0])->is_pwrdown;
for (i = 0; i <= PLAT_MAX_PWR_LEVEL; i++) {
/* Reached all levels with the valid power index values */
if (pstate_id_idx[i] == PWR_STATE_INIT_INDEX)
break;
assert(pstate_id_idx[i] <= power_states_per_level[i]);
local_state = plat_state_ptr[i] + pstate_id_idx[i];
state_id |= (local_state->state_ID << i * PLAT_LOCAL_PSTATE_WIDTH);
if (local_state->is_pwrdown > suspend_type)
suspend_type = local_state->is_pwrdown;
if (local_state->suspend_depth > suspend_depth)
psci_ret = PSCI_E_INVALID_PARAMS;
else
suspend_depth = local_state->suspend_depth;
}
*test_suspend_type = suspend_type;
*suspend_state_id = state_id;
*test_power_level = --i;
return psci_ret;
}
void tftf_set_next_local_state_id_idx(unsigned int power_level,
unsigned int pstate_id_idx[])
{
assert(power_level <= PLAT_MAX_PWR_LEVEL);
if (pstate_id_idx[power_level] + 1 >= power_states_per_level[power_level]) {
pstate_id_idx[power_level] = PWR_STATE_INIT_INDEX;
return;
}
pstate_id_idx[power_level]++;
}