PSCI: Introduce new platform interface to describe topology
This patch removes the assumption in the current PSCI implementation that MPIDR
based affinity levels map directly to levels in a power domain tree. This
enables PSCI generic code to support complex power domain topologies as
envisaged by PSCIv1.0 specification. The platform interface for querying
the power domain topology has been changed such that:
1. The generic PSCI code does not generate MPIDRs and use them to query the
platform about the number of power domains at a particular power level. The
platform now provides a description of the power domain tree on the SoC
through a data structure. The existing platform APIs to provide the same
information have been removed.
2. The linear indices returned by plat_core_pos_by_mpidr() and
plat_my_core_pos() are used to retrieve core power domain nodes from the
power domain tree. Power domains above the core level are accessed using a
'parent' field in the tree node descriptors.
The platform describes the power domain tree in an array of 'unsigned
char's. The first entry in the array specifies the number of power domains at
the highest power level implemented in the system. Each susbsequent entry
corresponds to a power domain and contains the number of power domains that are
its direct children. This array is exported to the generic PSCI implementation
via the new `plat_get_power_domain_tree_desc()` platform API.
The PSCI generic code uses this array to populate its internal power domain tree
using the Breadth First Search like algorithm. The tree is split into two
arrays:
1. An array that contains all the core power domain nodes
2. An array that contains all the other power domain nodes
A separate array for core nodes allows certain core specific optimisations to
be implemented e.g. remove the bakery lock, re-use per-cpu data framework for
storing some information.
Entries in the core power domain array are allocated such that the
array index of the domain is equal to the linear index returned by
plat_core_pos_by_mpidr() and plat_my_core_pos() for the MPIDR
corresponding to that domain. This relationship is key to be able to use
an MPIDR to find the corresponding core power domain node, traverse to higher
power domain nodes and index into arrays that contain core specific
information.
An introductory document has been added to briefly describe the new interface.
Change-Id: I4b444719e8e927ba391cae48a23558308447da13
diff --git a/services/std_svc/psci1.0/psci_private.h b/services/std_svc/psci1.0/psci_private.h
index 79909a8..e7ad711 100644
--- a/services/std_svc/psci1.0/psci_private.h
+++ b/services/std_svc/psci1.0/psci_private.h
@@ -34,25 +34,30 @@
#include <arch.h>
#include <bakery_lock.h>
#include <bl_common.h>
+#include <cpu_data.h>
#include <psci.h>
+#include <spinlock.h>
/*
* The following helper macros abstract the interface to the Bakery
* Lock API.
*/
#if USE_COHERENT_MEM
-#define psci_lock_init(pwr_map, idx) bakery_lock_init(&(pwr_map)[(idx)].lock)
-#define psci_lock_get(node) bakery_lock_get(&((node)->lock))
-#define psci_lock_release(node) bakery_lock_release(&((node)->lock))
+#define psci_lock_init(non_cpu_pd_node, idx) \
+ bakery_lock_init(&(non_cpu_pd_node)[(idx)].lock)
+#define psci_lock_get(non_cpu_pd_node) \
+ bakery_lock_get(&((non_cpu_pd_node)->lock))
+#define psci_lock_release(non_cpu_pd_node) \
+ bakery_lock_release(&((non_cpu_pd_node)->lock))
#else
-#define psci_lock_init(pwr_map, idx) \
- ((pwr_map)[(idx)].pwr_domain_index = (idx))
-#define psci_lock_get(node) \
- bakery_lock_get((node)->pwr_domain_index,\
- CPU_DATA_PSCI_LOCK_OFFSET)
-#define psci_lock_release(node) \
- bakery_lock_release((node)->pwr_domain_index,\
- CPU_DATA_PSCI_LOCK_OFFSET)
+#define psci_lock_init(non_cpu_pd_node, idx) \
+ ((non_cpu_pd_node)[(idx)].lock_index = (idx))
+#define psci_lock_get(non_cpu_pd_node) \
+ bakery_lock_get((non_cpu_pd_node)->lock_index, \
+ CPU_DATA_PSCI_LOCK_OFFSET)
+#define psci_lock_release(non_cpu_pd_node) \
+ bakery_lock_release((non_cpu_pd_node)->lock_index, \
+ CPU_DATA_PSCI_LOCK_OFFSET)
#endif
/*
@@ -75,39 +80,76 @@
define_psci_cap(PSCI_MIG_INFO_UP_CPU_AARCH64) | \
define_psci_cap(PSCI_SYSTEM_SUSPEND_AARCH64))
+/*
+ * Helper macros for the CPU level spinlocks
+ */
+#define psci_spin_lock_cpu(idx) spin_lock(&psci_cpu_pd_nodes[idx].cpu_lock)
+#define psci_spin_unlock_cpu(idx) spin_unlock(&psci_cpu_pd_nodes[idx].cpu_lock)
/*******************************************************************************
- * The following two data structures hold the topology tree which in turn tracks
- * the state of the all the power domain instances supported by the platform.
+ * The following two data structures implement the power domain tree. The tree
+ * is used to track the state of all the nodes i.e. power domain instances
+ * described by the platform. The tree consists of nodes that describe CPU power
+ * domains i.e. leaf nodes and all other power domains which are parents of a
+ * CPU power domain i.e. non-leaf nodes.
******************************************************************************/
-typedef struct pwr_map_node {
- unsigned long mpidr;
+typedef struct non_cpu_pwr_domain_node {
+ /*
+ * Index of the first CPU power domain node level 0 which has this node
+ * as its parent.
+ */
+ unsigned int cpu_start_idx;
+
+ /*
+ * Number of CPU power domains which are siblings of the domain indexed
+ * by 'cpu_start_idx' i.e. all the domains in the range 'cpu_start_idx
+ * -> cpu_start_idx + ncpus' have this node as their parent.
+ */
+ unsigned int ncpus;
+
+ /*
+ * Index of the parent power domain node.
+ * TODO: Figure out whether to whether using pointer is more efficient.
+ */
+ unsigned int parent_node;
+
unsigned char ref_count;
- unsigned char state;
unsigned char level;
#if USE_COHERENT_MEM
bakery_lock_t lock;
#else
/* For indexing the bakery_info array in per CPU data */
- unsigned char pwr_domain_index;
+ unsigned char lock_index;
#endif
-} pwr_map_node_t;
+} non_cpu_pd_node_t;
-typedef struct pwr_lvl_limits_node {
- int min;
- int max;
-} pwr_lvl_limits_node_t;
+typedef struct cpu_pwr_domain_node {
+ unsigned long mpidr;
-typedef pwr_map_node_t (*mpidr_pwr_map_nodes_t[MPIDR_MAX_AFFLVL + 1]);
-typedef void (*pwrlvl_power_on_finisher_t)(pwr_map_node_t *mpidr_nodes[],
- int pwrlvl);
+ /*
+ * Index of the parent power domain node.
+ * TODO: Figure out whether to whether using pointer is more efficient.
+ */
+ unsigned int parent_node;
+
+ /*
+ * A CPU power domain does not require state coordination like its
+ * parent power domains. Hence this node does not include a bakery
+ * lock. A spinlock is required by the CPU_ON handler to prevent a race
+ * when multiple CPUs try to turn ON the same target CPU.
+ */
+ spinlock_t cpu_lock;
+} cpu_pd_node_t;
+
+typedef void (*pwrlvl_power_on_finisher_t)(unsigned int cpu_idx,
+ int max_off_pwrlvl);
/*******************************************************************************
* Data prototypes
******************************************************************************/
extern const plat_pm_ops_t *psci_plat_pm_ops;
-extern pwr_map_node_t psci_pwr_domain_map[PSCI_NUM_PWR_DOMAINS];
-extern pwr_lvl_limits_node_t psci_pwr_domain_map[MPIDR_MAX_AFFLVL + 1];
+extern non_cpu_pd_node_t psci_non_cpu_pd_nodes[PSCI_NUM_NON_CPU_PWR_DOMAINS];
+extern cpu_pd_node_t psci_cpu_pd_nodes[PLATFORM_CORE_COUNT];
extern uint32_t psci_caps;
/*******************************************************************************
@@ -119,56 +161,47 @@
* Function prototypes
******************************************************************************/
/* Private exported functions from psci_common.c */
-unsigned short psci_get_state(pwr_map_node_t *node);
-unsigned short psci_get_phys_state(pwr_map_node_t *node);
-void psci_set_state(pwr_map_node_t *node, unsigned short state);
-unsigned long mpidr_set_pwr_domain_inst(unsigned long, unsigned char, int);
+unsigned short psci_get_state(unsigned int idx, int level);
+unsigned short psci_get_phys_state(unsigned int idx, int level);
+void psci_set_state(unsigned int idx, unsigned short state, int level);
int psci_validate_mpidr(unsigned long mpidr);
int get_power_on_target_pwrlvl(void);
void psci_power_up_finish(int end_pwrlvl,
pwrlvl_power_on_finisher_t pon_handler);
int psci_get_ns_ep_info(entry_point_info_t *ep,
uint64_t entrypoint, uint64_t context_id);
-int psci_check_pwrlvl_range(int start_pwrlvl, int end_pwrlvl);
-void psci_do_state_coordination(uint32_t start_pwrlvl,
- uint32_t end_pwrlvl,
- pwr_map_node_t *mpidr_nodes[],
- uint32_t state);
-void psci_acquire_pwr_domain_locks(int start_pwrlvl,
- int end_pwrlvl,
- pwr_map_node_t *mpidr_nodes[]);
-void psci_release_pwr_domain_locks(int start_pwrlvl,
- int end_pwrlvl,
- mpidr_pwr_map_nodes_t mpidr_nodes);
+void psci_get_parent_pwr_domain_nodes(unsigned int cpu_idx,
+ int end_lvl,
+ unsigned int node_index[]);
+void psci_do_state_coordination(int end_pwrlvl,
+ unsigned int cpu_idx,
+ uint32_t state);
+void psci_acquire_pwr_domain_locks(int end_pwrlvl,
+ unsigned int cpu_idx);
+void psci_release_pwr_domain_locks(int end_pwrlvl,
+ unsigned int cpu_idx);
void psci_print_power_domain_map(void);
-uint32_t psci_find_max_phys_off_pwrlvl(uint32_t start_pwrlvl,
- uint32_t end_pwrlvl,
- pwr_map_node_t *mpidr_nodes[]);
+uint32_t psci_find_max_phys_off_pwrlvl(uint32_t end_pwrlvl,
+ unsigned int cpu_idx);
unsigned int psci_is_last_on_cpu(void);
int psci_spd_migrate_info(uint64_t *mpidr);
-/* Private exported functions from psci_setup.c */
-int psci_get_pwr_map_nodes(unsigned long mpidr,
- int start_pwrlvl,
- int end_pwrlvl,
- pwr_map_node_t *mpidr_nodes[]);
-pwr_map_node_t *psci_get_pwr_map_node(unsigned long, int);
-
-/* Private exported functions from psci_cpu_on.c */
+/* Private exported functions from psci_on.c */
int psci_cpu_on_start(unsigned long target_cpu,
- entry_point_info_t *ep,
- int end_pwrlvl);
+ entry_point_info_t *ep,
+ int end_pwrlvl);
-void psci_cpu_on_finish(pwr_map_node_t *node[], int pwrlvl);
+void psci_cpu_on_finish(unsigned int cpu_idx,
+ int max_off_pwrlvl);
/* Private exported functions from psci_cpu_off.c */
int psci_do_cpu_off(int end_pwrlvl);
-/* Private exported functions from psci_cpu_suspend.c */
+/* Private exported functions from psci_suspend.c */
void psci_cpu_suspend_start(entry_point_info_t *ep,
int end_pwrlvl);
-
-void psci_cpu_suspend_finish(pwr_map_node_t *node[], int pwrlvl);
+void psci_cpu_suspend_finish(unsigned int cpu_idx,
+ int max_off_pwrlvl);
void psci_set_suspend_power_state(unsigned int power_state);