psci: rectify and homogenise generic code

This patch performs a major rework of the psci generic implementation
to achieve the following:

1. replace recursion with iteration where possible to aid code
   readability e.g. affinity instance states are changed iteratively
   instead of recursively.

2. acquire pointers to affinity instance nodes at the beginning of a
   psci operation. All subsequent actions use these pointers instead
   of calling psci_get_aff_map_node() repeatedly e.g. management of
   locks has been abstracted under functions which use these pointers
   to ensure correct ordering. Helper functions have been added to
   create these abstractions.

3. assertions have been added to cpu level handlers to ensure correct
   state transition

4. the affinity level extents specified to various functions have the
   same meaning i.e. start level is always less than the end level.

Change-Id: If0508c3a7b20ea3ddda2a66128429382afc3dfc8
This commit is contained in:
Achin Gupta 2013-12-02 17:33:04 +00:00 committed by Dan Handley
parent 3140a9e5c7
commit 0959db5c99
7 changed files with 603 additions and 334 deletions

View File

@ -162,97 +162,135 @@ static const afflvl_off_handler psci_afflvl_off_handlers[] = {
}; };
/******************************************************************************* /*******************************************************************************
* This function implements the core of the processing required to turn a cpu * This function takes an array of pointers to affinity instance nodes in the
* off. It's assumed that along with turning the cpu off, higher affinity levels * topology tree and calls the off handler for the corresponding affinity
* will be turned off as far as possible. We first need to determine the new * levels
* state off all the affinity instances in the mpidr corresponding to the target ******************************************************************************/
* cpu. Action will be taken on the basis of this new state. To do the state static int psci_call_off_handlers(mpidr_aff_map_nodes mpidr_nodes,
* change we first need to acquire the locks for all the implemented affinity int start_afflvl,
* level to be able to snapshot the system state. Then we need to start turning int end_afflvl,
* affinity levels off from the lowest to the highest (e.g. a cpu needs to be unsigned long mpidr)
* off before a cluster can be turned off). To achieve this flow, we start {
* acquiring the locks from the highest to the lowest affinity level. Once we int rc = PSCI_E_INVALID_PARAMS, level;
* reach affinity level 0, we do the state change followed by the actions aff_map_node *node;
* corresponding to the new state for affinity level 0. Actions as per the
* updated state for higher affinity levels are performed as we unwind back to for (level = start_afflvl; level <= end_afflvl; level++) {
* highest affinity level. node = mpidr_nodes[level];
if (node == NULL)
continue;
/*
* TODO: In case of an error should there be a way
* of restoring what we might have torn down at
* lower affinity levels.
*/
rc = psci_afflvl_off_handlers[level](mpidr, node);
if (rc != PSCI_E_SUCCESS)
break;
}
return rc;
}
/*******************************************************************************
* Top level handler which is called when a cpu wants to power itself down.
* It's assumed that along with turning the cpu off, higher affinity levels will
* be turned off as far as possible. It traverses through all the affinity
* levels performing generic, architectural, platform setup and state management
* e.g. for a cluster that's to be powered off, it will call the platform
* specific code which will disable coherency at the interconnect level if the
* cpu is the last in the cluster. For a cpu it could mean programming the power
* the power controller etc.
*
* The state of all the relevant affinity levels is changed prior to calling the
* affinity level specific handlers as their actions would depend upon the state
* the affinity level is about to enter.
*
* The affinity level specific handlers are called in ascending order i.e. from
* the lowest to the highest affinity level implemented by the platform because
* to turn off affinity level X it is neccesary to turn off affinity level X - 1
* first.
*
* CAUTION: This function is called with coherent stacks so that coherency can
* be turned off and caches can be flushed safely.
******************************************************************************/ ******************************************************************************/
int psci_afflvl_off(unsigned long mpidr, int psci_afflvl_off(unsigned long mpidr,
int cur_afflvl, int start_afflvl,
int tgt_afflvl) int end_afflvl)
{ {
int rc = PSCI_E_SUCCESS, level; int rc = PSCI_E_SUCCESS;
unsigned int next_state, prev_state; unsigned int prev_state;
aff_map_node *aff_node; mpidr_aff_map_nodes mpidr_nodes;
mpidr &= MPIDR_AFFINITY_MASK;; mpidr &= MPIDR_AFFINITY_MASK;;
/* /*
* Some affinity instances at levels between the current and * Collect the pointers to the nodes in the topology tree for
* target levels could be absent in the mpidr. Skip them and * each affinity instance in the mpidr. If this function does
* start from the first present instance. * not return successfully then either the mpidr or the affinity
* levels are incorrect. In either case, we cannot return back
* to the caller as it would not know what to do.
*/ */
level = psci_get_first_present_afflvl(mpidr, rc = psci_get_aff_map_nodes(mpidr,
cur_afflvl, start_afflvl,
tgt_afflvl, end_afflvl,
&aff_node); mpidr_nodes);
/* assert (rc == PSCI_E_SUCCESS);
* Return if there are no more affinity instances beyond this
* level to process. Else ensure that the returned affinity
* node makes sense.
*/
if (aff_node == NULL)
return rc;
assert(level == aff_node->level);
/* /*
* This function acquires the lock corresponding to each * This function acquires the lock corresponding to each affinity
* affinity level so that state management can be done safely. * level so that by the time all locks are taken, the system topology
* is snapshot and state management can be done safely.
*/ */
bakery_lock_get(mpidr, &aff_node->lock); psci_acquire_afflvl_locks(mpidr,
start_afflvl,
/* Keep the old state and the next one handy */ end_afflvl,
prev_state = psci_get_state(aff_node->state); mpidr_nodes);
next_state = PSCI_STATE_OFF;
/* /*
* We start from the highest affinity level and work our way * Keep the old cpu state handy. It will be used to restore the
* downwards to the lowest i.e. MPIDR_AFFLVL0. * system to its original state in case something goes wrong
*/ */
if (aff_node->level == tgt_afflvl) { prev_state = psci_get_state(mpidr_nodes[MPIDR_AFFLVL0]->state);
psci_change_state(mpidr,
tgt_afflvl,
get_max_afflvl(),
next_state);
} else {
rc = psci_afflvl_off(mpidr, level - 1, tgt_afflvl);
if (rc != PSCI_E_SUCCESS) {
psci_set_state(aff_node->state, prev_state);
goto exit;
}
}
/* /*
* Perform generic, architecture and platform specific * State management: Update the state of each affinity instance
* handling * between the start and end affinity levels
*/ */
rc = psci_afflvl_off_handlers[level](mpidr, aff_node); psci_change_state(mpidr_nodes,
if (rc != PSCI_E_SUCCESS) { start_afflvl,
psci_set_state(aff_node->state, prev_state); end_afflvl,
goto exit; PSCI_STATE_OFF);
}
/* Perform generic, architecture and platform specific handling */
rc = psci_call_off_handlers(mpidr_nodes,
start_afflvl,
end_afflvl,
mpidr);
/* /*
* If all has gone as per plan then this cpu should be * If an error is returned by a handler then restore the cpu state
* marked as OFF * to its original value. If the cpu state is restored then that
* should result in the state of the higher affinity levels to
* get restored as well.
* TODO: We are not undoing any architectural or platform specific
* operations that might have completed before encountering the
* error. The system might not be in a stable state.
*/ */
if (level == MPIDR_AFFLVL0) { if (rc != PSCI_E_SUCCESS)
next_state = psci_get_state(aff_node->state); psci_change_state(mpidr_nodes,
assert(next_state == PSCI_STATE_OFF); start_afflvl,
} end_afflvl,
prev_state);
/*
* Release the locks corresponding to each affinity level in the
* reverse order to which they were acquired.
*/
psci_release_afflvl_locks(mpidr,
start_afflvl,
end_afflvl,
mpidr_nodes);
exit:
bakery_lock_release(mpidr, &aff_node->lock);
return rc; return rc;
} }

