PSCI: Add framework to handle composite power states

The state-id field in the power-state parameter of a CPU_SUSPEND call can be
used to describe composite power states specific to a platform. The current PSCI
implementation does not interpret the state-id field. It relies on the target
power level and the state type fields in the power-state parameter to perform
state coordination and power management operations. The framework introduced
in this patch allows the PSCI implementation to intepret generic global states
like RUN, RETENTION or OFF from the State-ID to make global state coordination
decisions and reduce the complexity of platform ports. It adds support to
involve the platform in state coordination which facilitates the use of
composite power states and improves the support for entering standby states
at multiple power domains.

The patch also includes support for extended state-id format for the power
state parameter as specified by PSCIv1.0.

The PSCI implementation now defines a generic representation of the power-state
parameter. It depends on the platform port to convert the power-state parameter
(possibly encoding a composite power state) passed in a CPU_SUSPEND call to this
representation via the `validate_power_state()` plat_psci_ops handler. It is an
array where each index corresponds to a power level. Each entry contains the
local power state the power domain at that power level could enter.

The meaning of the local power state values is platform defined, and may vary
between levels in a single platform. The PSCI implementation constrains the
values only so that it can classify the state as RUN, RETENTION or OFF as
required by the specification:
   * zero means RUN
   * all OFF state values at all levels must be higher than all RETENTION
     state values at all levels
   * the platform provides PLAT_MAX_RET_STATE and PLAT_MAX_OFF_STATE values
     to the framework

The platform also must define the macros PLAT_MAX_RET_STATE and
PLAT_MAX_OFF_STATE which lets the PSCI implementation find out which power
domains have been requested to enter a retention or power down state. The PSCI
implementation does not interpret the local power states defined by the
platform. The only constraint is that the PLAT_MAX_RET_STATE <
PLAT_MAX_OFF_STATE.

For a power domain tree, the generic implementation maintains an array of local
power states. These are the states requested for each power domain by all the
cores contained within the domain. During a request to place multiple power
domains in a low power state, the platform is passed an array of requested
power-states for each power domain through the plat_get_target_pwr_state()
API. It coordinates amongst these states to determine a target local power
state for the power domain. A default weak implementation of this API is
provided in the platform layer which returns the minimum of the requested
power-states back to the PSCI state coordination.

Finally, the plat_psci_ops power management handlers are passed the target
local power states for each affected power domain using the generic
representation described above. The platform executes operations specific to
these target states.

The platform power management handler for placing a power domain in a standby
state (plat_pm_ops_t.pwr_domain_standby()) is now only used as a fast path for
placing a core power domain into a standby or retention state should now be
used to only place the core power domain in a standby or retention state.

The extended state-id power state format can be enabled by setting the
build flag PSCI_EXTENDED_STATE_ID=1 and it is disabled by default.

Change-Id: I9d4123d97e179529802c1f589baaa4101759d80c
diff --git a/include/bl31/cpu_data.h b/include/bl31/cpu_data.h
index 50f509b..db702ba 100644
--- a/include/bl31/cpu_data.h
+++ b/include/bl31/cpu_data.h
@@ -129,6 +129,9 @@
 #define flush_cpu_data(_m)	   flush_dcache_range((uint64_t) 	  \
 						      &(_cpu_data()->_m), \
 						      sizeof(_cpu_data()->_m))
+#define inv_cpu_data(_m)	   inv_dcache_range((uint64_t) 	  \
+						      &(_cpu_data()->_m), \
+						      sizeof(_cpu_data()->_m))
 #define flush_cpu_data_by_index(_ix, _m)	\
 				   flush_dcache_range((uint64_t)	  \
 					 &(_cpu_data_by_index(_ix)->_m),  \
diff --git a/include/bl31/services/psci1.0/psci.h b/include/bl31/services/psci1.0/psci.h
index c31562c..d79ab74 100644
--- a/include/bl31/services/psci1.0/psci.h
+++ b/include/bl31/services/psci1.0/psci.h
@@ -97,27 +97,35 @@
  * PSCI CPU_SUSPEND 'power_state' parameter specific defines
  ******************************************************************************/
 #define PSTATE_ID_SHIFT		0
+
+#if PSCI_EXTENDED_STATE_ID
+#define PSTATE_VALID_MASK	0xB0000000
+#define PSTATE_TYPE_SHIFT	30
+#define PSTATE_ID_MASK		0xfffffff
+#else
+#define PSTATE_VALID_MASK	0xFCFE0000
 #define PSTATE_TYPE_SHIFT	16
 #define PSTATE_PWR_LVL_SHIFT	24
-
 #define PSTATE_ID_MASK		0xffff
-#define PSTATE_TYPE_MASK	0x1
 #define PSTATE_PWR_LVL_MASK	0x3
-#define PSTATE_VALID_MASK     0xFCFE0000
+
+#define psci_get_pstate_pwrlvl(pstate)	(((pstate) >> PSTATE_PWR_LVL_SHIFT) & \
+					PSTATE_PWR_LVL_MASK)
+#define psci_make_powerstate(state_id, type, pwrlvl) \
+			(((state_id) & PSTATE_ID_MASK) << PSTATE_ID_SHIFT) |\
+			(((type) & PSTATE_TYPE_MASK) << PSTATE_TYPE_SHIFT) |\
+			(((pwrlvl) & PSTATE_PWR_LVL_MASK) << PSTATE_PWR_LVL_SHIFT)
+#endif /* __PSCI_EXTENDED_STATE_ID__ */
 
 #define PSTATE_TYPE_STANDBY	0x0
 #define PSTATE_TYPE_POWERDOWN	0x1
