diff options
Diffstat (limited to 'common/psci/psci_common.c')
-rw-r--r-- | common/psci/psci_common.c | 247 |
1 files changed, 70 insertions, 177 deletions
diff --git a/common/psci/psci_common.c b/common/psci/psci_common.c index e9028ccd..e6be2f86 100644 --- a/common/psci/psci_common.c +++ b/common/psci/psci_common.c @@ -93,7 +93,7 @@ int get_power_on_target_afflvl(unsigned long mpidr) * Call the handler in the suspend code if this cpu had been suspended. * Any other state is invalid. */ - state = psci_get_state(node->state); + state = psci_get_state(node); if (state == PSCI_STATE_ON_PENDING) return get_max_afflvl(); @@ -214,164 +214,6 @@ int psci_validate_mpidr(unsigned long mpidr, int level) } /******************************************************************************* - * Simple routine to determine the first affinity level instance that is present - * between the start and end affinity levels. This helps to skip handling of - * absent affinity levels while performing psci operations. - * The start level can be > or <= to the end level depending upon whether this - * routine is expected to search top down or bottom up. - ******************************************************************************/ -int psci_get_first_present_afflvl(unsigned long mpidr, - int start_afflvl, - int end_afflvl, - aff_map_node **node) -{ - int level; - - /* Check whether we have to search up or down */ - if (start_afflvl <= end_afflvl) { - for (level = start_afflvl; level <= end_afflvl; level++) { - *node = psci_get_aff_map_node(mpidr, level); - if (*node && ((*node)->state & PSCI_AFF_PRESENT)) - break; - } - } else { - for (level = start_afflvl; level >= end_afflvl; level--) { - *node = psci_get_aff_map_node(mpidr, level); - if (*node && ((*node)->state & PSCI_AFF_PRESENT)) - break; - } - } - - return level; -} - -/******************************************************************************* - * Iteratively change the affinity state between the current and target affinity - * 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 - * levels. - ******************************************************************************/ -int psci_change_state(mpidr_aff_map_nodes mpidr_nodes, - int start_afflvl, - int end_afflvl, - unsigned int tgt_state) -{ - int rc = PSCI_E_SUCCESS, level; - unsigned int state; - aff_map_node *node; - - /* - * 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++) { - - node = mpidr_nodes[level]; - if (level == MPIDR_AFFLVL0) { - assert(node); - psci_set_state(node->state, tgt_state); - } else { - if (node == NULL) - continue; - state = psci_calculate_affinity_state(node); - psci_set_state(node->state, state); - } - } - - /* If all went well then the cpu should be in the target state */ - if (start_afflvl == MPIDR_AFFLVL0) { - node = mpidr_nodes[MPIDR_AFFLVL0]; - state = psci_get_state(node->state); - assert(tgt_state == state); - } - - return rc; -} - -/******************************************************************************* - * This routine does the heavy lifting for psci_change_state(). It examines the - * state of each affinity instance at the next lower affinity level and decides - * its final state accordingly. If a lower affinity instance is ON then the - * higher affinity instance is ON. If all the lower affinity instances are OFF - * then the higher affinity instance is OFF. If atleast one lower affinity - * instance is SUSPENDED then the higher affinity instance is SUSPENDED. If only - * a single lower affinity instance is ON_PENDING then the higher affinity - * instance in ON_PENDING as well. - ******************************************************************************/ -unsigned int psci_calculate_affinity_state(aff_map_node *aff_node) -{ - int ctr; - unsigned int aff_count, hi_aff_state; - unsigned long tempidr; - aff_map_node *lo_aff_node; - - /* Cannot calculate lowest affinity state. It is simply assigned */ - assert(aff_node->level > MPIDR_AFFLVL0); - - /* - * Find the number of affinity instances at level X-1 e.g. number of - * cpus in a cluster. The level X state depends upon the state of each - * instance at level X-1 - */ - hi_aff_state = PSCI_STATE_OFF; - aff_count = plat_get_aff_count(aff_node->level - 1, aff_node->mpidr); - for (ctr = 0; ctr < aff_count; ctr++) { - - /* - * Create a mpidr for each lower affinity level (X-1). Use their - * states to influence the higher affinity state (X). - */ - tempidr = mpidr_set_aff_inst(aff_node->mpidr, - ctr, - aff_node->level - 1); - lo_aff_node = psci_get_aff_map_node(tempidr, - aff_node->level - 1); - assert(lo_aff_node); - - /* Continue only if the cpu exists within the cluster */ - if (!(lo_aff_node->state & PSCI_AFF_PRESENT)) - continue; - - switch (psci_get_state(lo_aff_node->state)) { - - /* - * If any lower affinity is on within the cluster, then - * the higher affinity is on. - */ - case PSCI_STATE_ON: - return PSCI_STATE_ON; - - /* - * At least one X-1 needs to be suspended for X to be suspended - * but it is effectively on for the affinity_info call. - * SUSPEND > ON_PENDING > OFF. - */ - case PSCI_STATE_SUSPEND: - hi_aff_state = PSCI_STATE_SUSPEND; - continue; - - /* - * Atleast one X-1 needs to be on_pending & the rest off for X - * to be on_pending. ON_PENDING > OFF. - */ - case PSCI_STATE_ON_PENDING: - if (hi_aff_state != PSCI_STATE_SUSPEND) - hi_aff_state = PSCI_STATE_ON_PENDING; - continue; - - /* Higher affinity is off if all lower affinities are off. */ - case PSCI_STATE_OFF: - continue; - - default: - assert(0); - } - } - - return hi_aff_state; -} - -/******************************************************************************* * This function retrieves all the stashed information needed to correctly * resume a cpu's execution in the non-secure state after it has been physically * powered on i.e. turned ON or resumed from SUSPEND @@ -518,23 +360,83 @@ int psci_set_ns_entry_info(unsigned int index, } /******************************************************************************* + * This function takes a pointer to an affinity node in the topology tree and + * returns its state. State of a non-leaf node needs to be calculated. + ******************************************************************************/ +unsigned short psci_get_state(aff_map_node *node) +{ + assert(node->level >= MPIDR_AFFLVL0 && node->level <= MPIDR_MAX_AFFLVL); + + /* A cpu node just contains the state which can be directly returned */ + if (node->level == MPIDR_AFFLVL0) + return (node->state >> PSCI_STATE_SHIFT) & PSCI_STATE_MASK; + + /* + * For an affinity level higher than a cpu, the state has to be + * calculated. It depends upon the value of the reference count + * which is managed by each node at the next lower affinity level + * e.g. for a cluster, each cpu increments/decrements the reference + * count. If the reference count is 0 then the affinity level is + * OFF else ON. + */ + if (node->ref_count) + return PSCI_STATE_ON; + else + return PSCI_STATE_OFF; +} + +/******************************************************************************* + * This function takes a pointer to an affinity node in the topology tree and + * a target state. State of a non-leaf node needs to be converted to a reference + * count. State of a leaf node can be set directly. + ******************************************************************************/ +void psci_set_state(aff_map_node *node, unsigned short state) +{ + assert(node->level >= MPIDR_AFFLVL0 && node->level <= MPIDR_MAX_AFFLVL); + + /* + * For an affinity level higher than a cpu, the state is used + * to decide whether the reference count is incremented or + * decremented. Entry into the ON_PENDING state does not have + * effect. + */ + if (node->level > MPIDR_AFFLVL0) { + switch (state) { + case PSCI_STATE_ON: + node->ref_count++; + break; + case PSCI_STATE_OFF: + case PSCI_STATE_SUSPEND: + node->ref_count--; + break; + case PSCI_STATE_ON_PENDING: + /* + * An affinity level higher than a cpu will not undergo + * a state change when it is about to be turned on + */ + return; + default: + assert(0); + } + } else { + node->state &= ~(PSCI_STATE_MASK << PSCI_STATE_SHIFT); + node->state |= (state & PSCI_STATE_MASK) << PSCI_STATE_SHIFT; + } +} + +/******************************************************************************* * An affinity level could be on, on_pending, suspended or off. These are the * logical states it can be in. Physically either it is off or on. When it is in * the state on_pending then it is about to be turned on. It is not possible to * tell whether that's actually happenned or not. So we err on the side of * caution & treat the affinity level as being turned off. ******************************************************************************/ -inline unsigned int psci_get_phys_state(unsigned int aff_state) +unsigned short psci_get_phys_state(aff_map_node *node) { - return (aff_state != PSCI_STATE_ON ? PSCI_STATE_OFF : PSCI_STATE_ON); -} - -unsigned int psci_get_aff_phys_state(aff_map_node *aff_node) -{ - unsigned int aff_state; + unsigned int state; - aff_state = psci_get_state(aff_node->state); - return psci_get_phys_state(aff_state); + state = psci_get_state(node); + return get_phys_state(state); } /******************************************************************************* @@ -630,15 +532,6 @@ void psci_afflvl_power_on_finish(unsigned long mpidr, assert (rc == PSCI_E_SUCCESS); /* - * State management: Update the state of each affinity instance - * between the start and end affinity levels - */ - psci_change_state(mpidr_nodes, - start_afflvl, - end_afflvl, - PSCI_STATE_ON); - - /* * This loop releases the lock corresponding to each affinity level * in the reverse order to which they were acquired. */ |