View File

@ -207,84 +207,119 @@ static const afflvl_on_handler psci_afflvl_on_handlers[] = {
}; };
/******************************************************************************* /*******************************************************************************
* This function implements the core of the processing required to turn a cpu * This function takes an array of pointers to affinity instance nodes in the
* on. It avoids recursion to traverse from the lowest to the highest affinity * topology tree and calls the on handler for the corresponding affinity
* level unlike the off/suspend/pon_finisher functions. It does ensure that the * levels
* locks are picked in the same order as the order routines to avoid deadlocks. ******************************************************************************/
* The flow is: Take all the locks until the highest affinity level, Call the static int psci_call_on_handlers(mpidr_aff_map_nodes target_cpu_nodes,
* handlers for turning an affinity level on & finally change the state of the int start_afflvl,
* affinity level. int end_afflvl,
unsigned long target_cpu,
unsigned long entrypoint,
unsigned long context_id)
{
int rc = PSCI_E_INVALID_PARAMS, level;
aff_map_node *node;
for (level = end_afflvl; level >= start_afflvl; level--) {
node = target_cpu_nodes[level];
if (node == NULL)
continue;
/*
* TODO: In case of an error should there be a way
* of undoing what we might have setup at higher
* affinity levels.
*/
rc = psci_afflvl_on_handlers[level](target_cpu,
node,
entrypoint,
context_id);
if (rc != PSCI_E_SUCCESS)
break;
}
return rc;
}
/*******************************************************************************
* Generic handler which is called to physically power on a cpu identified by
* its mpidr. It traverses through all the affinity levels performing generic,
* architectural, platform setup and state management e.g. for a cpu that is
* to be powered on, it will ensure that enough information is stashed for it
* to resume execution in the non-secure security state.
*
* The state of all the relevant affinity levels is changed after calling the
* affinity level specific handlers as their actions would depend upon the state
* the affinity level is currently in.
*
* The affinity level specific handlers are called in descending order i.e. from
* the highest to the lowest affinity level implemented by the platform because
* to turn on affinity level X it is neccesary to turn on affinity level X + 1
* first.
******************************************************************************/ ******************************************************************************/
int psci_afflvl_on(unsigned long target_cpu, int psci_afflvl_on(unsigned long target_cpu,
unsigned long entrypoint, unsigned long entrypoint,
unsigned long context_id, unsigned long context_id,
int current_afflvl, int start_afflvl,
int target_afflvl) int end_afflvl)
{ {
unsigned int prev_state, next_state; int rc = PSCI_E_SUCCESS;
int rc = PSCI_E_SUCCESS, level; mpidr_aff_map_nodes target_cpu_nodes;
aff_map_node *aff_node;
unsigned long mpidr = read_mpidr() & MPIDR_AFFINITY_MASK; unsigned long mpidr = read_mpidr() & MPIDR_AFFINITY_MASK;
/* /*
* This loop acquires the lock corresponding to each * Collect the pointers to the nodes in the topology tree for
* affinity level so that by the time we hit the lowest * each affinity instance in the mpidr. If this function does
* affinity level, the system topology is snapshot and * not return successfully then either the mpidr or the affinity
* state management can be done safely. * levels are incorrect.
*/ */
for (level = current_afflvl; level >= target_afflvl; level--) { rc = psci_get_aff_map_nodes(target_cpu,
aff_node = psci_get_aff_map_node(target_cpu, level); start_afflvl,
if (aff_node) end_afflvl,
bakery_lock_get(mpidr, &aff_node->lock); target_cpu_nodes);
} if (rc != PSCI_E_SUCCESS)
return rc;
/* /*
* Perform generic, architecture and platform specific * This function acquires the lock corresponding to each affinity
* handling * level so that by the time all locks are taken, the system topology
* is snapshot and state management can be done safely.
*/ */
for (level = current_afflvl; level >= target_afflvl; level--) { psci_acquire_afflvl_locks(mpidr,
start_afflvl,
end_afflvl,
target_cpu_nodes);
/* Grab the node for each affinity level once again */ /* Perform generic, architecture and platform specific handling. */
aff_node = psci_get_aff_map_node(target_cpu, level); rc = psci_call_on_handlers(target_cpu_nodes,
if (aff_node) { start_afflvl,
end_afflvl,
/* Keep the old state and the next one handy */ target_cpu,
prev_state = psci_get_state(aff_node->state); entrypoint,
rc = psci_afflvl_on_handlers[level](target_cpu, context_id);
aff_node, if (rc != PSCI_E_SUCCESS)
entrypoint, goto exit;
context_id);
if (rc != PSCI_E_SUCCESS) {
psci_set_state(aff_node->state, prev_state);
goto exit;
}
}
}
/* /*
* State management: Update the states since this is the * State management: Update the state of each affinity instance
* target affinity level requested. * between the start and end affinity levels
*/ */
psci_change_state(target_cpu, psci_change_state(target_cpu_nodes,
target_afflvl, start_afflvl,
get_max_afflvl(), end_afflvl,
PSCI_STATE_ON_PENDING); PSCI_STATE_ON_PENDING);
exit: exit:
/* /*
* This loop releases the lock corresponding to each affinity level * This loop releases the lock corresponding to each affinity level
* in the reverse order. It also checks the final state of the cpu. * in the reverse order to which they were acquired.
*/ */
for (level = target_afflvl; level <= current_afflvl; level++) { psci_release_afflvl_locks(mpidr,
aff_node = psci_get_aff_map_node(target_cpu, level); start_afflvl,
if (aff_node) { end_afflvl,
if (level == MPIDR_AFFLVL0) { target_cpu_nodes);
next_state = psci_get_state(aff_node->state);
assert(next_state == PSCI_STATE_ON_PENDING);
}
bakery_lock_release(mpidr, &aff_node->lock);
}
}
return rc; return rc;
} }
@ -294,13 +329,16 @@ exit:
* are called by the common finisher routine in psci_common.c. * are called by the common finisher routine in psci_common.c.
******************************************************************************/ ******************************************************************************/
static unsigned int psci_afflvl0_on_finish(unsigned long mpidr, static unsigned int psci_afflvl0_on_finish(unsigned long mpidr,
aff_map_node *cpu_node, aff_map_node *cpu_node)
unsigned int prev_state)
{ {
unsigned int index, plat_state, rc = PSCI_E_SUCCESS; unsigned int index, plat_state, state, rc = PSCI_E_SUCCESS;
assert(cpu_node->level == MPIDR_AFFLVL0); assert(cpu_node->level == MPIDR_AFFLVL0);
/* Ensure we have been explicitly woken up by another cpu */
state = psci_get_state(cpu_node->state);
assert(state == PSCI_STATE_ON_PENDING);
/* /*
* Plat. management: Perform the platform specific actions * Plat. management: Perform the platform specific actions
* for this cpu e.g. enabling the gic or zeroing the mailbox * for this cpu e.g. enabling the gic or zeroing the mailbox
@ -309,8 +347,8 @@ static unsigned int psci_afflvl0_on_finish(unsigned long mpidr,
*/ */
if (psci_plat_pm_ops->affinst_on_finish) { if (psci_plat_pm_ops->affinst_on_finish) {
/* Get the previous physical state of this cpu */ /* Get the physical state of this cpu */
plat_state = psci_get_phys_state(prev_state); plat_state = psci_get_phys_state(state);
rc = psci_plat_pm_ops->affinst_on_finish(mpidr, rc = psci_plat_pm_ops->affinst_on_finish(mpidr,
cpu_node->level, cpu_node->level,
plat_state); plat_state);
@ -346,11 +384,9 @@ static unsigned int psci_afflvl0_on_finish(unsigned long mpidr,
} }
static unsigned int psci_afflvl1_on_finish(unsigned long mpidr, static unsigned int psci_afflvl1_on_finish(unsigned long mpidr,
aff_map_node *cluster_node, aff_map_node *cluster_node)
unsigned int prev_state)
{ {
unsigned int rc = PSCI_E_SUCCESS; unsigned int plat_state, rc = PSCI_E_SUCCESS;
unsigned int plat_state;
assert(cluster_node->level == MPIDR_AFFLVL1); assert(cluster_node->level == MPIDR_AFFLVL1);
@ -363,7 +399,9 @@ static unsigned int psci_afflvl1_on_finish(unsigned long mpidr,
* situation. * situation.
*/ */
if (psci_plat_pm_ops->affinst_on_finish) { if (psci_plat_pm_ops->affinst_on_finish) {
plat_state = psci_get_phys_state(prev_state);
/* Get the physical state of this cluster */
plat_state = psci_get_aff_phys_state(cluster_node);
rc = psci_plat_pm_ops->affinst_on_finish(mpidr, rc = psci_plat_pm_ops->affinst_on_finish(mpidr,
cluster_node->level, cluster_node->level,
plat_state); plat_state);
@ -375,11 +413,9 @@ static unsigned int psci_afflvl1_on_finish(unsigned long mpidr,
static unsigned int psci_afflvl2_on_finish(unsigned long mpidr, static unsigned int psci_afflvl2_on_finish(unsigned long mpidr,
aff_map_node *system_node, aff_map_node *system_node)
unsigned int prev_state)
{ {
int rc = PSCI_E_SUCCESS; unsigned int plat_state, rc = PSCI_E_SUCCESS;
unsigned int plat_state;
/* Cannot go beyond this affinity level */ /* Cannot go beyond this affinity level */
assert(system_node->level == MPIDR_AFFLVL2); assert(system_node->level == MPIDR_AFFLVL2);
@ -398,7 +434,9 @@ static unsigned int psci_afflvl2_on_finish(unsigned long mpidr,
* situation. * situation.
*/ */
if (psci_plat_pm_ops->affinst_on_finish) { if (psci_plat_pm_ops->affinst_on_finish) {
plat_state = psci_get_phys_state(system_node->state);
/* Get the physical state of the system */
plat_state = psci_get_aff_phys_state(system_node);
rc = psci_plat_pm_ops->affinst_on_finish(mpidr, rc = psci_plat_pm_ops->affinst_on_finish(mpidr,
system_node->level, system_node->level,
plat_state); plat_state);

View File

@ -226,112 +226,149 @@ static const afflvl_suspend_handler psci_afflvl_suspend_handlers[] = {
}; };
/******************************************************************************* /*******************************************************************************
* This function implements the core of the processing required to suspend a cpu * This function takes an array of pointers to affinity instance nodes in the
* It'S assumed that along with suspending the cpu, higher affinity levels will * topology tree and calls the suspend handler for the corresponding affinity
* be suspended as far as possible. Suspending a cpu is equivalent to physically * levels
* powering it down, but the cpu is still available to the OS for scheduling. ******************************************************************************/
* We first need to determine the new state off all the affinity instances in static int psci_call_suspend_handlers(mpidr_aff_map_nodes mpidr_nodes,
* the mpidr corresponding to the target cpu. Action will be taken on the basis int start_afflvl,
* of this new state. To do the state change we first need to acquire the locks int end_afflvl,
* for all the implemented affinity level to be able to snapshot the system unsigned long mpidr,
* state. Then we need to start suspending affinity levels from the lowest to unsigned long entrypoint,
* the highest (e.g. a cpu needs to be suspended before a cluster can be). To unsigned long context_id,
* achieve this flow, we start acquiring the locks from the highest to the unsigned int power_state)
* lowest affinity level. Once we reach affinity level 0, we do the state change {
* followed by the actions corresponding to the new state for affinity level 0. int rc = PSCI_E_INVALID_PARAMS, level;
* Actions as per the updated state for higher affinity levels are performed as aff_map_node *node;
* we unwind back to highest affinity level.
for (level = start_afflvl; level <= end_afflvl; level++) {
node = mpidr_nodes[level];
if (node == NULL)
continue;
/*
* TODO: In case of an error should there be a way
* of restoring what we might have torn down at
* lower affinity levels.
*/
rc = psci_afflvl_suspend_handlers[level](mpidr,
node,
entrypoint,
context_id,
power_state);
if (rc != PSCI_E_SUCCESS)
break;
}
return rc;
}
/*******************************************************************************
* Top level handler which is called when a cpu wants to suspend its execution.
* It is assumed that along with turning the cpu off, higher affinity levels
* until the target affinity level will be turned off as well. It traverses
* through all the affinity levels performing generic, architectural, platform
* setup and state management e.g. for a cluster that's to be suspended, it will
* call the platform specific code which will disable coherency at the
* interconnect level if the cpu is the last in the cluster. For a cpu it could
* mean programming the power controller etc.
*
* The state of all the relevant affinity levels is changed prior to calling the
* affinity level specific handlers as their actions would depend upon the state
* the affinity level is about to enter.
*
* The affinity level specific handlers are called in ascending order i.e. from
* the lowest to the highest affinity level implemented by the platform because
* to turn off affinity level X it is neccesary to turn off affinity level X - 1
* first.
*
* CAUTION: This function is called with coherent stacks so that coherency can
* be turned off and caches can be flushed safely.
******************************************************************************/ ******************************************************************************/
int psci_afflvl_suspend(unsigned long mpidr, int psci_afflvl_suspend(unsigned long mpidr,
unsigned long entrypoint, unsigned long entrypoint,
unsigned long context_id, unsigned long context_id,
unsigned int power_state, unsigned int power_state,
int cur_afflvl, int start_afflvl,
int tgt_afflvl) int end_afflvl)
{ {
int rc = PSCI_E_SUCCESS, level; int rc = PSCI_E_SUCCESS;
unsigned int prev_state, next_state; unsigned int prev_state;
aff_map_node *aff_node; mpidr_aff_map_nodes mpidr_nodes;
mpidr &= MPIDR_AFFINITY_MASK; mpidr &= MPIDR_AFFINITY_MASK;
/* /*
* Some affinity instances at levels between the current and * Collect the pointers to the nodes in the topology tree for
* target levels could be absent in the mpidr. Skip them and * each affinity instance in the mpidr. If this function does
* start from the first present instance. * not return successfully then either the mpidr or the affinity
* levels are incorrect.
*/ */
level = psci_get_first_present_afflvl(mpidr, rc = psci_get_aff_map_nodes(mpidr,
cur_afflvl, start_afflvl,
tgt_afflvl, end_afflvl,
&aff_node); mpidr_nodes);
if (rc != PSCI_E_SUCCESS)
/*
* Return if there are no more affinity instances beyond this
* level to process. Else ensure that the returned affinity
* node makes sense.
*/
if (aff_node == NULL)
return rc; return rc;
assert(level == aff_node->level); /*
* This function acquires the lock corresponding to each affinity
* level so that by the time all locks are taken, the system topology
* is snapshot and state management can be done safely.
*/
psci_acquire_afflvl_locks(mpidr,
start_afflvl,
end_afflvl,
mpidr_nodes);
/* /*
* This function acquires the lock corresponding to each * Keep the old cpu state handy. It will be used to restore the
* affinity level so that state management can be done safely. * system to its original state in case something goes wrong
*/ */
bakery_lock_get(mpidr, &aff_node->lock); prev_state = psci_get_state(mpidr_nodes[MPIDR_AFFLVL0]->state);
/* Keep the old state and the next one handy */
prev_state = psci_get_state(aff_node->state);
next_state = PSCI_STATE_SUSPEND;
/* /*
* We start from the highest affinity level and work our way * State management: Update the state of each affinity instance
* downwards to the lowest i.e. MPIDR_AFFLVL0. * between the start and end affinity levels
*/ */
if (aff_node->level == tgt_afflvl) { psci_change_state(mpidr_nodes,
psci_change_state(mpidr, start_afflvl,
tgt_afflvl, end_afflvl,
get_max_afflvl(), PSCI_STATE_SUSPEND);
next_state);
} else { /* Perform generic, architecture and platform specific handling */
rc = psci_afflvl_suspend(mpidr, rc = psci_call_suspend_handlers(mpidr_nodes,
entrypoint, start_afflvl,
context_id, end_afflvl,
power_state, mpidr,
level - 1, entrypoint,
tgt_afflvl); context_id,
if (rc != PSCI_E_SUCCESS) { power_state);
psci_set_state(aff_node->state, prev_state);
goto exit;
}
}
/* /*
* Perform generic, architecture and platform specific * If an error is returned by a handler then restore the cpu state
* handling * to its original value. If the cpu state is restored then that
* should result in the state of the higher affinity levels to
* get restored as well.
* TODO: We are not undoing any architectural or platform specific
* operations that might have completed before encountering the
* error. The system might not be in a stable state.
*/ */
rc = psci_afflvl_suspend_handlers[level](mpidr, if (rc != PSCI_E_SUCCESS)
aff_node, psci_change_state(mpidr_nodes,
entrypoint, start_afflvl,
context_id, end_afflvl,
power_state); prev_state);
if (rc != PSCI_E_SUCCESS) {
psci_set_state(aff_node->state, prev_state);
goto exit;
}
/* /*
* If all has gone as per plan then this cpu should be * Release the locks corresponding to each affinity level in the
* marked as OFF * reverse order to which they were acquired.
*/ */
if (level == MPIDR_AFFLVL0) { psci_release_afflvl_locks(mpidr,
next_state = psci_get_state(aff_node->state); start_afflvl,
assert(next_state == PSCI_STATE_SUSPEND); end_afflvl,
} mpidr_nodes);
exit:
bakery_lock_release(mpidr, &aff_node->lock);
return rc; return rc;
} }
@ -340,13 +377,16 @@ exit:
* are called by the common finisher routine in psci_common.c. * are called by the common finisher routine in psci_common.c.
******************************************************************************/ ******************************************************************************/
static unsigned int psci_afflvl0_suspend_finish(unsigned long mpidr, static unsigned int psci_afflvl0_suspend_finish(unsigned long mpidr,
aff_map_node *cpu_node, aff_map_node *cpu_node)
unsigned int prev_state)
{ {
unsigned int index, plat_state, rc = 0; unsigned int index, plat_state, state, rc = PSCI_E_SUCCESS;
assert(cpu_node->level == MPIDR_AFFLVL0); assert(cpu_node->level == MPIDR_AFFLVL0);
/* Ensure we have been woken up from a suspended state */
state = psci_get_state(cpu_node->state);
assert(state == PSCI_STATE_SUSPEND);
/* /*
* Plat. management: Perform the platform specific actions * Plat. management: Perform the platform specific actions
* before we change the state of the cpu e.g. enabling the * before we change the state of the cpu e.g. enabling the
@ -355,7 +395,9 @@ static unsigned int psci_afflvl0_suspend_finish(unsigned long mpidr,
* situation. * situation.
*/ */
if (psci_plat_pm_ops->affinst_suspend_finish) { if (psci_plat_pm_ops->affinst_suspend_finish) {
plat_state = psci_get_phys_state(prev_state);
/* Get the physical state of this cpu */
plat_state = psci_get_phys_state(state);
rc = psci_plat_pm_ops->affinst_suspend_finish(mpidr, rc = psci_plat_pm_ops->affinst_suspend_finish(mpidr,
cpu_node->level, cpu_node->level,
plat_state); plat_state);
@ -396,11 +438,9 @@ static unsigned int psci_afflvl0_suspend_finish(unsigned long mpidr,
} }
static unsigned int psci_afflvl1_suspend_finish(unsigned long mpidr, static unsigned int psci_afflvl1_suspend_finish(unsigned long mpidr,
aff_map_node *cluster_node, aff_map_node *cluster_node)
unsigned int prev_state)
{ {
unsigned int rc = 0; unsigned int plat_state, rc = PSCI_E_SUCCESS;
unsigned int plat_state;
assert(cluster_node->level == MPIDR_AFFLVL1); assert(cluster_node->level == MPIDR_AFFLVL1);
@ -413,7 +453,9 @@ static unsigned int psci_afflvl1_suspend_finish(unsigned long mpidr,
* situation. * situation.
*/ */
if (psci_plat_pm_ops->affinst_suspend_finish) { if (psci_plat_pm_ops->affinst_suspend_finish) {
plat_state = psci_get_phys_state(prev_state);
/* Get the physical state of this cpu */
plat_state = psci_get_aff_phys_state(cluster_node);
rc = psci_plat_pm_ops->affinst_suspend_finish(mpidr, rc = psci_plat_pm_ops->affinst_suspend_finish(mpidr,
cluster_node->level, cluster_node->level,
plat_state); plat_state);
@ -425,11 +467,9 @@ static unsigned int psci_afflvl1_suspend_finish(unsigned long mpidr,
static unsigned int psci_afflvl2_suspend_finish(unsigned long mpidr, static unsigned int psci_afflvl2_suspend_finish(unsigned long mpidr,
aff_map_node *system_node, aff_map_node *system_node)
unsigned int target_afflvl)
{ {
int rc = PSCI_E_SUCCESS; unsigned int plat_state, rc = PSCI_E_SUCCESS;;
unsigned int plat_state;
/* Cannot go beyond this affinity level */ /* Cannot go beyond this affinity level */
assert(system_node->level == MPIDR_AFFLVL2); assert(system_node->level == MPIDR_AFFLVL2);
@ -448,7 +488,9 @@ static unsigned int psci_afflvl2_suspend_finish(unsigned long mpidr,
* situation. * situation.
*/ */
if (psci_plat_pm_ops->affinst_suspend_finish) { if (psci_plat_pm_ops->affinst_suspend_finish) {
plat_state = psci_get_phys_state(system_node->state);
/* Get the physical state of the system */
plat_state = psci_get_aff_phys_state(system_node);
rc = psci_plat_pm_ops->affinst_suspend_finish(mpidr, rc = psci_plat_pm_ops->affinst_suspend_finish(mpidr,
system_node->level, system_node->level,
plat_state); plat_state);

View File

@ -110,6 +110,62 @@ unsigned long mpidr_set_aff_inst(unsigned long mpidr,
return mpidr; return mpidr;
} }
/*******************************************************************************
* This function sanity checks a range of affinity levels.
******************************************************************************/
int psci_check_afflvl_range(int start_afflvl, int end_afflvl)
{
/* Sanity check the parameters passed */
if (end_afflvl > MPIDR_MAX_AFFLVL)
return PSCI_E_INVALID_PARAMS;
if (start_afflvl < MPIDR_AFFLVL0)
return PSCI_E_INVALID_PARAMS;
if (end_afflvl < start_afflvl)
return PSCI_E_INVALID_PARAMS;
return PSCI_E_SUCCESS;
}
/*******************************************************************************
* This function is passed an array of pointers to affinity level nodes in the
* topology tree for an mpidr. It picks up locks for each affinity level bottom
* up in the range specified.
******************************************************************************/
void psci_acquire_afflvl_locks(unsigned long mpidr,
int start_afflvl,
int end_afflvl,
mpidr_aff_map_nodes mpidr_nodes)
{
int level;
for (level = start_afflvl; level <= end_afflvl; level++) {
if (mpidr_nodes[level] == NULL)
continue;
bakery_lock_get(mpidr, &mpidr_nodes[level]->lock);
}
}
/*******************************************************************************
* This function is passed an array of pointers to affinity level nodes in the
* topology tree for an mpidr. It releases the lock for each affinity level top
* down in the range specified.
******************************************************************************/
void psci_release_afflvl_locks(unsigned long mpidr,
int start_afflvl,
int end_afflvl,
mpidr_aff_map_nodes mpidr_nodes)
{
int level;
for (level = end_afflvl; level >= start_afflvl; level--) {
if (mpidr_nodes[level] == NULL)
continue;
bakery_lock_release(mpidr, &mpidr_nodes[level]->lock);
}
}
/******************************************************************************* /*******************************************************************************
* Simple routine to determine whether an affinity instance at a given level * Simple routine to determine whether an affinity instance at a given level
* in an mpidr exists or not. * in an mpidr exists or not.
@ -158,37 +214,44 @@ int psci_get_first_present_afflvl(unsigned long mpidr,
} }
/******************************************************************************* /*******************************************************************************
* Recursively change the affinity state between the current and target affinity * Iteratively change the affinity state between the current and target affinity
* levels. The target state matters only if we are starting from affinity level * levels. The target state matters only if we are starting from affinity level
* 0 i.e. a cpu otherwise the state depends upon the state of the lower affinity * 0 i.e. a cpu otherwise the state depends upon the state of the lower affinity
* levels. * levels.
******************************************************************************/ ******************************************************************************/
int psci_change_state(unsigned long mpidr, int psci_change_state(mpidr_aff_map_nodes mpidr_nodes,
int cur_afflvl, int start_afflvl,
int tgt_afflvl, int end_afflvl,
unsigned int tgt_state) unsigned int tgt_state)
{ {
int rc = PSCI_E_SUCCESS; int rc = PSCI_E_SUCCESS, level;
unsigned int state; unsigned int state;
aff_map_node *aff_node; aff_map_node *node;
/* Sanity check the affinity levels */ /*
assert(tgt_afflvl >= cur_afflvl); * Get a temp pointer to the node. It is not possible that affinity
* level 0 is missing. Simply ignore higher missing levels.
*/
for (level = start_afflvl; level <= end_afflvl; level++) {
aff_node = psci_get_aff_map_node(mpidr, cur_afflvl); node = mpidr_nodes[level];
assert(aff_node); if (level == MPIDR_AFFLVL0) {
assert(node);
/* TODO: Check whether the affinity level is present or absent*/ psci_set_state(node->state, tgt_state);
} else {
if (cur_afflvl == MPIDR_AFFLVL0) { if (node == NULL)
psci_set_state(aff_node->state, tgt_state); continue;
} else { state = psci_calculate_affinity_state(node);
state = psci_calculate_affinity_state(aff_node); psci_set_state(node->state, state);
psci_set_state(aff_node->state, state); }
} }
if (cur_afflvl != tgt_afflvl) /* If all went well then the cpu should be in the target state */
psci_change_state(mpidr, cur_afflvl + 1, tgt_afflvl, tgt_state); if (start_afflvl == MPIDR_AFFLVL0) {
node = mpidr_nodes[MPIDR_AFFLVL0];
state = psci_get_state(node->state);
assert(tgt_state == state);
}
return rc; return rc;
} }
@ -442,93 +505,113 @@ unsigned int psci_get_aff_phys_state(aff_map_node *aff_node)
return psci_get_phys_state(aff_state); return psci_get_phys_state(aff_state);
} }
/*******************************************************************************
* This function takes an array of pointers to affinity instance nodes in the
* topology tree and calls the physical power on handler for the corresponding
* affinity levels
******************************************************************************/
static int psci_call_power_on_handlers(mpidr_aff_map_nodes mpidr_nodes,
int start_afflvl,
int end_afflvl,
afflvl_power_on_finisher *pon_handlers,
unsigned long mpidr)
{
int rc = PSCI_E_INVALID_PARAMS, level;
aff_map_node *node;
for (level = end_afflvl; level >= start_afflvl; level--) {
node = mpidr_nodes[level];
if (node == NULL)
continue;
/*
* If we run into any trouble while powering up an
* affinity instance, then there is no recovery path
* so simply return an error and let the caller take
* care of the situation.
*/
rc = pon_handlers[level](mpidr, node);
if (rc != PSCI_E_SUCCESS)
break;
}
return rc;
}
/******************************************************************************* /*******************************************************************************
* Generic handler which is called when a cpu is physically powered on. It * Generic handler which is called when a cpu is physically powered on. It
* recurses through all the affinity levels performing generic, architectural, * traverses through all the affinity levels performing generic, architectural,
* platform setup and state management e.g. for a cluster that's been powered * platform setup and state management e.g. for a cluster that's been powered
* on, it will call the platform specific code which will enable coherency at * on, it will call the platform specific code which will enable coherency at
* the interconnect level. For a cpu it could mean turning on the MMU etc. * the interconnect level. For a cpu it could mean turning on the MMU etc.
* *
* This function traverses from the lowest to the highest affinity level * The state of all the relevant affinity levels is changed after calling the
* implemented by the platform. Since it's recursive, for each call the * affinity level specific handlers as their actions would depend upon the state
* 'cur_afflvl' & 'tgt_afflvl' parameters keep track of which level we are at * the affinity level is exiting from.
* and which level we need to get to respectively. Locks are picked up along the *
* way so that when the lowest affinity level is hit, state management can be * The affinity level specific handlers are called in descending order i.e. from
* safely done. Prior to this, each affinity level does it's bookeeping as per * the highest to the lowest affinity level implemented by the platform because
* the state out of reset. * to turn on affinity level X it is neccesary to turn on affinity level X + 1
* first.
* *
* CAUTION: This function is called with coherent stacks so that coherency and * CAUTION: This function is called with coherent stacks so that coherency and
* the mmu can be turned on safely. * the mmu can be turned on safely.
******************************************************************************/ ******************************************************************************/
unsigned int psci_afflvl_power_on_finish(unsigned long mpidr, void psci_afflvl_power_on_finish(unsigned long mpidr,
int cur_afflvl, int start_afflvl,
int tgt_afflvl, int end_afflvl,
afflvl_power_on_finisher *pon_handlers) afflvl_power_on_finisher *pon_handlers)
{ {
unsigned int prev_state, next_state, rc = PSCI_E_SUCCESS; mpidr_aff_map_nodes mpidr_nodes;
aff_map_node *aff_node; int rc;
int level;
mpidr &= MPIDR_AFFINITY_MASK;; mpidr &= MPIDR_AFFINITY_MASK;;
/* /*
* Some affinity instances at levels between the current and * Collect the pointers to the nodes in the topology tree for
* target levels could be absent in the mpidr. Skip them and * each affinity instance in the mpidr. If this function does
* start from the first present instance. * not return successfully then either the mpidr or the affinity
* levels are incorrect. Either case is an irrecoverable error.
*/ */
level = psci_get_first_present_afflvl(mpidr, rc = psci_get_aff_map_nodes(mpidr,
cur_afflvl, start_afflvl,
tgt_afflvl, end_afflvl,
&aff_node); mpidr_nodes);
/* assert (rc == PSCI_E_SUCCESS);
* Return if there are no more affinity instances beyond this
* level to process. Else ensure that the returned affinity
* node makes sense.
*/
if (aff_node == NULL)
return rc;
assert(level == aff_node->level);
/* /*
* This function acquires the lock corresponding to each * This function acquires the lock corresponding to each affinity
* affinity level so that by the time we hit the highest * level so that by the time all locks are taken, the system topology
* affinity level, the system topology is snapshot and state * is snapshot and state management can be done safely.
* management can be done safely.
*/ */
bakery_lock_get(mpidr, &aff_node->lock); psci_acquire_afflvl_locks(mpidr,
start_afflvl,
/* Keep the old and new state handy */ end_afflvl,
prev_state = psci_get_state(aff_node->state); mpidr_nodes);
next_state = PSCI_STATE_ON;
/* Perform generic, architecture and platform specific handling */ /* Perform generic, architecture and platform specific handling */
rc = pon_handlers[level](mpidr, aff_node, prev_state); rc = psci_call_power_on_handlers(mpidr_nodes,
if (rc != PSCI_E_SUCCESS) { start_afflvl,
psci_set_state(aff_node->state, prev_state); end_afflvl,
goto exit; pon_handlers,
} mpidr);
assert (rc == PSCI_E_SUCCESS);
/* /*
* State management: Update the states if this is the highest * State management: Update the state of each affinity instance
* affinity level requested else pass the job to the next level. * between the start and end affinity levels
*/ */
if (aff_node->level != tgt_afflvl) { psci_change_state(mpidr_nodes,
rc = psci_afflvl_power_on_finish(mpidr, start_afflvl,
level + 1, end_afflvl,
tgt_afflvl, PSCI_STATE_ON);
pon_handlers);
} else {
psci_change_state(mpidr, MPIDR_AFFLVL0, tgt_afflvl, next_state);
}
/* If all has gone as per plan then this cpu should be marked as ON */ /*
if (level == MPIDR_AFFLVL0) { * This loop releases the lock corresponding to each affinity level
next_state = psci_get_state(aff_node->state); * in the reverse order to which they were acquired.
assert(next_state == PSCI_STATE_ON); */
} psci_release_afflvl_locks(mpidr,
start_afflvl,
exit: end_afflvl,
bakery_lock_release(mpidr, &aff_node->lock); mpidr_nodes);
return rc;
} }

View File

@ -45,7 +45,7 @@ int psci_cpu_on(unsigned long target_cpu,
{ {
int rc; int rc;
unsigned int start_afflvl, target_afflvl; unsigned int start_afflvl, end_afflvl;
/* Determine if the cpu exists of not */ /* Determine if the cpu exists of not */
rc = psci_validate_mpidr(target_cpu, MPIDR_AFFLVL0); rc = psci_validate_mpidr(target_cpu, MPIDR_AFFLVL0);
@ -53,13 +53,17 @@ int psci_cpu_on(unsigned long target_cpu,
goto exit; goto exit;
} }
start_afflvl = get_max_afflvl(); /*
target_afflvl = MPIDR_AFFLVL0; * To turn this cpu on, specify which affinity
* levels need to be turned on
*/
start_afflvl = MPIDR_AFFLVL0;
end_afflvl = get_max_afflvl();
rc = psci_afflvl_on(target_cpu, rc = psci_afflvl_on(target_cpu,
entrypoint, entrypoint,
context_id, context_id,
start_afflvl, start_afflvl,
target_afflvl); end_afflvl);
exit: exit:
return rc; return rc;
@ -76,7 +80,7 @@ int psci_cpu_suspend(unsigned int power_state,
{ {
int rc; int rc;
unsigned long mpidr; unsigned long mpidr;
unsigned int tgt_afflvl, pstate_type; unsigned int target_afflvl, pstate_type;
/* TODO: Standby states are not supported at the moment */ /* TODO: Standby states are not supported at the moment */
pstate_type = psci_get_pstate_type(power_state); pstate_type = psci_get_pstate_type(power_state);
@ -86,8 +90,8 @@ int psci_cpu_suspend(unsigned int power_state,
} }
/* Sanity check the requested state */ /* Sanity check the requested state */
tgt_afflvl = psci_get_pstate_afflvl(power_state); target_afflvl = psci_get_pstate_afflvl(power_state);
if (tgt_afflvl > MPIDR_MAX_AFFLVL) { if (target_afflvl > MPIDR_MAX_AFFLVL) {
rc = PSCI_E_INVALID_PARAMS; rc = PSCI_E_INVALID_PARAMS;
goto exit; goto exit;
} }
@ -97,8 +101,8 @@ int psci_cpu_suspend(unsigned int power_state,
entrypoint, entrypoint,
context_id, context_id,
power_state, power_state,
tgt_afflvl, MPIDR_AFFLVL0,
MPIDR_AFFLVL0); target_afflvl);
exit: exit:
if (rc != PSCI_E_SUCCESS) if (rc != PSCI_E_SUCCESS)
@ -120,7 +124,7 @@ int psci_cpu_off(void)
* management is done immediately followed by cpu, cluster ... * management is done immediately followed by cpu, cluster ...
* ..target_afflvl specific actions as this function unwinds back. * ..target_afflvl specific actions as this function unwinds back.
*/ */
rc = psci_afflvl_off(mpidr, target_afflvl, MPIDR_AFFLVL0); rc = psci_afflvl_off(mpidr, MPIDR_AFFLVL0, target_afflvl);
/* /*
* The only error cpu_off can return is E_DENIED. So check if that's * The only error cpu_off can return is E_DENIED. So check if that's

View File

@ -84,9 +84,9 @@ typedef struct {
int max; int max;
} aff_limits_node; } aff_limits_node;
typedef aff_map_node *mpidr_aff_map_nodes[MPIDR_MAX_AFFLVL];
typedef unsigned int (*afflvl_power_on_finisher)(unsigned long, typedef unsigned int (*afflvl_power_on_finisher)(unsigned long,
aff_map_node *, aff_map_node *);
unsigned int);
/******************************************************************************* /*******************************************************************************
* Data prototypes * Data prototypes
@ -110,9 +110,9 @@ extern unsigned int psci_get_aff_phys_state(aff_map_node *);
extern unsigned int psci_calculate_affinity_state(aff_map_node *); extern unsigned int psci_calculate_affinity_state(aff_map_node *);
extern void psci_get_ns_entry_info(unsigned int index); extern void psci_get_ns_entry_info(unsigned int index);
extern unsigned long mpidr_set_aff_inst(unsigned long,unsigned char, int); extern unsigned long mpidr_set_aff_inst(unsigned long,unsigned char, int);
extern int psci_change_state(unsigned long, int, int, unsigned int); extern int psci_change_state(mpidr_aff_map_nodes, int, int, unsigned int);
extern int psci_validate_mpidr(unsigned long, int); extern int psci_validate_mpidr(unsigned long, int);
extern unsigned int psci_afflvl_power_on_finish(unsigned long, extern void psci_afflvl_power_on_finish(unsigned long,
int, int,
int, int,
afflvl_power_on_finisher *); afflvl_power_on_finisher *);
@ -122,7 +122,21 @@ extern int psci_set_ns_entry_info(unsigned int index,
extern int psci_get_first_present_afflvl(unsigned long, extern int psci_get_first_present_afflvl(unsigned long,
int, int, int, int,
aff_map_node **); aff_map_node **);
extern int psci_check_afflvl_range(int start_afflvl, int end_afflvl);
extern void psci_acquire_afflvl_locks(unsigned long mpidr,
int start_afflvl,
int end_afflvl,
mpidr_aff_map_nodes mpidr_nodes);
extern void psci_release_afflvl_locks(unsigned long mpidr,
int start_afflvl,
int end_afflvl,
mpidr_aff_map_nodes mpidr_nodes);
/* Private exported functions from psci_setup.c */ /* Private exported functions from psci_setup.c */
extern int psci_get_aff_map_nodes(unsigned long mpidr,
int start_afflvl,
int end_afflvl,
mpidr_aff_map_nodes mpidr_nodes);
extern aff_map_node *psci_get_aff_map_node(unsigned long, int); extern aff_map_node *psci_get_aff_map_node(unsigned long, int);
/* Private exported functions from psci_affinity_on.c */ /* Private exported functions from psci_affinity_on.c */

View File

@ -86,6 +86,56 @@ aff_map_node *psci_get_aff_map_node(unsigned long mpidr, int aff_lvl)
return NULL; return NULL;
} }
/*******************************************************************************
* This function populates an array with nodes corresponding to a given range of
* affinity levels in an mpidr. It returns successfully only when the affinity
* levels are correct, the mpidr is valid i.e. no affinity level is absent from
* the topology tree & the affinity instance at level 0 is not absent.
******************************************************************************/
int psci_get_aff_map_nodes(unsigned long mpidr,
int start_afflvl,
int end_afflvl,
mpidr_aff_map_nodes mpidr_nodes)
{
int rc = PSCI_E_INVALID_PARAMS, level;
aff_map_node *node;
rc = psci_check_afflvl_range(start_afflvl, end_afflvl);
if (rc != PSCI_E_SUCCESS)
return rc;
for (level = start_afflvl; level <= end_afflvl; level++) {
/*
* Grab the node for each affinity level. No affinity level
* can be missing as that would mean that the topology tree
* is corrupted.
*/
node = psci_get_aff_map_node(mpidr, level);
if (node == NULL) {
rc = PSCI_E_INVALID_PARAMS;
break;
}
/*
* Skip absent affinity levels unless it's afffinity level 0.
* An absent cpu means that the mpidr is invalid. Save the
* pointer to the node for the present affinity level
*/
if (!(node->state & PSCI_AFF_PRESENT)) {
if (level == MPIDR_AFFLVL0) {
rc = PSCI_E_INVALID_PARAMS;
break;
}
mpidr_nodes[level] = NULL;
} else
mpidr_nodes[level] = node;
}
return rc;
}
/******************************************************************************* /*******************************************************************************
* Function which initializes the 'aff_map_node' corresponding to an affinity * Function which initializes the 'aff_map_node' corresponding to an affinity
* level instance. Each node has a unique mpidr, level and bakery lock. The data * level instance. Each node has a unique mpidr, level and bakery lock. The data