+#define PSTATE_TYPE_MASK	0x1
 
 #define psci_get_pstate_id(pstate)	(((pstate) >> PSTATE_ID_SHIFT) & \
 					PSTATE_ID_MASK)
 #define psci_get_pstate_type(pstate)	(((pstate) >> PSTATE_TYPE_SHIFT) & \
 					PSTATE_TYPE_MASK)
-#define psci_get_pstate_pwrlvl(pstate)	((pstate >> PSTATE_PWR_LVL_SHIFT) & \
-					PSTATE_PWR_LVL_MASK)
-#define psci_make_powerstate(state_id, type, pwrlvl) \
-			(((state_id) & PSTATE_ID_MASK) << PSTATE_ID_SHIFT) |\
-			(((type) & PSTATE_TYPE_MASK) << PSTATE_TYPE_SHIFT) |\
-			(((pwrlvl) & PSTATE_PWR_LVL_MASK) << PSTATE_PWR_LVL_SHIFT)
+#define psci_check_power_state(pstate)	((pstate) & PSTATE_VALID_MASK)
 
 /*******************************************************************************
  * PSCI CPU_FEATURES feature flag specific defines
@@ -126,6 +134,11 @@
 #define FF_PSTATE_SHIFT		1
 #define FF_PSTATE_ORIG		0
 #define FF_PSTATE_EXTENDED	1
+#if PSCI_EXTENDED_STATE_ID
+#define FF_PSTATE		FF_PSTATE_EXTENDED
+#else
+#define FF_PSTATE		FF_PSTATE_ORIG
+#endif
 
 /* Features flags for CPU SUSPEND OS Initiated mode support. Bits [0:0] */
 #define FF_MODE_SUPPORT_SHIFT		0
@@ -152,25 +165,70 @@
 
 #define PSCI_INVALID_MPIDR	~(0ULL)
 
-/*******************************************************************************
- * PSCI power domain state related constants.
- ******************************************************************************/
-#define PSCI_STATE_ON		0x0
-#define PSCI_STATE_OFF		0x1
-#define PSCI_STATE_ON_PENDING	0x2
-#define PSCI_STATE_SUSPEND	0x3
+#ifndef __ASSEMBLY__
 
+#include <stdint.h>
+#include <types.h>
+
+/*
+ * These are the states reported by the PSCI_AFFINITY_INFO API for the specified
+ * CPU. The definitions of these states can be found in Section 5.7.1 in the
+ * PSCI specification (ARM DEN 0022C).
+ */
+typedef enum aff_info_state {
+	AFF_STATE_ON = 0,
+	AFF_STATE_OFF = 1,
+	AFF_STATE_ON_PENDING = 2
+} aff_info_state_t;
+
+/*
+ * Macro to represent invalid affinity level within PSCI.
+ */
 #define PSCI_INVALID_DATA -1
 
-#define get_phys_state(x)	(x != PSCI_STATE_ON ? \
-				 PSCI_STATE_OFF : PSCI_STATE_ON)
+/*
+ * Type for representing the local power state at a particular level.
+ */
+typedef uint8_t plat_local_state_t;
 
-#define psci_validate_power_state(pstate) (pstate & PSTATE_VALID_MASK)
+/* The local state macro used to represent RUN state. */
+#define PSCI_LOCAL_STATE_RUN  	0
 
+/*
+ * Macro to test whether the plat_local_state is RUN state
+ */
+#define is_local_state_run(plat_local_state) \
+			((plat_local_state) == PSCI_LOCAL_STATE_RUN)
 
-#ifndef __ASSEMBLY__
+/*
+ * Macro to test whether the plat_local_state is RETENTION state
+ */
+#define is_local_state_retn(plat_local_state) \
+			(((plat_local_state) > PSCI_LOCAL_STATE_RUN) && \
+			((plat_local_state) <= PLAT_MAX_RET_STATE))
 
