aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSoby Mathew <soby.mathew@arm.com>2015-06-10 13:49:59 +0100
committerAchin Gupta <achin.gupta@arm.com>2015-08-13 20:08:19 +0100
commit32bc85f2d5234fbc4d1f724db3d033b8e1be5dd3 (patch)
tree3f225586e975d04f768dc3126e0544abe929f39d
parenteb975f52ea2e70216d214efcff3154f3cf081cb0 (diff)
downloadtrusted-firmware-a-32bc85f2d5234fbc4d1f724db3d033b8e1be5dd3.tar.gz
PSCI: Implement platform compatibility layer
The new PSCI topology framework and PSCI extended State framework introduces a breaking change in the platform port APIs. To ease the migration of the platform ports to the new porting interface, a compatibility layer is introduced which essentially defines the new platform API in terms of the old API. The old PSCI helpers to retrieve the power-state, its associated fields and the highest coordinated physical OFF affinity level of a core are also implemented for compatibility. This allows the existing platform ports to work with the new PSCI framework without significant rework. This layer will be enabled by default once the switch to the new PSCI framework is done and is controlled by the build flag ENABLE_PLAT_COMPAT. Change-Id: I4b17cac3a4f3375910a36dba6b03d8f1700d07e3
-rw-r--r--Makefile18
-rw-r--r--include/bl31/services/psci1.0/psci.h5
-rw-r--r--include/bl31/services/psci1.0/psci_compat.h112
-rw-r--r--include/plat/common/psci1.0/platform.h26
-rw-r--r--plat/common/aarch64/platform_mp_stack.S48
-rw-r--r--plat/compat/aarch64/plat_helpers_compat.S71
-rw-r--r--plat/compat/plat_compat.mk41
-rw-r--r--plat/compat/plat_pm_compat.c337
-rw-r--r--plat/compat/plat_topology_compat.c218
-rw-r--r--services/std_svc/psci1.0/psci_common.c78
10 files changed, 948 insertions, 6 deletions
diff --git a/Makefile b/Makefile
index b1c42eb0fa..c1e96177a5 100644
--- a/Makefile
+++ b/Makefile
@@ -169,6 +169,20 @@ msg_start:
include ${PLAT_MAKEFILE_FULL}
+# Disable the Platform Compatibility layer till the new PSCI framework is
+# introduced.
+ENABLE_PLAT_COMPAT := 0
+
+# If the platform has not defined ENABLE_PLAT_COMPAT, then enable it by default
+ifndef ENABLE_PLAT_COMPAT
+ENABLE_PLAT_COMPAT := 1
+endif
+
+# Include the platform compatibility helpers for PSCI
+ifneq (${ENABLE_PLAT_COMPAT}, 0)
+include plat/compat/plat_compat.mk
+endif
+
# Include the CPU specific operations makefile. By default all CPU errata
# workarounds and CPU specifc optimisations are disabled. This can be
# overridden by the platform.
@@ -288,6 +302,10 @@ $(eval $(call add_define,TRUSTED_BOARD_BOOT))
$(eval $(call assert_boolean,PROGRAMMABLE_RESET_ADDRESS))
$(eval $(call add_define,PROGRAMMABLE_RESET_ADDRESS))
+# Process ENABLE_PLAT_COMPAT flag
+$(eval $(call assert_boolean,ENABLE_PLAT_COMPAT))
+$(eval $(call add_define,ENABLE_PLAT_COMPAT))
+
ASFLAGS += -nostdinc -ffreestanding -Wa,--fatal-warnings \
-Werror -Wmissing-include-dirs \
-mgeneral-regs-only -D__ASSEMBLY__ \
diff --git a/include/bl31/services/psci1.0/psci.h b/include/bl31/services/psci1.0/psci.h
index fe23711e13..9361187618 100644
--- a/include/bl31/services/psci1.0/psci.h
+++ b/include/bl31/services/psci1.0/psci.h
@@ -33,6 +33,9 @@
#include <bakery_lock.h>
#include <platform_def.h> /* for PLAT_NUM_PWR_DOMAINS */
+#if ENABLE_PLAT_COMPAT
+#include <psci_compat.h>
+#endif
/*******************************************************************************
* Number of power domains whose state this psci imp. can track
@@ -316,8 +319,6 @@ uint64_t psci_smc_handler(uint32_t smc_fid,
/* PSCI setup function */
int32_t psci_setup(void);
-
#endif /*__ASSEMBLY__*/
-
#endif /* __PSCI_H__ */
diff --git a/include/bl31/services/psci1.0/psci_compat.h b/include/bl31/services/psci1.0/psci_compat.h
new file mode 100644
index 0000000000..cc80ae3030
--- /dev/null
+++ b/include/bl31/services/psci1.0/psci_compat.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __PSCI_COMPAT_H__
+#define __PSCI_COMPAT_H__
+
+#include <arch.h>
+#include <platform_def.h>
+
+#ifndef __ASSEMBLY__
+/*
+ * The below declarations are to enable compatibility for the platform ports
+ * using the old platform interface and psci helpers.
+ */
+#define PLAT_MAX_PWR_LVL PLATFORM_MAX_AFFLVL
+#define PLAT_NUM_PWR_DOMAINS PLATFORM_NUM_AFFS
+
+/*******************************************************************************
+ * PSCI affinity related constants. An affinity instance could
+ * be present or absent physically to cater for asymmetric topologies.
+ ******************************************************************************/
+#define PSCI_AFF_ABSENT 0x0
+#define PSCI_AFF_PRESENT 0x1
+
+#define PSCI_STATE_ON 0x0
+#define PSCI_STATE_OFF 0x1
+#define PSCI_STATE_ON_PENDING 0x2
+#define PSCI_STATE_SUSPEND 0x3
+
+/*
+ * Using the compatibility platform interfaces means that the local states
+ * used in psci_power_state_t need to only convey whether its power down
+ * or standby state. The onus is on the platform port to do the right thing
+ * including the state coordination in case multiple power down states are
+ * involved. Hence if we assume 3 generic states viz, run, standby and
+ * power down, we can assign 1 and 2 to standby and power down respectively.
+ */
+#define PLAT_MAX_RET_STATE 1
+#define PLAT_MAX_OFF_STATE 2
+
+
+#define psci_get_pstate_afflvl(pstate) psci_get_pstate_pwrlvl(pstate)
+
+/*
+ * This array stores the 'power_state' requests of each CPU during
+ * CPU_SUSPEND and SYSTEM_SUSPEND which will be populated by the
+ * compatibility layer when appropriate platform hooks are invoked.
+ */
+extern unsigned int psci_power_state_compat[PLATFORM_CORE_COUNT];
+
+/*******************************************************************************
+ * Structure populated by platform specific code to export routines which
+ * perform common low level pm functions
+ ******************************************************************************/
+typedef struct plat_pm_ops {
+ void (*affinst_standby)(unsigned int power_state);
+ int (*affinst_on)(unsigned long mpidr,
+ unsigned long sec_entrypoint,
+ unsigned int afflvl,
+ unsigned int state);
+ void (*affinst_off)(unsigned int afflvl, unsigned int state);
+ void (*affinst_suspend)(unsigned long sec_entrypoint,
+ unsigned int afflvl,
+ unsigned int state);
+ void (*affinst_on_finish)(unsigned int afflvl, unsigned int state);
+ void (*affinst_suspend_finish)(unsigned int afflvl,
+ unsigned int state);
+ void (*system_off)(void) __dead2;
+ void (*system_reset)(void) __dead2;
+ int (*validate_power_state)(unsigned int power_state);
+ int (*validate_ns_entrypoint)(unsigned long ns_entrypoint);
+ unsigned int (*get_sys_suspend_power_state)(void);
+} plat_pm_ops_t;
+
+/*******************************************************************************
+ * Function & Data prototypes to enable compatibility for older platform ports
+ ******************************************************************************/
+int psci_get_suspend_stateid_by_mpidr(unsigned long);
+int psci_get_suspend_stateid(void);
+int psci_get_suspend_powerstate(void);
+unsigned int psci_get_max_phys_off_afflvl(void);
+int psci_get_suspend_afflvl(void);
+
+#endif /* ____ASSEMBLY__ */
+#endif /* __PSCI_COMPAT_H__ */
diff --git a/include/plat/common/psci1.0/platform.h b/include/plat/common/psci1.0/platform.h
index d29fcfc255..f054cd0a20 100644
--- a/include/plat/common/psci1.0/platform.h
+++ b/include/plat/common/psci1.0/platform.h
@@ -31,8 +31,10 @@
#ifndef __PLATFORM_H__
#define __PLATFORM_H__
-#include <stdint.h>
#include <psci.h>
+#include <stdint.h>
+#include <types.h>
+
/*******************************************************************************
* Forward declarations
@@ -59,7 +61,7 @@ int plat_get_image_source(unsigned int image_id,
uintptr_t *image_spec);
unsigned long plat_get_ns_image_entrypoint(void);
unsigned int plat_my_core_pos(void);
-int plat_core_pos_by_mpidr(unsigned long mpidr);
+int plat_core_pos_by_mpidr(u_register_t mpidr);
/*******************************************************************************
* Mandatory interrupt management functions
@@ -208,4 +210,24 @@ void bl32_plat_enable_mmu(uint32_t flags);
int plat_get_rotpk_info(void *cookie, void **key_ptr, unsigned int *key_len,
unsigned int *flags);
+#if ENABLE_PLAT_COMPAT
+/*
+ * The below declarations are to enable compatibility for the platform ports
+ * using the old platform interface.
+ */
+
+/*******************************************************************************
+ * Optional common functions (may be overridden)
+ ******************************************************************************/
+unsigned int platform_get_core_pos(unsigned long mpidr);
+
+/*******************************************************************************
+ * Mandatory PSCI Compatibility functions (BL3-1)
+ ******************************************************************************/
+int platform_setup_pm(const plat_pm_ops_t **);
+
+unsigned int plat_get_aff_count(unsigned int, unsigned long);
+unsigned int plat_get_aff_state(unsigned int, unsigned long);
+#endif /* __ENABLE_PLAT_COMPAT__ */
+
#endif /* __PLATFORM_H__ */
diff --git a/plat/common/aarch64/platform_mp_stack.S b/plat/common/aarch64/platform_mp_stack.S
index 0cea9ac357..b1f7b6dcd4 100644
--- a/plat/common/aarch64/platform_mp_stack.S
+++ b/plat/common/aarch64/platform_mp_stack.S
@@ -30,16 +30,58 @@
#include <arch.h>
#include <asm_macros.S>
+#include <assert_macros.S>
#include <platform_def.h>
-
.local platform_normal_stacks
- .weak platform_set_stack
+#if ENABLE_PLAT_COMPAT
+ .globl plat_get_my_stack
+ .globl plat_set_my_stack
+#else
.weak platform_get_stack
+ .weak platform_set_stack
.weak plat_get_my_stack
.weak plat_set_my_stack
+#endif /*__ENABLE_PLAT_COMPAT__*/
+
+
+#if ENABLE_PLAT_COMPAT
+ /* ---------------------------------------------------------------------
+ * When the compatility layer is enabled, the new platform APIs
+ * viz plat_get_my_stack() and plat_set_my_stack() need to be
+ * defined using the previous APIs platform_get_stack() and
+ * platform_set_stack(). Also we need to provide weak definitions
+ * of platform_get_stack() and platform_set_stack() for the platforms
+ * to reuse.
+ * --------------------------------------------------------------------
+ */
/* -----------------------------------------------------
+ * unsigned long plat_get_my_stack ()
+ *
+ * For the current CPU, this function returns the stack
+ * pointer for a stack allocated in device memory.
+ * -----------------------------------------------------
+ */
+func plat_get_my_stack
+ mrs x0, mpidr_el1
+ b platform_get_stack
+endfunc plat_get_my_stack
+
+ /* -----------------------------------------------------
+ * void plat_set_my_stack ()
+ *
+ * For the current CPU, this function sets the stack
+ * pointer to a stack allocated in normal memory.
+ * -----------------------------------------------------
+ */
+func plat_set_my_stack
+ mrs x0, mpidr_el1
+ b platform_set_stack
+endfunc plat_set_my_stack
+
+#else
+ /* -----------------------------------------------------
* unsigned long platform_get_stack (unsigned long mpidr)
*
* For a given CPU, this function returns the stack
@@ -93,6 +135,8 @@ func plat_set_my_stack
ret x9
endfunc plat_set_my_stack
+#endif /*__ENABLE_PLAT_COMPAT__*/
+
/* -----------------------------------------------------
* Per-cpu stacks in normal memory. Each cpu gets a
* stack of PLATFORM_STACK_SIZE bytes.
diff --git a/plat/compat/aarch64/plat_helpers_compat.S b/plat/compat/aarch64/plat_helpers_compat.S
new file mode 100644
index 0000000000..6d83d236a3
--- /dev/null
+++ b/plat/compat/aarch64/plat_helpers_compat.S
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <arch.h>
+#include <asm_macros.S>
+#include <assert_macros.S>
+#include <platform_def.h>
+
+ .globl plat_my_core_pos
+ .globl plat_is_my_cpu_primary
+ .globl plat_get_my_entrypoint
+ .weak platform_get_core_pos
+
+ /* -----------------------------------------------------
+ * Compatibility wrappers for new platform APIs.
+ * -----------------------------------------------------
+ */
+func plat_my_core_pos
+ mrs x0, mpidr_el1
+ b platform_get_core_pos
+endfunc plat_my_core_pos
+
+func plat_is_my_cpu_primary
+ mrs x0, mpidr_el1
+ b platform_is_primary_cpu
+endfunc plat_is_my_cpu_primary
+
+func plat_get_my_entrypoint
+ mrs x0, mpidr_el1
+ b platform_get_entrypoint
+endfunc plat_get_my_entrypoint
+
+ /* -----------------------------------------------------
+ * int platform_get_core_pos(int mpidr);
+ * With this function: CorePos = (ClusterId * 4) +
+ * CoreId
+ * -----------------------------------------------------
+ */
+func platform_get_core_pos
+ and x1, x0, #MPIDR_CPU_MASK
+ and x0, x0, #MPIDR_CLUSTER_MASK
+ add x0, x1, x0, LSR #6
+ ret
+endfunc platform_get_core_pos
diff --git a/plat/compat/plat_compat.mk b/plat/compat/plat_compat.mk
new file mode 100644
index 0000000000..c0c8ecef1d
--- /dev/null
+++ b/plat/compat/plat_compat.mk
@@ -0,0 +1,41 @@
+#
+# Copyright (c) 2015, ARM Limited and Contributors. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# Neither the name of ARM nor the names of its contributors may be used
+# to endorse or promote products derived from this software without specific
+# prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+ifeq (${PSCI_EXTENDED_STATE_ID}, 1)
+ $(error "PSCI Compatibility mode can be enabled only if \
+ PSCI_EXTENDED_STATE_ID is not set")
+endif
+
+
+PLAT_BL_COMMON_SOURCES += plat/compat/aarch64/plat_helpers_compat.S
+
+BL31_SOURCES += plat/common/aarch64/plat_psci_common.c \
+ plat/compat/plat_pm_compat.c \
+ plat/compat/plat_topology_compat.c
diff --git a/plat/compat/plat_pm_compat.c b/plat/compat/plat_pm_compat.c
new file mode 100644
index 0000000000..f51bb5586e
--- /dev/null
+++ b/plat/compat/plat_pm_compat.c
@@ -0,0 +1,337 @@
+/*
+ * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <errno.h>
+#include <platform.h>
+#include <psci.h>
+
+/*
+ * The platform hooks exported by the platform using the earlier version of
+ * platform interface
+ */
+const plat_pm_ops_t *pm_ops;
+
+/*
+ * The hooks exported by the compatibility layer
+ */
+static plat_psci_ops_t compat_psci_ops;
+
+/*
+ * The secure entry point to be used on warm reset.
+ */
+static unsigned long secure_entrypoint;
+
+/*
+ * This array stores the 'power_state' requests of each CPU during
+ * CPU_SUSPEND and SYSTEM_SUSPEND to support querying of state-ID
+ * by the platform.
+ */
+unsigned int psci_power_state_compat[PLATFORM_CORE_COUNT];
+
+/*******************************************************************************
+ * The PSCI compatibility helper to parse the power state and populate the
+ * 'pwr_domain_state' for each power level. It is assumed that, when in
+ * compatibility mode, the PSCI generic layer need to know only whether the
+ * affinity level will be OFF or in RETENTION and if the platform supports
+ * multiple power down and retention states, it will be taken care within
+ * the platform layer.
+ ******************************************************************************/
+static int parse_power_state(unsigned int power_state,
+ psci_power_state_t *req_state)
+{
+ int i;
+ int pstate = psci_get_pstate_type(power_state);
+ int aff_lvl = psci_get_pstate_pwrlvl(power_state);
+
+ if (aff_lvl > PLATFORM_MAX_AFFLVL)
+ return PSCI_E_INVALID_PARAMS;
+
+ /* Sanity check the requested state */
+ if (pstate == PSTATE_TYPE_STANDBY) {
+ /*
+ * Set the CPU local state as retention and ignore the higher
+ * levels. This allows the generic PSCI layer to invoke
+ * plat_psci_ops 'cpu_standby' hook and the compatibility
+ * layer invokes the 'affinst_standby' handler with the
+ * correct power_state parameter thus preserving the correct
+ * behavior.
+ */
+ req_state->pwr_domain_state[0] =
+ PLAT_MAX_RET_STATE;
+ } else {
+ for (i = 0; i <= aff_lvl; i++)
+ req_state->pwr_domain_state[i] =
+ PLAT_MAX_OFF_STATE;
+ }
+
+ return PSCI_E_SUCCESS;
+}
+
+/*******************************************************************************
+ * The PSCI compatibility helper to set the 'power_state' in
+ * psci_power_state_compat[] at index corresponding to the current core.
+ ******************************************************************************/
+static void set_psci_power_state_compat(unsigned int power_state)
+{
+ unsigned int my_core_pos = plat_my_core_pos();
+
+ psci_power_state_compat[my_core_pos] = power_state;
+ flush_dcache_range((uintptr_t) &psci_power_state_compat[my_core_pos],
+ sizeof(psci_power_state_compat[my_core_pos]));
+}
+
+/*******************************************************************************
+ * The PSCI compatibility helper for plat_pm_ops_t 'validate_power_state'
+ * hook.
+ ******************************************************************************/
+static int validate_power_state_compat(unsigned int power_state,
+ psci_power_state_t *req_state)
+{
+ int rc;
+ assert(req_state);
+
+ if (pm_ops->validate_power_state) {
+ rc = pm_ops->validate_power_state(power_state);
+ if (rc != PSCI_E_SUCCESS)
+ return rc;
+ }
+
+ /* Store the 'power_state' parameter for the current CPU. */
+ set_psci_power_state_compat(power_state);
+
+ return parse_power_state(power_state, req_state);
+}
+
+/*******************************************************************************
+ * The PSCI compatibility helper for plat_pm_ops_t
+ * 'get_sys_suspend_power_state' hook.
+ ******************************************************************************/
+void get_sys_suspend_power_state_compat(psci_power_state_t *req_state)
+{
+ unsigned int power_state;
+ assert(req_state);
+
+ power_state = pm_ops->get_sys_suspend_power_state();
+
+ /* Store the 'power_state' parameter for the current CPU. */
+ set_psci_power_state_compat(power_state);
+
+ if (parse_power_state(power_state, req_state) != PSCI_E_SUCCESS)
+ assert(0);
+}
+
+/*******************************************************************************
+ * The PSCI compatibility helper for plat_pm_ops_t 'validate_ns_entrypoint'
+ * hook.
+ ******************************************************************************/
+static int validate_ns_entrypoint_compat(uintptr_t ns_entrypoint)
+{
+ return pm_ops->validate_ns_entrypoint(ns_entrypoint);
+}
+
+/*******************************************************************************
+ * The PSCI compatibility helper for plat_pm_ops_t 'affinst_standby' hook.
+ ******************************************************************************/
+static void cpu_standby_compat(plat_local_state_t cpu_state)
+{
+ unsigned int powerstate = psci_get_suspend_powerstate();
+
+ assert(powerstate != PSCI_INVALID_DATA);
+
+ pm_ops->affinst_standby(powerstate);
+}
+
+/*******************************************************************************
+ * The PSCI compatibility helper for plat_pm_ops_t 'affinst_on' hook.
+ ******************************************************************************/
+static int pwr_domain_on_compat(u_register_t mpidr)
+{
+ int level, rc;
+
+ /*
+ * The new PSCI framework does not hold the locks for higher level
+ * power domain nodes when this hook is invoked. Hence figuring out the
+ * target state of the parent power domains does not make much sense.
+ * Hence we hard-code the state as PSCI_STATE_OFF for all the levels.
+ * We expect the platform to perform the necessary CPU_ON operations
+ * when the 'affinst_on' is invoked only for level 0.
+ */
+ for (level = PLATFORM_MAX_AFFLVL; level >= 0; level--) {
+ rc = pm_ops->affinst_on((unsigned long)mpidr, secure_entrypoint,
+ level, PSCI_STATE_OFF);
+ if (rc != PSCI_E_SUCCESS)
+ break;
+ }
+
+ return rc;
+}
+
+/*******************************************************************************
+ * The PSCI compatibility helper for plat_pm_ops_t 'affinst_off' hook.
+ ******************************************************************************/
+static void pwr_domain_off_compat(const psci_power_state_t *target_state)
+{
+ int level;
+ unsigned int plat_state;
+
+ for (level = 0; level <= PLATFORM_MAX_AFFLVL; level++) {
+ plat_state = (is_local_state_run(
+ target_state->pwr_domain_state[level]) ?
+ PSCI_STATE_ON : PSCI_STATE_OFF);
+ pm_ops->affinst_off(level, plat_state);
+ }
+}
+
+/*******************************************************************************
+ * The PSCI compatibility helper for plat_pm_ops_t 'affinst_suspend' hook.
+ ******************************************************************************/
+static void pwr_domain_suspend_compat(const psci_power_state_t *target_state)
+{
+ int level;
+ unsigned int plat_state;
+
+ for (level = 0; level <= psci_get_suspend_afflvl(); level++) {
+ plat_state = (is_local_state_run(
+ target_state->pwr_domain_state[level]) ?
+ PSCI_STATE_ON : PSCI_STATE_OFF);
+ pm_ops->affinst_suspend(secure_entrypoint, level, plat_state);
+ }
+}
+
+/*******************************************************************************
+ * The PSCI compatibility helper for plat_pm_ops_t 'affinst_on_finish'
+ * hook.
+ ******************************************************************************/
+static void pwr_domain_on_finish_compat(const psci_power_state_t *target_state)
+{
+ int level;
+ unsigned int plat_state;
+
+ for (level = PLATFORM_MAX_AFFLVL; level >= 0; level--) {
+ plat_state = (is_local_state_run(
+ target_state->pwr_domain_state[level]) ?
+ PSCI_STATE_ON : PSCI_STATE_OFF);
+ pm_ops->affinst_on_finish(level, plat_state);
+ }
+}
+
+/*******************************************************************************
+ * The PSCI compatibility helper for plat_pm_ops_t
+ * 'affinst_suspend_finish' hook.
+ ******************************************************************************/
+static void pwr_domain_suspend_finish_compat(
+ const psci_power_state_t *target_state)
+{
+ int level;
+ unsigned int plat_state;
+
+ for (level = psci_get_suspend_afflvl(); level >= 0; level--) {
+ plat_state = (is_local_state_run(
+ target_state->pwr_domain_state[level]) ?
+ PSCI_STATE_ON : PSCI_STATE_OFF);
+ pm_ops->affinst_suspend_finish(level, plat_state);
+ }
+}
+
+/*******************************************************************************
+ * The PSCI compatibility helper for plat_pm_ops_t 'system_off' hook.
+ ******************************************************************************/
+static void __dead2 system_off_compat(void)
+{
+ pm_ops->system_off();
+}
+
+/*******************************************************************************
+ * The PSCI compatibility helper for plat_pm_ops_t 'system_reset' hook.
+ ******************************************************************************/
+static void __dead2 system_reset_compat(void)
+{
+ pm_ops->system_reset();
+}
+
+/*******************************************************************************
+ * Export the compatibility compat_psci_ops. The assumption made is that the
+ * power domains correspond to affinity instances on the platform.
+ ******************************************************************************/
+int plat_setup_psci_ops(uintptr_t sec_entrypoint,
+ const plat_psci_ops_t **psci_ops)
+{
+ platform_setup_pm(&pm_ops);
+
+ secure_entrypoint = (unsigned long) sec_entrypoint;
+
+ /*
+ * It is compulsory for the platform ports using the new porting
+ * interface to export a hook to validate the power state parameter
+ */
+ compat_psci_ops.validate_power_state = validate_power_state_compat;
+
+ /*
+ * Populate the compatibility plat_psci_ops_t hooks if available
+ */
+ if (pm_ops->validate_ns_entrypoint)
+ compat_psci_ops.validate_ns_entrypoint =
+ validate_ns_entrypoint_compat;
+
+ if (pm_ops->affinst_standby)
+ compat_psci_ops.cpu_standby = cpu_standby_compat;
+
+ if (pm_ops->affinst_on)
+ compat_psci_ops.pwr_domain_on = pwr_domain_on_compat;
+
+ if (pm_ops->affinst_off)
+ compat_psci_ops.pwr_domain_off = pwr_domain_off_compat;
+
+ if (pm_ops->affinst_suspend)
+ compat_psci_ops.pwr_domain_suspend = pwr_domain_suspend_compat;
+
+ if (pm_ops->affinst_on_finish)
+ compat_psci_ops.pwr_domain_on_finish =
+ pwr_domain_on_finish_compat;
+
+ if (pm_ops->affinst_suspend_finish)
+ compat_psci_ops.pwr_domain_suspend_finish =
+ pwr_domain_suspend_finish_compat;
+
+ if (pm_ops->system_off)
+ compat_psci_ops.system_off = system_off_compat;
+
+ if (pm_ops->system_reset)
+ compat_psci_ops.system_reset = system_reset_compat;
+
+ if (pm_ops->get_sys_suspend_power_state)
+ compat_psci_ops.get_sys_suspend_power_state =
+ get_sys_suspend_power_state_compat;
+
+ *psci_ops = &compat_psci_ops;
+ return 0;
+}
diff --git a/plat/compat/plat_topology_compat.c b/plat/compat/plat_topology_compat.c
new file mode 100644
index 0000000000..f65ad9d370
--- /dev/null
+++ b/plat/compat/plat_topology_compat.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <platform.h>
+#include <platform_def.h>
+#include <psci.h>
+
+/* The power domain tree descriptor */
+static unsigned char power_domain_tree_desc
+ [PLATFORM_NUM_AFFS - PLATFORM_CORE_COUNT + 1];
+
+/*******************************************************************************
+ * Simple routine to set the id of an affinity instance at a given level
+ * in the mpidr. The assumption is that the affinity level and the power
+ * domain level are the same.
+ ******************************************************************************/
+unsigned long mpidr_set_aff_inst(unsigned long mpidr,
+ unsigned char aff_inst,
+ int aff_lvl)
+{
+ unsigned long aff_shift;
+
+ assert(aff_lvl <= MPIDR_AFFLVL3);
+
+ /*
+ * Decide the number of bits to shift by depending upon
+ * the power level
+ */
+ aff_shift = get_afflvl_shift(aff_lvl);
+
+ /* Clear the existing power instance & set the new one*/
+ mpidr &= ~((unsigned long)MPIDR_AFFLVL_MASK << aff_shift);
+ mpidr |= (unsigned long)aff_inst << aff_shift;
+
+ return mpidr;
+}
+
+/******************************************************************************
+ * This function uses insertion sort to sort a given list of mpidr's in the
+ * ascending order of the index returned by platform_get_core_pos.
+ *****************************************************************************/
+void sort_mpidr_by_cpu_idx(unsigned int aff_count, unsigned long mpidr_list[])
+{
+ int i, j;
+ unsigned long temp_mpidr;
+
+ for (i = 1; i < aff_count; i++) {
+ temp_mpidr = mpidr_list[i];
+
+ for (j = i;
+ j > 0 &&
+ platform_get_core_pos(mpidr_list[j-1]) >
+ platform_get_core_pos(temp_mpidr);
+ j--)
+ mpidr_list[j] = mpidr_list[j-1];
+
+ mpidr_list[j] = temp_mpidr;
+ }
+}
+
+/*******************************************************************************
+ * The compatibility routine to construct the power domain tree description.
+ * The assumption made is that the power domains correspond to affinity
+ * instances on the platform. This routine's aim is to traverse to the target
+ * affinity level and populate the number of siblings at that level in
+ * 'power_domain_tree_desc' array. It uses the current affinity level to keep
+ * track of how many levels from the root of the tree have been traversed.
+ * If the current affinity level != target affinity level, then the platform
+ * is asked to return the number of children that each affinity instance has
+ * at the current affinity level. Traversal is then done for each child at the
+ * next lower level i.e. current affinity level - 1.
+ *
+ * The power domain description needs to be constructed in such a way that
+ * affinity instances containing CPUs with lower cpu indices need to be
+ * described first. Hence when traversing the power domain levels, the list
+ * of mpidrs at that power domain level is sorted in the ascending order of CPU
+ * indices before the lower levels are recursively described.
+ *
+ * CAUTION: This routine assumes that affinity instance ids are allocated in a
+ * monotonically increasing manner at each affinity level in a mpidr starting
+ * from 0. If the platform breaks this assumption then this code will have to
+ * be reworked accordingly.
+ ******************************************************************************/
+static unsigned int init_pwr_domain_tree_desc(unsigned long mpidr,
+ unsigned int affmap_idx,
+ int cur_afflvl,
+ int tgt_afflvl)
+{
+ unsigned int ctr, aff_count;
+
+ /*
+ * Temporary list to hold the MPIDR list at a particular power domain
+ * level so as to sort them.
+ */
+ unsigned long mpidr_list[PLATFORM_CORE_COUNT];
+
+ assert(cur_afflvl >= tgt_afflvl);
+
+ /*
+ * Find the number of siblings at the current power level &
+ * assert if there are none 'cause then we have been invoked with
+ * an invalid mpidr.
+ */
+ aff_count = plat_get_aff_count(cur_afflvl, mpidr);
+ assert(aff_count);
+
+ if (tgt_afflvl < cur_afflvl) {
+ for (ctr = 0; ctr < aff_count; ctr++) {
+ mpidr_list[ctr] = mpidr_set_aff_inst(mpidr, ctr,
+ cur_afflvl);
+ }
+
+ /* Need to sort mpidr list according to CPU index */
+ sort_mpidr_by_cpu_idx(aff_count, mpidr_list);
+ for (ctr = 0; ctr < aff_count; ctr++) {
+ affmap_idx = init_pwr_domain_tree_desc(mpidr_list[ctr],
+ affmap_idx,
+ cur_afflvl - 1,
+ tgt_afflvl);
+ }
+ } else {
+ power_domain_tree_desc[affmap_idx++] = aff_count;
+ }
+ return affmap_idx;
+}
+
+
+/*******************************************************************************
+ * This function constructs the topology tree description at runtime
+ * and returns it. The assumption made is that the power domains correspond
+ * to affinity instances on the platform.
+ ******************************************************************************/
+const unsigned char *plat_get_power_domain_tree_desc(void)
+{
+ int afflvl, affmap_idx;
+
+ /*
+ * We assume that the platform allocates affinity instance ids from
+ * 0 onwards at each affinity level in the mpidr. FIRST_MPIDR = 0.0.0.0
+ */
+ affmap_idx = 0;
+ for (afflvl = PLATFORM_MAX_AFFLVL; afflvl >= MPIDR_AFFLVL0; afflvl--) {
+ affmap_idx = init_pwr_domain_tree_desc(FIRST_MPIDR,
+ affmap_idx,
+ PLATFORM_MAX_AFFLVL,
+ afflvl);
+ }
+
+ assert(affmap_idx == (PLATFORM_NUM_AFFS - PLATFORM_CORE_COUNT + 1));
+
+ return power_domain_tree_desc;
+}
+
+/******************************************************************************
+ * The compatibility helper function for plat_core_pos_by_mpidr(). It
+ * validates the 'mpidr' by making sure that it is within acceptable bounds
+ * for the platform and queries the platform layer whether the CPU specified
+ * by the mpidr is present or not. If present, it returns the index of the
+ * core corresponding to the 'mpidr'. Else it returns -1.
+ *****************************************************************************/
+int plat_core_pos_by_mpidr(u_register_t mpidr)
+{
+ unsigned long shift, aff_inst;
+ int i;
+
+ /* Ignore the Reserved bits and U bit in MPIDR */
+ mpidr &= MPIDR_AFFINITY_MASK;
+
+ /*
+ * Check if any affinity field higher than
+ * the PLATFORM_MAX_AFFLVL is set.
+ */
+ shift = get_afflvl_shift(PLATFORM_MAX_AFFLVL + 1);
+ if (mpidr >> shift)
+ return -1;
+
+ for (i = PLATFORM_MAX_AFFLVL; i >= 0; i--) {
+ shift = get_afflvl_shift(i);
+ aff_inst = ((mpidr &
+ ((unsigned long)MPIDR_AFFLVL_MASK << shift)) >> shift);
+ if (aff_inst >= plat_get_aff_count(i, mpidr))
+ return -1;
+ }
+
+ if (plat_get_aff_state(0, mpidr) == PSCI_AFF_ABSENT)
+ return -1;
+
+ return platform_get_core_pos(mpidr);
+}
diff --git a/services/std_svc/psci1.0/psci_common.c b/services/std_svc/psci1.0/psci_common.c
index 0b885cde3d..7f1a5fd0d8 100644
--- a/services/std_svc/psci1.0/psci_common.c
+++ b/services/std_svc/psci1.0/psci_common.c
@@ -797,3 +797,81 @@ void psci_print_power_domain_map(void)
}
#endif
}
+
+#if ENABLE_PLAT_COMPAT
+/*******************************************************************************
+ * PSCI Compatibility helper function to return the 'power_state' parameter of
+ * the PSCI CPU SUSPEND request for the current CPU. Returns PSCI_INVALID_DATA
+ * if not invoked within CPU_SUSPEND for the current CPU.
+ ******************************************************************************/
+int psci_get_suspend_powerstate(void)
+{
+ /* Sanity check to verify that CPU is within CPU_SUSPEND */
+ if (psci_get_aff_info_state() == AFF_STATE_ON &&
+ !is_local_state_run(psci_get_cpu_local_state()))
+ return psci_power_state_compat[plat_my_core_pos()];
+
+ return PSCI_INVALID_DATA;
+}
+
+/*******************************************************************************
+ * PSCI Compatibility helper function to return the state id of the current
+ * cpu encoded in the 'power_state' parameter. Returns PSCI_INVALID_DATA
+ * if not invoked within CPU_SUSPEND for the current CPU.
+ ******************************************************************************/
+int psci_get_suspend_stateid(void)
+{
+ unsigned int power_state;
+ power_state = psci_get_suspend_powerstate();
+ if (power_state != PSCI_INVALID_DATA)
+ return psci_get_pstate_id(power_state);
+
+ return PSCI_INVALID_DATA;
+}
+
+/*******************************************************************************
+ * PSCI Compatibility helper function to return the state id encoded in the
+ * 'power_state' parameter of the CPU specified by 'mpidr'. Returns
+ * PSCI_INVALID_DATA if the CPU is not in CPU_SUSPEND.
+ ******************************************************************************/
+int psci_get_suspend_stateid_by_mpidr(unsigned long mpidr)
+{
+ int cpu_idx = plat_core_pos_by_mpidr(mpidr);
+
+ if (cpu_idx == -1)
+ return PSCI_INVALID_DATA;
+
+ /* Sanity check to verify that the CPU is in CPU_SUSPEND */
+ if (psci_get_aff_info_state_by_idx(cpu_idx) == AFF_STATE_ON &&
+ !is_local_state_run(psci_get_cpu_local_state_by_idx(cpu_idx)))
+ return psci_get_pstate_id(psci_power_state_compat[cpu_idx]);
+
+ return PSCI_INVALID_DATA;
+}
+
+/*******************************************************************************
+ * This function returns highest affinity level which is in OFF
+ * state. The affinity instance with which the level is associated is
+ * determined by the caller.
+ ******************************************************************************/
+unsigned int psci_get_max_phys_off_afflvl(void)
+{
+ psci_power_state_t state_info;
+
+ memset(&state_info, 0, sizeof(state_info));
+ psci_get_target_local_pwr_states(PLAT_MAX_PWR_LVL, &state_info);
+
+ return psci_find_target_suspend_lvl(&state_info);
+}
+
+/*******************************************************************************
+ * PSCI Compatibility helper function to return target affinity level requested
+ * for the CPU_SUSPEND. This function assumes affinity levels correspond to
+ * power domain levels on the platform.
+ ******************************************************************************/
+int psci_get_suspend_afflvl(void)
+{
+ return psci_get_suspend_pwrlvl();
+}
+
+#endif