diff --git a/Makefile b/Makefile index b1c42eb..c1e9617 100644 --- a/Makefile +++ b/Makefile @@ -169,6 +169,20 @@ 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 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 fe23711..9361187 100644 --- a/include/bl31/services/psci1.0/psci.h +++ b/include/bl31/services/psci1.0/psci.h @@ -33,6 +33,9 @@ #include #include /* for PLAT_NUM_PWR_DOMAINS */ +#if ENABLE_PLAT_COMPAT +#include +#endif /******************************************************************************* * Number of power domains whose state this psci imp. can track @@ -316,8 +319,6 @@ /* 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 0000000..cc80ae3 --- /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 +#include + +#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 d29fcfc..f054cd0 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 #include +#include +#include + /******************************************************************************* * Forward declarations @@ -59,7 +61,7 @@ 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 @@ 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 0cea9ac..b1f7b6d 100644 --- a/plat/common/aarch64/platform_mp_stack.S +++ b/plat/common/aarch64/platform_mp_stack.S @@ -30,15 +30,57 @@ #include #include +#include #include - .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) * @@ -93,6 +135,8 @@ 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 0000000..6d83d23 --- /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 +#include +#include +#include + + .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 0000000..c0c8ece --- /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 0000000..f51bb55 --- /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 +#include +#include +#include +#include + +/* + * 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 0000000..f65ad9d --- /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 +#include +#include +#include +#include + +/* 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 0b885cd..7f1a5fd 100644 --- a/services/std_svc/psci1.0/psci_common.c +++ b/services/std_svc/psci1.0/psci_common.c @@ -797,3 +797,81 @@ } #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