-#include <stdint.h>
+/*
+ * Macro to test whether the plat_local_state is OFF state
+ */
+#define is_local_state_off(plat_local_state) \
+			(((plat_local_state) > PLAT_MAX_RET_STATE) && \
+			((plat_local_state) <= PLAT_MAX_OFF_STATE))
+
+/*****************************************************************************
+ * This data structure defines the representation of the power state parameter
+ * for its exchange between the generic PSCI code and the platform port. For
+ * example, it is used by the platform port to specify the requested power
+ * states during a power management operation. It is used by the generic code
+ * to inform the platform about the target power states that each level
+ * should enter.
+ ****************************************************************************/
+typedef struct psci_power_state {
+	/*
+	 * The pwr_domain_state[] stores the local power state at each level
+	 * for the CPU.
+	 */
+	plat_local_state_t pwr_domain_state[PLAT_MAX_PWR_LVL + 1];
+} psci_power_state_t;
 
 /*******************************************************************************
  * Structure used to store per-cpu information relevant to the PSCI service.
@@ -178,8 +236,15 @@
  * this information will not reside on a cache line shared with another cpu.
  ******************************************************************************/
 typedef struct psci_cpu_data {
-	uint32_t power_state;	/* The power state from CPU_SUSPEND */
-	unsigned char psci_state;   /* The state of this CPU as seen by PSCI */
+	/* State as seen by PSCI Affinity Info API */
+	aff_info_state_t aff_info_state;
+	/*
+	 * Highest power level which takes part in a power management
+	 * operation.
+	 */
+	int8_t target_pwrlvl;
+	/* The local power state of this CPU */
+	plat_local_state_t local_state;
 #if !USE_COHERENT_MEM
 	bakery_info_t pcpu_bakery_info[PSCI_NUM_NON_CPU_PWR_DOMAINS];
 #endif
@@ -189,22 +254,23 @@
  * Structure populated by platform specific code to export routines which
  * perform common low level pm functions
  ******************************************************************************/
-typedef struct plat_pm_ops {
-	void (*pwr_domain_standby)(unsigned int power_state);
-	int (*pwr_domain_on)(unsigned long mpidr,
-			  unsigned long sec_entrypoint,
-			  unsigned int pwrlvl);
-	void (*pwr_domain_off)(unsigned int pwrlvl);
+typedef struct plat_psci_ops {
+	void (*cpu_standby)(plat_local_state_t cpu_state);
+	int (*pwr_domain_on)(u_register_t mpidr,
+			  unsigned long sec_entrypoint);
+	void (*pwr_domain_off)(const psci_power_state_t *target_state);
 	void (*pwr_domain_suspend)(unsigned long sec_entrypoint,
-			       unsigned int pwrlvl);
-	void (*pwr_domain_on_finish)(unsigned int pwrlvl);
-	void (*pwr_domain_suspend_finish)(unsigned int pwrlvl);
+			       const psci_power_state_t *target_state);
+	void (*pwr_domain_on_finish)(const psci_power_state_t *target_state);
+	void (*pwr_domain_suspend_finish)(const psci_power_state_t *target_state);
 	void (*system_off)(void) __dead2;
 	void (*system_reset)(void) __dead2;
-	int (*validate_power_state)(unsigned int power_state);
+	int (*validate_power_state)(unsigned int power_state,
+				    psci_power_state_t *req_state);
 	int (*validate_ns_entrypoint)(unsigned long ns_entrypoint);
-	unsigned int (*get_sys_suspend_power_state)(void);
-} plat_pm_ops_t;
+	void (*get_sys_suspend_power_state)(
+				    psci_power_state_t *req_state);
+} plat_psci_ops_t;
 
 /*******************************************************************************
  * Optional structure populated by the Secure Payload Dispatcher to be given a
@@ -239,9 +305,6 @@
 void psci_cpu_on_finish_entry(void);
 void psci_cpu_suspend_finish_entry(void);
 void psci_register_spd_pm_hook(const spd_pm_ops_t *);
-int psci_get_suspend_stateid_by_idx(unsigned long);
-int psci_get_suspend_stateid(void);
-int psci_get_suspend_pwrlvl(void);
 
 uint64_t psci_smc_handler(uint32_t smc_fid,
 			  uint64_t x1,
diff --git a/include/plat/common/psci1.0/platform.h b/include/plat/common/psci1.0/platform.h
index acc5b95..a961863 100644
--- a/include/plat/common/psci1.0/platform.h
+++ b/include/plat/common/psci1.0/platform.h
@@ -32,12 +32,11 @@
 #define __PLATFORM_H__
 
 #include <stdint.h>
-
+#include <psci.h>
 
 /*******************************************************************************
  * Forward declarations
  ******************************************************************************/
-struct plat_pm_ops;
 struct meminfo;
 struct image_info;
 struct entry_point_info;
@@ -182,10 +181,17 @@
 /*******************************************************************************
  * Mandatory PSCI functions (BL3-1)
  ******************************************************************************/
-int platform_setup_pm(const struct plat_pm_ops **);
+int plat_setup_psci_ops(const struct plat_psci_ops **);
 const unsigned char *plat_get_power_domain_tree_desc(void);
 
 /*******************************************************************************
+ * Optional PSCI functions (BL3-1).
+ ******************************************************************************/
+plat_local_state_t plat_get_target_pwr_state(unsigned int lvl,
+			const plat_local_state_t *states,
+			unsigned int ncpu);
+
+/*******************************************************************************
  * Optional BL3-1 functions (may be overridden)
  ******************************************************************************/
 void bl31_plat_enable_mmu(uint32_t flags);