summaryrefslogtreecommitdiff
path: root/services/std_svc/psci
diff options
context:
space:
mode:
Diffstat (limited to 'services/std_svc/psci')
-rw-r--r--services/std_svc/psci/psci_afflvl_suspend.c76
-rw-r--r--services/std_svc/psci/psci_common.c42
-rw-r--r--services/std_svc/psci/psci_entry.S5
-rw-r--r--services/std_svc/psci/psci_private.h7
-rw-r--r--services/std_svc/psci/psci_setup.c8
5 files changed, 59 insertions, 79 deletions
diff --git a/services/std_svc/psci/psci_afflvl_suspend.c b/services/std_svc/psci/psci_afflvl_suspend.c
index 70f90a15..a123dc39 100644
--- a/services/std_svc/psci/psci_afflvl_suspend.c
+++ b/services/std_svc/psci/psci_afflvl_suspend.c
@@ -34,6 +34,7 @@
#include <arch_helpers.h>
#include <context.h>
#include <context_mgmt.h>
+#include <cpu_data.h>
#include <platform.h>
#include <runtime_svc.h>
#include <stddef.h>
@@ -45,76 +46,59 @@ typedef int (*afflvl_suspend_handler_t)(aff_map_node_t *,
unsigned int);
/*******************************************************************************
- * This function sets the power state of the current cpu while
- * powering down during a cpu_suspend call
+ * This function saves the power state parameter passed in the current PSCI
+ * cpu_suspend call in the per-cpu data array.
******************************************************************************/
-void psci_set_suspend_power_state(aff_map_node_t *node, unsigned int power_state)
+void psci_set_suspend_power_state(unsigned int power_state)
{
- /*
- * Check that nobody else is calling this function on our behalf &
- * this information is being set only in the cpu node
- */
- assert(node->mpidr == (read_mpidr() & MPIDR_AFFINITY_MASK));
- assert(node->level == MPIDR_AFFLVL0);
-
- /*
- * Save PSCI power state parameter for the core in suspend context.
- * The node is in always-coherent RAM so it does not need to be flushed
- */
- node->power_state = power_state;
+ set_cpu_data(psci_svc_cpu_data.power_state, power_state);
+ flush_cpu_data(psci_svc_cpu_data.power_state);
}
/*******************************************************************************
- * This function gets the affinity level till which a cpu is powered down
- * during a cpu_suspend call. Returns PSCI_INVALID_DATA if the
- * power state saved for the node is invalid
+ * This function gets the affinity level till which the current cpu could be
+ * powered down during a cpu_suspend call. Returns PSCI_INVALID_DATA if the
+ * power state is invalid.
******************************************************************************/
-int psci_get_suspend_afflvl(unsigned long mpidr)
+int psci_get_suspend_afflvl()
{
- aff_map_node_t *node;
+ unsigned int power_state;
- node = psci_get_aff_map_node(mpidr & MPIDR_AFFINITY_MASK,
- MPIDR_AFFLVL0);
- assert(node);
+ power_state = get_cpu_data(psci_svc_cpu_data.power_state);
- return psci_get_aff_map_node_suspend_afflvl(node);
+ return ((power_state == PSCI_INVALID_DATA) ?
+ power_state : psci_get_pstate_afflvl(power_state));
}
-
/*******************************************************************************
- * This function gets the affinity level till which the current cpu was powered
- * down during a cpu_suspend call. Returns PSCI_INVALID_DATA if the
- * power state saved for the node is invalid
+ * This function gets the state id of the current cpu from the power state
+ * parameter saved in the per-cpu data array. Returns PSCI_INVALID_DATA if the
+ * power state saved is invalid.
******************************************************************************/
-int psci_get_aff_map_node_suspend_afflvl(aff_map_node_t *node)
+int psci_get_suspend_stateid()
{
unsigned int power_state;
- assert(node->level == MPIDR_AFFLVL0);
+ power_state = get_cpu_data(psci_svc_cpu_data.power_state);
- power_state = node->power_state;
return ((power_state == PSCI_INVALID_DATA) ?
- power_state : psci_get_pstate_afflvl(power_state));
+ power_state : psci_get_pstate_id(power_state));
}
/*******************************************************************************
- * This function gets the state id of a cpu stored in suspend context
- * while powering down during a cpu_suspend call. Returns 0xFFFFFFFF
- * if the power state saved for the node is invalid
+ * This function gets the state id of the cpu specified by the 'mpidr' parameter
+ * from the power state parameter saved in the per-cpu data array. Returns
+ * PSCI_INVALID_DATA if the power state saved is invalid.
******************************************************************************/
-int psci_get_suspend_stateid(unsigned long mpidr)
+int psci_get_suspend_stateid_by_mpidr(unsigned long mpidr)
{
- aff_map_node_t *node;
unsigned int power_state;
- node = psci_get_aff_map_node(mpidr & MPIDR_AFFINITY_MASK,
- MPIDR_AFFLVL0);
- assert(node);
- assert(node->level == MPIDR_AFFLVL0);
+ power_state = get_cpu_data_by_mpidr(mpidr,
+ psci_svc_cpu_data.power_state);
- power_state = node->power_state;
return ((power_state == PSCI_INVALID_DATA) ?
- power_state : psci_get_pstate_id(power_state));
+ power_state : psci_get_pstate_id(power_state));
}
/*******************************************************************************
@@ -136,7 +120,7 @@ static int psci_afflvl0_suspend(aff_map_node_t *cpu_node,
assert(cpu_node->level == MPIDR_AFFLVL0);
/* Save PSCI power state parameter for the core in suspend context */
- psci_set_suspend_power_state(cpu_node, power_state);
+ psci_set_suspend_power_state(power_state);
/*
* Generic management: Store the re-entry information for the non-secure
@@ -451,13 +435,13 @@ static unsigned int psci_afflvl0_suspend_finish(aff_map_node_t *cpu_node)
* error, it's expected to assert within
*/
if (psci_spd_pm && psci_spd_pm->svc_suspend) {
- suspend_level = psci_get_aff_map_node_suspend_afflvl(cpu_node);
+ suspend_level = psci_get_suspend_afflvl();
assert (suspend_level != PSCI_INVALID_DATA);
psci_spd_pm->svc_suspend_finish(suspend_level);
}
/* Invalidate the suspend context for the node */
- psci_set_suspend_power_state(cpu_node, PSCI_INVALID_DATA);
+ psci_set_suspend_power_state(PSCI_INVALID_DATA);
/*
* Generic management: Now we just need to retrieve the
diff --git a/services/std_svc/psci/psci_common.c b/services/std_svc/psci/psci_common.c
index 2fd1764c..9daf6f04 100644
--- a/services/std_svc/psci/psci_common.c
+++ b/services/std_svc/psci/psci_common.c
@@ -61,37 +61,39 @@ const plat_pm_ops_t *psci_plat_pm_ops;
/*******************************************************************************
* Routine to return the maximum affinity level to traverse to after a cpu has
* been physically powered up. It is expected to be called immediately after
- * reset from assembler code. It has to find its 'aff_map_node' instead of
- * getting it as an argument.
- * TODO: Calling psci_get_aff_map_node() with the MMU disabled is slow. Add
- * support to allow faster access to the target affinity level.
+ * reset from assembler code.
******************************************************************************/
-int get_power_on_target_afflvl(unsigned long mpidr)
+int get_power_on_target_afflvl()
{
- aff_map_node_t *node;
- unsigned int state;
int afflvl;
+#if DEBUG
+ unsigned int state;
+ aff_map_node_t *node;
+
/* Retrieve our node from the topology tree */
- node = psci_get_aff_map_node(mpidr & MPIDR_AFFINITY_MASK,
- MPIDR_AFFLVL0);
+ node = psci_get_aff_map_node(read_mpidr_el1() & MPIDR_AFFINITY_MASK,
+ MPIDR_AFFLVL0);
assert(node);
/*
- * Return the maximum supported affinity level if this cpu was off.
- * Call the handler in the suspend code if this cpu had been suspended.
- * Any other state is invalid.
+ * Sanity check the state of the cpu. It should be either suspend or "on
+ * pending"
*/
state = psci_get_state(node);
- if (state == PSCI_STATE_ON_PENDING)
- return get_max_afflvl();
+ assert(state == PSCI_STATE_SUSPEND || state == PSCI_STATE_ON_PENDING);
+#endif
- if (state == PSCI_STATE_SUSPEND) {
- afflvl = psci_get_aff_map_node_suspend_afflvl(node);
- assert(afflvl != PSCI_INVALID_DATA);
- return afflvl;
- }
- return PSCI_E_INVALID_PARAMS;
+ /*
+ * Assume that this cpu was suspended and retrieve its target affinity
+ * level. If it is invalid then it could only have been turned off
+ * earlier. get_max_afflvl() will return the highest affinity level a
+ * cpu can be turned off to.
+ */
+ afflvl = psci_get_suspend_afflvl();
+ if (afflvl == PSCI_INVALID_DATA)
+ afflvl = get_max_afflvl();
+ return afflvl;
}
/*******************************************************************************
diff --git a/services/std_svc/psci/psci_entry.S b/services/std_svc/psci/psci_entry.S
index 68b917e3..4b2b1067 100644
--- a/services/std_svc/psci/psci_entry.S
+++ b/services/std_svc/psci/psci_entry.S
@@ -134,18 +134,13 @@ psci_aff_common_finish_entry:
* level 0.
* ---------------------------------------------
*/
- mrs x0, mpidr_el1
bl get_power_on_target_afflvl
- cmp x0, xzr
- b.lt _panic
mov x2, x23
mov x1, x0
mov x0, #MPIDR_AFFLVL0
bl psci_afflvl_power_on_finish
b el3_exit
-_panic:
- b _panic
/* --------------------------------------------
* This function is called to indicate to the
diff --git a/services/std_svc/psci/psci_private.h b/services/std_svc/psci/psci_private.h
index 4bf9107f..158a5f75 100644
--- a/services/std_svc/psci/psci_private.h
+++ b/services/std_svc/psci/psci_private.h
@@ -52,7 +52,6 @@ typedef struct aff_map_node {
unsigned short ref_count;
unsigned char state;
unsigned char level;
- unsigned int power_state;
bakery_lock_t lock;
} aff_map_node_t;
@@ -85,7 +84,7 @@ unsigned short psci_get_phys_state(aff_map_node_t *node);
void psci_set_state(aff_map_node_t *node, unsigned short state);
unsigned long mpidr_set_aff_inst(unsigned long, unsigned char, int);
int psci_validate_mpidr(unsigned long, int);
-int get_power_on_target_afflvl(unsigned long mpidr);
+int get_power_on_target_afflvl(void);
void psci_afflvl_power_on_finish(int,
int,
afflvl_power_on_finisher_t *);
@@ -119,15 +118,13 @@ int psci_afflvl_on(unsigned long,
int psci_afflvl_off(int, int);
/* Private exported functions from psci_affinity_suspend.c */
-void psci_set_suspend_power_state(aff_map_node_t *node,
- unsigned int power_state);
-int psci_get_aff_map_node_suspend_afflvl(aff_map_node_t *node);
int psci_afflvl_suspend(unsigned long,
unsigned long,
unsigned int,
int,
int);
unsigned int psci_afflvl_suspend_finish(int, int);
+void psci_set_suspend_power_state(unsigned int power_state);
/* Private exported functions from psci_helpers.S */
void psci_do_pwrdown_cache_maintenance(uint32_t affinity_level);
diff --git a/services/std_svc/psci/psci_setup.c b/services/std_svc/psci/psci_setup.c
index 68f19a00..9e4955dc 100644
--- a/services/std_svc/psci/psci_setup.c
+++ b/services/std_svc/psci/psci_setup.c
@@ -189,9 +189,6 @@ static void psci_init_aff_map_node(unsigned long mpidr,
if (state & PSCI_AFF_PRESENT)
psci_set_state(&psci_aff_map[idx], PSCI_STATE_OFF);
- /* Invalidate the suspend context for the node */
- psci_aff_map[idx].power_state = PSCI_INVALID_DATA;
-
/*
* Associate a non-secure context with this affinity
* instance through the context management library.
@@ -199,6 +196,11 @@ static void psci_init_aff_map_node(unsigned long mpidr,
linear_id = platform_get_core_pos(mpidr);
assert(linear_id < PLATFORM_CORE_COUNT);
+ /* Invalidate the suspend context for the node */
+ set_cpu_data_by_index(linear_id,
+ psci_svc_cpu_data.power_state,
+ PSCI_INVALID_DATA);
+
cm_set_context_by_mpidr(mpidr,
(void *) &psci_ns_context[linear_id],
NON_SECURE);