From b48349eb07103e23cea3d890dd01d52c74667514 Mon Sep 17 00:00:00 2001 From: Soby Mathew Date: Mon, 29 Jun 2015 16:30:12 +0100 Subject: [PATCH 01/20] PSCI: Create new directory to implement new frameworks This patch creates a copy of the existing PSCI files and related psci.h and platform.h header files in a new `PSCI1.0` directory. The changes for the new PSCI power domain topology and extended state-ID frameworks will be added incrementally to these files. This incremental approach will aid in review and in understanding the changes better. Once all the changes have been introduced, these files will replace the existing PSCI files. Change-Id: Ibb8a52e265daa4204e34829ed050bddd7e3316ff --- include/bl31/services/psci1.0/psci.h | 259 +++++++ include/plat/common/psci1.0/platform.h | 204 ++++++ services/std_svc/psci1.0/psci_afflvl_off.c | 248 +++++++ services/std_svc/psci1.0/psci_afflvl_on.c | 405 +++++++++++ .../std_svc/psci1.0/psci_afflvl_suspend.c | 469 +++++++++++++ services/std_svc/psci1.0/psci_common.c | 648 ++++++++++++++++++ services/std_svc/psci1.0/psci_entry.S | 130 ++++ services/std_svc/psci1.0/psci_helpers.S | 168 +++++ services/std_svc/psci1.0/psci_main.c | 463 +++++++++++++ services/std_svc/psci1.0/psci_private.h | 180 +++++ services/std_svc/psci1.0/psci_setup.c | 400 +++++++++++ services/std_svc/psci1.0/psci_system_off.c | 70 ++ 12 files changed, 3644 insertions(+) create mode 100644 include/bl31/services/psci1.0/psci.h create mode 100644 include/plat/common/psci1.0/platform.h create mode 100644 services/std_svc/psci1.0/psci_afflvl_off.c create mode 100644 services/std_svc/psci1.0/psci_afflvl_on.c create mode 100644 services/std_svc/psci1.0/psci_afflvl_suspend.c create mode 100644 services/std_svc/psci1.0/psci_common.c create mode 100644 services/std_svc/psci1.0/psci_entry.S create mode 100644 services/std_svc/psci1.0/psci_helpers.S create mode 100644 services/std_svc/psci1.0/psci_main.c create mode 100644 services/std_svc/psci1.0/psci_private.h create mode 100644 services/std_svc/psci1.0/psci_setup.c create mode 100644 services/std_svc/psci1.0/psci_system_off.c diff --git a/include/bl31/services/psci1.0/psci.h b/include/bl31/services/psci1.0/psci.h new file mode 100644 index 0000000000..dd1891c6fe --- /dev/null +++ b/include/bl31/services/psci1.0/psci.h @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2013-2014, 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_H__ +#define __PSCI_H__ + +#include +#include /* for PLATFORM_NUM_AFFS */ + +/******************************************************************************* + * Number of affinity instances whose state this psci imp. can track + ******************************************************************************/ +#ifdef PLATFORM_NUM_AFFS +#define PSCI_NUM_AFFS PLATFORM_NUM_AFFS +#else +#define PSCI_NUM_AFFS (2 * PLATFORM_CORE_COUNT) +#endif + +/******************************************************************************* + * Defines for runtime services func ids + ******************************************************************************/ +#define PSCI_VERSION 0x84000000 +#define PSCI_CPU_SUSPEND_AARCH32 0x84000001 +#define PSCI_CPU_SUSPEND_AARCH64 0xc4000001 +#define PSCI_CPU_OFF 0x84000002 +#define PSCI_CPU_ON_AARCH32 0x84000003 +#define PSCI_CPU_ON_AARCH64 0xc4000003 +#define PSCI_AFFINITY_INFO_AARCH32 0x84000004 +#define PSCI_AFFINITY_INFO_AARCH64 0xc4000004 +#define PSCI_MIG_AARCH32 0x84000005 +#define PSCI_MIG_AARCH64 0xc4000005 +#define PSCI_MIG_INFO_TYPE 0x84000006 +#define PSCI_MIG_INFO_UP_CPU_AARCH32 0x84000007 +#define PSCI_MIG_INFO_UP_CPU_AARCH64 0xc4000007 +#define PSCI_SYSTEM_OFF 0x84000008 +#define PSCI_SYSTEM_RESET 0x84000009 +#define PSCI_FEATURES 0x8400000A +#define PSCI_SYSTEM_SUSPEND_AARCH32 0x8400000E +#define PSCI_SYSTEM_SUSPEND_AARCH64 0xc400000E + +/* Macro to help build the psci capabilities bitfield */ +#define define_psci_cap(x) (1 << (x & 0x1f)) + +/* + * Number of PSCI calls (above) implemented + */ +#define PSCI_NUM_CALLS 18 + +/******************************************************************************* + * PSCI Migrate and friends + ******************************************************************************/ +#define PSCI_TOS_UP_MIG_CAP 0 +#define PSCI_TOS_NOT_UP_MIG_CAP 1 +#define PSCI_TOS_NOT_PRESENT_MP 2 + +/******************************************************************************* + * PSCI CPU_SUSPEND 'power_state' parameter specific defines + ******************************************************************************/ +#define PSTATE_ID_SHIFT 0 +#define PSTATE_TYPE_SHIFT 16 +#define PSTATE_AFF_LVL_SHIFT 24 + +#define PSTATE_ID_MASK 0xffff +#define PSTATE_TYPE_MASK 0x1 +#define PSTATE_AFF_LVL_MASK 0x3 +#define PSTATE_VALID_MASK 0xFCFE0000 + +#define PSTATE_TYPE_STANDBY 0x0 +#define PSTATE_TYPE_POWERDOWN 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_afflvl(pstate) (((pstate) >> PSTATE_AFF_LVL_SHIFT) & \ + PSTATE_AFF_LVL_MASK) +#define psci_make_powerstate(state_id, type, afflvl) \ + (((state_id) & PSTATE_ID_MASK) << PSTATE_ID_SHIFT) |\ + (((type) & PSTATE_TYPE_MASK) << PSTATE_TYPE_SHIFT) |\ + (((afflvl) & PSTATE_AFF_LVL_MASK) << PSTATE_AFF_LVL_SHIFT) + +/******************************************************************************* + * PSCI CPU_FEATURES feature flag specific defines + ******************************************************************************/ +/* Features flags for CPU SUSPEND power state parameter format. Bits [1:1] */ +#define FF_PSTATE_SHIFT 1 +#define FF_PSTATE_ORIG 0 +#define FF_PSTATE_EXTENDED 1 + +/* Features flags for CPU SUSPEND OS Initiated mode support. Bits [0:0] */ +#define FF_MODE_SUPPORT_SHIFT 0 +#define FF_SUPPORTS_OS_INIT_MODE 1 + +/******************************************************************************* + * PSCI version + ******************************************************************************/ +#define PSCI_MAJOR_VER (1 << 16) +#define PSCI_MINOR_VER 0x0 + +/******************************************************************************* + * PSCI error codes + ******************************************************************************/ +#define PSCI_E_SUCCESS 0 +#define PSCI_E_NOT_SUPPORTED -1 +#define PSCI_E_INVALID_PARAMS -2 +#define PSCI_E_DENIED -3 +#define PSCI_E_ALREADY_ON -4 +#define PSCI_E_ON_PENDING -5 +#define PSCI_E_INTERN_FAIL -6 +#define PSCI_E_NOT_PRESENT -7 +#define PSCI_E_DISABLED -8 + +/******************************************************************************* + * PSCI affinity state related constants. An affinity instance could be present + * or absent physically to cater for asymmetric topologies. If present then it + * could in one of the 4 further defined states. + ******************************************************************************/ +#define PSCI_STATE_SHIFT 1 +#define PSCI_STATE_MASK 0xff + +#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 + +#define PSCI_INVALID_DATA -1 + +#define get_phys_state(x) (x != PSCI_STATE_ON ? \ + PSCI_STATE_OFF : PSCI_STATE_ON) + +#define psci_validate_power_state(pstate) (pstate & PSTATE_VALID_MASK) + + +#ifndef __ASSEMBLY__ + +#include + +/******************************************************************************* + * Structure used to store per-cpu information relevant to the PSCI service. + * It is populated in the per-cpu data array. In return we get a guarantee that + * this information will not reside on a cache line shared with another cpu. + ******************************************************************************/ +typedef struct psci_cpu_data { + uint32_t power_state; + uint32_t max_phys_off_afflvl; /* Highest affinity level in physically + powered off state */ +#if !USE_COHERENT_MEM + bakery_info_t pcpu_bakery_info[PSCI_NUM_AFFS]; +#endif +} psci_cpu_data_t; + +/******************************************************************************* + * 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; + +/******************************************************************************* + * Optional structure populated by the Secure Payload Dispatcher to be given a + * chance to perform any bookkeeping before PSCI executes a power mgmt. + * operation. It also allows PSCI to determine certain properties of the SP e.g. + * migrate capability etc. + ******************************************************************************/ +typedef struct spd_pm_ops { + void (*svc_on)(uint64_t target_cpu); + int32_t (*svc_off)(uint64_t __unused); + void (*svc_suspend)(uint64_t __unused); + void (*svc_on_finish)(uint64_t __unused); + void (*svc_suspend_finish)(uint64_t suspend_level); + int32_t (*svc_migrate)(uint64_t from_cpu, uint64_t to_cpu); + int32_t (*svc_migrate_info)(uint64_t *resident_cpu); + void (*svc_system_off)(void); + void (*svc_system_reset)(void); +} spd_pm_ops_t; + +/******************************************************************************* + * Function & Data prototypes + ******************************************************************************/ +unsigned int psci_version(void); +int psci_affinity_info(unsigned long, unsigned int); +int psci_migrate(unsigned long); +int psci_migrate_info_type(void); +long psci_migrate_info_up_cpu(void); +int psci_cpu_on(unsigned long, + unsigned long, + unsigned long); +void __dead2 psci_power_down_wfi(void); +void psci_aff_on_finish_entry(void); +void psci_aff_suspend_finish_entry(void); +void psci_register_spd_pm_hook(const spd_pm_ops_t *); +int psci_get_suspend_stateid_by_mpidr(unsigned long); +int psci_get_suspend_stateid(void); +int psci_get_suspend_afflvl(void); +uint32_t psci_get_max_phys_off_afflvl(void); + +uint64_t psci_smc_handler(uint32_t smc_fid, + uint64_t x1, + uint64_t x2, + uint64_t x3, + uint64_t x4, + void *cookie, + void *handle, + uint64_t flags); + +/* PSCI setup function */ +int32_t psci_setup(void); + + +#endif /*__ASSEMBLY__*/ + + +#endif /* __PSCI_H__ */ diff --git a/include/plat/common/psci1.0/platform.h b/include/plat/common/psci1.0/platform.h new file mode 100644 index 0000000000..469d46b678 --- /dev/null +++ b/include/plat/common/psci1.0/platform.h @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2013-2014, 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 __PLATFORM_H__ +#define __PLATFORM_H__ + +#include + + +/******************************************************************************* + * Forward declarations + ******************************************************************************/ +struct plat_pm_ops; +struct meminfo; +struct image_info; +struct entry_point_info; +struct bl31_params; + +/******************************************************************************* + * plat_get_rotpk_info() flags + ******************************************************************************/ +#define ROTPK_IS_HASH (1 << 0) + +/******************************************************************************* + * Function declarations + ******************************************************************************/ +/******************************************************************************* + * Mandatory common functions + ******************************************************************************/ +uint64_t plat_get_syscnt_freq(void); +int plat_get_image_source(unsigned int image_id, + uintptr_t *dev_handle, + uintptr_t *image_spec); +unsigned long plat_get_ns_image_entrypoint(void); + +/******************************************************************************* + * Mandatory interrupt management functions + ******************************************************************************/ +uint32_t plat_ic_get_pending_interrupt_id(void); +uint32_t plat_ic_get_pending_interrupt_type(void); +uint32_t plat_ic_acknowledge_interrupt(void); +uint32_t plat_ic_get_interrupt_type(uint32_t id); +void plat_ic_end_of_interrupt(uint32_t id); +uint32_t plat_interrupt_type_to_line(uint32_t type, + uint32_t security_state); + +/******************************************************************************* + * Optional common functions (may be overridden) + ******************************************************************************/ +unsigned int platform_get_core_pos(unsigned long mpidr); +unsigned long platform_get_stack(unsigned long mpidr); +void plat_report_exception(unsigned long); +int plat_crash_console_init(void); +int plat_crash_console_putc(int c); + +/******************************************************************************* + * Mandatory BL1 functions + ******************************************************************************/ +void bl1_early_platform_setup(void); +void bl1_plat_arch_setup(void); +void bl1_platform_setup(void); +struct meminfo *bl1_plat_sec_mem_layout(void); + +/* + * This function allows the platform to change the entrypoint information for + * BL2, after BL1 has loaded BL2 into memory but before BL2 is executed. + */ +void bl1_plat_set_bl2_ep_info(struct image_info *image, + struct entry_point_info *ep); + +/******************************************************************************* + * Optional BL1 functions (may be overridden) + ******************************************************************************/ +void bl1_init_bl2_mem_layout(const struct meminfo *bl1_mem_layout, + struct meminfo *bl2_mem_layout); + +/******************************************************************************* + * Mandatory BL2 functions + ******************************************************************************/ +void bl2_early_platform_setup(struct meminfo *mem_layout); +void bl2_plat_arch_setup(void); +void bl2_platform_setup(void); +struct meminfo *bl2_plat_sec_mem_layout(void); + +/* + * This function returns a pointer to the shared memory that the platform has + * kept aside to pass trusted firmware related information that BL3-1 + * could need + */ +struct bl31_params *bl2_plat_get_bl31_params(void); + +/* + * This function returns a pointer to the shared memory that the platform + * has kept to point to entry point information of BL31 to BL2 + */ +struct entry_point_info *bl2_plat_get_bl31_ep_info(void); + +/* + * This function flushes to main memory all the params that are + * passed to BL3-1 + */ +void bl2_plat_flush_bl31_params(void); + +/* + * The next 2 functions allow the platform to change the entrypoint information + * for the mandatory 3rd level BL images, BL3-1 and BL3-3. This is done after + * BL2 has loaded those images into memory but before BL3-1 is executed. + */ +void bl2_plat_set_bl31_ep_info(struct image_info *image, + struct entry_point_info *ep); + +void bl2_plat_set_bl33_ep_info(struct image_info *image, + struct entry_point_info *ep); + +/* Gets the memory layout for BL3-3 */ +void bl2_plat_get_bl33_meminfo(struct meminfo *mem_info); + +/******************************************************************************* + * Conditionally mandatory BL2 functions: must be implemented if BL3-0 image + * is supported + ******************************************************************************/ +/* Gets the memory layout for BL3-0 */ +void bl2_plat_get_bl30_meminfo(struct meminfo *mem_info); + +/* + * This function is called after loading BL3-0 image and it is used to perform + * any platform-specific actions required to handle the SCP firmware. + */ +int bl2_plat_handle_bl30(struct image_info *bl30_image_info); + +/******************************************************************************* + * Conditionally mandatory BL2 functions: must be implemented if BL3-2 image + * is supported + ******************************************************************************/ +void bl2_plat_set_bl32_ep_info(struct image_info *image, + struct entry_point_info *ep); + +/* Gets the memory layout for BL3-2 */ +void bl2_plat_get_bl32_meminfo(struct meminfo *mem_info); + +/******************************************************************************* + * Optional BL2 functions (may be overridden) + ******************************************************************************/ + +/******************************************************************************* + * Mandatory BL3-1 functions + ******************************************************************************/ +void bl31_early_platform_setup(struct bl31_params *from_bl2, + void *plat_params_from_bl2); +void bl31_plat_arch_setup(void); +void bl31_platform_setup(void); +struct entry_point_info *bl31_plat_get_next_image_ep_info(uint32_t type); + +/******************************************************************************* + * Mandatory PSCI functions (BL3-1) + ******************************************************************************/ +int platform_setup_pm(const struct plat_pm_ops **); +unsigned int plat_get_aff_count(unsigned int, unsigned long); +unsigned int plat_get_aff_state(unsigned int, unsigned long); + +/******************************************************************************* + * Optional BL3-1 functions (may be overridden) + ******************************************************************************/ +void bl31_plat_enable_mmu(uint32_t flags); + +/******************************************************************************* + * Optional BL3-2 functions (may be overridden) + ******************************************************************************/ +void bl32_plat_enable_mmu(uint32_t flags); + +/******************************************************************************* + * Trusted Board Boot functions + ******************************************************************************/ +int plat_get_rotpk_info(void *cookie, void **key_ptr, unsigned int *key_len, + unsigned int *flags); + +#endif /* __PLATFORM_H__ */ diff --git a/services/std_svc/psci1.0/psci_afflvl_off.c b/services/std_svc/psci1.0/psci_afflvl_off.c new file mode 100644 index 0000000000..7eb968899e --- /dev/null +++ b/services/std_svc/psci1.0/psci_afflvl_off.c @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2013-2014, 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 +#include "psci_private.h" + +typedef void (*afflvl_off_handler_t)(aff_map_node_t *node); + +/******************************************************************************* + * The next three functions implement a handler for each supported affinity + * level which is called when that affinity level is turned off. + ******************************************************************************/ +static void psci_afflvl0_off(aff_map_node_t *cpu_node) +{ + assert(cpu_node->level == MPIDR_AFFLVL0); + + /* + * Arch. management. Perform the necessary steps to flush all + * cpu caches. + */ + psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL0); + + /* + * Plat. management: Perform platform specific actions to turn this + * cpu off e.g. exit cpu coherency, program the power controller etc. + */ + psci_plat_pm_ops->affinst_off(cpu_node->level, + psci_get_phys_state(cpu_node)); +} + +static void psci_afflvl1_off(aff_map_node_t *cluster_node) +{ + /* Sanity check the cluster level */ + assert(cluster_node->level == MPIDR_AFFLVL1); + + /* + * Arch. Management. Flush all levels of caches to PoC if + * the cluster is to be shutdown. + */ + psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL1); + + /* + * Plat. Management. Allow the platform to do its cluster + * specific bookeeping e.g. turn off interconnect coherency, + * program the power controller etc. + */ + psci_plat_pm_ops->affinst_off(cluster_node->level, + psci_get_phys_state(cluster_node)); +} + +static void psci_afflvl2_off(aff_map_node_t *system_node) +{ + /* Cannot go beyond this level */ + assert(system_node->level == MPIDR_AFFLVL2); + + /* + * Keep the physical state of the system handy to decide what + * action needs to be taken + */ + + /* + * Arch. Management. Flush all levels of caches to PoC if + * the system is to be shutdown. + */ + psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL2); + + /* + * Plat. Management : Allow the platform to do its bookeeping + * at this affinity level + */ + psci_plat_pm_ops->affinst_off(system_node->level, + psci_get_phys_state(system_node)); +} + +static const afflvl_off_handler_t psci_afflvl_off_handlers[] = { + psci_afflvl0_off, + psci_afflvl1_off, + psci_afflvl2_off, +}; + +/******************************************************************************* + * This function takes an array of pointers to affinity instance nodes in the + * topology tree and calls the off handler for the corresponding affinity + * levels + ******************************************************************************/ +static void psci_call_off_handlers(aff_map_node_t *mpidr_nodes[], + int start_afflvl, + int end_afflvl) +{ + int level; + aff_map_node_t *node; + + for (level = start_afflvl; level <= end_afflvl; level++) { + node = mpidr_nodes[level]; + if (node == NULL) + continue; + + psci_afflvl_off_handlers[level](node); + } +} + +/******************************************************************************* + * Top level handler which is called when a cpu wants to power itself down. + * It's assumed that along with turning the cpu off, higher affinity levels will + * be turned off as far as possible. It traverses through all the affinity + * levels performing generic, architectural, platform setup and state management + * e.g. for a cluster that's to be powered off, it will call the platform + * specific code which will disable coherency at the interconnect level if the + * cpu is the last in the cluster. For a cpu it could mean programming the power + * the power controller etc. + * + * The state of all the relevant affinity levels is changed prior to calling the + * affinity level specific handlers as their actions would depend upon the state + * the affinity level is about to enter. + * + * The affinity level specific handlers are called in ascending order i.e. from + * the lowest to the highest affinity level implemented by the platform because + * to turn off affinity level X it is neccesary to turn off affinity level X - 1 + * first. + ******************************************************************************/ +int psci_afflvl_off(int start_afflvl, + int end_afflvl) +{ + int rc; + mpidr_aff_map_nodes_t mpidr_nodes; + unsigned int max_phys_off_afflvl; + + /* + * This function must only be called on platforms where the + * CPU_OFF platform hooks have been implemented. + */ + assert(psci_plat_pm_ops->affinst_off); + + /* + * Collect the pointers to the nodes in the topology tree for + * each affinity instance in the mpidr. If this function does + * not return successfully then either the mpidr or the affinity + * levels are incorrect. Either way, this an internal TF error + * therefore assert. + */ + rc = psci_get_aff_map_nodes(read_mpidr_el1() & MPIDR_AFFINITY_MASK, + start_afflvl, + end_afflvl, + mpidr_nodes); + assert(rc == PSCI_E_SUCCESS); + + /* + * This function acquires the lock corresponding to each affinity + * level so that by the time all locks are taken, the system topology + * is snapshot and state management can be done safely. + */ + psci_acquire_afflvl_locks(start_afflvl, + end_afflvl, + mpidr_nodes); + + + /* + * Call the cpu off handler registered by the Secure Payload Dispatcher + * to let it do any bookkeeping. Assume that the SPD always reports an + * E_DENIED error if SP refuse to power down + */ + if (psci_spd_pm && psci_spd_pm->svc_off) { + rc = psci_spd_pm->svc_off(0); + if (rc) + goto exit; + } + + /* + * This function updates the state of each affinity instance + * corresponding to the mpidr in the range of affinity levels + * specified. + */ + psci_do_afflvl_state_mgmt(start_afflvl, + end_afflvl, + mpidr_nodes, + PSCI_STATE_OFF); + + max_phys_off_afflvl = psci_find_max_phys_off_afflvl(start_afflvl, + end_afflvl, + mpidr_nodes); + assert(max_phys_off_afflvl != PSCI_INVALID_DATA); + + /* Stash the highest affinity level that will enter the OFF state. */ + psci_set_max_phys_off_afflvl(max_phys_off_afflvl); + + /* Perform generic, architecture and platform specific handling */ + psci_call_off_handlers(mpidr_nodes, + start_afflvl, + end_afflvl); + + /* + * Invalidate the entry for the highest affinity level stashed earlier. + * This ensures that any reads of this variable outside the power + * up/down sequences return PSCI_INVALID_DATA. + * + */ + psci_set_max_phys_off_afflvl(PSCI_INVALID_DATA); + +exit: + /* + * Release the locks corresponding to each affinity level in the + * reverse order to which they were acquired. + */ + psci_release_afflvl_locks(start_afflvl, + end_afflvl, + mpidr_nodes); + + /* + * Check if all actions needed to safely power down this cpu have + * successfully completed. Enter a wfi loop which will allow the + * power controller to physically power down this cpu. + */ + if (rc == PSCI_E_SUCCESS) + psci_power_down_wfi(); + + return rc; +} diff --git a/services/std_svc/psci1.0/psci_afflvl_on.c b/services/std_svc/psci1.0/psci_afflvl_on.c new file mode 100644 index 0000000000..0dbd0e0608 --- /dev/null +++ b/services/std_svc/psci1.0/psci_afflvl_on.c @@ -0,0 +1,405 @@ +/* + * Copyright (c) 2013-2014, 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 +#include +#include +#include +#include +#include +#include "psci_private.h" + +typedef int (*afflvl_on_handler_t)(unsigned long target_cpu, + aff_map_node_t *node); + +/******************************************************************************* + * This function checks whether a cpu which has been requested to be turned on + * is OFF to begin with. + ******************************************************************************/ +static int cpu_on_validate_state(unsigned int psci_state) +{ + if (psci_state == PSCI_STATE_ON || psci_state == PSCI_STATE_SUSPEND) + return PSCI_E_ALREADY_ON; + + if (psci_state == PSCI_STATE_ON_PENDING) + return PSCI_E_ON_PENDING; + + assert(psci_state == PSCI_STATE_OFF); + return PSCI_E_SUCCESS; +} + +/******************************************************************************* + * Handler routine to turn a cpu on. It takes care of any generic, architectural + * or platform specific setup required. + * TODO: Split this code across separate handlers for each type of setup? + ******************************************************************************/ +static int psci_afflvl0_on(unsigned long target_cpu, + aff_map_node_t *cpu_node) +{ + unsigned long psci_entrypoint; + + /* Sanity check to safeguard against data corruption */ + assert(cpu_node->level == MPIDR_AFFLVL0); + + /* Set the secure world (EL3) re-entry point after BL1 */ + psci_entrypoint = (unsigned long) psci_aff_on_finish_entry; + + /* + * Plat. management: Give the platform the current state + * of the target cpu to allow it to perform the necessary + * steps to power on. + */ + return psci_plat_pm_ops->affinst_on(target_cpu, + psci_entrypoint, + cpu_node->level, + psci_get_phys_state(cpu_node)); +} + +/******************************************************************************* + * Handler routine to turn a cluster on. It takes care or any generic, arch. + * or platform specific setup required. + * TODO: Split this code across separate handlers for each type of setup? + ******************************************************************************/ +static int psci_afflvl1_on(unsigned long target_cpu, + aff_map_node_t *cluster_node) +{ + unsigned long psci_entrypoint; + + assert(cluster_node->level == MPIDR_AFFLVL1); + + /* + * There is no generic and arch. specific cluster + * management required + */ + + /* State management: Is not required while turning a cluster on */ + + /* + * Plat. management: Give the platform the current state + * of the target cpu to allow it to perform the necessary + * steps to power on. + */ + psci_entrypoint = (unsigned long) psci_aff_on_finish_entry; + return psci_plat_pm_ops->affinst_on(target_cpu, + psci_entrypoint, + cluster_node->level, + psci_get_phys_state(cluster_node)); +} + +/******************************************************************************* + * Handler routine to turn a cluster of clusters on. It takes care or any + * generic, arch. or platform specific setup required. + * TODO: Split this code across separate handlers for each type of setup? + ******************************************************************************/ +static int psci_afflvl2_on(unsigned long target_cpu, + aff_map_node_t *system_node) +{ + unsigned long psci_entrypoint; + + /* Cannot go beyond affinity level 2 in this psci imp. */ + assert(system_node->level == MPIDR_AFFLVL2); + + /* + * There is no generic and arch. specific system management + * required + */ + + /* State management: Is not required while turning a system on */ + + /* + * Plat. management: Give the platform the current state + * of the target cpu to allow it to perform the necessary + * steps to power on. + */ + psci_entrypoint = (unsigned long) psci_aff_on_finish_entry; + return psci_plat_pm_ops->affinst_on(target_cpu, + psci_entrypoint, + system_node->level, + psci_get_phys_state(system_node)); +} + +/* Private data structure to make this handlers accessible through indexing */ +static const afflvl_on_handler_t psci_afflvl_on_handlers[] = { + psci_afflvl0_on, + psci_afflvl1_on, + psci_afflvl2_on, +}; + +/******************************************************************************* + * This function takes an array of pointers to affinity instance nodes in the + * topology tree and calls the on handler for the corresponding affinity + * levels + ******************************************************************************/ +static int psci_call_on_handlers(aff_map_node_t *target_cpu_nodes[], + int start_afflvl, + int end_afflvl, + unsigned long target_cpu) +{ + int rc = PSCI_E_INVALID_PARAMS, level; + aff_map_node_t *node; + + for (level = end_afflvl; level >= start_afflvl; level--) { + node = target_cpu_nodes[level]; + if (node == NULL) + continue; + + /* + * TODO: In case of an error should there be a way + * of undoing what we might have setup at higher + * affinity levels. + */ + rc = psci_afflvl_on_handlers[level](target_cpu, + node); + if (rc != PSCI_E_SUCCESS) + break; + } + + return rc; +} + +/******************************************************************************* + * Generic handler which is called to physically power on a cpu identified by + * its mpidr. It traverses through all the affinity levels performing generic, + * architectural, platform setup and state management e.g. for a cpu that is + * to be powered on, it will ensure that enough information is stashed for it + * to resume execution in the non-secure security state. + * + * The state of all the relevant affinity levels is changed after calling the + * affinity level specific handlers as their actions would depend upon the state + * the affinity level is currently in. + * + * The affinity level specific handlers are called in descending order i.e. from + * the highest to the lowest affinity level implemented by the platform because + * to turn on affinity level X it is necessary to turn on affinity level X + 1 + * first. + ******************************************************************************/ +int psci_afflvl_on(unsigned long target_cpu, + entry_point_info_t *ep, + int start_afflvl, + int end_afflvl) +{ + int rc; + mpidr_aff_map_nodes_t target_cpu_nodes; + + /* + * This function must only be called on platforms where the + * CPU_ON platform hooks have been implemented. + */ + assert(psci_plat_pm_ops->affinst_on && + psci_plat_pm_ops->affinst_on_finish); + + /* + * Collect the pointers to the nodes in the topology tree for + * each affinity instance in the mpidr. If this function does + * not return successfully then either the mpidr or the affinity + * levels are incorrect. + */ + rc = psci_get_aff_map_nodes(target_cpu, + start_afflvl, + end_afflvl, + target_cpu_nodes); + assert(rc == PSCI_E_SUCCESS); + + /* + * This function acquires the lock corresponding to each affinity + * level so that by the time all locks are taken, the system topology + * is snapshot and state management can be done safely. + */ + psci_acquire_afflvl_locks(start_afflvl, + end_afflvl, + target_cpu_nodes); + + /* + * Generic management: Ensure that the cpu is off to be + * turned on. + */ + rc = cpu_on_validate_state(psci_get_state( + target_cpu_nodes[MPIDR_AFFLVL0])); + if (rc != PSCI_E_SUCCESS) + goto exit; + + /* + * Call the cpu on handler registered by the Secure Payload Dispatcher + * to let it do any bookeeping. If the handler encounters an error, it's + * expected to assert within + */ + if (psci_spd_pm && psci_spd_pm->svc_on) + psci_spd_pm->svc_on(target_cpu); + + /* + * This function updates the state of each affinity instance + * corresponding to the mpidr in the range of affinity levels + * specified. + */ + psci_do_afflvl_state_mgmt(start_afflvl, + end_afflvl, + target_cpu_nodes, + PSCI_STATE_ON_PENDING); + + /* Perform generic, architecture and platform specific handling. */ + rc = psci_call_on_handlers(target_cpu_nodes, + start_afflvl, + end_afflvl, + target_cpu); + + assert(rc == PSCI_E_SUCCESS || rc == PSCI_E_INTERN_FAIL); + + if (rc == PSCI_E_SUCCESS) + /* Store the re-entry information for the non-secure world. */ + cm_init_context(target_cpu, ep); + else + /* Restore the state on error. */ + psci_do_afflvl_state_mgmt(start_afflvl, + end_afflvl, + target_cpu_nodes, + PSCI_STATE_OFF); +exit: + /* + * This loop releases the lock corresponding to each affinity level + * in the reverse order to which they were acquired. + */ + psci_release_afflvl_locks(start_afflvl, + end_afflvl, + target_cpu_nodes); + + return rc; +} + +/******************************************************************************* + * The following functions finish an earlier affinity power on request. They + * are called by the common finisher routine in psci_common.c. + ******************************************************************************/ +static void psci_afflvl0_on_finish(aff_map_node_t *cpu_node) +{ + unsigned int plat_state, state; + + assert(cpu_node->level == MPIDR_AFFLVL0); + + /* Ensure we have been explicitly woken up by another cpu */ + state = psci_get_state(cpu_node); + assert(state == PSCI_STATE_ON_PENDING); + + /* + * Plat. management: Perform the platform specific actions + * for this cpu e.g. enabling the gic or zeroing the mailbox + * register. The actual state of this cpu has already been + * changed. + */ + + /* Get the physical state of this cpu */ + plat_state = get_phys_state(state); + psci_plat_pm_ops->affinst_on_finish(cpu_node->level, + plat_state); + + /* + * Arch. management: Enable data cache and manage stack memory + */ + psci_do_pwrup_cache_maintenance(); + + /* + * All the platform specific actions for turning this cpu + * on have completed. Perform enough arch.initialization + * to run in the non-secure address space. + */ + bl31_arch_setup(); + + /* + * Call the cpu on finish handler registered by the Secure Payload + * Dispatcher to let it do any bookeeping. If the handler encounters an + * error, it's expected to assert within + */ + if (psci_spd_pm && psci_spd_pm->svc_on_finish) + psci_spd_pm->svc_on_finish(0); + + /* + * Generic management: Now we just need to retrieve the + * information that we had stashed away during the cpu_on + * call to set this cpu on its way. + */ + cm_prepare_el3_exit(NON_SECURE); + + /* Clean caches before re-entering normal world */ + dcsw_op_louis(DCCSW); +} + +static void psci_afflvl1_on_finish(aff_map_node_t *cluster_node) +{ + unsigned int plat_state; + + assert(cluster_node->level == MPIDR_AFFLVL1); + + /* + * Plat. management: Perform the platform specific actions + * as per the old state of the cluster e.g. enabling + * coherency at the interconnect depends upon the state with + * which this cluster was powered up. If anything goes wrong + * then assert as there is no way to recover from this + * situation. + */ + plat_state = psci_get_phys_state(cluster_node); + psci_plat_pm_ops->affinst_on_finish(cluster_node->level, + plat_state); +} + + +static void psci_afflvl2_on_finish(aff_map_node_t *system_node) +{ + unsigned int plat_state; + + /* Cannot go beyond this affinity level */ + assert(system_node->level == MPIDR_AFFLVL2); + + /* + * Currently, there are no architectural actions to perform + * at the system level. + */ + + /* + * Plat. management: Perform the platform specific actions + * as per the old state of the cluster e.g. enabling + * coherency at the interconnect depends upon the state with + * which this cluster was powered up. If anything goes wrong + * then assert as there is no way to recover from this + * situation. + */ + plat_state = psci_get_phys_state(system_node); + psci_plat_pm_ops->affinst_on_finish(system_node->level, + plat_state); +} + +const afflvl_power_on_finisher_t psci_afflvl_on_finishers[] = { + psci_afflvl0_on_finish, + psci_afflvl1_on_finish, + psci_afflvl2_on_finish, +}; diff --git a/services/std_svc/psci1.0/psci_afflvl_suspend.c b/services/std_svc/psci1.0/psci_afflvl_suspend.c new file mode 100644 index 0000000000..76e8c908a0 --- /dev/null +++ b/services/std_svc/psci1.0/psci_afflvl_suspend.c @@ -0,0 +1,469 @@ +/* + * Copyright (c) 2013-2014, 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 +#include +#include +#include +#include +#include +#include +#include "psci_private.h" + +typedef void (*afflvl_suspend_handler_t)(aff_map_node_t *node); + +/******************************************************************************* + * This function saves the power state parameter passed in the current PSCI + * cpu_suspend call in the per-cpu data array. + ******************************************************************************/ +void psci_set_suspend_power_state(unsigned int power_state) +{ + set_cpu_data(psci_svc_cpu_data.power_state, power_state); + flush_cpu_data(psci_svc_cpu_data.power_state); +} + +/******************************************************************************* + * This function gets the affinity level till which the current cpu could be + * powered down during a cpu_suspend call. Returns PSCI_INVALID_DATA if the + * power state is invalid. + ******************************************************************************/ +int psci_get_suspend_afflvl(void) +{ + unsigned int power_state; + + power_state = get_cpu_data(psci_svc_cpu_data.power_state); + + return ((power_state == PSCI_INVALID_DATA) ? + power_state : psci_get_pstate_afflvl(power_state)); +} + +/******************************************************************************* + * This function gets the state id of the current cpu from the power state + * parameter saved in the per-cpu data array. Returns PSCI_INVALID_DATA if the + * power state saved is invalid. + ******************************************************************************/ +int psci_get_suspend_stateid(void) +{ + unsigned int power_state; + + power_state = get_cpu_data(psci_svc_cpu_data.power_state); + + return ((power_state == PSCI_INVALID_DATA) ? + power_state : psci_get_pstate_id(power_state)); +} + +/******************************************************************************* + * This function gets the state id of the cpu specified by the 'mpidr' parameter + * from the power state parameter saved in the per-cpu data array. Returns + * PSCI_INVALID_DATA if the power state saved is invalid. + ******************************************************************************/ +int psci_get_suspend_stateid_by_mpidr(unsigned long mpidr) +{ + unsigned int power_state; + + power_state = get_cpu_data_by_mpidr(mpidr, + psci_svc_cpu_data.power_state); + + return ((power_state == PSCI_INVALID_DATA) ? + power_state : psci_get_pstate_id(power_state)); +} + +/******************************************************************************* + * The next three functions implement a handler for each supported affinity + * level which is called when that affinity level is about to be suspended. + ******************************************************************************/ +static void psci_afflvl0_suspend(aff_map_node_t *cpu_node) +{ + unsigned long psci_entrypoint; + + /* Sanity check to safeguard against data corruption */ + assert(cpu_node->level == MPIDR_AFFLVL0); + + /* Set the secure world (EL3) re-entry point after BL1 */ + psci_entrypoint = (unsigned long) psci_aff_suspend_finish_entry; + + /* + * Arch. management. Perform the necessary steps to flush all + * cpu caches. + */ + psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL0); + + /* + * Plat. management: Allow the platform to perform the + * necessary actions to turn off this cpu e.g. set the + * platform defined mailbox with the psci entrypoint, + * program the power controller etc. + */ + psci_plat_pm_ops->affinst_suspend(psci_entrypoint, + cpu_node->level, + psci_get_phys_state(cpu_node)); +} + +static void psci_afflvl1_suspend(aff_map_node_t *cluster_node) +{ + unsigned int plat_state; + unsigned long psci_entrypoint; + + /* Sanity check the cluster level */ + assert(cluster_node->level == MPIDR_AFFLVL1); + + /* + * Arch. management: Flush all levels of caches to PoC if the + * cluster is to be shutdown. + */ + psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL1); + + /* + * Plat. Management. Allow the platform to do its cluster specific + * bookeeping e.g. turn off interconnect coherency, program the power + * controller etc. Sending the psci entrypoint is currently redundant + * beyond affinity level 0 but one never knows what a platform might + * do. Also it allows us to keep the platform handler prototype the + * same. + */ + plat_state = psci_get_phys_state(cluster_node); + psci_entrypoint = (unsigned long) psci_aff_suspend_finish_entry; + psci_plat_pm_ops->affinst_suspend(psci_entrypoint, + cluster_node->level, + plat_state); +} + + +static void psci_afflvl2_suspend(aff_map_node_t *system_node) +{ + unsigned int plat_state; + unsigned long psci_entrypoint; + + /* Cannot go beyond this */ + assert(system_node->level == MPIDR_AFFLVL2); + + /* + * Keep the physical state of the system handy to decide what + * action needs to be taken + */ + plat_state = psci_get_phys_state(system_node); + + /* + * Arch. management: Flush all levels of caches to PoC if the + * system is to be shutdown. + */ + psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL2); + + /* + * Plat. Management : Allow the platform to do its bookeeping + * at this affinity level + */ + + /* + * Sending the psci entrypoint is currently redundant + * beyond affinity level 0 but one never knows what a + * platform might do. Also it allows us to keep the + * platform handler prototype the same. + */ + plat_state = psci_get_phys_state(system_node); + psci_entrypoint = (unsigned long) psci_aff_suspend_finish_entry; + psci_plat_pm_ops->affinst_suspend(psci_entrypoint, + system_node->level, + plat_state); +} + +static const afflvl_suspend_handler_t psci_afflvl_suspend_handlers[] = { + psci_afflvl0_suspend, + psci_afflvl1_suspend, + psci_afflvl2_suspend, +}; + +/******************************************************************************* + * This function takes an array of pointers to affinity instance nodes in the + * topology tree and calls the suspend handler for the corresponding affinity + * levels + ******************************************************************************/ +static void psci_call_suspend_handlers(aff_map_node_t *mpidr_nodes[], + int start_afflvl, + int end_afflvl) +{ + int level; + aff_map_node_t *node; + + for (level = start_afflvl; level <= end_afflvl; level++) { + node = mpidr_nodes[level]; + if (node == NULL) + continue; + + psci_afflvl_suspend_handlers[level](node); + } +} + +/******************************************************************************* + * Top level handler which is called when a cpu wants to suspend its execution. + * It is assumed that along with turning the cpu off, higher affinity levels + * until the target affinity level will be turned off as well. It traverses + * through all the affinity levels performing generic, architectural, platform + * setup and state management e.g. for a cluster that's to be suspended, it will + * call the platform specific code which will disable coherency at the + * interconnect level if the cpu is the last in the cluster. For a cpu it could + * mean programming the power controller etc. + * + * The state of all the relevant affinity levels is changed prior to calling the + * affinity level specific handlers as their actions would depend upon the state + * the affinity level is about to enter. + * + * The affinity level specific handlers are called in ascending order i.e. from + * the lowest to the highest affinity level implemented by the platform because + * to turn off affinity level X it is neccesary to turn off affinity level X - 1 + * first. + * + * All the required parameter checks are performed at the beginning and after + * the state transition has been done, no further error is expected and it + * is not possible to undo any of the actions taken beyond that point. + ******************************************************************************/ +void psci_afflvl_suspend(entry_point_info_t *ep, + int start_afflvl, + int end_afflvl) +{ + int skip_wfi = 0; + mpidr_aff_map_nodes_t mpidr_nodes; + unsigned int max_phys_off_afflvl; + + /* + * This function must only be called on platforms where the + * CPU_SUSPEND platform hooks have been implemented. + */ + assert(psci_plat_pm_ops->affinst_suspend && + psci_plat_pm_ops->affinst_suspend_finish); + + /* + * Collect the pointers to the nodes in the topology tree for + * each affinity instance in the mpidr. If this function does + * not return successfully then either the mpidr or the affinity + * levels are incorrect. Either way, this an internal TF error + * therefore assert. + */ + if (psci_get_aff_map_nodes(read_mpidr_el1() & MPIDR_AFFINITY_MASK, + start_afflvl, end_afflvl, mpidr_nodes) != PSCI_E_SUCCESS) + assert(0); + + /* + * This function acquires the lock corresponding to each affinity + * level so that by the time all locks are taken, the system topology + * is snapshot and state management can be done safely. + */ + psci_acquire_afflvl_locks(start_afflvl, + end_afflvl, + mpidr_nodes); + + /* + * We check if there are any pending interrupts after the delay + * introduced by lock contention to increase the chances of early + * detection that a wake-up interrupt has fired. + */ + if (read_isr_el1()) { + skip_wfi = 1; + goto exit; + } + + /* + * Call the cpu suspend handler registered by the Secure Payload + * Dispatcher to let it do any bookeeping. If the handler encounters an + * error, it's expected to assert within + */ + if (psci_spd_pm && psci_spd_pm->svc_suspend) + psci_spd_pm->svc_suspend(0); + + /* + * This function updates the state of each affinity instance + * corresponding to the mpidr in the range of affinity levels + * specified. + */ + psci_do_afflvl_state_mgmt(start_afflvl, + end_afflvl, + mpidr_nodes, + PSCI_STATE_SUSPEND); + + max_phys_off_afflvl = psci_find_max_phys_off_afflvl(start_afflvl, + end_afflvl, + mpidr_nodes); + assert(max_phys_off_afflvl != PSCI_INVALID_DATA); + + /* Stash the highest affinity level that will be turned off */ + psci_set_max_phys_off_afflvl(max_phys_off_afflvl); + + /* + * Store the re-entry information for the non-secure world. + */ + cm_init_context(read_mpidr_el1(), ep); + + /* Perform generic, architecture and platform specific handling */ + psci_call_suspend_handlers(mpidr_nodes, + start_afflvl, + end_afflvl); + + /* + * Invalidate the entry for the highest affinity level stashed earlier. + * This ensures that any reads of this variable outside the power + * up/down sequences return PSCI_INVALID_DATA. + */ + psci_set_max_phys_off_afflvl(PSCI_INVALID_DATA); + +exit: + /* + * Release the locks corresponding to each affinity level in the + * reverse order to which they were acquired. + */ + psci_release_afflvl_locks(start_afflvl, + end_afflvl, + mpidr_nodes); + if (!skip_wfi) + psci_power_down_wfi(); +} + +/******************************************************************************* + * The following functions finish an earlier affinity suspend request. They + * are called by the common finisher routine in psci_common.c. + ******************************************************************************/ +static void psci_afflvl0_suspend_finish(aff_map_node_t *cpu_node) +{ + unsigned int plat_state, state; + int32_t suspend_level; + uint64_t counter_freq; + + assert(cpu_node->level == MPIDR_AFFLVL0); + + /* Ensure we have been woken up from a suspended state */ + state = psci_get_state(cpu_node); + assert(state == PSCI_STATE_SUSPEND); + + /* + * Plat. management: Perform the platform specific actions + * before we change the state of the cpu e.g. enabling the + * gic or zeroing the mailbox register. If anything goes + * wrong then assert as there is no way to recover from this + * situation. + */ + + /* Get the physical state of this cpu */ + plat_state = get_phys_state(state); + psci_plat_pm_ops->affinst_suspend_finish(cpu_node->level, + plat_state); + + /* + * Arch. management: Enable the data cache, manage stack memory and + * restore the stashed EL3 architectural context from the 'cpu_context' + * structure for this cpu. + */ + psci_do_pwrup_cache_maintenance(); + + /* Re-init the cntfrq_el0 register */ + counter_freq = plat_get_syscnt_freq(); + write_cntfrq_el0(counter_freq); + + /* + * Call the cpu suspend finish handler registered by the Secure Payload + * Dispatcher to let it do any bookeeping. If the handler encounters an + * error, it's expected to assert within + */ + if (psci_spd_pm && psci_spd_pm->svc_suspend) { + suspend_level = psci_get_suspend_afflvl(); + assert (suspend_level != PSCI_INVALID_DATA); + psci_spd_pm->svc_suspend_finish(suspend_level); + } + + /* Invalidate the suspend context for the node */ + psci_set_suspend_power_state(PSCI_INVALID_DATA); + + /* + * Generic management: Now we just need to retrieve the + * information that we had stashed away during the suspend + * call to set this cpu on its way. + */ + cm_prepare_el3_exit(NON_SECURE); + + /* Clean caches before re-entering normal world */ + dcsw_op_louis(DCCSW); +} + +static void psci_afflvl1_suspend_finish(aff_map_node_t *cluster_node) +{ + unsigned int plat_state; + + assert(cluster_node->level == MPIDR_AFFLVL1); + + /* + * Plat. management: Perform the platform specific actions + * as per the old state of the cluster e.g. enabling + * coherency at the interconnect depends upon the state with + * which this cluster was powered up. If anything goes wrong + * then assert as there is no way to recover from this + * situation. + */ + + /* Get the physical state of this cpu */ + plat_state = psci_get_phys_state(cluster_node); + psci_plat_pm_ops->affinst_suspend_finish(cluster_node->level, + plat_state); +} + + +static void psci_afflvl2_suspend_finish(aff_map_node_t *system_node) +{ + unsigned int plat_state; + + /* Cannot go beyond this affinity level */ + assert(system_node->level == MPIDR_AFFLVL2); + + /* + * Currently, there are no architectural actions to perform + * at the system level. + */ + + /* + * Plat. management: Perform the platform specific actions + * as per the old state of the cluster e.g. enabling + * coherency at the interconnect depends upon the state with + * which this cluster was powered up. If anything goes wrong + * then assert as there is no way to recover from this + * situation. + */ + + /* Get the physical state of the system */ + plat_state = psci_get_phys_state(system_node); + psci_plat_pm_ops->affinst_suspend_finish(system_node->level, + plat_state); +} + +const afflvl_power_on_finisher_t psci_afflvl_suspend_finishers[] = { + psci_afflvl0_suspend_finish, + psci_afflvl1_suspend_finish, + psci_afflvl2_suspend_finish, +}; diff --git a/services/std_svc/psci1.0/psci_common.c b/services/std_svc/psci1.0/psci_common.c new file mode 100644 index 0000000000..1b74ff2c5c --- /dev/null +++ b/services/std_svc/psci1.0/psci_common.c @@ -0,0 +1,648 @@ +/* + * Copyright (c) 2013-2014, 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 +#include +#include +#include +#include +#include "psci_private.h" + +/* + * SPD power management operations, expected to be supplied by the registered + * SPD on successful SP initialization + */ +const spd_pm_ops_t *psci_spd_pm; + +/******************************************************************************* + * Grand array that holds the platform's topology information for state + * management of affinity instances. Each node (aff_map_node) in the array + * corresponds to an affinity instance e.g. cluster, cpu within an mpidr + ******************************************************************************/ +aff_map_node_t psci_aff_map[PSCI_NUM_AFFS] +#if USE_COHERENT_MEM +__attribute__ ((section("tzfw_coherent_mem"))) +#endif +; + +/******************************************************************************* + * Pointer to functions exported by the platform to complete power mgmt. ops + ******************************************************************************/ +const plat_pm_ops_t *psci_plat_pm_ops; + +/******************************************************************************* + * Check that the maximum affinity level supported by the platform makes sense + * ****************************************************************************/ +CASSERT(PLATFORM_MAX_AFFLVL <= MPIDR_MAX_AFFLVL && \ + PLATFORM_MAX_AFFLVL >= MPIDR_AFFLVL0, \ + assert_platform_max_afflvl_check); + +/******************************************************************************* + * This function is passed an array of pointers to affinity level nodes in the + * topology tree for an mpidr. It iterates through the nodes to find the highest + * affinity level which is marked as physically powered off. + ******************************************************************************/ +uint32_t psci_find_max_phys_off_afflvl(uint32_t start_afflvl, + uint32_t end_afflvl, + aff_map_node_t *mpidr_nodes[]) +{ + uint32_t max_afflvl = PSCI_INVALID_DATA; + + for (; start_afflvl <= end_afflvl; start_afflvl++) { + if (mpidr_nodes[start_afflvl] == NULL) + continue; + + if (psci_get_phys_state(mpidr_nodes[start_afflvl]) == + PSCI_STATE_OFF) + max_afflvl = start_afflvl; + } + + return max_afflvl; +} + +/******************************************************************************* + * This function verifies that the all the other cores in the system have been + * turned OFF and the current CPU is the last running CPU in the system. + * Returns 1 (true) if the current CPU is the last ON CPU or 0 (false) + * otherwise. + ******************************************************************************/ +unsigned int psci_is_last_on_cpu(void) +{ + unsigned long mpidr = read_mpidr_el1() & MPIDR_AFFINITY_MASK; + unsigned int i; + + for (i = psci_aff_limits[MPIDR_AFFLVL0].min; + i <= psci_aff_limits[MPIDR_AFFLVL0].max; i++) { + + assert(psci_aff_map[i].level == MPIDR_AFFLVL0); + + if (!(psci_aff_map[i].state & PSCI_AFF_PRESENT)) + continue; + + if (psci_aff_map[i].mpidr == mpidr) { + assert(psci_get_state(&psci_aff_map[i]) + == PSCI_STATE_ON); + continue; + } + + if (psci_get_state(&psci_aff_map[i]) != PSCI_STATE_OFF) + return 0; + } + + return 1; +} + +/******************************************************************************* + * This function saves the highest affinity level which is in OFF state. The + * affinity instance with which the level is associated is determined by the + * caller. + ******************************************************************************/ +void psci_set_max_phys_off_afflvl(uint32_t afflvl) +{ + set_cpu_data(psci_svc_cpu_data.max_phys_off_afflvl, afflvl); + + /* + * Ensure that the saved value is flushed to main memory and any + * speculatively pre-fetched stale copies are invalidated from the + * caches of other cpus in the same coherency domain. This ensures that + * the value can be safely read irrespective of the state of the data + * cache. + */ + flush_cpu_data(psci_svc_cpu_data.max_phys_off_afflvl); +} + +/******************************************************************************* + * This function reads the saved highest affinity level which is in OFF + * state. The affinity instance with which the level is associated is determined + * by the caller. + ******************************************************************************/ +uint32_t psci_get_max_phys_off_afflvl(void) +{ + /* + * Ensure that the last update of this value in this cpu's cache is + * flushed to main memory and any speculatively pre-fetched stale copies + * are invalidated from the caches of other cpus in the same coherency + * domain. This ensures that the value is always read from the main + * memory when it was written before the data cache was enabled. + */ + flush_cpu_data(psci_svc_cpu_data.max_phys_off_afflvl); + return get_cpu_data(psci_svc_cpu_data.max_phys_off_afflvl); +} + +/******************************************************************************* + * Routine to return the maximum affinity level to traverse to after a cpu has + * been physically powered up. It is expected to be called immediately after + * reset from assembler code. + ******************************************************************************/ +int get_power_on_target_afflvl(void) +{ + int afflvl; + +#if DEBUG + unsigned int state; + aff_map_node_t *node; + + /* Retrieve our node from the topology tree */ + node = psci_get_aff_map_node(read_mpidr_el1() & MPIDR_AFFINITY_MASK, + MPIDR_AFFLVL0); + assert(node); + + /* + * Sanity check the state of the cpu. It should be either suspend or "on + * pending" + */ + state = psci_get_state(node); + assert(state == PSCI_STATE_SUSPEND || state == PSCI_STATE_ON_PENDING); +#endif + + /* + * Assume that this cpu was suspended and retrieve its target affinity + * level. If it is invalid then it could only have been turned off + * earlier. PLATFORM_MAX_AFFLVL will be the highest affinity level a + * cpu can be turned off to. + */ + afflvl = psci_get_suspend_afflvl(); + if (afflvl == PSCI_INVALID_DATA) + afflvl = PLATFORM_MAX_AFFLVL; + return afflvl; +} + +/******************************************************************************* + * Simple routine to set the id of an affinity instance at a given level in the + * mpidr. + ******************************************************************************/ +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 affinity level + */ + aff_shift = get_afflvl_shift(aff_lvl); + + /* Clear the existing affinity instance & set the new one*/ + mpidr &= ~(((unsigned long)MPIDR_AFFLVL_MASK) << aff_shift); + mpidr |= ((unsigned long)aff_inst) << aff_shift; + + return mpidr; +} + +/******************************************************************************* + * This function sanity checks a range of affinity levels. + ******************************************************************************/ +int psci_check_afflvl_range(int start_afflvl, int end_afflvl) +{ + /* Sanity check the parameters passed */ + if (end_afflvl > PLATFORM_MAX_AFFLVL) + return PSCI_E_INVALID_PARAMS; + + if (start_afflvl < MPIDR_AFFLVL0) + return PSCI_E_INVALID_PARAMS; + + if (end_afflvl < start_afflvl) + return PSCI_E_INVALID_PARAMS; + + return PSCI_E_SUCCESS; +} + +/******************************************************************************* + * This function is passed an array of pointers to affinity level nodes in the + * topology tree for an mpidr and the state which each node should transition + * to. It updates the state of each node between the specified affinity levels. + ******************************************************************************/ +void psci_do_afflvl_state_mgmt(uint32_t start_afflvl, + uint32_t end_afflvl, + aff_map_node_t *mpidr_nodes[], + uint32_t state) +{ + uint32_t level; + + for (level = start_afflvl; level <= end_afflvl; level++) { + if (mpidr_nodes[level] == NULL) + continue; + psci_set_state(mpidr_nodes[level], state); + } +} + +/******************************************************************************* + * This function is passed an array of pointers to affinity level nodes in the + * topology tree for an mpidr. It picks up locks for each affinity level bottom + * up in the range specified. + ******************************************************************************/ +void psci_acquire_afflvl_locks(int start_afflvl, + int end_afflvl, + aff_map_node_t *mpidr_nodes[]) +{ + int level; + + for (level = start_afflvl; level <= end_afflvl; level++) { + if (mpidr_nodes[level] == NULL) + continue; + + psci_lock_get(mpidr_nodes[level]); + } +} + +/******************************************************************************* + * This function is passed an array of pointers to affinity level nodes in the + * topology tree for an mpidr. It releases the lock for each affinity level top + * down in the range specified. + ******************************************************************************/ +void psci_release_afflvl_locks(int start_afflvl, + int end_afflvl, + aff_map_node_t *mpidr_nodes[]) +{ + int level; + + for (level = end_afflvl; level >= start_afflvl; level--) { + if (mpidr_nodes[level] == NULL) + continue; + + psci_lock_release(mpidr_nodes[level]); + } +} + +/******************************************************************************* + * Simple routine to determine whether an affinity instance at a given level + * in an mpidr exists or not. + ******************************************************************************/ +int psci_validate_mpidr(unsigned long mpidr, int level) +{ + aff_map_node_t *node; + + node = psci_get_aff_map_node(mpidr, level); + if (node && (node->state & PSCI_AFF_PRESENT)) + return PSCI_E_SUCCESS; + else + return PSCI_E_INVALID_PARAMS; +} + +/******************************************************************************* + * This function determines the full entrypoint information for the requested + * PSCI entrypoint on power on/resume and returns it. + ******************************************************************************/ +int psci_get_ns_ep_info(entry_point_info_t *ep, + uint64_t entrypoint, uint64_t context_id) +{ + uint32_t ep_attr, mode, sctlr, daif, ee; + uint32_t ns_scr_el3 = read_scr_el3(); + uint32_t ns_sctlr_el1 = read_sctlr_el1(); + + sctlr = ns_scr_el3 & SCR_HCE_BIT ? read_sctlr_el2() : ns_sctlr_el1; + ee = 0; + + ep_attr = NON_SECURE | EP_ST_DISABLE; + if (sctlr & SCTLR_EE_BIT) { + ep_attr |= EP_EE_BIG; + ee = 1; + } + SET_PARAM_HEAD(ep, PARAM_EP, VERSION_1, ep_attr); + + ep->pc = entrypoint; + memset(&ep->args, 0, sizeof(ep->args)); + ep->args.arg0 = context_id; + + /* + * Figure out whether the cpu enters the non-secure address space + * in aarch32 or aarch64 + */ + if (ns_scr_el3 & SCR_RW_BIT) { + + /* + * Check whether a Thumb entry point has been provided for an + * aarch64 EL + */ + if (entrypoint & 0x1) + return PSCI_E_INVALID_PARAMS; + + mode = ns_scr_el3 & SCR_HCE_BIT ? MODE_EL2 : MODE_EL1; + + ep->spsr = SPSR_64(mode, MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS); + } else { + + mode = ns_scr_el3 & SCR_HCE_BIT ? MODE32_hyp : MODE32_svc; + + /* + * TODO: Choose async. exception bits if HYP mode is not + * implemented according to the values of SCR.{AW, FW} bits + */ + daif = DAIF_ABT_BIT | DAIF_IRQ_BIT | DAIF_FIQ_BIT; + + ep->spsr = SPSR_MODE32(mode, entrypoint & 0x1, ee, daif); + } + + return PSCI_E_SUCCESS; +} + +/******************************************************************************* + * This function takes a pointer to an affinity node in the topology tree and + * returns its state. State of a non-leaf node needs to be calculated. + ******************************************************************************/ +unsigned short psci_get_state(aff_map_node_t *node) +{ +#if !USE_COHERENT_MEM + flush_dcache_range((uint64_t) node, sizeof(*node)); +#endif + + assert(node->level >= MPIDR_AFFLVL0 && node->level <= MPIDR_MAX_AFFLVL); + + /* A cpu node just contains the state which can be directly returned */ + if (node->level == MPIDR_AFFLVL0) + return (node->state >> PSCI_STATE_SHIFT) & PSCI_STATE_MASK; + + /* + * For an affinity level higher than a cpu, the state has to be + * calculated. It depends upon the value of the reference count + * which is managed by each node at the next lower affinity level + * e.g. for a cluster, each cpu increments/decrements the reference + * count. If the reference count is 0 then the affinity level is + * OFF else ON. + */ + if (node->ref_count) + return PSCI_STATE_ON; + else + return PSCI_STATE_OFF; +} + +/******************************************************************************* + * This function takes a pointer to an affinity node in the topology tree and + * a target state. State of a non-leaf node needs to be converted to a reference + * count. State of a leaf node can be set directly. + ******************************************************************************/ +void psci_set_state(aff_map_node_t *node, unsigned short state) +{ + assert(node->level >= MPIDR_AFFLVL0 && node->level <= MPIDR_MAX_AFFLVL); + + /* + * For an affinity level higher than a cpu, the state is used + * to decide whether the reference count is incremented or + * decremented. Entry into the ON_PENDING state does not have + * effect. + */ + if (node->level > MPIDR_AFFLVL0) { + switch (state) { + case PSCI_STATE_ON: + node->ref_count++; + break; + case PSCI_STATE_OFF: + case PSCI_STATE_SUSPEND: + node->ref_count--; + break; + case PSCI_STATE_ON_PENDING: + /* + * An affinity level higher than a cpu will not undergo + * a state change when it is about to be turned on + */ + return; + default: + assert(0); + } + } else { + node->state &= ~(PSCI_STATE_MASK << PSCI_STATE_SHIFT); + node->state |= (state & PSCI_STATE_MASK) << PSCI_STATE_SHIFT; + } + +#if !USE_COHERENT_MEM + flush_dcache_range((uint64_t) node, sizeof(*node)); +#endif +} + +/******************************************************************************* + * An affinity level could be on, on_pending, suspended or off. These are the + * logical states it can be in. Physically either it is off or on. When it is in + * the state on_pending then it is about to be turned on. It is not possible to + * tell whether that's actually happenned or not. So we err on the side of + * caution & treat the affinity level as being turned off. + ******************************************************************************/ +unsigned short psci_get_phys_state(aff_map_node_t *node) +{ + unsigned int state; + + state = psci_get_state(node); + return get_phys_state(state); +} + +/******************************************************************************* + * This function takes an array of pointers to affinity instance nodes in the + * topology tree and calls the physical power on handler for the corresponding + * affinity levels + ******************************************************************************/ +static void psci_call_power_on_handlers(aff_map_node_t *mpidr_nodes[], + int start_afflvl, + int end_afflvl, + afflvl_power_on_finisher_t *pon_handlers) +{ + int level; + aff_map_node_t *node; + + for (level = end_afflvl; level >= start_afflvl; level--) { + node = mpidr_nodes[level]; + if (node == NULL) + continue; + + /* + * If we run into any trouble while powering up an + * affinity instance, then there is no recovery path + * so simply return an error and let the caller take + * care of the situation. + */ + pon_handlers[level](node); + } +} + +/******************************************************************************* + * Generic handler which is called when a cpu is physically powered on. It + * traverses through all the affinity levels performing generic, architectural, + * platform setup and state management e.g. for a cluster that's been powered + * on, it will call the platform specific code which will enable coherency at + * the interconnect level. For a cpu it could mean turning on the MMU etc. + * + * The state of all the relevant affinity levels is changed after calling the + * affinity level specific handlers as their actions would depend upon the state + * the affinity level is exiting from. + * + * The affinity level specific handlers are called in descending order i.e. from + * the highest to the lowest affinity level implemented by the platform because + * to turn on affinity level X it is neccesary to turn on affinity level X + 1 + * first. + ******************************************************************************/ +void psci_afflvl_power_on_finish(int start_afflvl, + int end_afflvl, + afflvl_power_on_finisher_t *pon_handlers) +{ + mpidr_aff_map_nodes_t mpidr_nodes; + int rc; + unsigned int max_phys_off_afflvl; + + + /* + * Collect the pointers to the nodes in the topology tree for + * each affinity instance in the mpidr. If this function does + * not return successfully then either the mpidr or the affinity + * levels are incorrect. Either case is an irrecoverable error. + */ + rc = psci_get_aff_map_nodes(read_mpidr_el1() & MPIDR_AFFINITY_MASK, + start_afflvl, + end_afflvl, + mpidr_nodes); + if (rc != PSCI_E_SUCCESS) + panic(); + + /* + * This function acquires the lock corresponding to each affinity + * level so that by the time all locks are taken, the system topology + * is snapshot and state management can be done safely. + */ + psci_acquire_afflvl_locks(start_afflvl, + end_afflvl, + mpidr_nodes); + + max_phys_off_afflvl = psci_find_max_phys_off_afflvl(start_afflvl, + end_afflvl, + mpidr_nodes); + assert(max_phys_off_afflvl != PSCI_INVALID_DATA); + + /* + * Stash the highest affinity level that will come out of the OFF or + * SUSPEND states. + */ + psci_set_max_phys_off_afflvl(max_phys_off_afflvl); + + /* Perform generic, architecture and platform specific handling */ + psci_call_power_on_handlers(mpidr_nodes, + start_afflvl, + end_afflvl, + pon_handlers); + + /* + * This function updates the state of each affinity instance + * corresponding to the mpidr in the range of affinity levels + * specified. + */ + psci_do_afflvl_state_mgmt(start_afflvl, + end_afflvl, + mpidr_nodes, + PSCI_STATE_ON); + + /* + * Invalidate the entry for the highest affinity level stashed earlier. + * This ensures that any reads of this variable outside the power + * up/down sequences return PSCI_INVALID_DATA + */ + psci_set_max_phys_off_afflvl(PSCI_INVALID_DATA); + + /* + * This loop releases the lock corresponding to each affinity level + * in the reverse order to which they were acquired. + */ + psci_release_afflvl_locks(start_afflvl, + end_afflvl, + mpidr_nodes); +} + +/******************************************************************************* + * This function initializes the set of hooks that PSCI invokes as part of power + * management operation. The power management hooks are expected to be provided + * by the SPD, after it finishes all its initialization + ******************************************************************************/ +void psci_register_spd_pm_hook(const spd_pm_ops_t *pm) +{ + assert(pm); + psci_spd_pm = pm; + + if (pm->svc_migrate) + psci_caps |= define_psci_cap(PSCI_MIG_AARCH64); + + if (pm->svc_migrate_info) + psci_caps |= define_psci_cap(PSCI_MIG_INFO_UP_CPU_AARCH64) + | define_psci_cap(PSCI_MIG_INFO_TYPE); +} + +/******************************************************************************* + * This function invokes the migrate info hook in the spd_pm_ops. It performs + * the necessary return value validation. If the Secure Payload is UP and + * migrate capable, it returns the mpidr of the CPU on which the Secure payload + * is resident through the mpidr parameter. Else the value of the parameter on + * return is undefined. + ******************************************************************************/ +int psci_spd_migrate_info(uint64_t *mpidr) +{ + int rc; + + if (!psci_spd_pm || !psci_spd_pm->svc_migrate_info) + return PSCI_E_NOT_SUPPORTED; + + rc = psci_spd_pm->svc_migrate_info(mpidr); + + assert(rc == PSCI_TOS_UP_MIG_CAP || rc == PSCI_TOS_NOT_UP_MIG_CAP \ + || rc == PSCI_TOS_NOT_PRESENT_MP || rc == PSCI_E_NOT_SUPPORTED); + + return rc; +} + + +/******************************************************************************* + * This function prints the state of all affinity instances present in the + * system + ******************************************************************************/ +void psci_print_affinity_map(void) +{ +#if LOG_LEVEL >= LOG_LEVEL_INFO + aff_map_node_t *node; + unsigned int idx; + /* This array maps to the PSCI_STATE_X definitions in psci.h */ + static const char *psci_state_str[] = { + "ON", + "OFF", + "ON_PENDING", + "SUSPEND" + }; + + INFO("PSCI Affinity Map:\n"); + for (idx = 0; idx < PSCI_NUM_AFFS ; idx++) { + node = &psci_aff_map[idx]; + if (!(node->state & PSCI_AFF_PRESENT)) { + continue; + } + INFO(" AffInst: Level %u, MPID 0x%lx, State %s\n", + node->level, node->mpidr, + psci_state_str[psci_get_state(node)]); + } +#endif +} diff --git a/services/std_svc/psci1.0/psci_entry.S b/services/std_svc/psci1.0/psci_entry.S new file mode 100644 index 0000000000..050f6c6c6d --- /dev/null +++ b/services/std_svc/psci1.0/psci_entry.S @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2013-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 + + .globl psci_aff_on_finish_entry + .globl psci_aff_suspend_finish_entry + .globl psci_power_down_wfi + + /* ----------------------------------------------------- + * This cpu has been physically powered up. Depending + * upon whether it was resumed from suspend or simply + * turned on, call the common power on finisher with + * the handlers (chosen depending upon original state). + * ----------------------------------------------------- + */ +func psci_aff_on_finish_entry + adr x23, psci_afflvl_on_finishers + b psci_aff_common_finish_entry + +psci_aff_suspend_finish_entry: + adr x23, psci_afflvl_suspend_finishers + +psci_aff_common_finish_entry: + /* + * On the warm boot path, most of the EL3 initialisations performed by + * 'el3_entrypoint_common' must be skipped: + * + * - Only when the platform bypasses the BL1/BL3-1 entrypoint by + * programming the reset address do we need to set the CPU endianness. + * In other cases, we assume this has been taken care by the + * entrypoint code. + * + * - No need to determine the type of boot, we know it is a warm boot. + * + * - Do not try to distinguish between primary and secondary CPUs, this + * notion only exists for a cold boot. + * + * - No need to initialise the memory or the C runtime environment, + * it has been done once and for all on the cold boot path. + */ + el3_entrypoint_common \ + _set_endian=PROGRAMMABLE_RESET_ADDRESS \ + _warm_boot_mailbox=0 \ + _secondary_cold_boot=0 \ + _init_memory=0 \ + _init_c_runtime=0 \ + _exception_vectors=runtime_exceptions + + /* -------------------------------------------- + * Enable the MMU with the DCache disabled. It + * is safe to use stacks allocated in normal + * memory as a result. All memory accesses are + * marked nGnRnE when the MMU is disabled. So + * all the stack writes will make it to memory. + * All memory accesses are marked Non-cacheable + * when the MMU is enabled but D$ is disabled. + * So used stack memory is guaranteed to be + * visible immediately after the MMU is enabled + * Enabling the DCache at the same time as the + * MMU can lead to speculatively fetched and + * possibly stale stack memory being read from + * other caches. This can lead to coherency + * issues. + * -------------------------------------------- + */ + mov x0, #DISABLE_DCACHE + bl bl31_plat_enable_mmu + + /* --------------------------------------------- + * Call the finishers starting from affinity + * level 0. + * --------------------------------------------- + */ + bl get_power_on_target_afflvl + mov x2, x23 + mov x1, x0 + mov x0, #MPIDR_AFFLVL0 + bl psci_afflvl_power_on_finish + + b el3_exit +endfunc psci_aff_on_finish_entry + + /* -------------------------------------------- + * This function is called to indicate to the + * power controller that it is safe to power + * down this cpu. It should not exit the wfi + * and will be released from reset upon power + * up. 'wfi_spill' is used to catch erroneous + * exits from wfi. + * -------------------------------------------- + */ +func psci_power_down_wfi + dsb sy // ensure write buffer empty + wfi +wfi_spill: + b wfi_spill +endfunc psci_power_down_wfi + diff --git a/services/std_svc/psci1.0/psci_helpers.S b/services/std_svc/psci1.0/psci_helpers.S new file mode 100644 index 0000000000..1d99158e12 --- /dev/null +++ b/services/std_svc/psci1.0/psci_helpers.S @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2014, 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 + + .globl psci_do_pwrdown_cache_maintenance + .globl psci_do_pwrup_cache_maintenance + +/* ----------------------------------------------------------------------- + * void psci_do_pwrdown_cache_maintenance(uint32_t affinity level); + * + * This function performs cache maintenance if the specified affinity + * level is the equal to the level of the highest affinity instance which + * will be/is physically powered off. The levels of cache affected are + * determined by the affinity level which is passed as the argument i.e. + * level 0 results in a flush of the L1 cache. Both the L1 and L2 caches + * are flushed for a higher affinity level. + * + * Additionally, this function also ensures that stack memory is correctly + * flushed out to avoid coherency issues due to a change in its memory + * attributes after the data cache is disabled. + * ----------------------------------------------------------------------- + */ +func psci_do_pwrdown_cache_maintenance + stp x29, x30, [sp,#-16]! + stp x19, x20, [sp,#-16]! + + mov x19, x0 + bl psci_get_max_phys_off_afflvl +#if ASM_ASSERTION + cmp x0, #PSCI_INVALID_DATA + ASM_ASSERT(ne) +#endif + cmp x0, x19 + b.ne 1f + + /* --------------------------------------------- + * Determine to how many levels of cache will be + * subject to cache maintenance. Affinity level + * 0 implies that only the cpu is being powered + * down. Only the L1 data cache needs to be + * flushed to the PoU in this case. For a higher + * affinity level we are assuming that a flush + * of L1 data and L2 unified cache is enough. + * This information should be provided by the + * platform. + * --------------------------------------------- + */ + cmp x0, #MPIDR_AFFLVL0 + b.eq do_core_pwr_dwn + bl prepare_cluster_pwr_dwn + b do_stack_maintenance + +do_core_pwr_dwn: + bl prepare_core_pwr_dwn + + /* --------------------------------------------- + * Do stack maintenance by flushing the used + * stack to the main memory and invalidating the + * remainder. + * --------------------------------------------- + */ +do_stack_maintenance: + mrs x0, mpidr_el1 + bl platform_get_stack + + /* --------------------------------------------- + * Calculate and store the size of the used + * stack memory in x1. + * --------------------------------------------- + */ + mov x19, x0 + mov x1, sp + sub x1, x0, x1 + mov x0, sp + bl flush_dcache_range + + /* --------------------------------------------- + * Calculate and store the size of the unused + * stack memory in x1. Calculate and store the + * stack base address in x0. + * --------------------------------------------- + */ + sub x0, x19, #PLATFORM_STACK_SIZE + sub x1, sp, x0 + bl inv_dcache_range + +1: + ldp x19, x20, [sp], #16 + ldp x29, x30, [sp], #16 + ret +endfunc psci_do_pwrdown_cache_maintenance + + +/* ----------------------------------------------------------------------- + * void psci_do_pwrup_cache_maintenance(void); + * + * This function performs cache maintenance after this cpu is powered up. + * Currently, this involves managing the used stack memory before turning + * on the data cache. + * ----------------------------------------------------------------------- + */ +func psci_do_pwrup_cache_maintenance + stp x29, x30, [sp,#-16]! + + /* --------------------------------------------- + * Ensure any inflight stack writes have made it + * to main memory. + * --------------------------------------------- + */ + dmb st + + /* --------------------------------------------- + * Calculate and store the size of the used + * stack memory in x1. Calculate and store the + * stack base address in x0. + * --------------------------------------------- + */ + mrs x0, mpidr_el1 + bl platform_get_stack + mov x1, sp + sub x1, x0, x1 + mov x0, sp + bl inv_dcache_range + + /* --------------------------------------------- + * Enable the data cache. + * --------------------------------------------- + */ + mrs x0, sctlr_el3 + orr x0, x0, #SCTLR_C_BIT + msr sctlr_el3, x0 + isb + + ldp x29, x30, [sp], #16 + ret +endfunc psci_do_pwrup_cache_maintenance diff --git a/services/std_svc/psci1.0/psci_main.c b/services/std_svc/psci1.0/psci_main.c new file mode 100644 index 0000000000..b389287b00 --- /dev/null +++ b/services/std_svc/psci1.0/psci_main.c @@ -0,0 +1,463 @@ +/* + * Copyright (c) 2013-2014, 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 +#include +#include +#include "psci_private.h" + +/******************************************************************************* + * PSCI frontend api for servicing SMCs. Described in the PSCI spec. + ******************************************************************************/ +int psci_cpu_on(unsigned long target_cpu, + unsigned long entrypoint, + unsigned long context_id) + +{ + int rc; + unsigned int start_afflvl, end_afflvl; + entry_point_info_t ep; + + /* Determine if the cpu exists of not */ + rc = psci_validate_mpidr(target_cpu, MPIDR_AFFLVL0); + if (rc != PSCI_E_SUCCESS) { + return PSCI_E_INVALID_PARAMS; + } + + /* Validate the entrypoint using platform pm_ops */ + if (psci_plat_pm_ops->validate_ns_entrypoint) { + rc = psci_plat_pm_ops->validate_ns_entrypoint(entrypoint); + if (rc != PSCI_E_SUCCESS) { + assert(rc == PSCI_E_INVALID_PARAMS); + return PSCI_E_INVALID_PARAMS; + } + } + + /* + * Verify and derive the re-entry information for + * the non-secure world from the non-secure state from + * where this call originated. + */ + rc = psci_get_ns_ep_info(&ep, entrypoint, context_id); + if (rc != PSCI_E_SUCCESS) + return rc; + + + /* + * To turn this cpu on, specify which affinity + * levels need to be turned on + */ + start_afflvl = MPIDR_AFFLVL0; + end_afflvl = PLATFORM_MAX_AFFLVL; + rc = psci_afflvl_on(target_cpu, + &ep, + start_afflvl, + end_afflvl); + + return rc; +} + +unsigned int psci_version(void) +{ + return PSCI_MAJOR_VER | PSCI_MINOR_VER; +} + +int psci_cpu_suspend(unsigned int power_state, + unsigned long entrypoint, + unsigned long context_id) +{ + int rc; + unsigned int target_afflvl, pstate_type; + entry_point_info_t ep; + + /* Check SBZ bits in power state are zero */ + if (psci_validate_power_state(power_state)) + return PSCI_E_INVALID_PARAMS; + + /* Sanity check the requested state */ + target_afflvl = psci_get_pstate_afflvl(power_state); + if (target_afflvl > PLATFORM_MAX_AFFLVL) + return PSCI_E_INVALID_PARAMS; + + /* Validate the power_state using platform pm_ops */ + if (psci_plat_pm_ops->validate_power_state) { + rc = psci_plat_pm_ops->validate_power_state(power_state); + if (rc != PSCI_E_SUCCESS) { + assert(rc == PSCI_E_INVALID_PARAMS); + return PSCI_E_INVALID_PARAMS; + } + } + + /* Validate the entrypoint using platform pm_ops */ + if (psci_plat_pm_ops->validate_ns_entrypoint) { + rc = psci_plat_pm_ops->validate_ns_entrypoint(entrypoint); + if (rc != PSCI_E_SUCCESS) { + assert(rc == PSCI_E_INVALID_PARAMS); + return PSCI_E_INVALID_PARAMS; + } + } + + /* Determine the 'state type' in the 'power_state' parameter */ + pstate_type = psci_get_pstate_type(power_state); + + /* + * Ensure that we have a platform specific handler for entering + * a standby state. + */ + if (pstate_type == PSTATE_TYPE_STANDBY) { + if (!psci_plat_pm_ops->affinst_standby) + return PSCI_E_INVALID_PARAMS; + + psci_plat_pm_ops->affinst_standby(power_state); + return PSCI_E_SUCCESS; + } + + /* + * Verify and derive the re-entry information for + * the non-secure world from the non-secure state from + * where this call originated. + */ + rc = psci_get_ns_ep_info(&ep, entrypoint, context_id); + if (rc != PSCI_E_SUCCESS) + return rc; + + /* Save PSCI power state parameter for the core in suspend context */ + psci_set_suspend_power_state(power_state); + + /* + * Do what is needed to enter the power down state. Upon success, + * enter the final wfi which will power down this CPU. + */ + psci_afflvl_suspend(&ep, + MPIDR_AFFLVL0, + target_afflvl); + + /* Reset PSCI power state parameter for the core. */ + psci_set_suspend_power_state(PSCI_INVALID_DATA); + return PSCI_E_SUCCESS; +} + +int psci_system_suspend(unsigned long entrypoint, + unsigned long context_id) +{ + int rc; + unsigned int power_state; + entry_point_info_t ep; + + /* Validate the entrypoint using platform pm_ops */ + if (psci_plat_pm_ops->validate_ns_entrypoint) { + rc = psci_plat_pm_ops->validate_ns_entrypoint(entrypoint); + if (rc != PSCI_E_SUCCESS) { + assert(rc == PSCI_E_INVALID_PARAMS); + return PSCI_E_INVALID_PARAMS; + } + } + + /* Check if the current CPU is the last ON CPU in the system */ + if (!psci_is_last_on_cpu()) + return PSCI_E_DENIED; + + /* + * Verify and derive the re-entry information for + * the non-secure world from the non-secure state from + * where this call originated. + */ + rc = psci_get_ns_ep_info(&ep, entrypoint, context_id); + if (rc != PSCI_E_SUCCESS) + return rc; + + /* + * Assert that the required pm_ops hook is implemented to ensure that + * the capability detected during psci_setup() is valid. + */ + assert(psci_plat_pm_ops->get_sys_suspend_power_state); + + /* + * Query the platform for the power_state required for system suspend + */ + power_state = psci_plat_pm_ops->get_sys_suspend_power_state(); + + /* Save PSCI power state parameter for the core in suspend context */ + psci_set_suspend_power_state(power_state); + + /* + * Do what is needed to enter the power down state. Upon success, + * enter the final wfi which will power down this cpu. + */ + psci_afflvl_suspend(&ep, + MPIDR_AFFLVL0, + PLATFORM_MAX_AFFLVL); + + /* Reset PSCI power state parameter for the core. */ + psci_set_suspend_power_state(PSCI_INVALID_DATA); + return PSCI_E_SUCCESS; +} + +int psci_cpu_off(void) +{ + int rc; + int target_afflvl = PLATFORM_MAX_AFFLVL; + + /* + * Traverse from the highest to the lowest affinity level. When the + * lowest affinity level is hit, all the locks are acquired. State + * management is done immediately followed by cpu, cluster ... + * ..target_afflvl specific actions as this function unwinds back. + */ + rc = psci_afflvl_off(MPIDR_AFFLVL0, target_afflvl); + + /* + * The only error cpu_off can return is E_DENIED. So check if that's + * indeed the case. + */ + assert (rc == PSCI_E_DENIED); + + return rc; +} + +int psci_affinity_info(unsigned long target_affinity, + unsigned int lowest_affinity_level) +{ + int rc = PSCI_E_INVALID_PARAMS; + unsigned int aff_state; + aff_map_node_t *node; + + if (lowest_affinity_level > PLATFORM_MAX_AFFLVL) + return rc; + + node = psci_get_aff_map_node(target_affinity, lowest_affinity_level); + if (node && (node->state & PSCI_AFF_PRESENT)) { + + /* + * TODO: For affinity levels higher than 0 i.e. cpu, the + * state will always be either ON or OFF. Need to investigate + * how critical is it to support ON_PENDING here. + */ + aff_state = psci_get_state(node); + + /* A suspended cpu is available & on for the OS */ + if (aff_state == PSCI_STATE_SUSPEND) { + aff_state = PSCI_STATE_ON; + } + + rc = aff_state; + } + + return rc; +} + +int psci_migrate(unsigned long target_cpu) +{ + int rc; + unsigned long resident_cpu_mpidr; + + rc = psci_spd_migrate_info(&resident_cpu_mpidr); + if (rc != PSCI_TOS_UP_MIG_CAP) + return (rc == PSCI_TOS_NOT_UP_MIG_CAP) ? + PSCI_E_DENIED : PSCI_E_NOT_SUPPORTED; + + /* + * Migrate should only be invoked on the CPU where + * the Secure OS is resident. + */ + if (resident_cpu_mpidr != read_mpidr_el1()) + return PSCI_E_NOT_PRESENT; + + /* Check the validity of the specified target cpu */ + rc = psci_validate_mpidr(target_cpu, MPIDR_AFFLVL0); + if (rc != PSCI_E_SUCCESS) + return PSCI_E_INVALID_PARAMS; + + assert(psci_spd_pm && psci_spd_pm->svc_migrate); + + rc = psci_spd_pm->svc_migrate(read_mpidr_el1(), target_cpu); + assert(rc == PSCI_E_SUCCESS || rc == PSCI_E_INTERN_FAIL); + + return rc; +} + +int psci_migrate_info_type(void) +{ + unsigned long resident_cpu_mpidr; + + return psci_spd_migrate_info(&resident_cpu_mpidr); +} + +long psci_migrate_info_up_cpu(void) +{ + unsigned long resident_cpu_mpidr; + int rc; + + /* + * Return value of this depends upon what + * psci_spd_migrate_info() returns. + */ + rc = psci_spd_migrate_info(&resident_cpu_mpidr); + if (rc != PSCI_TOS_NOT_UP_MIG_CAP && rc != PSCI_TOS_UP_MIG_CAP) + return PSCI_E_INVALID_PARAMS; + + return resident_cpu_mpidr; +} + +int psci_features(unsigned int psci_fid) +{ + uint32_t local_caps = psci_caps; + + /* Check if it is a 64 bit function */ + if (((psci_fid >> FUNCID_CC_SHIFT) & FUNCID_CC_MASK) == SMC_64) + local_caps &= PSCI_CAP_64BIT_MASK; + + /* Check for invalid fid */ + if (!(is_std_svc_call(psci_fid) && is_valid_fast_smc(psci_fid) + && is_psci_fid(psci_fid))) + return PSCI_E_NOT_SUPPORTED; + + + /* Check if the psci fid is supported or not */ + if (!(local_caps & define_psci_cap(psci_fid))) + return PSCI_E_NOT_SUPPORTED; + + /* Format the feature flags */ + if (psci_fid == PSCI_CPU_SUSPEND_AARCH32 || + psci_fid == PSCI_CPU_SUSPEND_AARCH64) { + /* + * The trusted firmware uses the original power state format + * and does not support OS Initiated Mode. + */ + return (FF_PSTATE_ORIG << FF_PSTATE_SHIFT) | + ((!FF_SUPPORTS_OS_INIT_MODE) << FF_MODE_SUPPORT_SHIFT); + } + + /* Return 0 for all other fid's */ + return PSCI_E_SUCCESS; +} + +/******************************************************************************* + * PSCI top level handler for servicing SMCs. + ******************************************************************************/ +uint64_t psci_smc_handler(uint32_t smc_fid, + uint64_t x1, + uint64_t x2, + uint64_t x3, + uint64_t x4, + void *cookie, + void *handle, + uint64_t flags) +{ + if (is_caller_secure(flags)) + SMC_RET1(handle, SMC_UNK); + + /* Check the fid against the capabilities */ + if (!(psci_caps & define_psci_cap(smc_fid))) + SMC_RET1(handle, SMC_UNK); + + if (((smc_fid >> FUNCID_CC_SHIFT) & FUNCID_CC_MASK) == SMC_32) { + /* 32-bit PSCI function, clear top parameter bits */ + + x1 = (uint32_t)x1; + x2 = (uint32_t)x2; + x3 = (uint32_t)x3; + + switch (smc_fid) { + case PSCI_VERSION: + SMC_RET1(handle, psci_version()); + + case PSCI_CPU_OFF: + SMC_RET1(handle, psci_cpu_off()); + + case PSCI_CPU_SUSPEND_AARCH32: + SMC_RET1(handle, psci_cpu_suspend(x1, x2, x3)); + + case PSCI_CPU_ON_AARCH32: + SMC_RET1(handle, psci_cpu_on(x1, x2, x3)); + + case PSCI_AFFINITY_INFO_AARCH32: + SMC_RET1(handle, psci_affinity_info(x1, x2)); + + case PSCI_MIG_AARCH32: + SMC_RET1(handle, psci_migrate(x1)); + + case PSCI_MIG_INFO_TYPE: + SMC_RET1(handle, psci_migrate_info_type()); + + case PSCI_MIG_INFO_UP_CPU_AARCH32: + SMC_RET1(handle, psci_migrate_info_up_cpu()); + + case PSCI_SYSTEM_SUSPEND_AARCH32: + SMC_RET1(handle, psci_system_suspend(x1, x2)); + + case PSCI_SYSTEM_OFF: + psci_system_off(); + /* We should never return from psci_system_off() */ + + case PSCI_SYSTEM_RESET: + psci_system_reset(); + /* We should never return from psci_system_reset() */ + + case PSCI_FEATURES: + SMC_RET1(handle, psci_features(x1)); + + default: + break; + } + } else { + /* 64-bit PSCI function */ + + switch (smc_fid) { + case PSCI_CPU_SUSPEND_AARCH64: + SMC_RET1(handle, psci_cpu_suspend(x1, x2, x3)); + + case PSCI_CPU_ON_AARCH64: + SMC_RET1(handle, psci_cpu_on(x1, x2, x3)); + + case PSCI_AFFINITY_INFO_AARCH64: + SMC_RET1(handle, psci_affinity_info(x1, x2)); + + case PSCI_MIG_AARCH64: + SMC_RET1(handle, psci_migrate(x1)); + + case PSCI_MIG_INFO_UP_CPU_AARCH64: + SMC_RET1(handle, psci_migrate_info_up_cpu()); + + case PSCI_SYSTEM_SUSPEND_AARCH64: + SMC_RET1(handle, psci_system_suspend(x1, x2)); + + default: + break; + } + } + + WARN("Unimplemented PSCI Call: 0x%x \n", smc_fid); + SMC_RET1(handle, SMC_UNK); +} diff --git a/services/std_svc/psci1.0/psci_private.h b/services/std_svc/psci1.0/psci_private.h new file mode 100644 index 0000000000..2955de7a0f --- /dev/null +++ b/services/std_svc/psci1.0/psci_private.h @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2013-2014, 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_PRIVATE_H__ +#define __PSCI_PRIVATE_H__ + +#include +#include +#include +#include + +/* + * The following helper macros abstract the interface to the Bakery + * Lock API. + */ +#if USE_COHERENT_MEM +#define psci_lock_init(aff_map, idx) bakery_lock_init(&(aff_map)[(idx)].lock) +#define psci_lock_get(node) bakery_lock_get(&((node)->lock)) +#define psci_lock_release(node) bakery_lock_release(&((node)->lock)) +#else +#define psci_lock_init(aff_map, idx) ((aff_map)[(idx)].aff_map_index = (idx)) +#define psci_lock_get(node) bakery_lock_get((node)->aff_map_index, \ + CPU_DATA_PSCI_LOCK_OFFSET) +#define psci_lock_release(node) bakery_lock_release((node)->aff_map_index,\ + CPU_DATA_PSCI_LOCK_OFFSET) +#endif + +/* + * The PSCI capability which are provided by the generic code but does not + * depend on the platform or spd capabilities. + */ +#define PSCI_GENERIC_CAP \ + (define_psci_cap(PSCI_VERSION) | \ + define_psci_cap(PSCI_AFFINITY_INFO_AARCH64) | \ + define_psci_cap(PSCI_FEATURES)) + +/* + * The PSCI capabilities mask for 64 bit functions. + */ +#define PSCI_CAP_64BIT_MASK \ + (define_psci_cap(PSCI_CPU_SUSPEND_AARCH64) | \ + define_psci_cap(PSCI_CPU_ON_AARCH64) | \ + define_psci_cap(PSCI_AFFINITY_INFO_AARCH64) | \ + define_psci_cap(PSCI_MIG_AARCH64) | \ + define_psci_cap(PSCI_MIG_INFO_UP_CPU_AARCH64) | \ + define_psci_cap(PSCI_SYSTEM_SUSPEND_AARCH64)) + + +/******************************************************************************* + * The following two data structures hold the topology tree which in turn tracks + * the state of the all the affinity instances supported by the platform. + ******************************************************************************/ +typedef struct aff_map_node { + unsigned long mpidr; + unsigned char ref_count; + unsigned char state; + unsigned char level; +#if USE_COHERENT_MEM + bakery_lock_t lock; +#else + /* For indexing the bakery_info array in per CPU data */ + unsigned char aff_map_index; +#endif +} aff_map_node_t; + +typedef struct aff_limits_node { + int min; + int max; +} aff_limits_node_t; + +typedef aff_map_node_t (*mpidr_aff_map_nodes_t[MPIDR_MAX_AFFLVL + 1]); +typedef void (*afflvl_power_on_finisher_t)(aff_map_node_t *); + +/******************************************************************************* + * Data prototypes + ******************************************************************************/ +extern const plat_pm_ops_t *psci_plat_pm_ops; +extern aff_map_node_t psci_aff_map[PSCI_NUM_AFFS]; +extern aff_limits_node_t psci_aff_limits[MPIDR_MAX_AFFLVL + 1]; +extern uint32_t psci_caps; + +/******************************************************************************* + * SPD's power management hooks registered with PSCI + ******************************************************************************/ +extern const spd_pm_ops_t *psci_spd_pm; + +/******************************************************************************* + * Function prototypes + ******************************************************************************/ +/* Private exported functions from psci_common.c */ +unsigned short psci_get_state(aff_map_node_t *node); +unsigned short psci_get_phys_state(aff_map_node_t *node); +void psci_set_state(aff_map_node_t *node, unsigned short state); +unsigned long mpidr_set_aff_inst(unsigned long, unsigned char, int); +int psci_validate_mpidr(unsigned long, int); +int get_power_on_target_afflvl(void); +void psci_afflvl_power_on_finish(int, + int, + afflvl_power_on_finisher_t *); +int psci_get_ns_ep_info(entry_point_info_t *ep, + uint64_t entrypoint, uint64_t context_id); +int psci_check_afflvl_range(int start_afflvl, int end_afflvl); +void psci_do_afflvl_state_mgmt(uint32_t start_afflvl, + uint32_t end_afflvl, + aff_map_node_t *mpidr_nodes[], + uint32_t state); +void psci_acquire_afflvl_locks(int start_afflvl, + int end_afflvl, + aff_map_node_t *mpidr_nodes[]); +void psci_release_afflvl_locks(int start_afflvl, + int end_afflvl, + mpidr_aff_map_nodes_t mpidr_nodes); +void psci_print_affinity_map(void); +void psci_set_max_phys_off_afflvl(uint32_t afflvl); +uint32_t psci_find_max_phys_off_afflvl(uint32_t start_afflvl, + uint32_t end_afflvl, + aff_map_node_t *mpidr_nodes[]); +unsigned int psci_is_last_on_cpu(void); +int psci_spd_migrate_info(uint64_t *mpidr); + +/* Private exported functions from psci_setup.c */ +int psci_get_aff_map_nodes(unsigned long mpidr, + int start_afflvl, + int end_afflvl, + aff_map_node_t *mpidr_nodes[]); +aff_map_node_t *psci_get_aff_map_node(unsigned long, int); + +/* Private exported functions from psci_affinity_on.c */ +int psci_afflvl_on(unsigned long target_cpu, + entry_point_info_t *ep, + int start_afflvl, + int end_afflvl); + +/* Private exported functions from psci_affinity_off.c */ +int psci_afflvl_off(int, int); + +/* Private exported functions from psci_affinity_suspend.c */ +void psci_afflvl_suspend(entry_point_info_t *ep, + int start_afflvl, + int end_afflvl); + +unsigned int psci_afflvl_suspend_finish(int, int); +void psci_set_suspend_power_state(unsigned int power_state); + +/* Private exported functions from psci_helpers.S */ +void psci_do_pwrdown_cache_maintenance(uint32_t affinity_level); +void psci_do_pwrup_cache_maintenance(void); + +/* Private exported functions from psci_system_off.c */ +void __dead2 psci_system_off(void); +void __dead2 psci_system_reset(void); + +#endif /* __PSCI_PRIVATE_H__ */ diff --git a/services/std_svc/psci1.0/psci_setup.c b/services/std_svc/psci1.0/psci_setup.c new file mode 100644 index 0000000000..01b559cf38 --- /dev/null +++ b/services/std_svc/psci1.0/psci_setup.c @@ -0,0 +1,400 @@ +/* + * Copyright (c) 2013-2014, 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 +#include +#include +#include +#include "psci_private.h" + +/******************************************************************************* + * Per cpu non-secure contexts used to program the architectural state prior + * return to the normal world. + * TODO: Use the memory allocator to set aside memory for the contexts instead + * of relying on platform defined constants. Using PSCI_NUM_AFFS will be an + * overkill. + ******************************************************************************/ +static cpu_context_t psci_ns_context[PLATFORM_CORE_COUNT]; + +/******************************************************************************* + * In a system, a certain number of affinity instances are present at an + * affinity level. The cumulative number of instances across all levels are + * stored in 'psci_aff_map'. The topology tree has been flattenned into this + * array. To retrieve nodes, information about the extents of each affinity + * level i.e. start index and end index needs to be present. 'psci_aff_limits' + * stores this information. + ******************************************************************************/ +aff_limits_node_t psci_aff_limits[MPIDR_MAX_AFFLVL + 1]; + +/****************************************************************************** + * Define the psci capability variable. + *****************************************************************************/ +uint32_t psci_caps; + + +/******************************************************************************* + * Routines for retrieving the node corresponding to an affinity level instance + * in the mpidr. The first one uses binary search to find the node corresponding + * to the mpidr (key) at a particular affinity level. The second routine decides + * extents of the binary search at each affinity level. + ******************************************************************************/ +static int psci_aff_map_get_idx(unsigned long key, + int min_idx, + int max_idx) +{ + int mid; + + /* + * Terminating condition: If the max and min indices have crossed paths + * during the binary search then the key has not been found. + */ + if (max_idx < min_idx) + return PSCI_E_INVALID_PARAMS; + + /* + * Make sure we are within array limits. + */ + assert(min_idx >= 0 && max_idx < PSCI_NUM_AFFS); + + /* + * Bisect the array around 'mid' and then recurse into the array chunk + * where the key is likely to be found. The mpidrs in each node in the + * 'psci_aff_map' for a given affinity level are stored in an ascending + * order which makes the binary search possible. + */ + mid = min_idx + ((max_idx - min_idx) >> 1); /* Divide by 2 */ + + if (psci_aff_map[mid].mpidr > key) + return psci_aff_map_get_idx(key, min_idx, mid - 1); + else if (psci_aff_map[mid].mpidr < key) + return psci_aff_map_get_idx(key, mid + 1, max_idx); + else + return mid; +} + +aff_map_node_t *psci_get_aff_map_node(unsigned long mpidr, int aff_lvl) +{ + int rc; + + if (aff_lvl > PLATFORM_MAX_AFFLVL) + return NULL; + + /* Right shift the mpidr to the required affinity level */ + mpidr = mpidr_mask_lower_afflvls(mpidr, aff_lvl); + + rc = psci_aff_map_get_idx(mpidr, + psci_aff_limits[aff_lvl].min, + psci_aff_limits[aff_lvl].max); + if (rc >= 0) + return &psci_aff_map[rc]; + else + return NULL; +} + +/******************************************************************************* + * This function populates an array with nodes corresponding to a given range of + * affinity levels in an mpidr. It returns successfully only when the affinity + * levels are correct, the mpidr is valid i.e. no affinity level is absent from + * the topology tree & the affinity instance at level 0 is not absent. + ******************************************************************************/ +int psci_get_aff_map_nodes(unsigned long mpidr, + int start_afflvl, + int end_afflvl, + aff_map_node_t *mpidr_nodes[]) +{ + int rc = PSCI_E_INVALID_PARAMS, level; + aff_map_node_t *node; + + rc = psci_check_afflvl_range(start_afflvl, end_afflvl); + if (rc != PSCI_E_SUCCESS) + return rc; + + for (level = start_afflvl; level <= end_afflvl; level++) { + + /* + * Grab the node for each affinity level. No affinity level + * can be missing as that would mean that the topology tree + * is corrupted. + */ + node = psci_get_aff_map_node(mpidr, level); + if (node == NULL) { + rc = PSCI_E_INVALID_PARAMS; + break; + } + + /* + * Skip absent affinity levels unless it's afffinity level 0. + * An absent cpu means that the mpidr is invalid. Save the + * pointer to the node for the present affinity level + */ + if (!(node->state & PSCI_AFF_PRESENT)) { + if (level == MPIDR_AFFLVL0) { + rc = PSCI_E_INVALID_PARAMS; + break; + } + + mpidr_nodes[level] = NULL; + } else + mpidr_nodes[level] = node; + } + + return rc; +} + +/******************************************************************************* + * Function which initializes the 'aff_map_node' corresponding to an affinity + * level instance. Each node has a unique mpidr, level and bakery lock. The data + * field is opaque and holds affinity level specific data e.g. for affinity + * level 0 it contains the index into arrays that hold the secure/non-secure + * state for a cpu that's been turned on/off + ******************************************************************************/ +static void psci_init_aff_map_node(unsigned long mpidr, + int level, + unsigned int idx) +{ + unsigned char state; + uint32_t linear_id; + psci_aff_map[idx].mpidr = mpidr; + psci_aff_map[idx].level = level; + psci_lock_init(psci_aff_map, idx); + + /* + * If an affinity instance is present then mark it as OFF to begin with. + */ + state = plat_get_aff_state(level, mpidr); + psci_aff_map[idx].state = state; + + if (level == MPIDR_AFFLVL0) { + + /* + * Mark the cpu as OFF. Higher affinity level reference counts + * have already been memset to 0 + */ + if (state & PSCI_AFF_PRESENT) + psci_set_state(&psci_aff_map[idx], PSCI_STATE_OFF); + + /* + * Associate a non-secure context with this affinity + * instance through the context management library. + */ + linear_id = platform_get_core_pos(mpidr); + assert(linear_id < PLATFORM_CORE_COUNT); + + /* Invalidate the suspend context for the node */ + set_cpu_data_by_index(linear_id, + psci_svc_cpu_data.power_state, + PSCI_INVALID_DATA); + + /* + * There is no state associated with the current execution + * context so ensure that any reads of the highest affinity + * level in a powered down state return PSCI_INVALID_DATA. + */ + set_cpu_data_by_index(linear_id, + psci_svc_cpu_data.max_phys_off_afflvl, + PSCI_INVALID_DATA); + + flush_cpu_data_by_index(linear_id, psci_svc_cpu_data); + + cm_set_context_by_mpidr(mpidr, + (void *) &psci_ns_context[linear_id], + NON_SECURE); + } + + return; +} + +/******************************************************************************* + * Core routine used by the Breadth-First-Search algorithm to populate the + * affinity tree. Each level in the tree corresponds to an affinity level. This + * routine's aim is to traverse to the target affinity level and populate nodes + * in the 'psci_aff_map' for all the siblings at that level. 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. + * + * 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 psci_init_aff_map(unsigned long mpidr, + unsigned int affmap_idx, + int cur_afflvl, + int tgt_afflvl) +{ + unsigned int ctr, aff_count; + + assert(cur_afflvl >= tgt_afflvl); + + /* + * Find the number of siblings at the current affinity 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 = mpidr_set_aff_inst(mpidr, ctr, cur_afflvl); + affmap_idx = psci_init_aff_map(mpidr, + affmap_idx, + cur_afflvl - 1, + tgt_afflvl); + } + } else { + for (ctr = 0; ctr < aff_count; ctr++, affmap_idx++) { + mpidr = mpidr_set_aff_inst(mpidr, ctr, cur_afflvl); + psci_init_aff_map_node(mpidr, cur_afflvl, affmap_idx); + } + + /* affmap_idx is 1 greater than the max index of cur_afflvl */ + psci_aff_limits[cur_afflvl].max = affmap_idx - 1; + } + + return affmap_idx; +} + +/******************************************************************************* + * This function initializes the topology tree by querying the platform. To do + * so, it's helper routines implement a Breadth-First-Search. At each affinity + * level the platform conveys the number of affinity instances that exist i.e. + * the affinity count. The algorithm populates the psci_aff_map recursively + * using this information. On a platform that implements two clusters of 4 cpus + * each, the populated aff_map_array would look like this: + * + * <- cpus cluster0 -><- cpus cluster1 -> + * --------------------------------------------------- + * | 0 | 1 | 0 | 1 | 2 | 3 | 0 | 1 | 2 | 3 | + * --------------------------------------------------- + * ^ ^ + * cluster __| cpu __| + * limit limit + * + * The first 2 entries are of the cluster nodes. The next 4 entries are of cpus + * within cluster 0. The last 4 entries are of cpus within cluster 1. + * The 'psci_aff_limits' array contains the max & min index of each affinity + * level within the 'psci_aff_map' array. This allows restricting search of a + * node at an affinity level between the indices in the limits array. + ******************************************************************************/ +int32_t psci_setup(void) +{ + unsigned long mpidr = read_mpidr(); + int afflvl, affmap_idx, max_afflvl; + aff_map_node_t *node; + + psci_plat_pm_ops = NULL; + + /* Find out the maximum affinity level that the platform implements */ + max_afflvl = PLATFORM_MAX_AFFLVL; + assert(max_afflvl <= MPIDR_MAX_AFFLVL); + + /* + * This call traverses the topology tree with help from the platform and + * populates the affinity map using a breadth-first-search recursively. + * 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 = max_afflvl; afflvl >= MPIDR_AFFLVL0; afflvl--) { + affmap_idx = psci_init_aff_map(FIRST_MPIDR, + affmap_idx, + max_afflvl, + afflvl); + } + +#if !USE_COHERENT_MEM + /* + * The psci_aff_map only needs flushing when it's not allocated in + * coherent memory. + */ + flush_dcache_range((uint64_t) &psci_aff_map, sizeof(psci_aff_map)); +#endif + + /* + * Set the bounds for the affinity counts of each level in the map. Also + * flush out the entire array so that it's visible to subsequent power + * management operations. The 'psci_aff_limits' array is allocated in + * normal memory. It will be accessed when the mmu is off e.g. after + * reset. Hence it needs to be flushed. + */ + for (afflvl = MPIDR_AFFLVL0; afflvl < max_afflvl; afflvl++) { + psci_aff_limits[afflvl].min = + psci_aff_limits[afflvl + 1].max + 1; + } + + flush_dcache_range((unsigned long) psci_aff_limits, + sizeof(psci_aff_limits)); + + /* + * Mark the affinity instances in our mpidr as ON. No need to lock as + * this is the primary cpu. + */ + mpidr &= MPIDR_AFFINITY_MASK; + for (afflvl = MPIDR_AFFLVL0; afflvl <= max_afflvl; afflvl++) { + + node = psci_get_aff_map_node(mpidr, afflvl); + assert(node); + + /* Mark each present node as ON. */ + if (node->state & PSCI_AFF_PRESENT) + psci_set_state(node, PSCI_STATE_ON); + } + + platform_setup_pm(&psci_plat_pm_ops); + assert(psci_plat_pm_ops); + + /* Initialize the psci capability */ + psci_caps = PSCI_GENERIC_CAP; + + if (psci_plat_pm_ops->affinst_off) + psci_caps |= define_psci_cap(PSCI_CPU_OFF); + if (psci_plat_pm_ops->affinst_on && psci_plat_pm_ops->affinst_on_finish) + psci_caps |= define_psci_cap(PSCI_CPU_ON_AARCH64); + if (psci_plat_pm_ops->affinst_suspend && + psci_plat_pm_ops->affinst_suspend_finish) { + psci_caps |= define_psci_cap(PSCI_CPU_SUSPEND_AARCH64); + if (psci_plat_pm_ops->get_sys_suspend_power_state) + psci_caps |= define_psci_cap(PSCI_SYSTEM_SUSPEND_AARCH64); + } + if (psci_plat_pm_ops->system_off) + psci_caps |= define_psci_cap(PSCI_SYSTEM_OFF); + if (psci_plat_pm_ops->system_reset) + psci_caps |= define_psci_cap(PSCI_SYSTEM_RESET); + + return 0; +} diff --git a/services/std_svc/psci1.0/psci_system_off.c b/services/std_svc/psci1.0/psci_system_off.c new file mode 100644 index 0000000000..970d4bb501 --- /dev/null +++ b/services/std_svc/psci1.0/psci_system_off.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2014, 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 +#include "psci_private.h" + +void psci_system_off(void) +{ + psci_print_affinity_map(); + + assert(psci_plat_pm_ops->system_off); + + /* Notify the Secure Payload Dispatcher */ + if (psci_spd_pm && psci_spd_pm->svc_system_off) { + psci_spd_pm->svc_system_off(); + } + + /* Call the platform specific hook */ + psci_plat_pm_ops->system_off(); + + /* This function does not return. We should never get here */ +} + +void psci_system_reset(void) +{ + psci_print_affinity_map(); + + assert(psci_plat_pm_ops->system_reset); + + /* Notify the Secure Payload Dispatcher */ + if (psci_spd_pm && psci_spd_pm->svc_system_reset) { + psci_spd_pm->svc_system_reset(); + } + + /* Call the platform specific hook */ + psci_plat_pm_ops->system_reset(); + + /* This function does not return. We should never get here */ +} From 6590ce22955357d5d6f2bce13f84a1189e3b8d0f Mon Sep 17 00:00:00 2001 From: Soby Mathew Date: Tue, 30 Jun 2015 11:00:24 +0100 Subject: [PATCH 02/20] PSCI: Invoke PM hooks only for the highest level This patch optimizes the invocation of the platform power management hooks for ON, OFF and SUSPEND such that they are called only for the highest affinity level which will be powered off/on. Earlier, the hooks were being invoked for all the intermediate levels as well. This patch requires that the platforms migrate to the new semantics of the PM hooks. It also removes the `state` parameter from the pm hooks as the `afflvl` parameter now indicates the highest affinity level for which power management operations are required. Change-Id: I57c87931d8a2723aeade14acc710e5b78ac41732 --- include/bl31/services/psci1.0/psci.h | 16 +- services/std_svc/psci1.0/psci_afflvl_off.c | 155 ++--------- services/std_svc/psci1.0/psci_afflvl_on.c | 251 +++-------------- .../std_svc/psci1.0/psci_afflvl_suspend.c | 263 +++--------------- services/std_svc/psci1.0/psci_common.c | 118 ++------ services/std_svc/psci1.0/psci_entry.S | 13 +- services/std_svc/psci1.0/psci_helpers.S | 10 - services/std_svc/psci1.0/psci_main.c | 9 +- services/std_svc/psci1.0/psci_private.h | 18 +- services/std_svc/psci1.0/psci_setup.c | 9 - 10 files changed, 127 insertions(+), 735 deletions(-) diff --git a/include/bl31/services/psci1.0/psci.h b/include/bl31/services/psci1.0/psci.h index dd1891c6fe..905fc8ab0b 100644 --- a/include/bl31/services/psci1.0/psci.h +++ b/include/bl31/services/psci1.0/psci.h @@ -171,8 +171,6 @@ ******************************************************************************/ typedef struct psci_cpu_data { uint32_t power_state; - uint32_t max_phys_off_afflvl; /* Highest affinity level in physically - powered off state */ #if !USE_COHERENT_MEM bakery_info_t pcpu_bakery_info[PSCI_NUM_AFFS]; #endif @@ -186,15 +184,12 @@ 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); + unsigned int afflvl); + void (*affinst_off)(unsigned int afflvl); 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); + unsigned int afflvl); + void (*affinst_on_finish)(unsigned int afflvl); + void (*affinst_suspend_finish)(unsigned int afflvl); void (*system_off)(void) __dead2; void (*system_reset)(void) __dead2; int (*validate_power_state)(unsigned int power_state); @@ -238,7 +233,6 @@ void psci_register_spd_pm_hook(const spd_pm_ops_t *); int psci_get_suspend_stateid_by_mpidr(unsigned long); int psci_get_suspend_stateid(void); int psci_get_suspend_afflvl(void); -uint32_t psci_get_max_phys_off_afflvl(void); uint64_t psci_smc_handler(uint32_t smc_fid, uint64_t x1, diff --git a/services/std_svc/psci1.0/psci_afflvl_off.c b/services/std_svc/psci1.0/psci_afflvl_off.c index 7eb968899e..b96682253d 100644 --- a/services/std_svc/psci1.0/psci_afflvl_off.c +++ b/services/std_svc/psci1.0/psci_afflvl_off.c @@ -35,122 +35,19 @@ #include #include "psci_private.h" -typedef void (*afflvl_off_handler_t)(aff_map_node_t *node); - -/******************************************************************************* - * The next three functions implement a handler for each supported affinity - * level which is called when that affinity level is turned off. - ******************************************************************************/ -static void psci_afflvl0_off(aff_map_node_t *cpu_node) -{ - assert(cpu_node->level == MPIDR_AFFLVL0); - - /* - * Arch. management. Perform the necessary steps to flush all - * cpu caches. - */ - psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL0); - - /* - * Plat. management: Perform platform specific actions to turn this - * cpu off e.g. exit cpu coherency, program the power controller etc. - */ - psci_plat_pm_ops->affinst_off(cpu_node->level, - psci_get_phys_state(cpu_node)); -} - -static void psci_afflvl1_off(aff_map_node_t *cluster_node) -{ - /* Sanity check the cluster level */ - assert(cluster_node->level == MPIDR_AFFLVL1); - - /* - * Arch. Management. Flush all levels of caches to PoC if - * the cluster is to be shutdown. - */ - psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL1); - - /* - * Plat. Management. Allow the platform to do its cluster - * specific bookeeping e.g. turn off interconnect coherency, - * program the power controller etc. - */ - psci_plat_pm_ops->affinst_off(cluster_node->level, - psci_get_phys_state(cluster_node)); -} - -static void psci_afflvl2_off(aff_map_node_t *system_node) -{ - /* Cannot go beyond this level */ - assert(system_node->level == MPIDR_AFFLVL2); - - /* - * Keep the physical state of the system handy to decide what - * action needs to be taken - */ - - /* - * Arch. Management. Flush all levels of caches to PoC if - * the system is to be shutdown. - */ - psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL2); - - /* - * Plat. Management : Allow the platform to do its bookeeping - * at this affinity level - */ - psci_plat_pm_ops->affinst_off(system_node->level, - psci_get_phys_state(system_node)); -} - -static const afflvl_off_handler_t psci_afflvl_off_handlers[] = { - psci_afflvl0_off, - psci_afflvl1_off, - psci_afflvl2_off, -}; - -/******************************************************************************* - * This function takes an array of pointers to affinity instance nodes in the - * topology tree and calls the off handler for the corresponding affinity - * levels - ******************************************************************************/ -static void psci_call_off_handlers(aff_map_node_t *mpidr_nodes[], - int start_afflvl, - int end_afflvl) -{ - int level; - aff_map_node_t *node; - - for (level = start_afflvl; level <= end_afflvl; level++) { - node = mpidr_nodes[level]; - if (node == NULL) - continue; - - psci_afflvl_off_handlers[level](node); - } -} - -/******************************************************************************* +/****************************************************************************** * Top level handler which is called when a cpu wants to power itself down. - * It's assumed that along with turning the cpu off, higher affinity levels will - * be turned off as far as possible. It traverses through all the affinity - * levels performing generic, architectural, platform setup and state management - * e.g. for a cluster that's to be powered off, it will call the platform - * specific code which will disable coherency at the interconnect level if the - * cpu is the last in the cluster. For a cpu it could mean programming the power - * the power controller etc. - * - * The state of all the relevant affinity levels is changed prior to calling the - * affinity level specific handlers as their actions would depend upon the state - * the affinity level is about to enter. - * - * The affinity level specific handlers are called in ascending order i.e. from - * the lowest to the highest affinity level implemented by the platform because - * to turn off affinity level X it is neccesary to turn off affinity level X - 1 - * first. + * It's assumed that along with turning the cpu off, higher affinity levels + * will be turned off as far as possible. It finds the highest level to be + * powered off by traversing the node information and then performs generic, + * architectural, platform setup and state management required to turn OFF + * that affinity level and affinity levels below it. e.g. For a cpu that's to + * be powered OFF, it could mean programming the power controller whereas for + * a cluster that's to be powered off, it will call the platform specific code + * which will disable coherency at the interconnect level if the cpu is the + * last in the cluster and also the program the power controller. ******************************************************************************/ -int psci_afflvl_off(int start_afflvl, - int end_afflvl) +int psci_afflvl_off(int end_afflvl) { int rc; mpidr_aff_map_nodes_t mpidr_nodes; @@ -170,7 +67,7 @@ int psci_afflvl_off(int start_afflvl, * therefore assert. */ rc = psci_get_aff_map_nodes(read_mpidr_el1() & MPIDR_AFFINITY_MASK, - start_afflvl, + MPIDR_AFFLVL0, end_afflvl, mpidr_nodes); assert(rc == PSCI_E_SUCCESS); @@ -180,7 +77,7 @@ int psci_afflvl_off(int start_afflvl, * level so that by the time all locks are taken, the system topology * is snapshot and state management can be done safely. */ - psci_acquire_afflvl_locks(start_afflvl, + psci_acquire_afflvl_locks(MPIDR_AFFLVL0, end_afflvl, mpidr_nodes); @@ -201,38 +98,34 @@ int psci_afflvl_off(int start_afflvl, * corresponding to the mpidr in the range of affinity levels * specified. */ - psci_do_afflvl_state_mgmt(start_afflvl, + psci_do_afflvl_state_mgmt(MPIDR_AFFLVL0, end_afflvl, mpidr_nodes, PSCI_STATE_OFF); - max_phys_off_afflvl = psci_find_max_phys_off_afflvl(start_afflvl, + max_phys_off_afflvl = psci_find_max_phys_off_afflvl(MPIDR_AFFLVL0, end_afflvl, mpidr_nodes); assert(max_phys_off_afflvl != PSCI_INVALID_DATA); - /* Stash the highest affinity level that will enter the OFF state. */ - psci_set_max_phys_off_afflvl(max_phys_off_afflvl); - - /* Perform generic, architecture and platform specific handling */ - psci_call_off_handlers(mpidr_nodes, - start_afflvl, - end_afflvl); + /* + * Arch. management. Perform the necessary steps to flush all + * cpu caches. + */ + psci_do_pwrdown_cache_maintenance(max_phys_off_afflvl); /* - * Invalidate the entry for the highest affinity level stashed earlier. - * This ensures that any reads of this variable outside the power - * up/down sequences return PSCI_INVALID_DATA. - * + * Plat. management: Perform platform specific actions to turn this + * cpu off e.g. exit cpu coherency, program the power controller etc. */ - psci_set_max_phys_off_afflvl(PSCI_INVALID_DATA); + psci_plat_pm_ops->affinst_off(max_phys_off_afflvl); exit: /* * Release the locks corresponding to each affinity level in the * reverse order to which they were acquired. */ - psci_release_afflvl_locks(start_afflvl, + psci_release_afflvl_locks(MPIDR_AFFLVL0, end_afflvl, mpidr_nodes); diff --git a/services/std_svc/psci1.0/psci_afflvl_on.c b/services/std_svc/psci1.0/psci_afflvl_on.c index 0dbd0e0608..61003dc03d 100644 --- a/services/std_svc/psci1.0/psci_afflvl_on.c +++ b/services/std_svc/psci1.0/psci_afflvl_on.c @@ -40,9 +40,6 @@ #include #include "psci_private.h" -typedef int (*afflvl_on_handler_t)(unsigned long target_cpu, - aff_map_node_t *node); - /******************************************************************************* * This function checks whether a cpu which has been requested to be turned on * is OFF to begin with. @@ -59,158 +56,23 @@ static int cpu_on_validate_state(unsigned int psci_state) return PSCI_E_SUCCESS; } -/******************************************************************************* - * Handler routine to turn a cpu on. It takes care of any generic, architectural - * or platform specific setup required. - * TODO: Split this code across separate handlers for each type of setup? - ******************************************************************************/ -static int psci_afflvl0_on(unsigned long target_cpu, - aff_map_node_t *cpu_node) -{ - unsigned long psci_entrypoint; - - /* Sanity check to safeguard against data corruption */ - assert(cpu_node->level == MPIDR_AFFLVL0); - - /* Set the secure world (EL3) re-entry point after BL1 */ - psci_entrypoint = (unsigned long) psci_aff_on_finish_entry; - - /* - * Plat. management: Give the platform the current state - * of the target cpu to allow it to perform the necessary - * steps to power on. - */ - return psci_plat_pm_ops->affinst_on(target_cpu, - psci_entrypoint, - cpu_node->level, - psci_get_phys_state(cpu_node)); -} - -/******************************************************************************* - * Handler routine to turn a cluster on. It takes care or any generic, arch. - * or platform specific setup required. - * TODO: Split this code across separate handlers for each type of setup? - ******************************************************************************/ -static int psci_afflvl1_on(unsigned long target_cpu, - aff_map_node_t *cluster_node) -{ - unsigned long psci_entrypoint; - - assert(cluster_node->level == MPIDR_AFFLVL1); - - /* - * There is no generic and arch. specific cluster - * management required - */ - - /* State management: Is not required while turning a cluster on */ - - /* - * Plat. management: Give the platform the current state - * of the target cpu to allow it to perform the necessary - * steps to power on. - */ - psci_entrypoint = (unsigned long) psci_aff_on_finish_entry; - return psci_plat_pm_ops->affinst_on(target_cpu, - psci_entrypoint, - cluster_node->level, - psci_get_phys_state(cluster_node)); -} - -/******************************************************************************* - * Handler routine to turn a cluster of clusters on. It takes care or any - * generic, arch. or platform specific setup required. - * TODO: Split this code across separate handlers for each type of setup? - ******************************************************************************/ -static int psci_afflvl2_on(unsigned long target_cpu, - aff_map_node_t *system_node) -{ - unsigned long psci_entrypoint; - - /* Cannot go beyond affinity level 2 in this psci imp. */ - assert(system_node->level == MPIDR_AFFLVL2); - - /* - * There is no generic and arch. specific system management - * required - */ - - /* State management: Is not required while turning a system on */ - - /* - * Plat. management: Give the platform the current state - * of the target cpu to allow it to perform the necessary - * steps to power on. - */ - psci_entrypoint = (unsigned long) psci_aff_on_finish_entry; - return psci_plat_pm_ops->affinst_on(target_cpu, - psci_entrypoint, - system_node->level, - psci_get_phys_state(system_node)); -} - -/* Private data structure to make this handlers accessible through indexing */ -static const afflvl_on_handler_t psci_afflvl_on_handlers[] = { - psci_afflvl0_on, - psci_afflvl1_on, - psci_afflvl2_on, -}; - -/******************************************************************************* - * This function takes an array of pointers to affinity instance nodes in the - * topology tree and calls the on handler for the corresponding affinity - * levels - ******************************************************************************/ -static int psci_call_on_handlers(aff_map_node_t *target_cpu_nodes[], - int start_afflvl, - int end_afflvl, - unsigned long target_cpu) -{ - int rc = PSCI_E_INVALID_PARAMS, level; - aff_map_node_t *node; - - for (level = end_afflvl; level >= start_afflvl; level--) { - node = target_cpu_nodes[level]; - if (node == NULL) - continue; - - /* - * TODO: In case of an error should there be a way - * of undoing what we might have setup at higher - * affinity levels. - */ - rc = psci_afflvl_on_handlers[level](target_cpu, - node); - if (rc != PSCI_E_SUCCESS) - break; - } - - return rc; -} - /******************************************************************************* * Generic handler which is called to physically power on a cpu identified by - * its mpidr. It traverses through all the affinity levels performing generic, - * architectural, platform setup and state management e.g. for a cpu that is - * to be powered on, it will ensure that enough information is stashed for it - * to resume execution in the non-secure security state. + * its mpidr. It performs the generic, architectural, platform setup and state + * management to power on the target cpu e.g. it will ensure that + * enough information is stashed for it to resume execution in the non-secure + * security state. * * The state of all the relevant affinity levels is changed after calling the - * affinity level specific handlers as their actions would depend upon the state - * the affinity level is currently in. - * - * The affinity level specific handlers are called in descending order i.e. from - * the highest to the lowest affinity level implemented by the platform because - * to turn on affinity level X it is necessary to turn on affinity level X + 1 - * first. + * platform handler as it can return error. ******************************************************************************/ int psci_afflvl_on(unsigned long target_cpu, entry_point_info_t *ep, - int start_afflvl, int end_afflvl) { int rc; mpidr_aff_map_nodes_t target_cpu_nodes; + unsigned long psci_entrypoint; /* * This function must only be called on platforms where the @@ -226,7 +88,7 @@ int psci_afflvl_on(unsigned long target_cpu, * levels are incorrect. */ rc = psci_get_aff_map_nodes(target_cpu, - start_afflvl, + MPIDR_AFFLVL0, end_afflvl, target_cpu_nodes); assert(rc == PSCI_E_SUCCESS); @@ -236,7 +98,7 @@ int psci_afflvl_on(unsigned long target_cpu, * level so that by the time all locks are taken, the system topology * is snapshot and state management can be done safely. */ - psci_acquire_afflvl_locks(start_afflvl, + psci_acquire_afflvl_locks(MPIDR_AFFLVL0, end_afflvl, target_cpu_nodes); @@ -262,17 +124,25 @@ int psci_afflvl_on(unsigned long target_cpu, * corresponding to the mpidr in the range of affinity levels * specified. */ - psci_do_afflvl_state_mgmt(start_afflvl, - end_afflvl, - target_cpu_nodes, - PSCI_STATE_ON_PENDING); - - /* Perform generic, architecture and platform specific handling. */ - rc = psci_call_on_handlers(target_cpu_nodes, - start_afflvl, + psci_do_afflvl_state_mgmt(MPIDR_AFFLVL0, end_afflvl, - target_cpu); + target_cpu_nodes, + PSCI_STATE_ON_PENDING); + /* + * Perform generic, architecture and platform specific handling. + */ + /* Set the secure world (EL3) re-entry point after BL1 */ + psci_entrypoint = (unsigned long) psci_aff_on_finish_entry; + + /* + * Plat. management: Give the platform the current state + * of the target cpu to allow it to perform the necessary + * steps to power on. + */ + rc = psci_plat_pm_ops->affinst_on(target_cpu, + psci_entrypoint, + MPIDR_AFFLVL0); assert(rc == PSCI_E_SUCCESS || rc == PSCI_E_INTERN_FAIL); if (rc == PSCI_E_SUCCESS) @@ -280,7 +150,7 @@ int psci_afflvl_on(unsigned long target_cpu, cm_init_context(target_cpu, ep); else /* Restore the state on error. */ - psci_do_afflvl_state_mgmt(start_afflvl, + psci_do_afflvl_state_mgmt(MPIDR_AFFLVL0, end_afflvl, target_cpu_nodes, PSCI_STATE_OFF); @@ -289,7 +159,7 @@ exit: * This loop releases the lock corresponding to each affinity level * in the reverse order to which they were acquired. */ - psci_release_afflvl_locks(start_afflvl, + psci_release_afflvl_locks(MPIDR_AFFLVL0, end_afflvl, target_cpu_nodes); @@ -297,18 +167,15 @@ exit: } /******************************************************************************* - * The following functions finish an earlier affinity power on request. They + * The following function finish an earlier affinity power on request. They * are called by the common finisher routine in psci_common.c. ******************************************************************************/ -static void psci_afflvl0_on_finish(aff_map_node_t *cpu_node) +void psci_afflvl_on_finisher(aff_map_node_t *node[], int afflvl) { - unsigned int plat_state, state; - - assert(cpu_node->level == MPIDR_AFFLVL0); + assert(node[afflvl]->level == afflvl); /* Ensure we have been explicitly woken up by another cpu */ - state = psci_get_state(cpu_node); - assert(state == PSCI_STATE_ON_PENDING); + assert(psci_get_state(node[MPIDR_AFFLVL0]) == PSCI_STATE_ON_PENDING); /* * Plat. management: Perform the platform specific actions @@ -316,11 +183,7 @@ static void psci_afflvl0_on_finish(aff_map_node_t *cpu_node) * register. The actual state of this cpu has already been * changed. */ - - /* Get the physical state of this cpu */ - plat_state = get_phys_state(state); - psci_plat_pm_ops->affinst_on_finish(cpu_node->level, - plat_state); + psci_plat_pm_ops->affinst_on_finish(afflvl); /* * Arch. management: Enable data cache and manage stack memory @@ -353,53 +216,3 @@ static void psci_afflvl0_on_finish(aff_map_node_t *cpu_node) dcsw_op_louis(DCCSW); } -static void psci_afflvl1_on_finish(aff_map_node_t *cluster_node) -{ - unsigned int plat_state; - - assert(cluster_node->level == MPIDR_AFFLVL1); - - /* - * Plat. management: Perform the platform specific actions - * as per the old state of the cluster e.g. enabling - * coherency at the interconnect depends upon the state with - * which this cluster was powered up. If anything goes wrong - * then assert as there is no way to recover from this - * situation. - */ - plat_state = psci_get_phys_state(cluster_node); - psci_plat_pm_ops->affinst_on_finish(cluster_node->level, - plat_state); -} - - -static void psci_afflvl2_on_finish(aff_map_node_t *system_node) -{ - unsigned int plat_state; - - /* Cannot go beyond this affinity level */ - assert(system_node->level == MPIDR_AFFLVL2); - - /* - * Currently, there are no architectural actions to perform - * at the system level. - */ - - /* - * Plat. management: Perform the platform specific actions - * as per the old state of the cluster e.g. enabling - * coherency at the interconnect depends upon the state with - * which this cluster was powered up. If anything goes wrong - * then assert as there is no way to recover from this - * situation. - */ - plat_state = psci_get_phys_state(system_node); - psci_plat_pm_ops->affinst_on_finish(system_node->level, - plat_state); -} - -const afflvl_power_on_finisher_t psci_afflvl_on_finishers[] = { - psci_afflvl0_on_finish, - psci_afflvl1_on_finish, - psci_afflvl2_on_finish, -}; diff --git a/services/std_svc/psci1.0/psci_afflvl_suspend.c b/services/std_svc/psci1.0/psci_afflvl_suspend.c index 76e8c908a0..9b57a47464 100644 --- a/services/std_svc/psci1.0/psci_afflvl_suspend.c +++ b/services/std_svc/psci1.0/psci_afflvl_suspend.c @@ -41,8 +41,6 @@ #include #include "psci_private.h" -typedef void (*afflvl_suspend_handler_t)(aff_map_node_t *node); - /******************************************************************************* * This function saves the power state parameter passed in the current PSCI * cpu_suspend call in the per-cpu data array. @@ -99,162 +97,30 @@ int psci_get_suspend_stateid_by_mpidr(unsigned long mpidr) power_state : psci_get_pstate_id(power_state)); } -/******************************************************************************* - * The next three functions implement a handler for each supported affinity - * level which is called when that affinity level is about to be suspended. - ******************************************************************************/ -static void psci_afflvl0_suspend(aff_map_node_t *cpu_node) -{ - unsigned long psci_entrypoint; - - /* Sanity check to safeguard against data corruption */ - assert(cpu_node->level == MPIDR_AFFLVL0); - - /* Set the secure world (EL3) re-entry point after BL1 */ - psci_entrypoint = (unsigned long) psci_aff_suspend_finish_entry; - - /* - * Arch. management. Perform the necessary steps to flush all - * cpu caches. - */ - psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL0); - - /* - * Plat. management: Allow the platform to perform the - * necessary actions to turn off this cpu e.g. set the - * platform defined mailbox with the psci entrypoint, - * program the power controller etc. - */ - psci_plat_pm_ops->affinst_suspend(psci_entrypoint, - cpu_node->level, - psci_get_phys_state(cpu_node)); -} - -static void psci_afflvl1_suspend(aff_map_node_t *cluster_node) -{ - unsigned int plat_state; - unsigned long psci_entrypoint; - - /* Sanity check the cluster level */ - assert(cluster_node->level == MPIDR_AFFLVL1); - - /* - * Arch. management: Flush all levels of caches to PoC if the - * cluster is to be shutdown. - */ - psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL1); - - /* - * Plat. Management. Allow the platform to do its cluster specific - * bookeeping e.g. turn off interconnect coherency, program the power - * controller etc. Sending the psci entrypoint is currently redundant - * beyond affinity level 0 but one never knows what a platform might - * do. Also it allows us to keep the platform handler prototype the - * same. - */ - plat_state = psci_get_phys_state(cluster_node); - psci_entrypoint = (unsigned long) psci_aff_suspend_finish_entry; - psci_plat_pm_ops->affinst_suspend(psci_entrypoint, - cluster_node->level, - plat_state); -} - - -static void psci_afflvl2_suspend(aff_map_node_t *system_node) -{ - unsigned int plat_state; - unsigned long psci_entrypoint; - - /* Cannot go beyond this */ - assert(system_node->level == MPIDR_AFFLVL2); - - /* - * Keep the physical state of the system handy to decide what - * action needs to be taken - */ - plat_state = psci_get_phys_state(system_node); - - /* - * Arch. management: Flush all levels of caches to PoC if the - * system is to be shutdown. - */ - psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL2); - - /* - * Plat. Management : Allow the platform to do its bookeeping - * at this affinity level - */ - - /* - * Sending the psci entrypoint is currently redundant - * beyond affinity level 0 but one never knows what a - * platform might do. Also it allows us to keep the - * platform handler prototype the same. - */ - plat_state = psci_get_phys_state(system_node); - psci_entrypoint = (unsigned long) psci_aff_suspend_finish_entry; - psci_plat_pm_ops->affinst_suspend(psci_entrypoint, - system_node->level, - plat_state); -} - -static const afflvl_suspend_handler_t psci_afflvl_suspend_handlers[] = { - psci_afflvl0_suspend, - psci_afflvl1_suspend, - psci_afflvl2_suspend, -}; - -/******************************************************************************* - * This function takes an array of pointers to affinity instance nodes in the - * topology tree and calls the suspend handler for the corresponding affinity - * levels - ******************************************************************************/ -static void psci_call_suspend_handlers(aff_map_node_t *mpidr_nodes[], - int start_afflvl, - int end_afflvl) -{ - int level; - aff_map_node_t *node; - - for (level = start_afflvl; level <= end_afflvl; level++) { - node = mpidr_nodes[level]; - if (node == NULL) - continue; - - psci_afflvl_suspend_handlers[level](node); - } -} - /******************************************************************************* * Top level handler which is called when a cpu wants to suspend its execution. - * It is assumed that along with turning the cpu off, higher affinity levels - * until the target affinity level will be turned off as well. It traverses - * through all the affinity levels performing generic, architectural, platform - * setup and state management e.g. for a cluster that's to be suspended, it will - * call the platform specific code which will disable coherency at the - * interconnect level if the cpu is the last in the cluster. For a cpu it could - * mean programming the power controller etc. - * - * The state of all the relevant affinity levels is changed prior to calling the - * affinity level specific handlers as their actions would depend upon the state - * the affinity level is about to enter. - * - * The affinity level specific handlers are called in ascending order i.e. from - * the lowest to the highest affinity level implemented by the platform because - * to turn off affinity level X it is neccesary to turn off affinity level X - 1 - * first. + * It is assumed that along with suspending the cpu, higher affinity levels + * until the target affinity level will be suspended as well. It finds the + * highest level to be suspended by traversing the node information and then + * performs generic, architectural, platform setup and state management + * required to suspend that affinity level and affinity levels below it. + * e.g. For a cpu that's to be suspended, it could mean programming the + * power controller whereas for a cluster that's to be suspended, it will call + * the platform specific code which will disable coherency at the interconnect + * level if the cpu is the last in the cluster and also the program the power + * controller. * * All the required parameter checks are performed at the beginning and after - * the state transition has been done, no further error is expected and it - * is not possible to undo any of the actions taken beyond that point. + * the state transition has been done, no further error is expected and it is + * not possible to undo any of the actions taken beyond that point. ******************************************************************************/ void psci_afflvl_suspend(entry_point_info_t *ep, - int start_afflvl, int end_afflvl) { int skip_wfi = 0; mpidr_aff_map_nodes_t mpidr_nodes; unsigned int max_phys_off_afflvl; + unsigned long psci_entrypoint; /* * This function must only be called on platforms where the @@ -271,7 +137,7 @@ void psci_afflvl_suspend(entry_point_info_t *ep, * therefore assert. */ if (psci_get_aff_map_nodes(read_mpidr_el1() & MPIDR_AFFINITY_MASK, - start_afflvl, end_afflvl, mpidr_nodes) != PSCI_E_SUCCESS) + MPIDR_AFFLVL0, end_afflvl, mpidr_nodes) != PSCI_E_SUCCESS) assert(0); /* @@ -279,7 +145,7 @@ void psci_afflvl_suspend(entry_point_info_t *ep, * level so that by the time all locks are taken, the system topology * is snapshot and state management can be done safely. */ - psci_acquire_afflvl_locks(start_afflvl, + psci_acquire_afflvl_locks(MPIDR_AFFLVL0, end_afflvl, mpidr_nodes); @@ -306,42 +172,45 @@ void psci_afflvl_suspend(entry_point_info_t *ep, * corresponding to the mpidr in the range of affinity levels * specified. */ - psci_do_afflvl_state_mgmt(start_afflvl, + psci_do_afflvl_state_mgmt(MPIDR_AFFLVL0, end_afflvl, mpidr_nodes, PSCI_STATE_SUSPEND); - max_phys_off_afflvl = psci_find_max_phys_off_afflvl(start_afflvl, + max_phys_off_afflvl = psci_find_max_phys_off_afflvl(MPIDR_AFFLVL0, end_afflvl, mpidr_nodes); assert(max_phys_off_afflvl != PSCI_INVALID_DATA); - /* Stash the highest affinity level that will be turned off */ - psci_set_max_phys_off_afflvl(max_phys_off_afflvl); - /* * Store the re-entry information for the non-secure world. */ cm_init_context(read_mpidr_el1(), ep); - /* Perform generic, architecture and platform specific handling */ - psci_call_suspend_handlers(mpidr_nodes, - start_afflvl, - end_afflvl); + /* Set the secure world (EL3) re-entry point after BL1 */ + psci_entrypoint = (unsigned long) psci_aff_suspend_finish_entry; /* - * Invalidate the entry for the highest affinity level stashed earlier. - * This ensures that any reads of this variable outside the power - * up/down sequences return PSCI_INVALID_DATA. + * Arch. management. Perform the necessary steps to flush all + * cpu caches. */ - psci_set_max_phys_off_afflvl(PSCI_INVALID_DATA); + psci_do_pwrdown_cache_maintenance(max_phys_off_afflvl); + + /* + * Plat. management: Allow the platform to perform the + * necessary actions to turn off this cpu e.g. set the + * platform defined mailbox with the psci entrypoint, + * program the power controller etc. + */ + psci_plat_pm_ops->affinst_suspend(psci_entrypoint, + max_phys_off_afflvl); exit: /* * Release the locks corresponding to each affinity level in the * reverse order to which they were acquired. */ - psci_release_afflvl_locks(start_afflvl, + psci_release_afflvl_locks(MPIDR_AFFLVL0, end_afflvl, mpidr_nodes); if (!skip_wfi) @@ -352,17 +221,15 @@ exit: * The following functions finish an earlier affinity suspend request. They * are called by the common finisher routine in psci_common.c. ******************************************************************************/ -static void psci_afflvl0_suspend_finish(aff_map_node_t *cpu_node) +void psci_afflvl_suspend_finisher(aff_map_node_t *node[], int afflvl) { - unsigned int plat_state, state; int32_t suspend_level; uint64_t counter_freq; - assert(cpu_node->level == MPIDR_AFFLVL0); + assert(node[afflvl]->level == afflvl); /* Ensure we have been woken up from a suspended state */ - state = psci_get_state(cpu_node); - assert(state == PSCI_STATE_SUSPEND); + assert(psci_get_state(node[MPIDR_AFFLVL0]) == PSCI_STATE_SUSPEND); /* * Plat. management: Perform the platform specific actions @@ -371,11 +238,7 @@ static void psci_afflvl0_suspend_finish(aff_map_node_t *cpu_node) * wrong then assert as there is no way to recover from this * situation. */ - - /* Get the physical state of this cpu */ - plat_state = get_phys_state(state); - psci_plat_pm_ops->affinst_suspend_finish(cpu_node->level, - plat_state); + psci_plat_pm_ops->affinst_suspend_finish(afflvl); /* * Arch. management: Enable the data cache, manage stack memory and @@ -413,57 +276,3 @@ static void psci_afflvl0_suspend_finish(aff_map_node_t *cpu_node) dcsw_op_louis(DCCSW); } -static void psci_afflvl1_suspend_finish(aff_map_node_t *cluster_node) -{ - unsigned int plat_state; - - assert(cluster_node->level == MPIDR_AFFLVL1); - - /* - * Plat. management: Perform the platform specific actions - * as per the old state of the cluster e.g. enabling - * coherency at the interconnect depends upon the state with - * which this cluster was powered up. If anything goes wrong - * then assert as there is no way to recover from this - * situation. - */ - - /* Get the physical state of this cpu */ - plat_state = psci_get_phys_state(cluster_node); - psci_plat_pm_ops->affinst_suspend_finish(cluster_node->level, - plat_state); -} - - -static void psci_afflvl2_suspend_finish(aff_map_node_t *system_node) -{ - unsigned int plat_state; - - /* Cannot go beyond this affinity level */ - assert(system_node->level == MPIDR_AFFLVL2); - - /* - * Currently, there are no architectural actions to perform - * at the system level. - */ - - /* - * Plat. management: Perform the platform specific actions - * as per the old state of the cluster e.g. enabling - * coherency at the interconnect depends upon the state with - * which this cluster was powered up. If anything goes wrong - * then assert as there is no way to recover from this - * situation. - */ - - /* Get the physical state of the system */ - plat_state = psci_get_phys_state(system_node); - psci_plat_pm_ops->affinst_suspend_finish(system_node->level, - plat_state); -} - -const afflvl_power_on_finisher_t psci_afflvl_suspend_finishers[] = { - psci_afflvl0_suspend_finish, - psci_afflvl1_suspend_finish, - psci_afflvl2_suspend_finish, -}; diff --git a/services/std_svc/psci1.0/psci_common.c b/services/std_svc/psci1.0/psci_common.c index 1b74ff2c5c..55bf7ef893 100644 --- a/services/std_svc/psci1.0/psci_common.c +++ b/services/std_svc/psci1.0/psci_common.c @@ -123,43 +123,6 @@ unsigned int psci_is_last_on_cpu(void) return 1; } -/******************************************************************************* - * This function saves the highest affinity level which is in OFF state. The - * affinity instance with which the level is associated is determined by the - * caller. - ******************************************************************************/ -void psci_set_max_phys_off_afflvl(uint32_t afflvl) -{ - set_cpu_data(psci_svc_cpu_data.max_phys_off_afflvl, afflvl); - - /* - * Ensure that the saved value is flushed to main memory and any - * speculatively pre-fetched stale copies are invalidated from the - * caches of other cpus in the same coherency domain. This ensures that - * the value can be safely read irrespective of the state of the data - * cache. - */ - flush_cpu_data(psci_svc_cpu_data.max_phys_off_afflvl); -} - -/******************************************************************************* - * This function reads the saved highest affinity level which is in OFF - * state. The affinity instance with which the level is associated is determined - * by the caller. - ******************************************************************************/ -uint32_t psci_get_max_phys_off_afflvl(void) -{ - /* - * Ensure that the last update of this value in this cpu's cache is - * flushed to main memory and any speculatively pre-fetched stale copies - * are invalidated from the caches of other cpus in the same coherency - * domain. This ensures that the value is always read from the main - * memory when it was written before the data cache was enabled. - */ - flush_cpu_data(psci_svc_cpu_data.max_phys_off_afflvl); - return get_cpu_data(psci_svc_cpu_data.max_phys_off_afflvl); -} - /******************************************************************************* * Routine to return the maximum affinity level to traverse to after a cpu has * been physically powered up. It is expected to be called immediately after @@ -458,53 +421,20 @@ unsigned short psci_get_phys_state(aff_map_node_t *node) return get_phys_state(state); } -/******************************************************************************* - * This function takes an array of pointers to affinity instance nodes in the - * topology tree and calls the physical power on handler for the corresponding - * affinity levels - ******************************************************************************/ -static void psci_call_power_on_handlers(aff_map_node_t *mpidr_nodes[], - int start_afflvl, - int end_afflvl, - afflvl_power_on_finisher_t *pon_handlers) -{ - int level; - aff_map_node_t *node; - - for (level = end_afflvl; level >= start_afflvl; level--) { - node = mpidr_nodes[level]; - if (node == NULL) - continue; - - /* - * If we run into any trouble while powering up an - * affinity instance, then there is no recovery path - * so simply return an error and let the caller take - * care of the situation. - */ - pon_handlers[level](node); - } -} - /******************************************************************************* * Generic handler which is called when a cpu is physically powered on. It - * traverses through all the affinity levels performing generic, architectural, - * platform setup and state management e.g. for a cluster that's been powered - * on, it will call the platform specific code which will enable coherency at - * the interconnect level. For a cpu it could mean turning on the MMU etc. + * traverses the node information and finds the highest affinity level powered + * off and performs generic, architectural, platform setup and state management + * to power on that affinity level and affinity levels below it. + * e.g. For a cpu that's been powered on, it will call the platform specific + * code to enable the gic cpu interface and for a cluster it will enable + * coherency at the interconnect level in addition to gic cpu interface. * - * The state of all the relevant affinity levels is changed after calling the - * affinity level specific handlers as their actions would depend upon the state - * the affinity level is exiting from. - * - * The affinity level specific handlers are called in descending order i.e. from - * the highest to the lowest affinity level implemented by the platform because - * to turn on affinity level X it is neccesary to turn on affinity level X + 1 - * first. + * The state of all the relevant affinity levels is changed prior to calling + * the platform specific code. ******************************************************************************/ -void psci_afflvl_power_on_finish(int start_afflvl, - int end_afflvl, - afflvl_power_on_finisher_t *pon_handlers) +void psci_afflvl_power_on_finish(int end_afflvl, + afflvl_power_on_finisher_t pon_handler) { mpidr_aff_map_nodes_t mpidr_nodes; int rc; @@ -518,7 +448,7 @@ void psci_afflvl_power_on_finish(int start_afflvl, * levels are incorrect. Either case is an irrecoverable error. */ rc = psci_get_aff_map_nodes(read_mpidr_el1() & MPIDR_AFFINITY_MASK, - start_afflvl, + MPIDR_AFFLVL0, end_afflvl, mpidr_nodes); if (rc != PSCI_E_SUCCESS) @@ -529,49 +459,33 @@ void psci_afflvl_power_on_finish(int start_afflvl, * level so that by the time all locks are taken, the system topology * is snapshot and state management can be done safely. */ - psci_acquire_afflvl_locks(start_afflvl, + psci_acquire_afflvl_locks(MPIDR_AFFLVL0, end_afflvl, mpidr_nodes); - max_phys_off_afflvl = psci_find_max_phys_off_afflvl(start_afflvl, + max_phys_off_afflvl = psci_find_max_phys_off_afflvl(MPIDR_AFFLVL0, end_afflvl, mpidr_nodes); assert(max_phys_off_afflvl != PSCI_INVALID_DATA); - /* - * Stash the highest affinity level that will come out of the OFF or - * SUSPEND states. - */ - psci_set_max_phys_off_afflvl(max_phys_off_afflvl); - /* Perform generic, architecture and platform specific handling */ - psci_call_power_on_handlers(mpidr_nodes, - start_afflvl, - end_afflvl, - pon_handlers); + pon_handler(mpidr_nodes, max_phys_off_afflvl); /* * This function updates the state of each affinity instance * corresponding to the mpidr in the range of affinity levels * specified. */ - psci_do_afflvl_state_mgmt(start_afflvl, + psci_do_afflvl_state_mgmt(MPIDR_AFFLVL0, end_afflvl, mpidr_nodes, PSCI_STATE_ON); - /* - * Invalidate the entry for the highest affinity level stashed earlier. - * This ensures that any reads of this variable outside the power - * up/down sequences return PSCI_INVALID_DATA - */ - psci_set_max_phys_off_afflvl(PSCI_INVALID_DATA); - /* * This loop releases the lock corresponding to each affinity level * in the reverse order to which they were acquired. */ - psci_release_afflvl_locks(start_afflvl, + psci_release_afflvl_locks(MPIDR_AFFLVL0, end_afflvl, mpidr_nodes); } diff --git a/services/std_svc/psci1.0/psci_entry.S b/services/std_svc/psci1.0/psci_entry.S index 050f6c6c6d..13a0b860a1 100644 --- a/services/std_svc/psci1.0/psci_entry.S +++ b/services/std_svc/psci1.0/psci_entry.S @@ -46,11 +46,11 @@ * ----------------------------------------------------- */ func psci_aff_on_finish_entry - adr x23, psci_afflvl_on_finishers + adr x23, psci_afflvl_on_finisher b psci_aff_common_finish_entry psci_aff_suspend_finish_entry: - adr x23, psci_afflvl_suspend_finishers + adr x23, psci_afflvl_suspend_finisher psci_aff_common_finish_entry: /* @@ -98,15 +98,8 @@ psci_aff_common_finish_entry: mov x0, #DISABLE_DCACHE bl bl31_plat_enable_mmu - /* --------------------------------------------- - * Call the finishers starting from affinity - * level 0. - * --------------------------------------------- - */ bl get_power_on_target_afflvl - mov x2, x23 - mov x1, x0 - mov x0, #MPIDR_AFFLVL0 + mov x1, x23 bl psci_afflvl_power_on_finish b el3_exit diff --git a/services/std_svc/psci1.0/psci_helpers.S b/services/std_svc/psci1.0/psci_helpers.S index 1d99158e12..05a80f2d4a 100644 --- a/services/std_svc/psci1.0/psci_helpers.S +++ b/services/std_svc/psci1.0/psci_helpers.S @@ -56,15 +56,6 @@ func psci_do_pwrdown_cache_maintenance stp x29, x30, [sp,#-16]! stp x19, x20, [sp,#-16]! - mov x19, x0 - bl psci_get_max_phys_off_afflvl -#if ASM_ASSERTION - cmp x0, #PSCI_INVALID_DATA - ASM_ASSERT(ne) -#endif - cmp x0, x19 - b.ne 1f - /* --------------------------------------------- * Determine to how many levels of cache will be * subject to cache maintenance. Affinity level @@ -116,7 +107,6 @@ do_stack_maintenance: sub x1, sp, x0 bl inv_dcache_range -1: ldp x19, x20, [sp], #16 ldp x29, x30, [sp], #16 ret diff --git a/services/std_svc/psci1.0/psci_main.c b/services/std_svc/psci1.0/psci_main.c index b389287b00..9741fb68fd 100644 --- a/services/std_svc/psci1.0/psci_main.c +++ b/services/std_svc/psci1.0/psci_main.c @@ -46,7 +46,7 @@ int psci_cpu_on(unsigned long target_cpu, { int rc; - unsigned int start_afflvl, end_afflvl; + unsigned int end_afflvl; entry_point_info_t ep; /* Determine if the cpu exists of not */ @@ -73,18 +73,14 @@ int psci_cpu_on(unsigned long target_cpu, if (rc != PSCI_E_SUCCESS) return rc; - /* * To turn this cpu on, specify which affinity * levels need to be turned on */ - start_afflvl = MPIDR_AFFLVL0; end_afflvl = PLATFORM_MAX_AFFLVL; rc = psci_afflvl_on(target_cpu, &ep, - start_afflvl, end_afflvl); - return rc; } @@ -160,7 +156,6 @@ int psci_cpu_suspend(unsigned int power_state, * enter the final wfi which will power down this CPU. */ psci_afflvl_suspend(&ep, - MPIDR_AFFLVL0, target_afflvl); /* Reset PSCI power state parameter for the core. */ @@ -235,7 +230,7 @@ int psci_cpu_off(void) * management is done immediately followed by cpu, cluster ... * ..target_afflvl specific actions as this function unwinds back. */ - rc = psci_afflvl_off(MPIDR_AFFLVL0, target_afflvl); + rc = psci_afflvl_off(target_afflvl); /* * The only error cpu_off can return is E_DENIED. So check if that's diff --git a/services/std_svc/psci1.0/psci_private.h b/services/std_svc/psci1.0/psci_private.h index 2955de7a0f..7797fa7927 100644 --- a/services/std_svc/psci1.0/psci_private.h +++ b/services/std_svc/psci1.0/psci_private.h @@ -96,7 +96,8 @@ typedef struct aff_limits_node { } aff_limits_node_t; typedef aff_map_node_t (*mpidr_aff_map_nodes_t[MPIDR_MAX_AFFLVL + 1]); -typedef void (*afflvl_power_on_finisher_t)(aff_map_node_t *); +typedef void (*afflvl_power_on_finisher_t)(aff_map_node_t *mpidr_nodes[], + int afflvl); /******************************************************************************* * Data prototypes @@ -121,9 +122,8 @@ void psci_set_state(aff_map_node_t *node, unsigned short state); unsigned long mpidr_set_aff_inst(unsigned long, unsigned char, int); int psci_validate_mpidr(unsigned long, int); int get_power_on_target_afflvl(void); -void psci_afflvl_power_on_finish(int, - int, - afflvl_power_on_finisher_t *); +void psci_afflvl_power_on_finish(int end_afflvl, + afflvl_power_on_finisher_t pon_handler); int psci_get_ns_ep_info(entry_point_info_t *ep, uint64_t entrypoint, uint64_t context_id); int psci_check_afflvl_range(int start_afflvl, int end_afflvl); @@ -138,7 +138,6 @@ void psci_release_afflvl_locks(int start_afflvl, int end_afflvl, mpidr_aff_map_nodes_t mpidr_nodes); void psci_print_affinity_map(void); -void psci_set_max_phys_off_afflvl(uint32_t afflvl); uint32_t psci_find_max_phys_off_afflvl(uint32_t start_afflvl, uint32_t end_afflvl, aff_map_node_t *mpidr_nodes[]); @@ -155,18 +154,19 @@ aff_map_node_t *psci_get_aff_map_node(unsigned long, int); /* Private exported functions from psci_affinity_on.c */ int psci_afflvl_on(unsigned long target_cpu, entry_point_info_t *ep, - int start_afflvl, int end_afflvl); +void psci_afflvl_on_finisher(aff_map_node_t *node[], int afflvl); + /* Private exported functions from psci_affinity_off.c */ -int psci_afflvl_off(int, int); +int psci_afflvl_off(int end_afflvl); /* Private exported functions from psci_affinity_suspend.c */ void psci_afflvl_suspend(entry_point_info_t *ep, - int start_afflvl, int end_afflvl); -unsigned int psci_afflvl_suspend_finish(int, int); +void psci_afflvl_suspend_finisher(aff_map_node_t *node[], int afflvl); + void psci_set_suspend_power_state(unsigned int power_state); /* Private exported functions from psci_helpers.S */ diff --git a/services/std_svc/psci1.0/psci_setup.c b/services/std_svc/psci1.0/psci_setup.c index 01b559cf38..a04f8e792e 100644 --- a/services/std_svc/psci1.0/psci_setup.c +++ b/services/std_svc/psci1.0/psci_setup.c @@ -216,15 +216,6 @@ static void psci_init_aff_map_node(unsigned long mpidr, psci_svc_cpu_data.power_state, PSCI_INVALID_DATA); - /* - * There is no state associated with the current execution - * context so ensure that any reads of the highest affinity - * level in a powered down state return PSCI_INVALID_DATA. - */ - set_cpu_data_by_index(linear_id, - psci_svc_cpu_data.max_phys_off_afflvl, - PSCI_INVALID_DATA); - flush_cpu_data_by_index(linear_id, psci_svc_cpu_data); cm_set_context_by_mpidr(mpidr, From 4067dc3112d4385c1d3b244aeb708c325983f6e1 Mon Sep 17 00:00:00 2001 From: Soby Mathew Date: Tue, 5 May 2015 16:33:16 +0100 Subject: [PATCH 03/20] PSCI: Remove references to affinity based power management As per Section 4.2.2. in the PSCI specification, the term "affinity" is used in the context of describing the hierarchical arrangement of cores. This often, but not always, maps directly to the processor power domain topology of the system. The current PSCI implementation assumes that this is always the case i.e. MPIDR based levels of affinity always map to levels in a power domain topology tree. This patch is the first in a series of patches which remove this assumption. It removes all occurences of the terms "affinity instances and levels" when used to describe the power domain topology. Only the terminology is changed in this patch. Subsequent patches will implement functional changes to remove the above mentioned assumption. Change-Id: Iee162f051b228828310610c5a320ff9d31009b4e --- include/bl31/services/psci1.0/psci.h | 58 ++-- include/plat/common/psci1.0/platform.h | 6 +- services/std_svc/psci1.0/psci_common.c | 251 ++++++++-------- services/std_svc/psci1.0/psci_entry.S | 22 +- services/std_svc/psci1.0/psci_helpers.S | 19 +- services/std_svc/psci1.0/psci_main.c | 61 ++-- .../psci1.0/{psci_afflvl_off.c => psci_off.c} | 67 ++--- .../psci1.0/{psci_afflvl_on.c => psci_on.c} | 54 ++-- services/std_svc/psci1.0/psci_private.h | 109 +++---- services/std_svc/psci1.0/psci_setup.c | 270 +++++++++--------- .../{psci_afflvl_suspend.c => psci_suspend.c} | 92 +++--- services/std_svc/psci1.0/psci_system_off.c | 6 +- 12 files changed, 509 insertions(+), 506 deletions(-) rename services/std_svc/psci1.0/{psci_afflvl_off.c => psci_off.c} (64%) rename services/std_svc/psci1.0/{psci_afflvl_on.c => psci_on.c} (83%) rename services/std_svc/psci1.0/{psci_afflvl_suspend.c => psci_suspend.c} (75%) diff --git a/include/bl31/services/psci1.0/psci.h b/include/bl31/services/psci1.0/psci.h index 905fc8ab0b..d00796c5f4 100644 --- a/include/bl31/services/psci1.0/psci.h +++ b/include/bl31/services/psci1.0/psci.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-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: @@ -32,15 +32,15 @@ #define __PSCI_H__ #include -#include /* for PLATFORM_NUM_AFFS */ +#include /* for PLAT_NUM_PWR_DOMAINS */ /******************************************************************************* - * Number of affinity instances whose state this psci imp. can track + * Number of power domains whose state this psci imp. can track ******************************************************************************/ -#ifdef PLATFORM_NUM_AFFS -#define PSCI_NUM_AFFS PLATFORM_NUM_AFFS +#ifdef PLAT_NUM_PWR_DOMAINS +#define PSCI_NUM_PWR_DOMAINS PLAT_NUM_PWR_DOMAINS #else -#define PSCI_NUM_AFFS (2 * PLATFORM_CORE_COUNT) +#define PSCI_NUM_PWR_DOMAINS (2 * PLATFORM_CORE_COUNT) #endif /******************************************************************************* @@ -85,11 +85,11 @@ ******************************************************************************/ #define PSTATE_ID_SHIFT 0 #define PSTATE_TYPE_SHIFT 16 -#define PSTATE_AFF_LVL_SHIFT 24 +#define PSTATE_PWR_LVL_SHIFT 24 #define PSTATE_ID_MASK 0xffff #define PSTATE_TYPE_MASK 0x1 -#define PSTATE_AFF_LVL_MASK 0x3 +#define PSTATE_PWR_LVL_MASK 0x3 #define PSTATE_VALID_MASK 0xFCFE0000 #define PSTATE_TYPE_STANDBY 0x0 @@ -99,12 +99,12 @@ PSTATE_ID_MASK) #define psci_get_pstate_type(pstate) (((pstate) >> PSTATE_TYPE_SHIFT) & \ PSTATE_TYPE_MASK) -#define psci_get_pstate_afflvl(pstate) (((pstate) >> PSTATE_AFF_LVL_SHIFT) & \ - PSTATE_AFF_LVL_MASK) -#define psci_make_powerstate(state_id, type, afflvl) \ +#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) |\ - (((afflvl) & PSTATE_AFF_LVL_MASK) << PSTATE_AFF_LVL_SHIFT) + (((pwrlvl) & PSTATE_PWR_LVL_MASK) << PSTATE_PWR_LVL_SHIFT) /******************************************************************************* * PSCI CPU_FEATURES feature flag specific defines @@ -138,15 +138,15 @@ #define PSCI_E_DISABLED -8 /******************************************************************************* - * PSCI affinity state related constants. An affinity instance could be present - * or absent physically to cater for asymmetric topologies. If present then it - * could in one of the 4 further defined states. + * PSCI power domain state related constants. A power domain instance could + * be present or absent physically to cater for asymmetric topologies. If + * present then it could be in one of the 4 further defined states. ******************************************************************************/ #define PSCI_STATE_SHIFT 1 #define PSCI_STATE_MASK 0xff -#define PSCI_AFF_ABSENT 0x0 -#define PSCI_AFF_PRESENT 0x1 +#define PSCI_PWR_DOMAIN_ABSENT 0x0 +#define PSCI_PWR_DOMAIN_PRESENT 0x1 #define PSCI_STATE_ON 0x0 #define PSCI_STATE_OFF 0x1 #define PSCI_STATE_ON_PENDING 0x2 @@ -172,7 +172,7 @@ typedef struct psci_cpu_data { uint32_t power_state; #if !USE_COHERENT_MEM - bakery_info_t pcpu_bakery_info[PSCI_NUM_AFFS]; + bakery_info_t pcpu_bakery_info[PSCI_NUM_PWR_DOMAINS]; #endif } psci_cpu_data_t; @@ -181,15 +181,15 @@ typedef struct psci_cpu_data { * perform common low level pm functions ******************************************************************************/ typedef struct plat_pm_ops { - void (*affinst_standby)(unsigned int power_state); - int (*affinst_on)(unsigned long mpidr, + void (*pwr_domain_standby)(unsigned int power_state); + int (*pwr_domain_on)(unsigned long mpidr, unsigned long sec_entrypoint, - unsigned int afflvl); - void (*affinst_off)(unsigned int afflvl); - void (*affinst_suspend)(unsigned long sec_entrypoint, - unsigned int afflvl); - void (*affinst_on_finish)(unsigned int afflvl); - void (*affinst_suspend_finish)(unsigned int afflvl); + unsigned int pwrlvl); + void (*pwr_domain_off)(unsigned int pwrlvl); + 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); void (*system_off)(void) __dead2; void (*system_reset)(void) __dead2; int (*validate_power_state)(unsigned int power_state); @@ -227,12 +227,12 @@ int psci_cpu_on(unsigned long, unsigned long, unsigned long); void __dead2 psci_power_down_wfi(void); -void psci_aff_on_finish_entry(void); -void psci_aff_suspend_finish_entry(void); +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_mpidr(unsigned long); int psci_get_suspend_stateid(void); -int psci_get_suspend_afflvl(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 469d46b678..50af4f85e6 100644 --- a/include/plat/common/psci1.0/platform.h +++ b/include/plat/common/psci1.0/platform.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-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: @@ -182,8 +182,8 @@ struct entry_point_info *bl31_plat_get_next_image_ep_info(uint32_t type); * Mandatory PSCI functions (BL3-1) ******************************************************************************/ int platform_setup_pm(const struct plat_pm_ops **); -unsigned int plat_get_aff_count(unsigned int, unsigned long); -unsigned int plat_get_aff_state(unsigned int, unsigned long); +unsigned int plat_get_pwr_domain_count(unsigned int, unsigned long); +unsigned int plat_get_pwr_domain_state(unsigned int, unsigned long); /******************************************************************************* * Optional BL3-1 functions (may be overridden) diff --git a/services/std_svc/psci1.0/psci_common.c b/services/std_svc/psci1.0/psci_common.c index 55bf7ef893..55578379dc 100644 --- a/services/std_svc/psci1.0/psci_common.c +++ b/services/std_svc/psci1.0/psci_common.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-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: @@ -47,10 +47,10 @@ const spd_pm_ops_t *psci_spd_pm; /******************************************************************************* * Grand array that holds the platform's topology information for state - * management of affinity instances. Each node (aff_map_node) in the array - * corresponds to an affinity instance e.g. cluster, cpu within an mpidr + * management of power domain instances. Each node (pwr_map_node) in the array + * corresponds to a power domain instance e.g. cluster, cpu within an mpidr ******************************************************************************/ -aff_map_node_t psci_aff_map[PSCI_NUM_AFFS] +pwr_map_node_t psci_pwr_domain_map[PSCI_NUM_PWR_DOMAINS] #if USE_COHERENT_MEM __attribute__ ((section("tzfw_coherent_mem"))) #endif @@ -62,33 +62,34 @@ __attribute__ ((section("tzfw_coherent_mem"))) const plat_pm_ops_t *psci_plat_pm_ops; /******************************************************************************* - * Check that the maximum affinity level supported by the platform makes sense + * Check that the maximum power level supported by the platform makes sense * ****************************************************************************/ -CASSERT(PLATFORM_MAX_AFFLVL <= MPIDR_MAX_AFFLVL && \ - PLATFORM_MAX_AFFLVL >= MPIDR_AFFLVL0, \ - assert_platform_max_afflvl_check); +CASSERT(PLAT_MAX_PWR_LVL <= MPIDR_MAX_AFFLVL && \ + PLAT_MAX_PWR_LVL >= MPIDR_AFFLVL0, \ + assert_platform_max_pwrlvl_check); /******************************************************************************* - * This function is passed an array of pointers to affinity level nodes in the - * topology tree for an mpidr. It iterates through the nodes to find the highest - * affinity level which is marked as physically powered off. + * This function is passed an array of pointers to power domain nodes in the + * topology tree for an mpidr. It iterates through the nodes to find the + * highest power level where the power domain is marked as physically powered + * off. ******************************************************************************/ -uint32_t psci_find_max_phys_off_afflvl(uint32_t start_afflvl, - uint32_t end_afflvl, - aff_map_node_t *mpidr_nodes[]) +uint32_t psci_find_max_phys_off_pwrlvl(uint32_t start_pwrlvl, + uint32_t end_pwrlvl, + pwr_map_node_t *mpidr_nodes[]) { - uint32_t max_afflvl = PSCI_INVALID_DATA; + uint32_t max_pwrlvl = PSCI_INVALID_DATA; - for (; start_afflvl <= end_afflvl; start_afflvl++) { - if (mpidr_nodes[start_afflvl] == NULL) + for (; start_pwrlvl <= end_pwrlvl; start_pwrlvl++) { + if (mpidr_nodes[start_pwrlvl] == NULL) continue; - if (psci_get_phys_state(mpidr_nodes[start_afflvl]) == + if (psci_get_phys_state(mpidr_nodes[start_pwrlvl]) == PSCI_STATE_OFF) - max_afflvl = start_afflvl; + max_pwrlvl = start_pwrlvl; } - return max_afflvl; + return max_pwrlvl; } /******************************************************************************* @@ -102,21 +103,21 @@ unsigned int psci_is_last_on_cpu(void) unsigned long mpidr = read_mpidr_el1() & MPIDR_AFFINITY_MASK; unsigned int i; - for (i = psci_aff_limits[MPIDR_AFFLVL0].min; - i <= psci_aff_limits[MPIDR_AFFLVL0].max; i++) { + for (i = psci_pwr_lvl_limits[MPIDR_AFFLVL0].min; + i <= psci_pwr_lvl_limits[MPIDR_AFFLVL0].max; i++) { - assert(psci_aff_map[i].level == MPIDR_AFFLVL0); + assert(psci_pwr_domain_map[i].level == MPIDR_AFFLVL0); - if (!(psci_aff_map[i].state & PSCI_AFF_PRESENT)) + if (!(psci_pwr_domain_map[i].state & PSCI_AFF_PRESENT)) continue; - if (psci_aff_map[i].mpidr == mpidr) { - assert(psci_get_state(&psci_aff_map[i]) + if (psci_pwr_domain_map[i].mpidr == mpidr) { + assert(psci_get_state(&psci_pwr_domain_map[i]) == PSCI_STATE_ON); continue; } - if (psci_get_state(&psci_aff_map[i]) != PSCI_STATE_OFF) + if (psci_get_state(&psci_pwr_domain_map[i]) != PSCI_STATE_OFF) return 0; } @@ -124,20 +125,20 @@ unsigned int psci_is_last_on_cpu(void) } /******************************************************************************* - * Routine to return the maximum affinity level to traverse to after a cpu has + * Routine to return the maximum power level to traverse to after a cpu has * been physically powered up. It is expected to be called immediately after * reset from assembler code. ******************************************************************************/ -int get_power_on_target_afflvl(void) +int get_power_on_target_pwrlvl(void) { - int afflvl; + int pwrlvl; #if DEBUG unsigned int state; - aff_map_node_t *node; + pwr_map_node_t *node; /* Retrieve our node from the topology tree */ - node = psci_get_aff_map_node(read_mpidr_el1() & MPIDR_AFFINITY_MASK, + node = psci_get_pwr_map_node(read_mpidr_el1() & MPIDR_AFFINITY_MASK, MPIDR_AFFLVL0); assert(node); @@ -150,73 +151,74 @@ int get_power_on_target_afflvl(void) #endif /* - * Assume that this cpu was suspended and retrieve its target affinity + * Assume that this cpu was suspended and retrieve its target power * level. If it is invalid then it could only have been turned off - * earlier. PLATFORM_MAX_AFFLVL will be the highest affinity level a + * earlier. PLAT_MAX_PWR_LVL will be the highest power level a * cpu can be turned off to. */ - afflvl = psci_get_suspend_afflvl(); - if (afflvl == PSCI_INVALID_DATA) - afflvl = PLATFORM_MAX_AFFLVL; - return afflvl; + pwrlvl = psci_get_suspend_pwrlvl(); + if (pwrlvl == PSCI_INVALID_DATA) + pwrlvl = PLAT_MAX_PWR_LVL; + return pwrlvl; } /******************************************************************************* - * Simple routine to set the id of an affinity instance at a given level in the - * mpidr. + * Simple routine to set the id of a power domain instance at a given level + * in the mpidr. The assumption is that the affinity level and the power + * level are the same. ******************************************************************************/ -unsigned long mpidr_set_aff_inst(unsigned long mpidr, - unsigned char aff_inst, - int aff_lvl) +unsigned long mpidr_set_pwr_domain_inst(unsigned long mpidr, + unsigned char pwr_inst, + int pwr_lvl) { unsigned long aff_shift; - assert(aff_lvl <= MPIDR_AFFLVL3); + assert(pwr_lvl <= MPIDR_AFFLVL3); /* * Decide the number of bits to shift by depending upon - * the affinity level + * the power level */ - aff_shift = get_afflvl_shift(aff_lvl); + aff_shift = get_afflvl_shift(pwr_lvl); /* Clear the existing affinity instance & set the new one*/ mpidr &= ~(((unsigned long)MPIDR_AFFLVL_MASK) << aff_shift); - mpidr |= ((unsigned long)aff_inst) << aff_shift; + mpidr |= ((unsigned long)pwr_inst) << aff_shift; return mpidr; } /******************************************************************************* - * This function sanity checks a range of affinity levels. + * This function sanity checks a range of power levels. ******************************************************************************/ -int psci_check_afflvl_range(int start_afflvl, int end_afflvl) +int psci_check_pwrlvl_range(int start_pwrlvl, int end_pwrlvl) { /* Sanity check the parameters passed */ - if (end_afflvl > PLATFORM_MAX_AFFLVL) + if (end_pwrlvl > PLAT_MAX_PWR_LVL) return PSCI_E_INVALID_PARAMS; - if (start_afflvl < MPIDR_AFFLVL0) + if (start_pwrlvl < MPIDR_AFFLVL0) return PSCI_E_INVALID_PARAMS; - if (end_afflvl < start_afflvl) + if (end_pwrlvl < start_pwrlvl) return PSCI_E_INVALID_PARAMS; return PSCI_E_SUCCESS; } /******************************************************************************* - * This function is passed an array of pointers to affinity level nodes in the + * This function is passed an array of pointers to power domain nodes in the * topology tree for an mpidr and the state which each node should transition - * to. It updates the state of each node between the specified affinity levels. + * to. It updates the state of each node between the specified power levels. ******************************************************************************/ -void psci_do_afflvl_state_mgmt(uint32_t start_afflvl, - uint32_t end_afflvl, - aff_map_node_t *mpidr_nodes[], +void psci_do_state_coordination(uint32_t start_pwrlvl, + uint32_t end_pwrlvl, + pwr_map_node_t *mpidr_nodes[], uint32_t state) { uint32_t level; - for (level = start_afflvl; level <= end_afflvl; level++) { + for (level = start_pwrlvl; level <= end_pwrlvl; level++) { if (mpidr_nodes[level] == NULL) continue; psci_set_state(mpidr_nodes[level], state); @@ -224,17 +226,17 @@ void psci_do_afflvl_state_mgmt(uint32_t start_afflvl, } /******************************************************************************* - * This function is passed an array of pointers to affinity level nodes in the - * topology tree for an mpidr. It picks up locks for each affinity level bottom + * This function is passed an array of pointers to power domain nodes in the + * topology tree for an mpidr. It picks up locks for each power level bottom * up in the range specified. ******************************************************************************/ -void psci_acquire_afflvl_locks(int start_afflvl, - int end_afflvl, - aff_map_node_t *mpidr_nodes[]) +void psci_acquire_pwr_domain_locks(int start_pwrlvl, + int end_pwrlvl, + pwr_map_node_t *mpidr_nodes[]) { int level; - for (level = start_afflvl; level <= end_afflvl; level++) { + for (level = start_pwrlvl; level <= end_pwrlvl; level++) { if (mpidr_nodes[level] == NULL) continue; @@ -243,17 +245,17 @@ void psci_acquire_afflvl_locks(int start_afflvl, } /******************************************************************************* - * This function is passed an array of pointers to affinity level nodes in the - * topology tree for an mpidr. It releases the lock for each affinity level top + * This function is passed an array of pointers to power domain nodes in the + * topology tree for an mpidr. It releases the lock for each power level top * down in the range specified. ******************************************************************************/ -void psci_release_afflvl_locks(int start_afflvl, - int end_afflvl, - aff_map_node_t *mpidr_nodes[]) +void psci_release_pwr_domain_locks(int start_pwrlvl, + int end_pwrlvl, + pwr_map_node_t *mpidr_nodes[]) { int level; - for (level = end_afflvl; level >= start_afflvl; level--) { + for (level = end_pwrlvl; level >= start_pwrlvl; level--) { if (mpidr_nodes[level] == NULL) continue; @@ -262,15 +264,15 @@ void psci_release_afflvl_locks(int start_afflvl, } /******************************************************************************* - * Simple routine to determine whether an affinity instance at a given level - * in an mpidr exists or not. + * Simple routine to determine whether an power domain instance at a given + * level in an mpidr exists or not. ******************************************************************************/ int psci_validate_mpidr(unsigned long mpidr, int level) { - aff_map_node_t *node; + pwr_map_node_t *node; - node = psci_get_aff_map_node(mpidr, level); - if (node && (node->state & PSCI_AFF_PRESENT)) + node = psci_get_pwr_map_node(mpidr, level); + if (node && (node->state & PSCI_PWR_DOMAIN_PRESENT)) return PSCI_E_SUCCESS; else return PSCI_E_INVALID_PARAMS; @@ -334,10 +336,10 @@ int psci_get_ns_ep_info(entry_point_info_t *ep, } /******************************************************************************* - * This function takes a pointer to an affinity node in the topology tree and - * returns its state. State of a non-leaf node needs to be calculated. + * This function takes a pointer to a power domain node in the topology tree + * and returns its state. State of a non-leaf node needs to be calculated. ******************************************************************************/ -unsigned short psci_get_state(aff_map_node_t *node) +unsigned short psci_get_state(pwr_map_node_t *node) { #if !USE_COHERENT_MEM flush_dcache_range((uint64_t) node, sizeof(*node)); @@ -350,11 +352,11 @@ unsigned short psci_get_state(aff_map_node_t *node) return (node->state >> PSCI_STATE_SHIFT) & PSCI_STATE_MASK; /* - * For an affinity level higher than a cpu, the state has to be + * For a power level higher than a cpu, the state has to be * calculated. It depends upon the value of the reference count - * which is managed by each node at the next lower affinity level + * which is managed by each node at the next lower power level * e.g. for a cluster, each cpu increments/decrements the reference - * count. If the reference count is 0 then the affinity level is + * count. If the reference count is 0 then the power level is * OFF else ON. */ if (node->ref_count) @@ -364,16 +366,16 @@ unsigned short psci_get_state(aff_map_node_t *node) } /******************************************************************************* - * This function takes a pointer to an affinity node in the topology tree and - * a target state. State of a non-leaf node needs to be converted to a reference - * count. State of a leaf node can be set directly. + * This function takes a pointer to a power domain node in the topology + * tree and a target state. State of a non-leaf node needs to be converted + * to a reference count. State of a leaf node can be set directly. ******************************************************************************/ -void psci_set_state(aff_map_node_t *node, unsigned short state) +void psci_set_state(pwr_map_node_t *node, unsigned short state) { assert(node->level >= MPIDR_AFFLVL0 && node->level <= MPIDR_MAX_AFFLVL); /* - * For an affinity level higher than a cpu, the state is used + * For a power level higher than a cpu, the state is used * to decide whether the reference count is incremented or * decremented. Entry into the ON_PENDING state does not have * effect. @@ -389,7 +391,7 @@ void psci_set_state(aff_map_node_t *node, unsigned short state) break; case PSCI_STATE_ON_PENDING: /* - * An affinity level higher than a cpu will not undergo + * A power level higher than a cpu will not undergo * a state change when it is about to be turned on */ return; @@ -407,13 +409,13 @@ void psci_set_state(aff_map_node_t *node, unsigned short state) } /******************************************************************************* - * An affinity level could be on, on_pending, suspended or off. These are the + * A power domain could be on, on_pending, suspended or off. These are the * logical states it can be in. Physically either it is off or on. When it is in * the state on_pending then it is about to be turned on. It is not possible to - * tell whether that's actually happenned or not. So we err on the side of - * caution & treat the affinity level as being turned off. + * tell whether that's actually happened or not. So we err on the side of + * caution & treat the power domain as being turned off. ******************************************************************************/ -unsigned short psci_get_phys_state(aff_map_node_t *node) +unsigned short psci_get_phys_state(pwr_map_node_t *node) { unsigned int state; @@ -423,70 +425,67 @@ unsigned short psci_get_phys_state(aff_map_node_t *node) /******************************************************************************* * Generic handler which is called when a cpu is physically powered on. It - * traverses the node information and finds the highest affinity level powered + * traverses the node information and finds the highest power level powered * off and performs generic, architectural, platform setup and state management - * to power on that affinity level and affinity levels below it. + * to power on that power level and power levels below it. * e.g. For a cpu that's been powered on, it will call the platform specific * code to enable the gic cpu interface and for a cluster it will enable * coherency at the interconnect level in addition to gic cpu interface. - * - * The state of all the relevant affinity levels is changed prior to calling - * the platform specific code. ******************************************************************************/ -void psci_afflvl_power_on_finish(int end_afflvl, - afflvl_power_on_finisher_t pon_handler) +void psci_power_up_finish(int end_pwrlvl, + pwrlvl_power_on_finisher_t pon_handler) { - mpidr_aff_map_nodes_t mpidr_nodes; + mpidr_pwr_map_nodes_t mpidr_nodes; int rc; - unsigned int max_phys_off_afflvl; + unsigned int max_phys_off_pwrlvl; /* * Collect the pointers to the nodes in the topology tree for - * each affinity instance in the mpidr. If this function does - * not return successfully then either the mpidr or the affinity + * each power domain instances in the mpidr. If this function does + * not return successfully then either the mpidr or the power * levels are incorrect. Either case is an irrecoverable error. */ - rc = psci_get_aff_map_nodes(read_mpidr_el1() & MPIDR_AFFINITY_MASK, + rc = psci_get_pwr_map_nodes(read_mpidr_el1() & MPIDR_AFFINITY_MASK, MPIDR_AFFLVL0, - end_afflvl, + end_pwrlvl, mpidr_nodes); if (rc != PSCI_E_SUCCESS) panic(); /* - * This function acquires the lock corresponding to each affinity + * This function acquires the lock corresponding to each power * level so that by the time all locks are taken, the system topology * is snapshot and state management can be done safely. */ - psci_acquire_afflvl_locks(MPIDR_AFFLVL0, - end_afflvl, + psci_acquire_pwr_domain_locks(MPIDR_AFFLVL0, + end_pwrlvl, mpidr_nodes); - max_phys_off_afflvl = psci_find_max_phys_off_afflvl(MPIDR_AFFLVL0, - end_afflvl, + max_phys_off_pwrlvl = psci_find_max_phys_off_pwrlvl(MPIDR_AFFLVL0, + end_pwrlvl, mpidr_nodes); - assert(max_phys_off_afflvl != PSCI_INVALID_DATA); + assert(max_phys_off_pwrlvl != PSCI_INVALID_DATA); /* Perform generic, architecture and platform specific handling */ - pon_handler(mpidr_nodes, max_phys_off_afflvl); + pon_handler(mpidr_nodes, max_phys_off_pwrlvl); /* - * This function updates the state of each affinity instance - * corresponding to the mpidr in the range of affinity levels + * This function updates the state of each power instance + * corresponding to the mpidr in the range of power levels * specified. */ - psci_do_afflvl_state_mgmt(MPIDR_AFFLVL0, - end_afflvl, + psci_do_state_coordination(MPIDR_AFFLVL0, + end_pwrlvl, mpidr_nodes, PSCI_STATE_ON); /* - * This loop releases the lock corresponding to each affinity level + * This loop releases the lock corresponding to each power level * in the reverse order to which they were acquired. */ - psci_release_afflvl_locks(MPIDR_AFFLVL0, - end_afflvl, + psci_release_pwr_domain_locks(MPIDR_AFFLVL0, + end_pwrlvl, mpidr_nodes); } @@ -532,13 +531,13 @@ int psci_spd_migrate_info(uint64_t *mpidr) /******************************************************************************* - * This function prints the state of all affinity instances present in the + * This function prints the state of all power domains present in the * system ******************************************************************************/ -void psci_print_affinity_map(void) +void psci_print_power_domain_map(void) { #if LOG_LEVEL >= LOG_LEVEL_INFO - aff_map_node_t *node; + pwr_map_node_t *node; unsigned int idx; /* This array maps to the PSCI_STATE_X definitions in psci.h */ static const char *psci_state_str[] = { @@ -548,13 +547,13 @@ void psci_print_affinity_map(void) "SUSPEND" }; - INFO("PSCI Affinity Map:\n"); - for (idx = 0; idx < PSCI_NUM_AFFS ; idx++) { - node = &psci_aff_map[idx]; - if (!(node->state & PSCI_AFF_PRESENT)) { + INFO("PSCI Power Domain Map:\n"); + for (idx = 0; idx < PSCI_NUM_PWR_DOMAINS; idx++) { + node = &psci_pwr_domain_map[idx]; + if (!(node->state & PSCI_PWR_DOMAIN_PRESENT)) { continue; } - INFO(" AffInst: Level %u, MPID 0x%lx, State %s\n", + INFO(" pwrInst: Level %u, MPID 0x%lx, State %s\n", node->level, node->mpidr, psci_state_str[psci_get_state(node)]); } diff --git a/services/std_svc/psci1.0/psci_entry.S b/services/std_svc/psci1.0/psci_entry.S index 13a0b860a1..4e7456dbd4 100644 --- a/services/std_svc/psci1.0/psci_entry.S +++ b/services/std_svc/psci1.0/psci_entry.S @@ -34,8 +34,8 @@ #include #include - .globl psci_aff_on_finish_entry - .globl psci_aff_suspend_finish_entry + .globl psci_cpu_on_finish_entry + .globl psci_cpu_suspend_finish_entry .globl psci_power_down_wfi /* ----------------------------------------------------- @@ -45,14 +45,14 @@ * the handlers (chosen depending upon original state). * ----------------------------------------------------- */ -func psci_aff_on_finish_entry - adr x23, psci_afflvl_on_finisher - b psci_aff_common_finish_entry +func psci_cpu_on_finish_entry + adr x23, psci_cpu_on_finish + b psci_power_up_entry -psci_aff_suspend_finish_entry: - adr x23, psci_afflvl_suspend_finisher +psci_cpu_suspend_finish_entry: + adr x23, psci_cpu_suspend_finish -psci_aff_common_finish_entry: +psci_power_up_entry: /* * On the warm boot path, most of the EL3 initialisations performed by * 'el3_entrypoint_common' must be skipped: @@ -98,12 +98,12 @@ psci_aff_common_finish_entry: mov x0, #DISABLE_DCACHE bl bl31_plat_enable_mmu - bl get_power_on_target_afflvl + bl get_power_on_target_pwrlvl mov x1, x23 - bl psci_afflvl_power_on_finish + bl psci_power_up_finish b el3_exit -endfunc psci_aff_on_finish_entry +endfunc psci_cpu_on_finish_entry /* -------------------------------------------- * This function is called to indicate to the diff --git a/services/std_svc/psci1.0/psci_helpers.S b/services/std_svc/psci1.0/psci_helpers.S index 05a80f2d4a..07fb889389 100644 --- a/services/std_svc/psci1.0/psci_helpers.S +++ b/services/std_svc/psci1.0/psci_helpers.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2014-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: @@ -38,14 +38,13 @@ .globl psci_do_pwrup_cache_maintenance /* ----------------------------------------------------------------------- - * void psci_do_pwrdown_cache_maintenance(uint32_t affinity level); + * void psci_do_pwrdown_cache_maintenance(uint32_t power level); * - * This function performs cache maintenance if the specified affinity - * level is the equal to the level of the highest affinity instance which - * will be/is physically powered off. The levels of cache affected are - * determined by the affinity level which is passed as the argument i.e. - * level 0 results in a flush of the L1 cache. Both the L1 and L2 caches - * are flushed for a higher affinity level. + * This function performs cache maintenance for the specified power + * level. The levels of cache affected are determined by the power + * level which is passed as the argument i.e. level 0 results + * in a flush of the L1 cache. Both the L1 and L2 caches are flushed + * for a higher power level. * * Additionally, this function also ensures that stack memory is correctly * flushed out to avoid coherency issues due to a change in its memory @@ -58,11 +57,11 @@ func psci_do_pwrdown_cache_maintenance /* --------------------------------------------- * Determine to how many levels of cache will be - * subject to cache maintenance. Affinity level + * subject to cache maintenance. Power level * 0 implies that only the cpu is being powered * down. Only the L1 data cache needs to be * flushed to the PoU in this case. For a higher - * affinity level we are assuming that a flush + * power level we are assuming that a flush * of L1 data and L2 unified cache is enough. * This information should be provided by the * platform. diff --git a/services/std_svc/psci1.0/psci_main.c b/services/std_svc/psci1.0/psci_main.c index 9741fb68fd..0421b15f82 100644 --- a/services/std_svc/psci1.0/psci_main.c +++ b/services/std_svc/psci1.0/psci_main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-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: @@ -46,7 +46,7 @@ int psci_cpu_on(unsigned long target_cpu, { int rc; - unsigned int end_afflvl; + unsigned int end_pwrlvl; entry_point_info_t ep; /* Determine if the cpu exists of not */ @@ -74,13 +74,13 @@ int psci_cpu_on(unsigned long target_cpu, return rc; /* - * To turn this cpu on, specify which affinity + * To turn this cpu on, specify which power * levels need to be turned on */ - end_afflvl = PLATFORM_MAX_AFFLVL; - rc = psci_afflvl_on(target_cpu, + end_pwrlvl = PLAT_MAX_PWR_LVL; + rc = psci_cpu_on_start(target_cpu, &ep, - end_afflvl); + end_pwrlvl); return rc; } @@ -94,7 +94,7 @@ int psci_cpu_suspend(unsigned int power_state, unsigned long context_id) { int rc; - unsigned int target_afflvl, pstate_type; + unsigned int target_pwrlvl, pstate_type; entry_point_info_t ep; /* Check SBZ bits in power state are zero */ @@ -102,8 +102,8 @@ int psci_cpu_suspend(unsigned int power_state, return PSCI_E_INVALID_PARAMS; /* Sanity check the requested state */ - target_afflvl = psci_get_pstate_afflvl(power_state); - if (target_afflvl > PLATFORM_MAX_AFFLVL) + target_pwrlvl = psci_get_pstate_pwrlvl(power_state); + if (target_pwrlvl > PLAT_MAX_PWR_LVL) return PSCI_E_INVALID_PARAMS; /* Validate the power_state using platform pm_ops */ @@ -132,10 +132,10 @@ int psci_cpu_suspend(unsigned int power_state, * a standby state. */ if (pstate_type == PSTATE_TYPE_STANDBY) { - if (!psci_plat_pm_ops->affinst_standby) + if (!psci_plat_pm_ops->pwr_domain_standby) return PSCI_E_INVALID_PARAMS; - psci_plat_pm_ops->affinst_standby(power_state); + psci_plat_pm_ops->pwr_domain_standby(power_state); return PSCI_E_SUCCESS; } @@ -155,8 +155,8 @@ int psci_cpu_suspend(unsigned int power_state, * Do what is needed to enter the power down state. Upon success, * enter the final wfi which will power down this CPU. */ - psci_afflvl_suspend(&ep, - target_afflvl); + psci_cpu_suspend_start(&ep, + target_pwrlvl); /* Reset PSCI power state parameter for the core. */ psci_set_suspend_power_state(PSCI_INVALID_DATA); @@ -210,9 +210,7 @@ int psci_system_suspend(unsigned long entrypoint, * Do what is needed to enter the power down state. Upon success, * enter the final wfi which will power down this cpu. */ - psci_afflvl_suspend(&ep, - MPIDR_AFFLVL0, - PLATFORM_MAX_AFFLVL); + psci_cpu_suspend_start(&ep, PLAT_MAX_PWR_LVL); /* Reset PSCI power state parameter for the core. */ psci_set_suspend_power_state(PSCI_INVALID_DATA); @@ -222,15 +220,14 @@ int psci_system_suspend(unsigned long entrypoint, int psci_cpu_off(void) { int rc; - int target_afflvl = PLATFORM_MAX_AFFLVL; + int target_pwrlvl = PLAT_MAX_PWR_LVL; /* - * Traverse from the highest to the lowest affinity level. When the - * lowest affinity level is hit, all the locks are acquired. State - * management is done immediately followed by cpu, cluster ... - * ..target_afflvl specific actions as this function unwinds back. + * Do what is needed to power off this CPU and possible higher power + * levels if it able to do so. Upon success, enter the final wfi + * which will power down this CPU. */ - rc = psci_afflvl_off(target_afflvl); + rc = psci_do_cpu_off(target_pwrlvl); /* * The only error cpu_off can return is E_DENIED. So check if that's @@ -245,28 +242,28 @@ int psci_affinity_info(unsigned long target_affinity, unsigned int lowest_affinity_level) { int rc = PSCI_E_INVALID_PARAMS; - unsigned int aff_state; - aff_map_node_t *node; + unsigned int pwr_domain_state; + pwr_map_node_t *node; - if (lowest_affinity_level > PLATFORM_MAX_AFFLVL) + if (lowest_affinity_level > PLAT_MAX_PWR_LVL) return rc; - node = psci_get_aff_map_node(target_affinity, lowest_affinity_level); - if (node && (node->state & PSCI_AFF_PRESENT)) { + node = psci_get_pwr_map_node(target_affinity, lowest_affinity_level); + if (node && (node->state & PSCI_PWR_DOMAIN_PRESENT)) { /* - * TODO: For affinity levels higher than 0 i.e. cpu, the + * TODO: For power levels higher than 0 i.e. cpu, the * state will always be either ON or OFF. Need to investigate * how critical is it to support ON_PENDING here. */ - aff_state = psci_get_state(node); + pwr_domain_state = psci_get_state(node); /* A suspended cpu is available & on for the OS */ - if (aff_state == PSCI_STATE_SUSPEND) { - aff_state = PSCI_STATE_ON; + if (pwr_domain_state == PSCI_STATE_SUSPEND) { + pwr_domain_state = PSCI_STATE_ON; } - rc = aff_state; + rc = pwr_domain_state; } return rc; diff --git a/services/std_svc/psci1.0/psci_afflvl_off.c b/services/std_svc/psci1.0/psci_off.c similarity index 64% rename from services/std_svc/psci1.0/psci_afflvl_off.c rename to services/std_svc/psci1.0/psci_off.c index b96682253d..f4fb9809ee 100644 --- a/services/std_svc/psci1.0/psci_afflvl_off.c +++ b/services/std_svc/psci1.0/psci_off.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-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: @@ -37,48 +37,49 @@ /****************************************************************************** * Top level handler which is called when a cpu wants to power itself down. - * It's assumed that along with turning the cpu off, higher affinity levels - * will be turned off as far as possible. It finds the highest level to be - * powered off by traversing the node information and then performs generic, - * architectural, platform setup and state management required to turn OFF - * that affinity level and affinity levels below it. e.g. For a cpu that's to - * be powered OFF, it could mean programming the power controller whereas for - * a cluster that's to be powered off, it will call the platform specific code - * which will disable coherency at the interconnect level if the cpu is the - * last in the cluster and also the program the power controller. + * It's assumed that along with turning the cpu power domain off, power + * domains at higher levels will be turned off as far as possible. It finds + * the highest level where a domain has to be powered off by traversing the + * node information and then performs generic, architectural, platform setup + * and state management required to turn OFF that power domain and domains + * below it. e.g. For a cpu that's to be powered OFF, it could mean programming + * the power controller whereas for a cluster that's to be powered off, it will + * call the platform specific code which will disable coherency at the + * interconnect level if the cpu is the last in the cluster and also the + * program the power controller. ******************************************************************************/ -int psci_afflvl_off(int end_afflvl) +int psci_do_cpu_off(int end_pwrlvl) { int rc; - mpidr_aff_map_nodes_t mpidr_nodes; - unsigned int max_phys_off_afflvl; + mpidr_pwr_map_nodes_t mpidr_nodes; + unsigned int max_phys_off_pwrlvl; /* * This function must only be called on platforms where the * CPU_OFF platform hooks have been implemented. */ - assert(psci_plat_pm_ops->affinst_off); + assert(psci_plat_pm_ops->pwr_domain_off); /* * Collect the pointers to the nodes in the topology tree for - * each affinity instance in the mpidr. If this function does - * not return successfully then either the mpidr or the affinity + * each power domain instance in the mpidr. If this function does + * not return successfully then either the mpidr or the power * levels are incorrect. Either way, this an internal TF error * therefore assert. */ - rc = psci_get_aff_map_nodes(read_mpidr_el1() & MPIDR_AFFINITY_MASK, + rc = psci_get_pwr_map_nodes(read_mpidr_el1() & MPIDR_AFFINITY_MASK, MPIDR_AFFLVL0, - end_afflvl, + end_pwrlvl, mpidr_nodes); assert(rc == PSCI_E_SUCCESS); /* - * This function acquires the lock corresponding to each affinity + * This function acquires the lock corresponding to each power * level so that by the time all locks are taken, the system topology * is snapshot and state management can be done safely. */ - psci_acquire_afflvl_locks(MPIDR_AFFLVL0, - end_afflvl, + psci_acquire_pwr_domain_locks(MPIDR_AFFLVL0, + end_pwrlvl, mpidr_nodes); @@ -94,39 +95,39 @@ int psci_afflvl_off(int end_afflvl) } /* - * This function updates the state of each affinity instance - * corresponding to the mpidr in the range of affinity levels + * This function updates the state of each power domain instance + * corresponding to the mpidr in the range of power levels * specified. */ - psci_do_afflvl_state_mgmt(MPIDR_AFFLVL0, - end_afflvl, + psci_do_state_coordination(MPIDR_AFFLVL0, + end_pwrlvl, mpidr_nodes, PSCI_STATE_OFF); - max_phys_off_afflvl = psci_find_max_phys_off_afflvl(MPIDR_AFFLVL0, - end_afflvl, + max_phys_off_pwrlvl = psci_find_max_phys_off_pwrlvl(MPIDR_AFFLVL0, + end_pwrlvl, mpidr_nodes); - assert(max_phys_off_afflvl != PSCI_INVALID_DATA); + assert(max_phys_off_pwrlvl != PSCI_INVALID_DATA); /* * Arch. management. Perform the necessary steps to flush all * cpu caches. */ - psci_do_pwrdown_cache_maintenance(max_phys_off_afflvl); + psci_do_pwrdown_cache_maintenance(max_phys_off_pwrlvl); /* * Plat. management: Perform platform specific actions to turn this * cpu off e.g. exit cpu coherency, program the power controller etc. */ - psci_plat_pm_ops->affinst_off(max_phys_off_afflvl); + psci_plat_pm_ops->pwr_domain_off(max_phys_off_pwrlvl); exit: /* - * Release the locks corresponding to each affinity level in the + * Release the locks corresponding to each power level in the * reverse order to which they were acquired. */ - psci_release_afflvl_locks(MPIDR_AFFLVL0, - end_afflvl, + psci_release_pwr_domain_locks(MPIDR_AFFLVL0, + end_pwrlvl, mpidr_nodes); /* diff --git a/services/std_svc/psci1.0/psci_afflvl_on.c b/services/std_svc/psci1.0/psci_on.c similarity index 83% rename from services/std_svc/psci1.0/psci_afflvl_on.c rename to services/std_svc/psci1.0/psci_on.c index 61003dc03d..43f58f9b93 100644 --- a/services/std_svc/psci1.0/psci_afflvl_on.c +++ b/services/std_svc/psci1.0/psci_on.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-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: @@ -63,43 +63,43 @@ static int cpu_on_validate_state(unsigned int psci_state) * enough information is stashed for it to resume execution in the non-secure * security state. * - * The state of all the relevant affinity levels is changed after calling the + * The state of all the relevant power domains are changed after calling the * platform handler as it can return error. ******************************************************************************/ -int psci_afflvl_on(unsigned long target_cpu, +int psci_cpu_on_start(unsigned long target_cpu, entry_point_info_t *ep, - int end_afflvl) + int end_pwrlvl) { int rc; - mpidr_aff_map_nodes_t target_cpu_nodes; + mpidr_pwr_map_nodes_t target_cpu_nodes; unsigned long psci_entrypoint; /* * This function must only be called on platforms where the * CPU_ON platform hooks have been implemented. */ - assert(psci_plat_pm_ops->affinst_on && - psci_plat_pm_ops->affinst_on_finish); + assert(psci_plat_pm_ops->pwr_domain_on && + psci_plat_pm_ops->pwr_domain_on_finish); /* * Collect the pointers to the nodes in the topology tree for - * each affinity instance in the mpidr. If this function does - * not return successfully then either the mpidr or the affinity + * each power domain instance in the mpidr. If this function does + * not return successfully then either the mpidr or the power * levels are incorrect. */ - rc = psci_get_aff_map_nodes(target_cpu, + rc = psci_get_pwr_map_nodes(target_cpu, MPIDR_AFFLVL0, - end_afflvl, + end_pwrlvl, target_cpu_nodes); assert(rc == PSCI_E_SUCCESS); /* - * This function acquires the lock corresponding to each affinity + * This function acquires the lock corresponding to each power * level so that by the time all locks are taken, the system topology * is snapshot and state management can be done safely. */ - psci_acquire_afflvl_locks(MPIDR_AFFLVL0, - end_afflvl, + psci_acquire_pwr_domain_locks(MPIDR_AFFLVL0, + end_pwrlvl, target_cpu_nodes); /* @@ -124,8 +124,8 @@ int psci_afflvl_on(unsigned long target_cpu, * corresponding to the mpidr in the range of affinity levels * specified. */ - psci_do_afflvl_state_mgmt(MPIDR_AFFLVL0, - end_afflvl, + psci_do_state_coordination(MPIDR_AFFLVL0, + end_pwrlvl, target_cpu_nodes, PSCI_STATE_ON_PENDING); @@ -133,14 +133,14 @@ int psci_afflvl_on(unsigned long target_cpu, * Perform generic, architecture and platform specific handling. */ /* Set the secure world (EL3) re-entry point after BL1 */ - psci_entrypoint = (unsigned long) psci_aff_on_finish_entry; + psci_entrypoint = (unsigned long) psci_cpu_on_finish_entry; /* * Plat. management: Give the platform the current state * of the target cpu to allow it to perform the necessary * steps to power on. */ - rc = psci_plat_pm_ops->affinst_on(target_cpu, + rc = psci_plat_pm_ops->pwr_domain_on(target_cpu, psci_entrypoint, MPIDR_AFFLVL0); assert(rc == PSCI_E_SUCCESS || rc == PSCI_E_INTERN_FAIL); @@ -150,29 +150,29 @@ int psci_afflvl_on(unsigned long target_cpu, cm_init_context(target_cpu, ep); else /* Restore the state on error. */ - psci_do_afflvl_state_mgmt(MPIDR_AFFLVL0, - end_afflvl, + psci_do_state_coordination(MPIDR_AFFLVL0, + end_pwrlvl, target_cpu_nodes, PSCI_STATE_OFF); exit: /* - * This loop releases the lock corresponding to each affinity level + * This loop releases the lock corresponding to each power level * in the reverse order to which they were acquired. */ - psci_release_afflvl_locks(MPIDR_AFFLVL0, - end_afflvl, + psci_release_pwr_domain_locks(MPIDR_AFFLVL0, + end_pwrlvl, target_cpu_nodes); return rc; } /******************************************************************************* - * The following function finish an earlier affinity power on request. They + * The following function finish an earlier power on request. They * are called by the common finisher routine in psci_common.c. ******************************************************************************/ -void psci_afflvl_on_finisher(aff_map_node_t *node[], int afflvl) +void psci_cpu_on_finish(pwr_map_node_t *node[], int pwrlvl) { - assert(node[afflvl]->level == afflvl); + assert(node[pwrlvl]->level == pwrlvl); /* Ensure we have been explicitly woken up by another cpu */ assert(psci_get_state(node[MPIDR_AFFLVL0]) == PSCI_STATE_ON_PENDING); @@ -183,7 +183,7 @@ void psci_afflvl_on_finisher(aff_map_node_t *node[], int afflvl) * register. The actual state of this cpu has already been * changed. */ - psci_plat_pm_ops->affinst_on_finish(afflvl); + psci_plat_pm_ops->pwr_domain_on_finish(pwrlvl); /* * Arch. management: Enable data cache and manage stack memory diff --git a/services/std_svc/psci1.0/psci_private.h b/services/std_svc/psci1.0/psci_private.h index 7797fa7927..e56d848bce 100644 --- a/services/std_svc/psci1.0/psci_private.h +++ b/services/std_svc/psci1.0/psci_private.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-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: @@ -41,14 +41,17 @@ * Lock API. */ #if USE_COHERENT_MEM -#define psci_lock_init(aff_map, idx) bakery_lock_init(&(aff_map)[(idx)].lock) +#define psci_lock_init(pwr_map, idx) bakery_lock_init(&(pwr_map)[(idx)].lock) #define psci_lock_get(node) bakery_lock_get(&((node)->lock)) #define psci_lock_release(node) bakery_lock_release(&((node)->lock)) #else -#define psci_lock_init(aff_map, idx) ((aff_map)[(idx)].aff_map_index = (idx)) -#define psci_lock_get(node) bakery_lock_get((node)->aff_map_index, \ +#define psci_lock_init(pwr_map, idx) \ + ((pwr_map)[(idx)].pwr_domain_index = (idx)) +#define psci_lock_get(node) \ + bakery_lock_get((node)->pwr_domain_index,\ CPU_DATA_PSCI_LOCK_OFFSET) -#define psci_lock_release(node) bakery_lock_release((node)->aff_map_index,\ +#define psci_lock_release(node) \ + bakery_lock_release((node)->pwr_domain_index,\ CPU_DATA_PSCI_LOCK_OFFSET) #endif @@ -75,9 +78,9 @@ /******************************************************************************* * The following two data structures hold the topology tree which in turn tracks - * the state of the all the affinity instances supported by the platform. + * the state of the all the power domain instances supported by the platform. ******************************************************************************/ -typedef struct aff_map_node { +typedef struct pwr_map_node { unsigned long mpidr; unsigned char ref_count; unsigned char state; @@ -86,25 +89,25 @@ typedef struct aff_map_node { bakery_lock_t lock; #else /* For indexing the bakery_info array in per CPU data */ - unsigned char aff_map_index; + unsigned char pwr_domain_index; #endif -} aff_map_node_t; +} pwr_map_node_t; -typedef struct aff_limits_node { +typedef struct pwr_lvl_limits_node { int min; int max; -} aff_limits_node_t; +} pwr_lvl_limits_node_t; -typedef aff_map_node_t (*mpidr_aff_map_nodes_t[MPIDR_MAX_AFFLVL + 1]); -typedef void (*afflvl_power_on_finisher_t)(aff_map_node_t *mpidr_nodes[], - int afflvl); +typedef pwr_map_node_t (*mpidr_pwr_map_nodes_t[MPIDR_MAX_AFFLVL + 1]); +typedef void (*pwrlvl_power_on_finisher_t)(pwr_map_node_t *mpidr_nodes[], + int pwrlvl); /******************************************************************************* * Data prototypes ******************************************************************************/ extern const plat_pm_ops_t *psci_plat_pm_ops; -extern aff_map_node_t psci_aff_map[PSCI_NUM_AFFS]; -extern aff_limits_node_t psci_aff_limits[MPIDR_MAX_AFFLVL + 1]; +extern pwr_map_node_t psci_pwr_domain_map[PSCI_NUM_PWR_DOMAINS]; +extern pwr_lvl_limits_node_t psci_pwr_domain_map[MPIDR_MAX_AFFLVL + 1]; extern uint32_t psci_caps; /******************************************************************************* @@ -116,61 +119,61 @@ extern const spd_pm_ops_t *psci_spd_pm; * Function prototypes ******************************************************************************/ /* Private exported functions from psci_common.c */ -unsigned short psci_get_state(aff_map_node_t *node); -unsigned short psci_get_phys_state(aff_map_node_t *node); -void psci_set_state(aff_map_node_t *node, unsigned short state); -unsigned long mpidr_set_aff_inst(unsigned long, unsigned char, int); +unsigned short psci_get_state(pwr_map_node_t *node); +unsigned short psci_get_phys_state(pwr_map_node_t *node); +void psci_set_state(pwr_map_node_t *node, unsigned short state); +unsigned long mpidr_set_pwr_domain_inst(unsigned long, unsigned char, int); int psci_validate_mpidr(unsigned long, int); -int get_power_on_target_afflvl(void); -void psci_afflvl_power_on_finish(int end_afflvl, - afflvl_power_on_finisher_t pon_handler); +int get_power_on_target_pwrlvl(void); +void psci_power_up_finish(int end_pwrlvl, + pwrlvl_power_on_finisher_t pon_handler); int psci_get_ns_ep_info(entry_point_info_t *ep, uint64_t entrypoint, uint64_t context_id); -int psci_check_afflvl_range(int start_afflvl, int end_afflvl); -void psci_do_afflvl_state_mgmt(uint32_t start_afflvl, - uint32_t end_afflvl, - aff_map_node_t *mpidr_nodes[], +int psci_check_pwrlvl_range(int start_pwrlvl, int end_pwrlvl); +void psci_do_state_coordination(uint32_t start_pwrlvl, + uint32_t end_pwrlvl, + pwr_map_node_t *mpidr_nodes[], uint32_t state); -void psci_acquire_afflvl_locks(int start_afflvl, - int end_afflvl, - aff_map_node_t *mpidr_nodes[]); -void psci_release_afflvl_locks(int start_afflvl, - int end_afflvl, - mpidr_aff_map_nodes_t mpidr_nodes); -void psci_print_affinity_map(void); -uint32_t psci_find_max_phys_off_afflvl(uint32_t start_afflvl, - uint32_t end_afflvl, - aff_map_node_t *mpidr_nodes[]); +void psci_acquire_pwr_domain_locks(int start_pwrlvl, + int end_pwrlvl, + pwr_map_node_t *mpidr_nodes[]); +void psci_release_pwr_domain_locks(int start_pwrlvl, + int end_pwrlvl, + mpidr_pwr_map_nodes_t mpidr_nodes); +void psci_print_power_domain_map(void); +uint32_t psci_find_max_phys_off_pwrlvl(uint32_t start_pwrlvl, + uint32_t end_pwrlvl, + pwr_map_node_t *mpidr_nodes[]); unsigned int psci_is_last_on_cpu(void); int psci_spd_migrate_info(uint64_t *mpidr); /* Private exported functions from psci_setup.c */ -int psci_get_aff_map_nodes(unsigned long mpidr, - int start_afflvl, - int end_afflvl, - aff_map_node_t *mpidr_nodes[]); -aff_map_node_t *psci_get_aff_map_node(unsigned long, int); +int psci_get_pwr_map_nodes(unsigned long mpidr, + int start_pwrlvl, + int end_pwrlvl, + pwr_map_node_t *mpidr_nodes[]); +pwr_map_node_t *psci_get_pwr_map_node(unsigned long, int); -/* Private exported functions from psci_affinity_on.c */ -int psci_afflvl_on(unsigned long target_cpu, +/* Private exported functions from psci_cpu_on.c */ +int psci_cpu_on_start(unsigned long target_cpu, entry_point_info_t *ep, - int end_afflvl); + int end_pwrlvl); -void psci_afflvl_on_finisher(aff_map_node_t *node[], int afflvl); +void psci_cpu_on_finish(pwr_map_node_t *node[], int pwrlvl); -/* Private exported functions from psci_affinity_off.c */ -int psci_afflvl_off(int end_afflvl); +/* Private exported functions from psci_cpu_off.c */ +int psci_do_cpu_off(int end_pwrlvl); -/* Private exported functions from psci_affinity_suspend.c */ -void psci_afflvl_suspend(entry_point_info_t *ep, - int end_afflvl); +/* Private exported functions from psci_cpu_suspend.c */ +void psci_cpu_suspend_start(entry_point_info_t *ep, + int end_pwrlvl); -void psci_afflvl_suspend_finisher(aff_map_node_t *node[], int afflvl); +void psci_cpu_suspend_finish(pwr_map_node_t *node[], int pwrlvl); void psci_set_suspend_power_state(unsigned int power_state); /* Private exported functions from psci_helpers.S */ -void psci_do_pwrdown_cache_maintenance(uint32_t affinity_level); +void psci_do_pwrdown_cache_maintenance(uint32_t pwr_level); void psci_do_pwrup_cache_maintenance(void); /* Private exported functions from psci_system_off.c */ diff --git a/services/std_svc/psci1.0/psci_setup.c b/services/std_svc/psci1.0/psci_setup.c index a04f8e792e..002e220e6e 100644 --- a/services/std_svc/psci1.0/psci_setup.c +++ b/services/std_svc/psci1.0/psci_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-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: @@ -42,20 +42,20 @@ * Per cpu non-secure contexts used to program the architectural state prior * return to the normal world. * TODO: Use the memory allocator to set aside memory for the contexts instead - * of relying on platform defined constants. Using PSCI_NUM_AFFS will be an - * overkill. + * of relying on platform defined constants. Using PSCI_NUM_PWR_DOMAINS will be + * an overkill. ******************************************************************************/ static cpu_context_t psci_ns_context[PLATFORM_CORE_COUNT]; /******************************************************************************* - * In a system, a certain number of affinity instances are present at an - * affinity level. The cumulative number of instances across all levels are - * stored in 'psci_aff_map'. The topology tree has been flattenned into this - * array. To retrieve nodes, information about the extents of each affinity - * level i.e. start index and end index needs to be present. 'psci_aff_limits' - * stores this information. + * In a system, a certain number of power domain instances are present at a + * power level. The cumulative number of instances across all levels are + * stored in 'psci_pwr_domain_map'. The topology tree has been flattenned into + * this array. To retrieve nodes, information about the extents of each power + * level i.e. start index and end index needs to be present. + * 'psci_pwr_lvl_limits' stores this information. ******************************************************************************/ -aff_limits_node_t psci_aff_limits[MPIDR_MAX_AFFLVL + 1]; +pwr_lvl_limits_node_t psci_pwr_lvl_limits[MPIDR_MAX_AFFLVL + 1]; /****************************************************************************** * Define the psci capability variable. @@ -64,12 +64,12 @@ uint32_t psci_caps; /******************************************************************************* - * Routines for retrieving the node corresponding to an affinity level instance + * Routines for retrieving the node corresponding to a power domain instance * in the mpidr. The first one uses binary search to find the node corresponding - * to the mpidr (key) at a particular affinity level. The second routine decides - * extents of the binary search at each affinity level. + * to the mpidr (key) at a particular power level. The second routine decides + * extents of the binary search at each power level. ******************************************************************************/ -static int psci_aff_map_get_idx(unsigned long key, +static int psci_pwr_domain_map_get_idx(unsigned long key, int min_idx, int max_idx) { @@ -85,80 +85,80 @@ static int psci_aff_map_get_idx(unsigned long key, /* * Make sure we are within array limits. */ - assert(min_idx >= 0 && max_idx < PSCI_NUM_AFFS); + assert(min_idx >= 0 && max_idx < PSCI_NUM_PWR_DOMAINS); /* * Bisect the array around 'mid' and then recurse into the array chunk * where the key is likely to be found. The mpidrs in each node in the - * 'psci_aff_map' for a given affinity level are stored in an ascending - * order which makes the binary search possible. + * 'psci_pwr_domain_map' for a given power level are stored in an + * ascending order which makes the binary search possible. */ mid = min_idx + ((max_idx - min_idx) >> 1); /* Divide by 2 */ - if (psci_aff_map[mid].mpidr > key) - return psci_aff_map_get_idx(key, min_idx, mid - 1); - else if (psci_aff_map[mid].mpidr < key) - return psci_aff_map_get_idx(key, mid + 1, max_idx); + if (psci_pwr_domain_map[mid].mpidr > key) + return psci_pwr_domain_map_get_idx(key, min_idx, mid - 1); + else if (psci_pwr_domain_map[mid].mpidr < key) + return psci_pwr_domain_map_get_idx(key, mid + 1, max_idx); else return mid; } -aff_map_node_t *psci_get_aff_map_node(unsigned long mpidr, int aff_lvl) +pwr_map_node_t *psci_get_pwr_map_node(unsigned long mpidr, int pwr_lvl) { int rc; - if (aff_lvl > PLATFORM_MAX_AFFLVL) + if (pwr_lvl > PLAT_MAX_PWR_LVL) return NULL; - /* Right shift the mpidr to the required affinity level */ - mpidr = mpidr_mask_lower_afflvls(mpidr, aff_lvl); + /* Right shift the mpidr to the required power level */ + mpidr = mpidr_mask_lower_afflvls(mpidr, pwr_lvl); - rc = psci_aff_map_get_idx(mpidr, - psci_aff_limits[aff_lvl].min, - psci_aff_limits[aff_lvl].max); + rc = psci_pwr_domain_map_get_idx(mpidr, + psci_pwr_lvl_limits[pwr_lvl].min, + psci_pwr_lvl_limits[pwr_lvl].max); if (rc >= 0) - return &psci_aff_map[rc]; + return &psci_pwr_domain_map[rc]; else return NULL; } /******************************************************************************* * This function populates an array with nodes corresponding to a given range of - * affinity levels in an mpidr. It returns successfully only when the affinity - * levels are correct, the mpidr is valid i.e. no affinity level is absent from - * the topology tree & the affinity instance at level 0 is not absent. + * power levels in an mpidr. It returns successfully only when the power + * levels are correct, the mpidr is valid i.e. no power level is absent from + * the topology tree & the power domain instance at level 0 is not absent. ******************************************************************************/ -int psci_get_aff_map_nodes(unsigned long mpidr, - int start_afflvl, - int end_afflvl, - aff_map_node_t *mpidr_nodes[]) +int psci_get_pwr_map_nodes(unsigned long mpidr, + int start_pwrlvl, + int end_pwrlvl, + pwr_map_node_t *mpidr_nodes[]) { int rc = PSCI_E_INVALID_PARAMS, level; - aff_map_node_t *node; + pwr_map_node_t *node; - rc = psci_check_afflvl_range(start_afflvl, end_afflvl); + rc = psci_check_pwrlvl_range(start_pwrlvl, end_pwrlvl); if (rc != PSCI_E_SUCCESS) return rc; - for (level = start_afflvl; level <= end_afflvl; level++) { + for (level = start_pwrlvl; level <= end_pwrlvl; level++) { /* - * Grab the node for each affinity level. No affinity level + * Grab the node for each power level. No power level * can be missing as that would mean that the topology tree * is corrupted. */ - node = psci_get_aff_map_node(mpidr, level); + node = psci_get_pwr_map_node(mpidr, level); if (node == NULL) { rc = PSCI_E_INVALID_PARAMS; break; } /* - * Skip absent affinity levels unless it's afffinity level 0. + * Skip absent power levels unless it's power level 0. * An absent cpu means that the mpidr is invalid. Save the - * pointer to the node for the present affinity level + * pointer to the node for the present power level */ - if (!(node->state & PSCI_AFF_PRESENT)) { + if (!(node->state & PSCI_PWR_DOMAIN_PRESENT)) { if (level == MPIDR_AFFLVL0) { rc = PSCI_E_INVALID_PARAMS; break; @@ -173,39 +173,38 @@ int psci_get_aff_map_nodes(unsigned long mpidr, } /******************************************************************************* - * Function which initializes the 'aff_map_node' corresponding to an affinity - * level instance. Each node has a unique mpidr, level and bakery lock. The data - * field is opaque and holds affinity level specific data e.g. for affinity - * level 0 it contains the index into arrays that hold the secure/non-secure - * state for a cpu that's been turned on/off + * Function which initializes the 'pwr_map_node' corresponding to a power + * domain instance. Each node has a unique mpidr, level and bakery lock. ******************************************************************************/ -static void psci_init_aff_map_node(unsigned long mpidr, +static void psci_init_pwr_map_node(unsigned long mpidr, int level, unsigned int idx) { unsigned char state; uint32_t linear_id; - psci_aff_map[idx].mpidr = mpidr; - psci_aff_map[idx].level = level; - psci_lock_init(psci_aff_map, idx); + psci_pwr_domain_map[idx].mpidr = mpidr; + psci_pwr_domain_map[idx].level = level; + psci_lock_init(psci_pwr_domain_map, idx); /* - * If an affinity instance is present then mark it as OFF to begin with. + * If an power domain instance is present then mark it as OFF + * to begin with. */ - state = plat_get_aff_state(level, mpidr); - psci_aff_map[idx].state = state; + state = plat_get_pwr_domain_state(level, mpidr); + psci_pwr_domain_map[idx].state = state; if (level == MPIDR_AFFLVL0) { /* - * Mark the cpu as OFF. Higher affinity level reference counts + * Mark the cpu as OFF. Higher power level reference counts * have already been memset to 0 */ - if (state & PSCI_AFF_PRESENT) - psci_set_state(&psci_aff_map[idx], PSCI_STATE_OFF); + if (state & PSCI_PWR_DOMAIN_PRESENT) + psci_set_state(&psci_pwr_domain_map[idx], + PSCI_STATE_OFF); /* - * Associate a non-secure context with this affinity + * Associate a non-secure context with this power * instance through the context management library. */ linear_id = platform_get_core_pos(mpidr); @@ -228,65 +227,68 @@ static void psci_init_aff_map_node(unsigned long mpidr, /******************************************************************************* * Core routine used by the Breadth-First-Search algorithm to populate the - * affinity tree. Each level in the tree corresponds to an affinity level. This - * routine's aim is to traverse to the target affinity level and populate nodes - * in the 'psci_aff_map' for all the siblings at that level. 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, + * power domain tree. Each level in the tree corresponds to a power level. This + * routine's aim is to traverse to the target power level and populate nodes + * in the 'psci_pwr_domain_map' for all the siblings at that level. It uses the + * current power level to keep track of how many levels from the root of the + * tree have been traversed. If the current power level != target power 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. + * power domain instance has at the current power level. Traversal is then done + * for each child at the next lower level i.e. current power level - 1. * - * CAUTION: This routine assumes that affinity instance ids are allocated in a - * monotonically increasing manner at each affinity level in a mpidr starting + * CAUTION: This routine assumes that power domain instance ids are allocated + * in a monotonically increasing manner at each power 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 psci_init_aff_map(unsigned long mpidr, - unsigned int affmap_idx, - int cur_afflvl, - int tgt_afflvl) +static unsigned int psci_init_pwr_map(unsigned long mpidr, + unsigned int pwrmap_idx, + int cur_pwrlvl, + int tgt_pwrlvl) { - unsigned int ctr, aff_count; + unsigned int ctr, pwr_inst_count; - assert(cur_afflvl >= tgt_afflvl); + assert(cur_pwrlvl >= tgt_pwrlvl); /* - * Find the number of siblings at the current affinity level & + * 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); + pwr_inst_count = plat_get_pwr_domain_count(cur_pwrlvl, mpidr); + assert(pwr_inst_count); - if (tgt_afflvl < cur_afflvl) { - for (ctr = 0; ctr < aff_count; ctr++) { - mpidr = mpidr_set_aff_inst(mpidr, ctr, cur_afflvl); - affmap_idx = psci_init_aff_map(mpidr, - affmap_idx, - cur_afflvl - 1, - tgt_afflvl); + if (tgt_pwrlvl < cur_pwrlvl) { + for (ctr = 0; ctr < pwr_inst_count; ctr++) { + mpidr = mpidr_set_pwr_domain_inst(mpidr, ctr, + cur_pwrlvl); + pwrmap_idx = psci_init_pwr_map(mpidr, + pwrmap_idx, + cur_pwrlvl - 1, + tgt_pwrlvl); } } else { - for (ctr = 0; ctr < aff_count; ctr++, affmap_idx++) { - mpidr = mpidr_set_aff_inst(mpidr, ctr, cur_afflvl); - psci_init_aff_map_node(mpidr, cur_afflvl, affmap_idx); + for (ctr = 0; ctr < pwr_inst_count; ctr++, pwrmap_idx++) { + mpidr = mpidr_set_pwr_domain_inst(mpidr, ctr, + cur_pwrlvl); + psci_init_pwr_map_node(mpidr, cur_pwrlvl, pwrmap_idx); } - /* affmap_idx is 1 greater than the max index of cur_afflvl */ - psci_aff_limits[cur_afflvl].max = affmap_idx - 1; + /* pwrmap_idx is 1 greater than the max index of cur_pwrlvl */ + psci_pwr_lvl_limits[cur_pwrlvl].max = pwrmap_idx - 1; } - return affmap_idx; + return pwrmap_idx; } /******************************************************************************* * This function initializes the topology tree by querying the platform. To do - * so, it's helper routines implement a Breadth-First-Search. At each affinity - * level the platform conveys the number of affinity instances that exist i.e. - * the affinity count. The algorithm populates the psci_aff_map recursively - * using this information. On a platform that implements two clusters of 4 cpus - * each, the populated aff_map_array would look like this: + * so, it's helper routines implement a Breadth-First-Search. At each power + * level the platform conveys the number of power domain instances that exist + * i.e. the power instance count. The algorithm populates the + * psci_pwr_domain_map* recursively using this information. On a platform that + * implements two clusters of 4 cpus each, the populated pwr_map_array would + * look like this: * * <- cpus cluster0 -><- cpus cluster1 -> * --------------------------------------------------- @@ -298,71 +300,72 @@ static unsigned int psci_init_aff_map(unsigned long mpidr, * * The first 2 entries are of the cluster nodes. The next 4 entries are of cpus * within cluster 0. The last 4 entries are of cpus within cluster 1. - * The 'psci_aff_limits' array contains the max & min index of each affinity - * level within the 'psci_aff_map' array. This allows restricting search of a - * node at an affinity level between the indices in the limits array. + * The 'psci_pwr_lvl_limits' array contains the max & min index of each power + * level within the 'psci_pwr_domain_map' array. This allows restricting search + * of a node at a power level between the indices in the limits array. ******************************************************************************/ int32_t psci_setup(void) { unsigned long mpidr = read_mpidr(); - int afflvl, affmap_idx, max_afflvl; - aff_map_node_t *node; + int pwrlvl, pwrmap_idx, max_pwrlvl; + pwr_map_node_t *node; psci_plat_pm_ops = NULL; - /* Find out the maximum affinity level that the platform implements */ - max_afflvl = PLATFORM_MAX_AFFLVL; - assert(max_afflvl <= MPIDR_MAX_AFFLVL); + /* Find out the maximum power level that the platform implements */ + max_pwrlvl = PLAT_MAX_PWR_LVL; + assert(max_pwrlvl <= MPIDR_MAX_AFFLVL); /* * This call traverses the topology tree with help from the platform and - * populates the affinity map using a breadth-first-search recursively. - * 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 + * populates the power map using a breadth-first-search recursively. + * We assume that the platform allocates power domain instance ids from + * 0 onwards at each power level in the mpidr. FIRST_MPIDR = 0.0.0.0 */ - affmap_idx = 0; - for (afflvl = max_afflvl; afflvl >= MPIDR_AFFLVL0; afflvl--) { - affmap_idx = psci_init_aff_map(FIRST_MPIDR, - affmap_idx, - max_afflvl, - afflvl); + pwrmap_idx = 0; + for (pwrlvl = max_pwrlvl; pwrlvl >= MPIDR_AFFLVL0; pwrlvl--) { + pwrmap_idx = psci_init_pwr_map(FIRST_MPIDR, + pwrmap_idx, + max_pwrlvl, + pwrlvl); } #if !USE_COHERENT_MEM /* - * The psci_aff_map only needs flushing when it's not allocated in - * coherent memory. + * The psci_pwr_domain_map only needs flushing when it's not allocated + * in coherent memory. */ - flush_dcache_range((uint64_t) &psci_aff_map, sizeof(psci_aff_map)); + flush_dcache_range((uint64_t) &psci_pwr_domain_map, + sizeof(psci_pwr_domain_map)); #endif /* - * Set the bounds for the affinity counts of each level in the map. Also + * Set the bounds for number of instances of each level in the map. Also * flush out the entire array so that it's visible to subsequent power - * management operations. The 'psci_aff_limits' array is allocated in - * normal memory. It will be accessed when the mmu is off e.g. after + * management operations. The 'psci_pwr_lvl_limits' array is allocated + * in normal memory. It will be accessed when the mmu is off e.g. after * reset. Hence it needs to be flushed. */ - for (afflvl = MPIDR_AFFLVL0; afflvl < max_afflvl; afflvl++) { - psci_aff_limits[afflvl].min = - psci_aff_limits[afflvl + 1].max + 1; + for (pwrlvl = MPIDR_AFFLVL0; pwrlvl < max_pwrlvl; pwrlvl++) { + psci_pwr_lvl_limits[pwrlvl].min = + psci_pwr_lvl_limits[pwrlvl + 1].max + 1; } - flush_dcache_range((unsigned long) psci_aff_limits, - sizeof(psci_aff_limits)); + flush_dcache_range((unsigned long) psci_pwr_lvl_limits, + sizeof(psci_pwr_lvl_limits)); /* - * Mark the affinity instances in our mpidr as ON. No need to lock as - * this is the primary cpu. + * Mark the power domain instances in our mpidr as ON. No need to lock + * as this is the primary cpu. */ mpidr &= MPIDR_AFFINITY_MASK; - for (afflvl = MPIDR_AFFLVL0; afflvl <= max_afflvl; afflvl++) { + for (pwrlvl = MPIDR_AFFLVL0; pwrlvl <= max_pwrlvl; pwrlvl++) { - node = psci_get_aff_map_node(mpidr, afflvl); + node = psci_get_pwr_map_node(mpidr, pwrlvl); assert(node); /* Mark each present node as ON. */ - if (node->state & PSCI_AFF_PRESENT) + if (node->state & PSCI_PWR_DOMAIN_PRESENT) psci_set_state(node, PSCI_STATE_ON); } @@ -372,12 +375,13 @@ int32_t psci_setup(void) /* Initialize the psci capability */ psci_caps = PSCI_GENERIC_CAP; - if (psci_plat_pm_ops->affinst_off) + if (psci_plat_pm_ops->pwr_domain_off) psci_caps |= define_psci_cap(PSCI_CPU_OFF); - if (psci_plat_pm_ops->affinst_on && psci_plat_pm_ops->affinst_on_finish) + if (psci_plat_pm_ops->pwr_domain_on && + psci_plat_pm_ops->pwr_domain_on_finish) psci_caps |= define_psci_cap(PSCI_CPU_ON_AARCH64); - if (psci_plat_pm_ops->affinst_suspend && - psci_plat_pm_ops->affinst_suspend_finish) { + if (psci_plat_pm_ops->pwr_domain_suspend && + psci_plat_pm_ops->pwr_domain_suspend_finish) { psci_caps |= define_psci_cap(PSCI_CPU_SUSPEND_AARCH64); if (psci_plat_pm_ops->get_sys_suspend_power_state) psci_caps |= define_psci_cap(PSCI_SYSTEM_SUSPEND_AARCH64); diff --git a/services/std_svc/psci1.0/psci_afflvl_suspend.c b/services/std_svc/psci1.0/psci_suspend.c similarity index 75% rename from services/std_svc/psci1.0/psci_afflvl_suspend.c rename to services/std_svc/psci1.0/psci_suspend.c index 9b57a47464..7832b82b61 100644 --- a/services/std_svc/psci1.0/psci_afflvl_suspend.c +++ b/services/std_svc/psci1.0/psci_suspend.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-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: @@ -52,18 +52,18 @@ void psci_set_suspend_power_state(unsigned int power_state) } /******************************************************************************* - * This function gets the affinity level till which the current cpu could be + * This function gets the power level till which the current cpu could be * powered down during a cpu_suspend call. Returns PSCI_INVALID_DATA if the * power state is invalid. ******************************************************************************/ -int psci_get_suspend_afflvl(void) +int psci_get_suspend_pwrlvl(void) { unsigned int power_state; power_state = get_cpu_data(psci_svc_cpu_data.power_state); return ((power_state == PSCI_INVALID_DATA) ? - power_state : psci_get_pstate_afflvl(power_state)); + power_state : psci_get_pstate_pwrlvl(power_state)); } /******************************************************************************* @@ -99,54 +99,54 @@ int psci_get_suspend_stateid_by_mpidr(unsigned long mpidr) /******************************************************************************* * Top level handler which is called when a cpu wants to suspend its execution. - * It is assumed that along with suspending the cpu, higher affinity levels - * until the target affinity level will be suspended as well. It finds the - * highest level to be suspended by traversing the node information and then - * performs generic, architectural, platform setup and state management - * required to suspend that affinity level and affinity levels below it. - * e.g. For a cpu that's to be suspended, it could mean programming the - * power controller whereas for a cluster that's to be suspended, it will call - * the platform specific code which will disable coherency at the interconnect - * level if the cpu is the last in the cluster and also the program the power - * controller. + * It is assumed that along with suspending the cpu power domain, power domains + * at higher levels until the target power level will be suspended as well. + * It finds the highest level where a domain has to be suspended by traversing + * the node information and then performs generic, architectural, platform + * setup and state management required to suspend that power domain and domains + * below it. * e.g. For a cpu that's to be suspended, it could mean programming + * the power controller whereas for a cluster that's to be suspended, it will + * call the platform specific code which will disable coherency at the + * interconnect level if the cpu is the last in the cluster and also the + * program the power controller. * * All the required parameter checks are performed at the beginning and after * the state transition has been done, no further error is expected and it is * not possible to undo any of the actions taken beyond that point. ******************************************************************************/ -void psci_afflvl_suspend(entry_point_info_t *ep, - int end_afflvl) +void psci_cpu_suspend_start(entry_point_info_t *ep, + int end_pwrlvl) { int skip_wfi = 0; - mpidr_aff_map_nodes_t mpidr_nodes; - unsigned int max_phys_off_afflvl; + mpidr_pwr_map_nodes_t mpidr_nodes; + unsigned int max_phys_off_pwrlvl; unsigned long psci_entrypoint; /* * This function must only be called on platforms where the * CPU_SUSPEND platform hooks have been implemented. */ - assert(psci_plat_pm_ops->affinst_suspend && - psci_plat_pm_ops->affinst_suspend_finish); + assert(psci_plat_pm_ops->pwr_domain_suspend && + psci_plat_pm_ops->pwr_domain_suspend_finish); /* * Collect the pointers to the nodes in the topology tree for - * each affinity instance in the mpidr. If this function does - * not return successfully then either the mpidr or the affinity + * each power domain instance in the mpidr. If this function does + * not return successfully then either the mpidr or the power * levels are incorrect. Either way, this an internal TF error * therefore assert. */ - if (psci_get_aff_map_nodes(read_mpidr_el1() & MPIDR_AFFINITY_MASK, - MPIDR_AFFLVL0, end_afflvl, mpidr_nodes) != PSCI_E_SUCCESS) + if (psci_get_pwr_map_nodes(read_mpidr_el1() & MPIDR_AFFINITY_MASK, + MPIDR_AFFLVL0, end_pwrlvl, mpidr_nodes) != PSCI_E_SUCCESS) assert(0); /* - * This function acquires the lock corresponding to each affinity + * This function acquires the lock corresponding to each power * level so that by the time all locks are taken, the system topology * is snapshot and state management can be done safely. */ - psci_acquire_afflvl_locks(MPIDR_AFFLVL0, - end_afflvl, + psci_acquire_pwr_domain_locks(MPIDR_AFFLVL0, + end_pwrlvl, mpidr_nodes); /* @@ -168,19 +168,19 @@ void psci_afflvl_suspend(entry_point_info_t *ep, psci_spd_pm->svc_suspend(0); /* - * This function updates the state of each affinity instance - * corresponding to the mpidr in the range of affinity levels + * This function updates the state of each power domain instance + * corresponding to the mpidr in the range of power levels * specified. */ - psci_do_afflvl_state_mgmt(MPIDR_AFFLVL0, - end_afflvl, + psci_do_state_coordination(MPIDR_AFFLVL0, + end_pwrlvl, mpidr_nodes, PSCI_STATE_SUSPEND); - max_phys_off_afflvl = psci_find_max_phys_off_afflvl(MPIDR_AFFLVL0, - end_afflvl, + max_phys_off_pwrlvl = psci_find_max_phys_off_pwrlvl(MPIDR_AFFLVL0, + end_pwrlvl, mpidr_nodes); - assert(max_phys_off_afflvl != PSCI_INVALID_DATA); + assert(max_phys_off_pwrlvl != PSCI_INVALID_DATA); /* * Store the re-entry information for the non-secure world. @@ -188,13 +188,13 @@ void psci_afflvl_suspend(entry_point_info_t *ep, cm_init_context(read_mpidr_el1(), ep); /* Set the secure world (EL3) re-entry point after BL1 */ - psci_entrypoint = (unsigned long) psci_aff_suspend_finish_entry; + psci_entrypoint = (unsigned long) psci_cpu_suspend_finish_entry; /* * Arch. management. Perform the necessary steps to flush all * cpu caches. */ - psci_do_pwrdown_cache_maintenance(max_phys_off_afflvl); + psci_do_pwrdown_cache_maintenance(max_phys_off_pwrlvl); /* * Plat. management: Allow the platform to perform the @@ -202,31 +202,31 @@ void psci_afflvl_suspend(entry_point_info_t *ep, * platform defined mailbox with the psci entrypoint, * program the power controller etc. */ - psci_plat_pm_ops->affinst_suspend(psci_entrypoint, - max_phys_off_afflvl); + psci_plat_pm_ops->pwr_domain_suspend(psci_entrypoint, + max_phys_off_pwrlvl); exit: /* - * Release the locks corresponding to each affinity level in the + * Release the locks corresponding to each power level in the * reverse order to which they were acquired. */ - psci_release_afflvl_locks(MPIDR_AFFLVL0, - end_afflvl, + psci_release_pwr_domain_locks(MPIDR_AFFLVL0, + end_pwrlvl, mpidr_nodes); if (!skip_wfi) psci_power_down_wfi(); } /******************************************************************************* - * The following functions finish an earlier affinity suspend request. They + * The following functions finish an earlier suspend request. They * are called by the common finisher routine in psci_common.c. ******************************************************************************/ -void psci_afflvl_suspend_finisher(aff_map_node_t *node[], int afflvl) +void psci_cpu_suspend_finish(pwr_map_node_t *node[], int pwrlvl) { int32_t suspend_level; uint64_t counter_freq; - assert(node[afflvl]->level == afflvl); + assert(node[pwrlvl]->level == pwrlvl); /* Ensure we have been woken up from a suspended state */ assert(psci_get_state(node[MPIDR_AFFLVL0]) == PSCI_STATE_SUSPEND); @@ -238,7 +238,7 @@ void psci_afflvl_suspend_finisher(aff_map_node_t *node[], int afflvl) * wrong then assert as there is no way to recover from this * situation. */ - psci_plat_pm_ops->affinst_suspend_finish(afflvl); + psci_plat_pm_ops->pwr_domain_suspend_finish(pwrlvl); /* * Arch. management: Enable the data cache, manage stack memory and @@ -257,7 +257,7 @@ void psci_afflvl_suspend_finisher(aff_map_node_t *node[], int afflvl) * error, it's expected to assert within */ if (psci_spd_pm && psci_spd_pm->svc_suspend) { - suspend_level = psci_get_suspend_afflvl(); + suspend_level = psci_get_suspend_pwrlvl(); assert (suspend_level != PSCI_INVALID_DATA); psci_spd_pm->svc_suspend_finish(suspend_level); } diff --git a/services/std_svc/psci1.0/psci_system_off.c b/services/std_svc/psci1.0/psci_system_off.c index 970d4bb501..28315d6b59 100644 --- a/services/std_svc/psci1.0/psci_system_off.c +++ b/services/std_svc/psci1.0/psci_system_off.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2014-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: @@ -37,7 +37,7 @@ void psci_system_off(void) { - psci_print_affinity_map(); + psci_print_power_domain_map(); assert(psci_plat_pm_ops->system_off); @@ -54,7 +54,7 @@ void psci_system_off(void) void psci_system_reset(void) { - psci_print_affinity_map(); + psci_print_power_domain_map(); assert(psci_plat_pm_ops->system_reset); From 12d0d00d1e4714fc59c4abbe1d8a2a3ae8a84f56 Mon Sep 17 00:00:00 2001 From: Soby Mathew Date: Thu, 9 Apr 2015 13:40:55 +0100 Subject: [PATCH 04/20] PSCI: Introduce new platform and CM helper APIs This patch introduces new platform APIs and context management helper APIs to support the new topology framework based on linear core position. This framework will be introduced in the follwoing patch and it removes the assumption that the MPIDR based affinity levels map directly to levels in a power domain tree. The new platforms APIs and context management helpers based on core position are as described below: * plat_my_core_pos() and plat_core_pos_by_mpidr() These 2 new mandatory platform APIs are meant to replace the existing 'platform_get_core_pos()' API. The 'plat_my_core_pos()' API returns the linear index of the calling core and 'plat_core_pos_by_mpidr()' returns the linear index of a core specified by its MPIDR. The latter API will also validate the MPIDR passed as an argument and will return an error code (-1) if an invalid MPIDR is passed as the argument. This enables the caller to safely convert an MPIDR of another core to its linear index without querying the PSCI topology tree e.g. during a call to PSCI CPU_ON. Since the 'plat_core_pos_by_mpidr()' API verifies an MPIDR, which is always platform specific, it is no longer possible to maintain a default implementation of this API. Also it might not be possible for a platform port to verify an MPIDR before the C runtime has been setup or the topology has been initialized. This would prevent 'plat_core_pos_by_mpidr()' from being callable prior to topology setup. As a result, the generic Trusted Firmware code does not call this API before the topology setup has been done. The 'plat_my_core_pos' API should be able to run without a C runtime. Since this API needs to return a core position which is equal to the one returned by 'plat_core_pos_by_mpidr()' API for the corresponding MPIDR, this too cannot have default implementation and is a mandatory API for platform ports. These APIs will be implemented by the ARM reference platform ports later in the patch stack. * plat_get_my_stack() and plat_set_my_stack() These APIs are the stack management APIs which set/return stack addresses appropriate for the calling core. These replace the 'platform_get_stack()' and 'platform_set_stack()' APIs. A default weak MP version and a global UP version of these APIs are provided for the platforms. * Context management helpers based on linear core position A set of new context management(CM) helpers viz cm_get_context_by_index(), cm_set_context_by_index(), cm_init_my_context() and cm_init_context_by_index() are defined which are meant to replace the old helpers which took MPIDR as argument. The old CM helpers are implemented based on the new helpers to allow for code consolidation and will be deprecated once the switch to the new framework is done. Change-Id: I89758632b370c2812973a4b2efdd9b81a41f9b69 --- bl31/context_mgmt.c | 85 ++++++++++++++++++++++--- include/bl31/context_mgmt.h | 10 ++- include/common/asm_macros.S | 14 ++++ include/plat/common/psci1.0/platform.h | 5 +- plat/common/aarch64/platform_mp_stack.S | 30 ++++++++- plat/common/aarch64/platform_up_stack.S | 14 ++-- services/std_svc/psci1.0/psci_common.c | 14 ++-- services/std_svc/psci1.0/psci_helpers.S | 6 +- services/std_svc/psci1.0/psci_main.c | 7 +- services/std_svc/psci1.0/psci_on.c | 3 +- services/std_svc/psci1.0/psci_private.h | 2 +- services/std_svc/psci1.0/psci_setup.c | 51 ++++++++------- services/std_svc/psci1.0/psci_suspend.c | 4 +- 13 files changed, 182 insertions(+), 63 deletions(-) diff --git a/bl31/context_mgmt.c b/bl31/context_mgmt.c index 6f27176c1d..a0fd1b6541 100644 --- a/bl31/context_mgmt.c +++ b/bl31/context_mgmt.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-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: @@ -64,6 +64,32 @@ void cm_init(void) */ } +/******************************************************************************* + * This function returns a pointer to the most recent 'cpu_context' structure + * for the CPU identified by `cpu_idx` that was set as the context for the + * specified security state. NULL is returned if no such structure has been + * specified. + ******************************************************************************/ +void *cm_get_context_by_index(unsigned int cpu_idx, + unsigned int security_state) +{ + assert(sec_state_is_valid(security_state)); + + return get_cpu_data_by_index(cpu_idx, cpu_context[security_state]); +} + +/******************************************************************************* + * This function sets the pointer to the current 'cpu_context' structure for the + * specified security state for the CPU identified by CPU index. + ******************************************************************************/ +void cm_set_context_by_index(unsigned int cpu_idx, void *context, + unsigned int security_state) +{ + assert(sec_state_is_valid(security_state)); + + set_cpu_data_by_index(cpu_idx, cpu_context[security_state], context); +} + /******************************************************************************* * This function returns a pointer to the most recent 'cpu_context' structure * for the CPU identified by MPIDR that was set as the context for the specified @@ -73,7 +99,7 @@ void *cm_get_context_by_mpidr(uint64_t mpidr, uint32_t security_state) { assert(sec_state_is_valid(security_state)); - return get_cpu_data_by_mpidr(mpidr, cpu_context[security_state]); + return cm_get_context_by_index(platform_get_core_pos(mpidr), security_state); } /******************************************************************************* @@ -84,7 +110,8 @@ void cm_set_context_by_mpidr(uint64_t mpidr, void *context, uint32_t security_st { assert(sec_state_is_valid(security_state)); - set_cpu_data_by_mpidr(mpidr, cpu_context[security_state], context); + cm_set_context_by_index(platform_get_core_pos(mpidr), + context, security_state); } /******************************************************************************* @@ -114,7 +141,7 @@ static inline void cm_set_next_context(void *context) } /******************************************************************************* - * The following function initializes a cpu_context for the current CPU for + * The following function initializes the cpu_context 'ctx' for * first use, and sets the initial entrypoint state as specified by the * entry_point_info structure. * @@ -123,25 +150,24 @@ static inline void cm_set_next_context(void *context) * context and sets this as the next context to return to. * * The EE and ST attributes are used to configure the endianess and secure - * timer availability for the new excution context. + * timer availability for the new execution context. * * To prepare the register state for entry call cm_prepare_el3_exit() and * el3_exit(). For Secure-EL1 cm_prepare_el3_exit() is equivalent to * cm_e1_sysreg_context_restore(). ******************************************************************************/ -void cm_init_context(uint64_t mpidr, const entry_point_info_t *ep) +static void cm_init_context_common(cpu_context_t *ctx, const entry_point_info_t *ep) { - uint32_t security_state; - cpu_context_t *ctx; + unsigned int security_state; uint32_t scr_el3; el3_state_t *state; gp_regs_t *gp_regs; unsigned long sctlr_elx; - security_state = GET_SECURITY_STATE(ep->h.attr); - ctx = cm_get_context_by_mpidr(mpidr, security_state); assert(ctx); + security_state = GET_SECURITY_STATE(ep->h.attr); + /* Clear any residual register values from the context */ memset(ctx, 0, sizeof(*ctx)); @@ -209,6 +235,45 @@ void cm_init_context(uint64_t mpidr, const entry_point_info_t *ep) memcpy(gp_regs, (void *)&ep->args, sizeof(aapcs64_params_t)); } +/******************************************************************************* + * The following function initializes the cpu_context for a CPU specified by + * its `cpu_idx` for first use, and sets the initial entrypoint state as + * specified by the entry_point_info structure. + ******************************************************************************/ +void cm_init_context_by_index(unsigned int cpu_idx, + const entry_point_info_t *ep) +{ + cpu_context_t *ctx; + ctx = cm_get_context_by_index(cpu_idx, GET_SECURITY_STATE(ep->h.attr)); + cm_init_context_common(ctx, ep); +} + +/******************************************************************************* + * The following function initializes the cpu_context for the current CPU + * for first use, and sets the initial entrypoint state as specified by the + * entry_point_info structure. + ******************************************************************************/ +void cm_init_my_context(const entry_point_info_t *ep) +{ + cpu_context_t *ctx; + ctx = cm_get_context(GET_SECURITY_STATE(ep->h.attr)); + cm_init_context_common(ctx, ep); +} + +/******************************************************************************* + * The following function provides a compatibility function for SPDs using the + * existing cm library routines. This function is expected to be invoked for + * initializing the cpu_context for the CPU specified by MPIDR for first use. + ******************************************************************************/ +void cm_init_context(unsigned long mpidr, const entry_point_info_t *ep) +{ + if ((mpidr & MPIDR_AFFINITY_MASK) == + (read_mpidr_el1() & MPIDR_AFFINITY_MASK)) + cm_init_my_context(ep); + else + cm_init_context_by_index(platform_get_core_pos(mpidr), ep); +} + /******************************************************************************* * Prepare the CPU system registers for first entry into secure or normal world * diff --git a/include/bl31/context_mgmt.h b/include/bl31/context_mgmt.h index 6e82fb70e8..7e9fe832c4 100644 --- a/include/bl31/context_mgmt.h +++ b/include/bl31/context_mgmt.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-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: @@ -48,8 +48,16 @@ static inline void *cm_get_context(uint32_t security_state); void cm_set_context_by_mpidr(uint64_t mpidr, void *context, uint32_t security_state); +void *cm_get_context_by_index(unsigned int cpu_idx, + unsigned int security_state); +void cm_set_context_by_index(unsigned int cpu_idx, + void *context, + unsigned int security_state); static inline void cm_set_context(void *context, uint32_t security_state); void cm_init_context(uint64_t mpidr, const struct entry_point_info *ep); +void cm_init_my_context(const struct entry_point_info *ep); +void cm_init_context_by_index(unsigned int cpu_idx, + const struct entry_point_info *ep); void cm_prepare_el3_exit(uint32_t security_state); void cm_el1_sysregs_context_save(uint32_t security_state); void cm_el1_sysregs_context_restore(uint32_t security_state); diff --git a/include/common/asm_macros.S b/include/common/asm_macros.S index 45058a60f3..902127ec00 100644 --- a/include/common/asm_macros.S +++ b/include/common/asm_macros.S @@ -130,6 +130,20 @@ madd x0, x0, x1, x2 .endm + /* + * This macro calculates the base address of the current CPU's MP stack + * using the plat_my_core_pos() index, the name of the stack storage + * and the size of each stack + * Out: X0 = physical address of stack base + * Clobber: X30, X1, X2 + */ + .macro get_my_mp_stack _name, _size + bl plat_my_core_pos + ldr x2, =(\_name + \_size) + mov x1, #\_size + madd x0, x0, x1, x2 + .endm + /* * This macro calculates the base address of a UP stack using the * name of the stack storage and the size of the stack diff --git a/include/plat/common/psci1.0/platform.h b/include/plat/common/psci1.0/platform.h index 50af4f85e6..059320f10a 100644 --- a/include/plat/common/psci1.0/platform.h +++ b/include/plat/common/psci1.0/platform.h @@ -59,6 +59,8 @@ int plat_get_image_source(unsigned int image_id, uintptr_t *dev_handle, 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); /******************************************************************************* * Mandatory interrupt management functions @@ -74,8 +76,7 @@ uint32_t plat_interrupt_type_to_line(uint32_t type, /******************************************************************************* * Optional common functions (may be overridden) ******************************************************************************/ -unsigned int platform_get_core_pos(unsigned long mpidr); -unsigned long platform_get_stack(unsigned long mpidr); +unsigned long plat_get_my_stack(void); void plat_report_exception(unsigned long); int plat_crash_console_init(void); int plat_crash_console_putc(int c); diff --git a/plat/common/aarch64/platform_mp_stack.S b/plat/common/aarch64/platform_mp_stack.S index 7a98d290dd..0cea9ac357 100644 --- a/plat/common/aarch64/platform_mp_stack.S +++ b/plat/common/aarch64/platform_mp_stack.S @@ -36,7 +36,8 @@ .local platform_normal_stacks .weak platform_set_stack .weak platform_get_stack - + .weak plat_get_my_stack + .weak plat_set_my_stack /* ----------------------------------------------------- * unsigned long platform_get_stack (unsigned long mpidr) @@ -65,6 +66,33 @@ func platform_set_stack ret x9 endfunc platform_set_stack + /* ----------------------------------------------------- + * 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 + mov x10, x30 // lr + get_my_mp_stack platform_normal_stacks, PLATFORM_STACK_SIZE + ret x10 +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 + mov x9, x30 // lr + bl plat_get_my_stack + mov sp, x0 + ret x9 +endfunc plat_set_my_stack + /* ----------------------------------------------------- * Per-cpu stacks in normal memory. Each cpu gets a * stack of PLATFORM_STACK_SIZE bytes. diff --git a/plat/common/aarch64/platform_up_stack.S b/plat/common/aarch64/platform_up_stack.S index ea6641a7ea..d6d6c6e223 100644 --- a/plat/common/aarch64/platform_up_stack.S +++ b/plat/common/aarch64/platform_up_stack.S @@ -34,10 +34,13 @@ .local platform_normal_stacks + .globl plat_set_my_stack + .globl plat_get_my_stack .globl platform_set_stack .globl platform_get_stack /* ----------------------------------------------------- + * unsigned long plat_get_my_stack () * unsigned long platform_get_stack (unsigned long) * * For cold-boot BL images, only the primary CPU needs a @@ -45,12 +48,14 @@ * stack allocated in device memory. * ----------------------------------------------------- */ -func platform_get_stack +func plat_get_my_stack +platform_get_stack: get_up_stack platform_normal_stacks, PLATFORM_STACK_SIZE ret -endfunc platform_get_stack +endfunc plat_get_my_stack /* ----------------------------------------------------- + * void plat_set_my_stack () * void platform_set_stack (unsigned long) * * For cold-boot BL images, only the primary CPU needs a @@ -58,11 +63,12 @@ endfunc platform_get_stack * allocated in normal memory. * ----------------------------------------------------- */ -func platform_set_stack +func plat_set_my_stack +platform_set_stack: get_up_stack platform_normal_stacks, PLATFORM_STACK_SIZE mov sp, x0 ret -endfunc platform_set_stack +endfunc plat_set_my_stack /* ----------------------------------------------------- * Single cpu stack in normal memory. diff --git a/services/std_svc/psci1.0/psci_common.c b/services/std_svc/psci1.0/psci_common.c index 55578379dc..d9e9ccee0f 100644 --- a/services/std_svc/psci1.0/psci_common.c +++ b/services/std_svc/psci1.0/psci_common.c @@ -264,18 +264,14 @@ void psci_release_pwr_domain_locks(int start_pwrlvl, } /******************************************************************************* - * Simple routine to determine whether an power domain instance at a given - * level in an mpidr exists or not. + * Simple routine to determine whether a mpidr is valid or not. ******************************************************************************/ -int psci_validate_mpidr(unsigned long mpidr, int level) +int psci_validate_mpidr(unsigned long mpidr) { - pwr_map_node_t *node; - - node = psci_get_pwr_map_node(mpidr, level); - if (node && (node->state & PSCI_PWR_DOMAIN_PRESENT)) - return PSCI_E_SUCCESS; - else + if (plat_core_pos_by_mpidr(mpidr) < 0) return PSCI_E_INVALID_PARAMS; + + return PSCI_E_SUCCESS; } /******************************************************************************* diff --git a/services/std_svc/psci1.0/psci_helpers.S b/services/std_svc/psci1.0/psci_helpers.S index 07fb889389..7ef4cdd64e 100644 --- a/services/std_svc/psci1.0/psci_helpers.S +++ b/services/std_svc/psci1.0/psci_helpers.S @@ -82,8 +82,7 @@ do_core_pwr_dwn: * --------------------------------------------- */ do_stack_maintenance: - mrs x0, mpidr_el1 - bl platform_get_stack + bl plat_get_my_stack /* --------------------------------------------- * Calculate and store the size of the used @@ -136,8 +135,7 @@ func psci_do_pwrup_cache_maintenance * stack base address in x0. * --------------------------------------------- */ - mrs x0, mpidr_el1 - bl platform_get_stack + bl plat_get_my_stack mov x1, sp sub x1, x0, x1 mov x0, sp diff --git a/services/std_svc/psci1.0/psci_main.c b/services/std_svc/psci1.0/psci_main.c index 0421b15f82..f87fd52e7c 100644 --- a/services/std_svc/psci1.0/psci_main.c +++ b/services/std_svc/psci1.0/psci_main.c @@ -50,10 +50,9 @@ int psci_cpu_on(unsigned long target_cpu, entry_point_info_t ep; /* Determine if the cpu exists of not */ - rc = psci_validate_mpidr(target_cpu, MPIDR_AFFLVL0); - if (rc != PSCI_E_SUCCESS) { + rc = psci_validate_mpidr(target_cpu); + if (rc != PSCI_E_SUCCESS) return PSCI_E_INVALID_PARAMS; - } /* Validate the entrypoint using platform pm_ops */ if (psci_plat_pm_ops->validate_ns_entrypoint) { @@ -287,7 +286,7 @@ int psci_migrate(unsigned long target_cpu) return PSCI_E_NOT_PRESENT; /* Check the validity of the specified target cpu */ - rc = psci_validate_mpidr(target_cpu, MPIDR_AFFLVL0); + rc = psci_validate_mpidr(target_cpu); if (rc != PSCI_E_SUCCESS) return PSCI_E_INVALID_PARAMS; diff --git a/services/std_svc/psci1.0/psci_on.c b/services/std_svc/psci1.0/psci_on.c index 43f58f9b93..c78e119a6c 100644 --- a/services/std_svc/psci1.0/psci_on.c +++ b/services/std_svc/psci1.0/psci_on.c @@ -147,13 +147,14 @@ int psci_cpu_on_start(unsigned long target_cpu, if (rc == PSCI_E_SUCCESS) /* Store the re-entry information for the non-secure world. */ - cm_init_context(target_cpu, ep); + cm_init_context_by_index(target_idx, ep); else /* Restore the state on error. */ psci_do_state_coordination(MPIDR_AFFLVL0, end_pwrlvl, target_cpu_nodes, PSCI_STATE_OFF); + exit: /* * This loop releases the lock corresponding to each power level diff --git a/services/std_svc/psci1.0/psci_private.h b/services/std_svc/psci1.0/psci_private.h index e56d848bce..79909a8629 100644 --- a/services/std_svc/psci1.0/psci_private.h +++ b/services/std_svc/psci1.0/psci_private.h @@ -123,7 +123,7 @@ unsigned short psci_get_state(pwr_map_node_t *node); unsigned short psci_get_phys_state(pwr_map_node_t *node); void psci_set_state(pwr_map_node_t *node, unsigned short state); unsigned long mpidr_set_pwr_domain_inst(unsigned long, unsigned char, int); -int psci_validate_mpidr(unsigned long, int); +int psci_validate_mpidr(unsigned long mpidr); int get_power_on_target_pwrlvl(void); void psci_power_up_finish(int end_pwrlvl, pwrlvl_power_on_finisher_t pon_handler); diff --git a/services/std_svc/psci1.0/psci_setup.c b/services/std_svc/psci1.0/psci_setup.c index 002e220e6e..b1eef69f75 100644 --- a/services/std_svc/psci1.0/psci_setup.c +++ b/services/std_svc/psci1.0/psci_setup.c @@ -193,36 +193,39 @@ static void psci_init_pwr_map_node(unsigned long mpidr, state = plat_get_pwr_domain_state(level, mpidr); psci_pwr_domain_map[idx].state = state; - if (level == MPIDR_AFFLVL0) { + /* + * Check if this is a CPU node and is present in which case certain + * other initialisations are required. + */ + if (level != MPIDR_AFFLVL0) + return; - /* - * Mark the cpu as OFF. Higher power level reference counts - * have already been memset to 0 - */ - if (state & PSCI_PWR_DOMAIN_PRESENT) - psci_set_state(&psci_pwr_domain_map[idx], - PSCI_STATE_OFF); + if (!(state & PSCI_PWR_DOMAIN_PRESENT)) + return; - /* - * Associate a non-secure context with this power - * instance through the context management library. - */ - linear_id = platform_get_core_pos(mpidr); - assert(linear_id < PLATFORM_CORE_COUNT); + /* + * Mark the cpu as OFF. Higher power level reference counts + * have already been memset to 0 + */ + psci_set_state(&psci_pwr_domain_map[idx], PSCI_STATE_OFF); - /* Invalidate the suspend context for the node */ - set_cpu_data_by_index(linear_id, - psci_svc_cpu_data.power_state, - PSCI_INVALID_DATA); + /* + * Associate a non-secure context with this power + * instance through the context management library. + */ + linear_id = plat_core_pos_by_mpidr(mpidr); + assert(linear_id < PLATFORM_CORE_COUNT); - flush_cpu_data_by_index(linear_id, psci_svc_cpu_data); + /* Invalidate the suspend context for the node */ + set_cpu_data_by_index(linear_id, + psci_svc_cpu_data.power_state, + PSCI_INVALID_DATA); - cm_set_context_by_mpidr(mpidr, - (void *) &psci_ns_context[linear_id], - NON_SECURE); - } + flush_cpu_data_by_index(linear_id, psci_svc_cpu_data); - return; + cm_set_context_by_index(linear_id, + (void *) &psci_ns_context[linear_id], + NON_SECURE); } /******************************************************************************* diff --git a/services/std_svc/psci1.0/psci_suspend.c b/services/std_svc/psci1.0/psci_suspend.c index 7832b82b61..3d1bf09e85 100644 --- a/services/std_svc/psci1.0/psci_suspend.c +++ b/services/std_svc/psci1.0/psci_suspend.c @@ -90,7 +90,7 @@ int psci_get_suspend_stateid_by_mpidr(unsigned long mpidr) { unsigned int power_state; - power_state = get_cpu_data_by_mpidr(mpidr, + power_state = get_cpu_data_by_index(plat_core_pos_by_mpidr(mpidr), psci_svc_cpu_data.power_state); return ((power_state == PSCI_INVALID_DATA) ? @@ -185,7 +185,7 @@ void psci_cpu_suspend_start(entry_point_info_t *ep, /* * Store the re-entry information for the non-secure world. */ - cm_init_context(read_mpidr_el1(), ep); + cm_init_my_context(ep); /* Set the secure world (EL3) re-entry point after BL1 */ psci_entrypoint = (unsigned long) psci_cpu_suspend_finish_entry; From 82dcc039812fafb912c9a7e582ffa3fcc1db2d71 Mon Sep 17 00:00:00 2001 From: Soby Mathew Date: Wed, 8 Apr 2015 17:42:06 +0100 Subject: [PATCH 05/20] PSCI: Introduce new platform interface to describe topology This patch removes the assumption in the current PSCI implementation that MPIDR based affinity levels map directly to levels in a power domain tree. This enables PSCI generic code to support complex power domain topologies as envisaged by PSCIv1.0 specification. The platform interface for querying the power domain topology has been changed such that: 1. The generic PSCI code does not generate MPIDRs and use them to query the platform about the number of power domains at a particular power level. The platform now provides a description of the power domain tree on the SoC through a data structure. The existing platform APIs to provide the same information have been removed. 2. The linear indices returned by plat_core_pos_by_mpidr() and plat_my_core_pos() are used to retrieve core power domain nodes from the power domain tree. Power domains above the core level are accessed using a 'parent' field in the tree node descriptors. The platform describes the power domain tree in an array of 'unsigned char's. The first entry in the array specifies the number of power domains at the highest power level implemented in the system. Each susbsequent entry corresponds to a power domain and contains the number of power domains that are its direct children. This array is exported to the generic PSCI implementation via the new `plat_get_power_domain_tree_desc()` platform API. The PSCI generic code uses this array to populate its internal power domain tree using the Breadth First Search like algorithm. The tree is split into two arrays: 1. An array that contains all the core power domain nodes 2. An array that contains all the other power domain nodes A separate array for core nodes allows certain core specific optimisations to be implemented e.g. remove the bakery lock, re-use per-cpu data framework for storing some information. Entries in the core power domain array are allocated such that the array index of the domain is equal to the linear index returned by plat_core_pos_by_mpidr() and plat_my_core_pos() for the MPIDR corresponding to that domain. This relationship is key to be able to use an MPIDR to find the corresponding core power domain node, traverse to higher power domain nodes and index into arrays that contain core specific information. An introductory document has been added to briefly describe the new interface. Change-Id: I4b444719e8e927ba391cae48a23558308447da13 --- docs/psci-pd-tree.md | 295 +++++++++++++++ include/bl31/services/psci1.0/psci.h | 33 +- include/plat/common/psci1.0/platform.h | 3 +- services/std_svc/psci1.0/psci_common.c | 326 ++++++++--------- services/std_svc/psci1.0/psci_helpers.S | 3 +- services/std_svc/psci1.0/psci_main.c | 34 +- services/std_svc/psci1.0/psci_off.c | 41 +-- services/std_svc/psci1.0/psci_on.c | 76 ++-- services/std_svc/psci1.0/psci_private.h | 151 +++++--- services/std_svc/psci1.0/psci_setup.c | 454 +++++++++--------------- services/std_svc/psci1.0/psci_suspend.c | 57 +-- 11 files changed, 785 insertions(+), 688 deletions(-) create mode 100644 docs/psci-pd-tree.md diff --git a/docs/psci-pd-tree.md b/docs/psci-pd-tree.md new file mode 100644 index 0000000000..6ae686d8db --- /dev/null +++ b/docs/psci-pd-tree.md @@ -0,0 +1,295 @@ +------------ +Requirements +------------ + +1. A platform must export the `plat_get_aff_count()` and + `plat_get_aff_state()` APIs to enable the generic PSCI code to + populate a tree that describes the hierarchy of power domains in the + system. This approach is inflexible because a change to the topology + requires a change in the code. + + It would be much simpler for the platform to describe its power domain tree + in a data structure. + +2. The generic PSCI code generates MPIDRs in order to populate the power domain + tree. It also uses an MPIDR to find a node in the tree. The assumption that + a platform will use exactly the same MPIDRs as generated by the generic PSCI + code is not scalable. The use of an MPIDR also restricts the number of + levels in the power domain tree to four. + + Therefore, there is a need to decouple allocation of MPIDRs from the + mechanism used to populate the power domain topology tree. + +3. The current arrangement of the power domain tree requires a binary search + over the sibling nodes at a particular level to find a specified power + domain node. During a power management operation, the tree is traversed from + a 'start' to an 'end' power level. The binary search is required to find the + node at each level. The natural way to perform this traversal is to + start from a leaf node and follow the parent node pointer to reach the end + level. + + Therefore, there is a need to define data structures that implement the tree in + a way which facilitates such a traversal. + +4. The attributes of a core power domain differ from the attributes of power + domains at higher levels. For example, only a core power domain can be identified + using an MPIDR. There is no requirement to perform state coordination while + performing a power management operation on the core power domain. + + Therefore, there is a need to implement the tree in a way which facilitates this + distinction between a leaf and non-leaf node and any associated + optimizations. + + +------ +Design +------ + +### Describing a power domain tree + +To fulfill requirement 1., the existing platform APIs +`plat_get_aff_count()` and `plat_get_aff_state()` have been +removed. A platform must define an array of unsigned chars such that: + +1. The first entry in the array specifies the number of power domains at the + highest power level implemented in the platform. This caters for platforms + where the power domain tree does not have a single root node, for example, + the FVP has two cluster power domains at the highest level (1). + +2. Each subsequent entry corresponds to a power domain and contains the number + of power domains that are its direct children. + +3. The size of the array minus the first entry will be equal to the number of + non-leaf power domains. + +4. The value in each entry in the array is used to find the number of entries + to consider at the next level. The sum of the values (number of children) of + all the entries at a level specifies the number of entries in the array for + the next level. + +The following example power domain topology tree will be used to describe the +above text further. The leaf and non-leaf nodes in this tree have been numbered +separately. + +``` + +-+ + |0| + +-+ + / \ + / \ + / \ + / \ + / \ + / \ + / \ + / \ + / \ + / \ + +-+ +-+ + |1| |2| + +-+ +-+ + / \ / \ + / \ / \ + / \ / \ + / \ / \ + +-+ +-+ +-+ +-+ + |3| |4| |5| |6| + +-+ +-+ +-+ +-+ + +---+-----+ +----+----| +----+----+ +----+-----+-----+ + | | | | | | | | | | | | | + | | | | | | | | | | | | | + v v v v v v v v v v v v v + +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +--+ +--+ +--+ + |0| |1| |2| |3| |4| |5| |6| |7| |8| |9| |10| |11| |12| + +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +--+ +--+ +--+ +``` + + +This tree is defined by the platform as the array described above as follows: + +``` + #define PLAT_NUM_POWER_DOMAINS 20 + #define PLATFORM_CORE_COUNT 13 + #define PSCI_NUM_NON_CPU_PWR_DOMAINS \ + (PLAT_NUM_POWER_DOMAINS - PLATFORM_CORE_COUNT) + + unsigned char plat_power_domain_tree_desc[] = { 1, 2, 2, 2, 3, 3, 3, 4}; +``` + +### Removing assumptions about MPIDRs used in a platform + +To fulfill requirement 2., it is assumed that the platform assigns a +unique number (core index) between `0` and `PLAT_CORE_COUNT - 1` to each core +power domain. MPIDRs could be allocated in any manner and will not be used to +populate the tree. + +`plat_core_pos_by_mpidr(mpidr)` will return the core index for the core +corresponding to the MPIDR. It will return an error (-1) if an MPIDR is passed +which is not allocated or corresponds to an absent core. The semantics of this +platform API have changed since it is required to validate the passed MPIDR. It +has been made a mandatory API as a result. + +Another mandatory API, `plat_my_core_pos()` has been added to return the core +index for the calling core. This API provides a more lightweight mechanism to get +the index since there is no need to validate the MPIDR of the calling core. + +The platform should assign the core indices (as illustrated in the diagram above) +such that, if the core nodes are numbered from left to right, then the index +for a core domain will be the same as the index returned by + `plat_core_pos_by_mpidr()` or `plat_my_core_pos()` for that core. This +relationship allows the core nodes to be allocated in a separate array +(requirement 4.) during `psci_setup()` in such an order that the index of the +core in the array is the same as the return value from these APIs. + +#### Dealing with holes in MPIDR allocation + +For platforms where the number of allocated MPIDRs is equal to the number of +core power domains, for example, Juno and FVPs, the logic to convert an MPIDR to +a core index should remain unchanged. Both Juno and FVP use a simple collision +proof hash function to do this. + +It is possible that on some platforms, the allocation of MPIDRs is not +contiguous or certain cores have been disabled. This essentially means that the +MPIDRs have been sparsely allocated, that is, the size of the range of MPIDRs +used by the platform is not equal to the number of core power domains. + +The platform could adopt one of the following approaches to deal with this +scenario: + +1. Implement more complex logic to convert a valid MPIDR to a core index while + maintaining the relationship described earlier. This means that the power + domain tree descriptor will not describe any core power domains which are + disabled or absent. Entries will not be allocated in the tree for these + domains. + +2. Treat unallocated MPIDRs and disabled cores as absent but still describe them + in the power domain descriptor, that is, the number of core nodes described + is equal to the size of the range of MPIDRs allocated. This approach will + lead to memory wastage since entries will be allocated in the tree but will + allow use of a simpler logic to convert an MPIDR to a core index. + + +### Traversing through and distinguishing between core and non-core power domains + +To fulfill requirement 3 and 4, separate data structures have been defined +to represent leaf and non-leaf power domain nodes in the tree. + +``` +/******************************************************************************* + * The following two data structures implement the power domain tree. The tree + * is used to track the state of all the nodes i.e. power domain instances + * described by the platform. The tree consists of nodes that describe CPU power + * domains i.e. leaf nodes and all other power domains which are parents of a + * CPU power domain i.e. non-leaf nodes. + ******************************************************************************/ +typedef struct non_cpu_pwr_domain_node { + /* + * Index of the first CPU power domain node level 0 which has this node + * as its parent. + */ + unsigned int cpu_start_idx; + + /* + * Number of CPU power domains which are siblings of the domain indexed + * by 'cpu_start_idx' i.e. all the domains in the range 'cpu_start_idx + * -> cpu_start_idx + ncpus' have this node as their parent. + */ + unsigned int ncpus; + + /* Index of the parent power domain node */ + unsigned int parent_node; + + ----- +} non_cpu_pd_node_t; + +typedef struct cpu_pwr_domain_node { + unsigned long mpidr; + + /* Index of the parent power domain node */ + unsigned int parent_node; + + ----- +} cpu_pd_node_t; +``` + +The power domain tree is implemented as a combination of the following data +structures. + +``` +non_cpu_pd_node_t psci_non_cpu_pd_nodes[PSCI_NUM_NON_CPU_PWR_DOMAINS]; +cpu_pd_node_t psci_cpu_pd_nodes[PLATFORM_CORE_COUNT]; +``` + +### Populating the power domain tree + +The `populate_power_domain_tree()` function in `psci_setup.c` implements the +algorithm to parse the power domain descriptor exported by the platform to +populate the two arrays. It is essentially a breadth-first-search. The nodes for +each level starting from the root are laid out one after another in the +`psci_non_cpu_pd_nodes` and `psci_cpu_pd_nodes` arrays as follows: + +``` +psci_non_cpu_pd_nodes -> [[Level 3 nodes][Level 2 nodes][Level 1 nodes]] +psci_cpu_pd_nodes -> [Level 0 nodes] +``` + +For the example power domain tree illustrated above, the `psci_cpu_pd_nodes` +will be populated as follows. The value in each entry is the index of the parent +node. Other fields have been ignored for simplicity. + +``` + +-------------+ ^ + CPU0 | 3 | | + +-------------+ | + CPU1 | 3 | | + +-------------+ | + CPU2 | 3 | | + +-------------+ | + CPU3 | 4 | | + +-------------+ | + CPU4 | 4 | | + +-------------+ | + CPU5 | 4 | | PLATFORM_CORE_COUNT + +-------------+ | + CPU6 | 5 | | + +-------------+ | + CPU7 | 5 | | + +-------------+ | + CPU8 | 5 | | + +-------------+ | + CPU9 | 6 | | + +-------------+ | + CPU10 | 6 | | + +-------------+ | + CPU11 | 6 | | + +-------------+ | + CPU12 | 6 | v + +-------------+ +``` + +The `psci_non_cpu_pd_nodes` array will be populated as follows. The value in +each entry is the index of the parent node. + +``` + +-------------+ ^ + PD0 | -1 | | + +-------------+ | + PD1 | 0 | | + +-------------+ | + PD2 | 0 | | + +-------------+ | + PD3 | 1 | | PLAT_NUM_POWER_DOMAINS - + +-------------+ | PLATFORM_CORE_COUNT + PD4 | 1 | | + +-------------+ | + PD5 | 2 | | + +-------------+ | + PD6 | 2 | | + +-------------+ v +``` + +Each core can find its node in the `psci_cpu_pd_nodes` array using the +`plat_my_core_pos()` function. When a core is turned on, the normal world +provides an MPIDR. The `plat_core_pos_by_mpidr()` function is used to validate +the MPIDR before using it to find the corresponding core node. The non-core power +domain nodes do not need to be identified. diff --git a/include/bl31/services/psci1.0/psci.h b/include/bl31/services/psci1.0/psci.h index d00796c5f4..c31562c0ef 100644 --- a/include/bl31/services/psci1.0/psci.h +++ b/include/bl31/services/psci1.0/psci.h @@ -43,6 +43,19 @@ #define PSCI_NUM_PWR_DOMAINS (2 * PLATFORM_CORE_COUNT) #endif +#define PSCI_NUM_NON_CPU_PWR_DOMAINS (PSCI_NUM_PWR_DOMAINS - \ + PLATFORM_CORE_COUNT) + +/* This is the power level corresponding to a CPU */ +#define PSCI_CPU_PWR_LVL 0 + +/* + * The maximum power level supported by PSCI. Since PSCI CPU_SUSPEND + * uses the old power_state parameter format which has 2 bits to specify the + * power level, this constant is defined to be 3. + */ +#define PSCI_MAX_PWR_LVL 3 + /******************************************************************************* * Defines for runtime services func ids ******************************************************************************/ @@ -137,16 +150,11 @@ #define PSCI_E_NOT_PRESENT -7 #define PSCI_E_DISABLED -8 -/******************************************************************************* - * PSCI power domain state related constants. A power domain instance could - * be present or absent physically to cater for asymmetric topologies. If - * present then it could be in one of the 4 further defined states. - ******************************************************************************/ -#define PSCI_STATE_SHIFT 1 -#define PSCI_STATE_MASK 0xff +#define PSCI_INVALID_MPIDR ~(0ULL) -#define PSCI_PWR_DOMAIN_ABSENT 0x0 -#define PSCI_PWR_DOMAIN_PRESENT 0x1 +/******************************************************************************* + * PSCI power domain state related constants. + ******************************************************************************/ #define PSCI_STATE_ON 0x0 #define PSCI_STATE_OFF 0x1 #define PSCI_STATE_ON_PENDING 0x2 @@ -170,9 +178,10 @@ * this information will not reside on a cache line shared with another cpu. ******************************************************************************/ typedef struct psci_cpu_data { - uint32_t power_state; + uint32_t power_state; /* The power state from CPU_SUSPEND */ + unsigned char psci_state; /* The state of this CPU as seen by PSCI */ #if !USE_COHERENT_MEM - bakery_info_t pcpu_bakery_info[PSCI_NUM_PWR_DOMAINS]; + bakery_info_t pcpu_bakery_info[PSCI_NUM_NON_CPU_PWR_DOMAINS]; #endif } psci_cpu_data_t; @@ -230,7 +239,7 @@ void __dead2 psci_power_down_wfi(void); 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_mpidr(unsigned long); +int psci_get_suspend_stateid_by_idx(unsigned long); int psci_get_suspend_stateid(void); int psci_get_suspend_pwrlvl(void); diff --git a/include/plat/common/psci1.0/platform.h b/include/plat/common/psci1.0/platform.h index 059320f10a..acc5b95280 100644 --- a/include/plat/common/psci1.0/platform.h +++ b/include/plat/common/psci1.0/platform.h @@ -183,8 +183,7 @@ struct entry_point_info *bl31_plat_get_next_image_ep_info(uint32_t type); * Mandatory PSCI functions (BL3-1) ******************************************************************************/ int platform_setup_pm(const struct plat_pm_ops **); -unsigned int plat_get_pwr_domain_count(unsigned int, unsigned long); -unsigned int plat_get_pwr_domain_state(unsigned int, unsigned long); +const unsigned char *plat_get_power_domain_tree_desc(void); /******************************************************************************* * Optional BL3-1 functions (may be overridden) diff --git a/services/std_svc/psci1.0/psci_common.c b/services/std_svc/psci1.0/psci_common.c index d9e9ccee0f..4812bc460b 100644 --- a/services/std_svc/psci1.0/psci_common.c +++ b/services/std_svc/psci1.0/psci_common.c @@ -46,16 +46,20 @@ const spd_pm_ops_t *psci_spd_pm; /******************************************************************************* - * Grand array that holds the platform's topology information for state - * management of power domain instances. Each node (pwr_map_node) in the array - * corresponds to a power domain instance e.g. cluster, cpu within an mpidr + * Arrays that hold the platform's power domain tree information for state + * management of power domains. + * Each node in the array 'psci_non_cpu_pd_nodes' corresponds to a power domain + * which is an ancestor of a CPU power domain. + * Each node in the array 'psci_cpu_pd_nodes' corresponds to a cpu power domain ******************************************************************************/ -pwr_map_node_t psci_pwr_domain_map[PSCI_NUM_PWR_DOMAINS] +non_cpu_pd_node_t psci_non_cpu_pd_nodes[PSCI_NUM_NON_CPU_PWR_DOMAINS] #if USE_COHERENT_MEM __attribute__ ((section("tzfw_coherent_mem"))) #endif ; +cpu_pd_node_t psci_cpu_pd_nodes[PLATFORM_CORE_COUNT]; + /******************************************************************************* * Pointer to functions exported by the platform to complete power mgmt. ops ******************************************************************************/ @@ -64,29 +68,31 @@ const plat_pm_ops_t *psci_plat_pm_ops; /******************************************************************************* * Check that the maximum power level supported by the platform makes sense * ****************************************************************************/ -CASSERT(PLAT_MAX_PWR_LVL <= MPIDR_MAX_AFFLVL && \ - PLAT_MAX_PWR_LVL >= MPIDR_AFFLVL0, \ +CASSERT(PLAT_MAX_PWR_LVL <= PSCI_MAX_PWR_LVL && \ + PLAT_MAX_PWR_LVL >= PSCI_CPU_PWR_LVL, \ assert_platform_max_pwrlvl_check); /******************************************************************************* - * This function is passed an array of pointers to power domain nodes in the - * topology tree for an mpidr. It iterates through the nodes to find the - * highest power level where the power domain is marked as physically powered - * off. + * This function is passed a cpu_index and the highest level in the topology + * tree. It iterates through the nodes to find the highest power level at which + * a domain is physically powered off. ******************************************************************************/ -uint32_t psci_find_max_phys_off_pwrlvl(uint32_t start_pwrlvl, - uint32_t end_pwrlvl, - pwr_map_node_t *mpidr_nodes[]) +uint32_t psci_find_max_phys_off_pwrlvl(uint32_t end_pwrlvl, + unsigned int cpu_idx) { - uint32_t max_pwrlvl = PSCI_INVALID_DATA; + int max_pwrlvl, level; + unsigned int parent_idx = psci_cpu_pd_nodes[cpu_idx].parent_node; - for (; start_pwrlvl <= end_pwrlvl; start_pwrlvl++) { - if (mpidr_nodes[start_pwrlvl] == NULL) - continue; + if (psci_get_phys_state(cpu_idx, PSCI_CPU_PWR_LVL) != PSCI_STATE_OFF) + return PSCI_INVALID_DATA; - if (psci_get_phys_state(mpidr_nodes[start_pwrlvl]) == - PSCI_STATE_OFF) - max_pwrlvl = start_pwrlvl; + max_pwrlvl = PSCI_CPU_PWR_LVL; + + for (level = PSCI_CPU_PWR_LVL + 1; level <= end_pwrlvl; level++) { + if (psci_get_phys_state(parent_idx, level) == PSCI_STATE_OFF) + max_pwrlvl = level; + + parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node; } return max_pwrlvl; @@ -103,21 +109,14 @@ unsigned int psci_is_last_on_cpu(void) unsigned long mpidr = read_mpidr_el1() & MPIDR_AFFINITY_MASK; unsigned int i; - for (i = psci_pwr_lvl_limits[MPIDR_AFFLVL0].min; - i <= psci_pwr_lvl_limits[MPIDR_AFFLVL0].max; i++) { - - assert(psci_pwr_domain_map[i].level == MPIDR_AFFLVL0); - - if (!(psci_pwr_domain_map[i].state & PSCI_AFF_PRESENT)) - continue; - - if (psci_pwr_domain_map[i].mpidr == mpidr) { - assert(psci_get_state(&psci_pwr_domain_map[i]) + for (i = 0; i < PLATFORM_CORE_COUNT; i++) { + if (psci_cpu_pd_nodes[i].mpidr == mpidr) { + assert(psci_get_state(i, PSCI_CPU_PWR_LVL) == PSCI_STATE_ON); continue; } - if (psci_get_state(&psci_pwr_domain_map[i]) != PSCI_STATE_OFF) + if (psci_get_state(i, PSCI_CPU_PWR_LVL) != PSCI_STATE_OFF) return 0; } @@ -135,18 +134,12 @@ int get_power_on_target_pwrlvl(void) #if DEBUG unsigned int state; - pwr_map_node_t *node; - - /* Retrieve our node from the topology tree */ - node = psci_get_pwr_map_node(read_mpidr_el1() & MPIDR_AFFINITY_MASK, - MPIDR_AFFLVL0); - assert(node); /* * Sanity check the state of the cpu. It should be either suspend or "on * pending" */ - state = psci_get_state(node); + state = psci_get_state(plat_my_core_pos(), PSCI_CPU_PWR_LVL); assert(state == PSCI_STATE_SUSPEND || state == PSCI_STATE_ON_PENDING); #endif @@ -163,103 +156,74 @@ int get_power_on_target_pwrlvl(void) } /******************************************************************************* - * Simple routine to set the id of a power domain instance at a given level - * in the mpidr. The assumption is that the affinity level and the power - * level are the same. + * PSCI helper function to get the parent nodes corresponding to a cpu_index. ******************************************************************************/ -unsigned long mpidr_set_pwr_domain_inst(unsigned long mpidr, - unsigned char pwr_inst, - int pwr_lvl) +void psci_get_parent_pwr_domain_nodes(unsigned int cpu_idx, + int end_lvl, + unsigned int node_index[]) { - unsigned long aff_shift; + unsigned int parent_node = psci_cpu_pd_nodes[cpu_idx].parent_node; + int i; - assert(pwr_lvl <= MPIDR_AFFLVL3); - - /* - * Decide the number of bits to shift by depending upon - * the power level - */ - aff_shift = get_afflvl_shift(pwr_lvl); - - /* Clear the existing affinity instance & set the new one*/ - mpidr &= ~(((unsigned long)MPIDR_AFFLVL_MASK) << aff_shift); - mpidr |= ((unsigned long)pwr_inst) << aff_shift; - - return mpidr; -} - -/******************************************************************************* - * This function sanity checks a range of power levels. - ******************************************************************************/ -int psci_check_pwrlvl_range(int start_pwrlvl, int end_pwrlvl) -{ - /* Sanity check the parameters passed */ - if (end_pwrlvl > PLAT_MAX_PWR_LVL) - return PSCI_E_INVALID_PARAMS; - - if (start_pwrlvl < MPIDR_AFFLVL0) - return PSCI_E_INVALID_PARAMS; - - if (end_pwrlvl < start_pwrlvl) - return PSCI_E_INVALID_PARAMS; - - return PSCI_E_SUCCESS; -} - -/******************************************************************************* - * This function is passed an array of pointers to power domain nodes in the - * topology tree for an mpidr and the state which each node should transition - * to. It updates the state of each node between the specified power levels. - ******************************************************************************/ -void psci_do_state_coordination(uint32_t start_pwrlvl, - uint32_t end_pwrlvl, - pwr_map_node_t *mpidr_nodes[], - uint32_t state) -{ - uint32_t level; - - for (level = start_pwrlvl; level <= end_pwrlvl; level++) { - if (mpidr_nodes[level] == NULL) - continue; - psci_set_state(mpidr_nodes[level], state); + for (i = PSCI_CPU_PWR_LVL + 1; i <= end_lvl; i++) { + *node_index++ = parent_node; + parent_node = psci_non_cpu_pd_nodes[parent_node].parent_node; } } /******************************************************************************* - * This function is passed an array of pointers to power domain nodes in the - * topology tree for an mpidr. It picks up locks for each power level bottom - * up in the range specified. + * This function is passed a cpu_index and the highest level in the topology + * tree and the state which each node should transition to. It updates the + * state of each node between the specified power levels. ******************************************************************************/ -void psci_acquire_pwr_domain_locks(int start_pwrlvl, - int end_pwrlvl, - pwr_map_node_t *mpidr_nodes[]) +void psci_do_state_coordination(int end_pwrlvl, + unsigned int cpu_idx, + uint32_t state) { int level; + unsigned int parent_idx = psci_cpu_pd_nodes[cpu_idx].parent_node; + psci_set_state(cpu_idx, state, PSCI_CPU_PWR_LVL); - for (level = start_pwrlvl; level <= end_pwrlvl; level++) { - if (mpidr_nodes[level] == NULL) - continue; - - psci_lock_get(mpidr_nodes[level]); + for (level = PSCI_CPU_PWR_LVL + 1; level <= end_pwrlvl; level++) { + psci_set_state(parent_idx, state, level); + parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node; } } /******************************************************************************* - * This function is passed an array of pointers to power domain nodes in the - * topology tree for an mpidr. It releases the lock for each power level top - * down in the range specified. + * This function is passed a cpu_index and the highest level in the topology + * tree that the operation should be applied to. It picks up locks in order of + * increasing power domain level in the range specified. ******************************************************************************/ -void psci_release_pwr_domain_locks(int start_pwrlvl, - int end_pwrlvl, - pwr_map_node_t *mpidr_nodes[]) +void psci_acquire_pwr_domain_locks(int end_pwrlvl, unsigned int cpu_idx) { + unsigned int parent_idx = psci_cpu_pd_nodes[cpu_idx].parent_node; int level; - for (level = end_pwrlvl; level >= start_pwrlvl; level--) { - if (mpidr_nodes[level] == NULL) - continue; + /* No locking required for level 0. Hence start locking from level 1 */ + for (level = PSCI_CPU_PWR_LVL + 1; level <= end_pwrlvl; level++) { + psci_lock_get(&psci_non_cpu_pd_nodes[parent_idx]); + parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node; + } +} - psci_lock_release(mpidr_nodes[level]); +/******************************************************************************* + * This function is passed a cpu_index and the highest level in the topology + * tree that the operation should be applied to. It releases the locks in order + * of decreasing power domain level in the range specified. + ******************************************************************************/ +void psci_release_pwr_domain_locks(int end_pwrlvl, unsigned int cpu_idx) +{ + unsigned int parent_idx, parent_nodes[PLAT_MAX_PWR_LVL] = {0}; + int level; + + /* Get the parent nodes */ + psci_get_parent_pwr_domain_nodes(cpu_idx, end_pwrlvl, parent_nodes); + + /* Unlock top down. No unlocking required for level 0. */ + for (level = end_pwrlvl; level >= PSCI_CPU_PWR_LVL + 1; level--) { + parent_idx = parent_nodes[level - 1]; + psci_lock_release(&psci_non_cpu_pd_nodes[parent_idx]); } } @@ -332,21 +296,22 @@ int psci_get_ns_ep_info(entry_point_info_t *ep, } /******************************************************************************* - * This function takes a pointer to a power domain node in the topology tree - * and returns its state. State of a non-leaf node needs to be calculated. + * This function takes an index and level of a power domain node in the topology + * tree and returns its state. State of a non-leaf node needs to be calculated. ******************************************************************************/ -unsigned short psci_get_state(pwr_map_node_t *node) +unsigned short psci_get_state(unsigned int idx, + int level) { -#if !USE_COHERENT_MEM - flush_dcache_range((uint64_t) node, sizeof(*node)); -#endif - - assert(node->level >= MPIDR_AFFLVL0 && node->level <= MPIDR_MAX_AFFLVL); - /* A cpu node just contains the state which can be directly returned */ - if (node->level == MPIDR_AFFLVL0) - return (node->state >> PSCI_STATE_SHIFT) & PSCI_STATE_MASK; + if (level == PSCI_CPU_PWR_LVL) { + flush_cpu_data_by_index(idx, psci_svc_cpu_data.psci_state); + return get_cpu_data_by_index(idx, psci_svc_cpu_data.psci_state); + } +#if !USE_COHERENT_MEM + flush_dcache_range((uint64_t) &psci_non_cpu_pd_nodes[idx], + sizeof(psci_non_cpu_pd_nodes[idx])); +#endif /* * For a power level higher than a cpu, the state has to be * calculated. It depends upon the value of the reference count @@ -355,35 +320,35 @@ unsigned short psci_get_state(pwr_map_node_t *node) * count. If the reference count is 0 then the power level is * OFF else ON. */ - if (node->ref_count) + if (psci_non_cpu_pd_nodes[idx].ref_count) return PSCI_STATE_ON; else return PSCI_STATE_OFF; } /******************************************************************************* - * This function takes a pointer to a power domain node in the topology - * tree and a target state. State of a non-leaf node needs to be converted - * to a reference count. State of a leaf node can be set directly. + * This function takes an index and level of a power domain node in the topology + * tree and a target state. State of a non-leaf node needs to be converted to + * a reference count. State of a leaf node can be set directly. ******************************************************************************/ -void psci_set_state(pwr_map_node_t *node, unsigned short state) +void psci_set_state(unsigned int idx, + unsigned short state, + int level) { - assert(node->level >= MPIDR_AFFLVL0 && node->level <= MPIDR_MAX_AFFLVL); - /* * For a power level higher than a cpu, the state is used * to decide whether the reference count is incremented or * decremented. Entry into the ON_PENDING state does not have * effect. */ - if (node->level > MPIDR_AFFLVL0) { + if (level > PSCI_CPU_PWR_LVL) { switch (state) { case PSCI_STATE_ON: - node->ref_count++; + psci_non_cpu_pd_nodes[idx].ref_count++; break; case PSCI_STATE_OFF: case PSCI_STATE_SUSPEND: - node->ref_count--; + psci_non_cpu_pd_nodes[idx].ref_count--; break; case PSCI_STATE_ON_PENDING: /* @@ -393,15 +358,16 @@ void psci_set_state(pwr_map_node_t *node, unsigned short state) return; default: assert(0); - } - } else { - node->state &= ~(PSCI_STATE_MASK << PSCI_STATE_SHIFT); - node->state |= (state & PSCI_STATE_MASK) << PSCI_STATE_SHIFT; - } #if !USE_COHERENT_MEM - flush_dcache_range((uint64_t) node, sizeof(*node)); + flush_dcache_range((uint64_t) &psci_non_cpu_pd_nodes[idx], + sizeof(psci_non_cpu_pd_nodes[idx])); #endif + } + } else { + set_cpu_data_by_index(idx, psci_svc_cpu_data.psci_state, state); + flush_cpu_data_by_index(idx, psci_svc_cpu_data.psci_state); + } } /******************************************************************************* @@ -411,11 +377,12 @@ void psci_set_state(pwr_map_node_t *node, unsigned short state) * tell whether that's actually happened or not. So we err on the side of * caution & treat the power domain as being turned off. ******************************************************************************/ -unsigned short psci_get_phys_state(pwr_map_node_t *node) +unsigned short psci_get_phys_state(unsigned int idx, + int level) { unsigned int state; - state = psci_get_state(node); + state = psci_get_state(idx, level); return get_phys_state(state); } @@ -429,60 +396,41 @@ unsigned short psci_get_phys_state(pwr_map_node_t *node) * coherency at the interconnect level in addition to gic cpu interface. ******************************************************************************/ void psci_power_up_finish(int end_pwrlvl, - pwrlvl_power_on_finisher_t pon_handler) + pwrlvl_power_on_finisher_t pon_handler) { - mpidr_pwr_map_nodes_t mpidr_nodes; - int rc; + unsigned int cpu_idx = plat_my_core_pos(); unsigned int max_phys_off_pwrlvl; - - /* - * Collect the pointers to the nodes in the topology tree for - * each power domain instances in the mpidr. If this function does - * not return successfully then either the mpidr or the power - * levels are incorrect. Either case is an irrecoverable error. - */ - rc = psci_get_pwr_map_nodes(read_mpidr_el1() & MPIDR_AFFINITY_MASK, - MPIDR_AFFLVL0, - end_pwrlvl, - mpidr_nodes); - if (rc != PSCI_E_SUCCESS) - panic(); - /* * This function acquires the lock corresponding to each power * level so that by the time all locks are taken, the system topology * is snapshot and state management can be done safely. */ - psci_acquire_pwr_domain_locks(MPIDR_AFFLVL0, - end_pwrlvl, - mpidr_nodes); + psci_acquire_pwr_domain_locks(end_pwrlvl, + cpu_idx); - max_phys_off_pwrlvl = psci_find_max_phys_off_pwrlvl(MPIDR_AFFLVL0, - end_pwrlvl, - mpidr_nodes); + max_phys_off_pwrlvl = psci_find_max_phys_off_pwrlvl(end_pwrlvl, + cpu_idx); assert(max_phys_off_pwrlvl != PSCI_INVALID_DATA); /* Perform generic, architecture and platform specific handling */ - pon_handler(mpidr_nodes, max_phys_off_pwrlvl); + pon_handler(cpu_idx, max_phys_off_pwrlvl); /* * This function updates the state of each power instance - * corresponding to the mpidr in the range of power levels + * corresponding to the cpu index in the range of power levels * specified. */ - psci_do_state_coordination(MPIDR_AFFLVL0, - end_pwrlvl, - mpidr_nodes, - PSCI_STATE_ON); + psci_do_state_coordination(end_pwrlvl, + cpu_idx, + PSCI_STATE_ON); /* * This loop releases the lock corresponding to each power level * in the reverse order to which they were acquired. */ - psci_release_pwr_domain_locks(MPIDR_AFFLVL0, - end_pwrlvl, - mpidr_nodes); + psci_release_pwr_domain_locks(end_pwrlvl, + cpu_idx); } /******************************************************************************* @@ -533,8 +481,8 @@ int psci_spd_migrate_info(uint64_t *mpidr) void psci_print_power_domain_map(void) { #if LOG_LEVEL >= LOG_LEVEL_INFO - pwr_map_node_t *node; - unsigned int idx; + unsigned int idx, state; + /* This array maps to the PSCI_STATE_X definitions in psci.h */ static const char *psci_state_str[] = { "ON", @@ -544,14 +492,20 @@ void psci_print_power_domain_map(void) }; INFO("PSCI Power Domain Map:\n"); - for (idx = 0; idx < PSCI_NUM_PWR_DOMAINS; idx++) { - node = &psci_pwr_domain_map[idx]; - if (!(node->state & PSCI_PWR_DOMAIN_PRESENT)) { - continue; - } - INFO(" pwrInst: Level %u, MPID 0x%lx, State %s\n", - node->level, node->mpidr, - psci_state_str[psci_get_state(node)]); + for (idx = 0; idx < (PSCI_NUM_PWR_DOMAINS - PLATFORM_CORE_COUNT); idx++) { + state = psci_get_state(idx, psci_non_cpu_pd_nodes[idx].level); + INFO(" Domain Node : Level %u, parent_node %d, State %s\n", + psci_non_cpu_pd_nodes[idx].level, + psci_non_cpu_pd_nodes[idx].parent_node, + psci_state_str[state]); + } + + for (idx = 0; idx < PLATFORM_CORE_COUNT; idx++) { + state = psci_get_state(idx, PSCI_CPU_PWR_LVL); + INFO(" CPU Node : MPID 0x%lx, parent_node %d, State %s\n", + psci_cpu_pd_nodes[idx].mpidr, + psci_cpu_pd_nodes[idx].parent_node, + psci_state_str[state]); } #endif } diff --git a/services/std_svc/psci1.0/psci_helpers.S b/services/std_svc/psci1.0/psci_helpers.S index 7ef4cdd64e..bbfa5d5d7b 100644 --- a/services/std_svc/psci1.0/psci_helpers.S +++ b/services/std_svc/psci1.0/psci_helpers.S @@ -28,7 +28,6 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include #include #include #include @@ -67,7 +66,7 @@ func psci_do_pwrdown_cache_maintenance * platform. * --------------------------------------------- */ - cmp x0, #MPIDR_AFFLVL0 + cmp x0, #PSCI_CPU_PWR_LVL b.eq do_core_pwr_dwn bl prepare_cluster_pwr_dwn b do_stack_maintenance diff --git a/services/std_svc/psci1.0/psci_main.c b/services/std_svc/psci1.0/psci_main.c index f87fd52e7c..5732da203d 100644 --- a/services/std_svc/psci1.0/psci_main.c +++ b/services/std_svc/psci1.0/psci_main.c @@ -240,32 +240,26 @@ int psci_cpu_off(void) int psci_affinity_info(unsigned long target_affinity, unsigned int lowest_affinity_level) { - int rc = PSCI_E_INVALID_PARAMS; - unsigned int pwr_domain_state; - pwr_map_node_t *node; + unsigned int cpu_idx; + unsigned char cpu_pwr_domain_state; - if (lowest_affinity_level > PLAT_MAX_PWR_LVL) - return rc; + /* We dont support level higher than PSCI_CPU_PWR_LVL */ + if (lowest_affinity_level > PSCI_CPU_PWR_LVL) + return PSCI_E_INVALID_PARAMS; - node = psci_get_pwr_map_node(target_affinity, lowest_affinity_level); - if (node && (node->state & PSCI_PWR_DOMAIN_PRESENT)) { + /* Calculate the cpu index of the target */ + cpu_idx = plat_core_pos_by_mpidr(target_affinity); + if (cpu_idx == -1) + return PSCI_E_INVALID_PARAMS; - /* - * TODO: For power levels higher than 0 i.e. cpu, the - * state will always be either ON or OFF. Need to investigate - * how critical is it to support ON_PENDING here. - */ - pwr_domain_state = psci_get_state(node); + cpu_pwr_domain_state = psci_get_state(cpu_idx, PSCI_CPU_PWR_LVL); - /* A suspended cpu is available & on for the OS */ - if (pwr_domain_state == PSCI_STATE_SUSPEND) { - pwr_domain_state = PSCI_STATE_ON; - } - - rc = pwr_domain_state; + /* A suspended cpu is available & on for the OS */ + if (cpu_pwr_domain_state == PSCI_STATE_SUSPEND) { + cpu_pwr_domain_state = PSCI_STATE_ON; } - return rc; + return cpu_pwr_domain_state; } int psci_migrate(unsigned long target_cpu) diff --git a/services/std_svc/psci1.0/psci_off.c b/services/std_svc/psci1.0/psci_off.c index f4fb9809ee..78410f1d9d 100644 --- a/services/std_svc/psci1.0/psci_off.c +++ b/services/std_svc/psci1.0/psci_off.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include "psci_private.h" @@ -50,8 +51,7 @@ ******************************************************************************/ int psci_do_cpu_off(int end_pwrlvl) { - int rc; - mpidr_pwr_map_nodes_t mpidr_nodes; + int rc, idx = plat_my_core_pos(); unsigned int max_phys_off_pwrlvl; /* @@ -60,28 +60,13 @@ int psci_do_cpu_off(int end_pwrlvl) */ assert(psci_plat_pm_ops->pwr_domain_off); - /* - * Collect the pointers to the nodes in the topology tree for - * each power domain instance in the mpidr. If this function does - * not return successfully then either the mpidr or the power - * levels are incorrect. Either way, this an internal TF error - * therefore assert. - */ - rc = psci_get_pwr_map_nodes(read_mpidr_el1() & MPIDR_AFFINITY_MASK, - MPIDR_AFFLVL0, - end_pwrlvl, - mpidr_nodes); - assert(rc == PSCI_E_SUCCESS); - /* * This function acquires the lock corresponding to each power * level so that by the time all locks are taken, the system topology * is snapshot and state management can be done safely. */ - psci_acquire_pwr_domain_locks(MPIDR_AFFLVL0, - end_pwrlvl, - mpidr_nodes); - + psci_acquire_pwr_domain_locks(end_pwrlvl, + idx); /* * Call the cpu off handler registered by the Secure Payload Dispatcher @@ -96,17 +81,14 @@ int psci_do_cpu_off(int end_pwrlvl) /* * This function updates the state of each power domain instance - * corresponding to the mpidr in the range of power levels + * corresponding to the cpu index in the range of power levels * specified. */ - psci_do_state_coordination(MPIDR_AFFLVL0, - end_pwrlvl, - mpidr_nodes, - PSCI_STATE_OFF); + psci_do_state_coordination(end_pwrlvl, + idx, + PSCI_STATE_OFF); - max_phys_off_pwrlvl = psci_find_max_phys_off_pwrlvl(MPIDR_AFFLVL0, - end_pwrlvl, - mpidr_nodes); + max_phys_off_pwrlvl = psci_find_max_phys_off_pwrlvl(end_pwrlvl, idx); assert(max_phys_off_pwrlvl != PSCI_INVALID_DATA); /* @@ -126,9 +108,8 @@ exit: * Release the locks corresponding to each power level in the * reverse order to which they were acquired. */ - psci_release_pwr_domain_locks(MPIDR_AFFLVL0, - end_pwrlvl, - mpidr_nodes); + psci_release_pwr_domain_locks(end_pwrlvl, + idx); /* * Check if all actions needed to safely power down this cpu have diff --git a/services/std_svc/psci1.0/psci_on.c b/services/std_svc/psci1.0/psci_on.c index c78e119a6c..c85e349309 100644 --- a/services/std_svc/psci1.0/psci_on.c +++ b/services/std_svc/psci1.0/psci_on.c @@ -71,8 +71,8 @@ int psci_cpu_on_start(unsigned long target_cpu, int end_pwrlvl) { int rc; - mpidr_pwr_map_nodes_t target_cpu_nodes; unsigned long psci_entrypoint; + unsigned int target_idx = plat_core_pos_by_mpidr(target_cpu); /* * This function must only be called on platforms where the @@ -81,33 +81,14 @@ int psci_cpu_on_start(unsigned long target_cpu, assert(psci_plat_pm_ops->pwr_domain_on && psci_plat_pm_ops->pwr_domain_on_finish); - /* - * Collect the pointers to the nodes in the topology tree for - * each power domain instance in the mpidr. If this function does - * not return successfully then either the mpidr or the power - * levels are incorrect. - */ - rc = psci_get_pwr_map_nodes(target_cpu, - MPIDR_AFFLVL0, - end_pwrlvl, - target_cpu_nodes); - assert(rc == PSCI_E_SUCCESS); - - /* - * This function acquires the lock corresponding to each power - * level so that by the time all locks are taken, the system topology - * is snapshot and state management can be done safely. - */ - psci_acquire_pwr_domain_locks(MPIDR_AFFLVL0, - end_pwrlvl, - target_cpu_nodes); + /* Protect against multiple CPUs trying to turn ON the same target CPU */ + psci_spin_lock_cpu(target_idx); /* * Generic management: Ensure that the cpu is off to be * turned on. */ - rc = cpu_on_validate_state(psci_get_state( - target_cpu_nodes[MPIDR_AFFLVL0])); + rc = cpu_on_validate_state(psci_get_state(target_idx, PSCI_CPU_PWR_LVL)); if (rc != PSCI_E_SUCCESS) goto exit; @@ -121,13 +102,12 @@ int psci_cpu_on_start(unsigned long target_cpu, /* * This function updates the state of each affinity instance - * corresponding to the mpidr in the range of affinity levels + * corresponding to the mpidr in the range of power domain levels * specified. */ - psci_do_state_coordination(MPIDR_AFFLVL0, - end_pwrlvl, - target_cpu_nodes, - PSCI_STATE_ON_PENDING); + psci_do_state_coordination(end_pwrlvl, + target_idx, + PSCI_STATE_ON_PENDING); /* * Perform generic, architecture and platform specific handling. @@ -150,20 +130,12 @@ int psci_cpu_on_start(unsigned long target_cpu, cm_init_context_by_index(target_idx, ep); else /* Restore the state on error. */ - psci_do_state_coordination(MPIDR_AFFLVL0, - end_pwrlvl, - target_cpu_nodes, - PSCI_STATE_OFF); + psci_do_state_coordination(end_pwrlvl, + target_idx, + PSCI_STATE_OFF); exit: - /* - * This loop releases the lock corresponding to each power level - * in the reverse order to which they were acquired. - */ - psci_release_pwr_domain_locks(MPIDR_AFFLVL0, - end_pwrlvl, - target_cpu_nodes); - + psci_spin_unlock_cpu(target_idx); return rc; } @@ -171,12 +143,12 @@ exit: * The following function finish an earlier power on request. They * are called by the common finisher routine in psci_common.c. ******************************************************************************/ -void psci_cpu_on_finish(pwr_map_node_t *node[], int pwrlvl) +void psci_cpu_on_finish(unsigned int cpu_idx, + int max_off_pwrlvl) { - assert(node[pwrlvl]->level == pwrlvl); - /* Ensure we have been explicitly woken up by another cpu */ - assert(psci_get_state(node[MPIDR_AFFLVL0]) == PSCI_STATE_ON_PENDING); + assert(psci_get_state(cpu_idx, PSCI_CPU_PWR_LVL) + == PSCI_STATE_ON_PENDING); /* * Plat. management: Perform the platform specific actions @@ -184,7 +156,7 @@ void psci_cpu_on_finish(pwr_map_node_t *node[], int pwrlvl) * register. The actual state of this cpu has already been * changed. */ - psci_plat_pm_ops->pwr_domain_on_finish(pwrlvl); + psci_plat_pm_ops->pwr_domain_on_finish(max_off_pwrlvl); /* * Arch. management: Enable data cache and manage stack memory @@ -198,6 +170,15 @@ void psci_cpu_on_finish(pwr_map_node_t *node[], int pwrlvl) */ bl31_arch_setup(); + /* + * Lock the CPU spin lock to make sure that the context initialization + * is done. Since the lock is only used in this function to create + * a synchronization point with cpu_on_start(), it can be released + * immediately. + */ + psci_spin_lock_cpu(cpu_idx); + psci_spin_unlock_cpu(cpu_idx); + /* * Call the cpu on finish handler registered by the Secure Payload * Dispatcher to let it do any bookeeping. If the handler encounters an @@ -206,6 +187,10 @@ void psci_cpu_on_finish(pwr_map_node_t *node[], int pwrlvl) if (psci_spd_pm && psci_spd_pm->svc_on_finish) psci_spd_pm->svc_on_finish(0); + /* Populate the mpidr field within the cpu node array */ + /* This needs to be done only once */ + psci_cpu_pd_nodes[cpu_idx].mpidr = read_mpidr() & MPIDR_AFFINITY_MASK; + /* * Generic management: Now we just need to retrieve the * information that we had stashed away during the cpu_on @@ -216,4 +201,3 @@ void psci_cpu_on_finish(pwr_map_node_t *node[], int pwrlvl) /* Clean caches before re-entering normal world */ dcsw_op_louis(DCCSW); } - diff --git a/services/std_svc/psci1.0/psci_private.h b/services/std_svc/psci1.0/psci_private.h index 79909a8629..e7ad711fd9 100644 --- a/services/std_svc/psci1.0/psci_private.h +++ b/services/std_svc/psci1.0/psci_private.h @@ -34,25 +34,30 @@ #include #include #include +#include #include +#include /* * The following helper macros abstract the interface to the Bakery * Lock API. */ #if USE_COHERENT_MEM -#define psci_lock_init(pwr_map, idx) bakery_lock_init(&(pwr_map)[(idx)].lock) -#define psci_lock_get(node) bakery_lock_get(&((node)->lock)) -#define psci_lock_release(node) bakery_lock_release(&((node)->lock)) +#define psci_lock_init(non_cpu_pd_node, idx) \ + bakery_lock_init(&(non_cpu_pd_node)[(idx)].lock) +#define psci_lock_get(non_cpu_pd_node) \ + bakery_lock_get(&((non_cpu_pd_node)->lock)) +#define psci_lock_release(non_cpu_pd_node) \ + bakery_lock_release(&((non_cpu_pd_node)->lock)) #else -#define psci_lock_init(pwr_map, idx) \ - ((pwr_map)[(idx)].pwr_domain_index = (idx)) -#define psci_lock_get(node) \ - bakery_lock_get((node)->pwr_domain_index,\ - CPU_DATA_PSCI_LOCK_OFFSET) -#define psci_lock_release(node) \ - bakery_lock_release((node)->pwr_domain_index,\ - CPU_DATA_PSCI_LOCK_OFFSET) +#define psci_lock_init(non_cpu_pd_node, idx) \ + ((non_cpu_pd_node)[(idx)].lock_index = (idx)) +#define psci_lock_get(non_cpu_pd_node) \ + bakery_lock_get((non_cpu_pd_node)->lock_index, \ + CPU_DATA_PSCI_LOCK_OFFSET) +#define psci_lock_release(non_cpu_pd_node) \ + bakery_lock_release((non_cpu_pd_node)->lock_index, \ + CPU_DATA_PSCI_LOCK_OFFSET) #endif /* @@ -75,39 +80,76 @@ define_psci_cap(PSCI_MIG_INFO_UP_CPU_AARCH64) | \ define_psci_cap(PSCI_SYSTEM_SUSPEND_AARCH64)) +/* + * Helper macros for the CPU level spinlocks + */ +#define psci_spin_lock_cpu(idx) spin_lock(&psci_cpu_pd_nodes[idx].cpu_lock) +#define psci_spin_unlock_cpu(idx) spin_unlock(&psci_cpu_pd_nodes[idx].cpu_lock) /******************************************************************************* - * The following two data structures hold the topology tree which in turn tracks - * the state of the all the power domain instances supported by the platform. + * The following two data structures implement the power domain tree. The tree + * is used to track the state of all the nodes i.e. power domain instances + * described by the platform. The tree consists of nodes that describe CPU power + * domains i.e. leaf nodes and all other power domains which are parents of a + * CPU power domain i.e. non-leaf nodes. ******************************************************************************/ -typedef struct pwr_map_node { - unsigned long mpidr; +typedef struct non_cpu_pwr_domain_node { + /* + * Index of the first CPU power domain node level 0 which has this node + * as its parent. + */ + unsigned int cpu_start_idx; + + /* + * Number of CPU power domains which are siblings of the domain indexed + * by 'cpu_start_idx' i.e. all the domains in the range 'cpu_start_idx + * -> cpu_start_idx + ncpus' have this node as their parent. + */ + unsigned int ncpus; + + /* + * Index of the parent power domain node. + * TODO: Figure out whether to whether using pointer is more efficient. + */ + unsigned int parent_node; + unsigned char ref_count; - unsigned char state; unsigned char level; #if USE_COHERENT_MEM bakery_lock_t lock; #else /* For indexing the bakery_info array in per CPU data */ - unsigned char pwr_domain_index; + unsigned char lock_index; #endif -} pwr_map_node_t; +} non_cpu_pd_node_t; -typedef struct pwr_lvl_limits_node { - int min; - int max; -} pwr_lvl_limits_node_t; +typedef struct cpu_pwr_domain_node { + unsigned long mpidr; -typedef pwr_map_node_t (*mpidr_pwr_map_nodes_t[MPIDR_MAX_AFFLVL + 1]); -typedef void (*pwrlvl_power_on_finisher_t)(pwr_map_node_t *mpidr_nodes[], - int pwrlvl); + /* + * Index of the parent power domain node. + * TODO: Figure out whether to whether using pointer is more efficient. + */ + unsigned int parent_node; + + /* + * A CPU power domain does not require state coordination like its + * parent power domains. Hence this node does not include a bakery + * lock. A spinlock is required by the CPU_ON handler to prevent a race + * when multiple CPUs try to turn ON the same target CPU. + */ + spinlock_t cpu_lock; +} cpu_pd_node_t; + +typedef void (*pwrlvl_power_on_finisher_t)(unsigned int cpu_idx, + int max_off_pwrlvl); /******************************************************************************* * Data prototypes ******************************************************************************/ extern const plat_pm_ops_t *psci_plat_pm_ops; -extern pwr_map_node_t psci_pwr_domain_map[PSCI_NUM_PWR_DOMAINS]; -extern pwr_lvl_limits_node_t psci_pwr_domain_map[MPIDR_MAX_AFFLVL + 1]; +extern non_cpu_pd_node_t psci_non_cpu_pd_nodes[PSCI_NUM_NON_CPU_PWR_DOMAINS]; +extern cpu_pd_node_t psci_cpu_pd_nodes[PLATFORM_CORE_COUNT]; extern uint32_t psci_caps; /******************************************************************************* @@ -119,56 +161,47 @@ extern const spd_pm_ops_t *psci_spd_pm; * Function prototypes ******************************************************************************/ /* Private exported functions from psci_common.c */ -unsigned short psci_get_state(pwr_map_node_t *node); -unsigned short psci_get_phys_state(pwr_map_node_t *node); -void psci_set_state(pwr_map_node_t *node, unsigned short state); -unsigned long mpidr_set_pwr_domain_inst(unsigned long, unsigned char, int); +unsigned short psci_get_state(unsigned int idx, int level); +unsigned short psci_get_phys_state(unsigned int idx, int level); +void psci_set_state(unsigned int idx, unsigned short state, int level); int psci_validate_mpidr(unsigned long mpidr); int get_power_on_target_pwrlvl(void); void psci_power_up_finish(int end_pwrlvl, pwrlvl_power_on_finisher_t pon_handler); int psci_get_ns_ep_info(entry_point_info_t *ep, uint64_t entrypoint, uint64_t context_id); -int psci_check_pwrlvl_range(int start_pwrlvl, int end_pwrlvl); -void psci_do_state_coordination(uint32_t start_pwrlvl, - uint32_t end_pwrlvl, - pwr_map_node_t *mpidr_nodes[], - uint32_t state); -void psci_acquire_pwr_domain_locks(int start_pwrlvl, - int end_pwrlvl, - pwr_map_node_t *mpidr_nodes[]); -void psci_release_pwr_domain_locks(int start_pwrlvl, - int end_pwrlvl, - mpidr_pwr_map_nodes_t mpidr_nodes); +void psci_get_parent_pwr_domain_nodes(unsigned int cpu_idx, + int end_lvl, + unsigned int node_index[]); +void psci_do_state_coordination(int end_pwrlvl, + unsigned int cpu_idx, + uint32_t state); +void psci_acquire_pwr_domain_locks(int end_pwrlvl, + unsigned int cpu_idx); +void psci_release_pwr_domain_locks(int end_pwrlvl, + unsigned int cpu_idx); void psci_print_power_domain_map(void); -uint32_t psci_find_max_phys_off_pwrlvl(uint32_t start_pwrlvl, - uint32_t end_pwrlvl, - pwr_map_node_t *mpidr_nodes[]); +uint32_t psci_find_max_phys_off_pwrlvl(uint32_t end_pwrlvl, + unsigned int cpu_idx); unsigned int psci_is_last_on_cpu(void); int psci_spd_migrate_info(uint64_t *mpidr); -/* Private exported functions from psci_setup.c */ -int psci_get_pwr_map_nodes(unsigned long mpidr, - int start_pwrlvl, - int end_pwrlvl, - pwr_map_node_t *mpidr_nodes[]); -pwr_map_node_t *psci_get_pwr_map_node(unsigned long, int); - -/* Private exported functions from psci_cpu_on.c */ +/* Private exported functions from psci_on.c */ int psci_cpu_on_start(unsigned long target_cpu, - entry_point_info_t *ep, - int end_pwrlvl); + entry_point_info_t *ep, + int end_pwrlvl); -void psci_cpu_on_finish(pwr_map_node_t *node[], int pwrlvl); +void psci_cpu_on_finish(unsigned int cpu_idx, + int max_off_pwrlvl); /* Private exported functions from psci_cpu_off.c */ int psci_do_cpu_off(int end_pwrlvl); -/* Private exported functions from psci_cpu_suspend.c */ +/* Private exported functions from psci_suspend.c */ void psci_cpu_suspend_start(entry_point_info_t *ep, int end_pwrlvl); - -void psci_cpu_suspend_finish(pwr_map_node_t *node[], int pwrlvl); +void psci_cpu_suspend_finish(unsigned int cpu_idx, + int max_off_pwrlvl); void psci_set_suspend_power_state(unsigned int power_state); diff --git a/services/std_svc/psci1.0/psci_setup.c b/services/std_svc/psci1.0/psci_setup.c index b1eef69f75..5f7376be70 100644 --- a/services/std_svc/psci1.0/psci_setup.c +++ b/services/std_svc/psci1.0/psci_setup.c @@ -42,335 +42,203 @@ * Per cpu non-secure contexts used to program the architectural state prior * return to the normal world. * TODO: Use the memory allocator to set aside memory for the contexts instead - * of relying on platform defined constants. Using PSCI_NUM_PWR_DOMAINS will be - * an overkill. + * of relying on platform defined constants. ******************************************************************************/ static cpu_context_t psci_ns_context[PLATFORM_CORE_COUNT]; -/******************************************************************************* - * In a system, a certain number of power domain instances are present at a - * power level. The cumulative number of instances across all levels are - * stored in 'psci_pwr_domain_map'. The topology tree has been flattenned into - * this array. To retrieve nodes, information about the extents of each power - * level i.e. start index and end index needs to be present. - * 'psci_pwr_lvl_limits' stores this information. - ******************************************************************************/ -pwr_lvl_limits_node_t psci_pwr_lvl_limits[MPIDR_MAX_AFFLVL + 1]; - /****************************************************************************** * Define the psci capability variable. *****************************************************************************/ uint32_t psci_caps; - /******************************************************************************* - * Routines for retrieving the node corresponding to a power domain instance - * in the mpidr. The first one uses binary search to find the node corresponding - * to the mpidr (key) at a particular power level. The second routine decides - * extents of the binary search at each power level. + * Function which initializes the 'psci_non_cpu_pd_nodes' or the + * 'psci_cpu_pd_nodes' corresponding to the power level. ******************************************************************************/ -static int psci_pwr_domain_map_get_idx(unsigned long key, - int min_idx, - int max_idx) +static void psci_init_pwr_domain_node(int node_idx, int parent_idx, int level) { - int mid; - - /* - * Terminating condition: If the max and min indices have crossed paths - * during the binary search then the key has not been found. - */ - if (max_idx < min_idx) - return PSCI_E_INVALID_PARAMS; - - /* - * Make sure we are within array limits. - */ - assert(min_idx >= 0 && max_idx < PSCI_NUM_PWR_DOMAINS); - - /* - * Bisect the array around 'mid' and then recurse into the array chunk - * where the key is likely to be found. The mpidrs in each node in the - * 'psci_pwr_domain_map' for a given power level are stored in an - * ascending order which makes the binary search possible. - */ - mid = min_idx + ((max_idx - min_idx) >> 1); /* Divide by 2 */ - - if (psci_pwr_domain_map[mid].mpidr > key) - return psci_pwr_domain_map_get_idx(key, min_idx, mid - 1); - else if (psci_pwr_domain_map[mid].mpidr < key) - return psci_pwr_domain_map_get_idx(key, mid + 1, max_idx); - else - return mid; -} - -pwr_map_node_t *psci_get_pwr_map_node(unsigned long mpidr, int pwr_lvl) -{ - int rc; - - if (pwr_lvl > PLAT_MAX_PWR_LVL) - return NULL; - - /* Right shift the mpidr to the required power level */ - mpidr = mpidr_mask_lower_afflvls(mpidr, pwr_lvl); - - rc = psci_pwr_domain_map_get_idx(mpidr, - psci_pwr_lvl_limits[pwr_lvl].min, - psci_pwr_lvl_limits[pwr_lvl].max); - if (rc >= 0) - return &psci_pwr_domain_map[rc]; - else - return NULL; -} - -/******************************************************************************* - * This function populates an array with nodes corresponding to a given range of - * power levels in an mpidr. It returns successfully only when the power - * levels are correct, the mpidr is valid i.e. no power level is absent from - * the topology tree & the power domain instance at level 0 is not absent. - ******************************************************************************/ -int psci_get_pwr_map_nodes(unsigned long mpidr, - int start_pwrlvl, - int end_pwrlvl, - pwr_map_node_t *mpidr_nodes[]) -{ - int rc = PSCI_E_INVALID_PARAMS, level; - pwr_map_node_t *node; - - rc = psci_check_pwrlvl_range(start_pwrlvl, end_pwrlvl); - if (rc != PSCI_E_SUCCESS) - return rc; - - for (level = start_pwrlvl; level <= end_pwrlvl; level++) { - - /* - * Grab the node for each power level. No power level - * can be missing as that would mean that the topology tree - * is corrupted. - */ - node = psci_get_pwr_map_node(mpidr, level); - if (node == NULL) { - rc = PSCI_E_INVALID_PARAMS; - break; - } - - /* - * Skip absent power levels unless it's power level 0. - * An absent cpu means that the mpidr is invalid. Save the - * pointer to the node for the present power level - */ - if (!(node->state & PSCI_PWR_DOMAIN_PRESENT)) { - if (level == MPIDR_AFFLVL0) { - rc = PSCI_E_INVALID_PARAMS; - break; - } - - mpidr_nodes[level] = NULL; - } else - mpidr_nodes[level] = node; - } - - return rc; -} - -/******************************************************************************* - * Function which initializes the 'pwr_map_node' corresponding to a power - * domain instance. Each node has a unique mpidr, level and bakery lock. - ******************************************************************************/ -static void psci_init_pwr_map_node(unsigned long mpidr, - int level, - unsigned int idx) -{ - unsigned char state; - uint32_t linear_id; - psci_pwr_domain_map[idx].mpidr = mpidr; - psci_pwr_domain_map[idx].level = level; - psci_lock_init(psci_pwr_domain_map, idx); - - /* - * If an power domain instance is present then mark it as OFF - * to begin with. - */ - state = plat_get_pwr_domain_state(level, mpidr); - psci_pwr_domain_map[idx].state = state; - - /* - * Check if this is a CPU node and is present in which case certain - * other initialisations are required. - */ - if (level != MPIDR_AFFLVL0) - return; - - if (!(state & PSCI_PWR_DOMAIN_PRESENT)) - return; - - /* - * Mark the cpu as OFF. Higher power level reference counts - * have already been memset to 0 - */ - psci_set_state(&psci_pwr_domain_map[idx], PSCI_STATE_OFF); - - /* - * Associate a non-secure context with this power - * instance through the context management library. - */ - linear_id = plat_core_pos_by_mpidr(mpidr); - assert(linear_id < PLATFORM_CORE_COUNT); - - /* Invalidate the suspend context for the node */ - set_cpu_data_by_index(linear_id, - psci_svc_cpu_data.power_state, - PSCI_INVALID_DATA); - - flush_cpu_data_by_index(linear_id, psci_svc_cpu_data); - - cm_set_context_by_index(linear_id, - (void *) &psci_ns_context[linear_id], - NON_SECURE); -} - -/******************************************************************************* - * Core routine used by the Breadth-First-Search algorithm to populate the - * power domain tree. Each level in the tree corresponds to a power level. This - * routine's aim is to traverse to the target power level and populate nodes - * in the 'psci_pwr_domain_map' for all the siblings at that level. It uses the - * current power level to keep track of how many levels from the root of the - * tree have been traversed. If the current power level != target power level, - * then the platform is asked to return the number of children that each - * power domain instance has at the current power level. Traversal is then done - * for each child at the next lower level i.e. current power level - 1. - * - * CAUTION: This routine assumes that power domain instance ids are allocated - * in a monotonically increasing manner at each power 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 psci_init_pwr_map(unsigned long mpidr, - unsigned int pwrmap_idx, - int cur_pwrlvl, - int tgt_pwrlvl) -{ - unsigned int ctr, pwr_inst_count; - - assert(cur_pwrlvl >= tgt_pwrlvl); - - /* - * 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. - */ - pwr_inst_count = plat_get_pwr_domain_count(cur_pwrlvl, mpidr); - assert(pwr_inst_count); - - if (tgt_pwrlvl < cur_pwrlvl) { - for (ctr = 0; ctr < pwr_inst_count; ctr++) { - mpidr = mpidr_set_pwr_domain_inst(mpidr, ctr, - cur_pwrlvl); - pwrmap_idx = psci_init_pwr_map(mpidr, - pwrmap_idx, - cur_pwrlvl - 1, - tgt_pwrlvl); - } + if (level > PSCI_CPU_PWR_LVL) { + psci_non_cpu_pd_nodes[node_idx].level = level; + psci_lock_init(psci_non_cpu_pd_nodes, node_idx); + psci_non_cpu_pd_nodes[node_idx].parent_node = parent_idx; } else { - for (ctr = 0; ctr < pwr_inst_count; ctr++, pwrmap_idx++) { - mpidr = mpidr_set_pwr_domain_inst(mpidr, ctr, - cur_pwrlvl); - psci_init_pwr_map_node(mpidr, cur_pwrlvl, pwrmap_idx); - } - /* pwrmap_idx is 1 greater than the max index of cur_pwrlvl */ - psci_pwr_lvl_limits[cur_pwrlvl].max = pwrmap_idx - 1; + psci_cpu_pd_nodes[node_idx].parent_node = parent_idx; + + /* Initialize with an invalid mpidr */ + psci_cpu_pd_nodes[node_idx].mpidr = PSCI_INVALID_MPIDR; + + /* + * Mark the cpu as OFF. + */ + set_cpu_data_by_index(node_idx, + psci_svc_cpu_data.psci_state, + PSCI_STATE_OFF); + + /* Invalidate the suspend context for the node */ + set_cpu_data_by_index(node_idx, + psci_svc_cpu_data.power_state, + PSCI_INVALID_DATA); + + flush_cpu_data_by_index(node_idx, psci_svc_cpu_data); + + cm_set_context_by_index(node_idx, + (void *) &psci_ns_context[node_idx], + NON_SECURE); } - - return pwrmap_idx; } /******************************************************************************* - * This function initializes the topology tree by querying the platform. To do - * so, it's helper routines implement a Breadth-First-Search. At each power - * level the platform conveys the number of power domain instances that exist - * i.e. the power instance count. The algorithm populates the - * psci_pwr_domain_map* recursively using this information. On a platform that - * implements two clusters of 4 cpus each, the populated pwr_map_array would - * look like this: + * This functions updates cpu_start_idx and ncpus field for each of the node in + * psci_non_cpu_pd_nodes[]. It does so by comparing the parent nodes of each of + * the CPUs and check whether they match with the parent of the previous + * CPU. The basic assumption for this work is that children of the same parent + * are allocated adjacent indices. The platform should ensure this though proper + * mapping of the CPUs to indices via plat_core_pos_by_mpidr() and + * plat_my_core_pos() APIs. + *******************************************************************************/ +static void psci_update_pwrlvl_limits(void) +{ + int cpu_idx, j; + unsigned int nodes_idx[PLAT_MAX_PWR_LVL] = {0}; + unsigned int temp_index[PLAT_MAX_PWR_LVL]; + + for (cpu_idx = 0; cpu_idx < PLATFORM_CORE_COUNT; cpu_idx++) { + psci_get_parent_pwr_domain_nodes(cpu_idx, + PLAT_MAX_PWR_LVL, + temp_index); + for (j = PLAT_MAX_PWR_LVL - 1; j >= 0; j--) { + if (temp_index[j] != nodes_idx[j]) { + nodes_idx[j] = temp_index[j]; + psci_non_cpu_pd_nodes[nodes_idx[j]].cpu_start_idx + = cpu_idx; + } + psci_non_cpu_pd_nodes[nodes_idx[j]].ncpus++; + } + } +} + +/******************************************************************************* + * Core routine to populate the power domain tree. The tree descriptor passed by + * the platform is populated breadth-first and the first entry in the map + * informs the number of root power domains. The parent nodes of the root nodes + * will point to an invalid entry(-1). + ******************************************************************************/ +static void populate_power_domain_tree(const unsigned char *topology) +{ + unsigned int i, j = 0, num_nodes_at_lvl = 1, num_nodes_at_next_lvl; + unsigned int node_index = 0, parent_node_index = 0, num_children; + int level = PLAT_MAX_PWR_LVL; + + /* + * For each level the inputs are: + * - number of nodes at this level in plat_array i.e. num_nodes_at_level + * This is the sum of values of nodes at the parent level. + * - Index of first entry at this level in the plat_array i.e. + * parent_node_index. + * - Index of first free entry in psci_non_cpu_pd_nodes[] or + * psci_cpu_pd_nodes[] i.e. node_index depending upon the level. + */ + while (level >= PSCI_CPU_PWR_LVL) { + num_nodes_at_next_lvl = 0; + /* + * For each entry (parent node) at this level in the plat_array: + * - Find the number of children + * - Allocate a node in a power domain array for each child + * - Set the parent of the child to the parent_node_index - 1 + * - Increment parent_node_index to point to the next parent + * - Accumulate the number of children at next level. + */ + for (i = 0; i < num_nodes_at_lvl; i++) { + assert(parent_node_index <= + PSCI_NUM_NON_CPU_PWR_DOMAINS); + num_children = topology[parent_node_index]; + + for (j = node_index; + j < node_index + num_children; j++) + psci_init_pwr_domain_node(j, + parent_node_index - 1, + level); + + node_index = j; + num_nodes_at_next_lvl += num_children; + parent_node_index++; + } + + num_nodes_at_lvl = num_nodes_at_next_lvl; + level--; + + /* Reset the index for the cpu power domain array */ + if (level == PSCI_CPU_PWR_LVL) + node_index = 0; + } + + /* Validate the sanity of array exported by the platform */ + assert(j == PLATFORM_CORE_COUNT); + +#if !USE_COHERENT_MEM + /* Flush the non CPU power domain data to memory */ + flush_dcache_range((uint64_t) &psci_non_cpu_pd_nodes, + sizeof(psci_non_cpu_pd_nodes)); +#endif +} + +/******************************************************************************* + * This function initializes the power domain topology tree by querying the + * platform. The power domain nodes higher than the CPU are populated in the + * array psci_non_cpu_pd_nodes[] and the CPU power domains are populated in + * psci_cpu_pd_nodes[]. The platform exports its static topology map through the + * populate_power_domain_topology_tree() API. The algorithm populates the + * psci_non_cpu_pd_nodes and psci_cpu_pd_nodes iteratively by using this + * topology map. On a platform that implements two clusters of 2 cpus each, and + * supporting 3 domain levels, the populated psci_non_cpu_pd_nodes would look + * like this: * - * <- cpus cluster0 -><- cpus cluster1 -> * --------------------------------------------------- - * | 0 | 1 | 0 | 1 | 2 | 3 | 0 | 1 | 2 | 3 | + * | system node | cluster 0 node | cluster 1 node | * --------------------------------------------------- - * ^ ^ - * cluster __| cpu __| - * limit limit * - * The first 2 entries are of the cluster nodes. The next 4 entries are of cpus - * within cluster 0. The last 4 entries are of cpus within cluster 1. - * The 'psci_pwr_lvl_limits' array contains the max & min index of each power - * level within the 'psci_pwr_domain_map' array. This allows restricting search - * of a node at a power level between the indices in the limits array. + * And populated psci_cpu_pd_nodes would look like this : + * <- cpus cluster0 -><- cpus cluster1 -> + * ------------------------------------------------ + * | CPU 0 | CPU 1 | CPU 2 | CPU 3 | + * ------------------------------------------------ ******************************************************************************/ int32_t psci_setup(void) { - unsigned long mpidr = read_mpidr(); - int pwrlvl, pwrmap_idx, max_pwrlvl; - pwr_map_node_t *node; + const unsigned char *topology_tree; - psci_plat_pm_ops = NULL; + /* Query the topology map from the platform */ + topology_tree = plat_get_power_domain_tree_desc(); - /* Find out the maximum power level that the platform implements */ - max_pwrlvl = PLAT_MAX_PWR_LVL; - assert(max_pwrlvl <= MPIDR_MAX_AFFLVL); + /* Populate the power domain arrays using the platform topology map */ + populate_power_domain_tree(topology_tree); - /* - * This call traverses the topology tree with help from the platform and - * populates the power map using a breadth-first-search recursively. - * We assume that the platform allocates power domain instance ids from - * 0 onwards at each power level in the mpidr. FIRST_MPIDR = 0.0.0.0 - */ - pwrmap_idx = 0; - for (pwrlvl = max_pwrlvl; pwrlvl >= MPIDR_AFFLVL0; pwrlvl--) { - pwrmap_idx = psci_init_pwr_map(FIRST_MPIDR, - pwrmap_idx, - max_pwrlvl, - pwrlvl); - } + /* Update the CPU limits for each node in psci_non_cpu_pd_nodes */ + psci_update_pwrlvl_limits(); + + /* Populate the mpidr field of cpu node for this CPU */ + psci_cpu_pd_nodes[plat_my_core_pos()].mpidr = + read_mpidr() & MPIDR_AFFINITY_MASK; #if !USE_COHERENT_MEM /* - * The psci_pwr_domain_map only needs flushing when it's not allocated - * in coherent memory. + * The psci_non_cpu_pd_nodes only needs flushing when it's not allocated in + * coherent memory. */ - flush_dcache_range((uint64_t) &psci_pwr_domain_map, - sizeof(psci_pwr_domain_map)); + flush_dcache_range((uint64_t) &psci_non_cpu_pd_nodes, + sizeof(psci_non_cpu_pd_nodes)); #endif - /* - * Set the bounds for number of instances of each level in the map. Also - * flush out the entire array so that it's visible to subsequent power - * management operations. The 'psci_pwr_lvl_limits' array is allocated - * in normal memory. It will be accessed when the mmu is off e.g. after - * reset. Hence it needs to be flushed. - */ - for (pwrlvl = MPIDR_AFFLVL0; pwrlvl < max_pwrlvl; pwrlvl++) { - psci_pwr_lvl_limits[pwrlvl].min = - psci_pwr_lvl_limits[pwrlvl + 1].max + 1; - } - - flush_dcache_range((unsigned long) psci_pwr_lvl_limits, - sizeof(psci_pwr_lvl_limits)); + flush_dcache_range((uint64_t) &psci_cpu_pd_nodes, + sizeof(psci_cpu_pd_nodes)); /* - * Mark the power domain instances in our mpidr as ON. No need to lock - * as this is the primary cpu. + * Mark the current CPU and its parent power domains as ON. No need to lock + * as the system is UP on the primary at this stage of boot. */ - mpidr &= MPIDR_AFFINITY_MASK; - for (pwrlvl = MPIDR_AFFLVL0; pwrlvl <= max_pwrlvl; pwrlvl++) { - - node = psci_get_pwr_map_node(mpidr, pwrlvl); - assert(node); - - /* Mark each present node as ON. */ - if (node->state & PSCI_PWR_DOMAIN_PRESENT) - psci_set_state(node, PSCI_STATE_ON); - } + psci_do_state_coordination(PLAT_MAX_PWR_LVL, plat_my_core_pos(), + PSCI_STATE_ON); platform_setup_pm(&psci_plat_pm_ops); assert(psci_plat_pm_ops); diff --git a/services/std_svc/psci1.0/psci_suspend.c b/services/std_svc/psci1.0/psci_suspend.c index 3d1bf09e85..c402937ab3 100644 --- a/services/std_svc/psci1.0/psci_suspend.c +++ b/services/std_svc/psci1.0/psci_suspend.c @@ -82,15 +82,15 @@ int psci_get_suspend_stateid(void) } /******************************************************************************* - * This function gets the state id of the cpu specified by the 'mpidr' parameter + * This function gets the state id of the cpu specified by the cpu index * from the power state parameter saved in the per-cpu data array. Returns * PSCI_INVALID_DATA if the power state saved is invalid. ******************************************************************************/ -int psci_get_suspend_stateid_by_mpidr(unsigned long mpidr) +int psci_get_suspend_stateid_by_idx(unsigned long cpu_idx) { unsigned int power_state; - power_state = get_cpu_data_by_index(plat_core_pos_by_mpidr(mpidr), + power_state = get_cpu_data_by_index(cpu_idx, psci_svc_cpu_data.power_state); return ((power_state == PSCI_INVALID_DATA) ? @@ -114,12 +114,10 @@ int psci_get_suspend_stateid_by_mpidr(unsigned long mpidr) * the state transition has been done, no further error is expected and it is * not possible to undo any of the actions taken beyond that point. ******************************************************************************/ -void psci_cpu_suspend_start(entry_point_info_t *ep, - int end_pwrlvl) +void psci_cpu_suspend_start(entry_point_info_t *ep, int end_pwrlvl) { int skip_wfi = 0; - mpidr_pwr_map_nodes_t mpidr_nodes; - unsigned int max_phys_off_pwrlvl; + unsigned int max_phys_off_pwrlvl, idx = plat_my_core_pos(); unsigned long psci_entrypoint; /* @@ -129,25 +127,13 @@ void psci_cpu_suspend_start(entry_point_info_t *ep, assert(psci_plat_pm_ops->pwr_domain_suspend && psci_plat_pm_ops->pwr_domain_suspend_finish); - /* - * Collect the pointers to the nodes in the topology tree for - * each power domain instance in the mpidr. If this function does - * not return successfully then either the mpidr or the power - * levels are incorrect. Either way, this an internal TF error - * therefore assert. - */ - if (psci_get_pwr_map_nodes(read_mpidr_el1() & MPIDR_AFFINITY_MASK, - MPIDR_AFFLVL0, end_pwrlvl, mpidr_nodes) != PSCI_E_SUCCESS) - assert(0); - /* * This function acquires the lock corresponding to each power * level so that by the time all locks are taken, the system topology * is snapshot and state management can be done safely. */ - psci_acquire_pwr_domain_locks(MPIDR_AFFLVL0, - end_pwrlvl, - mpidr_nodes); + psci_acquire_pwr_domain_locks(end_pwrlvl, + idx); /* * We check if there are any pending interrupts after the delay @@ -169,17 +155,15 @@ void psci_cpu_suspend_start(entry_point_info_t *ep, /* * This function updates the state of each power domain instance - * corresponding to the mpidr in the range of power levels + * corresponding to the cpu index in the range of power levels * specified. */ - psci_do_state_coordination(MPIDR_AFFLVL0, - end_pwrlvl, - mpidr_nodes, - PSCI_STATE_SUSPEND); + psci_do_state_coordination(end_pwrlvl, + idx, + PSCI_STATE_SUSPEND); - max_phys_off_pwrlvl = psci_find_max_phys_off_pwrlvl(MPIDR_AFFLVL0, - end_pwrlvl, - mpidr_nodes); + max_phys_off_pwrlvl = psci_find_max_phys_off_pwrlvl(end_pwrlvl, + idx); assert(max_phys_off_pwrlvl != PSCI_INVALID_DATA); /* @@ -210,9 +194,8 @@ exit: * Release the locks corresponding to each power level in the * reverse order to which they were acquired. */ - psci_release_pwr_domain_locks(MPIDR_AFFLVL0, - end_pwrlvl, - mpidr_nodes); + psci_release_pwr_domain_locks(end_pwrlvl, + idx); if (!skip_wfi) psci_power_down_wfi(); } @@ -221,15 +204,14 @@ exit: * The following functions finish an earlier suspend request. They * are called by the common finisher routine in psci_common.c. ******************************************************************************/ -void psci_cpu_suspend_finish(pwr_map_node_t *node[], int pwrlvl) +void psci_cpu_suspend_finish(unsigned int cpu_idx, int max_off_pwrlvl) { int32_t suspend_level; uint64_t counter_freq; - assert(node[pwrlvl]->level == pwrlvl); - /* Ensure we have been woken up from a suspended state */ - assert(psci_get_state(node[MPIDR_AFFLVL0]) == PSCI_STATE_SUSPEND); + assert(psci_get_state(cpu_idx, PSCI_CPU_PWR_LVL) + == PSCI_STATE_SUSPEND); /* * Plat. management: Perform the platform specific actions @@ -238,7 +220,7 @@ void psci_cpu_suspend_finish(pwr_map_node_t *node[], int pwrlvl) * wrong then assert as there is no way to recover from this * situation. */ - psci_plat_pm_ops->pwr_domain_suspend_finish(pwrlvl); + psci_plat_pm_ops->pwr_domain_suspend_finish(max_off_pwrlvl); /* * Arch. management: Enable the data cache, manage stack memory and @@ -275,4 +257,3 @@ void psci_cpu_suspend_finish(pwr_map_node_t *node[], int pwrlvl) /* Clean caches before re-entering normal world */ dcsw_op_louis(DCCSW); } - From 8ee2498039f7409ab6aa3ed4ef7f0bce05b61fa3 Mon Sep 17 00:00:00 2001 From: Soby Mathew Date: Tue, 7 Apr 2015 12:16:56 +0100 Subject: [PATCH 06/20] 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 --- Makefile | 7 + docs/user-guide.md | 9 + include/bl31/cpu_data.h | 3 + include/bl31/services/psci1.0/psci.h | 151 ++++-- include/plat/common/psci1.0/platform.h | 12 +- plat/common/aarch64/plat_psci_common.c | 63 +++ services/std_svc/psci1.0/psci_common.c | 605 +++++++++++++++++------- services/std_svc/psci1.0/psci_main.c | 161 ++++--- services/std_svc/psci1.0/psci_off.c | 45 +- services/std_svc/psci1.0/psci_on.c | 63 ++- services/std_svc/psci1.0/psci_private.h | 63 ++- services/std_svc/psci1.0/psci_setup.c | 41 +- services/std_svc/psci1.0/psci_suspend.c | 201 ++++---- 13 files changed, 956 insertions(+), 468 deletions(-) create mode 100644 plat/common/aarch64/plat_psci_common.c diff --git a/Makefile b/Makefile index 6d3c5cba13..b1c42eb0fa 100644 --- a/Makefile +++ b/Makefile @@ -66,6 +66,9 @@ ARM_CCI_PRODUCT_ID := 400 ASM_ASSERTION := ${DEBUG} # Build option to choose whether Trusted firmware uses Coherent memory or not. USE_COHERENT_MEM := 1 +# Flag used to choose the power state format viz Extended State-ID or the Original +# format. +PSCI_EXTENDED_STATE_ID := 0 # Default FIP file name FIP_NAME := fip.bin # By default, use the -pedantic option in the gcc command line @@ -268,6 +271,10 @@ $(eval $(call add_define,LOG_LEVEL)) $(eval $(call assert_boolean,USE_COHERENT_MEM)) $(eval $(call add_define,USE_COHERENT_MEM)) +# Process PSCI_EXTENDED_STATE_ID flag +$(eval $(call assert_boolean,PSCI_EXTENDED_STATE_ID)) +$(eval $(call add_define,PSCI_EXTENDED_STATE_ID)) + # Process Generate CoT flags $(eval $(call assert_boolean,GENERATE_COT)) $(eval $(call assert_boolean,CREATE_KEYS)) diff --git a/docs/user-guide.md b/docs/user-guide.md index 006340b6c8..ad8d1c748f 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -353,6 +353,15 @@ performed. vector address can be programmed or is fixed on the platform. It can take either 0 (fixed) or 1 (programmable). Default is 0. +* `PSCI_EXTENDED_STATE_ID`: As per PSCI1.0 Specification, there are 2 formats + possible for the PSCI power-state parameter viz original and extended + State-ID formats. This flag if set to 1, configures the generic PSCI layer + to use the extended format. The default value of this flag is 0, which + means by default the original power-state format is used by the PSCI + implementation. This flag should be specified by the platform makefile + and it governs the return value of PSCI_FEATURES API for CPU_SUSPEND + smc function id. + #### ARM development platform specific build options * `ARM_TSP_RAM_LOCATION`: location of the TSP binary. Options: diff --git a/include/bl31/cpu_data.h b/include/bl31/cpu_data.h index 50f509bbda..db702ba352 100644 --- a/include/bl31/cpu_data.h +++ b/include/bl31/cpu_data.h @@ -129,6 +129,9 @@ void init_cpu_ops(void); #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 c31562c0ef..d79ab74db0 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 PSTATE_TYPE_STANDBY 0x0 -#define PSTATE_TYPE_POWERDOWN 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) & \ +#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_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 - -#define PSCI_INVALID_DATA -1 - -#define get_phys_state(x) (x != PSCI_STATE_ON ? \ - PSCI_STATE_OFF : PSCI_STATE_ON) - -#define psci_validate_power_state(pstate) (pstate & PSTATE_VALID_MASK) - - #ifndef __ASSEMBLY__ #include +#include + +/* + * 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 + +/* + * Type for representing the local power state at a particular level. + */ +typedef uint8_t plat_local_state_t; + +/* 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) + +/* + * 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)) + +/* + * 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 @@ typedef struct psci_cpu_data { * 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 __dead2 psci_power_down_wfi(void); 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 acc5b95280..a961863ac4 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 - +#include /******************************************************************************* * Forward declarations ******************************************************************************/ -struct plat_pm_ops; struct meminfo; struct image_info; struct entry_point_info; @@ -182,9 +181,16 @@ struct entry_point_info *bl31_plat_get_next_image_ep_info(uint32_t type); /******************************************************************************* * 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) ******************************************************************************/ diff --git a/plat/common/aarch64/plat_psci_common.c b/plat/common/aarch64/plat_psci_common.c new file mode 100644 index 0000000000..0748ef4deb --- /dev/null +++ b/plat/common/aarch64/plat_psci_common.c @@ -0,0 +1,63 @@ +/* + * 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 + +/* + * The PSCI generic code uses this API to let the platform participate in state + * coordination during a power management operation. It compares the platform + * specific local power states requested by each cpu for a given power domain + * and returns the coordinated target power state that the domain should + * enter. A platform assigns a number to a local power state. This default + * implementation assumes that the platform assigns these numbers in order of + * increasing depth of the power state i.e. for two power states X & Y, if X < Y + * then X represents a shallower power state than Y. As a result, the + * coordinated target local power state for a power domain will be the minimum + * of the requested local power states. + */ +plat_local_state_t plat_get_target_pwr_state(unsigned int lvl, + const plat_local_state_t *states, + unsigned int ncpu) +{ + plat_local_state_t target = PLAT_MAX_OFF_STATE, temp; + + assert(ncpu); + + do { + temp = *states++; + if (temp < target) + target = temp; + } while (--ncpu); + + return target; +} diff --git a/services/std_svc/psci1.0/psci_common.c b/services/std_svc/psci1.0/psci_common.c index 4812bc460b..70cc98d126 100644 --- a/services/std_svc/psci1.0/psci_common.c +++ b/services/std_svc/psci1.0/psci_common.c @@ -45,6 +45,26 @@ */ const spd_pm_ops_t *psci_spd_pm; +/* + * PSCI requested local power state map. This array is used to store the local + * power states requested by a CPU for power levels from level 1 to + * PLAT_MAX_PWR_LVL. It does not store the requested local power state for power + * level 0 (PSCI_CPU_PWR_LVL) as the requested and the target power state for a + * CPU are the same. + * + * During state coordination, the platform is passed an array containing the + * local states requested for a particular non cpu power domain by each cpu + * within the domain. + * + * TODO: Dense packing of the requested states will cause cache thrashing + * when multiple power domains write to it. If we allocate the requested + * states at each power level in a cache-line aligned per-domain memory, + * the cache thrashing can be avoided. + */ +static plat_local_state_t + psci_req_local_pwr_states[PLAT_MAX_PWR_LVL][PLATFORM_CORE_COUNT]; + + /******************************************************************************* * Arrays that hold the platform's power domain tree information for state * management of power domains. @@ -63,39 +83,82 @@ cpu_pd_node_t psci_cpu_pd_nodes[PLATFORM_CORE_COUNT]; /******************************************************************************* * Pointer to functions exported by the platform to complete power mgmt. ops ******************************************************************************/ -const plat_pm_ops_t *psci_plat_pm_ops; +const plat_psci_ops_t *psci_plat_pm_ops; -/******************************************************************************* +/****************************************************************************** * Check that the maximum power level supported by the platform makes sense - * ****************************************************************************/ + *****************************************************************************/ CASSERT(PLAT_MAX_PWR_LVL <= PSCI_MAX_PWR_LVL && \ PLAT_MAX_PWR_LVL >= PSCI_CPU_PWR_LVL, \ assert_platform_max_pwrlvl_check); -/******************************************************************************* - * This function is passed a cpu_index and the highest level in the topology - * tree. It iterates through the nodes to find the highest power level at which - * a domain is physically powered off. - ******************************************************************************/ -uint32_t psci_find_max_phys_off_pwrlvl(uint32_t end_pwrlvl, - unsigned int cpu_idx) +/* + * The plat_local_state used by the platform is one of these types: RUN, + * RETENTION and OFF. The platform can define further sub-states for each type + * apart from RUN. This categorization is done to verify the sanity of the + * psci_power_state passed by the platform and to print debug information. The + * categorization is done on the basis of the following conditions: + * + * 1. If (plat_local_state == 0) then the category is STATE_TYPE_RUN. + * + * 2. If (0 < plat_local_state <= PLAT_MAX_RET_STATE), then the category is + * STATE_TYPE_RETN. + * + * 3. If (plat_local_state > PLAT_MAX_RET_STATE), then the category is + * STATE_TYPE_OFF. + */ +typedef enum plat_local_state_type { + STATE_TYPE_RUN = 0, + STATE_TYPE_RETN, + STATE_TYPE_OFF +} plat_local_state_type_t; + +/* The macro used to categorize plat_local_state. */ +#define find_local_state_type(plat_local_state) \ + ((plat_local_state) ? ((plat_local_state > PLAT_MAX_RET_STATE) \ + ? STATE_TYPE_OFF : STATE_TYPE_RETN) \ + : STATE_TYPE_RUN) + +/****************************************************************************** + * Check that the maximum retention level supported by the platform is less + * than the maximum off level. + *****************************************************************************/ +CASSERT(PLAT_MAX_RET_STATE < PLAT_MAX_OFF_STATE, \ + assert_platform_max_off_and_retn_state_check); + +/****************************************************************************** + * This function ensures that the power state parameter in a CPU_SUSPEND request + * is valid. If so, it returns the requested states for each power level. + *****************************************************************************/ +int psci_validate_power_state(unsigned int power_state, + psci_power_state_t *state_info) { - int max_pwrlvl, level; - unsigned int parent_idx = psci_cpu_pd_nodes[cpu_idx].parent_node; + /* Check SBZ bits in power state are zero */ + if (psci_check_power_state(power_state)) + return PSCI_E_INVALID_PARAMS; - if (psci_get_phys_state(cpu_idx, PSCI_CPU_PWR_LVL) != PSCI_STATE_OFF) - return PSCI_INVALID_DATA; + assert(psci_plat_pm_ops->validate_power_state); - max_pwrlvl = PSCI_CPU_PWR_LVL; + /* Validate the power_state using platform pm_ops */ + return psci_plat_pm_ops->validate_power_state(power_state, state_info); +} - for (level = PSCI_CPU_PWR_LVL + 1; level <= end_pwrlvl; level++) { - if (psci_get_phys_state(parent_idx, level) == PSCI_STATE_OFF) - max_pwrlvl = level; +/****************************************************************************** + * This function retrieves the `psci_power_state_t` for system suspend from + * the platform. + *****************************************************************************/ +void psci_query_sys_suspend_pwrstate(psci_power_state_t *state_info) +{ + /* + * Assert that the required pm_ops hook is implemented to ensure that + * the capability detected during psci_setup() is valid. + */ + assert(psci_plat_pm_ops->get_sys_suspend_power_state); - parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node; - } - - return max_pwrlvl; + /* + * Query the platform for the power_state required for system suspend + */ + psci_plat_pm_ops->get_sys_suspend_power_state(state_info); } /******************************************************************************* @@ -106,17 +169,15 @@ uint32_t psci_find_max_phys_off_pwrlvl(uint32_t end_pwrlvl, ******************************************************************************/ unsigned int psci_is_last_on_cpu(void) { - unsigned long mpidr = read_mpidr_el1() & MPIDR_AFFINITY_MASK; - unsigned int i; + unsigned int cpu_idx, my_idx = plat_my_core_pos(); - for (i = 0; i < PLATFORM_CORE_COUNT; i++) { - if (psci_cpu_pd_nodes[i].mpidr == mpidr) { - assert(psci_get_state(i, PSCI_CPU_PWR_LVL) - == PSCI_STATE_ON); + for (cpu_idx = 0; cpu_idx < PLATFORM_CORE_COUNT; cpu_idx++) { + if (cpu_idx == my_idx) { + assert(psci_get_aff_info_state() == AFF_STATE_ON); continue; } - if (psci_get_state(i, PSCI_CPU_PWR_LVL) != PSCI_STATE_OFF) + if (psci_get_aff_info_state_by_idx(cpu_idx) != AFF_STATE_OFF) return 0; } @@ -132,17 +193,6 @@ int get_power_on_target_pwrlvl(void) { int pwrlvl; -#if DEBUG - unsigned int state; - - /* - * Sanity check the state of the cpu. It should be either suspend or "on - * pending" - */ - state = psci_get_state(plat_my_core_pos(), PSCI_CPU_PWR_LVL); - assert(state == PSCI_STATE_SUSPEND || state == PSCI_STATE_ON_PENDING); -#endif - /* * Assume that this cpu was suspended and retrieve its target power * level. If it is invalid then it could only have been turned off @@ -155,6 +205,119 @@ int get_power_on_target_pwrlvl(void) return pwrlvl; } +/****************************************************************************** + * Helper function to update the requested local power state array. This array + * does not store the requested state for the CPU power level. Hence an + * assertion is added to prevent us from accessing the wrong index. + *****************************************************************************/ +static void psci_set_req_local_pwr_state(unsigned int pwrlvl, + unsigned int cpu_idx, + plat_local_state_t req_pwr_state) +{ + assert(pwrlvl > PSCI_CPU_PWR_LVL); + psci_req_local_pwr_states[pwrlvl - 1][cpu_idx] = req_pwr_state; +} + +/****************************************************************************** + * This function initializes the psci_req_local_pwr_states. + *****************************************************************************/ +void psci_init_req_local_pwr_states(void) +{ + /* Initialize the requested state of all non CPU power domains as OFF */ + memset(&psci_req_local_pwr_states, PLAT_MAX_OFF_STATE, + sizeof(psci_req_local_pwr_states)); +} + +/****************************************************************************** + * Helper function to return a reference to an array containing the local power + * states requested by each cpu for a power domain at 'pwrlvl'. The size of the + * array will be the number of cpu power domains of which this power domain is + * an ancestor. These requested states will be used to determine a suitable + * target state for this power domain during psci state coordination. An + * assertion is added to prevent us from accessing the CPU power level. + *****************************************************************************/ +static plat_local_state_t *psci_get_req_local_pwr_states(int pwrlvl, + int cpu_idx) +{ + assert(pwrlvl > PSCI_CPU_PWR_LVL); + + return &psci_req_local_pwr_states[pwrlvl - 1][cpu_idx]; +} + +/****************************************************************************** + * Helper function to return the current local power state of each power domain + * from the current cpu power domain to its ancestor at the 'end_pwrlvl'. This + * function will be called after a cpu is powered on to find the local state + * each power domain has emerged from. + *****************************************************************************/ +static void psci_get_target_local_pwr_states(uint32_t end_pwrlvl, + psci_power_state_t *target_state) +{ + int lvl; + unsigned int parent_idx; + plat_local_state_t *pd_state = target_state->pwr_domain_state; + + pd_state[PSCI_CPU_PWR_LVL] = psci_get_cpu_local_state(); + parent_idx = psci_cpu_pd_nodes[plat_my_core_pos()].parent_node; + + /* Copy the local power state from node to state_info */ + for (lvl = PSCI_CPU_PWR_LVL + 1; lvl <= end_pwrlvl; lvl++) { +#if !USE_COHERENT_MEM + /* + * If using normal memory for psci_non_cpu_pd_nodes, we need + * to flush before reading the local power state as another + * cpu in the same power domain could have updated it and this + * code runs before caches are enabled. + */ + flush_dcache_range( + (uint64_t)&psci_non_cpu_pd_nodes[parent_idx], + sizeof(psci_non_cpu_pd_nodes[parent_idx])); +#endif + pd_state[lvl] = psci_non_cpu_pd_nodes[parent_idx].local_state; + parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node; + } + + /* Set the the higher levels to RUN */ + for (; lvl <= PLAT_MAX_PWR_LVL; lvl++) + target_state->pwr_domain_state[lvl] = PSCI_LOCAL_STATE_RUN; +} + +/****************************************************************************** + * Helper function to set the target local power state that each power domain + * from the current cpu power domain to its ancestor at the 'end_pwrlvl' will + * enter. This function will be called after coordination of requested power + * states has been done for each power level. + *****************************************************************************/ +static void psci_set_target_local_pwr_states(uint32_t end_pwrlvl, + const psci_power_state_t *target_state) +{ + int lvl; + unsigned int parent_idx; + const plat_local_state_t *pd_state = target_state->pwr_domain_state; + + psci_set_cpu_local_state(pd_state[PSCI_CPU_PWR_LVL]); + + /* + * Need to flush as local_state will be accessed with Data Cache + * disabled during power on + */ + flush_cpu_data(psci_svc_cpu_data.local_state); + + parent_idx = psci_cpu_pd_nodes[plat_my_core_pos()].parent_node; + + /* Copy the local_state from state_info */ + for (lvl = 1; lvl <= end_pwrlvl; lvl++) { + psci_non_cpu_pd_nodes[parent_idx].local_state = pd_state[lvl]; +#if !USE_COHERENT_MEM + flush_dcache_range( + (uint64_t)&psci_non_cpu_pd_nodes[parent_idx], + sizeof(psci_non_cpu_pd_nodes[parent_idx])); +#endif + parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node; + } +} + + /******************************************************************************* * PSCI helper function to get the parent nodes corresponding to a cpu_index. ******************************************************************************/ @@ -171,23 +334,207 @@ void psci_get_parent_pwr_domain_nodes(unsigned int cpu_idx, } } -/******************************************************************************* - * This function is passed a cpu_index and the highest level in the topology - * tree and the state which each node should transition to. It updates the - * state of each node between the specified power levels. - ******************************************************************************/ -void psci_do_state_coordination(int end_pwrlvl, - unsigned int cpu_idx, - uint32_t state) +/****************************************************************************** + * This function is invoked post CPU power up and initialization. It sets the + * affinity info state, target power state and requested power state for the + * current CPU and all its ancestor power domains to RUN. + *****************************************************************************/ +void psci_set_pwr_domains_to_run(uint32_t end_pwrlvl) { - int level; - unsigned int parent_idx = psci_cpu_pd_nodes[cpu_idx].parent_node; - psci_set_state(cpu_idx, state, PSCI_CPU_PWR_LVL); + int lvl; + unsigned int parent_idx, cpu_idx = plat_my_core_pos(); + parent_idx = psci_cpu_pd_nodes[cpu_idx].parent_node; - for (level = PSCI_CPU_PWR_LVL + 1; level <= end_pwrlvl; level++) { - psci_set_state(parent_idx, state, level); + /* Reset the local_state to RUN for the non cpu power domains. */ + for (lvl = PSCI_CPU_PWR_LVL + 1; lvl <= end_pwrlvl; lvl++) { + psci_non_cpu_pd_nodes[parent_idx].local_state = + PSCI_LOCAL_STATE_RUN; +#if !USE_COHERENT_MEM + flush_dcache_range( + (uint64_t)&psci_non_cpu_pd_nodes[parent_idx], + sizeof(psci_non_cpu_pd_nodes[parent_idx])); +#endif + psci_set_req_local_pwr_state(lvl, + cpu_idx, + PSCI_LOCAL_STATE_RUN); parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node; } + + /* Set the affinity info state to ON */ + psci_set_aff_info_state(AFF_STATE_ON); + + psci_set_cpu_local_state(PSCI_LOCAL_STATE_RUN); + flush_cpu_data(psci_svc_cpu_data); +} + +/****************************************************************************** + * This function is passed the local power states requested for each power + * domain (state_info) between the current CPU domain and its ancestors until + * the target power level (end_pwrlvl). It updates the array of requested power + * states with this information. + * + * Then, for each level (apart from the CPU level) until the 'end_pwrlvl', it + * retrieves the states requested by all the cpus of which the power domain at + * that level is an ancestor. It passes this information to the platform to + * coordinate and return the target power state. If the target state for a level + * is RUN then subsequent levels are not considered. At the CPU level, state + * coordination is not required. Hence, the requested and the target states are + * the same. + * + * The 'state_info' is updated with the target state for each level between the + * CPU and the 'end_pwrlvl' and returned to the caller. + * + * This function will only be invoked with data cache enabled and while + * powering down a core. + *****************************************************************************/ +void psci_do_state_coordination(int end_pwrlvl, psci_power_state_t *state_info) +{ + unsigned int lvl, parent_idx, cpu_idx = plat_my_core_pos(); + unsigned int start_idx, ncpus; + plat_local_state_t target_state, *req_states; + + parent_idx = psci_cpu_pd_nodes[cpu_idx].parent_node; + + /* For level 0, the requested state will be equivalent + to target state */ + for (lvl = PSCI_CPU_PWR_LVL + 1; lvl <= end_pwrlvl; lvl++) { + + /* First update the requested power state */ + psci_set_req_local_pwr_state(lvl, cpu_idx, + state_info->pwr_domain_state[lvl]); + + /* Get the requested power states for this power level */ + start_idx = psci_non_cpu_pd_nodes[parent_idx].cpu_start_idx; + req_states = psci_get_req_local_pwr_states(lvl, start_idx); + + /* + * Let the platform coordinate amongst the requested states at + * this power level and return the target local power state. + */ + ncpus = psci_non_cpu_pd_nodes[parent_idx].ncpus; + target_state = plat_get_target_pwr_state(lvl, + req_states, + ncpus); + + state_info->pwr_domain_state[lvl] = target_state; + + /* Break early if the negotiated target power state is RUN */ + if (is_local_state_run(state_info->pwr_domain_state[lvl])) + break; + + parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node; + } + + /* + * This is for cases when we break out of the above loop early because + * the target power state is RUN at a power level < end_pwlvl. + * We update the requested power state from state_info and then + * set the target state as RUN. + */ + for (lvl = lvl + 1; lvl <= end_pwrlvl; lvl++) { + psci_set_req_local_pwr_state(lvl, cpu_idx, + state_info->pwr_domain_state[lvl]); + state_info->pwr_domain_state[lvl] = PSCI_LOCAL_STATE_RUN; + + } + + /* Update the target state in the power domain nodes */ + psci_set_target_local_pwr_states(end_pwrlvl, state_info); +} + +/****************************************************************************** + * This function validates a suspend request by making sure that if a standby + * state is requested then no power level is turned off and the highest power + * level is placed in a standby/retention state. + * + * It also ensures that the state level X will enter is not shallower than the + * state level X + 1 will enter. + * + * This validation will be enabled only for DEBUG builds as the platform is + * expected to perform these validations as well. + *****************************************************************************/ +int psci_validate_suspend_req(const psci_power_state_t *state_info, + unsigned int is_power_down_state) +{ + unsigned int max_off_lvl, target_lvl, max_retn_lvl; + plat_local_state_t state; + plat_local_state_type_t req_state_type, deepest_state_type; + int i; + + /* Find the target suspend power level */ + target_lvl = psci_find_target_suspend_lvl(state_info); + if (target_lvl == PSCI_INVALID_DATA) + return PSCI_E_INVALID_PARAMS; + + /* All power domain levels are in a RUN state to begin with */ + deepest_state_type = STATE_TYPE_RUN; + + for (i = target_lvl; i >= PSCI_CPU_PWR_LVL; i--) { + state = state_info->pwr_domain_state[i]; + req_state_type = find_local_state_type(state); + + /* + * While traversing from the highest power level to the lowest, + * the state requested for lower levels has to be the same or + * deeper i.e. equal to or greater than the state at the higher + * levels. If this condition is true, then the requested state + * becomes the deepest state encountered so far. + */ + if (req_state_type < deepest_state_type) + return PSCI_E_INVALID_PARAMS; + deepest_state_type = req_state_type; + } + + /* Find the highest off power level */ + max_off_lvl = psci_find_max_off_lvl(state_info); + + /* The target_lvl is either equal to the max_off_lvl or max_retn_lvl */ + max_retn_lvl = PSCI_INVALID_DATA; + if (target_lvl != max_off_lvl) + max_retn_lvl = target_lvl; + + /* + * If this is not a request for a power down state then max off level + * has to be invalid and max retention level has to be a valid power + * level. + */ + if (!is_power_down_state && (max_off_lvl != PSCI_INVALID_DATA || + max_retn_lvl == PSCI_INVALID_DATA)) + return PSCI_E_INVALID_PARAMS; + + return PSCI_E_SUCCESS; +} + +/****************************************************************************** + * This function finds the highest power level which will be powered down + * amongst all the power levels specified in the 'state_info' structure + *****************************************************************************/ +unsigned int psci_find_max_off_lvl(const psci_power_state_t *state_info) +{ + int i; + + for (i = PLAT_MAX_PWR_LVL; i >= PSCI_CPU_PWR_LVL; i--) { + if (is_local_state_off(state_info->pwr_domain_state[i])) + return i; + } + + return PSCI_INVALID_DATA; +} + +/****************************************************************************** + * This functions finds the level of the highest power domain which will be + * placed in a low power state during a suspend operation. + *****************************************************************************/ +unsigned int psci_find_target_suspend_lvl(const psci_power_state_t *state_info) +{ + int i; + + for (i = PLAT_MAX_PWR_LVL; i >= PSCI_CPU_PWR_LVL; i--) { + if (!is_local_state_run(state_info->pwr_domain_state[i])) + return i; + } + + return PSCI_INVALID_DATA; } /******************************************************************************* @@ -295,97 +642,6 @@ int psci_get_ns_ep_info(entry_point_info_t *ep, return PSCI_E_SUCCESS; } -/******************************************************************************* - * This function takes an index and level of a power domain node in the topology - * tree and returns its state. State of a non-leaf node needs to be calculated. - ******************************************************************************/ -unsigned short psci_get_state(unsigned int idx, - int level) -{ - /* A cpu node just contains the state which can be directly returned */ - if (level == PSCI_CPU_PWR_LVL) { - flush_cpu_data_by_index(idx, psci_svc_cpu_data.psci_state); - return get_cpu_data_by_index(idx, psci_svc_cpu_data.psci_state); - } - -#if !USE_COHERENT_MEM - flush_dcache_range((uint64_t) &psci_non_cpu_pd_nodes[idx], - sizeof(psci_non_cpu_pd_nodes[idx])); -#endif - /* - * For a power level higher than a cpu, the state has to be - * calculated. It depends upon the value of the reference count - * which is managed by each node at the next lower power level - * e.g. for a cluster, each cpu increments/decrements the reference - * count. If the reference count is 0 then the power level is - * OFF else ON. - */ - if (psci_non_cpu_pd_nodes[idx].ref_count) - return PSCI_STATE_ON; - else - return PSCI_STATE_OFF; -} - -/******************************************************************************* - * This function takes an index and level of a power domain node in the topology - * tree and a target state. State of a non-leaf node needs to be converted to - * a reference count. State of a leaf node can be set directly. - ******************************************************************************/ -void psci_set_state(unsigned int idx, - unsigned short state, - int level) -{ - /* - * For a power level higher than a cpu, the state is used - * to decide whether the reference count is incremented or - * decremented. Entry into the ON_PENDING state does not have - * effect. - */ - if (level > PSCI_CPU_PWR_LVL) { - switch (state) { - case PSCI_STATE_ON: - psci_non_cpu_pd_nodes[idx].ref_count++; - break; - case PSCI_STATE_OFF: - case PSCI_STATE_SUSPEND: - psci_non_cpu_pd_nodes[idx].ref_count--; - break; - case PSCI_STATE_ON_PENDING: - /* - * A power level higher than a cpu will not undergo - * a state change when it is about to be turned on - */ - return; - default: - assert(0); - -#if !USE_COHERENT_MEM - flush_dcache_range((uint64_t) &psci_non_cpu_pd_nodes[idx], - sizeof(psci_non_cpu_pd_nodes[idx])); -#endif - } - } else { - set_cpu_data_by_index(idx, psci_svc_cpu_data.psci_state, state); - flush_cpu_data_by_index(idx, psci_svc_cpu_data.psci_state); - } -} - -/******************************************************************************* - * A power domain could be on, on_pending, suspended or off. These are the - * logical states it can be in. Physically either it is off or on. When it is in - * the state on_pending then it is about to be turned on. It is not possible to - * tell whether that's actually happened or not. So we err on the side of - * caution & treat the power domain as being turned off. - ******************************************************************************/ -unsigned short psci_get_phys_state(unsigned int idx, - int level) -{ - unsigned int state; - - state = psci_get_state(idx, level); - return get_phys_state(state); -} - /******************************************************************************* * Generic handler which is called when a cpu is physically powered on. It * traverses the node information and finds the highest power level powered @@ -396,34 +652,31 @@ unsigned short psci_get_phys_state(unsigned int idx, * coherency at the interconnect level in addition to gic cpu interface. ******************************************************************************/ void psci_power_up_finish(int end_pwrlvl, - pwrlvl_power_on_finisher_t pon_handler) + pwrlvl_power_on_finisher_t power_on_handler) { unsigned int cpu_idx = plat_my_core_pos(); - unsigned int max_phys_off_pwrlvl; + psci_power_state_t state_info = { {PSCI_LOCAL_STATE_RUN} }; /* - * This function acquires the lock corresponding to each power - * level so that by the time all locks are taken, the system topology - * is snapshot and state management can be done safely. + * This function acquires the lock corresponding to each power level so + * that by the time all locks are taken, the system topology is snapshot + * and state management can be done safely. */ psci_acquire_pwr_domain_locks(end_pwrlvl, cpu_idx); - max_phys_off_pwrlvl = psci_find_max_phys_off_pwrlvl(end_pwrlvl, - cpu_idx); - assert(max_phys_off_pwrlvl != PSCI_INVALID_DATA); - - /* Perform generic, architecture and platform specific handling */ - pon_handler(cpu_idx, max_phys_off_pwrlvl); + psci_get_target_local_pwr_states(end_pwrlvl, &state_info); /* - * This function updates the state of each power instance - * corresponding to the cpu index in the range of power levels - * specified. + * Perform generic, architecture and platform specific handling. */ - psci_do_state_coordination(end_pwrlvl, - cpu_idx, - PSCI_STATE_ON); + power_on_handler(cpu_idx, &state_info); + + /* + * Set the requested and target state of this CPU and all the higher + * power domains which are ancestors of this CPU to run. + */ + psci_set_pwr_domains_to_run(end_pwrlvl); /* * This loop releases the lock corresponding to each power level @@ -481,31 +734,39 @@ int psci_spd_migrate_info(uint64_t *mpidr) void psci_print_power_domain_map(void) { #if LOG_LEVEL >= LOG_LEVEL_INFO - unsigned int idx, state; + unsigned int idx; + plat_local_state_t state; + plat_local_state_type_t state_type; /* This array maps to the PSCI_STATE_X definitions in psci.h */ - static const char *psci_state_str[] = { + static const char *psci_state_type_str[] = { "ON", + "RETENTION", "OFF", - "ON_PENDING", - "SUSPEND" }; INFO("PSCI Power Domain Map:\n"); - for (idx = 0; idx < (PSCI_NUM_PWR_DOMAINS - PLATFORM_CORE_COUNT); idx++) { - state = psci_get_state(idx, psci_non_cpu_pd_nodes[idx].level); - INFO(" Domain Node : Level %u, parent_node %d, State %s\n", + for (idx = 0; idx < (PSCI_NUM_PWR_DOMAINS - PLATFORM_CORE_COUNT); + idx++) { + state_type = find_local_state_type( + psci_non_cpu_pd_nodes[idx].local_state); + INFO(" Domain Node : Level %u, parent_node %d," + " State %s (0x%x)\n", psci_non_cpu_pd_nodes[idx].level, psci_non_cpu_pd_nodes[idx].parent_node, - psci_state_str[state]); + psci_state_type_str[state_type], + psci_non_cpu_pd_nodes[idx].local_state); } for (idx = 0; idx < PLATFORM_CORE_COUNT; idx++) { - state = psci_get_state(idx, PSCI_CPU_PWR_LVL); - INFO(" CPU Node : MPID 0x%lx, parent_node %d, State %s\n", + state = psci_get_cpu_local_state_by_idx(idx); + state_type = find_local_state_type(state); + INFO(" CPU Node : MPID 0x%lx, parent_node %d," + " State %s (0x%x)\n", psci_cpu_pd_nodes[idx].mpidr, psci_cpu_pd_nodes[idx].parent_node, - psci_state_str[state]); + psci_state_type_str[state_type], + psci_get_cpu_local_state_by_idx(idx)); } #endif } diff --git a/services/std_svc/psci1.0/psci_main.c b/services/std_svc/psci1.0/psci_main.c index 5732da203d..f024291022 100644 --- a/services/std_svc/psci1.0/psci_main.c +++ b/services/std_svc/psci1.0/psci_main.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "psci_private.h" /******************************************************************************* @@ -93,72 +94,82 @@ int psci_cpu_suspend(unsigned int power_state, unsigned long context_id) { int rc; - unsigned int target_pwrlvl, pstate_type; + unsigned int target_pwrlvl, is_power_down_state; entry_point_info_t ep; + psci_power_state_t state_info = { {PSCI_LOCAL_STATE_RUN} }; + plat_local_state_t cpu_pd_state; - /* Check SBZ bits in power state are zero */ - if (psci_validate_power_state(power_state)) - return PSCI_E_INVALID_PARAMS; - - /* Sanity check the requested state */ - target_pwrlvl = psci_get_pstate_pwrlvl(power_state); - if (target_pwrlvl > PLAT_MAX_PWR_LVL) - return PSCI_E_INVALID_PARAMS; - - /* Validate the power_state using platform pm_ops */ - if (psci_plat_pm_ops->validate_power_state) { - rc = psci_plat_pm_ops->validate_power_state(power_state); - if (rc != PSCI_E_SUCCESS) { - assert(rc == PSCI_E_INVALID_PARAMS); - return PSCI_E_INVALID_PARAMS; - } + /* Validate the power_state parameter */ + rc = psci_validate_power_state(power_state, &state_info); + if (rc != PSCI_E_SUCCESS) { + assert(rc == PSCI_E_INVALID_PARAMS); + return rc; } - /* Validate the entrypoint using platform pm_ops */ - if (psci_plat_pm_ops->validate_ns_entrypoint) { - rc = psci_plat_pm_ops->validate_ns_entrypoint(entrypoint); - if (rc != PSCI_E_SUCCESS) { - assert(rc == PSCI_E_INVALID_PARAMS); - return PSCI_E_INVALID_PARAMS; - } - } - - /* Determine the 'state type' in the 'power_state' parameter */ - pstate_type = psci_get_pstate_type(power_state); - /* - * Ensure that we have a platform specific handler for entering - * a standby state. + * Get the value of the state type bit from the power state parameter. */ - if (pstate_type == PSTATE_TYPE_STANDBY) { - if (!psci_plat_pm_ops->pwr_domain_standby) + is_power_down_state = psci_get_pstate_type(power_state); + + /* Sanity check the requested suspend levels */ + assert (psci_validate_suspend_req(&state_info, is_power_down_state) + == PSCI_E_SUCCESS); + + target_pwrlvl = psci_find_target_suspend_lvl(&state_info); + + /* Fast path for CPU standby.*/ + if (is_cpu_standby_req(is_power_down_state, target_pwrlvl)) { + if (!psci_plat_pm_ops->cpu_standby) return PSCI_E_INVALID_PARAMS; - psci_plat_pm_ops->pwr_domain_standby(power_state); + /* + * Set the state of the CPU power domain to the platform + * specific retention state and enter the standby state. + */ + cpu_pd_state = state_info.pwr_domain_state[PSCI_CPU_PWR_LVL]; + psci_set_cpu_local_state(cpu_pd_state); + psci_plat_pm_ops->cpu_standby(cpu_pd_state); + + /* Upon exit from standby, set the state back to RUN. */ + psci_set_cpu_local_state(PSCI_LOCAL_STATE_RUN); + return PSCI_E_SUCCESS; } /* - * Verify and derive the re-entry information for - * the non-secure world from the non-secure state from - * where this call originated. + * If a power down state has been requested, we need to verify entry + * point and program entry information. */ - rc = psci_get_ns_ep_info(&ep, entrypoint, context_id); - if (rc != PSCI_E_SUCCESS) - return rc; + if (is_power_down_state) { + if (psci_plat_pm_ops->validate_ns_entrypoint) { + rc = psci_plat_pm_ops->validate_ns_entrypoint(entrypoint); + if (rc != PSCI_E_SUCCESS) { + assert(rc == PSCI_E_INVALID_PARAMS); + return rc; + } + } - /* Save PSCI power state parameter for the core in suspend context */ - psci_set_suspend_power_state(power_state); + /* + * Verify and derive the re-entry information for + * the non-secure world from the non-secure state from + * where this call originated. + */ + rc = psci_get_ns_ep_info(&ep, entrypoint, context_id); + if (rc != PSCI_E_SUCCESS) + return rc; + } /* * Do what is needed to enter the power down state. Upon success, - * enter the final wfi which will power down this CPU. + * enter the final wfi which will power down this CPU. This function + * might return if the power down was abandoned for any reason, e.g. + * arrival of an interrupt */ psci_cpu_suspend_start(&ep, - target_pwrlvl); + target_pwrlvl, + &state_info, + is_power_down_state); - /* Reset PSCI power state parameter for the core. */ - psci_set_suspend_power_state(PSCI_INVALID_DATA); return PSCI_E_SUCCESS; } @@ -166,7 +177,7 @@ int psci_system_suspend(unsigned long entrypoint, unsigned long context_id) { int rc; - unsigned int power_state; + psci_power_state_t state_info; entry_point_info_t ep; /* Validate the entrypoint using platform pm_ops */ @@ -174,7 +185,7 @@ int psci_system_suspend(unsigned long entrypoint, rc = psci_plat_pm_ops->validate_ns_entrypoint(entrypoint); if (rc != PSCI_E_SUCCESS) { assert(rc == PSCI_E_INVALID_PARAMS); - return PSCI_E_INVALID_PARAMS; + return rc; } } @@ -191,28 +202,25 @@ int psci_system_suspend(unsigned long entrypoint, if (rc != PSCI_E_SUCCESS) return rc; - /* - * Assert that the required pm_ops hook is implemented to ensure that - * the capability detected during psci_setup() is valid. - */ - assert(psci_plat_pm_ops->get_sys_suspend_power_state); + /* Query the psci_power_state for system suspend */ + psci_query_sys_suspend_pwrstate(&state_info); + + /* Ensure that the psci_power_state makes sense */ + assert(psci_find_target_suspend_lvl(&state_info) == PLAT_MAX_PWR_LVL); + assert(psci_validate_suspend_req(&state_info, PSTATE_TYPE_POWERDOWN) + == PSCI_E_SUCCESS); + assert(is_local_state_off(state_info.pwr_domain_state[PLAT_MAX_PWR_LVL])); /* - * Query the platform for the power_state required for system suspend + * Do what is needed to enter the system suspend state. This function + * might return if the power down was abandoned for any reason, e.g. + * arrival of an interrupt */ - power_state = psci_plat_pm_ops->get_sys_suspend_power_state(); + psci_cpu_suspend_start(&ep, + PLAT_MAX_PWR_LVL, + &state_info, + PSTATE_TYPE_POWERDOWN); - /* Save PSCI power state parameter for the core in suspend context */ - psci_set_suspend_power_state(power_state); - - /* - * Do what is needed to enter the power down state. Upon success, - * enter the final wfi which will power down this cpu. - */ - psci_cpu_suspend_start(&ep, PLAT_MAX_PWR_LVL); - - /* Reset PSCI power state parameter for the core. */ - psci_set_suspend_power_state(PSCI_INVALID_DATA); return PSCI_E_SUCCESS; } @@ -240,26 +248,18 @@ int psci_cpu_off(void) int psci_affinity_info(unsigned long target_affinity, unsigned int lowest_affinity_level) { - unsigned int cpu_idx; - unsigned char cpu_pwr_domain_state; + unsigned int target_idx; /* We dont support level higher than PSCI_CPU_PWR_LVL */ if (lowest_affinity_level > PSCI_CPU_PWR_LVL) return PSCI_E_INVALID_PARAMS; /* Calculate the cpu index of the target */ - cpu_idx = plat_core_pos_by_mpidr(target_affinity); - if (cpu_idx == -1) + target_idx = plat_core_pos_by_mpidr(target_affinity); + if (target_idx == -1) return PSCI_E_INVALID_PARAMS; - cpu_pwr_domain_state = psci_get_state(cpu_idx, PSCI_CPU_PWR_LVL); - - /* A suspended cpu is available & on for the OS */ - if (cpu_pwr_domain_state == PSCI_STATE_SUSPEND) { - cpu_pwr_domain_state = PSCI_STATE_ON; - } - - return cpu_pwr_domain_state; + return psci_get_aff_info_state_by_idx(target_idx); } int psci_migrate(unsigned long target_cpu) @@ -337,10 +337,9 @@ int psci_features(unsigned int psci_fid) if (psci_fid == PSCI_CPU_SUSPEND_AARCH32 || psci_fid == PSCI_CPU_SUSPEND_AARCH64) { /* - * The trusted firmware uses the original power state format - * and does not support OS Initiated Mode. + * The trusted firmware does not support OS Initiated Mode. */ - return (FF_PSTATE_ORIG << FF_PSTATE_SHIFT) | + return (FF_PSTATE << FF_PSTATE_SHIFT) | ((!FF_SUPPORTS_OS_INIT_MODE) << FF_MODE_SUPPORT_SHIFT); } diff --git a/services/std_svc/psci1.0/psci_off.c b/services/std_svc/psci1.0/psci_off.c index 78410f1d9d..28fa52c032 100644 --- a/services/std_svc/psci1.0/psci_off.c +++ b/services/std_svc/psci1.0/psci_off.c @@ -36,6 +36,17 @@ #include #include "psci_private.h" +/****************************************************************************** + * Construct the psci_power_state to request power OFF at all power levels. + ******************************************************************************/ +static void psci_set_power_off_state(psci_power_state_t *state_info) +{ + int lvl; + + for (lvl = PSCI_CPU_PWR_LVL; lvl <= PLAT_MAX_PWR_LVL; lvl++) + state_info->pwr_domain_state[lvl] = PLAT_MAX_OFF_STATE; +} + /****************************************************************************** * Top level handler which is called when a cpu wants to power itself down. * It's assumed that along with turning the cpu power domain off, power @@ -52,7 +63,7 @@ int psci_do_cpu_off(int end_pwrlvl) { int rc, idx = plat_my_core_pos(); - unsigned int max_phys_off_pwrlvl; + psci_power_state_t state_info; /* * This function must only be called on platforms where the @@ -79,29 +90,27 @@ int psci_do_cpu_off(int end_pwrlvl) goto exit; } - /* - * This function updates the state of each power domain instance - * corresponding to the cpu index in the range of power levels - * specified. - */ - psci_do_state_coordination(end_pwrlvl, - idx, - PSCI_STATE_OFF); + /* Construct the psci_power_state for CPU_OFF */ + psci_set_power_off_state(&state_info); - max_phys_off_pwrlvl = psci_find_max_phys_off_pwrlvl(end_pwrlvl, idx); - assert(max_phys_off_pwrlvl != PSCI_INVALID_DATA); + /* + * This function is passed the requested state info and + * it returns the negotiated state info for each power level upto + * the end level specified. + */ + psci_do_state_coordination(end_pwrlvl, &state_info); /* * Arch. management. Perform the necessary steps to flush all * cpu caches. */ - psci_do_pwrdown_cache_maintenance(max_phys_off_pwrlvl); + psci_do_pwrdown_cache_maintenance(psci_find_max_off_lvl(&state_info)); /* * Plat. management: Perform platform specific actions to turn this * cpu off e.g. exit cpu coherency, program the power controller etc. */ - psci_plat_pm_ops->pwr_domain_off(max_phys_off_pwrlvl); + psci_plat_pm_ops->pwr_domain_off(&state_info); exit: /* @@ -111,6 +120,16 @@ exit: psci_release_pwr_domain_locks(end_pwrlvl, idx); + /* + * Set the affinity info state to OFF. This writes directly to main + * memory as caches are disabled, so cache maintenance is required + * to ensure that later cached reads of aff_info_state return + * AFF_STATE_OFF. + */ + flush_cpu_data(psci_svc_cpu_data.aff_info_state); + psci_set_aff_info_state(AFF_STATE_OFF); + inv_cpu_data(psci_svc_cpu_data.aff_info_state); + /* * Check if all actions needed to safely power down this cpu have * successfully completed. Enter a wfi loop which will allow the diff --git a/services/std_svc/psci1.0/psci_on.c b/services/std_svc/psci1.0/psci_on.c index c85e349309..542cc239e0 100644 --- a/services/std_svc/psci1.0/psci_on.c +++ b/services/std_svc/psci1.0/psci_on.c @@ -44,18 +44,36 @@ * This function checks whether a cpu which has been requested to be turned on * is OFF to begin with. ******************************************************************************/ -static int cpu_on_validate_state(unsigned int psci_state) +static int cpu_on_validate_state(aff_info_state_t aff_state) { - if (psci_state == PSCI_STATE_ON || psci_state == PSCI_STATE_SUSPEND) + if (aff_state == AFF_STATE_ON) return PSCI_E_ALREADY_ON; - if (psci_state == PSCI_STATE_ON_PENDING) + if (aff_state == AFF_STATE_ON_PENDING) return PSCI_E_ON_PENDING; - assert(psci_state == PSCI_STATE_OFF); + assert(aff_state == AFF_STATE_OFF); return PSCI_E_SUCCESS; } +/******************************************************************************* + * This function sets the aff_info_state in the per-cpu data of the CPU + * specified by cpu_idx + ******************************************************************************/ +static void psci_set_aff_info_state_by_idx(unsigned int cpu_idx, + aff_info_state_t aff_state) +{ + + set_cpu_data_by_index(cpu_idx, + psci_svc_cpu_data.aff_info_state, + aff_state); + + /* + * Flush aff_info_state as it will be accessed with caches turned OFF. + */ + flush_cpu_data_by_index(cpu_idx, psci_svc_cpu_data.aff_info_state); +} + /******************************************************************************* * Generic handler which is called to physically power on a cpu identified by * its mpidr. It performs the generic, architectural, platform setup and state @@ -67,8 +85,8 @@ static int cpu_on_validate_state(unsigned int psci_state) * platform handler as it can return error. ******************************************************************************/ int psci_cpu_on_start(unsigned long target_cpu, - entry_point_info_t *ep, - int end_pwrlvl) + entry_point_info_t *ep, + int end_pwrlvl) { int rc; unsigned long psci_entrypoint; @@ -88,7 +106,7 @@ int psci_cpu_on_start(unsigned long target_cpu, * Generic management: Ensure that the cpu is off to be * turned on. */ - rc = cpu_on_validate_state(psci_get_state(target_idx, PSCI_CPU_PWR_LVL)); + rc = cpu_on_validate_state(psci_get_aff_info_state_by_idx(target_idx)); if (rc != PSCI_E_SUCCESS) goto exit; @@ -101,13 +119,9 @@ int psci_cpu_on_start(unsigned long target_cpu, psci_spd_pm->svc_on(target_cpu); /* - * This function updates the state of each affinity instance - * corresponding to the mpidr in the range of power domain levels - * specified. + * Set the Affinity info state of the target cpu to ON_PENDING. */ - psci_do_state_coordination(end_pwrlvl, - target_idx, - PSCI_STATE_ON_PENDING); + psci_set_aff_info_state_by_idx(target_idx, AFF_STATE_ON_PENDING); /* * Perform generic, architecture and platform specific handling. @@ -120,9 +134,8 @@ int psci_cpu_on_start(unsigned long target_cpu, * of the target cpu to allow it to perform the necessary * steps to power on. */ - rc = psci_plat_pm_ops->pwr_domain_on(target_cpu, - psci_entrypoint, - MPIDR_AFFLVL0); + rc = psci_plat_pm_ops->pwr_domain_on((u_register_t)target_cpu, + psci_entrypoint); assert(rc == PSCI_E_SUCCESS || rc == PSCI_E_INTERN_FAIL); if (rc == PSCI_E_SUCCESS) @@ -130,9 +143,7 @@ int psci_cpu_on_start(unsigned long target_cpu, cm_init_context_by_index(target_idx, ep); else /* Restore the state on error. */ - psci_do_state_coordination(end_pwrlvl, - target_idx, - PSCI_STATE_OFF); + psci_set_aff_info_state_by_idx(target_idx, AFF_STATE_OFF); exit: psci_spin_unlock_cpu(target_idx); @@ -141,22 +152,19 @@ exit: /******************************************************************************* * The following function finish an earlier power on request. They - * are called by the common finisher routine in psci_common.c. + * are called by the common finisher routine in psci_common.c. The `state_info` + * is the psci_power_state from which this CPU has woken up from. ******************************************************************************/ void psci_cpu_on_finish(unsigned int cpu_idx, - int max_off_pwrlvl) + psci_power_state_t *state_info) { - /* Ensure we have been explicitly woken up by another cpu */ - assert(psci_get_state(cpu_idx, PSCI_CPU_PWR_LVL) - == PSCI_STATE_ON_PENDING); - /* * Plat. management: Perform the platform specific actions * for this cpu e.g. enabling the gic or zeroing the mailbox * register. The actual state of this cpu has already been * changed. */ - psci_plat_pm_ops->pwr_domain_on_finish(max_off_pwrlvl); + psci_plat_pm_ops->pwr_domain_on_finish(state_info); /* * Arch. management: Enable data cache and manage stack memory @@ -179,6 +187,9 @@ void psci_cpu_on_finish(unsigned int cpu_idx, psci_spin_lock_cpu(cpu_idx); psci_spin_unlock_cpu(cpu_idx); + /* Ensure we have been explicitly woken up by another cpu */ + assert(psci_get_aff_info_state() == AFF_STATE_ON_PENDING); + /* * Call the cpu on finish handler registered by the Secure Payload * Dispatcher to let it do any bookeeping. If the handler encounters an diff --git a/services/std_svc/psci1.0/psci_private.h b/services/std_svc/psci1.0/psci_private.h index e7ad711fd9..8d08df4dab 100644 --- a/services/std_svc/psci1.0/psci_private.h +++ b/services/std_svc/psci1.0/psci_private.h @@ -80,12 +80,36 @@ define_psci_cap(PSCI_MIG_INFO_UP_CPU_AARCH64) | \ define_psci_cap(PSCI_SYSTEM_SUSPEND_AARCH64)) +/* + * Helper macros to get/set the fields of PSCI per-cpu data. + */ +#define psci_set_aff_info_state(aff_state) \ + set_cpu_data(psci_svc_cpu_data.aff_info_state, aff_state) +#define psci_get_aff_info_state() \ + get_cpu_data(psci_svc_cpu_data.aff_info_state) +#define psci_get_aff_info_state_by_idx(idx) \ + get_cpu_data_by_index(idx, psci_svc_cpu_data.aff_info_state) +#define psci_get_suspend_pwrlvl() \ + get_cpu_data(psci_svc_cpu_data.target_pwrlvl) +#define psci_set_suspend_pwrlvl(target_lvl) \ + set_cpu_data(psci_svc_cpu_data.target_pwrlvl, target_lvl) +#define psci_set_cpu_local_state(state) \ + set_cpu_data(psci_svc_cpu_data.local_state, state) +#define psci_get_cpu_local_state() \ + get_cpu_data(psci_svc_cpu_data.local_state) +#define psci_get_cpu_local_state_by_idx(idx) \ + get_cpu_data_by_index(idx, psci_svc_cpu_data.local_state) + /* * Helper macros for the CPU level spinlocks */ #define psci_spin_lock_cpu(idx) spin_lock(&psci_cpu_pd_nodes[idx].cpu_lock) #define psci_spin_unlock_cpu(idx) spin_unlock(&psci_cpu_pd_nodes[idx].cpu_lock) +/* Helper macro to identify a CPU standby request in PSCI Suspend call */ +#define is_cpu_standby_req(is_power_down_state, retn_lvl) \ + (((!(is_power_down_state)) && ((retn_lvl) == 0)) ? 1 : 0) + /******************************************************************************* * The following two data structures implement the power domain tree. The tree * is used to track the state of all the nodes i.e. power domain instances @@ -113,7 +137,8 @@ typedef struct non_cpu_pwr_domain_node { */ unsigned int parent_node; - unsigned char ref_count; + plat_local_state_t local_state; + unsigned char level; #if USE_COHERENT_MEM bakery_lock_t lock; @@ -142,12 +167,12 @@ typedef struct cpu_pwr_domain_node { } cpu_pd_node_t; typedef void (*pwrlvl_power_on_finisher_t)(unsigned int cpu_idx, - int max_off_pwrlvl); + psci_power_state_t *state_info); /******************************************************************************* * Data prototypes ******************************************************************************/ -extern const plat_pm_ops_t *psci_plat_pm_ops; +extern const plat_psci_ops_t *psci_plat_pm_ops; extern non_cpu_pd_node_t psci_non_cpu_pd_nodes[PSCI_NUM_NON_CPU_PWR_DOMAINS]; extern cpu_pd_node_t psci_cpu_pd_nodes[PLATFORM_CORE_COUNT]; extern uint32_t psci_caps; @@ -161,28 +186,31 @@ extern const spd_pm_ops_t *psci_spd_pm; * Function prototypes ******************************************************************************/ /* Private exported functions from psci_common.c */ -unsigned short psci_get_state(unsigned int idx, int level); -unsigned short psci_get_phys_state(unsigned int idx, int level); -void psci_set_state(unsigned int idx, unsigned short state, int level); +int psci_validate_power_state(unsigned int power_state, + psci_power_state_t *state_info); +void psci_query_sys_suspend_pwrstate(psci_power_state_t *state_info); int psci_validate_mpidr(unsigned long mpidr); int get_power_on_target_pwrlvl(void); +void psci_init_req_local_pwr_states(void); void psci_power_up_finish(int end_pwrlvl, - pwrlvl_power_on_finisher_t pon_handler); + pwrlvl_power_on_finisher_t power_on_handler); int psci_get_ns_ep_info(entry_point_info_t *ep, uint64_t entrypoint, uint64_t context_id); void psci_get_parent_pwr_domain_nodes(unsigned int cpu_idx, int end_lvl, unsigned int node_index[]); void psci_do_state_coordination(int end_pwrlvl, - unsigned int cpu_idx, - uint32_t state); + psci_power_state_t *state_info); void psci_acquire_pwr_domain_locks(int end_pwrlvl, unsigned int cpu_idx); void psci_release_pwr_domain_locks(int end_pwrlvl, unsigned int cpu_idx); +int psci_validate_suspend_req(const psci_power_state_t *state_info, + unsigned int is_power_down_state_req); +unsigned int psci_find_max_off_lvl(const psci_power_state_t *state_info); +unsigned int psci_find_target_suspend_lvl(const psci_power_state_t *state_info); +void psci_set_pwr_domains_to_run(uint32_t end_pwrlvl); void psci_print_power_domain_map(void); -uint32_t psci_find_max_phys_off_pwrlvl(uint32_t end_pwrlvl, - unsigned int cpu_idx); unsigned int psci_is_last_on_cpu(void); int psci_spd_migrate_info(uint64_t *mpidr); @@ -192,18 +220,19 @@ int psci_cpu_on_start(unsigned long target_cpu, int end_pwrlvl); void psci_cpu_on_finish(unsigned int cpu_idx, - int max_off_pwrlvl); + psci_power_state_t *state_info); /* Private exported functions from psci_cpu_off.c */ int psci_do_cpu_off(int end_pwrlvl); -/* Private exported functions from psci_suspend.c */ +/* Private exported functions from psci_pwrlvl_suspend.c */ void psci_cpu_suspend_start(entry_point_info_t *ep, - int end_pwrlvl); -void psci_cpu_suspend_finish(unsigned int cpu_idx, - int max_off_pwrlvl); + int end_pwrlvl, + psci_power_state_t *state_info, + unsigned int is_power_down_state_req); -void psci_set_suspend_power_state(unsigned int power_state); +void psci_cpu_suspend_finish(unsigned int cpu_idx, + psci_power_state_t *state_info); /* Private exported functions from psci_helpers.S */ void psci_do_pwrdown_cache_maintenance(uint32_t pwr_level); diff --git a/services/std_svc/psci1.0/psci_setup.c b/services/std_svc/psci1.0/psci_setup.c index 5f7376be70..599c09bcb4 100644 --- a/services/std_svc/psci1.0/psci_setup.c +++ b/services/std_svc/psci1.0/psci_setup.c @@ -61,26 +61,30 @@ static void psci_init_pwr_domain_node(int node_idx, int parent_idx, int level) psci_non_cpu_pd_nodes[node_idx].level = level; psci_lock_init(psci_non_cpu_pd_nodes, node_idx); psci_non_cpu_pd_nodes[node_idx].parent_node = parent_idx; + psci_non_cpu_pd_nodes[node_idx].local_state = + PLAT_MAX_OFF_STATE; } else { + psci_cpu_data_t *svc_cpu_data; psci_cpu_pd_nodes[node_idx].parent_node = parent_idx; /* Initialize with an invalid mpidr */ psci_cpu_pd_nodes[node_idx].mpidr = PSCI_INVALID_MPIDR; - /* - * Mark the cpu as OFF. - */ - set_cpu_data_by_index(node_idx, - psci_svc_cpu_data.psci_state, - PSCI_STATE_OFF); + svc_cpu_data = + &(_cpu_data_by_index(node_idx)->psci_svc_cpu_data); - /* Invalidate the suspend context for the node */ - set_cpu_data_by_index(node_idx, - psci_svc_cpu_data.power_state, - PSCI_INVALID_DATA); + /* Set the Affinity Info for the cores as OFF */ + svc_cpu_data->aff_info_state = AFF_STATE_OFF; - flush_cpu_data_by_index(node_idx, psci_svc_cpu_data); + /* Invalidate the suspend level for the cpu */ + svc_cpu_data->target_pwrlvl = PSCI_INVALID_DATA; + + /* Set the power state to OFF state */ + svc_cpu_data->local_state = PLAT_MAX_OFF_STATE; + + flush_dcache_range((uint64_t)svc_cpu_data, + sizeof(*svc_cpu_data)); cm_set_context_by_index(node_idx, (void *) &psci_ns_context[node_idx], @@ -233,14 +237,15 @@ int32_t psci_setup(void) flush_dcache_range((uint64_t) &psci_cpu_pd_nodes, sizeof(psci_cpu_pd_nodes)); - /* - * Mark the current CPU and its parent power domains as ON. No need to lock - * as the system is UP on the primary at this stage of boot. - */ - psci_do_state_coordination(PLAT_MAX_PWR_LVL, plat_my_core_pos(), - PSCI_STATE_ON); + psci_init_req_local_pwr_states(); - platform_setup_pm(&psci_plat_pm_ops); + /* + * Set the requested and target state of this CPU and all the higher + * power domain levels for this CPU to run. + */ + psci_set_pwr_domains_to_run(PLAT_MAX_PWR_LVL); + + plat_setup_psci_ops(&psci_plat_pm_ops); assert(psci_plat_pm_ops); /* Initialize the psci capability */ diff --git a/services/std_svc/psci1.0/psci_suspend.c b/services/std_svc/psci1.0/psci_suspend.c index c402937ab3..2e4270f0ad 100644 --- a/services/std_svc/psci1.0/psci_suspend.c +++ b/services/std_svc/psci1.0/psci_suspend.c @@ -42,82 +42,97 @@ #include "psci_private.h" /******************************************************************************* - * This function saves the power state parameter passed in the current PSCI - * cpu_suspend call in the per-cpu data array. + * This function does generic and platform specific operations after a wake-up + * from standby/retention states at multiple power levels. ******************************************************************************/ -void psci_set_suspend_power_state(unsigned int power_state) +static void psci_suspend_to_standby_finisher(unsigned int cpu_idx, + psci_power_state_t *state_info, + unsigned int end_pwrlvl) { - set_cpu_data(psci_svc_cpu_data.power_state, power_state); - flush_cpu_data(psci_svc_cpu_data.power_state); + psci_acquire_pwr_domain_locks(end_pwrlvl, + cpu_idx); + + /* + * Plat. management: Allow the platform to do operations + * on waking up from retention. + */ + psci_plat_pm_ops->pwr_domain_suspend_finish(state_info); + + /* + * Set the requested and target state of this CPU and all the higher + * power domain levels for this CPU to run. + */ + psci_set_pwr_domains_to_run(end_pwrlvl); + + psci_release_pwr_domain_locks(end_pwrlvl, + cpu_idx); } /******************************************************************************* - * This function gets the power level till which the current cpu could be - * powered down during a cpu_suspend call. Returns PSCI_INVALID_DATA if the - * power state is invalid. + * This function does generic and platform specific suspend to power down + * operations. ******************************************************************************/ -int psci_get_suspend_pwrlvl(void) +static void psci_suspend_to_pwrdown_start(int end_pwrlvl, + entry_point_info_t *ep, + psci_power_state_t *state_info) { - unsigned int power_state; + /* Save PSCI target power level for the suspend finisher handler */ + psci_set_suspend_pwrlvl(end_pwrlvl); - power_state = get_cpu_data(psci_svc_cpu_data.power_state); + /* + * Flush the target power level as it will be accessed on power up with + * Data cache disabled. + */ + flush_cpu_data(psci_svc_cpu_data.target_pwrlvl); - return ((power_state == PSCI_INVALID_DATA) ? - power_state : psci_get_pstate_pwrlvl(power_state)); -} + /* + * Call the cpu suspend handler registered by the Secure Payload + * Dispatcher to let it do any book-keeping. If the handler encounters an + * error, it's expected to assert within + */ + if (psci_spd_pm && psci_spd_pm->svc_suspend) + psci_spd_pm->svc_suspend(0); -/******************************************************************************* - * This function gets the state id of the current cpu from the power state - * parameter saved in the per-cpu data array. Returns PSCI_INVALID_DATA if the - * power state saved is invalid. - ******************************************************************************/ -int psci_get_suspend_stateid(void) -{ - unsigned int power_state; + /* + * Store the re-entry information for the non-secure world. + */ + cm_init_my_context(ep); - power_state = get_cpu_data(psci_svc_cpu_data.power_state); - - return ((power_state == PSCI_INVALID_DATA) ? - power_state : psci_get_pstate_id(power_state)); -} - -/******************************************************************************* - * This function gets the state id of the cpu specified by the cpu index - * from the power state parameter saved in the per-cpu data array. Returns - * PSCI_INVALID_DATA if the power state saved is invalid. - ******************************************************************************/ -int psci_get_suspend_stateid_by_idx(unsigned long cpu_idx) -{ - unsigned int power_state; - - power_state = get_cpu_data_by_index(cpu_idx, - psci_svc_cpu_data.power_state); - - return ((power_state == PSCI_INVALID_DATA) ? - power_state : psci_get_pstate_id(power_state)); + /* + * Arch. management. Perform the necessary steps to flush all + * cpu caches. Currently we assume that the power level correspond + * the cache level. + * TODO : Introduce a mechanism to query the cache level to flush + * and the cpu-ops power down to perform from the platform. + */ + psci_do_pwrdown_cache_maintenance(psci_find_max_off_lvl(state_info)); } /******************************************************************************* * Top level handler which is called when a cpu wants to suspend its execution. * It is assumed that along with suspending the cpu power domain, power domains - * at higher levels until the target power level will be suspended as well. - * It finds the highest level where a domain has to be suspended by traversing - * the node information and then performs generic, architectural, platform - * setup and state management required to suspend that power domain and domains - * below it. * e.g. For a cpu that's to be suspended, it could mean programming - * the power controller whereas for a cluster that's to be suspended, it will - * call the platform specific code which will disable coherency at the - * interconnect level if the cpu is the last in the cluster and also the - * program the power controller. + * at higher levels until the target power level will be suspended as well. It + * coordinates with the platform to negotiate the target state for each of + * the power domain level till the target power domain level. It then performs + * generic, architectural, platform setup and state management required to + * suspend that power domain level and power domain levels below it. + * e.g. For a cpu that's to be suspended, it could mean programming the + * power controller whereas for a cluster that's to be suspended, it will call + * the platform specific code which will disable coherency at the interconnect + * level if the cpu is the last in the cluster and also the program the power + * controller. * * All the required parameter checks are performed at the beginning and after * the state transition has been done, no further error is expected and it is * not possible to undo any of the actions taken beyond that point. ******************************************************************************/ -void psci_cpu_suspend_start(entry_point_info_t *ep, int end_pwrlvl) +void psci_cpu_suspend_start(entry_point_info_t *ep, + int end_pwrlvl, + psci_power_state_t *state_info, + unsigned int is_power_down_state) { int skip_wfi = 0; - unsigned int max_phys_off_pwrlvl, idx = plat_my_core_pos(); + unsigned int idx = plat_my_core_pos(); unsigned long psci_entrypoint; /* @@ -146,39 +161,20 @@ void psci_cpu_suspend_start(entry_point_info_t *ep, int end_pwrlvl) } /* - * Call the cpu suspend handler registered by the Secure Payload - * Dispatcher to let it do any bookeeping. If the handler encounters an - * error, it's expected to assert within + * This function is passed the requested state info and + * it returns the negotiated state info for each power level upto + * the end level specified. */ - if (psci_spd_pm && psci_spd_pm->svc_suspend) - psci_spd_pm->svc_suspend(0); + psci_do_state_coordination(end_pwrlvl, state_info); - /* - * This function updates the state of each power domain instance - * corresponding to the cpu index in the range of power levels - * specified. - */ - psci_do_state_coordination(end_pwrlvl, - idx, - PSCI_STATE_SUSPEND); + psci_entrypoint = 0; + if (is_power_down_state) { + psci_suspend_to_pwrdown_start(end_pwrlvl, ep, state_info); - max_phys_off_pwrlvl = psci_find_max_phys_off_pwrlvl(end_pwrlvl, - idx); - assert(max_phys_off_pwrlvl != PSCI_INVALID_DATA); - - /* - * Store the re-entry information for the non-secure world. - */ - cm_init_my_context(ep); - - /* Set the secure world (EL3) re-entry point after BL1 */ - psci_entrypoint = (unsigned long) psci_cpu_suspend_finish_entry; - - /* - * Arch. management. Perform the necessary steps to flush all - * cpu caches. - */ - psci_do_pwrdown_cache_maintenance(max_phys_off_pwrlvl); + /* Set the secure world (EL3) re-entry point after BL1. */ + psci_entrypoint = + (unsigned long) psci_cpu_suspend_finish_entry; + } /* * Plat. management: Allow the platform to perform the @@ -186,8 +182,7 @@ void psci_cpu_suspend_start(entry_point_info_t *ep, int end_pwrlvl) * platform defined mailbox with the psci entrypoint, * program the power controller etc. */ - psci_plat_pm_ops->pwr_domain_suspend(psci_entrypoint, - max_phys_off_pwrlvl); + psci_plat_pm_ops->pwr_domain_suspend(psci_entrypoint, state_info); exit: /* @@ -195,23 +190,41 @@ exit: * reverse order to which they were acquired. */ psci_release_pwr_domain_locks(end_pwrlvl, - idx); - if (!skip_wfi) + idx); + if (skip_wfi) + return; + + if (is_power_down_state) psci_power_down_wfi(); + + /* + * We will reach here if only retention/standby states have been + * requested at multiple power levels. This means that the cpu + * context will be preserved. + */ + wfi(); + + /* + * After we wake up from context retaining suspend, call the + * context retaining suspend finisher. + */ + psci_suspend_to_standby_finisher(idx, state_info, end_pwrlvl); } /******************************************************************************* * The following functions finish an earlier suspend request. They - * are called by the common finisher routine in psci_common.c. + * are called by the common finisher routine in psci_common.c. The `state_info` + * is the psci_power_state from which this CPU has woken up from. ******************************************************************************/ -void psci_cpu_suspend_finish(unsigned int cpu_idx, int max_off_pwrlvl) +void psci_cpu_suspend_finish(unsigned int cpu_idx, + psci_power_state_t *state_info) { int32_t suspend_level; uint64_t counter_freq; /* Ensure we have been woken up from a suspended state */ - assert(psci_get_state(cpu_idx, PSCI_CPU_PWR_LVL) - == PSCI_STATE_SUSPEND); + assert(psci_get_aff_info_state() == AFF_STATE_ON && is_local_state_off(\ + state_info->pwr_domain_state[PSCI_CPU_PWR_LVL])); /* * Plat. management: Perform the platform specific actions @@ -220,7 +233,7 @@ void psci_cpu_suspend_finish(unsigned int cpu_idx, int max_off_pwrlvl) * wrong then assert as there is no way to recover from this * situation. */ - psci_plat_pm_ops->pwr_domain_suspend_finish(max_off_pwrlvl); + psci_plat_pm_ops->pwr_domain_suspend_finish(state_info); /* * Arch. management: Enable the data cache, manage stack memory and @@ -244,8 +257,8 @@ void psci_cpu_suspend_finish(unsigned int cpu_idx, int max_off_pwrlvl) psci_spd_pm->svc_suspend_finish(suspend_level); } - /* Invalidate the suspend context for the node */ - psci_set_suspend_power_state(PSCI_INVALID_DATA); + /* Invalidate the suspend level for the cpu */ + psci_set_suspend_pwrlvl(PSCI_INVALID_DATA); /* * Generic management: Now we just need to retrieve the From eb975f52ea2e70216d214efcff3154f3cf081cb0 Mon Sep 17 00:00:00 2001 From: Sandrine Bailleux Date: Thu, 11 Jun 2015 10:46:48 +0100 Subject: [PATCH 07/20] PSCI: Unify warm reset entry points There used to be 2 warm reset entry points: - the "on finisher", for when the core has been turned on using a PSCI CPU_ON call; - the "suspend finisher", entered upon resumption from a previous PSCI CPU_SUSPEND call. The appropriate warm reset entry point used to be programmed into the mailboxes by the power management hooks. However, it is not required to provide this information to the PSCI entry point code, as it can figure it out by itself. By querying affinity info state, a core is able to determine on which execution path it is. If the state is ON_PENDING then it means it's been turned on else it is resuming from suspend. This patch unifies the 2 warm reset entry points into a single one: psci_entrypoint(). The patch also implements the necessary logic to distinguish between the 2 types of warm resets in the power up finisher. The plat_setup_psci_ops() API now takes the secure entry point as an additional parameter to enable the platforms to configure their mailbox. The platform hooks `pwr_domain_on` and `pwr_domain_suspend` no longer take secure entry point as a parameter. Change-Id: I7d1c93787b54213aefdbc046b8cd66a555dfbfd9 --- include/bl31/services/psci1.0/psci.h | 12 ++++---- include/plat/common/psci1.0/platform.h | 3 +- services/std_svc/psci1.0/psci_common.c | 37 +++++++++++++++++++++---- services/std_svc/psci1.0/psci_entry.S | 27 ++++++------------ services/std_svc/psci1.0/psci_on.c | 7 +---- services/std_svc/psci1.0/psci_private.h | 7 +---- services/std_svc/psci1.0/psci_setup.c | 3 +- services/std_svc/psci1.0/psci_suspend.c | 11 ++------ 8 files changed, 53 insertions(+), 54 deletions(-) diff --git a/include/bl31/services/psci1.0/psci.h b/include/bl31/services/psci1.0/psci.h index d79ab74db0..fe23711e13 100644 --- a/include/bl31/services/psci1.0/psci.h +++ b/include/bl31/services/psci1.0/psci.h @@ -256,13 +256,12 @@ typedef struct psci_cpu_data { ******************************************************************************/ 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); + int (*pwr_domain_on)(u_register_t mpidr); void (*pwr_domain_off)(const psci_power_state_t *target_state); - void (*pwr_domain_suspend)(unsigned long sec_entrypoint, - const psci_power_state_t *target_state); + void (*pwr_domain_suspend)(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 (*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, @@ -302,8 +301,7 @@ int psci_cpu_on(unsigned long, unsigned long, unsigned long); void __dead2 psci_power_down_wfi(void); -void psci_cpu_on_finish_entry(void); -void psci_cpu_suspend_finish_entry(void); +void psci_entrypoint(void); void psci_register_spd_pm_hook(const spd_pm_ops_t *); uint64_t psci_smc_handler(uint32_t smc_fid, diff --git a/include/plat/common/psci1.0/platform.h b/include/plat/common/psci1.0/platform.h index a961863ac4..d29fcfc255 100644 --- a/include/plat/common/psci1.0/platform.h +++ b/include/plat/common/psci1.0/platform.h @@ -181,7 +181,8 @@ struct entry_point_info *bl31_plat_get_next_image_ep_info(uint32_t type); /******************************************************************************* * Mandatory PSCI functions (BL3-1) ******************************************************************************/ -int plat_setup_psci_ops(const struct plat_psci_ops **); +int plat_setup_psci_ops(uintptr_t sec_entrypoint, + const struct plat_psci_ops **); const unsigned char *plat_get_power_domain_tree_desc(void); /******************************************************************************* diff --git a/services/std_svc/psci1.0/psci_common.c b/services/std_svc/psci1.0/psci_common.c index 70cc98d126..0b885cde3d 100644 --- a/services/std_svc/psci1.0/psci_common.c +++ b/services/std_svc/psci1.0/psci_common.c @@ -189,7 +189,7 @@ unsigned int psci_is_last_on_cpu(void) * been physically powered up. It is expected to be called immediately after * reset from assembler code. ******************************************************************************/ -int get_power_on_target_pwrlvl(void) +static int get_power_on_target_pwrlvl(void) { int pwrlvl; @@ -651,11 +651,26 @@ int psci_get_ns_ep_info(entry_point_info_t *ep, * code to enable the gic cpu interface and for a cluster it will enable * coherency at the interconnect level in addition to gic cpu interface. ******************************************************************************/ -void psci_power_up_finish(int end_pwrlvl, - pwrlvl_power_on_finisher_t power_on_handler) +void psci_power_up_finish(void) { unsigned int cpu_idx = plat_my_core_pos(); psci_power_state_t state_info = { {PSCI_LOCAL_STATE_RUN} }; + int end_pwrlvl; + + /* + * Verify that we have been explicitly turned ON or resumed from + * suspend. + */ + if (psci_get_aff_info_state() == AFF_STATE_OFF) { + ERROR("Unexpected affinity info state"); + panic(); + } + + /* + * Get the maximum power domain level to traverse to after this cpu + * has been physically powered up. + */ + end_pwrlvl = get_power_on_target_pwrlvl(); /* * This function acquires the lock corresponding to each power level so @@ -668,9 +683,21 @@ void psci_power_up_finish(int end_pwrlvl, psci_get_target_local_pwr_states(end_pwrlvl, &state_info); /* - * Perform generic, architecture and platform specific handling. + * This CPU could be resuming from suspend or it could have just been + * turned on. To distinguish between these 2 cases, we examine the + * affinity state of the CPU: + * - If the affinity state is ON_PENDING then it has just been + * turned on. + * - Else it is resuming from suspend. + * + * Depending on the type of warm reset identified, choose the right set + * of power management handler and perform the generic, architecture + * and platform specific handling. */ - power_on_handler(cpu_idx, &state_info); + if (psci_get_aff_info_state() == AFF_STATE_ON_PENDING) + psci_cpu_on_finish(cpu_idx, &state_info); + else + psci_cpu_suspend_finish(cpu_idx, &state_info); /* * Set the requested and target state of this CPU and all the higher diff --git a/services/std_svc/psci1.0/psci_entry.S b/services/std_svc/psci1.0/psci_entry.S index 4e7456dbd4..73c33779aa 100644 --- a/services/std_svc/psci1.0/psci_entry.S +++ b/services/std_svc/psci1.0/psci_entry.S @@ -34,25 +34,16 @@ #include #include - .globl psci_cpu_on_finish_entry - .globl psci_cpu_suspend_finish_entry + .globl psci_entrypoint .globl psci_power_down_wfi - /* ----------------------------------------------------- - * This cpu has been physically powered up. Depending - * upon whether it was resumed from suspend or simply - * turned on, call the common power on finisher with - * the handlers (chosen depending upon original state). - * ----------------------------------------------------- + /* -------------------------------------------------------------------- + * This CPU has been physically powered up. It is either resuming from + * suspend or has simply been turned on. In both cases, call the power + * on finisher. + * -------------------------------------------------------------------- */ -func psci_cpu_on_finish_entry - adr x23, psci_cpu_on_finish - b psci_power_up_entry - -psci_cpu_suspend_finish_entry: - adr x23, psci_cpu_suspend_finish - -psci_power_up_entry: +func psci_entrypoint /* * On the warm boot path, most of the EL3 initialisations performed by * 'el3_entrypoint_common' must be skipped: @@ -98,12 +89,10 @@ psci_power_up_entry: mov x0, #DISABLE_DCACHE bl bl31_plat_enable_mmu - bl get_power_on_target_pwrlvl - mov x1, x23 bl psci_power_up_finish b el3_exit -endfunc psci_cpu_on_finish_entry +endfunc psci_entrypoint /* -------------------------------------------- * This function is called to indicate to the diff --git a/services/std_svc/psci1.0/psci_on.c b/services/std_svc/psci1.0/psci_on.c index 542cc239e0..d68198f2e2 100644 --- a/services/std_svc/psci1.0/psci_on.c +++ b/services/std_svc/psci1.0/psci_on.c @@ -89,7 +89,6 @@ int psci_cpu_on_start(unsigned long target_cpu, int end_pwrlvl) { int rc; - unsigned long psci_entrypoint; unsigned int target_idx = plat_core_pos_by_mpidr(target_cpu); /* @@ -126,16 +125,12 @@ int psci_cpu_on_start(unsigned long target_cpu, /* * Perform generic, architecture and platform specific handling. */ - /* Set the secure world (EL3) re-entry point after BL1 */ - psci_entrypoint = (unsigned long) psci_cpu_on_finish_entry; - /* * Plat. management: Give the platform the current state * of the target cpu to allow it to perform the necessary * steps to power on. */ - rc = psci_plat_pm_ops->pwr_domain_on((u_register_t)target_cpu, - psci_entrypoint); + rc = psci_plat_pm_ops->pwr_domain_on((u_register_t)target_cpu); assert(rc == PSCI_E_SUCCESS || rc == PSCI_E_INTERN_FAIL); if (rc == PSCI_E_SUCCESS) diff --git a/services/std_svc/psci1.0/psci_private.h b/services/std_svc/psci1.0/psci_private.h index 8d08df4dab..e2e32c7962 100644 --- a/services/std_svc/psci1.0/psci_private.h +++ b/services/std_svc/psci1.0/psci_private.h @@ -166,9 +166,6 @@ typedef struct cpu_pwr_domain_node { spinlock_t cpu_lock; } cpu_pd_node_t; -typedef void (*pwrlvl_power_on_finisher_t)(unsigned int cpu_idx, - psci_power_state_t *state_info); - /******************************************************************************* * Data prototypes ******************************************************************************/ @@ -190,10 +187,8 @@ int psci_validate_power_state(unsigned int power_state, psci_power_state_t *state_info); void psci_query_sys_suspend_pwrstate(psci_power_state_t *state_info); int psci_validate_mpidr(unsigned long mpidr); -int get_power_on_target_pwrlvl(void); void psci_init_req_local_pwr_states(void); -void psci_power_up_finish(int end_pwrlvl, - pwrlvl_power_on_finisher_t power_on_handler); +void psci_power_up_finish(void); int psci_get_ns_ep_info(entry_point_info_t *ep, uint64_t entrypoint, uint64_t context_id); void psci_get_parent_pwr_domain_nodes(unsigned int cpu_idx, diff --git a/services/std_svc/psci1.0/psci_setup.c b/services/std_svc/psci1.0/psci_setup.c index 599c09bcb4..ce4da9599f 100644 --- a/services/std_svc/psci1.0/psci_setup.c +++ b/services/std_svc/psci1.0/psci_setup.c @@ -245,7 +245,8 @@ int32_t psci_setup(void) */ psci_set_pwr_domains_to_run(PLAT_MAX_PWR_LVL); - plat_setup_psci_ops(&psci_plat_pm_ops); + plat_setup_psci_ops((uintptr_t)psci_entrypoint, + &psci_plat_pm_ops); assert(psci_plat_pm_ops); /* Initialize the psci capability */ diff --git a/services/std_svc/psci1.0/psci_suspend.c b/services/std_svc/psci1.0/psci_suspend.c index 2e4270f0ad..71e477842e 100644 --- a/services/std_svc/psci1.0/psci_suspend.c +++ b/services/std_svc/psci1.0/psci_suspend.c @@ -133,7 +133,6 @@ void psci_cpu_suspend_start(entry_point_info_t *ep, { int skip_wfi = 0; unsigned int idx = plat_my_core_pos(); - unsigned long psci_entrypoint; /* * This function must only be called on platforms where the @@ -167,22 +166,16 @@ void psci_cpu_suspend_start(entry_point_info_t *ep, */ psci_do_state_coordination(end_pwrlvl, state_info); - psci_entrypoint = 0; - if (is_power_down_state) { + if (is_power_down_state) psci_suspend_to_pwrdown_start(end_pwrlvl, ep, state_info); - /* Set the secure world (EL3) re-entry point after BL1. */ - psci_entrypoint = - (unsigned long) psci_cpu_suspend_finish_entry; - } - /* * Plat. management: Allow the platform to perform the * necessary actions to turn off this cpu e.g. set the * platform defined mailbox with the psci entrypoint, * program the power controller etc. */ - psci_plat_pm_ops->pwr_domain_suspend(psci_entrypoint, state_info); + psci_plat_pm_ops->pwr_domain_suspend(state_info); exit: /* From 32bc85f2d5234fbc4d1f724db3d033b8e1be5dd3 Mon Sep 17 00:00:00 2001 From: Soby Mathew Date: Wed, 10 Jun 2015 13:49:59 +0100 Subject: [PATCH 08/20] 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 --- Makefile | 18 ++ include/bl31/services/psci1.0/psci.h | 5 +- include/bl31/services/psci1.0/psci_compat.h | 112 +++++++ include/plat/common/psci1.0/platform.h | 26 +- plat/common/aarch64/platform_mp_stack.S | 48 ++- plat/compat/aarch64/plat_helpers_compat.S | 71 +++++ plat/compat/plat_compat.mk | 41 +++ plat/compat/plat_pm_compat.c | 337 ++++++++++++++++++++ plat/compat/plat_topology_compat.c | 218 +++++++++++++ services/std_svc/psci1.0/psci_common.c | 78 +++++ 10 files changed, 948 insertions(+), 6 deletions(-) create mode 100644 include/bl31/services/psci1.0/psci_compat.h create mode 100644 plat/compat/aarch64/plat_helpers_compat.S create mode 100644 plat/compat/plat_compat.mk create mode 100644 plat/compat/plat_pm_compat.c create mode 100644 plat/compat/plat_topology_compat.c 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 #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 @@ 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 +#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 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 #include +#include +#include + /******************************************************************************* * 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,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 @@ 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 +#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 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 +#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 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 +#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 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 From 674878464a93bb6d6bf6cb746fc4b9dba6e101ac Mon Sep 17 00:00:00 2001 From: Soby Mathew Date: Mon, 13 Jul 2015 14:10:57 +0100 Subject: [PATCH 09/20] PSCI: Switch to the new PSCI frameworks This commit does the switch to the new PSCI framework implementation replacing the existing files in PSCI folder with the ones in PSCI1.0 folder. The corresponding makefiles are modified as required for the new implementation. The platform.h header file is also is switched to the new one as required by the new frameworks. The build flag ENABLE_PLAT_COMPAT defaults to 1 to enable compatibility layer which let the existing platform ports to continue to build and run with minimal changes. The default weak implementation of platform_get_core_pos() is now removed from platform_helpers.S and is provided by the compatibility layer. Note: The Secure Payloads and their dispatchers still use the old platform and framework APIs and hence it is expected that the ENABLE_PLAT_COMPAT build flag will remain enabled in subsequent patch. The compatibility for SPDs using the older APIs on platforms migrated to the new APIs will be added in the following patch. Change-Id: I18c51b3a085b564aa05fdd98d11c9f3335712719 --- Makefile | 4 - bl31/bl31.mk | 8 +- include/bl31/services/psci.h | 199 ++-- include/bl31/services/psci1.0/psci.h | 324 ------ .../bl31/services/{psci1.0 => }/psci_compat.h | 0 include/common/asm_macros.S | 2 + include/plat/common/platform.h | 43 +- include/plat/common/psci1.0/platform.h | 233 ----- plat/common/aarch64/platform_helpers.S | 26 - plat/common/aarch64/platform_mp_stack.S | 5 +- services/std_svc/psci/psci_afflvl_off.c | 248 ----- services/std_svc/psci/psci_afflvl_on.c | 405 -------- services/std_svc/psci/psci_afflvl_suspend.c | 469 --------- services/std_svc/psci/psci_common.c | 963 +++++++++++------- services/std_svc/psci/psci_entry.S | 36 +- services/std_svc/psci/psci_helpers.S | 38 +- services/std_svc/psci/psci_main.c | 216 ++-- services/std_svc/{psci1.0 => psci}/psci_off.c | 0 services/std_svc/{psci1.0 => psci}/psci_on.c | 0 services/std_svc/psci/psci_private.h | 202 ++-- services/std_svc/psci/psci_setup.c | 432 +++----- .../std_svc/{psci1.0 => psci}/psci_suspend.c | 0 services/std_svc/psci/psci_system_off.c | 6 +- services/std_svc/psci1.0/psci_common.c | 877 ---------------- services/std_svc/psci1.0/psci_entry.S | 112 -- services/std_svc/psci1.0/psci_helpers.S | 154 --- services/std_svc/psci1.0/psci_main.c | 447 -------- services/std_svc/psci1.0/psci_private.h | 240 ----- services/std_svc/psci1.0/psci_setup.c | 272 ----- services/std_svc/psci1.0/psci_system_off.c | 70 -- 30 files changed, 1180 insertions(+), 4851 deletions(-) delete mode 100644 include/bl31/services/psci1.0/psci.h rename include/bl31/services/{psci1.0 => }/psci_compat.h (100%) delete mode 100644 include/plat/common/psci1.0/platform.h delete mode 100644 services/std_svc/psci/psci_afflvl_off.c delete mode 100644 services/std_svc/psci/psci_afflvl_on.c delete mode 100644 services/std_svc/psci/psci_afflvl_suspend.c rename services/std_svc/{psci1.0 => psci}/psci_off.c (100%) rename services/std_svc/{psci1.0 => psci}/psci_on.c (100%) rename services/std_svc/{psci1.0 => psci}/psci_suspend.c (100%) delete mode 100644 services/std_svc/psci1.0/psci_common.c delete mode 100644 services/std_svc/psci1.0/psci_entry.S delete mode 100644 services/std_svc/psci1.0/psci_helpers.S delete mode 100644 services/std_svc/psci1.0/psci_main.c delete mode 100644 services/std_svc/psci1.0/psci_private.h delete mode 100644 services/std_svc/psci1.0/psci_setup.c delete mode 100644 services/std_svc/psci1.0/psci_system_off.c diff --git a/Makefile b/Makefile index c1e96177a5..2120cb3fcb 100644 --- a/Makefile +++ b/Makefile @@ -169,10 +169,6 @@ 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 diff --git a/bl31/bl31.mk b/bl31/bl31.mk index 4c25a60a3a..04e154200f 100644 --- a/bl31/bl31.mk +++ b/bl31/bl31.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2013-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: @@ -42,9 +42,9 @@ BL31_SOURCES += bl31/bl31_main.c \ lib/cpus/aarch64/cpu_helpers.S \ lib/locks/exclusive/spinlock.S \ services/std_svc/std_svc_setup.c \ - services/std_svc/psci/psci_afflvl_off.c \ - services/std_svc/psci/psci_afflvl_on.c \ - services/std_svc/psci/psci_afflvl_suspend.c \ + services/std_svc/psci/psci_off.c \ + services/std_svc/psci/psci_on.c \ + services/std_svc/psci/psci_suspend.c \ services/std_svc/psci/psci_common.c \ services/std_svc/psci/psci_entry.S \ services/std_svc/psci/psci_helpers.S \ diff --git a/include/bl31/services/psci.h b/include/bl31/services/psci.h index dd1891c6fe..c9b3f8d2c7 100644 --- a/include/bl31/services/psci.h +++ b/include/bl31/services/psci.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-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: @@ -32,17 +32,33 @@ #define __PSCI_H__ #include -#include /* for PLATFORM_NUM_AFFS */ +#include /* for PLAT_NUM_PWR_DOMAINS */ +#if ENABLE_PLAT_COMPAT +#include +#endif /******************************************************************************* - * Number of affinity instances whose state this psci imp. can track + * Number of power domains whose state this psci imp. can track ******************************************************************************/ -#ifdef PLATFORM_NUM_AFFS -#define PSCI_NUM_AFFS PLATFORM_NUM_AFFS +#ifdef PLAT_NUM_PWR_DOMAINS +#define PSCI_NUM_PWR_DOMAINS PLAT_NUM_PWR_DOMAINS #else -#define PSCI_NUM_AFFS (2 * PLATFORM_CORE_COUNT) +#define PSCI_NUM_PWR_DOMAINS (2 * PLATFORM_CORE_COUNT) #endif +#define PSCI_NUM_NON_CPU_PWR_DOMAINS (PSCI_NUM_PWR_DOMAINS - \ + PLATFORM_CORE_COUNT) + +/* This is the power level corresponding to a CPU */ +#define PSCI_CPU_PWR_LVL 0 + +/* + * The maximum power level supported by PSCI. Since PSCI CPU_SUSPEND + * uses the old power_state parameter format which has 2 bits to specify the + * power level, this constant is defined to be 3. + */ +#define PSCI_MAX_PWR_LVL 3 + /******************************************************************************* * Defines for runtime services func ids ******************************************************************************/ @@ -84,27 +100,35 @@ * PSCI CPU_SUSPEND 'power_state' parameter specific defines ******************************************************************************/ #define PSTATE_ID_SHIFT 0 -#define PSTATE_TYPE_SHIFT 16 -#define PSTATE_AFF_LVL_SHIFT 24 +#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_AFF_LVL_MASK 0x3 -#define PSTATE_VALID_MASK 0xFCFE0000 +#define PSTATE_PWR_LVL_MASK 0x3 + +#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_afflvl(pstate) (((pstate) >> PSTATE_AFF_LVL_SHIFT) & \ - PSTATE_AFF_LVL_MASK) -#define psci_make_powerstate(state_id, type, afflvl) \ - (((state_id) & PSTATE_ID_MASK) << PSTATE_ID_SHIFT) |\ - (((type) & PSTATE_TYPE_MASK) << PSTATE_TYPE_SHIFT) |\ - (((afflvl) & PSTATE_AFF_LVL_MASK) << PSTATE_AFF_LVL_SHIFT) +#define psci_check_power_state(pstate) ((pstate) & PSTATE_VALID_MASK) /******************************************************************************* * PSCI CPU_FEATURES feature flag specific defines @@ -113,6 +137,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 @@ -137,32 +166,72 @@ #define PSCI_E_NOT_PRESENT -7 #define PSCI_E_DISABLED -8 -/******************************************************************************* - * PSCI affinity state related constants. An affinity instance could be present - * or absent physically to cater for asymmetric topologies. If present then it - * could in one of the 4 further defined states. - ******************************************************************************/ -#define PSCI_STATE_SHIFT 1 -#define PSCI_STATE_MASK 0xff - -#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 - -#define PSCI_INVALID_DATA -1 - -#define get_phys_state(x) (x != PSCI_STATE_ON ? \ - PSCI_STATE_OFF : PSCI_STATE_ON) - -#define psci_validate_power_state(pstate) (pstate & PSTATE_VALID_MASK) - +#define PSCI_INVALID_MPIDR ~(0ULL) #ifndef __ASSEMBLY__ #include +#include + +/* + * 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_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 + +/* + * Type for representing the local power state at a particular level. + */ +typedef uint8_t plat_local_state_t; + +/* 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) + +/* + * 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)) + +/* + * 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. @@ -170,11 +239,17 @@ * this information will not reside on a cache line shared with another cpu. ******************************************************************************/ typedef struct psci_cpu_data { - uint32_t power_state; - uint32_t max_phys_off_afflvl; /* Highest affinity level in physically - powered off state */ + /* 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_AFFS]; + bakery_info_t pcpu_bakery_info[PSCI_NUM_NON_CPU_PWR_DOMAINS]; #endif } psci_cpu_data_t; @@ -182,25 +257,22 @@ typedef struct psci_cpu_data { * 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); +typedef struct plat_psci_ops { + void (*cpu_standby)(plat_local_state_t cpu_state); + int (*pwr_domain_on)(u_register_t mpidr); + void (*pwr_domain_off)(const psci_power_state_t *target_state); + void (*pwr_domain_suspend)(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 @@ -232,13 +304,8 @@ int psci_cpu_on(unsigned long, unsigned long, unsigned long); void __dead2 psci_power_down_wfi(void); -void psci_aff_on_finish_entry(void); -void psci_aff_suspend_finish_entry(void); +void psci_entrypoint(void); void psci_register_spd_pm_hook(const spd_pm_ops_t *); -int psci_get_suspend_stateid_by_mpidr(unsigned long); -int psci_get_suspend_stateid(void); -int psci_get_suspend_afflvl(void); -uint32_t psci_get_max_phys_off_afflvl(void); uint64_t psci_smc_handler(uint32_t smc_fid, uint64_t x1, @@ -252,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.h b/include/bl31/services/psci1.0/psci.h deleted file mode 100644 index 9361187618..0000000000 --- a/include/bl31/services/psci1.0/psci.h +++ /dev/null @@ -1,324 +0,0 @@ -/* - * Copyright (c) 2013-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_H__ -#define __PSCI_H__ - -#include -#include /* for PLAT_NUM_PWR_DOMAINS */ -#if ENABLE_PLAT_COMPAT -#include -#endif - -/******************************************************************************* - * Number of power domains whose state this psci imp. can track - ******************************************************************************/ -#ifdef PLAT_NUM_PWR_DOMAINS -#define PSCI_NUM_PWR_DOMAINS PLAT_NUM_PWR_DOMAINS -#else -#define PSCI_NUM_PWR_DOMAINS (2 * PLATFORM_CORE_COUNT) -#endif - -#define PSCI_NUM_NON_CPU_PWR_DOMAINS (PSCI_NUM_PWR_DOMAINS - \ - PLATFORM_CORE_COUNT) - -/* This is the power level corresponding to a CPU */ -#define PSCI_CPU_PWR_LVL 0 - -/* - * The maximum power level supported by PSCI. Since PSCI CPU_SUSPEND - * uses the old power_state parameter format which has 2 bits to specify the - * power level, this constant is defined to be 3. - */ -#define PSCI_MAX_PWR_LVL 3 - -/******************************************************************************* - * Defines for runtime services func ids - ******************************************************************************/ -#define PSCI_VERSION 0x84000000 -#define PSCI_CPU_SUSPEND_AARCH32 0x84000001 -#define PSCI_CPU_SUSPEND_AARCH64 0xc4000001 -#define PSCI_CPU_OFF 0x84000002 -#define PSCI_CPU_ON_AARCH32 0x84000003 -#define PSCI_CPU_ON_AARCH64 0xc4000003 -#define PSCI_AFFINITY_INFO_AARCH32 0x84000004 -#define PSCI_AFFINITY_INFO_AARCH64 0xc4000004 -#define PSCI_MIG_AARCH32 0x84000005 -#define PSCI_MIG_AARCH64 0xc4000005 -#define PSCI_MIG_INFO_TYPE 0x84000006 -#define PSCI_MIG_INFO_UP_CPU_AARCH32 0x84000007 -#define PSCI_MIG_INFO_UP_CPU_AARCH64 0xc4000007 -#define PSCI_SYSTEM_OFF 0x84000008 -#define PSCI_SYSTEM_RESET 0x84000009 -#define PSCI_FEATURES 0x8400000A -#define PSCI_SYSTEM_SUSPEND_AARCH32 0x8400000E -#define PSCI_SYSTEM_SUSPEND_AARCH64 0xc400000E - -/* Macro to help build the psci capabilities bitfield */ -#define define_psci_cap(x) (1 << (x & 0x1f)) - -/* - * Number of PSCI calls (above) implemented - */ -#define PSCI_NUM_CALLS 18 - -/******************************************************************************* - * PSCI Migrate and friends - ******************************************************************************/ -#define PSCI_TOS_UP_MIG_CAP 0 -#define PSCI_TOS_NOT_UP_MIG_CAP 1 -#define PSCI_TOS_NOT_PRESENT_MP 2 - -/******************************************************************************* - * 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_PWR_LVL_MASK 0x3 - -#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_check_power_state(pstate) ((pstate) & PSTATE_VALID_MASK) - -/******************************************************************************* - * PSCI CPU_FEATURES feature flag specific defines - ******************************************************************************/ -/* Features flags for CPU SUSPEND power state parameter format. Bits [1:1] */ -#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 -#define FF_SUPPORTS_OS_INIT_MODE 1 - -/******************************************************************************* - * PSCI version - ******************************************************************************/ -#define PSCI_MAJOR_VER (1 << 16) -#define PSCI_MINOR_VER 0x0 - -/******************************************************************************* - * PSCI error codes - ******************************************************************************/ -#define PSCI_E_SUCCESS 0 -#define PSCI_E_NOT_SUPPORTED -1 -#define PSCI_E_INVALID_PARAMS -2 -#define PSCI_E_DENIED -3 -#define PSCI_E_ALREADY_ON -4 -#define PSCI_E_ON_PENDING -5 -#define PSCI_E_INTERN_FAIL -6 -#define PSCI_E_NOT_PRESENT -7 -#define PSCI_E_DISABLED -8 - -#define PSCI_INVALID_MPIDR ~(0ULL) - -#ifndef __ASSEMBLY__ - -#include -#include - -/* - * 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 - -/* - * Type for representing the local power state at a particular level. - */ -typedef uint8_t plat_local_state_t; - -/* 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) - -/* - * 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)) - -/* - * 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. - * It is populated in the per-cpu data array. In return we get a guarantee that - * this information will not reside on a cache line shared with another cpu. - ******************************************************************************/ -typedef struct psci_cpu_data { - /* 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 -} psci_cpu_data_t; - -/******************************************************************************* - * Structure populated by platform specific code to export routines which - * perform common low level pm functions - ******************************************************************************/ -typedef struct plat_psci_ops { - void (*cpu_standby)(plat_local_state_t cpu_state); - int (*pwr_domain_on)(u_register_t mpidr); - void (*pwr_domain_off)(const psci_power_state_t *target_state); - void (*pwr_domain_suspend)(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, - psci_power_state_t *req_state); - int (*validate_ns_entrypoint)(unsigned long ns_entrypoint); - 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 - * chance to perform any bookkeeping before PSCI executes a power mgmt. - * operation. It also allows PSCI to determine certain properties of the SP e.g. - * migrate capability etc. - ******************************************************************************/ -typedef struct spd_pm_ops { - void (*svc_on)(uint64_t target_cpu); - int32_t (*svc_off)(uint64_t __unused); - void (*svc_suspend)(uint64_t __unused); - void (*svc_on_finish)(uint64_t __unused); - void (*svc_suspend_finish)(uint64_t suspend_level); - int32_t (*svc_migrate)(uint64_t from_cpu, uint64_t to_cpu); - int32_t (*svc_migrate_info)(uint64_t *resident_cpu); - void (*svc_system_off)(void); - void (*svc_system_reset)(void); -} spd_pm_ops_t; - -/******************************************************************************* - * Function & Data prototypes - ******************************************************************************/ -unsigned int psci_version(void); -int psci_affinity_info(unsigned long, unsigned int); -int psci_migrate(unsigned long); -int psci_migrate_info_type(void); -long psci_migrate_info_up_cpu(void); -int psci_cpu_on(unsigned long, - unsigned long, - unsigned long); -void __dead2 psci_power_down_wfi(void); -void psci_entrypoint(void); -void psci_register_spd_pm_hook(const spd_pm_ops_t *); - -uint64_t psci_smc_handler(uint32_t smc_fid, - uint64_t x1, - uint64_t x2, - uint64_t x3, - uint64_t x4, - void *cookie, - void *handle, - uint64_t flags); - -/* 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/psci_compat.h similarity index 100% rename from include/bl31/services/psci1.0/psci_compat.h rename to include/bl31/services/psci_compat.h diff --git a/include/common/asm_macros.S b/include/common/asm_macros.S index 902127ec00..f959eb4f9e 100644 --- a/include/common/asm_macros.S +++ b/include/common/asm_macros.S @@ -115,6 +115,7 @@ .space ((\_count) * (\_size)), 0 .endm +#if ENABLE_PLAT_COMPAT /* * This macro calculates the base address of an MP stack using the * platform_get_core_pos() index, the name of the stack storage and @@ -129,6 +130,7 @@ mov x1, #\_size madd x0, x0, x1, x2 .endm +#endif /* * This macro calculates the base address of the current CPU's MP stack diff --git a/include/plat/common/platform.h b/include/plat/common/platform.h index 469d46b678..f054cd0a20 100644 --- a/include/plat/common/platform.h +++ b/include/plat/common/platform.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-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: @@ -31,13 +31,14 @@ #ifndef __PLATFORM_H__ #define __PLATFORM_H__ +#include #include +#include /******************************************************************************* * Forward declarations ******************************************************************************/ -struct plat_pm_ops; struct meminfo; struct image_info; struct entry_point_info; @@ -59,6 +60,8 @@ int plat_get_image_source(unsigned int image_id, uintptr_t *dev_handle, 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(u_register_t mpidr); /******************************************************************************* * Mandatory interrupt management functions @@ -74,8 +77,7 @@ uint32_t plat_interrupt_type_to_line(uint32_t type, /******************************************************************************* * Optional common functions (may be overridden) ******************************************************************************/ -unsigned int platform_get_core_pos(unsigned long mpidr); -unsigned long platform_get_stack(unsigned long mpidr); +unsigned long plat_get_my_stack(void); void plat_report_exception(unsigned long); int plat_crash_console_init(void); int plat_crash_console_putc(int c); @@ -181,9 +183,16 @@ struct entry_point_info *bl31_plat_get_next_image_ep_info(uint32_t type); /******************************************************************************* * Mandatory PSCI functions (BL3-1) ******************************************************************************/ -int platform_setup_pm(const struct plat_pm_ops **); -unsigned int plat_get_aff_count(unsigned int, unsigned long); -unsigned int plat_get_aff_state(unsigned int, unsigned long); +int plat_setup_psci_ops(uintptr_t sec_entrypoint, + 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) @@ -201,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/include/plat/common/psci1.0/platform.h b/include/plat/common/psci1.0/platform.h deleted file mode 100644 index f054cd0a20..0000000000 --- a/include/plat/common/psci1.0/platform.h +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright (c) 2013-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 __PLATFORM_H__ -#define __PLATFORM_H__ - -#include -#include -#include - - -/******************************************************************************* - * Forward declarations - ******************************************************************************/ -struct meminfo; -struct image_info; -struct entry_point_info; -struct bl31_params; - -/******************************************************************************* - * plat_get_rotpk_info() flags - ******************************************************************************/ -#define ROTPK_IS_HASH (1 << 0) - -/******************************************************************************* - * Function declarations - ******************************************************************************/ -/******************************************************************************* - * Mandatory common functions - ******************************************************************************/ -uint64_t plat_get_syscnt_freq(void); -int plat_get_image_source(unsigned int image_id, - uintptr_t *dev_handle, - 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(u_register_t mpidr); - -/******************************************************************************* - * Mandatory interrupt management functions - ******************************************************************************/ -uint32_t plat_ic_get_pending_interrupt_id(void); -uint32_t plat_ic_get_pending_interrupt_type(void); -uint32_t plat_ic_acknowledge_interrupt(void); -uint32_t plat_ic_get_interrupt_type(uint32_t id); -void plat_ic_end_of_interrupt(uint32_t id); -uint32_t plat_interrupt_type_to_line(uint32_t type, - uint32_t security_state); - -/******************************************************************************* - * Optional common functions (may be overridden) - ******************************************************************************/ -unsigned long plat_get_my_stack(void); -void plat_report_exception(unsigned long); -int plat_crash_console_init(void); -int plat_crash_console_putc(int c); - -/******************************************************************************* - * Mandatory BL1 functions - ******************************************************************************/ -void bl1_early_platform_setup(void); -void bl1_plat_arch_setup(void); -void bl1_platform_setup(void); -struct meminfo *bl1_plat_sec_mem_layout(void); - -/* - * This function allows the platform to change the entrypoint information for - * BL2, after BL1 has loaded BL2 into memory but before BL2 is executed. - */ -void bl1_plat_set_bl2_ep_info(struct image_info *image, - struct entry_point_info *ep); - -/******************************************************************************* - * Optional BL1 functions (may be overridden) - ******************************************************************************/ -void bl1_init_bl2_mem_layout(const struct meminfo *bl1_mem_layout, - struct meminfo *bl2_mem_layout); - -/******************************************************************************* - * Mandatory BL2 functions - ******************************************************************************/ -void bl2_early_platform_setup(struct meminfo *mem_layout); -void bl2_plat_arch_setup(void); -void bl2_platform_setup(void); -struct meminfo *bl2_plat_sec_mem_layout(void); - -/* - * This function returns a pointer to the shared memory that the platform has - * kept aside to pass trusted firmware related information that BL3-1 - * could need - */ -struct bl31_params *bl2_plat_get_bl31_params(void); - -/* - * This function returns a pointer to the shared memory that the platform - * has kept to point to entry point information of BL31 to BL2 - */ -struct entry_point_info *bl2_plat_get_bl31_ep_info(void); - -/* - * This function flushes to main memory all the params that are - * passed to BL3-1 - */ -void bl2_plat_flush_bl31_params(void); - -/* - * The next 2 functions allow the platform to change the entrypoint information - * for the mandatory 3rd level BL images, BL3-1 and BL3-3. This is done after - * BL2 has loaded those images into memory but before BL3-1 is executed. - */ -void bl2_plat_set_bl31_ep_info(struct image_info *image, - struct entry_point_info *ep); - -void bl2_plat_set_bl33_ep_info(struct image_info *image, - struct entry_point_info *ep); - -/* Gets the memory layout for BL3-3 */ -void bl2_plat_get_bl33_meminfo(struct meminfo *mem_info); - -/******************************************************************************* - * Conditionally mandatory BL2 functions: must be implemented if BL3-0 image - * is supported - ******************************************************************************/ -/* Gets the memory layout for BL3-0 */ -void bl2_plat_get_bl30_meminfo(struct meminfo *mem_info); - -/* - * This function is called after loading BL3-0 image and it is used to perform - * any platform-specific actions required to handle the SCP firmware. - */ -int bl2_plat_handle_bl30(struct image_info *bl30_image_info); - -/******************************************************************************* - * Conditionally mandatory BL2 functions: must be implemented if BL3-2 image - * is supported - ******************************************************************************/ -void bl2_plat_set_bl32_ep_info(struct image_info *image, - struct entry_point_info *ep); - -/* Gets the memory layout for BL3-2 */ -void bl2_plat_get_bl32_meminfo(struct meminfo *mem_info); - -/******************************************************************************* - * Optional BL2 functions (may be overridden) - ******************************************************************************/ - -/******************************************************************************* - * Mandatory BL3-1 functions - ******************************************************************************/ -void bl31_early_platform_setup(struct bl31_params *from_bl2, - void *plat_params_from_bl2); -void bl31_plat_arch_setup(void); -void bl31_platform_setup(void); -struct entry_point_info *bl31_plat_get_next_image_ep_info(uint32_t type); - -/******************************************************************************* - * Mandatory PSCI functions (BL3-1) - ******************************************************************************/ -int plat_setup_psci_ops(uintptr_t sec_entrypoint, - 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); - -/******************************************************************************* - * Optional BL3-2 functions (may be overridden) - ******************************************************************************/ -void bl32_plat_enable_mmu(uint32_t flags); - -/******************************************************************************* - * Trusted Board Boot functions - ******************************************************************************/ -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_helpers.S b/plat/common/aarch64/platform_helpers.S index c117449d27..b88603c370 100644 --- a/plat/common/aarch64/platform_helpers.S +++ b/plat/common/aarch64/platform_helpers.S @@ -32,38 +32,12 @@ #include #include - - .weak platform_get_core_pos - .weak platform_check_mpidr .weak plat_report_exception .weak plat_crash_console_init .weak plat_crash_console_putc .weak plat_reset_handler .weak plat_disable_acp - /* ----------------------------------------------------- - * 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 - - /* ----------------------------------------------------- - * Placeholder function which should be redefined by - * each platform. - * ----------------------------------------------------- - */ -func platform_check_mpidr - mov x0, xzr - ret -endfunc platform_check_mpidr - /* ----------------------------------------------------- * Placeholder function which should be redefined by * each platform. diff --git a/plat/common/aarch64/platform_mp_stack.S b/plat/common/aarch64/platform_mp_stack.S index b1f7b6dcd4..6cfa0697b4 100644 --- a/plat/common/aarch64/platform_mp_stack.S +++ b/plat/common/aarch64/platform_mp_stack.S @@ -37,9 +37,9 @@ #if ENABLE_PLAT_COMPAT .globl plat_get_my_stack .globl plat_set_my_stack -#else .weak platform_get_stack .weak platform_set_stack +#else .weak plat_get_my_stack .weak plat_set_my_stack #endif /*__ENABLE_PLAT_COMPAT__*/ @@ -80,7 +80,6 @@ func plat_set_my_stack b platform_set_stack endfunc plat_set_my_stack -#else /* ----------------------------------------------------- * unsigned long platform_get_stack (unsigned long mpidr) * @@ -108,6 +107,8 @@ func platform_set_stack ret x9 endfunc platform_set_stack +#else + /* ----------------------------------------------------- * unsigned long plat_get_my_stack () * diff --git a/services/std_svc/psci/psci_afflvl_off.c b/services/std_svc/psci/psci_afflvl_off.c deleted file mode 100644 index 7eb968899e..0000000000 --- a/services/std_svc/psci/psci_afflvl_off.c +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright (c) 2013-2014, 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 -#include "psci_private.h" - -typedef void (*afflvl_off_handler_t)(aff_map_node_t *node); - -/******************************************************************************* - * The next three functions implement a handler for each supported affinity - * level which is called when that affinity level is turned off. - ******************************************************************************/ -static void psci_afflvl0_off(aff_map_node_t *cpu_node) -{ - assert(cpu_node->level == MPIDR_AFFLVL0); - - /* - * Arch. management. Perform the necessary steps to flush all - * cpu caches. - */ - psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL0); - - /* - * Plat. management: Perform platform specific actions to turn this - * cpu off e.g. exit cpu coherency, program the power controller etc. - */ - psci_plat_pm_ops->affinst_off(cpu_node->level, - psci_get_phys_state(cpu_node)); -} - -static void psci_afflvl1_off(aff_map_node_t *cluster_node) -{ - /* Sanity check the cluster level */ - assert(cluster_node->level == MPIDR_AFFLVL1); - - /* - * Arch. Management. Flush all levels of caches to PoC if - * the cluster is to be shutdown. - */ - psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL1); - - /* - * Plat. Management. Allow the platform to do its cluster - * specific bookeeping e.g. turn off interconnect coherency, - * program the power controller etc. - */ - psci_plat_pm_ops->affinst_off(cluster_node->level, - psci_get_phys_state(cluster_node)); -} - -static void psci_afflvl2_off(aff_map_node_t *system_node) -{ - /* Cannot go beyond this level */ - assert(system_node->level == MPIDR_AFFLVL2); - - /* - * Keep the physical state of the system handy to decide what - * action needs to be taken - */ - - /* - * Arch. Management. Flush all levels of caches to PoC if - * the system is to be shutdown. - */ - psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL2); - - /* - * Plat. Management : Allow the platform to do its bookeeping - * at this affinity level - */ - psci_plat_pm_ops->affinst_off(system_node->level, - psci_get_phys_state(system_node)); -} - -static const afflvl_off_handler_t psci_afflvl_off_handlers[] = { - psci_afflvl0_off, - psci_afflvl1_off, - psci_afflvl2_off, -}; - -/******************************************************************************* - * This function takes an array of pointers to affinity instance nodes in the - * topology tree and calls the off handler for the corresponding affinity - * levels - ******************************************************************************/ -static void psci_call_off_handlers(aff_map_node_t *mpidr_nodes[], - int start_afflvl, - int end_afflvl) -{ - int level; - aff_map_node_t *node; - - for (level = start_afflvl; level <= end_afflvl; level++) { - node = mpidr_nodes[level]; - if (node == NULL) - continue; - - psci_afflvl_off_handlers[level](node); - } -} - -/******************************************************************************* - * Top level handler which is called when a cpu wants to power itself down. - * It's assumed that along with turning the cpu off, higher affinity levels will - * be turned off as far as possible. It traverses through all the affinity - * levels performing generic, architectural, platform setup and state management - * e.g. for a cluster that's to be powered off, it will call the platform - * specific code which will disable coherency at the interconnect level if the - * cpu is the last in the cluster. For a cpu it could mean programming the power - * the power controller etc. - * - * The state of all the relevant affinity levels is changed prior to calling the - * affinity level specific handlers as their actions would depend upon the state - * the affinity level is about to enter. - * - * The affinity level specific handlers are called in ascending order i.e. from - * the lowest to the highest affinity level implemented by the platform because - * to turn off affinity level X it is neccesary to turn off affinity level X - 1 - * first. - ******************************************************************************/ -int psci_afflvl_off(int start_afflvl, - int end_afflvl) -{ - int rc; - mpidr_aff_map_nodes_t mpidr_nodes; - unsigned int max_phys_off_afflvl; - - /* - * This function must only be called on platforms where the - * CPU_OFF platform hooks have been implemented. - */ - assert(psci_plat_pm_ops->affinst_off); - - /* - * Collect the pointers to the nodes in the topology tree for - * each affinity instance in the mpidr. If this function does - * not return successfully then either the mpidr or the affinity - * levels are incorrect. Either way, this an internal TF error - * therefore assert. - */ - rc = psci_get_aff_map_nodes(read_mpidr_el1() & MPIDR_AFFINITY_MASK, - start_afflvl, - end_afflvl, - mpidr_nodes); - assert(rc == PSCI_E_SUCCESS); - - /* - * This function acquires the lock corresponding to each affinity - * level so that by the time all locks are taken, the system topology - * is snapshot and state management can be done safely. - */ - psci_acquire_afflvl_locks(start_afflvl, - end_afflvl, - mpidr_nodes); - - - /* - * Call the cpu off handler registered by the Secure Payload Dispatcher - * to let it do any bookkeeping. Assume that the SPD always reports an - * E_DENIED error if SP refuse to power down - */ - if (psci_spd_pm && psci_spd_pm->svc_off) { - rc = psci_spd_pm->svc_off(0); - if (rc) - goto exit; - } - - /* - * This function updates the state of each affinity instance - * corresponding to the mpidr in the range of affinity levels - * specified. - */ - psci_do_afflvl_state_mgmt(start_afflvl, - end_afflvl, - mpidr_nodes, - PSCI_STATE_OFF); - - max_phys_off_afflvl = psci_find_max_phys_off_afflvl(start_afflvl, - end_afflvl, - mpidr_nodes); - assert(max_phys_off_afflvl != PSCI_INVALID_DATA); - - /* Stash the highest affinity level that will enter the OFF state. */ - psci_set_max_phys_off_afflvl(max_phys_off_afflvl); - - /* Perform generic, architecture and platform specific handling */ - psci_call_off_handlers(mpidr_nodes, - start_afflvl, - end_afflvl); - - /* - * Invalidate the entry for the highest affinity level stashed earlier. - * This ensures that any reads of this variable outside the power - * up/down sequences return PSCI_INVALID_DATA. - * - */ - psci_set_max_phys_off_afflvl(PSCI_INVALID_DATA); - -exit: - /* - * Release the locks corresponding to each affinity level in the - * reverse order to which they were acquired. - */ - psci_release_afflvl_locks(start_afflvl, - end_afflvl, - mpidr_nodes); - - /* - * Check if all actions needed to safely power down this cpu have - * successfully completed. Enter a wfi loop which will allow the - * power controller to physically power down this cpu. - */ - if (rc == PSCI_E_SUCCESS) - psci_power_down_wfi(); - - return rc; -} diff --git a/services/std_svc/psci/psci_afflvl_on.c b/services/std_svc/psci/psci_afflvl_on.c deleted file mode 100644 index 0dbd0e0608..0000000000 --- a/services/std_svc/psci/psci_afflvl_on.c +++ /dev/null @@ -1,405 +0,0 @@ -/* - * Copyright (c) 2013-2014, 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 -#include -#include -#include -#include -#include -#include "psci_private.h" - -typedef int (*afflvl_on_handler_t)(unsigned long target_cpu, - aff_map_node_t *node); - -/******************************************************************************* - * This function checks whether a cpu which has been requested to be turned on - * is OFF to begin with. - ******************************************************************************/ -static int cpu_on_validate_state(unsigned int psci_state) -{ - if (psci_state == PSCI_STATE_ON || psci_state == PSCI_STATE_SUSPEND) - return PSCI_E_ALREADY_ON; - - if (psci_state == PSCI_STATE_ON_PENDING) - return PSCI_E_ON_PENDING; - - assert(psci_state == PSCI_STATE_OFF); - return PSCI_E_SUCCESS; -} - -/******************************************************************************* - * Handler routine to turn a cpu on. It takes care of any generic, architectural - * or platform specific setup required. - * TODO: Split this code across separate handlers for each type of setup? - ******************************************************************************/ -static int psci_afflvl0_on(unsigned long target_cpu, - aff_map_node_t *cpu_node) -{ - unsigned long psci_entrypoint; - - /* Sanity check to safeguard against data corruption */ - assert(cpu_node->level == MPIDR_AFFLVL0); - - /* Set the secure world (EL3) re-entry point after BL1 */ - psci_entrypoint = (unsigned long) psci_aff_on_finish_entry; - - /* - * Plat. management: Give the platform the current state - * of the target cpu to allow it to perform the necessary - * steps to power on. - */ - return psci_plat_pm_ops->affinst_on(target_cpu, - psci_entrypoint, - cpu_node->level, - psci_get_phys_state(cpu_node)); -} - -/******************************************************************************* - * Handler routine to turn a cluster on. It takes care or any generic, arch. - * or platform specific setup required. - * TODO: Split this code across separate handlers for each type of setup? - ******************************************************************************/ -static int psci_afflvl1_on(unsigned long target_cpu, - aff_map_node_t *cluster_node) -{ - unsigned long psci_entrypoint; - - assert(cluster_node->level == MPIDR_AFFLVL1); - - /* - * There is no generic and arch. specific cluster - * management required - */ - - /* State management: Is not required while turning a cluster on */ - - /* - * Plat. management: Give the platform the current state - * of the target cpu to allow it to perform the necessary - * steps to power on. - */ - psci_entrypoint = (unsigned long) psci_aff_on_finish_entry; - return psci_plat_pm_ops->affinst_on(target_cpu, - psci_entrypoint, - cluster_node->level, - psci_get_phys_state(cluster_node)); -} - -/******************************************************************************* - * Handler routine to turn a cluster of clusters on. It takes care or any - * generic, arch. or platform specific setup required. - * TODO: Split this code across separate handlers for each type of setup? - ******************************************************************************/ -static int psci_afflvl2_on(unsigned long target_cpu, - aff_map_node_t *system_node) -{ - unsigned long psci_entrypoint; - - /* Cannot go beyond affinity level 2 in this psci imp. */ - assert(system_node->level == MPIDR_AFFLVL2); - - /* - * There is no generic and arch. specific system management - * required - */ - - /* State management: Is not required while turning a system on */ - - /* - * Plat. management: Give the platform the current state - * of the target cpu to allow it to perform the necessary - * steps to power on. - */ - psci_entrypoint = (unsigned long) psci_aff_on_finish_entry; - return psci_plat_pm_ops->affinst_on(target_cpu, - psci_entrypoint, - system_node->level, - psci_get_phys_state(system_node)); -} - -/* Private data structure to make this handlers accessible through indexing */ -static const afflvl_on_handler_t psci_afflvl_on_handlers[] = { - psci_afflvl0_on, - psci_afflvl1_on, - psci_afflvl2_on, -}; - -/******************************************************************************* - * This function takes an array of pointers to affinity instance nodes in the - * topology tree and calls the on handler for the corresponding affinity - * levels - ******************************************************************************/ -static int psci_call_on_handlers(aff_map_node_t *target_cpu_nodes[], - int start_afflvl, - int end_afflvl, - unsigned long target_cpu) -{ - int rc = PSCI_E_INVALID_PARAMS, level; - aff_map_node_t *node; - - for (level = end_afflvl; level >= start_afflvl; level--) { - node = target_cpu_nodes[level]; - if (node == NULL) - continue; - - /* - * TODO: In case of an error should there be a way - * of undoing what we might have setup at higher - * affinity levels. - */ - rc = psci_afflvl_on_handlers[level](target_cpu, - node); - if (rc != PSCI_E_SUCCESS) - break; - } - - return rc; -} - -/******************************************************************************* - * Generic handler which is called to physically power on a cpu identified by - * its mpidr. It traverses through all the affinity levels performing generic, - * architectural, platform setup and state management e.g. for a cpu that is - * to be powered on, it will ensure that enough information is stashed for it - * to resume execution in the non-secure security state. - * - * The state of all the relevant affinity levels is changed after calling the - * affinity level specific handlers as their actions would depend upon the state - * the affinity level is currently in. - * - * The affinity level specific handlers are called in descending order i.e. from - * the highest to the lowest affinity level implemented by the platform because - * to turn on affinity level X it is necessary to turn on affinity level X + 1 - * first. - ******************************************************************************/ -int psci_afflvl_on(unsigned long target_cpu, - entry_point_info_t *ep, - int start_afflvl, - int end_afflvl) -{ - int rc; - mpidr_aff_map_nodes_t target_cpu_nodes; - - /* - * This function must only be called on platforms where the - * CPU_ON platform hooks have been implemented. - */ - assert(psci_plat_pm_ops->affinst_on && - psci_plat_pm_ops->affinst_on_finish); - - /* - * Collect the pointers to the nodes in the topology tree for - * each affinity instance in the mpidr. If this function does - * not return successfully then either the mpidr or the affinity - * levels are incorrect. - */ - rc = psci_get_aff_map_nodes(target_cpu, - start_afflvl, - end_afflvl, - target_cpu_nodes); - assert(rc == PSCI_E_SUCCESS); - - /* - * This function acquires the lock corresponding to each affinity - * level so that by the time all locks are taken, the system topology - * is snapshot and state management can be done safely. - */ - psci_acquire_afflvl_locks(start_afflvl, - end_afflvl, - target_cpu_nodes); - - /* - * Generic management: Ensure that the cpu is off to be - * turned on. - */ - rc = cpu_on_validate_state(psci_get_state( - target_cpu_nodes[MPIDR_AFFLVL0])); - if (rc != PSCI_E_SUCCESS) - goto exit; - - /* - * Call the cpu on handler registered by the Secure Payload Dispatcher - * to let it do any bookeeping. If the handler encounters an error, it's - * expected to assert within - */ - if (psci_spd_pm && psci_spd_pm->svc_on) - psci_spd_pm->svc_on(target_cpu); - - /* - * This function updates the state of each affinity instance - * corresponding to the mpidr in the range of affinity levels - * specified. - */ - psci_do_afflvl_state_mgmt(start_afflvl, - end_afflvl, - target_cpu_nodes, - PSCI_STATE_ON_PENDING); - - /* Perform generic, architecture and platform specific handling. */ - rc = psci_call_on_handlers(target_cpu_nodes, - start_afflvl, - end_afflvl, - target_cpu); - - assert(rc == PSCI_E_SUCCESS || rc == PSCI_E_INTERN_FAIL); - - if (rc == PSCI_E_SUCCESS) - /* Store the re-entry information for the non-secure world. */ - cm_init_context(target_cpu, ep); - else - /* Restore the state on error. */ - psci_do_afflvl_state_mgmt(start_afflvl, - end_afflvl, - target_cpu_nodes, - PSCI_STATE_OFF); -exit: - /* - * This loop releases the lock corresponding to each affinity level - * in the reverse order to which they were acquired. - */ - psci_release_afflvl_locks(start_afflvl, - end_afflvl, - target_cpu_nodes); - - return rc; -} - -/******************************************************************************* - * The following functions finish an earlier affinity power on request. They - * are called by the common finisher routine in psci_common.c. - ******************************************************************************/ -static void psci_afflvl0_on_finish(aff_map_node_t *cpu_node) -{ - unsigned int plat_state, state; - - assert(cpu_node->level == MPIDR_AFFLVL0); - - /* Ensure we have been explicitly woken up by another cpu */ - state = psci_get_state(cpu_node); - assert(state == PSCI_STATE_ON_PENDING); - - /* - * Plat. management: Perform the platform specific actions - * for this cpu e.g. enabling the gic or zeroing the mailbox - * register. The actual state of this cpu has already been - * changed. - */ - - /* Get the physical state of this cpu */ - plat_state = get_phys_state(state); - psci_plat_pm_ops->affinst_on_finish(cpu_node->level, - plat_state); - - /* - * Arch. management: Enable data cache and manage stack memory - */ - psci_do_pwrup_cache_maintenance(); - - /* - * All the platform specific actions for turning this cpu - * on have completed. Perform enough arch.initialization - * to run in the non-secure address space. - */ - bl31_arch_setup(); - - /* - * Call the cpu on finish handler registered by the Secure Payload - * Dispatcher to let it do any bookeeping. If the handler encounters an - * error, it's expected to assert within - */ - if (psci_spd_pm && psci_spd_pm->svc_on_finish) - psci_spd_pm->svc_on_finish(0); - - /* - * Generic management: Now we just need to retrieve the - * information that we had stashed away during the cpu_on - * call to set this cpu on its way. - */ - cm_prepare_el3_exit(NON_SECURE); - - /* Clean caches before re-entering normal world */ - dcsw_op_louis(DCCSW); -} - -static void psci_afflvl1_on_finish(aff_map_node_t *cluster_node) -{ - unsigned int plat_state; - - assert(cluster_node->level == MPIDR_AFFLVL1); - - /* - * Plat. management: Perform the platform specific actions - * as per the old state of the cluster e.g. enabling - * coherency at the interconnect depends upon the state with - * which this cluster was powered up. If anything goes wrong - * then assert as there is no way to recover from this - * situation. - */ - plat_state = psci_get_phys_state(cluster_node); - psci_plat_pm_ops->affinst_on_finish(cluster_node->level, - plat_state); -} - - -static void psci_afflvl2_on_finish(aff_map_node_t *system_node) -{ - unsigned int plat_state; - - /* Cannot go beyond this affinity level */ - assert(system_node->level == MPIDR_AFFLVL2); - - /* - * Currently, there are no architectural actions to perform - * at the system level. - */ - - /* - * Plat. management: Perform the platform specific actions - * as per the old state of the cluster e.g. enabling - * coherency at the interconnect depends upon the state with - * which this cluster was powered up. If anything goes wrong - * then assert as there is no way to recover from this - * situation. - */ - plat_state = psci_get_phys_state(system_node); - psci_plat_pm_ops->affinst_on_finish(system_node->level, - plat_state); -} - -const afflvl_power_on_finisher_t psci_afflvl_on_finishers[] = { - psci_afflvl0_on_finish, - psci_afflvl1_on_finish, - psci_afflvl2_on_finish, -}; diff --git a/services/std_svc/psci/psci_afflvl_suspend.c b/services/std_svc/psci/psci_afflvl_suspend.c deleted file mode 100644 index 76e8c908a0..0000000000 --- a/services/std_svc/psci/psci_afflvl_suspend.c +++ /dev/null @@ -1,469 +0,0 @@ -/* - * Copyright (c) 2013-2014, 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 -#include -#include -#include -#include -#include -#include -#include "psci_private.h" - -typedef void (*afflvl_suspend_handler_t)(aff_map_node_t *node); - -/******************************************************************************* - * This function saves the power state parameter passed in the current PSCI - * cpu_suspend call in the per-cpu data array. - ******************************************************************************/ -void psci_set_suspend_power_state(unsigned int power_state) -{ - set_cpu_data(psci_svc_cpu_data.power_state, power_state); - flush_cpu_data(psci_svc_cpu_data.power_state); -} - -/******************************************************************************* - * This function gets the affinity level till which the current cpu could be - * powered down during a cpu_suspend call. Returns PSCI_INVALID_DATA if the - * power state is invalid. - ******************************************************************************/ -int psci_get_suspend_afflvl(void) -{ - unsigned int power_state; - - power_state = get_cpu_data(psci_svc_cpu_data.power_state); - - return ((power_state == PSCI_INVALID_DATA) ? - power_state : psci_get_pstate_afflvl(power_state)); -} - -/******************************************************************************* - * This function gets the state id of the current cpu from the power state - * parameter saved in the per-cpu data array. Returns PSCI_INVALID_DATA if the - * power state saved is invalid. - ******************************************************************************/ -int psci_get_suspend_stateid(void) -{ - unsigned int power_state; - - power_state = get_cpu_data(psci_svc_cpu_data.power_state); - - return ((power_state == PSCI_INVALID_DATA) ? - power_state : psci_get_pstate_id(power_state)); -} - -/******************************************************************************* - * This function gets the state id of the cpu specified by the 'mpidr' parameter - * from the power state parameter saved in the per-cpu data array. Returns - * PSCI_INVALID_DATA if the power state saved is invalid. - ******************************************************************************/ -int psci_get_suspend_stateid_by_mpidr(unsigned long mpidr) -{ - unsigned int power_state; - - power_state = get_cpu_data_by_mpidr(mpidr, - psci_svc_cpu_data.power_state); - - return ((power_state == PSCI_INVALID_DATA) ? - power_state : psci_get_pstate_id(power_state)); -} - -/******************************************************************************* - * The next three functions implement a handler for each supported affinity - * level which is called when that affinity level is about to be suspended. - ******************************************************************************/ -static void psci_afflvl0_suspend(aff_map_node_t *cpu_node) -{ - unsigned long psci_entrypoint; - - /* Sanity check to safeguard against data corruption */ - assert(cpu_node->level == MPIDR_AFFLVL0); - - /* Set the secure world (EL3) re-entry point after BL1 */ - psci_entrypoint = (unsigned long) psci_aff_suspend_finish_entry; - - /* - * Arch. management. Perform the necessary steps to flush all - * cpu caches. - */ - psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL0); - - /* - * Plat. management: Allow the platform to perform the - * necessary actions to turn off this cpu e.g. set the - * platform defined mailbox with the psci entrypoint, - * program the power controller etc. - */ - psci_plat_pm_ops->affinst_suspend(psci_entrypoint, - cpu_node->level, - psci_get_phys_state(cpu_node)); -} - -static void psci_afflvl1_suspend(aff_map_node_t *cluster_node) -{ - unsigned int plat_state; - unsigned long psci_entrypoint; - - /* Sanity check the cluster level */ - assert(cluster_node->level == MPIDR_AFFLVL1); - - /* - * Arch. management: Flush all levels of caches to PoC if the - * cluster is to be shutdown. - */ - psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL1); - - /* - * Plat. Management. Allow the platform to do its cluster specific - * bookeeping e.g. turn off interconnect coherency, program the power - * controller etc. Sending the psci entrypoint is currently redundant - * beyond affinity level 0 but one never knows what a platform might - * do. Also it allows us to keep the platform handler prototype the - * same. - */ - plat_state = psci_get_phys_state(cluster_node); - psci_entrypoint = (unsigned long) psci_aff_suspend_finish_entry; - psci_plat_pm_ops->affinst_suspend(psci_entrypoint, - cluster_node->level, - plat_state); -} - - -static void psci_afflvl2_suspend(aff_map_node_t *system_node) -{ - unsigned int plat_state; - unsigned long psci_entrypoint; - - /* Cannot go beyond this */ - assert(system_node->level == MPIDR_AFFLVL2); - - /* - * Keep the physical state of the system handy to decide what - * action needs to be taken - */ - plat_state = psci_get_phys_state(system_node); - - /* - * Arch. management: Flush all levels of caches to PoC if the - * system is to be shutdown. - */ - psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL2); - - /* - * Plat. Management : Allow the platform to do its bookeeping - * at this affinity level - */ - - /* - * Sending the psci entrypoint is currently redundant - * beyond affinity level 0 but one never knows what a - * platform might do. Also it allows us to keep the - * platform handler prototype the same. - */ - plat_state = psci_get_phys_state(system_node); - psci_entrypoint = (unsigned long) psci_aff_suspend_finish_entry; - psci_plat_pm_ops->affinst_suspend(psci_entrypoint, - system_node->level, - plat_state); -} - -static const afflvl_suspend_handler_t psci_afflvl_suspend_handlers[] = { - psci_afflvl0_suspend, - psci_afflvl1_suspend, - psci_afflvl2_suspend, -}; - -/******************************************************************************* - * This function takes an array of pointers to affinity instance nodes in the - * topology tree and calls the suspend handler for the corresponding affinity - * levels - ******************************************************************************/ -static void psci_call_suspend_handlers(aff_map_node_t *mpidr_nodes[], - int start_afflvl, - int end_afflvl) -{ - int level; - aff_map_node_t *node; - - for (level = start_afflvl; level <= end_afflvl; level++) { - node = mpidr_nodes[level]; - if (node == NULL) - continue; - - psci_afflvl_suspend_handlers[level](node); - } -} - -/******************************************************************************* - * Top level handler which is called when a cpu wants to suspend its execution. - * It is assumed that along with turning the cpu off, higher affinity levels - * until the target affinity level will be turned off as well. It traverses - * through all the affinity levels performing generic, architectural, platform - * setup and state management e.g. for a cluster that's to be suspended, it will - * call the platform specific code which will disable coherency at the - * interconnect level if the cpu is the last in the cluster. For a cpu it could - * mean programming the power controller etc. - * - * The state of all the relevant affinity levels is changed prior to calling the - * affinity level specific handlers as their actions would depend upon the state - * the affinity level is about to enter. - * - * The affinity level specific handlers are called in ascending order i.e. from - * the lowest to the highest affinity level implemented by the platform because - * to turn off affinity level X it is neccesary to turn off affinity level X - 1 - * first. - * - * All the required parameter checks are performed at the beginning and after - * the state transition has been done, no further error is expected and it - * is not possible to undo any of the actions taken beyond that point. - ******************************************************************************/ -void psci_afflvl_suspend(entry_point_info_t *ep, - int start_afflvl, - int end_afflvl) -{ - int skip_wfi = 0; - mpidr_aff_map_nodes_t mpidr_nodes; - unsigned int max_phys_off_afflvl; - - /* - * This function must only be called on platforms where the - * CPU_SUSPEND platform hooks have been implemented. - */ - assert(psci_plat_pm_ops->affinst_suspend && - psci_plat_pm_ops->affinst_suspend_finish); - - /* - * Collect the pointers to the nodes in the topology tree for - * each affinity instance in the mpidr. If this function does - * not return successfully then either the mpidr or the affinity - * levels are incorrect. Either way, this an internal TF error - * therefore assert. - */ - if (psci_get_aff_map_nodes(read_mpidr_el1() & MPIDR_AFFINITY_MASK, - start_afflvl, end_afflvl, mpidr_nodes) != PSCI_E_SUCCESS) - assert(0); - - /* - * This function acquires the lock corresponding to each affinity - * level so that by the time all locks are taken, the system topology - * is snapshot and state management can be done safely. - */ - psci_acquire_afflvl_locks(start_afflvl, - end_afflvl, - mpidr_nodes); - - /* - * We check if there are any pending interrupts after the delay - * introduced by lock contention to increase the chances of early - * detection that a wake-up interrupt has fired. - */ - if (read_isr_el1()) { - skip_wfi = 1; - goto exit; - } - - /* - * Call the cpu suspend handler registered by the Secure Payload - * Dispatcher to let it do any bookeeping. If the handler encounters an - * error, it's expected to assert within - */ - if (psci_spd_pm && psci_spd_pm->svc_suspend) - psci_spd_pm->svc_suspend(0); - - /* - * This function updates the state of each affinity instance - * corresponding to the mpidr in the range of affinity levels - * specified. - */ - psci_do_afflvl_state_mgmt(start_afflvl, - end_afflvl, - mpidr_nodes, - PSCI_STATE_SUSPEND); - - max_phys_off_afflvl = psci_find_max_phys_off_afflvl(start_afflvl, - end_afflvl, - mpidr_nodes); - assert(max_phys_off_afflvl != PSCI_INVALID_DATA); - - /* Stash the highest affinity level that will be turned off */ - psci_set_max_phys_off_afflvl(max_phys_off_afflvl); - - /* - * Store the re-entry information for the non-secure world. - */ - cm_init_context(read_mpidr_el1(), ep); - - /* Perform generic, architecture and platform specific handling */ - psci_call_suspend_handlers(mpidr_nodes, - start_afflvl, - end_afflvl); - - /* - * Invalidate the entry for the highest affinity level stashed earlier. - * This ensures that any reads of this variable outside the power - * up/down sequences return PSCI_INVALID_DATA. - */ - psci_set_max_phys_off_afflvl(PSCI_INVALID_DATA); - -exit: - /* - * Release the locks corresponding to each affinity level in the - * reverse order to which they were acquired. - */ - psci_release_afflvl_locks(start_afflvl, - end_afflvl, - mpidr_nodes); - if (!skip_wfi) - psci_power_down_wfi(); -} - -/******************************************************************************* - * The following functions finish an earlier affinity suspend request. They - * are called by the common finisher routine in psci_common.c. - ******************************************************************************/ -static void psci_afflvl0_suspend_finish(aff_map_node_t *cpu_node) -{ - unsigned int plat_state, state; - int32_t suspend_level; - uint64_t counter_freq; - - assert(cpu_node->level == MPIDR_AFFLVL0); - - /* Ensure we have been woken up from a suspended state */ - state = psci_get_state(cpu_node); - assert(state == PSCI_STATE_SUSPEND); - - /* - * Plat. management: Perform the platform specific actions - * before we change the state of the cpu e.g. enabling the - * gic or zeroing the mailbox register. If anything goes - * wrong then assert as there is no way to recover from this - * situation. - */ - - /* Get the physical state of this cpu */ - plat_state = get_phys_state(state); - psci_plat_pm_ops->affinst_suspend_finish(cpu_node->level, - plat_state); - - /* - * Arch. management: Enable the data cache, manage stack memory and - * restore the stashed EL3 architectural context from the 'cpu_context' - * structure for this cpu. - */ - psci_do_pwrup_cache_maintenance(); - - /* Re-init the cntfrq_el0 register */ - counter_freq = plat_get_syscnt_freq(); - write_cntfrq_el0(counter_freq); - - /* - * Call the cpu suspend finish handler registered by the Secure Payload - * Dispatcher to let it do any bookeeping. If the handler encounters an - * error, it's expected to assert within - */ - if (psci_spd_pm && psci_spd_pm->svc_suspend) { - suspend_level = psci_get_suspend_afflvl(); - assert (suspend_level != PSCI_INVALID_DATA); - psci_spd_pm->svc_suspend_finish(suspend_level); - } - - /* Invalidate the suspend context for the node */ - psci_set_suspend_power_state(PSCI_INVALID_DATA); - - /* - * Generic management: Now we just need to retrieve the - * information that we had stashed away during the suspend - * call to set this cpu on its way. - */ - cm_prepare_el3_exit(NON_SECURE); - - /* Clean caches before re-entering normal world */ - dcsw_op_louis(DCCSW); -} - -static void psci_afflvl1_suspend_finish(aff_map_node_t *cluster_node) -{ - unsigned int plat_state; - - assert(cluster_node->level == MPIDR_AFFLVL1); - - /* - * Plat. management: Perform the platform specific actions - * as per the old state of the cluster e.g. enabling - * coherency at the interconnect depends upon the state with - * which this cluster was powered up. If anything goes wrong - * then assert as there is no way to recover from this - * situation. - */ - - /* Get the physical state of this cpu */ - plat_state = psci_get_phys_state(cluster_node); - psci_plat_pm_ops->affinst_suspend_finish(cluster_node->level, - plat_state); -} - - -static void psci_afflvl2_suspend_finish(aff_map_node_t *system_node) -{ - unsigned int plat_state; - - /* Cannot go beyond this affinity level */ - assert(system_node->level == MPIDR_AFFLVL2); - - /* - * Currently, there are no architectural actions to perform - * at the system level. - */ - - /* - * Plat. management: Perform the platform specific actions - * as per the old state of the cluster e.g. enabling - * coherency at the interconnect depends upon the state with - * which this cluster was powered up. If anything goes wrong - * then assert as there is no way to recover from this - * situation. - */ - - /* Get the physical state of the system */ - plat_state = psci_get_phys_state(system_node); - psci_plat_pm_ops->affinst_suspend_finish(system_node->level, - plat_state); -} - -const afflvl_power_on_finisher_t psci_afflvl_suspend_finishers[] = { - psci_afflvl0_suspend_finish, - psci_afflvl1_suspend_finish, - psci_afflvl2_suspend_finish, -}; diff --git a/services/std_svc/psci/psci_common.c b/services/std_svc/psci/psci_common.c index 1b74ff2c5c..7f1a5fd0d8 100644 --- a/services/std_svc/psci/psci_common.c +++ b/services/std_svc/psci/psci_common.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-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: @@ -45,50 +45,120 @@ */ const spd_pm_ops_t *psci_spd_pm; +/* + * PSCI requested local power state map. This array is used to store the local + * power states requested by a CPU for power levels from level 1 to + * PLAT_MAX_PWR_LVL. It does not store the requested local power state for power + * level 0 (PSCI_CPU_PWR_LVL) as the requested and the target power state for a + * CPU are the same. + * + * During state coordination, the platform is passed an array containing the + * local states requested for a particular non cpu power domain by each cpu + * within the domain. + * + * TODO: Dense packing of the requested states will cause cache thrashing + * when multiple power domains write to it. If we allocate the requested + * states at each power level in a cache-line aligned per-domain memory, + * the cache thrashing can be avoided. + */ +static plat_local_state_t + psci_req_local_pwr_states[PLAT_MAX_PWR_LVL][PLATFORM_CORE_COUNT]; + + /******************************************************************************* - * Grand array that holds the platform's topology information for state - * management of affinity instances. Each node (aff_map_node) in the array - * corresponds to an affinity instance e.g. cluster, cpu within an mpidr + * Arrays that hold the platform's power domain tree information for state + * management of power domains. + * Each node in the array 'psci_non_cpu_pd_nodes' corresponds to a power domain + * which is an ancestor of a CPU power domain. + * Each node in the array 'psci_cpu_pd_nodes' corresponds to a cpu power domain ******************************************************************************/ -aff_map_node_t psci_aff_map[PSCI_NUM_AFFS] +non_cpu_pd_node_t psci_non_cpu_pd_nodes[PSCI_NUM_NON_CPU_PWR_DOMAINS] #if USE_COHERENT_MEM __attribute__ ((section("tzfw_coherent_mem"))) #endif ; +cpu_pd_node_t psci_cpu_pd_nodes[PLATFORM_CORE_COUNT]; + /******************************************************************************* * Pointer to functions exported by the platform to complete power mgmt. ops ******************************************************************************/ -const plat_pm_ops_t *psci_plat_pm_ops; +const plat_psci_ops_t *psci_plat_pm_ops; -/******************************************************************************* - * Check that the maximum affinity level supported by the platform makes sense - * ****************************************************************************/ -CASSERT(PLATFORM_MAX_AFFLVL <= MPIDR_MAX_AFFLVL && \ - PLATFORM_MAX_AFFLVL >= MPIDR_AFFLVL0, \ - assert_platform_max_afflvl_check); +/****************************************************************************** + * Check that the maximum power level supported by the platform makes sense + *****************************************************************************/ +CASSERT(PLAT_MAX_PWR_LVL <= PSCI_MAX_PWR_LVL && \ + PLAT_MAX_PWR_LVL >= PSCI_CPU_PWR_LVL, \ + assert_platform_max_pwrlvl_check); -/******************************************************************************* - * This function is passed an array of pointers to affinity level nodes in the - * topology tree for an mpidr. It iterates through the nodes to find the highest - * affinity level which is marked as physically powered off. - ******************************************************************************/ -uint32_t psci_find_max_phys_off_afflvl(uint32_t start_afflvl, - uint32_t end_afflvl, - aff_map_node_t *mpidr_nodes[]) +/* + * The plat_local_state used by the platform is one of these types: RUN, + * RETENTION and OFF. The platform can define further sub-states for each type + * apart from RUN. This categorization is done to verify the sanity of the + * psci_power_state passed by the platform and to print debug information. The + * categorization is done on the basis of the following conditions: + * + * 1. If (plat_local_state == 0) then the category is STATE_TYPE_RUN. + * + * 2. If (0 < plat_local_state <= PLAT_MAX_RET_STATE), then the category is + * STATE_TYPE_RETN. + * + * 3. If (plat_local_state > PLAT_MAX_RET_STATE), then the category is + * STATE_TYPE_OFF. + */ +typedef enum plat_local_state_type { + STATE_TYPE_RUN = 0, + STATE_TYPE_RETN, + STATE_TYPE_OFF +} plat_local_state_type_t; + +/* The macro used to categorize plat_local_state. */ +#define find_local_state_type(plat_local_state) \ + ((plat_local_state) ? ((plat_local_state > PLAT_MAX_RET_STATE) \ + ? STATE_TYPE_OFF : STATE_TYPE_RETN) \ + : STATE_TYPE_RUN) + +/****************************************************************************** + * Check that the maximum retention level supported by the platform is less + * than the maximum off level. + *****************************************************************************/ +CASSERT(PLAT_MAX_RET_STATE < PLAT_MAX_OFF_STATE, \ + assert_platform_max_off_and_retn_state_check); + +/****************************************************************************** + * This function ensures that the power state parameter in a CPU_SUSPEND request + * is valid. If so, it returns the requested states for each power level. + *****************************************************************************/ +int psci_validate_power_state(unsigned int power_state, + psci_power_state_t *state_info) { - uint32_t max_afflvl = PSCI_INVALID_DATA; + /* Check SBZ bits in power state are zero */ + if (psci_check_power_state(power_state)) + return PSCI_E_INVALID_PARAMS; - for (; start_afflvl <= end_afflvl; start_afflvl++) { - if (mpidr_nodes[start_afflvl] == NULL) - continue; + assert(psci_plat_pm_ops->validate_power_state); - if (psci_get_phys_state(mpidr_nodes[start_afflvl]) == - PSCI_STATE_OFF) - max_afflvl = start_afflvl; - } + /* Validate the power_state using platform pm_ops */ + return psci_plat_pm_ops->validate_power_state(power_state, state_info); +} - return max_afflvl; +/****************************************************************************** + * This function retrieves the `psci_power_state_t` for system suspend from + * the platform. + *****************************************************************************/ +void psci_query_sys_suspend_pwrstate(psci_power_state_t *state_info) +{ + /* + * Assert that the required pm_ops hook is implemented to ensure that + * the capability detected during psci_setup() is valid. + */ + assert(psci_plat_pm_ops->get_sys_suspend_power_state); + + /* + * Query the platform for the power_state required for system suspend + */ + psci_plat_pm_ops->get_sys_suspend_power_state(state_info); } /******************************************************************************* @@ -99,24 +169,15 @@ uint32_t psci_find_max_phys_off_afflvl(uint32_t start_afflvl, ******************************************************************************/ unsigned int psci_is_last_on_cpu(void) { - unsigned long mpidr = read_mpidr_el1() & MPIDR_AFFINITY_MASK; - unsigned int i; + unsigned int cpu_idx, my_idx = plat_my_core_pos(); - for (i = psci_aff_limits[MPIDR_AFFLVL0].min; - i <= psci_aff_limits[MPIDR_AFFLVL0].max; i++) { - - assert(psci_aff_map[i].level == MPIDR_AFFLVL0); - - if (!(psci_aff_map[i].state & PSCI_AFF_PRESENT)) - continue; - - if (psci_aff_map[i].mpidr == mpidr) { - assert(psci_get_state(&psci_aff_map[i]) - == PSCI_STATE_ON); + for (cpu_idx = 0; cpu_idx < PLATFORM_CORE_COUNT; cpu_idx++) { + if (cpu_idx == my_idx) { + assert(psci_get_aff_info_state() == AFF_STATE_ON); continue; } - if (psci_get_state(&psci_aff_map[i]) != PSCI_STATE_OFF) + if (psci_get_aff_info_state_by_idx(cpu_idx) != AFF_STATE_OFF) return 0; } @@ -124,193 +185,404 @@ unsigned int psci_is_last_on_cpu(void) } /******************************************************************************* - * This function saves the highest affinity level which is in OFF state. The - * affinity instance with which the level is associated is determined by the - * caller. - ******************************************************************************/ -void psci_set_max_phys_off_afflvl(uint32_t afflvl) -{ - set_cpu_data(psci_svc_cpu_data.max_phys_off_afflvl, afflvl); - - /* - * Ensure that the saved value is flushed to main memory and any - * speculatively pre-fetched stale copies are invalidated from the - * caches of other cpus in the same coherency domain. This ensures that - * the value can be safely read irrespective of the state of the data - * cache. - */ - flush_cpu_data(psci_svc_cpu_data.max_phys_off_afflvl); -} - -/******************************************************************************* - * This function reads the saved highest affinity level which is in OFF - * state. The affinity instance with which the level is associated is determined - * by the caller. - ******************************************************************************/ -uint32_t psci_get_max_phys_off_afflvl(void) -{ - /* - * Ensure that the last update of this value in this cpu's cache is - * flushed to main memory and any speculatively pre-fetched stale copies - * are invalidated from the caches of other cpus in the same coherency - * domain. This ensures that the value is always read from the main - * memory when it was written before the data cache was enabled. - */ - flush_cpu_data(psci_svc_cpu_data.max_phys_off_afflvl); - return get_cpu_data(psci_svc_cpu_data.max_phys_off_afflvl); -} - -/******************************************************************************* - * Routine to return the maximum affinity level to traverse to after a cpu has + * Routine to return the maximum power level to traverse to after a cpu has * been physically powered up. It is expected to be called immediately after * reset from assembler code. ******************************************************************************/ -int get_power_on_target_afflvl(void) +static int get_power_on_target_pwrlvl(void) { - int afflvl; - -#if DEBUG - unsigned int state; - aff_map_node_t *node; - - /* Retrieve our node from the topology tree */ - node = psci_get_aff_map_node(read_mpidr_el1() & MPIDR_AFFINITY_MASK, - MPIDR_AFFLVL0); - assert(node); + int pwrlvl; /* - * Sanity check the state of the cpu. It should be either suspend or "on - * pending" - */ - state = psci_get_state(node); - assert(state == PSCI_STATE_SUSPEND || state == PSCI_STATE_ON_PENDING); -#endif - - /* - * Assume that this cpu was suspended and retrieve its target affinity + * Assume that this cpu was suspended and retrieve its target power * level. If it is invalid then it could only have been turned off - * earlier. PLATFORM_MAX_AFFLVL will be the highest affinity level a + * earlier. PLAT_MAX_PWR_LVL will be the highest power level a * cpu can be turned off to. */ - afflvl = psci_get_suspend_afflvl(); - if (afflvl == PSCI_INVALID_DATA) - afflvl = PLATFORM_MAX_AFFLVL; - return afflvl; + pwrlvl = psci_get_suspend_pwrlvl(); + if (pwrlvl == PSCI_INVALID_DATA) + pwrlvl = PLAT_MAX_PWR_LVL; + return pwrlvl; } -/******************************************************************************* - * Simple routine to set the id of an affinity instance at a given level in the - * mpidr. - ******************************************************************************/ -unsigned long mpidr_set_aff_inst(unsigned long mpidr, - unsigned char aff_inst, - int aff_lvl) +/****************************************************************************** + * Helper function to update the requested local power state array. This array + * does not store the requested state for the CPU power level. Hence an + * assertion is added to prevent us from accessing the wrong index. + *****************************************************************************/ +static void psci_set_req_local_pwr_state(unsigned int pwrlvl, + unsigned int cpu_idx, + plat_local_state_t req_pwr_state) { - unsigned long aff_shift; + assert(pwrlvl > PSCI_CPU_PWR_LVL); + psci_req_local_pwr_states[pwrlvl - 1][cpu_idx] = req_pwr_state; +} - assert(aff_lvl <= MPIDR_AFFLVL3); +/****************************************************************************** + * This function initializes the psci_req_local_pwr_states. + *****************************************************************************/ +void psci_init_req_local_pwr_states(void) +{ + /* Initialize the requested state of all non CPU power domains as OFF */ + memset(&psci_req_local_pwr_states, PLAT_MAX_OFF_STATE, + sizeof(psci_req_local_pwr_states)); +} + +/****************************************************************************** + * Helper function to return a reference to an array containing the local power + * states requested by each cpu for a power domain at 'pwrlvl'. The size of the + * array will be the number of cpu power domains of which this power domain is + * an ancestor. These requested states will be used to determine a suitable + * target state for this power domain during psci state coordination. An + * assertion is added to prevent us from accessing the CPU power level. + *****************************************************************************/ +static plat_local_state_t *psci_get_req_local_pwr_states(int pwrlvl, + int cpu_idx) +{ + assert(pwrlvl > PSCI_CPU_PWR_LVL); + + return &psci_req_local_pwr_states[pwrlvl - 1][cpu_idx]; +} + +/****************************************************************************** + * Helper function to return the current local power state of each power domain + * from the current cpu power domain to its ancestor at the 'end_pwrlvl'. This + * function will be called after a cpu is powered on to find the local state + * each power domain has emerged from. + *****************************************************************************/ +static void psci_get_target_local_pwr_states(uint32_t end_pwrlvl, + psci_power_state_t *target_state) +{ + int lvl; + unsigned int parent_idx; + plat_local_state_t *pd_state = target_state->pwr_domain_state; + + pd_state[PSCI_CPU_PWR_LVL] = psci_get_cpu_local_state(); + parent_idx = psci_cpu_pd_nodes[plat_my_core_pos()].parent_node; + + /* Copy the local power state from node to state_info */ + for (lvl = PSCI_CPU_PWR_LVL + 1; lvl <= end_pwrlvl; lvl++) { +#if !USE_COHERENT_MEM + /* + * If using normal memory for psci_non_cpu_pd_nodes, we need + * to flush before reading the local power state as another + * cpu in the same power domain could have updated it and this + * code runs before caches are enabled. + */ + flush_dcache_range( + (uint64_t)&psci_non_cpu_pd_nodes[parent_idx], + sizeof(psci_non_cpu_pd_nodes[parent_idx])); +#endif + pd_state[lvl] = psci_non_cpu_pd_nodes[parent_idx].local_state; + parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node; + } + + /* Set the the higher levels to RUN */ + for (; lvl <= PLAT_MAX_PWR_LVL; lvl++) + target_state->pwr_domain_state[lvl] = PSCI_LOCAL_STATE_RUN; +} + +/****************************************************************************** + * Helper function to set the target local power state that each power domain + * from the current cpu power domain to its ancestor at the 'end_pwrlvl' will + * enter. This function will be called after coordination of requested power + * states has been done for each power level. + *****************************************************************************/ +static void psci_set_target_local_pwr_states(uint32_t end_pwrlvl, + const psci_power_state_t *target_state) +{ + int lvl; + unsigned int parent_idx; + const plat_local_state_t *pd_state = target_state->pwr_domain_state; + + psci_set_cpu_local_state(pd_state[PSCI_CPU_PWR_LVL]); /* - * Decide the number of bits to shift by depending upon - * the affinity level + * Need to flush as local_state will be accessed with Data Cache + * disabled during power on */ - aff_shift = get_afflvl_shift(aff_lvl); + flush_cpu_data(psci_svc_cpu_data.local_state); - /* Clear the existing affinity instance & set the new one*/ - mpidr &= ~(((unsigned long)MPIDR_AFFLVL_MASK) << aff_shift); - mpidr |= ((unsigned long)aff_inst) << aff_shift; + parent_idx = psci_cpu_pd_nodes[plat_my_core_pos()].parent_node; - return mpidr; + /* Copy the local_state from state_info */ + for (lvl = 1; lvl <= end_pwrlvl; lvl++) { + psci_non_cpu_pd_nodes[parent_idx].local_state = pd_state[lvl]; +#if !USE_COHERENT_MEM + flush_dcache_range( + (uint64_t)&psci_non_cpu_pd_nodes[parent_idx], + sizeof(psci_non_cpu_pd_nodes[parent_idx])); +#endif + parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node; + } } + /******************************************************************************* - * This function sanity checks a range of affinity levels. + * PSCI helper function to get the parent nodes corresponding to a cpu_index. ******************************************************************************/ -int psci_check_afflvl_range(int start_afflvl, int end_afflvl) +void psci_get_parent_pwr_domain_nodes(unsigned int cpu_idx, + int end_lvl, + unsigned int node_index[]) { - /* Sanity check the parameters passed */ - if (end_afflvl > PLATFORM_MAX_AFFLVL) + unsigned int parent_node = psci_cpu_pd_nodes[cpu_idx].parent_node; + int i; + + for (i = PSCI_CPU_PWR_LVL + 1; i <= end_lvl; i++) { + *node_index++ = parent_node; + parent_node = psci_non_cpu_pd_nodes[parent_node].parent_node; + } +} + +/****************************************************************************** + * This function is invoked post CPU power up and initialization. It sets the + * affinity info state, target power state and requested power state for the + * current CPU and all its ancestor power domains to RUN. + *****************************************************************************/ +void psci_set_pwr_domains_to_run(uint32_t end_pwrlvl) +{ + int lvl; + unsigned int parent_idx, cpu_idx = plat_my_core_pos(); + parent_idx = psci_cpu_pd_nodes[cpu_idx].parent_node; + + /* Reset the local_state to RUN for the non cpu power domains. */ + for (lvl = PSCI_CPU_PWR_LVL + 1; lvl <= end_pwrlvl; lvl++) { + psci_non_cpu_pd_nodes[parent_idx].local_state = + PSCI_LOCAL_STATE_RUN; +#if !USE_COHERENT_MEM + flush_dcache_range( + (uint64_t)&psci_non_cpu_pd_nodes[parent_idx], + sizeof(psci_non_cpu_pd_nodes[parent_idx])); +#endif + psci_set_req_local_pwr_state(lvl, + cpu_idx, + PSCI_LOCAL_STATE_RUN); + parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node; + } + + /* Set the affinity info state to ON */ + psci_set_aff_info_state(AFF_STATE_ON); + + psci_set_cpu_local_state(PSCI_LOCAL_STATE_RUN); + flush_cpu_data(psci_svc_cpu_data); +} + +/****************************************************************************** + * This function is passed the local power states requested for each power + * domain (state_info) between the current CPU domain and its ancestors until + * the target power level (end_pwrlvl). It updates the array of requested power + * states with this information. + * + * Then, for each level (apart from the CPU level) until the 'end_pwrlvl', it + * retrieves the states requested by all the cpus of which the power domain at + * that level is an ancestor. It passes this information to the platform to + * coordinate and return the target power state. If the target state for a level + * is RUN then subsequent levels are not considered. At the CPU level, state + * coordination is not required. Hence, the requested and the target states are + * the same. + * + * The 'state_info' is updated with the target state for each level between the + * CPU and the 'end_pwrlvl' and returned to the caller. + * + * This function will only be invoked with data cache enabled and while + * powering down a core. + *****************************************************************************/ +void psci_do_state_coordination(int end_pwrlvl, psci_power_state_t *state_info) +{ + unsigned int lvl, parent_idx, cpu_idx = plat_my_core_pos(); + unsigned int start_idx, ncpus; + plat_local_state_t target_state, *req_states; + + parent_idx = psci_cpu_pd_nodes[cpu_idx].parent_node; + + /* For level 0, the requested state will be equivalent + to target state */ + for (lvl = PSCI_CPU_PWR_LVL + 1; lvl <= end_pwrlvl; lvl++) { + + /* First update the requested power state */ + psci_set_req_local_pwr_state(lvl, cpu_idx, + state_info->pwr_domain_state[lvl]); + + /* Get the requested power states for this power level */ + start_idx = psci_non_cpu_pd_nodes[parent_idx].cpu_start_idx; + req_states = psci_get_req_local_pwr_states(lvl, start_idx); + + /* + * Let the platform coordinate amongst the requested states at + * this power level and return the target local power state. + */ + ncpus = psci_non_cpu_pd_nodes[parent_idx].ncpus; + target_state = plat_get_target_pwr_state(lvl, + req_states, + ncpus); + + state_info->pwr_domain_state[lvl] = target_state; + + /* Break early if the negotiated target power state is RUN */ + if (is_local_state_run(state_info->pwr_domain_state[lvl])) + break; + + parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node; + } + + /* + * This is for cases when we break out of the above loop early because + * the target power state is RUN at a power level < end_pwlvl. + * We update the requested power state from state_info and then + * set the target state as RUN. + */ + for (lvl = lvl + 1; lvl <= end_pwrlvl; lvl++) { + psci_set_req_local_pwr_state(lvl, cpu_idx, + state_info->pwr_domain_state[lvl]); + state_info->pwr_domain_state[lvl] = PSCI_LOCAL_STATE_RUN; + + } + + /* Update the target state in the power domain nodes */ + psci_set_target_local_pwr_states(end_pwrlvl, state_info); +} + +/****************************************************************************** + * This function validates a suspend request by making sure that if a standby + * state is requested then no power level is turned off and the highest power + * level is placed in a standby/retention state. + * + * It also ensures that the state level X will enter is not shallower than the + * state level X + 1 will enter. + * + * This validation will be enabled only for DEBUG builds as the platform is + * expected to perform these validations as well. + *****************************************************************************/ +int psci_validate_suspend_req(const psci_power_state_t *state_info, + unsigned int is_power_down_state) +{ + unsigned int max_off_lvl, target_lvl, max_retn_lvl; + plat_local_state_t state; + plat_local_state_type_t req_state_type, deepest_state_type; + int i; + + /* Find the target suspend power level */ + target_lvl = psci_find_target_suspend_lvl(state_info); + if (target_lvl == PSCI_INVALID_DATA) return PSCI_E_INVALID_PARAMS; - if (start_afflvl < MPIDR_AFFLVL0) - return PSCI_E_INVALID_PARAMS; + /* All power domain levels are in a RUN state to begin with */ + deepest_state_type = STATE_TYPE_RUN; - if (end_afflvl < start_afflvl) + for (i = target_lvl; i >= PSCI_CPU_PWR_LVL; i--) { + state = state_info->pwr_domain_state[i]; + req_state_type = find_local_state_type(state); + + /* + * While traversing from the highest power level to the lowest, + * the state requested for lower levels has to be the same or + * deeper i.e. equal to or greater than the state at the higher + * levels. If this condition is true, then the requested state + * becomes the deepest state encountered so far. + */ + if (req_state_type < deepest_state_type) + return PSCI_E_INVALID_PARAMS; + deepest_state_type = req_state_type; + } + + /* Find the highest off power level */ + max_off_lvl = psci_find_max_off_lvl(state_info); + + /* The target_lvl is either equal to the max_off_lvl or max_retn_lvl */ + max_retn_lvl = PSCI_INVALID_DATA; + if (target_lvl != max_off_lvl) + max_retn_lvl = target_lvl; + + /* + * If this is not a request for a power down state then max off level + * has to be invalid and max retention level has to be a valid power + * level. + */ + if (!is_power_down_state && (max_off_lvl != PSCI_INVALID_DATA || + max_retn_lvl == PSCI_INVALID_DATA)) return PSCI_E_INVALID_PARAMS; return PSCI_E_SUCCESS; } -/******************************************************************************* - * This function is passed an array of pointers to affinity level nodes in the - * topology tree for an mpidr and the state which each node should transition - * to. It updates the state of each node between the specified affinity levels. - ******************************************************************************/ -void psci_do_afflvl_state_mgmt(uint32_t start_afflvl, - uint32_t end_afflvl, - aff_map_node_t *mpidr_nodes[], - uint32_t state) +/****************************************************************************** + * This function finds the highest power level which will be powered down + * amongst all the power levels specified in the 'state_info' structure + *****************************************************************************/ +unsigned int psci_find_max_off_lvl(const psci_power_state_t *state_info) { - uint32_t level; + int i; - for (level = start_afflvl; level <= end_afflvl; level++) { - if (mpidr_nodes[level] == NULL) - continue; - psci_set_state(mpidr_nodes[level], state); + for (i = PLAT_MAX_PWR_LVL; i >= PSCI_CPU_PWR_LVL; i--) { + if (is_local_state_off(state_info->pwr_domain_state[i])) + return i; } + + return PSCI_INVALID_DATA; +} + +/****************************************************************************** + * This functions finds the level of the highest power domain which will be + * placed in a low power state during a suspend operation. + *****************************************************************************/ +unsigned int psci_find_target_suspend_lvl(const psci_power_state_t *state_info) +{ + int i; + + for (i = PLAT_MAX_PWR_LVL; i >= PSCI_CPU_PWR_LVL; i--) { + if (!is_local_state_run(state_info->pwr_domain_state[i])) + return i; + } + + return PSCI_INVALID_DATA; } /******************************************************************************* - * This function is passed an array of pointers to affinity level nodes in the - * topology tree for an mpidr. It picks up locks for each affinity level bottom - * up in the range specified. + * This function is passed a cpu_index and the highest level in the topology + * tree that the operation should be applied to. It picks up locks in order of + * increasing power domain level in the range specified. ******************************************************************************/ -void psci_acquire_afflvl_locks(int start_afflvl, - int end_afflvl, - aff_map_node_t *mpidr_nodes[]) +void psci_acquire_pwr_domain_locks(int end_pwrlvl, unsigned int cpu_idx) { + unsigned int parent_idx = psci_cpu_pd_nodes[cpu_idx].parent_node; int level; - for (level = start_afflvl; level <= end_afflvl; level++) { - if (mpidr_nodes[level] == NULL) - continue; - - psci_lock_get(mpidr_nodes[level]); + /* No locking required for level 0. Hence start locking from level 1 */ + for (level = PSCI_CPU_PWR_LVL + 1; level <= end_pwrlvl; level++) { + psci_lock_get(&psci_non_cpu_pd_nodes[parent_idx]); + parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node; } } /******************************************************************************* - * This function is passed an array of pointers to affinity level nodes in the - * topology tree for an mpidr. It releases the lock for each affinity level top - * down in the range specified. + * This function is passed a cpu_index and the highest level in the topology + * tree that the operation should be applied to. It releases the locks in order + * of decreasing power domain level in the range specified. ******************************************************************************/ -void psci_release_afflvl_locks(int start_afflvl, - int end_afflvl, - aff_map_node_t *mpidr_nodes[]) +void psci_release_pwr_domain_locks(int end_pwrlvl, unsigned int cpu_idx) { + unsigned int parent_idx, parent_nodes[PLAT_MAX_PWR_LVL] = {0}; int level; - for (level = end_afflvl; level >= start_afflvl; level--) { - if (mpidr_nodes[level] == NULL) - continue; + /* Get the parent nodes */ + psci_get_parent_pwr_domain_nodes(cpu_idx, end_pwrlvl, parent_nodes); - psci_lock_release(mpidr_nodes[level]); + /* Unlock top down. No unlocking required for level 0. */ + for (level = end_pwrlvl; level >= PSCI_CPU_PWR_LVL + 1; level--) { + parent_idx = parent_nodes[level - 1]; + psci_lock_release(&psci_non_cpu_pd_nodes[parent_idx]); } } /******************************************************************************* - * Simple routine to determine whether an affinity instance at a given level - * in an mpidr exists or not. + * Simple routine to determine whether a mpidr is valid or not. ******************************************************************************/ -int psci_validate_mpidr(unsigned long mpidr, int level) +int psci_validate_mpidr(unsigned long mpidr) { - aff_map_node_t *node; - - node = psci_get_aff_map_node(mpidr, level); - if (node && (node->state & PSCI_AFF_PRESENT)) - return PSCI_E_SUCCESS; - else + if (plat_core_pos_by_mpidr(mpidr) < 0) return PSCI_E_INVALID_PARAMS; + + return PSCI_E_SUCCESS; } /******************************************************************************* @@ -370,210 +642,75 @@ int psci_get_ns_ep_info(entry_point_info_t *ep, return PSCI_E_SUCCESS; } -/******************************************************************************* - * This function takes a pointer to an affinity node in the topology tree and - * returns its state. State of a non-leaf node needs to be calculated. - ******************************************************************************/ -unsigned short psci_get_state(aff_map_node_t *node) -{ -#if !USE_COHERENT_MEM - flush_dcache_range((uint64_t) node, sizeof(*node)); -#endif - - assert(node->level >= MPIDR_AFFLVL0 && node->level <= MPIDR_MAX_AFFLVL); - - /* A cpu node just contains the state which can be directly returned */ - if (node->level == MPIDR_AFFLVL0) - return (node->state >> PSCI_STATE_SHIFT) & PSCI_STATE_MASK; - - /* - * For an affinity level higher than a cpu, the state has to be - * calculated. It depends upon the value of the reference count - * which is managed by each node at the next lower affinity level - * e.g. for a cluster, each cpu increments/decrements the reference - * count. If the reference count is 0 then the affinity level is - * OFF else ON. - */ - if (node->ref_count) - return PSCI_STATE_ON; - else - return PSCI_STATE_OFF; -} - -/******************************************************************************* - * This function takes a pointer to an affinity node in the topology tree and - * a target state. State of a non-leaf node needs to be converted to a reference - * count. State of a leaf node can be set directly. - ******************************************************************************/ -void psci_set_state(aff_map_node_t *node, unsigned short state) -{ - assert(node->level >= MPIDR_AFFLVL0 && node->level <= MPIDR_MAX_AFFLVL); - - /* - * For an affinity level higher than a cpu, the state is used - * to decide whether the reference count is incremented or - * decremented. Entry into the ON_PENDING state does not have - * effect. - */ - if (node->level > MPIDR_AFFLVL0) { - switch (state) { - case PSCI_STATE_ON: - node->ref_count++; - break; - case PSCI_STATE_OFF: - case PSCI_STATE_SUSPEND: - node->ref_count--; - break; - case PSCI_STATE_ON_PENDING: - /* - * An affinity level higher than a cpu will not undergo - * a state change when it is about to be turned on - */ - return; - default: - assert(0); - } - } else { - node->state &= ~(PSCI_STATE_MASK << PSCI_STATE_SHIFT); - node->state |= (state & PSCI_STATE_MASK) << PSCI_STATE_SHIFT; - } - -#if !USE_COHERENT_MEM - flush_dcache_range((uint64_t) node, sizeof(*node)); -#endif -} - -/******************************************************************************* - * An affinity level could be on, on_pending, suspended or off. These are the - * logical states it can be in. Physically either it is off or on. When it is in - * the state on_pending then it is about to be turned on. It is not possible to - * tell whether that's actually happenned or not. So we err on the side of - * caution & treat the affinity level as being turned off. - ******************************************************************************/ -unsigned short psci_get_phys_state(aff_map_node_t *node) -{ - unsigned int state; - - state = psci_get_state(node); - return get_phys_state(state); -} - -/******************************************************************************* - * This function takes an array of pointers to affinity instance nodes in the - * topology tree and calls the physical power on handler for the corresponding - * affinity levels - ******************************************************************************/ -static void psci_call_power_on_handlers(aff_map_node_t *mpidr_nodes[], - int start_afflvl, - int end_afflvl, - afflvl_power_on_finisher_t *pon_handlers) -{ - int level; - aff_map_node_t *node; - - for (level = end_afflvl; level >= start_afflvl; level--) { - node = mpidr_nodes[level]; - if (node == NULL) - continue; - - /* - * If we run into any trouble while powering up an - * affinity instance, then there is no recovery path - * so simply return an error and let the caller take - * care of the situation. - */ - pon_handlers[level](node); - } -} - /******************************************************************************* * Generic handler which is called when a cpu is physically powered on. It - * traverses through all the affinity levels performing generic, architectural, - * platform setup and state management e.g. for a cluster that's been powered - * on, it will call the platform specific code which will enable coherency at - * the interconnect level. For a cpu it could mean turning on the MMU etc. - * - * The state of all the relevant affinity levels is changed after calling the - * affinity level specific handlers as their actions would depend upon the state - * the affinity level is exiting from. - * - * The affinity level specific handlers are called in descending order i.e. from - * the highest to the lowest affinity level implemented by the platform because - * to turn on affinity level X it is neccesary to turn on affinity level X + 1 - * first. + * traverses the node information and finds the highest power level powered + * off and performs generic, architectural, platform setup and state management + * to power on that power level and power levels below it. + * e.g. For a cpu that's been powered on, it will call the platform specific + * code to enable the gic cpu interface and for a cluster it will enable + * coherency at the interconnect level in addition to gic cpu interface. ******************************************************************************/ -void psci_afflvl_power_on_finish(int start_afflvl, - int end_afflvl, - afflvl_power_on_finisher_t *pon_handlers) +void psci_power_up_finish(void) { - mpidr_aff_map_nodes_t mpidr_nodes; - int rc; - unsigned int max_phys_off_afflvl; - + unsigned int cpu_idx = plat_my_core_pos(); + psci_power_state_t state_info = { {PSCI_LOCAL_STATE_RUN} }; + int end_pwrlvl; /* - * Collect the pointers to the nodes in the topology tree for - * each affinity instance in the mpidr. If this function does - * not return successfully then either the mpidr or the affinity - * levels are incorrect. Either case is an irrecoverable error. + * Verify that we have been explicitly turned ON or resumed from + * suspend. */ - rc = psci_get_aff_map_nodes(read_mpidr_el1() & MPIDR_AFFINITY_MASK, - start_afflvl, - end_afflvl, - mpidr_nodes); - if (rc != PSCI_E_SUCCESS) + if (psci_get_aff_info_state() == AFF_STATE_OFF) { + ERROR("Unexpected affinity info state"); panic(); + } /* - * This function acquires the lock corresponding to each affinity - * level so that by the time all locks are taken, the system topology - * is snapshot and state management can be done safely. + * Get the maximum power domain level to traverse to after this cpu + * has been physically powered up. */ - psci_acquire_afflvl_locks(start_afflvl, - end_afflvl, - mpidr_nodes); - - max_phys_off_afflvl = psci_find_max_phys_off_afflvl(start_afflvl, - end_afflvl, - mpidr_nodes); - assert(max_phys_off_afflvl != PSCI_INVALID_DATA); + end_pwrlvl = get_power_on_target_pwrlvl(); /* - * Stash the highest affinity level that will come out of the OFF or - * SUSPEND states. + * This function acquires the lock corresponding to each power level so + * that by the time all locks are taken, the system topology is snapshot + * and state management can be done safely. */ - psci_set_max_phys_off_afflvl(max_phys_off_afflvl); + psci_acquire_pwr_domain_locks(end_pwrlvl, + cpu_idx); - /* Perform generic, architecture and platform specific handling */ - psci_call_power_on_handlers(mpidr_nodes, - start_afflvl, - end_afflvl, - pon_handlers); + psci_get_target_local_pwr_states(end_pwrlvl, &state_info); /* - * This function updates the state of each affinity instance - * corresponding to the mpidr in the range of affinity levels - * specified. + * This CPU could be resuming from suspend or it could have just been + * turned on. To distinguish between these 2 cases, we examine the + * affinity state of the CPU: + * - If the affinity state is ON_PENDING then it has just been + * turned on. + * - Else it is resuming from suspend. + * + * Depending on the type of warm reset identified, choose the right set + * of power management handler and perform the generic, architecture + * and platform specific handling. */ - psci_do_afflvl_state_mgmt(start_afflvl, - end_afflvl, - mpidr_nodes, - PSCI_STATE_ON); + if (psci_get_aff_info_state() == AFF_STATE_ON_PENDING) + psci_cpu_on_finish(cpu_idx, &state_info); + else + psci_cpu_suspend_finish(cpu_idx, &state_info); /* - * Invalidate the entry for the highest affinity level stashed earlier. - * This ensures that any reads of this variable outside the power - * up/down sequences return PSCI_INVALID_DATA + * Set the requested and target state of this CPU and all the higher + * power domains which are ancestors of this CPU to run. */ - psci_set_max_phys_off_afflvl(PSCI_INVALID_DATA); + psci_set_pwr_domains_to_run(end_pwrlvl); /* - * This loop releases the lock corresponding to each affinity level + * This loop releases the lock corresponding to each power level * in the reverse order to which they were acquired. */ - psci_release_afflvl_locks(start_afflvl, - end_afflvl, - mpidr_nodes); + psci_release_pwr_domain_locks(end_pwrlvl, + cpu_idx); } /******************************************************************************* @@ -618,31 +755,123 @@ int psci_spd_migrate_info(uint64_t *mpidr) /******************************************************************************* - * This function prints the state of all affinity instances present in the + * This function prints the state of all power domains present in the * system ******************************************************************************/ -void psci_print_affinity_map(void) +void psci_print_power_domain_map(void) { #if LOG_LEVEL >= LOG_LEVEL_INFO - aff_map_node_t *node; unsigned int idx; + plat_local_state_t state; + plat_local_state_type_t state_type; + /* This array maps to the PSCI_STATE_X definitions in psci.h */ - static const char *psci_state_str[] = { + static const char *psci_state_type_str[] = { "ON", + "RETENTION", "OFF", - "ON_PENDING", - "SUSPEND" }; - INFO("PSCI Affinity Map:\n"); - for (idx = 0; idx < PSCI_NUM_AFFS ; idx++) { - node = &psci_aff_map[idx]; - if (!(node->state & PSCI_AFF_PRESENT)) { - continue; - } - INFO(" AffInst: Level %u, MPID 0x%lx, State %s\n", - node->level, node->mpidr, - psci_state_str[psci_get_state(node)]); + INFO("PSCI Power Domain Map:\n"); + for (idx = 0; idx < (PSCI_NUM_PWR_DOMAINS - PLATFORM_CORE_COUNT); + idx++) { + state_type = find_local_state_type( + psci_non_cpu_pd_nodes[idx].local_state); + INFO(" Domain Node : Level %u, parent_node %d," + " State %s (0x%x)\n", + psci_non_cpu_pd_nodes[idx].level, + psci_non_cpu_pd_nodes[idx].parent_node, + psci_state_type_str[state_type], + psci_non_cpu_pd_nodes[idx].local_state); + } + + for (idx = 0; idx < PLATFORM_CORE_COUNT; idx++) { + state = psci_get_cpu_local_state_by_idx(idx); + state_type = find_local_state_type(state); + INFO(" CPU Node : MPID 0x%lx, parent_node %d," + " State %s (0x%x)\n", + psci_cpu_pd_nodes[idx].mpidr, + psci_cpu_pd_nodes[idx].parent_node, + psci_state_type_str[state_type], + psci_get_cpu_local_state_by_idx(idx)); } #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 diff --git a/services/std_svc/psci/psci_entry.S b/services/std_svc/psci/psci_entry.S index 050f6c6c6d..73c33779aa 100644 --- a/services/std_svc/psci/psci_entry.S +++ b/services/std_svc/psci/psci_entry.S @@ -34,25 +34,16 @@ #include #include - .globl psci_aff_on_finish_entry - .globl psci_aff_suspend_finish_entry + .globl psci_entrypoint .globl psci_power_down_wfi - /* ----------------------------------------------------- - * This cpu has been physically powered up. Depending - * upon whether it was resumed from suspend or simply - * turned on, call the common power on finisher with - * the handlers (chosen depending upon original state). - * ----------------------------------------------------- + /* -------------------------------------------------------------------- + * This CPU has been physically powered up. It is either resuming from + * suspend or has simply been turned on. In both cases, call the power + * on finisher. + * -------------------------------------------------------------------- */ -func psci_aff_on_finish_entry - adr x23, psci_afflvl_on_finishers - b psci_aff_common_finish_entry - -psci_aff_suspend_finish_entry: - adr x23, psci_afflvl_suspend_finishers - -psci_aff_common_finish_entry: +func psci_entrypoint /* * On the warm boot path, most of the EL3 initialisations performed by * 'el3_entrypoint_common' must be skipped: @@ -98,19 +89,10 @@ psci_aff_common_finish_entry: mov x0, #DISABLE_DCACHE bl bl31_plat_enable_mmu - /* --------------------------------------------- - * Call the finishers starting from affinity - * level 0. - * --------------------------------------------- - */ - bl get_power_on_target_afflvl - mov x2, x23 - mov x1, x0 - mov x0, #MPIDR_AFFLVL0 - bl psci_afflvl_power_on_finish + bl psci_power_up_finish b el3_exit -endfunc psci_aff_on_finish_entry +endfunc psci_entrypoint /* -------------------------------------------- * This function is called to indicate to the diff --git a/services/std_svc/psci/psci_helpers.S b/services/std_svc/psci/psci_helpers.S index 1d99158e12..bbfa5d5d7b 100644 --- a/services/std_svc/psci/psci_helpers.S +++ b/services/std_svc/psci/psci_helpers.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2014-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: @@ -28,7 +28,6 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include #include #include #include @@ -38,14 +37,13 @@ .globl psci_do_pwrup_cache_maintenance /* ----------------------------------------------------------------------- - * void psci_do_pwrdown_cache_maintenance(uint32_t affinity level); + * void psci_do_pwrdown_cache_maintenance(uint32_t power level); * - * This function performs cache maintenance if the specified affinity - * level is the equal to the level of the highest affinity instance which - * will be/is physically powered off. The levels of cache affected are - * determined by the affinity level which is passed as the argument i.e. - * level 0 results in a flush of the L1 cache. Both the L1 and L2 caches - * are flushed for a higher affinity level. + * This function performs cache maintenance for the specified power + * level. The levels of cache affected are determined by the power + * level which is passed as the argument i.e. level 0 results + * in a flush of the L1 cache. Both the L1 and L2 caches are flushed + * for a higher power level. * * Additionally, this function also ensures that stack memory is correctly * flushed out to avoid coherency issues due to a change in its memory @@ -56,28 +54,19 @@ func psci_do_pwrdown_cache_maintenance stp x29, x30, [sp,#-16]! stp x19, x20, [sp,#-16]! - mov x19, x0 - bl psci_get_max_phys_off_afflvl -#if ASM_ASSERTION - cmp x0, #PSCI_INVALID_DATA - ASM_ASSERT(ne) -#endif - cmp x0, x19 - b.ne 1f - /* --------------------------------------------- * Determine to how many levels of cache will be - * subject to cache maintenance. Affinity level + * subject to cache maintenance. Power level * 0 implies that only the cpu is being powered * down. Only the L1 data cache needs to be * flushed to the PoU in this case. For a higher - * affinity level we are assuming that a flush + * power level we are assuming that a flush * of L1 data and L2 unified cache is enough. * This information should be provided by the * platform. * --------------------------------------------- */ - cmp x0, #MPIDR_AFFLVL0 + cmp x0, #PSCI_CPU_PWR_LVL b.eq do_core_pwr_dwn bl prepare_cluster_pwr_dwn b do_stack_maintenance @@ -92,8 +81,7 @@ do_core_pwr_dwn: * --------------------------------------------- */ do_stack_maintenance: - mrs x0, mpidr_el1 - bl platform_get_stack + bl plat_get_my_stack /* --------------------------------------------- * Calculate and store the size of the used @@ -116,7 +104,6 @@ do_stack_maintenance: sub x1, sp, x0 bl inv_dcache_range -1: ldp x19, x20, [sp], #16 ldp x29, x30, [sp], #16 ret @@ -147,8 +134,7 @@ func psci_do_pwrup_cache_maintenance * stack base address in x0. * --------------------------------------------- */ - mrs x0, mpidr_el1 - bl platform_get_stack + bl plat_get_my_stack mov x1, sp sub x1, x0, x1 mov x0, sp diff --git a/services/std_svc/psci/psci_main.c b/services/std_svc/psci/psci_main.c index b389287b00..f024291022 100644 --- a/services/std_svc/psci/psci_main.c +++ b/services/std_svc/psci/psci_main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-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: @@ -35,6 +35,7 @@ #include #include #include +#include #include "psci_private.h" /******************************************************************************* @@ -46,14 +47,13 @@ int psci_cpu_on(unsigned long target_cpu, { int rc; - unsigned int start_afflvl, end_afflvl; + unsigned int end_pwrlvl; entry_point_info_t ep; /* Determine if the cpu exists of not */ - rc = psci_validate_mpidr(target_cpu, MPIDR_AFFLVL0); - if (rc != PSCI_E_SUCCESS) { + rc = psci_validate_mpidr(target_cpu); + if (rc != PSCI_E_SUCCESS) return PSCI_E_INVALID_PARAMS; - } /* Validate the entrypoint using platform pm_ops */ if (psci_plat_pm_ops->validate_ns_entrypoint) { @@ -73,18 +73,14 @@ int psci_cpu_on(unsigned long target_cpu, if (rc != PSCI_E_SUCCESS) return rc; - /* - * To turn this cpu on, specify which affinity + * To turn this cpu on, specify which power * levels need to be turned on */ - start_afflvl = MPIDR_AFFLVL0; - end_afflvl = PLATFORM_MAX_AFFLVL; - rc = psci_afflvl_on(target_cpu, + end_pwrlvl = PLAT_MAX_PWR_LVL; + rc = psci_cpu_on_start(target_cpu, &ep, - start_afflvl, - end_afflvl); - + end_pwrlvl); return rc; } @@ -98,73 +94,82 @@ int psci_cpu_suspend(unsigned int power_state, unsigned long context_id) { int rc; - unsigned int target_afflvl, pstate_type; + unsigned int target_pwrlvl, is_power_down_state; entry_point_info_t ep; + psci_power_state_t state_info = { {PSCI_LOCAL_STATE_RUN} }; + plat_local_state_t cpu_pd_state; - /* Check SBZ bits in power state are zero */ - if (psci_validate_power_state(power_state)) - return PSCI_E_INVALID_PARAMS; - - /* Sanity check the requested state */ - target_afflvl = psci_get_pstate_afflvl(power_state); - if (target_afflvl > PLATFORM_MAX_AFFLVL) - return PSCI_E_INVALID_PARAMS; - - /* Validate the power_state using platform pm_ops */ - if (psci_plat_pm_ops->validate_power_state) { - rc = psci_plat_pm_ops->validate_power_state(power_state); - if (rc != PSCI_E_SUCCESS) { - assert(rc == PSCI_E_INVALID_PARAMS); - return PSCI_E_INVALID_PARAMS; - } + /* Validate the power_state parameter */ + rc = psci_validate_power_state(power_state, &state_info); + if (rc != PSCI_E_SUCCESS) { + assert(rc == PSCI_E_INVALID_PARAMS); + return rc; } - /* Validate the entrypoint using platform pm_ops */ - if (psci_plat_pm_ops->validate_ns_entrypoint) { - rc = psci_plat_pm_ops->validate_ns_entrypoint(entrypoint); - if (rc != PSCI_E_SUCCESS) { - assert(rc == PSCI_E_INVALID_PARAMS); - return PSCI_E_INVALID_PARAMS; - } - } - - /* Determine the 'state type' in the 'power_state' parameter */ - pstate_type = psci_get_pstate_type(power_state); - /* - * Ensure that we have a platform specific handler for entering - * a standby state. + * Get the value of the state type bit from the power state parameter. */ - if (pstate_type == PSTATE_TYPE_STANDBY) { - if (!psci_plat_pm_ops->affinst_standby) + is_power_down_state = psci_get_pstate_type(power_state); + + /* Sanity check the requested suspend levels */ + assert (psci_validate_suspend_req(&state_info, is_power_down_state) + == PSCI_E_SUCCESS); + + target_pwrlvl = psci_find_target_suspend_lvl(&state_info); + + /* Fast path for CPU standby.*/ + if (is_cpu_standby_req(is_power_down_state, target_pwrlvl)) { + if (!psci_plat_pm_ops->cpu_standby) return PSCI_E_INVALID_PARAMS; - psci_plat_pm_ops->affinst_standby(power_state); + /* + * Set the state of the CPU power domain to the platform + * specific retention state and enter the standby state. + */ + cpu_pd_state = state_info.pwr_domain_state[PSCI_CPU_PWR_LVL]; + psci_set_cpu_local_state(cpu_pd_state); + psci_plat_pm_ops->cpu_standby(cpu_pd_state); + + /* Upon exit from standby, set the state back to RUN. */ + psci_set_cpu_local_state(PSCI_LOCAL_STATE_RUN); + return PSCI_E_SUCCESS; } /* - * Verify and derive the re-entry information for - * the non-secure world from the non-secure state from - * where this call originated. + * If a power down state has been requested, we need to verify entry + * point and program entry information. */ - rc = psci_get_ns_ep_info(&ep, entrypoint, context_id); - if (rc != PSCI_E_SUCCESS) - return rc; + if (is_power_down_state) { + if (psci_plat_pm_ops->validate_ns_entrypoint) { + rc = psci_plat_pm_ops->validate_ns_entrypoint(entrypoint); + if (rc != PSCI_E_SUCCESS) { + assert(rc == PSCI_E_INVALID_PARAMS); + return rc; + } + } - /* Save PSCI power state parameter for the core in suspend context */ - psci_set_suspend_power_state(power_state); + /* + * Verify and derive the re-entry information for + * the non-secure world from the non-secure state from + * where this call originated. + */ + rc = psci_get_ns_ep_info(&ep, entrypoint, context_id); + if (rc != PSCI_E_SUCCESS) + return rc; + } /* * Do what is needed to enter the power down state. Upon success, - * enter the final wfi which will power down this CPU. + * enter the final wfi which will power down this CPU. This function + * might return if the power down was abandoned for any reason, e.g. + * arrival of an interrupt */ - psci_afflvl_suspend(&ep, - MPIDR_AFFLVL0, - target_afflvl); + psci_cpu_suspend_start(&ep, + target_pwrlvl, + &state_info, + is_power_down_state); - /* Reset PSCI power state parameter for the core. */ - psci_set_suspend_power_state(PSCI_INVALID_DATA); return PSCI_E_SUCCESS; } @@ -172,7 +177,7 @@ int psci_system_suspend(unsigned long entrypoint, unsigned long context_id) { int rc; - unsigned int power_state; + psci_power_state_t state_info; entry_point_info_t ep; /* Validate the entrypoint using platform pm_ops */ @@ -180,7 +185,7 @@ int psci_system_suspend(unsigned long entrypoint, rc = psci_plat_pm_ops->validate_ns_entrypoint(entrypoint); if (rc != PSCI_E_SUCCESS) { assert(rc == PSCI_E_INVALID_PARAMS); - return PSCI_E_INVALID_PARAMS; + return rc; } } @@ -197,45 +202,39 @@ int psci_system_suspend(unsigned long entrypoint, if (rc != PSCI_E_SUCCESS) return rc; - /* - * Assert that the required pm_ops hook is implemented to ensure that - * the capability detected during psci_setup() is valid. - */ - assert(psci_plat_pm_ops->get_sys_suspend_power_state); + /* Query the psci_power_state for system suspend */ + psci_query_sys_suspend_pwrstate(&state_info); + + /* Ensure that the psci_power_state makes sense */ + assert(psci_find_target_suspend_lvl(&state_info) == PLAT_MAX_PWR_LVL); + assert(psci_validate_suspend_req(&state_info, PSTATE_TYPE_POWERDOWN) + == PSCI_E_SUCCESS); + assert(is_local_state_off(state_info.pwr_domain_state[PLAT_MAX_PWR_LVL])); /* - * Query the platform for the power_state required for system suspend + * Do what is needed to enter the system suspend state. This function + * might return if the power down was abandoned for any reason, e.g. + * arrival of an interrupt */ - power_state = psci_plat_pm_ops->get_sys_suspend_power_state(); + psci_cpu_suspend_start(&ep, + PLAT_MAX_PWR_LVL, + &state_info, + PSTATE_TYPE_POWERDOWN); - /* Save PSCI power state parameter for the core in suspend context */ - psci_set_suspend_power_state(power_state); - - /* - * Do what is needed to enter the power down state. Upon success, - * enter the final wfi which will power down this cpu. - */ - psci_afflvl_suspend(&ep, - MPIDR_AFFLVL0, - PLATFORM_MAX_AFFLVL); - - /* Reset PSCI power state parameter for the core. */ - psci_set_suspend_power_state(PSCI_INVALID_DATA); return PSCI_E_SUCCESS; } int psci_cpu_off(void) { int rc; - int target_afflvl = PLATFORM_MAX_AFFLVL; + int target_pwrlvl = PLAT_MAX_PWR_LVL; /* - * Traverse from the highest to the lowest affinity level. When the - * lowest affinity level is hit, all the locks are acquired. State - * management is done immediately followed by cpu, cluster ... - * ..target_afflvl specific actions as this function unwinds back. + * Do what is needed to power off this CPU and possible higher power + * levels if it able to do so. Upon success, enter the final wfi + * which will power down this CPU. */ - rc = psci_afflvl_off(MPIDR_AFFLVL0, target_afflvl); + rc = psci_do_cpu_off(target_pwrlvl); /* * The only error cpu_off can return is E_DENIED. So check if that's @@ -249,32 +248,18 @@ int psci_cpu_off(void) int psci_affinity_info(unsigned long target_affinity, unsigned int lowest_affinity_level) { - int rc = PSCI_E_INVALID_PARAMS; - unsigned int aff_state; - aff_map_node_t *node; + unsigned int target_idx; - if (lowest_affinity_level > PLATFORM_MAX_AFFLVL) - return rc; + /* We dont support level higher than PSCI_CPU_PWR_LVL */ + if (lowest_affinity_level > PSCI_CPU_PWR_LVL) + return PSCI_E_INVALID_PARAMS; - node = psci_get_aff_map_node(target_affinity, lowest_affinity_level); - if (node && (node->state & PSCI_AFF_PRESENT)) { + /* Calculate the cpu index of the target */ + target_idx = plat_core_pos_by_mpidr(target_affinity); + if (target_idx == -1) + return PSCI_E_INVALID_PARAMS; - /* - * TODO: For affinity levels higher than 0 i.e. cpu, the - * state will always be either ON or OFF. Need to investigate - * how critical is it to support ON_PENDING here. - */ - aff_state = psci_get_state(node); - - /* A suspended cpu is available & on for the OS */ - if (aff_state == PSCI_STATE_SUSPEND) { - aff_state = PSCI_STATE_ON; - } - - rc = aff_state; - } - - return rc; + return psci_get_aff_info_state_by_idx(target_idx); } int psci_migrate(unsigned long target_cpu) @@ -295,7 +280,7 @@ int psci_migrate(unsigned long target_cpu) return PSCI_E_NOT_PRESENT; /* Check the validity of the specified target cpu */ - rc = psci_validate_mpidr(target_cpu, MPIDR_AFFLVL0); + rc = psci_validate_mpidr(target_cpu); if (rc != PSCI_E_SUCCESS) return PSCI_E_INVALID_PARAMS; @@ -352,10 +337,9 @@ int psci_features(unsigned int psci_fid) if (psci_fid == PSCI_CPU_SUSPEND_AARCH32 || psci_fid == PSCI_CPU_SUSPEND_AARCH64) { /* - * The trusted firmware uses the original power state format - * and does not support OS Initiated Mode. + * The trusted firmware does not support OS Initiated Mode. */ - return (FF_PSTATE_ORIG << FF_PSTATE_SHIFT) | + return (FF_PSTATE << FF_PSTATE_SHIFT) | ((!FF_SUPPORTS_OS_INIT_MODE) << FF_MODE_SUPPORT_SHIFT); } diff --git a/services/std_svc/psci1.0/psci_off.c b/services/std_svc/psci/psci_off.c similarity index 100% rename from services/std_svc/psci1.0/psci_off.c rename to services/std_svc/psci/psci_off.c diff --git a/services/std_svc/psci1.0/psci_on.c b/services/std_svc/psci/psci_on.c similarity index 100% rename from services/std_svc/psci1.0/psci_on.c rename to services/std_svc/psci/psci_on.c diff --git a/services/std_svc/psci/psci_private.h b/services/std_svc/psci/psci_private.h index 2955de7a0f..e2e32c7962 100644 --- a/services/std_svc/psci/psci_private.h +++ b/services/std_svc/psci/psci_private.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-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: @@ -34,22 +34,30 @@ #include #include #include +#include #include +#include /* * The following helper macros abstract the interface to the Bakery * Lock API. */ #if USE_COHERENT_MEM -#define psci_lock_init(aff_map, idx) bakery_lock_init(&(aff_map)[(idx)].lock) -#define psci_lock_get(node) bakery_lock_get(&((node)->lock)) -#define psci_lock_release(node) bakery_lock_release(&((node)->lock)) +#define psci_lock_init(non_cpu_pd_node, idx) \ + bakery_lock_init(&(non_cpu_pd_node)[(idx)].lock) +#define psci_lock_get(non_cpu_pd_node) \ + bakery_lock_get(&((non_cpu_pd_node)->lock)) +#define psci_lock_release(non_cpu_pd_node) \ + bakery_lock_release(&((non_cpu_pd_node)->lock)) #else -#define psci_lock_init(aff_map, idx) ((aff_map)[(idx)].aff_map_index = (idx)) -#define psci_lock_get(node) bakery_lock_get((node)->aff_map_index, \ - CPU_DATA_PSCI_LOCK_OFFSET) -#define psci_lock_release(node) bakery_lock_release((node)->aff_map_index,\ - CPU_DATA_PSCI_LOCK_OFFSET) +#define psci_lock_init(non_cpu_pd_node, idx) \ + ((non_cpu_pd_node)[(idx)].lock_index = (idx)) +#define psci_lock_get(non_cpu_pd_node) \ + bakery_lock_get((non_cpu_pd_node)->lock_index, \ + CPU_DATA_PSCI_LOCK_OFFSET) +#define psci_lock_release(non_cpu_pd_node) \ + bakery_lock_release((non_cpu_pd_node)->lock_index, \ + CPU_DATA_PSCI_LOCK_OFFSET) #endif /* @@ -72,38 +80,98 @@ define_psci_cap(PSCI_MIG_INFO_UP_CPU_AARCH64) | \ define_psci_cap(PSCI_SYSTEM_SUSPEND_AARCH64)) +/* + * Helper macros to get/set the fields of PSCI per-cpu data. + */ +#define psci_set_aff_info_state(aff_state) \ + set_cpu_data(psci_svc_cpu_data.aff_info_state, aff_state) +#define psci_get_aff_info_state() \ + get_cpu_data(psci_svc_cpu_data.aff_info_state) +#define psci_get_aff_info_state_by_idx(idx) \ + get_cpu_data_by_index(idx, psci_svc_cpu_data.aff_info_state) +#define psci_get_suspend_pwrlvl() \ + get_cpu_data(psci_svc_cpu_data.target_pwrlvl) +#define psci_set_suspend_pwrlvl(target_lvl) \ + set_cpu_data(psci_svc_cpu_data.target_pwrlvl, target_lvl) +#define psci_set_cpu_local_state(state) \ + set_cpu_data(psci_svc_cpu_data.local_state, state) +#define psci_get_cpu_local_state() \ + get_cpu_data(psci_svc_cpu_data.local_state) +#define psci_get_cpu_local_state_by_idx(idx) \ + get_cpu_data_by_index(idx, psci_svc_cpu_data.local_state) + +/* + * Helper macros for the CPU level spinlocks + */ +#define psci_spin_lock_cpu(idx) spin_lock(&psci_cpu_pd_nodes[idx].cpu_lock) +#define psci_spin_unlock_cpu(idx) spin_unlock(&psci_cpu_pd_nodes[idx].cpu_lock) + +/* Helper macro to identify a CPU standby request in PSCI Suspend call */ +#define is_cpu_standby_req(is_power_down_state, retn_lvl) \ + (((!(is_power_down_state)) && ((retn_lvl) == 0)) ? 1 : 0) /******************************************************************************* - * The following two data structures hold the topology tree which in turn tracks - * the state of the all the affinity instances supported by the platform. + * The following two data structures implement the power domain tree. The tree + * is used to track the state of all the nodes i.e. power domain instances + * described by the platform. The tree consists of nodes that describe CPU power + * domains i.e. leaf nodes and all other power domains which are parents of a + * CPU power domain i.e. non-leaf nodes. ******************************************************************************/ -typedef struct aff_map_node { - unsigned long mpidr; - unsigned char ref_count; - unsigned char state; +typedef struct non_cpu_pwr_domain_node { + /* + * Index of the first CPU power domain node level 0 which has this node + * as its parent. + */ + unsigned int cpu_start_idx; + + /* + * Number of CPU power domains which are siblings of the domain indexed + * by 'cpu_start_idx' i.e. all the domains in the range 'cpu_start_idx + * -> cpu_start_idx + ncpus' have this node as their parent. + */ + unsigned int ncpus; + + /* + * Index of the parent power domain node. + * TODO: Figure out whether to whether using pointer is more efficient. + */ + unsigned int parent_node; + + plat_local_state_t local_state; + unsigned char level; #if USE_COHERENT_MEM bakery_lock_t lock; #else /* For indexing the bakery_info array in per CPU data */ - unsigned char aff_map_index; + unsigned char lock_index; #endif -} aff_map_node_t; +} non_cpu_pd_node_t; -typedef struct aff_limits_node { - int min; - int max; -} aff_limits_node_t; +typedef struct cpu_pwr_domain_node { + unsigned long mpidr; -typedef aff_map_node_t (*mpidr_aff_map_nodes_t[MPIDR_MAX_AFFLVL + 1]); -typedef void (*afflvl_power_on_finisher_t)(aff_map_node_t *); + /* + * Index of the parent power domain node. + * TODO: Figure out whether to whether using pointer is more efficient. + */ + unsigned int parent_node; + + /* + * A CPU power domain does not require state coordination like its + * parent power domains. Hence this node does not include a bakery + * lock. A spinlock is required by the CPU_ON handler to prevent a race + * when multiple CPUs try to turn ON the same target CPU. + */ + spinlock_t cpu_lock; +} cpu_pd_node_t; /******************************************************************************* * Data prototypes ******************************************************************************/ -extern const plat_pm_ops_t *psci_plat_pm_ops; -extern aff_map_node_t psci_aff_map[PSCI_NUM_AFFS]; -extern aff_limits_node_t psci_aff_limits[MPIDR_MAX_AFFLVL + 1]; +extern const plat_psci_ops_t *psci_plat_pm_ops; +extern non_cpu_pd_node_t psci_non_cpu_pd_nodes[PSCI_NUM_NON_CPU_PWR_DOMAINS]; +extern cpu_pd_node_t psci_cpu_pd_nodes[PLATFORM_CORE_COUNT]; extern uint32_t psci_caps; /******************************************************************************* @@ -115,62 +183,54 @@ extern const spd_pm_ops_t *psci_spd_pm; * Function prototypes ******************************************************************************/ /* Private exported functions from psci_common.c */ -unsigned short psci_get_state(aff_map_node_t *node); -unsigned short psci_get_phys_state(aff_map_node_t *node); -void psci_set_state(aff_map_node_t *node, unsigned short state); -unsigned long mpidr_set_aff_inst(unsigned long, unsigned char, int); -int psci_validate_mpidr(unsigned long, int); -int get_power_on_target_afflvl(void); -void psci_afflvl_power_on_finish(int, - int, - afflvl_power_on_finisher_t *); +int psci_validate_power_state(unsigned int power_state, + psci_power_state_t *state_info); +void psci_query_sys_suspend_pwrstate(psci_power_state_t *state_info); +int psci_validate_mpidr(unsigned long mpidr); +void psci_init_req_local_pwr_states(void); +void psci_power_up_finish(void); int psci_get_ns_ep_info(entry_point_info_t *ep, uint64_t entrypoint, uint64_t context_id); -int psci_check_afflvl_range(int start_afflvl, int end_afflvl); -void psci_do_afflvl_state_mgmt(uint32_t start_afflvl, - uint32_t end_afflvl, - aff_map_node_t *mpidr_nodes[], - uint32_t state); -void psci_acquire_afflvl_locks(int start_afflvl, - int end_afflvl, - aff_map_node_t *mpidr_nodes[]); -void psci_release_afflvl_locks(int start_afflvl, - int end_afflvl, - mpidr_aff_map_nodes_t mpidr_nodes); -void psci_print_affinity_map(void); -void psci_set_max_phys_off_afflvl(uint32_t afflvl); -uint32_t psci_find_max_phys_off_afflvl(uint32_t start_afflvl, - uint32_t end_afflvl, - aff_map_node_t *mpidr_nodes[]); +void psci_get_parent_pwr_domain_nodes(unsigned int cpu_idx, + int end_lvl, + unsigned int node_index[]); +void psci_do_state_coordination(int end_pwrlvl, + psci_power_state_t *state_info); +void psci_acquire_pwr_domain_locks(int end_pwrlvl, + unsigned int cpu_idx); +void psci_release_pwr_domain_locks(int end_pwrlvl, + unsigned int cpu_idx); +int psci_validate_suspend_req(const psci_power_state_t *state_info, + unsigned int is_power_down_state_req); +unsigned int psci_find_max_off_lvl(const psci_power_state_t *state_info); +unsigned int psci_find_target_suspend_lvl(const psci_power_state_t *state_info); +void psci_set_pwr_domains_to_run(uint32_t end_pwrlvl); +void psci_print_power_domain_map(void); unsigned int psci_is_last_on_cpu(void); int psci_spd_migrate_info(uint64_t *mpidr); -/* Private exported functions from psci_setup.c */ -int psci_get_aff_map_nodes(unsigned long mpidr, - int start_afflvl, - int end_afflvl, - aff_map_node_t *mpidr_nodes[]); -aff_map_node_t *psci_get_aff_map_node(unsigned long, int); +/* Private exported functions from psci_on.c */ +int psci_cpu_on_start(unsigned long target_cpu, + entry_point_info_t *ep, + int end_pwrlvl); -/* Private exported functions from psci_affinity_on.c */ -int psci_afflvl_on(unsigned long target_cpu, - entry_point_info_t *ep, - int start_afflvl, - int end_afflvl); +void psci_cpu_on_finish(unsigned int cpu_idx, + psci_power_state_t *state_info); -/* Private exported functions from psci_affinity_off.c */ -int psci_afflvl_off(int, int); +/* Private exported functions from psci_cpu_off.c */ +int psci_do_cpu_off(int end_pwrlvl); -/* Private exported functions from psci_affinity_suspend.c */ -void psci_afflvl_suspend(entry_point_info_t *ep, - int start_afflvl, - int end_afflvl); +/* Private exported functions from psci_pwrlvl_suspend.c */ +void psci_cpu_suspend_start(entry_point_info_t *ep, + int end_pwrlvl, + psci_power_state_t *state_info, + unsigned int is_power_down_state_req); -unsigned int psci_afflvl_suspend_finish(int, int); -void psci_set_suspend_power_state(unsigned int power_state); +void psci_cpu_suspend_finish(unsigned int cpu_idx, + psci_power_state_t *state_info); /* Private exported functions from psci_helpers.S */ -void psci_do_pwrdown_cache_maintenance(uint32_t affinity_level); +void psci_do_pwrdown_cache_maintenance(uint32_t pwr_level); void psci_do_pwrup_cache_maintenance(void); /* Private exported functions from psci_system_off.c */ diff --git a/services/std_svc/psci/psci_setup.c b/services/std_svc/psci/psci_setup.c index 01b559cf38..ce4da9599f 100644 --- a/services/std_svc/psci/psci_setup.c +++ b/services/std_svc/psci/psci_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-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: @@ -42,351 +42,223 @@ * Per cpu non-secure contexts used to program the architectural state prior * return to the normal world. * TODO: Use the memory allocator to set aside memory for the contexts instead - * of relying on platform defined constants. Using PSCI_NUM_AFFS will be an - * overkill. + * of relying on platform defined constants. ******************************************************************************/ static cpu_context_t psci_ns_context[PLATFORM_CORE_COUNT]; -/******************************************************************************* - * In a system, a certain number of affinity instances are present at an - * affinity level. The cumulative number of instances across all levels are - * stored in 'psci_aff_map'. The topology tree has been flattenned into this - * array. To retrieve nodes, information about the extents of each affinity - * level i.e. start index and end index needs to be present. 'psci_aff_limits' - * stores this information. - ******************************************************************************/ -aff_limits_node_t psci_aff_limits[MPIDR_MAX_AFFLVL + 1]; - /****************************************************************************** * Define the psci capability variable. *****************************************************************************/ uint32_t psci_caps; - /******************************************************************************* - * Routines for retrieving the node corresponding to an affinity level instance - * in the mpidr. The first one uses binary search to find the node corresponding - * to the mpidr (key) at a particular affinity level. The second routine decides - * extents of the binary search at each affinity level. + * Function which initializes the 'psci_non_cpu_pd_nodes' or the + * 'psci_cpu_pd_nodes' corresponding to the power level. ******************************************************************************/ -static int psci_aff_map_get_idx(unsigned long key, - int min_idx, - int max_idx) +static void psci_init_pwr_domain_node(int node_idx, int parent_idx, int level) { - int mid; + if (level > PSCI_CPU_PWR_LVL) { + psci_non_cpu_pd_nodes[node_idx].level = level; + psci_lock_init(psci_non_cpu_pd_nodes, node_idx); + psci_non_cpu_pd_nodes[node_idx].parent_node = parent_idx; + psci_non_cpu_pd_nodes[node_idx].local_state = + PLAT_MAX_OFF_STATE; + } else { + psci_cpu_data_t *svc_cpu_data; - /* - * Terminating condition: If the max and min indices have crossed paths - * during the binary search then the key has not been found. - */ - if (max_idx < min_idx) - return PSCI_E_INVALID_PARAMS; + psci_cpu_pd_nodes[node_idx].parent_node = parent_idx; - /* - * Make sure we are within array limits. - */ - assert(min_idx >= 0 && max_idx < PSCI_NUM_AFFS); + /* Initialize with an invalid mpidr */ + psci_cpu_pd_nodes[node_idx].mpidr = PSCI_INVALID_MPIDR; - /* - * Bisect the array around 'mid' and then recurse into the array chunk - * where the key is likely to be found. The mpidrs in each node in the - * 'psci_aff_map' for a given affinity level are stored in an ascending - * order which makes the binary search possible. - */ - mid = min_idx + ((max_idx - min_idx) >> 1); /* Divide by 2 */ + svc_cpu_data = + &(_cpu_data_by_index(node_idx)->psci_svc_cpu_data); - if (psci_aff_map[mid].mpidr > key) - return psci_aff_map_get_idx(key, min_idx, mid - 1); - else if (psci_aff_map[mid].mpidr < key) - return psci_aff_map_get_idx(key, mid + 1, max_idx); - else - return mid; -} + /* Set the Affinity Info for the cores as OFF */ + svc_cpu_data->aff_info_state = AFF_STATE_OFF; -aff_map_node_t *psci_get_aff_map_node(unsigned long mpidr, int aff_lvl) -{ - int rc; + /* Invalidate the suspend level for the cpu */ + svc_cpu_data->target_pwrlvl = PSCI_INVALID_DATA; - if (aff_lvl > PLATFORM_MAX_AFFLVL) - return NULL; + /* Set the power state to OFF state */ + svc_cpu_data->local_state = PLAT_MAX_OFF_STATE; - /* Right shift the mpidr to the required affinity level */ - mpidr = mpidr_mask_lower_afflvls(mpidr, aff_lvl); + flush_dcache_range((uint64_t)svc_cpu_data, + sizeof(*svc_cpu_data)); - rc = psci_aff_map_get_idx(mpidr, - psci_aff_limits[aff_lvl].min, - psci_aff_limits[aff_lvl].max); - if (rc >= 0) - return &psci_aff_map[rc]; - else - return NULL; -} - -/******************************************************************************* - * This function populates an array with nodes corresponding to a given range of - * affinity levels in an mpidr. It returns successfully only when the affinity - * levels are correct, the mpidr is valid i.e. no affinity level is absent from - * the topology tree & the affinity instance at level 0 is not absent. - ******************************************************************************/ -int psci_get_aff_map_nodes(unsigned long mpidr, - int start_afflvl, - int end_afflvl, - aff_map_node_t *mpidr_nodes[]) -{ - int rc = PSCI_E_INVALID_PARAMS, level; - aff_map_node_t *node; - - rc = psci_check_afflvl_range(start_afflvl, end_afflvl); - if (rc != PSCI_E_SUCCESS) - return rc; - - for (level = start_afflvl; level <= end_afflvl; level++) { - - /* - * Grab the node for each affinity level. No affinity level - * can be missing as that would mean that the topology tree - * is corrupted. - */ - node = psci_get_aff_map_node(mpidr, level); - if (node == NULL) { - rc = PSCI_E_INVALID_PARAMS; - break; - } - - /* - * Skip absent affinity levels unless it's afffinity level 0. - * An absent cpu means that the mpidr is invalid. Save the - * pointer to the node for the present affinity level - */ - if (!(node->state & PSCI_AFF_PRESENT)) { - if (level == MPIDR_AFFLVL0) { - rc = PSCI_E_INVALID_PARAMS; - break; - } - - mpidr_nodes[level] = NULL; - } else - mpidr_nodes[level] = node; - } - - return rc; -} - -/******************************************************************************* - * Function which initializes the 'aff_map_node' corresponding to an affinity - * level instance. Each node has a unique mpidr, level and bakery lock. The data - * field is opaque and holds affinity level specific data e.g. for affinity - * level 0 it contains the index into arrays that hold the secure/non-secure - * state for a cpu that's been turned on/off - ******************************************************************************/ -static void psci_init_aff_map_node(unsigned long mpidr, - int level, - unsigned int idx) -{ - unsigned char state; - uint32_t linear_id; - psci_aff_map[idx].mpidr = mpidr; - psci_aff_map[idx].level = level; - psci_lock_init(psci_aff_map, idx); - - /* - * If an affinity instance is present then mark it as OFF to begin with. - */ - state = plat_get_aff_state(level, mpidr); - psci_aff_map[idx].state = state; - - if (level == MPIDR_AFFLVL0) { - - /* - * Mark the cpu as OFF. Higher affinity level reference counts - * have already been memset to 0 - */ - if (state & PSCI_AFF_PRESENT) - psci_set_state(&psci_aff_map[idx], PSCI_STATE_OFF); - - /* - * Associate a non-secure context with this affinity - * instance through the context management library. - */ - linear_id = platform_get_core_pos(mpidr); - assert(linear_id < PLATFORM_CORE_COUNT); - - /* Invalidate the suspend context for the node */ - set_cpu_data_by_index(linear_id, - psci_svc_cpu_data.power_state, - PSCI_INVALID_DATA); - - /* - * There is no state associated with the current execution - * context so ensure that any reads of the highest affinity - * level in a powered down state return PSCI_INVALID_DATA. - */ - set_cpu_data_by_index(linear_id, - psci_svc_cpu_data.max_phys_off_afflvl, - PSCI_INVALID_DATA); - - flush_cpu_data_by_index(linear_id, psci_svc_cpu_data); - - cm_set_context_by_mpidr(mpidr, - (void *) &psci_ns_context[linear_id], + cm_set_context_by_index(node_idx, + (void *) &psci_ns_context[node_idx], NON_SECURE); } - - return; } /******************************************************************************* - * Core routine used by the Breadth-First-Search algorithm to populate the - * affinity tree. Each level in the tree corresponds to an affinity level. This - * routine's aim is to traverse to the target affinity level and populate nodes - * in the 'psci_aff_map' for all the siblings at that level. 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. - * - * 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 psci_init_aff_map(unsigned long mpidr, - unsigned int affmap_idx, - int cur_afflvl, - int tgt_afflvl) + * This functions updates cpu_start_idx and ncpus field for each of the node in + * psci_non_cpu_pd_nodes[]. It does so by comparing the parent nodes of each of + * the CPUs and check whether they match with the parent of the previous + * CPU. The basic assumption for this work is that children of the same parent + * are allocated adjacent indices. The platform should ensure this though proper + * mapping of the CPUs to indices via plat_core_pos_by_mpidr() and + * plat_my_core_pos() APIs. + *******************************************************************************/ +static void psci_update_pwrlvl_limits(void) { - unsigned int ctr, aff_count; + int cpu_idx, j; + unsigned int nodes_idx[PLAT_MAX_PWR_LVL] = {0}; + unsigned int temp_index[PLAT_MAX_PWR_LVL]; - assert(cur_afflvl >= tgt_afflvl); + for (cpu_idx = 0; cpu_idx < PLATFORM_CORE_COUNT; cpu_idx++) { + psci_get_parent_pwr_domain_nodes(cpu_idx, + PLAT_MAX_PWR_LVL, + temp_index); + for (j = PLAT_MAX_PWR_LVL - 1; j >= 0; j--) { + if (temp_index[j] != nodes_idx[j]) { + nodes_idx[j] = temp_index[j]; + psci_non_cpu_pd_nodes[nodes_idx[j]].cpu_start_idx + = cpu_idx; + } + psci_non_cpu_pd_nodes[nodes_idx[j]].ncpus++; + } + } +} + +/******************************************************************************* + * Core routine to populate the power domain tree. The tree descriptor passed by + * the platform is populated breadth-first and the first entry in the map + * informs the number of root power domains. The parent nodes of the root nodes + * will point to an invalid entry(-1). + ******************************************************************************/ +static void populate_power_domain_tree(const unsigned char *topology) +{ + unsigned int i, j = 0, num_nodes_at_lvl = 1, num_nodes_at_next_lvl; + unsigned int node_index = 0, parent_node_index = 0, num_children; + int level = PLAT_MAX_PWR_LVL; /* - * Find the number of siblings at the current affinity level & - * assert if there are none 'cause then we have been invoked with - * an invalid mpidr. + * For each level the inputs are: + * - number of nodes at this level in plat_array i.e. num_nodes_at_level + * This is the sum of values of nodes at the parent level. + * - Index of first entry at this level in the plat_array i.e. + * parent_node_index. + * - Index of first free entry in psci_non_cpu_pd_nodes[] or + * psci_cpu_pd_nodes[] i.e. node_index depending upon the level. */ - aff_count = plat_get_aff_count(cur_afflvl, mpidr); - assert(aff_count); + while (level >= PSCI_CPU_PWR_LVL) { + num_nodes_at_next_lvl = 0; + /* + * For each entry (parent node) at this level in the plat_array: + * - Find the number of children + * - Allocate a node in a power domain array for each child + * - Set the parent of the child to the parent_node_index - 1 + * - Increment parent_node_index to point to the next parent + * - Accumulate the number of children at next level. + */ + for (i = 0; i < num_nodes_at_lvl; i++) { + assert(parent_node_index <= + PSCI_NUM_NON_CPU_PWR_DOMAINS); + num_children = topology[parent_node_index]; - if (tgt_afflvl < cur_afflvl) { - for (ctr = 0; ctr < aff_count; ctr++) { - mpidr = mpidr_set_aff_inst(mpidr, ctr, cur_afflvl); - affmap_idx = psci_init_aff_map(mpidr, - affmap_idx, - cur_afflvl - 1, - tgt_afflvl); - } - } else { - for (ctr = 0; ctr < aff_count; ctr++, affmap_idx++) { - mpidr = mpidr_set_aff_inst(mpidr, ctr, cur_afflvl); - psci_init_aff_map_node(mpidr, cur_afflvl, affmap_idx); + for (j = node_index; + j < node_index + num_children; j++) + psci_init_pwr_domain_node(j, + parent_node_index - 1, + level); + + node_index = j; + num_nodes_at_next_lvl += num_children; + parent_node_index++; } - /* affmap_idx is 1 greater than the max index of cur_afflvl */ - psci_aff_limits[cur_afflvl].max = affmap_idx - 1; + num_nodes_at_lvl = num_nodes_at_next_lvl; + level--; + + /* Reset the index for the cpu power domain array */ + if (level == PSCI_CPU_PWR_LVL) + node_index = 0; } - return affmap_idx; + /* Validate the sanity of array exported by the platform */ + assert(j == PLATFORM_CORE_COUNT); + +#if !USE_COHERENT_MEM + /* Flush the non CPU power domain data to memory */ + flush_dcache_range((uint64_t) &psci_non_cpu_pd_nodes, + sizeof(psci_non_cpu_pd_nodes)); +#endif } /******************************************************************************* - * This function initializes the topology tree by querying the platform. To do - * so, it's helper routines implement a Breadth-First-Search. At each affinity - * level the platform conveys the number of affinity instances that exist i.e. - * the affinity count. The algorithm populates the psci_aff_map recursively - * using this information. On a platform that implements two clusters of 4 cpus - * each, the populated aff_map_array would look like this: + * This function initializes the power domain topology tree by querying the + * platform. The power domain nodes higher than the CPU are populated in the + * array psci_non_cpu_pd_nodes[] and the CPU power domains are populated in + * psci_cpu_pd_nodes[]. The platform exports its static topology map through the + * populate_power_domain_topology_tree() API. The algorithm populates the + * psci_non_cpu_pd_nodes and psci_cpu_pd_nodes iteratively by using this + * topology map. On a platform that implements two clusters of 2 cpus each, and + * supporting 3 domain levels, the populated psci_non_cpu_pd_nodes would look + * like this: * - * <- cpus cluster0 -><- cpus cluster1 -> * --------------------------------------------------- - * | 0 | 1 | 0 | 1 | 2 | 3 | 0 | 1 | 2 | 3 | + * | system node | cluster 0 node | cluster 1 node | * --------------------------------------------------- - * ^ ^ - * cluster __| cpu __| - * limit limit * - * The first 2 entries are of the cluster nodes. The next 4 entries are of cpus - * within cluster 0. The last 4 entries are of cpus within cluster 1. - * The 'psci_aff_limits' array contains the max & min index of each affinity - * level within the 'psci_aff_map' array. This allows restricting search of a - * node at an affinity level between the indices in the limits array. + * And populated psci_cpu_pd_nodes would look like this : + * <- cpus cluster0 -><- cpus cluster1 -> + * ------------------------------------------------ + * | CPU 0 | CPU 1 | CPU 2 | CPU 3 | + * ------------------------------------------------ ******************************************************************************/ int32_t psci_setup(void) { - unsigned long mpidr = read_mpidr(); - int afflvl, affmap_idx, max_afflvl; - aff_map_node_t *node; + const unsigned char *topology_tree; - psci_plat_pm_ops = NULL; + /* Query the topology map from the platform */ + topology_tree = plat_get_power_domain_tree_desc(); - /* Find out the maximum affinity level that the platform implements */ - max_afflvl = PLATFORM_MAX_AFFLVL; - assert(max_afflvl <= MPIDR_MAX_AFFLVL); + /* Populate the power domain arrays using the platform topology map */ + populate_power_domain_tree(topology_tree); - /* - * This call traverses the topology tree with help from the platform and - * populates the affinity map using a breadth-first-search recursively. - * 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 = max_afflvl; afflvl >= MPIDR_AFFLVL0; afflvl--) { - affmap_idx = psci_init_aff_map(FIRST_MPIDR, - affmap_idx, - max_afflvl, - afflvl); - } + /* Update the CPU limits for each node in psci_non_cpu_pd_nodes */ + psci_update_pwrlvl_limits(); + + /* Populate the mpidr field of cpu node for this CPU */ + psci_cpu_pd_nodes[plat_my_core_pos()].mpidr = + read_mpidr() & MPIDR_AFFINITY_MASK; #if !USE_COHERENT_MEM /* - * The psci_aff_map only needs flushing when it's not allocated in + * The psci_non_cpu_pd_nodes only needs flushing when it's not allocated in * coherent memory. */ - flush_dcache_range((uint64_t) &psci_aff_map, sizeof(psci_aff_map)); + flush_dcache_range((uint64_t) &psci_non_cpu_pd_nodes, + sizeof(psci_non_cpu_pd_nodes)); #endif - /* - * Set the bounds for the affinity counts of each level in the map. Also - * flush out the entire array so that it's visible to subsequent power - * management operations. The 'psci_aff_limits' array is allocated in - * normal memory. It will be accessed when the mmu is off e.g. after - * reset. Hence it needs to be flushed. - */ - for (afflvl = MPIDR_AFFLVL0; afflvl < max_afflvl; afflvl++) { - psci_aff_limits[afflvl].min = - psci_aff_limits[afflvl + 1].max + 1; - } + flush_dcache_range((uint64_t) &psci_cpu_pd_nodes, + sizeof(psci_cpu_pd_nodes)); - flush_dcache_range((unsigned long) psci_aff_limits, - sizeof(psci_aff_limits)); + psci_init_req_local_pwr_states(); /* - * Mark the affinity instances in our mpidr as ON. No need to lock as - * this is the primary cpu. + * Set the requested and target state of this CPU and all the higher + * power domain levels for this CPU to run. */ - mpidr &= MPIDR_AFFINITY_MASK; - for (afflvl = MPIDR_AFFLVL0; afflvl <= max_afflvl; afflvl++) { + psci_set_pwr_domains_to_run(PLAT_MAX_PWR_LVL); - node = psci_get_aff_map_node(mpidr, afflvl); - assert(node); - - /* Mark each present node as ON. */ - if (node->state & PSCI_AFF_PRESENT) - psci_set_state(node, PSCI_STATE_ON); - } - - platform_setup_pm(&psci_plat_pm_ops); + plat_setup_psci_ops((uintptr_t)psci_entrypoint, + &psci_plat_pm_ops); assert(psci_plat_pm_ops); /* Initialize the psci capability */ psci_caps = PSCI_GENERIC_CAP; - if (psci_plat_pm_ops->affinst_off) + if (psci_plat_pm_ops->pwr_domain_off) psci_caps |= define_psci_cap(PSCI_CPU_OFF); - if (psci_plat_pm_ops->affinst_on && psci_plat_pm_ops->affinst_on_finish) + if (psci_plat_pm_ops->pwr_domain_on && + psci_plat_pm_ops->pwr_domain_on_finish) psci_caps |= define_psci_cap(PSCI_CPU_ON_AARCH64); - if (psci_plat_pm_ops->affinst_suspend && - psci_plat_pm_ops->affinst_suspend_finish) { + if (psci_plat_pm_ops->pwr_domain_suspend && + psci_plat_pm_ops->pwr_domain_suspend_finish) { psci_caps |= define_psci_cap(PSCI_CPU_SUSPEND_AARCH64); if (psci_plat_pm_ops->get_sys_suspend_power_state) psci_caps |= define_psci_cap(PSCI_SYSTEM_SUSPEND_AARCH64); diff --git a/services/std_svc/psci1.0/psci_suspend.c b/services/std_svc/psci/psci_suspend.c similarity index 100% rename from services/std_svc/psci1.0/psci_suspend.c rename to services/std_svc/psci/psci_suspend.c diff --git a/services/std_svc/psci/psci_system_off.c b/services/std_svc/psci/psci_system_off.c index 970d4bb501..28315d6b59 100644 --- a/services/std_svc/psci/psci_system_off.c +++ b/services/std_svc/psci/psci_system_off.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2014-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: @@ -37,7 +37,7 @@ void psci_system_off(void) { - psci_print_affinity_map(); + psci_print_power_domain_map(); assert(psci_plat_pm_ops->system_off); @@ -54,7 +54,7 @@ void psci_system_off(void) void psci_system_reset(void) { - psci_print_affinity_map(); + psci_print_power_domain_map(); assert(psci_plat_pm_ops->system_reset); diff --git a/services/std_svc/psci1.0/psci_common.c b/services/std_svc/psci1.0/psci_common.c deleted file mode 100644 index 7f1a5fd0d8..0000000000 --- a/services/std_svc/psci1.0/psci_common.c +++ /dev/null @@ -1,877 +0,0 @@ -/* - * Copyright (c) 2013-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 -#include -#include -#include -#include -#include "psci_private.h" - -/* - * SPD power management operations, expected to be supplied by the registered - * SPD on successful SP initialization - */ -const spd_pm_ops_t *psci_spd_pm; - -/* - * PSCI requested local power state map. This array is used to store the local - * power states requested by a CPU for power levels from level 1 to - * PLAT_MAX_PWR_LVL. It does not store the requested local power state for power - * level 0 (PSCI_CPU_PWR_LVL) as the requested and the target power state for a - * CPU are the same. - * - * During state coordination, the platform is passed an array containing the - * local states requested for a particular non cpu power domain by each cpu - * within the domain. - * - * TODO: Dense packing of the requested states will cause cache thrashing - * when multiple power domains write to it. If we allocate the requested - * states at each power level in a cache-line aligned per-domain memory, - * the cache thrashing can be avoided. - */ -static plat_local_state_t - psci_req_local_pwr_states[PLAT_MAX_PWR_LVL][PLATFORM_CORE_COUNT]; - - -/******************************************************************************* - * Arrays that hold the platform's power domain tree information for state - * management of power domains. - * Each node in the array 'psci_non_cpu_pd_nodes' corresponds to a power domain - * which is an ancestor of a CPU power domain. - * Each node in the array 'psci_cpu_pd_nodes' corresponds to a cpu power domain - ******************************************************************************/ -non_cpu_pd_node_t psci_non_cpu_pd_nodes[PSCI_NUM_NON_CPU_PWR_DOMAINS] -#if USE_COHERENT_MEM -__attribute__ ((section("tzfw_coherent_mem"))) -#endif -; - -cpu_pd_node_t psci_cpu_pd_nodes[PLATFORM_CORE_COUNT]; - -/******************************************************************************* - * Pointer to functions exported by the platform to complete power mgmt. ops - ******************************************************************************/ -const plat_psci_ops_t *psci_plat_pm_ops; - -/****************************************************************************** - * Check that the maximum power level supported by the platform makes sense - *****************************************************************************/ -CASSERT(PLAT_MAX_PWR_LVL <= PSCI_MAX_PWR_LVL && \ - PLAT_MAX_PWR_LVL >= PSCI_CPU_PWR_LVL, \ - assert_platform_max_pwrlvl_check); - -/* - * The plat_local_state used by the platform is one of these types: RUN, - * RETENTION and OFF. The platform can define further sub-states for each type - * apart from RUN. This categorization is done to verify the sanity of the - * psci_power_state passed by the platform and to print debug information. The - * categorization is done on the basis of the following conditions: - * - * 1. If (plat_local_state == 0) then the category is STATE_TYPE_RUN. - * - * 2. If (0 < plat_local_state <= PLAT_MAX_RET_STATE), then the category is - * STATE_TYPE_RETN. - * - * 3. If (plat_local_state > PLAT_MAX_RET_STATE), then the category is - * STATE_TYPE_OFF. - */ -typedef enum plat_local_state_type { - STATE_TYPE_RUN = 0, - STATE_TYPE_RETN, - STATE_TYPE_OFF -} plat_local_state_type_t; - -/* The macro used to categorize plat_local_state. */ -#define find_local_state_type(plat_local_state) \ - ((plat_local_state) ? ((plat_local_state > PLAT_MAX_RET_STATE) \ - ? STATE_TYPE_OFF : STATE_TYPE_RETN) \ - : STATE_TYPE_RUN) - -/****************************************************************************** - * Check that the maximum retention level supported by the platform is less - * than the maximum off level. - *****************************************************************************/ -CASSERT(PLAT_MAX_RET_STATE < PLAT_MAX_OFF_STATE, \ - assert_platform_max_off_and_retn_state_check); - -/****************************************************************************** - * This function ensures that the power state parameter in a CPU_SUSPEND request - * is valid. If so, it returns the requested states for each power level. - *****************************************************************************/ -int psci_validate_power_state(unsigned int power_state, - psci_power_state_t *state_info) -{ - /* Check SBZ bits in power state are zero */ - if (psci_check_power_state(power_state)) - return PSCI_E_INVALID_PARAMS; - - assert(psci_plat_pm_ops->validate_power_state); - - /* Validate the power_state using platform pm_ops */ - return psci_plat_pm_ops->validate_power_state(power_state, state_info); -} - -/****************************************************************************** - * This function retrieves the `psci_power_state_t` for system suspend from - * the platform. - *****************************************************************************/ -void psci_query_sys_suspend_pwrstate(psci_power_state_t *state_info) -{ - /* - * Assert that the required pm_ops hook is implemented to ensure that - * the capability detected during psci_setup() is valid. - */ - assert(psci_plat_pm_ops->get_sys_suspend_power_state); - - /* - * Query the platform for the power_state required for system suspend - */ - psci_plat_pm_ops->get_sys_suspend_power_state(state_info); -} - -/******************************************************************************* - * This function verifies that the all the other cores in the system have been - * turned OFF and the current CPU is the last running CPU in the system. - * Returns 1 (true) if the current CPU is the last ON CPU or 0 (false) - * otherwise. - ******************************************************************************/ -unsigned int psci_is_last_on_cpu(void) -{ - unsigned int cpu_idx, my_idx = plat_my_core_pos(); - - for (cpu_idx = 0; cpu_idx < PLATFORM_CORE_COUNT; cpu_idx++) { - if (cpu_idx == my_idx) { - assert(psci_get_aff_info_state() == AFF_STATE_ON); - continue; - } - - if (psci_get_aff_info_state_by_idx(cpu_idx) != AFF_STATE_OFF) - return 0; - } - - return 1; -} - -/******************************************************************************* - * Routine to return the maximum power level to traverse to after a cpu has - * been physically powered up. It is expected to be called immediately after - * reset from assembler code. - ******************************************************************************/ -static int get_power_on_target_pwrlvl(void) -{ - int pwrlvl; - - /* - * Assume that this cpu was suspended and retrieve its target power - * level. If it is invalid then it could only have been turned off - * earlier. PLAT_MAX_PWR_LVL will be the highest power level a - * cpu can be turned off to. - */ - pwrlvl = psci_get_suspend_pwrlvl(); - if (pwrlvl == PSCI_INVALID_DATA) - pwrlvl = PLAT_MAX_PWR_LVL; - return pwrlvl; -} - -/****************************************************************************** - * Helper function to update the requested local power state array. This array - * does not store the requested state for the CPU power level. Hence an - * assertion is added to prevent us from accessing the wrong index. - *****************************************************************************/ -static void psci_set_req_local_pwr_state(unsigned int pwrlvl, - unsigned int cpu_idx, - plat_local_state_t req_pwr_state) -{ - assert(pwrlvl > PSCI_CPU_PWR_LVL); - psci_req_local_pwr_states[pwrlvl - 1][cpu_idx] = req_pwr_state; -} - -/****************************************************************************** - * This function initializes the psci_req_local_pwr_states. - *****************************************************************************/ -void psci_init_req_local_pwr_states(void) -{ - /* Initialize the requested state of all non CPU power domains as OFF */ - memset(&psci_req_local_pwr_states, PLAT_MAX_OFF_STATE, - sizeof(psci_req_local_pwr_states)); -} - -/****************************************************************************** - * Helper function to return a reference to an array containing the local power - * states requested by each cpu for a power domain at 'pwrlvl'. The size of the - * array will be the number of cpu power domains of which this power domain is - * an ancestor. These requested states will be used to determine a suitable - * target state for this power domain during psci state coordination. An - * assertion is added to prevent us from accessing the CPU power level. - *****************************************************************************/ -static plat_local_state_t *psci_get_req_local_pwr_states(int pwrlvl, - int cpu_idx) -{ - assert(pwrlvl > PSCI_CPU_PWR_LVL); - - return &psci_req_local_pwr_states[pwrlvl - 1][cpu_idx]; -} - -/****************************************************************************** - * Helper function to return the current local power state of each power domain - * from the current cpu power domain to its ancestor at the 'end_pwrlvl'. This - * function will be called after a cpu is powered on to find the local state - * each power domain has emerged from. - *****************************************************************************/ -static void psci_get_target_local_pwr_states(uint32_t end_pwrlvl, - psci_power_state_t *target_state) -{ - int lvl; - unsigned int parent_idx; - plat_local_state_t *pd_state = target_state->pwr_domain_state; - - pd_state[PSCI_CPU_PWR_LVL] = psci_get_cpu_local_state(); - parent_idx = psci_cpu_pd_nodes[plat_my_core_pos()].parent_node; - - /* Copy the local power state from node to state_info */ - for (lvl = PSCI_CPU_PWR_LVL + 1; lvl <= end_pwrlvl; lvl++) { -#if !USE_COHERENT_MEM - /* - * If using normal memory for psci_non_cpu_pd_nodes, we need - * to flush before reading the local power state as another - * cpu in the same power domain could have updated it and this - * code runs before caches are enabled. - */ - flush_dcache_range( - (uint64_t)&psci_non_cpu_pd_nodes[parent_idx], - sizeof(psci_non_cpu_pd_nodes[parent_idx])); -#endif - pd_state[lvl] = psci_non_cpu_pd_nodes[parent_idx].local_state; - parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node; - } - - /* Set the the higher levels to RUN */ - for (; lvl <= PLAT_MAX_PWR_LVL; lvl++) - target_state->pwr_domain_state[lvl] = PSCI_LOCAL_STATE_RUN; -} - -/****************************************************************************** - * Helper function to set the target local power state that each power domain - * from the current cpu power domain to its ancestor at the 'end_pwrlvl' will - * enter. This function will be called after coordination of requested power - * states has been done for each power level. - *****************************************************************************/ -static void psci_set_target_local_pwr_states(uint32_t end_pwrlvl, - const psci_power_state_t *target_state) -{ - int lvl; - unsigned int parent_idx; - const plat_local_state_t *pd_state = target_state->pwr_domain_state; - - psci_set_cpu_local_state(pd_state[PSCI_CPU_PWR_LVL]); - - /* - * Need to flush as local_state will be accessed with Data Cache - * disabled during power on - */ - flush_cpu_data(psci_svc_cpu_data.local_state); - - parent_idx = psci_cpu_pd_nodes[plat_my_core_pos()].parent_node; - - /* Copy the local_state from state_info */ - for (lvl = 1; lvl <= end_pwrlvl; lvl++) { - psci_non_cpu_pd_nodes[parent_idx].local_state = pd_state[lvl]; -#if !USE_COHERENT_MEM - flush_dcache_range( - (uint64_t)&psci_non_cpu_pd_nodes[parent_idx], - sizeof(psci_non_cpu_pd_nodes[parent_idx])); -#endif - parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node; - } -} - - -/******************************************************************************* - * PSCI helper function to get the parent nodes corresponding to a cpu_index. - ******************************************************************************/ -void psci_get_parent_pwr_domain_nodes(unsigned int cpu_idx, - int end_lvl, - unsigned int node_index[]) -{ - unsigned int parent_node = psci_cpu_pd_nodes[cpu_idx].parent_node; - int i; - - for (i = PSCI_CPU_PWR_LVL + 1; i <= end_lvl; i++) { - *node_index++ = parent_node; - parent_node = psci_non_cpu_pd_nodes[parent_node].parent_node; - } -} - -/****************************************************************************** - * This function is invoked post CPU power up and initialization. It sets the - * affinity info state, target power state and requested power state for the - * current CPU and all its ancestor power domains to RUN. - *****************************************************************************/ -void psci_set_pwr_domains_to_run(uint32_t end_pwrlvl) -{ - int lvl; - unsigned int parent_idx, cpu_idx = plat_my_core_pos(); - parent_idx = psci_cpu_pd_nodes[cpu_idx].parent_node; - - /* Reset the local_state to RUN for the non cpu power domains. */ - for (lvl = PSCI_CPU_PWR_LVL + 1; lvl <= end_pwrlvl; lvl++) { - psci_non_cpu_pd_nodes[parent_idx].local_state = - PSCI_LOCAL_STATE_RUN; -#if !USE_COHERENT_MEM - flush_dcache_range( - (uint64_t)&psci_non_cpu_pd_nodes[parent_idx], - sizeof(psci_non_cpu_pd_nodes[parent_idx])); -#endif - psci_set_req_local_pwr_state(lvl, - cpu_idx, - PSCI_LOCAL_STATE_RUN); - parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node; - } - - /* Set the affinity info state to ON */ - psci_set_aff_info_state(AFF_STATE_ON); - - psci_set_cpu_local_state(PSCI_LOCAL_STATE_RUN); - flush_cpu_data(psci_svc_cpu_data); -} - -/****************************************************************************** - * This function is passed the local power states requested for each power - * domain (state_info) between the current CPU domain and its ancestors until - * the target power level (end_pwrlvl). It updates the array of requested power - * states with this information. - * - * Then, for each level (apart from the CPU level) until the 'end_pwrlvl', it - * retrieves the states requested by all the cpus of which the power domain at - * that level is an ancestor. It passes this information to the platform to - * coordinate and return the target power state. If the target state for a level - * is RUN then subsequent levels are not considered. At the CPU level, state - * coordination is not required. Hence, the requested and the target states are - * the same. - * - * The 'state_info' is updated with the target state for each level between the - * CPU and the 'end_pwrlvl' and returned to the caller. - * - * This function will only be invoked with data cache enabled and while - * powering down a core. - *****************************************************************************/ -void psci_do_state_coordination(int end_pwrlvl, psci_power_state_t *state_info) -{ - unsigned int lvl, parent_idx, cpu_idx = plat_my_core_pos(); - unsigned int start_idx, ncpus; - plat_local_state_t target_state, *req_states; - - parent_idx = psci_cpu_pd_nodes[cpu_idx].parent_node; - - /* For level 0, the requested state will be equivalent - to target state */ - for (lvl = PSCI_CPU_PWR_LVL + 1; lvl <= end_pwrlvl; lvl++) { - - /* First update the requested power state */ - psci_set_req_local_pwr_state(lvl, cpu_idx, - state_info->pwr_domain_state[lvl]); - - /* Get the requested power states for this power level */ - start_idx = psci_non_cpu_pd_nodes[parent_idx].cpu_start_idx; - req_states = psci_get_req_local_pwr_states(lvl, start_idx); - - /* - * Let the platform coordinate amongst the requested states at - * this power level and return the target local power state. - */ - ncpus = psci_non_cpu_pd_nodes[parent_idx].ncpus; - target_state = plat_get_target_pwr_state(lvl, - req_states, - ncpus); - - state_info->pwr_domain_state[lvl] = target_state; - - /* Break early if the negotiated target power state is RUN */ - if (is_local_state_run(state_info->pwr_domain_state[lvl])) - break; - - parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node; - } - - /* - * This is for cases when we break out of the above loop early because - * the target power state is RUN at a power level < end_pwlvl. - * We update the requested power state from state_info and then - * set the target state as RUN. - */ - for (lvl = lvl + 1; lvl <= end_pwrlvl; lvl++) { - psci_set_req_local_pwr_state(lvl, cpu_idx, - state_info->pwr_domain_state[lvl]); - state_info->pwr_domain_state[lvl] = PSCI_LOCAL_STATE_RUN; - - } - - /* Update the target state in the power domain nodes */ - psci_set_target_local_pwr_states(end_pwrlvl, state_info); -} - -/****************************************************************************** - * This function validates a suspend request by making sure that if a standby - * state is requested then no power level is turned off and the highest power - * level is placed in a standby/retention state. - * - * It also ensures that the state level X will enter is not shallower than the - * state level X + 1 will enter. - * - * This validation will be enabled only for DEBUG builds as the platform is - * expected to perform these validations as well. - *****************************************************************************/ -int psci_validate_suspend_req(const psci_power_state_t *state_info, - unsigned int is_power_down_state) -{ - unsigned int max_off_lvl, target_lvl, max_retn_lvl; - plat_local_state_t state; - plat_local_state_type_t req_state_type, deepest_state_type; - int i; - - /* Find the target suspend power level */ - target_lvl = psci_find_target_suspend_lvl(state_info); - if (target_lvl == PSCI_INVALID_DATA) - return PSCI_E_INVALID_PARAMS; - - /* All power domain levels are in a RUN state to begin with */ - deepest_state_type = STATE_TYPE_RUN; - - for (i = target_lvl; i >= PSCI_CPU_PWR_LVL; i--) { - state = state_info->pwr_domain_state[i]; - req_state_type = find_local_state_type(state); - - /* - * While traversing from the highest power level to the lowest, - * the state requested for lower levels has to be the same or - * deeper i.e. equal to or greater than the state at the higher - * levels. If this condition is true, then the requested state - * becomes the deepest state encountered so far. - */ - if (req_state_type < deepest_state_type) - return PSCI_E_INVALID_PARAMS; - deepest_state_type = req_state_type; - } - - /* Find the highest off power level */ - max_off_lvl = psci_find_max_off_lvl(state_info); - - /* The target_lvl is either equal to the max_off_lvl or max_retn_lvl */ - max_retn_lvl = PSCI_INVALID_DATA; - if (target_lvl != max_off_lvl) - max_retn_lvl = target_lvl; - - /* - * If this is not a request for a power down state then max off level - * has to be invalid and max retention level has to be a valid power - * level. - */ - if (!is_power_down_state && (max_off_lvl != PSCI_INVALID_DATA || - max_retn_lvl == PSCI_INVALID_DATA)) - return PSCI_E_INVALID_PARAMS; - - return PSCI_E_SUCCESS; -} - -/****************************************************************************** - * This function finds the highest power level which will be powered down - * amongst all the power levels specified in the 'state_info' structure - *****************************************************************************/ -unsigned int psci_find_max_off_lvl(const psci_power_state_t *state_info) -{ - int i; - - for (i = PLAT_MAX_PWR_LVL; i >= PSCI_CPU_PWR_LVL; i--) { - if (is_local_state_off(state_info->pwr_domain_state[i])) - return i; - } - - return PSCI_INVALID_DATA; -} - -/****************************************************************************** - * This functions finds the level of the highest power domain which will be - * placed in a low power state during a suspend operation. - *****************************************************************************/ -unsigned int psci_find_target_suspend_lvl(const psci_power_state_t *state_info) -{ - int i; - - for (i = PLAT_MAX_PWR_LVL; i >= PSCI_CPU_PWR_LVL; i--) { - if (!is_local_state_run(state_info->pwr_domain_state[i])) - return i; - } - - return PSCI_INVALID_DATA; -} - -/******************************************************************************* - * This function is passed a cpu_index and the highest level in the topology - * tree that the operation should be applied to. It picks up locks in order of - * increasing power domain level in the range specified. - ******************************************************************************/ -void psci_acquire_pwr_domain_locks(int end_pwrlvl, unsigned int cpu_idx) -{ - unsigned int parent_idx = psci_cpu_pd_nodes[cpu_idx].parent_node; - int level; - - /* No locking required for level 0. Hence start locking from level 1 */ - for (level = PSCI_CPU_PWR_LVL + 1; level <= end_pwrlvl; level++) { - psci_lock_get(&psci_non_cpu_pd_nodes[parent_idx]); - parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node; - } -} - -/******************************************************************************* - * This function is passed a cpu_index and the highest level in the topology - * tree that the operation should be applied to. It releases the locks in order - * of decreasing power domain level in the range specified. - ******************************************************************************/ -void psci_release_pwr_domain_locks(int end_pwrlvl, unsigned int cpu_idx) -{ - unsigned int parent_idx, parent_nodes[PLAT_MAX_PWR_LVL] = {0}; - int level; - - /* Get the parent nodes */ - psci_get_parent_pwr_domain_nodes(cpu_idx, end_pwrlvl, parent_nodes); - - /* Unlock top down. No unlocking required for level 0. */ - for (level = end_pwrlvl; level >= PSCI_CPU_PWR_LVL + 1; level--) { - parent_idx = parent_nodes[level - 1]; - psci_lock_release(&psci_non_cpu_pd_nodes[parent_idx]); - } -} - -/******************************************************************************* - * Simple routine to determine whether a mpidr is valid or not. - ******************************************************************************/ -int psci_validate_mpidr(unsigned long mpidr) -{ - if (plat_core_pos_by_mpidr(mpidr) < 0) - return PSCI_E_INVALID_PARAMS; - - return PSCI_E_SUCCESS; -} - -/******************************************************************************* - * This function determines the full entrypoint information for the requested - * PSCI entrypoint on power on/resume and returns it. - ******************************************************************************/ -int psci_get_ns_ep_info(entry_point_info_t *ep, - uint64_t entrypoint, uint64_t context_id) -{ - uint32_t ep_attr, mode, sctlr, daif, ee; - uint32_t ns_scr_el3 = read_scr_el3(); - uint32_t ns_sctlr_el1 = read_sctlr_el1(); - - sctlr = ns_scr_el3 & SCR_HCE_BIT ? read_sctlr_el2() : ns_sctlr_el1; - ee = 0; - - ep_attr = NON_SECURE | EP_ST_DISABLE; - if (sctlr & SCTLR_EE_BIT) { - ep_attr |= EP_EE_BIG; - ee = 1; - } - SET_PARAM_HEAD(ep, PARAM_EP, VERSION_1, ep_attr); - - ep->pc = entrypoint; - memset(&ep->args, 0, sizeof(ep->args)); - ep->args.arg0 = context_id; - - /* - * Figure out whether the cpu enters the non-secure address space - * in aarch32 or aarch64 - */ - if (ns_scr_el3 & SCR_RW_BIT) { - - /* - * Check whether a Thumb entry point has been provided for an - * aarch64 EL - */ - if (entrypoint & 0x1) - return PSCI_E_INVALID_PARAMS; - - mode = ns_scr_el3 & SCR_HCE_BIT ? MODE_EL2 : MODE_EL1; - - ep->spsr = SPSR_64(mode, MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS); - } else { - - mode = ns_scr_el3 & SCR_HCE_BIT ? MODE32_hyp : MODE32_svc; - - /* - * TODO: Choose async. exception bits if HYP mode is not - * implemented according to the values of SCR.{AW, FW} bits - */ - daif = DAIF_ABT_BIT | DAIF_IRQ_BIT | DAIF_FIQ_BIT; - - ep->spsr = SPSR_MODE32(mode, entrypoint & 0x1, ee, daif); - } - - return PSCI_E_SUCCESS; -} - -/******************************************************************************* - * Generic handler which is called when a cpu is physically powered on. It - * traverses the node information and finds the highest power level powered - * off and performs generic, architectural, platform setup and state management - * to power on that power level and power levels below it. - * e.g. For a cpu that's been powered on, it will call the platform specific - * code to enable the gic cpu interface and for a cluster it will enable - * coherency at the interconnect level in addition to gic cpu interface. - ******************************************************************************/ -void psci_power_up_finish(void) -{ - unsigned int cpu_idx = plat_my_core_pos(); - psci_power_state_t state_info = { {PSCI_LOCAL_STATE_RUN} }; - int end_pwrlvl; - - /* - * Verify that we have been explicitly turned ON or resumed from - * suspend. - */ - if (psci_get_aff_info_state() == AFF_STATE_OFF) { - ERROR("Unexpected affinity info state"); - panic(); - } - - /* - * Get the maximum power domain level to traverse to after this cpu - * has been physically powered up. - */ - end_pwrlvl = get_power_on_target_pwrlvl(); - - /* - * This function acquires the lock corresponding to each power level so - * that by the time all locks are taken, the system topology is snapshot - * and state management can be done safely. - */ - psci_acquire_pwr_domain_locks(end_pwrlvl, - cpu_idx); - - psci_get_target_local_pwr_states(end_pwrlvl, &state_info); - - /* - * This CPU could be resuming from suspend or it could have just been - * turned on. To distinguish between these 2 cases, we examine the - * affinity state of the CPU: - * - If the affinity state is ON_PENDING then it has just been - * turned on. - * - Else it is resuming from suspend. - * - * Depending on the type of warm reset identified, choose the right set - * of power management handler and perform the generic, architecture - * and platform specific handling. - */ - if (psci_get_aff_info_state() == AFF_STATE_ON_PENDING) - psci_cpu_on_finish(cpu_idx, &state_info); - else - psci_cpu_suspend_finish(cpu_idx, &state_info); - - /* - * Set the requested and target state of this CPU and all the higher - * power domains which are ancestors of this CPU to run. - */ - psci_set_pwr_domains_to_run(end_pwrlvl); - - /* - * This loop releases the lock corresponding to each power level - * in the reverse order to which they were acquired. - */ - psci_release_pwr_domain_locks(end_pwrlvl, - cpu_idx); -} - -/******************************************************************************* - * This function initializes the set of hooks that PSCI invokes as part of power - * management operation. The power management hooks are expected to be provided - * by the SPD, after it finishes all its initialization - ******************************************************************************/ -void psci_register_spd_pm_hook(const spd_pm_ops_t *pm) -{ - assert(pm); - psci_spd_pm = pm; - - if (pm->svc_migrate) - psci_caps |= define_psci_cap(PSCI_MIG_AARCH64); - - if (pm->svc_migrate_info) - psci_caps |= define_psci_cap(PSCI_MIG_INFO_UP_CPU_AARCH64) - | define_psci_cap(PSCI_MIG_INFO_TYPE); -} - -/******************************************************************************* - * This function invokes the migrate info hook in the spd_pm_ops. It performs - * the necessary return value validation. If the Secure Payload is UP and - * migrate capable, it returns the mpidr of the CPU on which the Secure payload - * is resident through the mpidr parameter. Else the value of the parameter on - * return is undefined. - ******************************************************************************/ -int psci_spd_migrate_info(uint64_t *mpidr) -{ - int rc; - - if (!psci_spd_pm || !psci_spd_pm->svc_migrate_info) - return PSCI_E_NOT_SUPPORTED; - - rc = psci_spd_pm->svc_migrate_info(mpidr); - - assert(rc == PSCI_TOS_UP_MIG_CAP || rc == PSCI_TOS_NOT_UP_MIG_CAP \ - || rc == PSCI_TOS_NOT_PRESENT_MP || rc == PSCI_E_NOT_SUPPORTED); - - return rc; -} - - -/******************************************************************************* - * This function prints the state of all power domains present in the - * system - ******************************************************************************/ -void psci_print_power_domain_map(void) -{ -#if LOG_LEVEL >= LOG_LEVEL_INFO - unsigned int idx; - plat_local_state_t state; - plat_local_state_type_t state_type; - - /* This array maps to the PSCI_STATE_X definitions in psci.h */ - static const char *psci_state_type_str[] = { - "ON", - "RETENTION", - "OFF", - }; - - INFO("PSCI Power Domain Map:\n"); - for (idx = 0; idx < (PSCI_NUM_PWR_DOMAINS - PLATFORM_CORE_COUNT); - idx++) { - state_type = find_local_state_type( - psci_non_cpu_pd_nodes[idx].local_state); - INFO(" Domain Node : Level %u, parent_node %d," - " State %s (0x%x)\n", - psci_non_cpu_pd_nodes[idx].level, - psci_non_cpu_pd_nodes[idx].parent_node, - psci_state_type_str[state_type], - psci_non_cpu_pd_nodes[idx].local_state); - } - - for (idx = 0; idx < PLATFORM_CORE_COUNT; idx++) { - state = psci_get_cpu_local_state_by_idx(idx); - state_type = find_local_state_type(state); - INFO(" CPU Node : MPID 0x%lx, parent_node %d," - " State %s (0x%x)\n", - psci_cpu_pd_nodes[idx].mpidr, - psci_cpu_pd_nodes[idx].parent_node, - psci_state_type_str[state_type], - psci_get_cpu_local_state_by_idx(idx)); - } -#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 diff --git a/services/std_svc/psci1.0/psci_entry.S b/services/std_svc/psci1.0/psci_entry.S deleted file mode 100644 index 73c33779aa..0000000000 --- a/services/std_svc/psci1.0/psci_entry.S +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2013-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 - - .globl psci_entrypoint - .globl psci_power_down_wfi - - /* -------------------------------------------------------------------- - * This CPU has been physically powered up. It is either resuming from - * suspend or has simply been turned on. In both cases, call the power - * on finisher. - * -------------------------------------------------------------------- - */ -func psci_entrypoint - /* - * On the warm boot path, most of the EL3 initialisations performed by - * 'el3_entrypoint_common' must be skipped: - * - * - Only when the platform bypasses the BL1/BL3-1 entrypoint by - * programming the reset address do we need to set the CPU endianness. - * In other cases, we assume this has been taken care by the - * entrypoint code. - * - * - No need to determine the type of boot, we know it is a warm boot. - * - * - Do not try to distinguish between primary and secondary CPUs, this - * notion only exists for a cold boot. - * - * - No need to initialise the memory or the C runtime environment, - * it has been done once and for all on the cold boot path. - */ - el3_entrypoint_common \ - _set_endian=PROGRAMMABLE_RESET_ADDRESS \ - _warm_boot_mailbox=0 \ - _secondary_cold_boot=0 \ - _init_memory=0 \ - _init_c_runtime=0 \ - _exception_vectors=runtime_exceptions - - /* -------------------------------------------- - * Enable the MMU with the DCache disabled. It - * is safe to use stacks allocated in normal - * memory as a result. All memory accesses are - * marked nGnRnE when the MMU is disabled. So - * all the stack writes will make it to memory. - * All memory accesses are marked Non-cacheable - * when the MMU is enabled but D$ is disabled. - * So used stack memory is guaranteed to be - * visible immediately after the MMU is enabled - * Enabling the DCache at the same time as the - * MMU can lead to speculatively fetched and - * possibly stale stack memory being read from - * other caches. This can lead to coherency - * issues. - * -------------------------------------------- - */ - mov x0, #DISABLE_DCACHE - bl bl31_plat_enable_mmu - - bl psci_power_up_finish - - b el3_exit -endfunc psci_entrypoint - - /* -------------------------------------------- - * This function is called to indicate to the - * power controller that it is safe to power - * down this cpu. It should not exit the wfi - * and will be released from reset upon power - * up. 'wfi_spill' is used to catch erroneous - * exits from wfi. - * -------------------------------------------- - */ -func psci_power_down_wfi - dsb sy // ensure write buffer empty - wfi -wfi_spill: - b wfi_spill -endfunc psci_power_down_wfi - diff --git a/services/std_svc/psci1.0/psci_helpers.S b/services/std_svc/psci1.0/psci_helpers.S deleted file mode 100644 index bbfa5d5d7b..0000000000 --- a/services/std_svc/psci1.0/psci_helpers.S +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (c) 2014-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 psci_do_pwrdown_cache_maintenance - .globl psci_do_pwrup_cache_maintenance - -/* ----------------------------------------------------------------------- - * void psci_do_pwrdown_cache_maintenance(uint32_t power level); - * - * This function performs cache maintenance for the specified power - * level. The levels of cache affected are determined by the power - * level which is passed as the argument i.e. level 0 results - * in a flush of the L1 cache. Both the L1 and L2 caches are flushed - * for a higher power level. - * - * Additionally, this function also ensures that stack memory is correctly - * flushed out to avoid coherency issues due to a change in its memory - * attributes after the data cache is disabled. - * ----------------------------------------------------------------------- - */ -func psci_do_pwrdown_cache_maintenance - stp x29, x30, [sp,#-16]! - stp x19, x20, [sp,#-16]! - - /* --------------------------------------------- - * Determine to how many levels of cache will be - * subject to cache maintenance. Power level - * 0 implies that only the cpu is being powered - * down. Only the L1 data cache needs to be - * flushed to the PoU in this case. For a higher - * power level we are assuming that a flush - * of L1 data and L2 unified cache is enough. - * This information should be provided by the - * platform. - * --------------------------------------------- - */ - cmp x0, #PSCI_CPU_PWR_LVL - b.eq do_core_pwr_dwn - bl prepare_cluster_pwr_dwn - b do_stack_maintenance - -do_core_pwr_dwn: - bl prepare_core_pwr_dwn - - /* --------------------------------------------- - * Do stack maintenance by flushing the used - * stack to the main memory and invalidating the - * remainder. - * --------------------------------------------- - */ -do_stack_maintenance: - bl plat_get_my_stack - - /* --------------------------------------------- - * Calculate and store the size of the used - * stack memory in x1. - * --------------------------------------------- - */ - mov x19, x0 - mov x1, sp - sub x1, x0, x1 - mov x0, sp - bl flush_dcache_range - - /* --------------------------------------------- - * Calculate and store the size of the unused - * stack memory in x1. Calculate and store the - * stack base address in x0. - * --------------------------------------------- - */ - sub x0, x19, #PLATFORM_STACK_SIZE - sub x1, sp, x0 - bl inv_dcache_range - - ldp x19, x20, [sp], #16 - ldp x29, x30, [sp], #16 - ret -endfunc psci_do_pwrdown_cache_maintenance - - -/* ----------------------------------------------------------------------- - * void psci_do_pwrup_cache_maintenance(void); - * - * This function performs cache maintenance after this cpu is powered up. - * Currently, this involves managing the used stack memory before turning - * on the data cache. - * ----------------------------------------------------------------------- - */ -func psci_do_pwrup_cache_maintenance - stp x29, x30, [sp,#-16]! - - /* --------------------------------------------- - * Ensure any inflight stack writes have made it - * to main memory. - * --------------------------------------------- - */ - dmb st - - /* --------------------------------------------- - * Calculate and store the size of the used - * stack memory in x1. Calculate and store the - * stack base address in x0. - * --------------------------------------------- - */ - bl plat_get_my_stack - mov x1, sp - sub x1, x0, x1 - mov x0, sp - bl inv_dcache_range - - /* --------------------------------------------- - * Enable the data cache. - * --------------------------------------------- - */ - mrs x0, sctlr_el3 - orr x0, x0, #SCTLR_C_BIT - msr sctlr_el3, x0 - isb - - ldp x29, x30, [sp], #16 - ret -endfunc psci_do_pwrup_cache_maintenance diff --git a/services/std_svc/psci1.0/psci_main.c b/services/std_svc/psci1.0/psci_main.c deleted file mode 100644 index f024291022..0000000000 --- a/services/std_svc/psci1.0/psci_main.c +++ /dev/null @@ -1,447 +0,0 @@ -/* - * Copyright (c) 2013-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 -#include -#include -#include -#include "psci_private.h" - -/******************************************************************************* - * PSCI frontend api for servicing SMCs. Described in the PSCI spec. - ******************************************************************************/ -int psci_cpu_on(unsigned long target_cpu, - unsigned long entrypoint, - unsigned long context_id) - -{ - int rc; - unsigned int end_pwrlvl; - entry_point_info_t ep; - - /* Determine if the cpu exists of not */ - rc = psci_validate_mpidr(target_cpu); - if (rc != PSCI_E_SUCCESS) - return PSCI_E_INVALID_PARAMS; - - /* Validate the entrypoint using platform pm_ops */ - if (psci_plat_pm_ops->validate_ns_entrypoint) { - rc = psci_plat_pm_ops->validate_ns_entrypoint(entrypoint); - if (rc != PSCI_E_SUCCESS) { - assert(rc == PSCI_E_INVALID_PARAMS); - return PSCI_E_INVALID_PARAMS; - } - } - - /* - * Verify and derive the re-entry information for - * the non-secure world from the non-secure state from - * where this call originated. - */ - rc = psci_get_ns_ep_info(&ep, entrypoint, context_id); - if (rc != PSCI_E_SUCCESS) - return rc; - - /* - * To turn this cpu on, specify which power - * levels need to be turned on - */ - end_pwrlvl = PLAT_MAX_PWR_LVL; - rc = psci_cpu_on_start(target_cpu, - &ep, - end_pwrlvl); - return rc; -} - -unsigned int psci_version(void) -{ - return PSCI_MAJOR_VER | PSCI_MINOR_VER; -} - -int psci_cpu_suspend(unsigned int power_state, - unsigned long entrypoint, - unsigned long context_id) -{ - int rc; - unsigned int target_pwrlvl, is_power_down_state; - entry_point_info_t ep; - psci_power_state_t state_info = { {PSCI_LOCAL_STATE_RUN} }; - plat_local_state_t cpu_pd_state; - - /* Validate the power_state parameter */ - rc = psci_validate_power_state(power_state, &state_info); - if (rc != PSCI_E_SUCCESS) { - assert(rc == PSCI_E_INVALID_PARAMS); - return rc; - } - - /* - * Get the value of the state type bit from the power state parameter. - */ - is_power_down_state = psci_get_pstate_type(power_state); - - /* Sanity check the requested suspend levels */ - assert (psci_validate_suspend_req(&state_info, is_power_down_state) - == PSCI_E_SUCCESS); - - target_pwrlvl = psci_find_target_suspend_lvl(&state_info); - - /* Fast path for CPU standby.*/ - if (is_cpu_standby_req(is_power_down_state, target_pwrlvl)) { - if (!psci_plat_pm_ops->cpu_standby) - return PSCI_E_INVALID_PARAMS; - - /* - * Set the state of the CPU power domain to the platform - * specific retention state and enter the standby state. - */ - cpu_pd_state = state_info.pwr_domain_state[PSCI_CPU_PWR_LVL]; - psci_set_cpu_local_state(cpu_pd_state); - psci_plat_pm_ops->cpu_standby(cpu_pd_state); - - /* Upon exit from standby, set the state back to RUN. */ - psci_set_cpu_local_state(PSCI_LOCAL_STATE_RUN); - - return PSCI_E_SUCCESS; - } - - /* - * If a power down state has been requested, we need to verify entry - * point and program entry information. - */ - if (is_power_down_state) { - if (psci_plat_pm_ops->validate_ns_entrypoint) { - rc = psci_plat_pm_ops->validate_ns_entrypoint(entrypoint); - if (rc != PSCI_E_SUCCESS) { - assert(rc == PSCI_E_INVALID_PARAMS); - return rc; - } - } - - /* - * Verify and derive the re-entry information for - * the non-secure world from the non-secure state from - * where this call originated. - */ - rc = psci_get_ns_ep_info(&ep, entrypoint, context_id); - if (rc != PSCI_E_SUCCESS) - return rc; - } - - /* - * Do what is needed to enter the power down state. Upon success, - * enter the final wfi which will power down this CPU. This function - * might return if the power down was abandoned for any reason, e.g. - * arrival of an interrupt - */ - psci_cpu_suspend_start(&ep, - target_pwrlvl, - &state_info, - is_power_down_state); - - return PSCI_E_SUCCESS; -} - -int psci_system_suspend(unsigned long entrypoint, - unsigned long context_id) -{ - int rc; - psci_power_state_t state_info; - entry_point_info_t ep; - - /* Validate the entrypoint using platform pm_ops */ - if (psci_plat_pm_ops->validate_ns_entrypoint) { - rc = psci_plat_pm_ops->validate_ns_entrypoint(entrypoint); - if (rc != PSCI_E_SUCCESS) { - assert(rc == PSCI_E_INVALID_PARAMS); - return rc; - } - } - - /* Check if the current CPU is the last ON CPU in the system */ - if (!psci_is_last_on_cpu()) - return PSCI_E_DENIED; - - /* - * Verify and derive the re-entry information for - * the non-secure world from the non-secure state from - * where this call originated. - */ - rc = psci_get_ns_ep_info(&ep, entrypoint, context_id); - if (rc != PSCI_E_SUCCESS) - return rc; - - /* Query the psci_power_state for system suspend */ - psci_query_sys_suspend_pwrstate(&state_info); - - /* Ensure that the psci_power_state makes sense */ - assert(psci_find_target_suspend_lvl(&state_info) == PLAT_MAX_PWR_LVL); - assert(psci_validate_suspend_req(&state_info, PSTATE_TYPE_POWERDOWN) - == PSCI_E_SUCCESS); - assert(is_local_state_off(state_info.pwr_domain_state[PLAT_MAX_PWR_LVL])); - - /* - * Do what is needed to enter the system suspend state. This function - * might return if the power down was abandoned for any reason, e.g. - * arrival of an interrupt - */ - psci_cpu_suspend_start(&ep, - PLAT_MAX_PWR_LVL, - &state_info, - PSTATE_TYPE_POWERDOWN); - - return PSCI_E_SUCCESS; -} - -int psci_cpu_off(void) -{ - int rc; - int target_pwrlvl = PLAT_MAX_PWR_LVL; - - /* - * Do what is needed to power off this CPU and possible higher power - * levels if it able to do so. Upon success, enter the final wfi - * which will power down this CPU. - */ - rc = psci_do_cpu_off(target_pwrlvl); - - /* - * The only error cpu_off can return is E_DENIED. So check if that's - * indeed the case. - */ - assert (rc == PSCI_E_DENIED); - - return rc; -} - -int psci_affinity_info(unsigned long target_affinity, - unsigned int lowest_affinity_level) -{ - unsigned int target_idx; - - /* We dont support level higher than PSCI_CPU_PWR_LVL */ - if (lowest_affinity_level > PSCI_CPU_PWR_LVL) - return PSCI_E_INVALID_PARAMS; - - /* Calculate the cpu index of the target */ - target_idx = plat_core_pos_by_mpidr(target_affinity); - if (target_idx == -1) - return PSCI_E_INVALID_PARAMS; - - return psci_get_aff_info_state_by_idx(target_idx); -} - -int psci_migrate(unsigned long target_cpu) -{ - int rc; - unsigned long resident_cpu_mpidr; - - rc = psci_spd_migrate_info(&resident_cpu_mpidr); - if (rc != PSCI_TOS_UP_MIG_CAP) - return (rc == PSCI_TOS_NOT_UP_MIG_CAP) ? - PSCI_E_DENIED : PSCI_E_NOT_SUPPORTED; - - /* - * Migrate should only be invoked on the CPU where - * the Secure OS is resident. - */ - if (resident_cpu_mpidr != read_mpidr_el1()) - return PSCI_E_NOT_PRESENT; - - /* Check the validity of the specified target cpu */ - rc = psci_validate_mpidr(target_cpu); - if (rc != PSCI_E_SUCCESS) - return PSCI_E_INVALID_PARAMS; - - assert(psci_spd_pm && psci_spd_pm->svc_migrate); - - rc = psci_spd_pm->svc_migrate(read_mpidr_el1(), target_cpu); - assert(rc == PSCI_E_SUCCESS || rc == PSCI_E_INTERN_FAIL); - - return rc; -} - -int psci_migrate_info_type(void) -{ - unsigned long resident_cpu_mpidr; - - return psci_spd_migrate_info(&resident_cpu_mpidr); -} - -long psci_migrate_info_up_cpu(void) -{ - unsigned long resident_cpu_mpidr; - int rc; - - /* - * Return value of this depends upon what - * psci_spd_migrate_info() returns. - */ - rc = psci_spd_migrate_info(&resident_cpu_mpidr); - if (rc != PSCI_TOS_NOT_UP_MIG_CAP && rc != PSCI_TOS_UP_MIG_CAP) - return PSCI_E_INVALID_PARAMS; - - return resident_cpu_mpidr; -} - -int psci_features(unsigned int psci_fid) -{ - uint32_t local_caps = psci_caps; - - /* Check if it is a 64 bit function */ - if (((psci_fid >> FUNCID_CC_SHIFT) & FUNCID_CC_MASK) == SMC_64) - local_caps &= PSCI_CAP_64BIT_MASK; - - /* Check for invalid fid */ - if (!(is_std_svc_call(psci_fid) && is_valid_fast_smc(psci_fid) - && is_psci_fid(psci_fid))) - return PSCI_E_NOT_SUPPORTED; - - - /* Check if the psci fid is supported or not */ - if (!(local_caps & define_psci_cap(psci_fid))) - return PSCI_E_NOT_SUPPORTED; - - /* Format the feature flags */ - if (psci_fid == PSCI_CPU_SUSPEND_AARCH32 || - psci_fid == PSCI_CPU_SUSPEND_AARCH64) { - /* - * The trusted firmware does not support OS Initiated Mode. - */ - return (FF_PSTATE << FF_PSTATE_SHIFT) | - ((!FF_SUPPORTS_OS_INIT_MODE) << FF_MODE_SUPPORT_SHIFT); - } - - /* Return 0 for all other fid's */ - return PSCI_E_SUCCESS; -} - -/******************************************************************************* - * PSCI top level handler for servicing SMCs. - ******************************************************************************/ -uint64_t psci_smc_handler(uint32_t smc_fid, - uint64_t x1, - uint64_t x2, - uint64_t x3, - uint64_t x4, - void *cookie, - void *handle, - uint64_t flags) -{ - if (is_caller_secure(flags)) - SMC_RET1(handle, SMC_UNK); - - /* Check the fid against the capabilities */ - if (!(psci_caps & define_psci_cap(smc_fid))) - SMC_RET1(handle, SMC_UNK); - - if (((smc_fid >> FUNCID_CC_SHIFT) & FUNCID_CC_MASK) == SMC_32) { - /* 32-bit PSCI function, clear top parameter bits */ - - x1 = (uint32_t)x1; - x2 = (uint32_t)x2; - x3 = (uint32_t)x3; - - switch (smc_fid) { - case PSCI_VERSION: - SMC_RET1(handle, psci_version()); - - case PSCI_CPU_OFF: - SMC_RET1(handle, psci_cpu_off()); - - case PSCI_CPU_SUSPEND_AARCH32: - SMC_RET1(handle, psci_cpu_suspend(x1, x2, x3)); - - case PSCI_CPU_ON_AARCH32: - SMC_RET1(handle, psci_cpu_on(x1, x2, x3)); - - case PSCI_AFFINITY_INFO_AARCH32: - SMC_RET1(handle, psci_affinity_info(x1, x2)); - - case PSCI_MIG_AARCH32: - SMC_RET1(handle, psci_migrate(x1)); - - case PSCI_MIG_INFO_TYPE: - SMC_RET1(handle, psci_migrate_info_type()); - - case PSCI_MIG_INFO_UP_CPU_AARCH32: - SMC_RET1(handle, psci_migrate_info_up_cpu()); - - case PSCI_SYSTEM_SUSPEND_AARCH32: - SMC_RET1(handle, psci_system_suspend(x1, x2)); - - case PSCI_SYSTEM_OFF: - psci_system_off(); - /* We should never return from psci_system_off() */ - - case PSCI_SYSTEM_RESET: - psci_system_reset(); - /* We should never return from psci_system_reset() */ - - case PSCI_FEATURES: - SMC_RET1(handle, psci_features(x1)); - - default: - break; - } - } else { - /* 64-bit PSCI function */ - - switch (smc_fid) { - case PSCI_CPU_SUSPEND_AARCH64: - SMC_RET1(handle, psci_cpu_suspend(x1, x2, x3)); - - case PSCI_CPU_ON_AARCH64: - SMC_RET1(handle, psci_cpu_on(x1, x2, x3)); - - case PSCI_AFFINITY_INFO_AARCH64: - SMC_RET1(handle, psci_affinity_info(x1, x2)); - - case PSCI_MIG_AARCH64: - SMC_RET1(handle, psci_migrate(x1)); - - case PSCI_MIG_INFO_UP_CPU_AARCH64: - SMC_RET1(handle, psci_migrate_info_up_cpu()); - - case PSCI_SYSTEM_SUSPEND_AARCH64: - SMC_RET1(handle, psci_system_suspend(x1, x2)); - - default: - break; - } - } - - WARN("Unimplemented PSCI Call: 0x%x \n", smc_fid); - SMC_RET1(handle, SMC_UNK); -} diff --git a/services/std_svc/psci1.0/psci_private.h b/services/std_svc/psci1.0/psci_private.h deleted file mode 100644 index e2e32c7962..0000000000 --- a/services/std_svc/psci1.0/psci_private.h +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright (c) 2013-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_PRIVATE_H__ -#define __PSCI_PRIVATE_H__ - -#include -#include -#include -#include -#include -#include - -/* - * The following helper macros abstract the interface to the Bakery - * Lock API. - */ -#if USE_COHERENT_MEM -#define psci_lock_init(non_cpu_pd_node, idx) \ - bakery_lock_init(&(non_cpu_pd_node)[(idx)].lock) -#define psci_lock_get(non_cpu_pd_node) \ - bakery_lock_get(&((non_cpu_pd_node)->lock)) -#define psci_lock_release(non_cpu_pd_node) \ - bakery_lock_release(&((non_cpu_pd_node)->lock)) -#else -#define psci_lock_init(non_cpu_pd_node, idx) \ - ((non_cpu_pd_node)[(idx)].lock_index = (idx)) -#define psci_lock_get(non_cpu_pd_node) \ - bakery_lock_get((non_cpu_pd_node)->lock_index, \ - CPU_DATA_PSCI_LOCK_OFFSET) -#define psci_lock_release(non_cpu_pd_node) \ - bakery_lock_release((non_cpu_pd_node)->lock_index, \ - CPU_DATA_PSCI_LOCK_OFFSET) -#endif - -/* - * The PSCI capability which are provided by the generic code but does not - * depend on the platform or spd capabilities. - */ -#define PSCI_GENERIC_CAP \ - (define_psci_cap(PSCI_VERSION) | \ - define_psci_cap(PSCI_AFFINITY_INFO_AARCH64) | \ - define_psci_cap(PSCI_FEATURES)) - -/* - * The PSCI capabilities mask for 64 bit functions. - */ -#define PSCI_CAP_64BIT_MASK \ - (define_psci_cap(PSCI_CPU_SUSPEND_AARCH64) | \ - define_psci_cap(PSCI_CPU_ON_AARCH64) | \ - define_psci_cap(PSCI_AFFINITY_INFO_AARCH64) | \ - define_psci_cap(PSCI_MIG_AARCH64) | \ - define_psci_cap(PSCI_MIG_INFO_UP_CPU_AARCH64) | \ - define_psci_cap(PSCI_SYSTEM_SUSPEND_AARCH64)) - -/* - * Helper macros to get/set the fields of PSCI per-cpu data. - */ -#define psci_set_aff_info_state(aff_state) \ - set_cpu_data(psci_svc_cpu_data.aff_info_state, aff_state) -#define psci_get_aff_info_state() \ - get_cpu_data(psci_svc_cpu_data.aff_info_state) -#define psci_get_aff_info_state_by_idx(idx) \ - get_cpu_data_by_index(idx, psci_svc_cpu_data.aff_info_state) -#define psci_get_suspend_pwrlvl() \ - get_cpu_data(psci_svc_cpu_data.target_pwrlvl) -#define psci_set_suspend_pwrlvl(target_lvl) \ - set_cpu_data(psci_svc_cpu_data.target_pwrlvl, target_lvl) -#define psci_set_cpu_local_state(state) \ - set_cpu_data(psci_svc_cpu_data.local_state, state) -#define psci_get_cpu_local_state() \ - get_cpu_data(psci_svc_cpu_data.local_state) -#define psci_get_cpu_local_state_by_idx(idx) \ - get_cpu_data_by_index(idx, psci_svc_cpu_data.local_state) - -/* - * Helper macros for the CPU level spinlocks - */ -#define psci_spin_lock_cpu(idx) spin_lock(&psci_cpu_pd_nodes[idx].cpu_lock) -#define psci_spin_unlock_cpu(idx) spin_unlock(&psci_cpu_pd_nodes[idx].cpu_lock) - -/* Helper macro to identify a CPU standby request in PSCI Suspend call */ -#define is_cpu_standby_req(is_power_down_state, retn_lvl) \ - (((!(is_power_down_state)) && ((retn_lvl) == 0)) ? 1 : 0) - -/******************************************************************************* - * The following two data structures implement the power domain tree. The tree - * is used to track the state of all the nodes i.e. power domain instances - * described by the platform. The tree consists of nodes that describe CPU power - * domains i.e. leaf nodes and all other power domains which are parents of a - * CPU power domain i.e. non-leaf nodes. - ******************************************************************************/ -typedef struct non_cpu_pwr_domain_node { - /* - * Index of the first CPU power domain node level 0 which has this node - * as its parent. - */ - unsigned int cpu_start_idx; - - /* - * Number of CPU power domains which are siblings of the domain indexed - * by 'cpu_start_idx' i.e. all the domains in the range 'cpu_start_idx - * -> cpu_start_idx + ncpus' have this node as their parent. - */ - unsigned int ncpus; - - /* - * Index of the parent power domain node. - * TODO: Figure out whether to whether using pointer is more efficient. - */ - unsigned int parent_node; - - plat_local_state_t local_state; - - unsigned char level; -#if USE_COHERENT_MEM - bakery_lock_t lock; -#else - /* For indexing the bakery_info array in per CPU data */ - unsigned char lock_index; -#endif -} non_cpu_pd_node_t; - -typedef struct cpu_pwr_domain_node { - unsigned long mpidr; - - /* - * Index of the parent power domain node. - * TODO: Figure out whether to whether using pointer is more efficient. - */ - unsigned int parent_node; - - /* - * A CPU power domain does not require state coordination like its - * parent power domains. Hence this node does not include a bakery - * lock. A spinlock is required by the CPU_ON handler to prevent a race - * when multiple CPUs try to turn ON the same target CPU. - */ - spinlock_t cpu_lock; -} cpu_pd_node_t; - -/******************************************************************************* - * Data prototypes - ******************************************************************************/ -extern const plat_psci_ops_t *psci_plat_pm_ops; -extern non_cpu_pd_node_t psci_non_cpu_pd_nodes[PSCI_NUM_NON_CPU_PWR_DOMAINS]; -extern cpu_pd_node_t psci_cpu_pd_nodes[PLATFORM_CORE_COUNT]; -extern uint32_t psci_caps; - -/******************************************************************************* - * SPD's power management hooks registered with PSCI - ******************************************************************************/ -extern const spd_pm_ops_t *psci_spd_pm; - -/******************************************************************************* - * Function prototypes - ******************************************************************************/ -/* Private exported functions from psci_common.c */ -int psci_validate_power_state(unsigned int power_state, - psci_power_state_t *state_info); -void psci_query_sys_suspend_pwrstate(psci_power_state_t *state_info); -int psci_validate_mpidr(unsigned long mpidr); -void psci_init_req_local_pwr_states(void); -void psci_power_up_finish(void); -int psci_get_ns_ep_info(entry_point_info_t *ep, - uint64_t entrypoint, uint64_t context_id); -void psci_get_parent_pwr_domain_nodes(unsigned int cpu_idx, - int end_lvl, - unsigned int node_index[]); -void psci_do_state_coordination(int end_pwrlvl, - psci_power_state_t *state_info); -void psci_acquire_pwr_domain_locks(int end_pwrlvl, - unsigned int cpu_idx); -void psci_release_pwr_domain_locks(int end_pwrlvl, - unsigned int cpu_idx); -int psci_validate_suspend_req(const psci_power_state_t *state_info, - unsigned int is_power_down_state_req); -unsigned int psci_find_max_off_lvl(const psci_power_state_t *state_info); -unsigned int psci_find_target_suspend_lvl(const psci_power_state_t *state_info); -void psci_set_pwr_domains_to_run(uint32_t end_pwrlvl); -void psci_print_power_domain_map(void); -unsigned int psci_is_last_on_cpu(void); -int psci_spd_migrate_info(uint64_t *mpidr); - -/* Private exported functions from psci_on.c */ -int psci_cpu_on_start(unsigned long target_cpu, - entry_point_info_t *ep, - int end_pwrlvl); - -void psci_cpu_on_finish(unsigned int cpu_idx, - psci_power_state_t *state_info); - -/* Private exported functions from psci_cpu_off.c */ -int psci_do_cpu_off(int end_pwrlvl); - -/* Private exported functions from psci_pwrlvl_suspend.c */ -void psci_cpu_suspend_start(entry_point_info_t *ep, - int end_pwrlvl, - psci_power_state_t *state_info, - unsigned int is_power_down_state_req); - -void psci_cpu_suspend_finish(unsigned int cpu_idx, - psci_power_state_t *state_info); - -/* Private exported functions from psci_helpers.S */ -void psci_do_pwrdown_cache_maintenance(uint32_t pwr_level); -void psci_do_pwrup_cache_maintenance(void); - -/* Private exported functions from psci_system_off.c */ -void __dead2 psci_system_off(void); -void __dead2 psci_system_reset(void); - -#endif /* __PSCI_PRIVATE_H__ */ diff --git a/services/std_svc/psci1.0/psci_setup.c b/services/std_svc/psci1.0/psci_setup.c deleted file mode 100644 index ce4da9599f..0000000000 --- a/services/std_svc/psci1.0/psci_setup.c +++ /dev/null @@ -1,272 +0,0 @@ -/* - * Copyright (c) 2013-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 -#include -#include -#include -#include "psci_private.h" - -/******************************************************************************* - * Per cpu non-secure contexts used to program the architectural state prior - * return to the normal world. - * TODO: Use the memory allocator to set aside memory for the contexts instead - * of relying on platform defined constants. - ******************************************************************************/ -static cpu_context_t psci_ns_context[PLATFORM_CORE_COUNT]; - -/****************************************************************************** - * Define the psci capability variable. - *****************************************************************************/ -uint32_t psci_caps; - -/******************************************************************************* - * Function which initializes the 'psci_non_cpu_pd_nodes' or the - * 'psci_cpu_pd_nodes' corresponding to the power level. - ******************************************************************************/ -static void psci_init_pwr_domain_node(int node_idx, int parent_idx, int level) -{ - if (level > PSCI_CPU_PWR_LVL) { - psci_non_cpu_pd_nodes[node_idx].level = level; - psci_lock_init(psci_non_cpu_pd_nodes, node_idx); - psci_non_cpu_pd_nodes[node_idx].parent_node = parent_idx; - psci_non_cpu_pd_nodes[node_idx].local_state = - PLAT_MAX_OFF_STATE; - } else { - psci_cpu_data_t *svc_cpu_data; - - psci_cpu_pd_nodes[node_idx].parent_node = parent_idx; - - /* Initialize with an invalid mpidr */ - psci_cpu_pd_nodes[node_idx].mpidr = PSCI_INVALID_MPIDR; - - svc_cpu_data = - &(_cpu_data_by_index(node_idx)->psci_svc_cpu_data); - - /* Set the Affinity Info for the cores as OFF */ - svc_cpu_data->aff_info_state = AFF_STATE_OFF; - - /* Invalidate the suspend level for the cpu */ - svc_cpu_data->target_pwrlvl = PSCI_INVALID_DATA; - - /* Set the power state to OFF state */ - svc_cpu_data->local_state = PLAT_MAX_OFF_STATE; - - flush_dcache_range((uint64_t)svc_cpu_data, - sizeof(*svc_cpu_data)); - - cm_set_context_by_index(node_idx, - (void *) &psci_ns_context[node_idx], - NON_SECURE); - } -} - -/******************************************************************************* - * This functions updates cpu_start_idx and ncpus field for each of the node in - * psci_non_cpu_pd_nodes[]. It does so by comparing the parent nodes of each of - * the CPUs and check whether they match with the parent of the previous - * CPU. The basic assumption for this work is that children of the same parent - * are allocated adjacent indices. The platform should ensure this though proper - * mapping of the CPUs to indices via plat_core_pos_by_mpidr() and - * plat_my_core_pos() APIs. - *******************************************************************************/ -static void psci_update_pwrlvl_limits(void) -{ - int cpu_idx, j; - unsigned int nodes_idx[PLAT_MAX_PWR_LVL] = {0}; - unsigned int temp_index[PLAT_MAX_PWR_LVL]; - - for (cpu_idx = 0; cpu_idx < PLATFORM_CORE_COUNT; cpu_idx++) { - psci_get_parent_pwr_domain_nodes(cpu_idx, - PLAT_MAX_PWR_LVL, - temp_index); - for (j = PLAT_MAX_PWR_LVL - 1; j >= 0; j--) { - if (temp_index[j] != nodes_idx[j]) { - nodes_idx[j] = temp_index[j]; - psci_non_cpu_pd_nodes[nodes_idx[j]].cpu_start_idx - = cpu_idx; - } - psci_non_cpu_pd_nodes[nodes_idx[j]].ncpus++; - } - } -} - -/******************************************************************************* - * Core routine to populate the power domain tree. The tree descriptor passed by - * the platform is populated breadth-first and the first entry in the map - * informs the number of root power domains. The parent nodes of the root nodes - * will point to an invalid entry(-1). - ******************************************************************************/ -static void populate_power_domain_tree(const unsigned char *topology) -{ - unsigned int i, j = 0, num_nodes_at_lvl = 1, num_nodes_at_next_lvl; - unsigned int node_index = 0, parent_node_index = 0, num_children; - int level = PLAT_MAX_PWR_LVL; - - /* - * For each level the inputs are: - * - number of nodes at this level in plat_array i.e. num_nodes_at_level - * This is the sum of values of nodes at the parent level. - * - Index of first entry at this level in the plat_array i.e. - * parent_node_index. - * - Index of first free entry in psci_non_cpu_pd_nodes[] or - * psci_cpu_pd_nodes[] i.e. node_index depending upon the level. - */ - while (level >= PSCI_CPU_PWR_LVL) { - num_nodes_at_next_lvl = 0; - /* - * For each entry (parent node) at this level in the plat_array: - * - Find the number of children - * - Allocate a node in a power domain array for each child - * - Set the parent of the child to the parent_node_index - 1 - * - Increment parent_node_index to point to the next parent - * - Accumulate the number of children at next level. - */ - for (i = 0; i < num_nodes_at_lvl; i++) { - assert(parent_node_index <= - PSCI_NUM_NON_CPU_PWR_DOMAINS); - num_children = topology[parent_node_index]; - - for (j = node_index; - j < node_index + num_children; j++) - psci_init_pwr_domain_node(j, - parent_node_index - 1, - level); - - node_index = j; - num_nodes_at_next_lvl += num_children; - parent_node_index++; - } - - num_nodes_at_lvl = num_nodes_at_next_lvl; - level--; - - /* Reset the index for the cpu power domain array */ - if (level == PSCI_CPU_PWR_LVL) - node_index = 0; - } - - /* Validate the sanity of array exported by the platform */ - assert(j == PLATFORM_CORE_COUNT); - -#if !USE_COHERENT_MEM - /* Flush the non CPU power domain data to memory */ - flush_dcache_range((uint64_t) &psci_non_cpu_pd_nodes, - sizeof(psci_non_cpu_pd_nodes)); -#endif -} - -/******************************************************************************* - * This function initializes the power domain topology tree by querying the - * platform. The power domain nodes higher than the CPU are populated in the - * array psci_non_cpu_pd_nodes[] and the CPU power domains are populated in - * psci_cpu_pd_nodes[]. The platform exports its static topology map through the - * populate_power_domain_topology_tree() API. The algorithm populates the - * psci_non_cpu_pd_nodes and psci_cpu_pd_nodes iteratively by using this - * topology map. On a platform that implements two clusters of 2 cpus each, and - * supporting 3 domain levels, the populated psci_non_cpu_pd_nodes would look - * like this: - * - * --------------------------------------------------- - * | system node | cluster 0 node | cluster 1 node | - * --------------------------------------------------- - * - * And populated psci_cpu_pd_nodes would look like this : - * <- cpus cluster0 -><- cpus cluster1 -> - * ------------------------------------------------ - * | CPU 0 | CPU 1 | CPU 2 | CPU 3 | - * ------------------------------------------------ - ******************************************************************************/ -int32_t psci_setup(void) -{ - const unsigned char *topology_tree; - - /* Query the topology map from the platform */ - topology_tree = plat_get_power_domain_tree_desc(); - - /* Populate the power domain arrays using the platform topology map */ - populate_power_domain_tree(topology_tree); - - /* Update the CPU limits for each node in psci_non_cpu_pd_nodes */ - psci_update_pwrlvl_limits(); - - /* Populate the mpidr field of cpu node for this CPU */ - psci_cpu_pd_nodes[plat_my_core_pos()].mpidr = - read_mpidr() & MPIDR_AFFINITY_MASK; - -#if !USE_COHERENT_MEM - /* - * The psci_non_cpu_pd_nodes only needs flushing when it's not allocated in - * coherent memory. - */ - flush_dcache_range((uint64_t) &psci_non_cpu_pd_nodes, - sizeof(psci_non_cpu_pd_nodes)); -#endif - - flush_dcache_range((uint64_t) &psci_cpu_pd_nodes, - sizeof(psci_cpu_pd_nodes)); - - psci_init_req_local_pwr_states(); - - /* - * Set the requested and target state of this CPU and all the higher - * power domain levels for this CPU to run. - */ - psci_set_pwr_domains_to_run(PLAT_MAX_PWR_LVL); - - plat_setup_psci_ops((uintptr_t)psci_entrypoint, - &psci_plat_pm_ops); - assert(psci_plat_pm_ops); - - /* Initialize the psci capability */ - psci_caps = PSCI_GENERIC_CAP; - - if (psci_plat_pm_ops->pwr_domain_off) - psci_caps |= define_psci_cap(PSCI_CPU_OFF); - if (psci_plat_pm_ops->pwr_domain_on && - psci_plat_pm_ops->pwr_domain_on_finish) - psci_caps |= define_psci_cap(PSCI_CPU_ON_AARCH64); - if (psci_plat_pm_ops->pwr_domain_suspend && - psci_plat_pm_ops->pwr_domain_suspend_finish) { - psci_caps |= define_psci_cap(PSCI_CPU_SUSPEND_AARCH64); - if (psci_plat_pm_ops->get_sys_suspend_power_state) - psci_caps |= define_psci_cap(PSCI_SYSTEM_SUSPEND_AARCH64); - } - if (psci_plat_pm_ops->system_off) - psci_caps |= define_psci_cap(PSCI_SYSTEM_OFF); - if (psci_plat_pm_ops->system_reset) - psci_caps |= define_psci_cap(PSCI_SYSTEM_RESET); - - return 0; -} diff --git a/services/std_svc/psci1.0/psci_system_off.c b/services/std_svc/psci1.0/psci_system_off.c deleted file mode 100644 index 28315d6b59..0000000000 --- a/services/std_svc/psci1.0/psci_system_off.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2014-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 -#include "psci_private.h" - -void psci_system_off(void) -{ - psci_print_power_domain_map(); - - assert(psci_plat_pm_ops->system_off); - - /* Notify the Secure Payload Dispatcher */ - if (psci_spd_pm && psci_spd_pm->svc_system_off) { - psci_spd_pm->svc_system_off(); - } - - /* Call the platform specific hook */ - psci_plat_pm_ops->system_off(); - - /* This function does not return. We should never get here */ -} - -void psci_system_reset(void) -{ - psci_print_power_domain_map(); - - assert(psci_plat_pm_ops->system_reset); - - /* Notify the Secure Payload Dispatcher */ - if (psci_spd_pm && psci_spd_pm->svc_system_reset) { - psci_spd_pm->svc_system_reset(); - } - - /* Call the platform specific hook */ - psci_plat_pm_ops->system_reset(); - - /* This function does not return. We should never get here */ -} From 5c8babcd70149db57734a38be432fe6625f3888f Mon Sep 17 00:00:00 2001 From: Soby Mathew Date: Mon, 13 Jul 2015 16:26:11 +0100 Subject: [PATCH 10/20] PSCI: Add deprecated API for SPD when compatibility is disabled This patch defines deprecated platform APIs to enable Trusted Firmware components like Secure Payload and their dispatchers(SPD) to continue to build and run when platform compatibility is disabled. This decouples the migration of platform ports to the new platform API from SPD and enables them to be migrated independently. The deprecated platform APIs defined in this patch are : platform_get_core_pos(), platform_get_stack() and platform_set_stack(). The patch also deprecates MPIDR based context management helpers like cm_get_context_by_mpidr(), cm_set_context_by_mpidr() and cm_init_context(). A mechanism to deprecate APIs and identify callers of these APIs during build is introduced, which is controlled by the build flag WARN_DEPRECATED. If WARN_DEPRECATED is defined to 1, the users of the deprecated APIs will be flagged either as a link error for assembly files or compile time warning for C files during build. Change-Id: Ib72c7d5dc956e1a74d2294a939205b200f055613 --- Makefile | 6 +++ docs/user-guide.md | 5 +++ include/bl31/context_mgmt.h | 9 +++-- include/common/asm_macros.S | 23 +++++++++++ include/plat/common/common_def.h | 11 +++++ include/plat/common/platform.h | 9 ++++- plat/common/aarch64/plat_common.c | 18 ++++++++- plat/common/aarch64/platform_helpers.S | 27 +++++++++++++ plat/common/aarch64/platform_mp_stack.S | 53 ++++++++++++++++++++++++- plat/common/aarch64/platform_up_stack.S | 30 ++++++++++++-- 10 files changed, 180 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 2120cb3fcb..050a76e205 100644 --- a/Makefile +++ b/Makefile @@ -82,6 +82,8 @@ TRUSTED_BOARD_BOOT := 0 # By default, consider that the platform's reset address is not programmable. # The platform Makefile is free to override this value. PROGRAMMABLE_RESET_ADDRESS := 0 +# Build flag to warn about usage of deprecated platform and framework APIs +WARN_DEPRECATED := 0 # Checkpatch ignores CHECK_IGNORE = --ignore COMPLEX_MACRO \ @@ -302,6 +304,10 @@ $(eval $(call add_define,PROGRAMMABLE_RESET_ADDRESS)) $(eval $(call assert_boolean,ENABLE_PLAT_COMPAT)) $(eval $(call add_define,ENABLE_PLAT_COMPAT)) +# Process WARN_DEPRECATED flag +$(eval $(call assert_boolean,WARN_DEPRECATED)) +$(eval $(call add_define,WARN_DEPRECATED)) + ASFLAGS += -nostdinc -ffreestanding -Wa,--fatal-warnings \ -Werror -Wmissing-include-dirs \ -mgeneral-regs-only -D__ASSEMBLY__ \ diff --git a/docs/user-guide.md b/docs/user-guide.md index ad8d1c748f..5be3c3dc1a 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -362,6 +362,11 @@ performed. and it governs the return value of PSCI_FEATURES API for CPU_SUSPEND smc function id. +* `WARN_DEPRECATED`: This option decides whether to warn the usage of + deprecated platform APIs and context management helpers within Trusted + Firmware. It can take the value 1 (warn the use of deprecated APIs) or + 0. The default is 0. + #### ARM development platform specific build options * `ARM_TSP_RAM_LOCATION`: location of the TSP binary. Options: diff --git a/include/bl31/context_mgmt.h b/include/bl31/context_mgmt.h index 7e9fe832c4..1ef40766bc 100644 --- a/include/bl31/context_mgmt.h +++ b/include/bl31/context_mgmt.h @@ -31,6 +31,7 @@ #ifndef __CM_H__ #define __CM_H__ +#include #include #include @@ -43,18 +44,20 @@ struct entry_point_info; * Function & variable prototypes ******************************************************************************/ void cm_init(void); -void *cm_get_context_by_mpidr(uint64_t mpidr, uint32_t security_state); +void *cm_get_context_by_mpidr(uint64_t mpidr, + uint32_t security_state) __warn_deprecated; static inline void *cm_get_context(uint32_t security_state); void cm_set_context_by_mpidr(uint64_t mpidr, void *context, - uint32_t security_state); + uint32_t security_state) __warn_deprecated; void *cm_get_context_by_index(unsigned int cpu_idx, unsigned int security_state); void cm_set_context_by_index(unsigned int cpu_idx, void *context, unsigned int security_state); static inline void cm_set_context(void *context, uint32_t security_state); -void cm_init_context(uint64_t mpidr, const struct entry_point_info *ep); +void cm_init_context(uint64_t mpidr, + const struct entry_point_info *ep) __warn_deprecated; void cm_init_my_context(const struct entry_point_info *ep); void cm_init_context_by_index(unsigned int cpu_idx, const struct entry_point_info *ep); diff --git a/include/common/asm_macros.S b/include/common/asm_macros.S index f959eb4f9e..128259f194 100644 --- a/include/common/asm_macros.S +++ b/include/common/asm_macros.S @@ -99,6 +99,29 @@ .size \_name, . - \_name .endm + /* + * Theses macros are used to create function labels for deprecated + * APIs. If WARN_DEPRECATED is non zero, the callers of these APIs + * will fail to link and cause build failure. + */ +#if WARN_DEPRECATED + .macro func_deprecated _name + func deprecated\_name + .endm + + .macro endfunc_deprecated _name + endfunc deprecated\_name + .endm +#else + .macro func_deprecated _name + func \_name + .endm + + .macro endfunc_deprecated _name + endfunc \_name + .endm +#endif + /* * This macro declares an array of 1 or more stacks, properly * aligned and in the requested section diff --git a/include/plat/common/common_def.h b/include/plat/common/common_def.h index 1b3203e12e..077080df3b 100644 --- a/include/plat/common/common_def.h +++ b/include/plat/common/common_def.h @@ -67,6 +67,17 @@ #define MAKE_ULL(x) x #endif +/* + * Macros to wrap declarations of deprecated APIs within Trusted Firmware. + * The callers of these APIs will continue to compile as long as the build + * flag WARN_DEPRECATED is zero. Else the compiler will emit a warning + * when the callers of these APIs are compiled. + */ +#if WARN_DEPRECATED +#define __warn_deprecated __attribute__ ((deprecated)) +#else +#define __warn_deprecated +#endif #endif /* __COMMON_DEF_H__ */ diff --git a/include/plat/common/platform.h b/include/plat/common/platform.h index f054cd0a20..8071f39424 100644 --- a/include/plat/common/platform.h +++ b/include/plat/common/platform.h @@ -228,6 +228,13 @@ 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__ */ +#else +/* + * The below function enable Trusted Firmware components like SPDs which + * haven't migrated to the new platform API to compile on platforms which + * have the compatibility layer disabled. + */ +unsigned int platform_get_core_pos(unsigned long mpidr) __warn_deprecated; +#endif /* __ENABLE_PLAT_COMPAT__ */ #endif /* __PLATFORM_H__ */ diff --git a/plat/common/aarch64/plat_common.c b/plat/common/aarch64/plat_common.c index 90574fd664..a6a8476557 100644 --- a/plat/common/aarch64/plat_common.c +++ b/plat/common/aarch64/plat_common.c @@ -27,7 +27,8 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ - +#include +#include #include /* @@ -47,3 +48,18 @@ void bl32_plat_enable_mmu(uint32_t flags) { enable_mmu_el1(flags); } + +#if !ENABLE_PLAT_COMPAT +/* + * Helper function for platform_get_pos() when platform compatibility is + * disabled. This is to enable SPDs using the older platform API to continue + * to work. + */ +unsigned int platform_core_pos_helper(unsigned long mpidr) +{ + int idx = plat_core_pos_by_mpidr(mpidr); + assert(idx >= 0); + return idx; +} +#endif + diff --git a/plat/common/aarch64/platform_helpers.S b/plat/common/aarch64/platform_helpers.S index b88603c370..9f4b672ab8 100644 --- a/plat/common/aarch64/platform_helpers.S +++ b/plat/common/aarch64/platform_helpers.S @@ -38,6 +38,33 @@ .weak plat_reset_handler .weak plat_disable_acp +#if !ENABLE_PLAT_COMPAT + .globl platform_get_core_pos + +#define MPIDR_RES_BIT_MASK 0xff000000 + + /* ------------------------------------------------------------------ + * int platform_get_core_pos(int mpidr) + * Returns the CPU index of the CPU specified by mpidr. This is + * defined when platform compatibility is disabled to enable Trusted + * Firmware components like SPD using the old platform API to work. + * This API is deprecated and it assumes that the mpidr specified is + * that of a valid and present CPU. Instead, plat_my_core_pos() + * should be used for CPU index of the current CPU and + * plat_core_pos_by_mpidr() should be used for CPU index of a + * CPU specified by its mpidr. + * ------------------------------------------------------------------ + */ +func_deprecated platform_get_core_pos + bic x0, x0, #MPIDR_RES_BIT_MASK + mrs x1, mpidr_el1 + bic x1, x1, #MPIDR_RES_BIT_MASK + cmp x0, x1 + beq plat_my_core_pos + b platform_core_pos_helper +endfunc_deprecated platform_get_core_pos +#endif + /* ----------------------------------------------------- * Placeholder function which should be redefined by * each platform. diff --git a/plat/common/aarch64/platform_mp_stack.S b/plat/common/aarch64/platform_mp_stack.S index 6cfa0697b4..c719019ac4 100644 --- a/plat/common/aarch64/platform_mp_stack.S +++ b/plat/common/aarch64/platform_mp_stack.S @@ -42,8 +42,9 @@ #else .weak plat_get_my_stack .weak plat_set_my_stack -#endif /*__ENABLE_PLAT_COMPAT__*/ - + .globl platform_get_stack + .globl platform_set_stack +#endif /* __ENABLE_PLAT_COMPAT__ */ #if ENABLE_PLAT_COMPAT /* --------------------------------------------------------------------- @@ -108,6 +109,54 @@ func platform_set_stack endfunc platform_set_stack #else + /* --------------------------------------------------------------------- + * When the compatility layer is disabled, the new platform APIs + * viz plat_get_my_stack() and plat_set_my_stack() are + * supported by the platform and the previous APIs platform_get_stack() + * and platform_set_stack() are defined in terms of new APIs making use + * of the fact that they are only ever invoked for the current CPU. + * This is to enable components of Trusted Firmware like SPDs using the + * old platform APIs to continue to work. + * -------------------------------------------------------------------- + */ + + /* ------------------------------------------------------- + * unsigned long platform_get_stack (unsigned long mpidr) + * + * For the current CPU, this function returns the stack + * pointer for a stack allocated in device memory. The + * 'mpidr' should correspond to that of the current CPU. + * This function is deprecated and plat_get_my_stack() + * should be used instead. + * ------------------------------------------------------- + */ +func_deprecated platform_get_stack +#if ASM_ASSERTION + mrs x1, mpidr_el1 + cmp x0, x1 + ASM_ASSERT(eq) +#endif + b plat_get_my_stack +endfunc_deprecated platform_get_stack + + /* ----------------------------------------------------- + * void platform_set_stack (unsigned long mpidr) + * + * For the current CPU, this function sets the stack pointer + * to a stack allocated in normal memory. The + * 'mpidr' should correspond to that of the current CPU. + * This function is deprecated and plat_get_my_stack() + * should be used instead. + * ----------------------------------------------------- + */ +func_deprecated platform_set_stack +#if ASM_ASSERTION + mrs x1, mpidr_el1 + cmp x0, x1 + ASM_ASSERT(eq) +#endif + b plat_set_my_stack +endfunc_deprecated platform_set_stack /* ----------------------------------------------------- * unsigned long plat_get_my_stack () diff --git a/plat/common/aarch64/platform_up_stack.S b/plat/common/aarch64/platform_up_stack.S index d6d6c6e223..c01534af5f 100644 --- a/plat/common/aarch64/platform_up_stack.S +++ b/plat/common/aarch64/platform_up_stack.S @@ -41,7 +41,6 @@ /* ----------------------------------------------------- * unsigned long plat_get_my_stack () - * unsigned long platform_get_stack (unsigned long) * * For cold-boot BL images, only the primary CPU needs a * stack. This function returns the stack pointer for a @@ -49,14 +48,12 @@ * ----------------------------------------------------- */ func plat_get_my_stack -platform_get_stack: get_up_stack platform_normal_stacks, PLATFORM_STACK_SIZE ret endfunc plat_get_my_stack /* ----------------------------------------------------- * void plat_set_my_stack () - * void platform_set_stack (unsigned long) * * For cold-boot BL images, only the primary CPU needs a * stack. This function sets the stack pointer to a stack @@ -64,12 +61,37 @@ endfunc plat_get_my_stack * ----------------------------------------------------- */ func plat_set_my_stack -platform_set_stack: get_up_stack platform_normal_stacks, PLATFORM_STACK_SIZE mov sp, x0 ret endfunc plat_set_my_stack + /* ----------------------------------------------------- + * unsigned long platform_get_stack () + * + * For cold-boot BL images, only the primary CPU needs a + * stack. This function returns the stack pointer for a + * stack allocated in device memory. This function + * is deprecated. + * ----------------------------------------------------- + */ +func_deprecated platform_get_stack + b plat_get_my_stack +endfunc_deprecated platform_get_stack + + /* ----------------------------------------------------- + * void platform_set_stack () + * + * For cold-boot BL images, only the primary CPU needs a + * stack. This function sets the stack pointer to a stack + * allocated in normal memory.This function is + * deprecated. + * ----------------------------------------------------- + */ +func_deprecated platform_set_stack + b plat_set_my_stack +endfunc_deprecated platform_set_stack + /* ----------------------------------------------------- * Single cpu stack in normal memory. * Used for C code during boot, PLATFORM_STACK_SIZE bytes From 85a181ce3878187ba684f28b3c93cc098bc971fc Mon Sep 17 00:00:00 2001 From: Soby Mathew Date: Mon, 13 Jul 2015 11:21:11 +0100 Subject: [PATCH 11/20] PSCI: Migrate TF to the new platform API and CM helpers This patch migrates the rest of Trusted Firmware excluding Secure Payload and the dispatchers to the new platform and context management API. The per-cpu data framework APIs which took MPIDRs as their arguments are deleted and only the ones which take core index as parameter are retained. Change-Id: I839d05ad995df34d2163a1cfed6baa768a5a595d --- bl2/aarch64/bl2_entrypoint.S | 5 ++-- bl31/aarch64/cpu_data.S | 31 +++++-------------------- bl31/bl31_main.c | 4 ++-- include/bl31/cpu_data.h | 5 +--- include/common/el3_common_macros.S | 9 +++---- lib/locks/bakery/bakery_lock_coherent.c | 4 ++-- lib/locks/bakery/bakery_lock_normal.c | 2 +- 7 files changed, 17 insertions(+), 43 deletions(-) diff --git a/bl2/aarch64/bl2_entrypoint.S b/bl2/aarch64/bl2_entrypoint.S index c6ade6adf4..987d30e04e 100644 --- a/bl2/aarch64/bl2_entrypoint.S +++ b/bl2/aarch64/bl2_entrypoint.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-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: @@ -105,8 +105,7 @@ func bl2_entrypoint * primary cpu is running at the moment. * -------------------------------------------- */ - mrs x0, mpidr_el1 - bl platform_set_stack + bl plat_set_my_stack /* --------------------------------------------- * Perform early platform setup & platform diff --git a/bl31/aarch64/cpu_data.S b/bl31/aarch64/cpu_data.S index a53bd6d5e9..0842825cb7 100644 --- a/bl31/aarch64/cpu_data.S +++ b/bl31/aarch64/cpu_data.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2014-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: @@ -32,7 +32,6 @@ #include .globl init_cpu_data_ptr -.globl _cpu_data_by_mpidr .globl _cpu_data_by_index /* ----------------------------------------------------------------- @@ -41,37 +40,19 @@ * Initialise the TPIDR_EL3 register to refer to the cpu_data_t * for the calling CPU. This must be called before cm_get_cpu_data() * - * This can be called without a valid stack. - * clobbers: x0, x1, x9, x10 + * This can be called without a valid stack. It assumes that + * plat_my_core_pos() does not clobber register x10. + * clobbers: x0, x1, x10 * ----------------------------------------------------------------- */ func init_cpu_data_ptr mov x10, x30 - mrs x0, mpidr_el1 - bl _cpu_data_by_mpidr + bl plat_my_core_pos + bl _cpu_data_by_index msr tpidr_el3, x0 ret x10 endfunc init_cpu_data_ptr - -/* ----------------------------------------------------------------- - * cpu_data_t *_cpu_data_by_mpidr(uint64_t mpidr) - * - * Return the cpu_data structure for the CPU with given MPIDR - * - * This can be called without a valid stack. It assumes that - * platform_get_core_pos() does not clobber register x9. - * clobbers: x0, x1, x9 - * ----------------------------------------------------------------- - */ -func _cpu_data_by_mpidr - mov x9, x30 - bl platform_get_core_pos - mov x30, x9 - b _cpu_data_by_index -endfunc _cpu_data_by_mpidr - - /* ----------------------------------------------------------------- * cpu_data_t *_cpu_data_by_index(uint32_t cpu_index) * diff --git a/bl31/bl31_main.c b/bl31/bl31_main.c index 19f377436d..a1a3710583 100644 --- a/bl31/bl31_main.c +++ b/bl31/bl31_main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-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: @@ -155,7 +155,7 @@ void bl31_prepare_next_image_entry(void) INFO("BL3-1: Next image address = 0x%llx\n", (unsigned long long) next_image_info->pc); INFO("BL3-1: Next image spsr = 0x%x\n", next_image_info->spsr); - cm_init_context(read_mpidr_el1(), next_image_info); + cm_init_my_context(next_image_info); cm_prepare_el3_exit(image_type); } diff --git a/include/bl31/cpu_data.h b/include/bl31/cpu_data.h index db702ba352..2b506c730c 100644 --- a/include/bl31/cpu_data.h +++ b/include/bl31/cpu_data.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2014-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: @@ -103,7 +103,6 @@ CASSERT(CPU_DATA_CPU_OPS_PTR == __builtin_offsetof assert_cpu_data_cpu_ops_ptr_offset_mismatch); struct cpu_data *_cpu_data_by_index(uint32_t cpu_index); -struct cpu_data *_cpu_data_by_mpidr(uint64_t mpidr); /* Return the cpu_data structure for the current CPU. */ static inline struct cpu_data *_cpu_data(void) @@ -123,8 +122,6 @@ void init_cpu_ops(void); #define set_cpu_data(_m, _v) _cpu_data()->_m = _v #define get_cpu_data_by_index(_ix, _m) _cpu_data_by_index(_ix)->_m #define set_cpu_data_by_index(_ix, _m, _v) _cpu_data_by_index(_ix)->_m = _v -#define get_cpu_data_by_mpidr(_id, _m) _cpu_data_by_mpidr(_id)->_m -#define set_cpu_data_by_mpidr(_id, _m, _v) _cpu_data_by_mpidr(_id)->_m = _v #define flush_cpu_data(_m) flush_dcache_range((uint64_t) \ &(_cpu_data()->_m), \ diff --git a/include/common/el3_common_macros.S b/include/common/el3_common_macros.S index eb033a6e60..3b96081594 100644 --- a/include/common/el3_common_macros.S +++ b/include/common/el3_common_macros.S @@ -164,8 +164,7 @@ * then it means it is a warm boot so jump to this address. * ------------------------------------------------------------- */ - mrs x0, mpidr_el1 - bl platform_get_entrypoint + bl plat_get_my_entrypoint cbz x0, do_cold_boot br x0 @@ -181,8 +180,7 @@ * of that state and allows entry into the OS. * ------------------------------------------------------------- */ - mrs x0, mpidr_el1 - bl platform_is_primary_cpu + bl plat_is_my_cpu_primary cbnz x0, do_primary_cold_boot /* This is a cold boot on a secondary CPU */ @@ -249,8 +247,7 @@ * moment. * --------------------------------------------------------------------- */ - mrs x0, mpidr_el1 - bl platform_set_stack + bl plat_set_my_stack .endm #endif /* __EL3_COMMON_MACROS_S__ */ diff --git a/lib/locks/bakery/bakery_lock_coherent.c b/lib/locks/bakery/bakery_lock_coherent.c index fd871053a7..1c60dba78f 100644 --- a/lib/locks/bakery/bakery_lock_coherent.c +++ b/lib/locks/bakery/bakery_lock_coherent.c @@ -128,7 +128,7 @@ void bakery_lock_get(bakery_lock_t *bakery) unsigned int my_ticket, my_prio, their_ticket; unsigned int their_bakery_data; - me = platform_get_core_pos(read_mpidr_el1()); + me = plat_my_core_pos(); assert_bakery_entry_valid(me, bakery); @@ -174,7 +174,7 @@ void bakery_lock_get(bakery_lock_t *bakery) /* Release the lock and signal contenders */ void bakery_lock_release(bakery_lock_t *bakery) { - unsigned int me = platform_get_core_pos(read_mpidr_el1()); + unsigned int me = plat_my_core_pos(); assert_bakery_entry_valid(me, bakery); assert(bakery_ticket_number(bakery->lock_data[me])); diff --git a/lib/locks/bakery/bakery_lock_normal.c b/lib/locks/bakery/bakery_lock_normal.c index 5439271e97..3ca76e0d9b 100644 --- a/lib/locks/bakery/bakery_lock_normal.c +++ b/lib/locks/bakery/bakery_lock_normal.c @@ -148,7 +148,7 @@ void bakery_lock_get(unsigned int id, unsigned int offset) bakery_info_t *their_bakery_info; unsigned int their_bakery_data; - me = platform_get_core_pos(read_mpidr_el1()); + me = plat_my_core_pos(); is_cached = read_sctlr_el3() & SCTLR_C_BIT; From 38dce70f51fb83b27958ba3e2ad15f5635cb1061 Mon Sep 17 00:00:00 2001 From: Soby Mathew Date: Wed, 1 Jul 2015 16:16:20 +0100 Subject: [PATCH 12/20] PSCI: Migrate ARM reference platforms to new platform API This patch migrates ARM reference platforms, Juno and FVP, to the new platform API mandated by the new PSCI power domain topology and composite power state frameworks. The platform specific makefiles now exports the build flag ENABLE_PLAT_COMPAT=0 to disable the platform compatibility layer. Change-Id: I3040ed7cce446fc66facaee9c67cb54a8cd7ca29 --- include/plat/arm/common/arm_def.h | 37 +++- include/plat/arm/common/plat_arm.h | 9 +- plat/arm/board/fvp/aarch64/fvp_helpers.S | 21 ++- plat/arm/board/fvp/fvp_pm.c | 149 +++++++-------- plat/arm/board/fvp/fvp_topology.c | 215 +++------------------- plat/arm/board/fvp/platform.mk | 4 +- plat/arm/board/fvp/tsp/tsp-fvp.mk | 4 +- plat/arm/board/juno/platform.mk | 3 + plat/arm/board/juno/tsp/tsp-juno.mk | 2 + plat/arm/common/aarch64/arm_helpers.S | 28 ++- plat/arm/common/arm_bl31_setup.c | 3 - plat/arm/common/arm_common.mk | 3 +- plat/arm/common/arm_pm.c | 56 +++--- plat/arm/common/arm_topology.c | 58 +++--- plat/arm/common/tsp/arm_tsp.mk | 1 + plat/arm/css/common/aarch64/css_helpers.S | 43 ++--- plat/arm/css/common/css_common.mk | 3 +- plat/arm/css/common/css_pm.c | 154 ++++++++-------- plat/arm/css/common/css_topology.c | 70 +++++++ 19 files changed, 416 insertions(+), 447 deletions(-) create mode 100644 plat/arm/css/common/css_topology.c diff --git a/include/plat/arm/common/arm_def.h b/include/plat/arm/common/arm_def.h index 4447af2c9b..377bfaa222 100644 --- a/include/plat/arm/common/arm_def.h +++ b/include/plat/arm/common/arm_def.h @@ -30,6 +30,7 @@ #ifndef __ARM_DEF_H__ #define __ARM_DEF_H__ +#include #include #include #include @@ -47,6 +48,25 @@ #define ARM_CACHE_WRITEBACK_SHIFT 6 +/* + * Macros mapping the MPIDR Affinity levels to ARM Platform Power levels. The + * power levels have a 1:1 mapping with the MPIDR affinity levels. + */ +#define ARM_PWR_LVL0 MPIDR_AFFLVL0 +#define ARM_PWR_LVL1 MPIDR_AFFLVL1 + +/* + * Macros for local power states in ARM platforms encoded by State-ID field + * within the power-state parameter. + */ +/* Local power state for power domains in Run state. */ +#define ARM_LOCAL_STATE_RUN 0 +/* Local power state for retention. Valid only for CPU power domains */ +#define ARM_LOCAL_STATE_RET 1 +/* Local power state for OFF/power-down. Valid for CPU and cluster power + domains */ +#define ARM_LOCAL_STATE_OFF 2 + /* Memory location options for TSP */ #define ARM_TRUSTED_SRAM_ID 0 #define ARM_TRUSTED_DRAM_ID 1 @@ -163,9 +183,22 @@ #define ADDR_SPACE_SIZE (1ull << 32) -#define PLATFORM_NUM_AFFS (ARM_CLUSTER_COUNT + \ +#define PLAT_NUM_PWR_DOMAINS (ARM_CLUSTER_COUNT + \ PLATFORM_CORE_COUNT) -#define PLATFORM_MAX_AFFLVL MPIDR_AFFLVL1 +#define PLAT_MAX_PWR_LVL ARM_PWR_LVL1 + +/* + * This macro defines the deepest retention state possible. A higher state + * id will represent an invalid or a power down state. + */ +#define PLAT_MAX_RET_STATE ARM_LOCAL_STATE_RET + +/* + * This macro defines the deepest power down states possible. Any state ID + * higher than this is invalid. + */ +#define PLAT_MAX_OFF_STATE ARM_LOCAL_STATE_OFF + #define PLATFORM_CORE_COUNT (PLAT_ARM_CLUSTER0_CORE_COUNT + \ PLAT_ARM_CLUSTER1_CORE_COUNT) diff --git a/include/plat/arm/common/plat_arm.h b/include/plat/arm/common/plat_arm.h index d7eaac1d5b..29f1c9053c 100644 --- a/include/plat/arm/common/plat_arm.h +++ b/include/plat/arm/common/plat_arm.h @@ -159,8 +159,11 @@ void arm_io_setup(void); void arm_tzc_setup(void); /* PM utility functions */ -int32_t arm_do_affinst_actions(unsigned int afflvl, unsigned int state); -int arm_validate_power_state(unsigned int power_state); +int arm_validate_power_state(unsigned int power_state, + psci_power_state_t *req_state); + +/* Topology utility function */ +int arm_check_mpidr(u_register_t mpidr); /* BL1 utility functions */ void arm_bl1_early_platform_setup(void); @@ -199,7 +202,7 @@ int plat_arm_get_alt_image_source( unsigned int image_id, uintptr_t *dev_handle, uintptr_t *image_spec); -void plat_arm_topology_setup(void); +unsigned int plat_arm_calc_core_pos(u_register_t mpidr); #endif /* __PLAT_ARM_H__ */ diff --git a/plat/arm/board/fvp/aarch64/fvp_helpers.S b/plat/arm/board/fvp/aarch64/fvp_helpers.S index d176faccaf..2787ee674d 100644 --- a/plat/arm/board/fvp/aarch64/fvp_helpers.S +++ b/plat/arm/board/fvp/aarch64/fvp_helpers.S @@ -37,9 +37,9 @@ #include "../fvp_def.h" .globl plat_secondary_cold_boot_setup - .globl platform_get_entrypoint + .globl plat_get_my_entrypoint .globl platform_mem_init - .globl platform_is_primary_cpu + .globl plat_is_my_cpu_primary .macro fvp_choose_gicmmap param1, param2, x_tmp, w_tmp, res ldr \x_tmp, =V2M_SYSREGS_BASE + V2M_SYS_ID @@ -98,10 +98,10 @@ endfunc plat_secondary_cold_boot_setup /* ----------------------------------------------------- - * void platform_get_entrypoint (unsigned int mpid); + * unsigned long plat_get_my_entrypoint (void); * * Main job of this routine is to distinguish between - * a cold and warm boot. + * a cold and warm boot on the current CPU. * On a cold boot the secondaries first wait for the * platform to be initialized after which they are * hotplugged in. The primary proceeds to perform the @@ -117,9 +117,9 @@ endfunc plat_secondary_cold_boot_setup * reset all cpus will read the same WK field * ----------------------------------------------------- */ -func platform_get_entrypoint +func plat_get_my_entrypoint mov x9, x30 // lr - mov x2, x0 + mrs x2, mpidr_el1 ldr x1, =PWRC_BASE str w2, [x1, #PSYSR_OFF] ldr w2, [x1, #PSYSR_OFF] @@ -139,14 +139,14 @@ warm_reset: * --------------------------------------------- */ ldr x10, =MBOX_BASE - bl platform_get_core_pos + bl plat_my_core_pos lsl x0, x0, #ARM_CACHE_WRITEBACK_SHIFT ldr x0, [x10, x0] cbz x0, _panic exit: ret x9 _panic: b _panic -endfunc platform_get_entrypoint +endfunc plat_get_my_entrypoint /* ----------------------------------------------------- @@ -172,9 +172,10 @@ loop: endfunc platform_mem_init -func platform_is_primary_cpu +func plat_is_my_cpu_primary + mrs x0, mpidr_el1 and x0, x0, #(MPIDR_CLUSTER_MASK | MPIDR_CPU_MASK) cmp x0, #FVP_PRIMARY_CPU cset x0, eq ret -endfunc platform_is_primary_cpu +endfunc plat_is_my_cpu_primary diff --git a/plat/arm/board/fvp/fvp_pm.c b/plat/arm/board/fvp/fvp_pm.c index b1431c4215..c59ffd1969 100644 --- a/plat/arm/board/fvp/fvp_pm.c +++ b/plat/arm/board/fvp/fvp_pm.c @@ -43,6 +43,7 @@ #include "fvp_def.h" #include "fvp_private.h" +unsigned long wakeup_address; typedef volatile struct mailbox { unsigned long value __aligned(CACHE_WRITEBACK_GRANULE); @@ -57,7 +58,7 @@ static void fvp_program_mailbox(uint64_t mpidr, uint64_t address) uint64_t linear_id; mailbox_t *fvp_mboxes; - linear_id = platform_get_core_pos(mpidr); + linear_id = plat_arm_calc_core_pos(mpidr); fvp_mboxes = (mailbox_t *)MBOX_BASE; fvp_mboxes[linear_id].value = address; flush_dcache_range((unsigned long) &fvp_mboxes[linear_id], @@ -93,10 +94,13 @@ static void fvp_cluster_pwrdwn_common(void) } /******************************************************************************* - * FVP handler called when an affinity instance is about to enter standby. + * FVP handler called when a CPU is about to enter standby. ******************************************************************************/ -void fvp_affinst_standby(unsigned int power_state) +void fvp_cpu_standby(plat_local_state_t cpu_state) { + + assert(cpu_state == ARM_LOCAL_STATE_RET); + /* * Enter standby state * dsb is good practice before using wfi to enter low power states @@ -106,24 +110,14 @@ void fvp_affinst_standby(unsigned int power_state) } /******************************************************************************* - * FVP handler called when an affinity instance is about to be turned on. The - * level and mpidr determine the affinity instance. + * FVP handler called when a power domain is about to be turned on. The + * mpidr determines the CPU to be turned on. ******************************************************************************/ -int fvp_affinst_on(unsigned long mpidr, - unsigned long sec_entrypoint, - unsigned int afflvl, - unsigned int state) +int fvp_pwr_domain_on(u_register_t mpidr) { int rc = PSCI_E_SUCCESS; unsigned int psysr; - /* - * It's possible to turn on only affinity level 0 i.e. a cpu - * on the FVP. Ignore any other affinity level. - */ - if (afflvl != MPIDR_AFFLVL0) - return rc; - /* * Ensure that we do not cancel an inflight power off request * for the target cpu. That would leave it in a zombie wfi. @@ -135,68 +129,58 @@ int fvp_affinst_on(unsigned long mpidr, psysr = fvp_pwrc_read_psysr(mpidr); } while (psysr & PSYSR_AFF_L0); - fvp_program_mailbox(mpidr, sec_entrypoint); + fvp_program_mailbox(mpidr, wakeup_address); fvp_pwrc_write_pponr(mpidr); return rc; } /******************************************************************************* - * FVP handler called when an affinity instance is about to be turned off. The - * level and mpidr determine the affinity instance. The 'state' arg. allows the - * platform to decide whether the cluster is being turned off and take apt - * actions. - * - * CAUTION: There is no guarantee that caches will remain turned on across calls - * to this function as each affinity level is dealt with. So do not write & read - * global variables across calls. It will be wise to do flush a write to the - * global to prevent unpredictable results. + * FVP handler called when a power domain is about to be turned off. The + * target_state encodes the power state that each level should transition to. ******************************************************************************/ -void fvp_affinst_off(unsigned int afflvl, - unsigned int state) +void fvp_pwr_domain_off(const psci_power_state_t *target_state) { - /* Determine if any platform actions need to be executed */ - if (arm_do_affinst_actions(afflvl, state) == -EAGAIN) - return; + assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == + ARM_LOCAL_STATE_OFF); /* - * If execution reaches this stage then this affinity level will be - * suspended. Perform at least the cpu specific actions followed the - * cluster specific operations if applicable. + * If execution reaches this stage then this power domain will be + * suspended. Perform at least the cpu specific actions followed + * by the cluster specific operations if applicable. */ fvp_cpu_pwrdwn_common(); - if (afflvl != MPIDR_AFFLVL0) + if (target_state->pwr_domain_state[ARM_PWR_LVL1] == + ARM_LOCAL_STATE_OFF) fvp_cluster_pwrdwn_common(); } /******************************************************************************* - * FVP handler called when an affinity instance is about to be suspended. The - * level and mpidr determine the affinity instance. The 'state' arg. allows the - * platform to decide whether the cluster is being turned off and take apt - * actions. - * - * CAUTION: There is no guarantee that caches will remain turned on across calls - * to this function as each affinity level is dealt with. So do not write & read - * global variables across calls. It will be wise to do flush a write to the - * global to prevent unpredictable results. + * FVP handler called when a power domain is about to be suspended. The + * target_state encodes the power state that each level should transition to. ******************************************************************************/ -void fvp_affinst_suspend(unsigned long sec_entrypoint, - unsigned int afflvl, - unsigned int state) +void fvp_pwr_domain_suspend(const psci_power_state_t *target_state) { unsigned long mpidr; - /* Determine if any platform actions need to be executed. */ - if (arm_do_affinst_actions(afflvl, state) == -EAGAIN) + /* + * FVP has retention only at cpu level. Just return + * as nothing is to be done for retention. + */ + if (target_state->pwr_domain_state[ARM_PWR_LVL0] == + ARM_LOCAL_STATE_RET) return; + assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == + ARM_LOCAL_STATE_OFF); + /* Get the mpidr for this cpu */ mpidr = read_mpidr_el1(); /* Program the jump address for the this cpu */ - fvp_program_mailbox(mpidr, sec_entrypoint); + fvp_program_mailbox(mpidr, wakeup_address); /* Program the power controller to enable wakeup interrupts. */ fvp_pwrc_set_wen(mpidr); @@ -205,31 +189,29 @@ void fvp_affinst_suspend(unsigned long sec_entrypoint, fvp_cpu_pwrdwn_common(); /* Perform the common cluster specific operations */ - if (afflvl != MPIDR_AFFLVL0) + if (target_state->pwr_domain_state[ARM_PWR_LVL1] == + ARM_LOCAL_STATE_OFF) fvp_cluster_pwrdwn_common(); } /******************************************************************************* - * FVP handler called when an affinity instance has just been powered on after - * being turned off earlier. The level and mpidr determine the affinity - * instance. The 'state' arg. allows the platform to decide whether the cluster - * was turned off prior to wakeup and do what's necessary to setup it up - * correctly. + * FVP handler called when a power domain has just been powered on after + * being turned off earlier. The target_state encodes the low power state that + * each level has woken up from. ******************************************************************************/ -void fvp_affinst_on_finish(unsigned int afflvl, - unsigned int state) +void fvp_pwr_domain_on_finish(const psci_power_state_t *target_state) { unsigned long mpidr; - /* Determine if any platform actions need to be executed. */ - if (arm_do_affinst_actions(afflvl, state) == -EAGAIN) - return; + assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == + ARM_LOCAL_STATE_OFF); /* Get the mpidr for this cpu */ mpidr = read_mpidr_el1(); /* Perform the common cluster specific operations */ - if (afflvl != MPIDR_AFFLVL0) { + if (target_state->pwr_domain_state[ARM_PWR_LVL1] == + ARM_LOCAL_STATE_OFF) { /* * This CPU might have woken up whilst the cluster was * attempting to power down. In this case the FVP power @@ -262,16 +244,22 @@ void fvp_affinst_on_finish(unsigned int afflvl, } /******************************************************************************* - * FVP handler called when an affinity instance has just been powered on after - * having been suspended earlier. The level and mpidr determine the affinity - * instance. + * FVP handler called when a power domain has just been powered on after + * having been suspended earlier. The target_state encodes the low power state + * that each level has woken up from. * TODO: At the moment we reuse the on finisher and reinitialize the secure * context. Need to implement a separate suspend finisher. ******************************************************************************/ -void fvp_affinst_suspend_finish(unsigned int afflvl, - unsigned int state) +void fvp_pwr_domain_suspend_finish(const psci_power_state_t *target_state) { - fvp_affinst_on_finish(afflvl, state); + /* + * Nothing to be done on waking up from retention from CPU level. + */ + if (target_state->pwr_domain_state[ARM_PWR_LVL0] == + ARM_LOCAL_STATE_RET) + return; + + fvp_pwr_domain_on_finish(target_state); } /******************************************************************************* @@ -304,23 +292,28 @@ static void __dead2 fvp_system_reset(void) /******************************************************************************* * Export the platform handlers to enable psci to invoke them ******************************************************************************/ -static const plat_pm_ops_t fvp_plat_pm_ops = { - .affinst_standby = fvp_affinst_standby, - .affinst_on = fvp_affinst_on, - .affinst_off = fvp_affinst_off, - .affinst_suspend = fvp_affinst_suspend, - .affinst_on_finish = fvp_affinst_on_finish, - .affinst_suspend_finish = fvp_affinst_suspend_finish, +static const plat_psci_ops_t fvp_plat_psci_ops = { + .cpu_standby = fvp_cpu_standby, + .pwr_domain_on = fvp_pwr_domain_on, + .pwr_domain_off = fvp_pwr_domain_off, + .pwr_domain_suspend = fvp_pwr_domain_suspend, + .pwr_domain_on_finish = fvp_pwr_domain_on_finish, + .pwr_domain_suspend_finish = fvp_pwr_domain_suspend_finish, .system_off = fvp_system_off, .system_reset = fvp_system_reset, .validate_power_state = arm_validate_power_state }; /******************************************************************************* - * Export the platform specific power ops & initialize the fvp power controller + * Export the platform specific psci ops & initialize the fvp power controller ******************************************************************************/ -int platform_setup_pm(const plat_pm_ops_t **plat_ops) +int plat_setup_psci_ops(uintptr_t sec_entrypoint, + const plat_psci_ops_t **psci_ops) { - *plat_ops = &fvp_plat_pm_ops; + *psci_ops = &fvp_plat_psci_ops; + wakeup_address = sec_entrypoint; + + flush_dcache_range((unsigned long)&wakeup_address, + sizeof(wakeup_address)); return 0; } diff --git a/plat/arm/board/fvp/fvp_topology.c b/plat/arm/board/fvp/fvp_topology.c index c90e82f6b0..a212eda74c 100644 --- a/plat/arm/board/fvp/fvp_topology.c +++ b/plat/arm/board/fvp/fvp_topology.c @@ -29,204 +29,41 @@ */ #include -#include +#include #include -/* TODO: Reusing psci error codes & state information. Get our own! */ -#include #include "drivers/pwrc/fvp_pwrc.h" -#include "fvp_def.h" -/* We treat '255' as an invalid affinity instance */ -#define AFFINST_INVAL 0xff +/* + * The FVP power domain tree does not have a single system level power domain + * i.e. a single root node. The first entry in the power domain descriptor + * specifies the number of power domains at the highest power level. For the FVP + * this is 2 i.e. the number of cluster power domains. + */ +#define FVP_PWR_DOMAINS_AT_MAX_PWR_LVL ARM_CLUSTER_COUNT -/******************************************************************************* - * We support 3 flavours of the FVP: Foundation, Base AEM & Base Cortex. Each - * flavour has a different topology. The common bit is that there can be a max. - * of 2 clusters (affinity 1) and 4 cpus (affinity 0) per cluster. So we define - * a tree like data structure which caters to these maximum bounds. It simply - * marks the absent affinity level instances as PSCI_AFF_ABSENT e.g. there is no - * cluster 1 on the Foundation FVP. The 'data' field is currently unused. - ******************************************************************************/ -typedef struct affinity_info { - unsigned char sibling; - unsigned char child; - unsigned char state; - unsigned int data; -} affinity_info_t; - -/******************************************************************************* - * The following two data structures store the topology tree for the fvp. There - * is a separate array for each affinity level i.e. cpus and clusters. The child - * and sibling references allow traversal inside and in between the two arrays. - ******************************************************************************/ -static affinity_info_t fvp_aff1_topology_map[ARM_CLUSTER_COUNT]; -static affinity_info_t fvp_aff0_topology_map[PLATFORM_CORE_COUNT]; - -/* Simple global variable to safeguard us from stupidity */ -static unsigned int topology_setup_done; +/* The FVP power domain tree descriptor */ +const unsigned char arm_power_domain_tree_desc[] = { + /* No of root nodes */ + FVP_PWR_DOMAINS_AT_MAX_PWR_LVL, + /* No of children for the first node */ + PLAT_ARM_CLUSTER0_CORE_COUNT, + /* No of children for the second node */ + PLAT_ARM_CLUSTER1_CORE_COUNT +}; /******************************************************************************* * This function implements a part of the critical interface between the psci - * generic layer and the platform to allow the former to detect the platform - * topology. psci queries the platform to determine how many affinity instances - * are present at a particular level for a given mpidr e.g. consider a dual - * cluster platform where each cluster has 4 cpus. A call to this function with - * (0, 0x100) will return the number of cpus implemented under cluster 1 i.e. 4. - * Similarly a call with (1, 0x100) will return 2 i.e. the number of clusters. - * This is 'cause we are effectively asking how many affinity level 1 instances - * are implemented under affinity level 2 instance 0. + * generic layer and the platform that allows the former to query the platform + * to convert an MPIDR to a unique linear index. An error code (-1) is returned + * in case the MPIDR is invalid. ******************************************************************************/ -unsigned int plat_get_aff_count(unsigned int aff_lvl, - unsigned long mpidr) +int plat_core_pos_by_mpidr(u_register_t mpidr) { - unsigned int aff_count = 1, ctr; - unsigned char parent_aff_id; + if (arm_check_mpidr(mpidr) == -1) + return -1; - assert(topology_setup_done == 1); + if (fvp_pwrc_read_psysr(mpidr) == PSYSR_INVALID) + return -1; - switch (aff_lvl) { - case 3: - case 2: - /* - * Assert if the parent affinity instance is not 0. - * This also takes care of level 3 in an obfuscated way - */ - parent_aff_id = (mpidr >> MPIDR_AFF3_SHIFT) & MPIDR_AFFLVL_MASK; - assert(parent_aff_id == 0); - - /* - * Report that we implement a single instance of - * affinity levels 2 & 3 which are AFF_ABSENT - */ - break; - case 1: - /* Assert if the parent affinity instance is not 0. */ - parent_aff_id = (mpidr >> MPIDR_AFF2_SHIFT) & MPIDR_AFFLVL_MASK; - assert(parent_aff_id == 0); - - /* Fetch the starting index in the aff1 array */ - for (ctr = 0; - fvp_aff1_topology_map[ctr].sibling != AFFINST_INVAL; - ctr = fvp_aff1_topology_map[ctr].sibling) { - aff_count++; - } - - break; - case 0: - /* Assert if the cluster id is anything apart from 0 or 1 */ - parent_aff_id = (mpidr >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK; - assert(parent_aff_id < ARM_CLUSTER_COUNT); - - /* Fetch the starting index in the aff0 array */ - for (ctr = fvp_aff1_topology_map[parent_aff_id].child; - fvp_aff0_topology_map[ctr].sibling != AFFINST_INVAL; - ctr = fvp_aff0_topology_map[ctr].sibling) { - aff_count++; - } - - break; - default: - assert(0); - } - - return aff_count; -} - -/******************************************************************************* - * This function implements a part of the critical interface between the psci - * generic layer and the platform to allow the former to detect the state of a - * affinity instance in the platform topology. psci queries the platform to - * determine whether an affinity instance is present or absent. This caters for - * topologies where an intermediate affinity level instance is missing e.g. - * consider a platform which implements a single cluster with 4 cpus and there - * is another cpu sitting directly on the interconnect along with the cluster. - * The mpidrs of the cluster would range from 0x0-0x3. The mpidr of the single - * cpu would be 0x100 to highlight that it does not belong to cluster 0. Cluster - * 1 is however missing but needs to be accounted to reach this single cpu in - * the topology tree. Hence it will be marked as PSCI_AFF_ABSENT. This is not - * applicable to the FVP but depicted as an example. - ******************************************************************************/ -unsigned int plat_get_aff_state(unsigned int aff_lvl, - unsigned long mpidr) -{ - unsigned int aff_state = PSCI_AFF_ABSENT, idx; - idx = (mpidr >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK; - - assert(topology_setup_done == 1); - - switch (aff_lvl) { - case 3: - case 2: - /* Report affinity levels 2 & 3 as absent */ - break; - case 1: - aff_state = fvp_aff1_topology_map[idx].state; - break; - case 0: - /* - * First get start index of the aff0 in its array & then add - * to it the affinity id that we want the state of - */ - idx = fvp_aff1_topology_map[idx].child; - idx += (mpidr >> MPIDR_AFF0_SHIFT) & MPIDR_AFFLVL_MASK; - aff_state = fvp_aff0_topology_map[idx].state; - break; - default: - assert(0); - } - - return aff_state; -} - -/******************************************************************************* - * This function populates the FVP specific topology information depending upon - * the FVP flavour its running on. We construct all the mpidrs we can handle - * and rely on the PWRC.PSYSR to flag absent cpus when their status is queried. - ******************************************************************************/ -void plat_arm_topology_setup(void) -{ - unsigned char aff0, aff1, aff_state, aff0_offset = 0; - unsigned long mpidr; - - topology_setup_done = 0; - - for (aff1 = 0; aff1 < ARM_CLUSTER_COUNT; aff1++) { - - fvp_aff1_topology_map[aff1].child = aff0_offset; - fvp_aff1_topology_map[aff1].sibling = aff1 + 1; - - for (aff0 = 0; aff0 < FVP_MAX_CPUS_PER_CLUSTER; aff0++) { - - mpidr = aff1 << MPIDR_AFF1_SHIFT; - mpidr |= aff0 << MPIDR_AFF0_SHIFT; - - if (fvp_pwrc_read_psysr(mpidr) != PSYSR_INVALID) { - /* - * Presence of even a single aff0 indicates - * presence of parent aff1 on the FVP. - */ - aff_state = PSCI_AFF_PRESENT; - fvp_aff1_topology_map[aff1].state = - PSCI_AFF_PRESENT; - } else { - aff_state = PSCI_AFF_ABSENT; - } - - fvp_aff0_topology_map[aff0_offset].child = AFFINST_INVAL; - fvp_aff0_topology_map[aff0_offset].state = aff_state; - fvp_aff0_topology_map[aff0_offset].sibling = - aff0_offset + 1; - - /* Increment the absolute number of aff0s traversed */ - aff0_offset++; - } - - /* Tie-off the last aff0 sibling to -1 to avoid overflow */ - fvp_aff0_topology_map[aff0_offset - 1].sibling = AFFINST_INVAL; - } - - /* Tie-off the last aff1 sibling to AFFINST_INVAL to avoid overflow */ - fvp_aff1_topology_map[aff1 - 1].sibling = AFFINST_INVAL; - - topology_setup_done = 1; + return plat_arm_calc_core_pos(mpidr); } diff --git a/plat/arm/board/fvp/platform.mk b/plat/arm/board/fvp/platform.mk index a1a0971465..51b718ee13 100644 --- a/plat/arm/board/fvp/platform.mk +++ b/plat/arm/board/fvp/platform.mk @@ -28,7 +28,6 @@ # POSSIBILITY OF SUCH DAMAGE. # - PLAT_INCLUDES := -Iplat/arm/board/fvp/include @@ -63,5 +62,8 @@ BL31_SOURCES += lib/cpus/aarch64/aem_generic.S \ plat/arm/board/fvp/aarch64/fvp_helpers.S \ plat/arm/board/fvp/drivers/pwrc/fvp_pwrc.c +# Disable the PSCI platform compatibility layer +ENABLE_PLAT_COMPAT := 0 + include plat/arm/board/common/board_common.mk include plat/arm/common/arm_common.mk diff --git a/plat/arm/board/fvp/tsp/tsp-fvp.mk b/plat/arm/board/fvp/tsp/tsp-fvp.mk index 8773ee037d..99db2f4951 100644 --- a/plat/arm/board/fvp/tsp/tsp-fvp.mk +++ b/plat/arm/board/fvp/tsp/tsp-fvp.mk @@ -29,6 +29,8 @@ # # TSP source files specific to FVP platform -BL32_SOURCES += plat/arm/board/fvp/tsp/fvp_tsp_setup.c +BL32_SOURCES += plat/arm/board/fvp/fvp_topology.c \ + plat/arm/board/fvp/drivers/pwrc/fvp_pwrc.c \ + plat/arm/board/fvp/tsp/fvp_tsp_setup.c include plat/arm/common/tsp/arm_tsp.mk diff --git a/plat/arm/board/juno/platform.mk b/plat/arm/board/juno/platform.mk index 98834e7b18..b80cfb300a 100644 --- a/plat/arm/board/juno/platform.mk +++ b/plat/arm/board/juno/platform.mk @@ -48,6 +48,9 @@ ERRATA_A57_813420 := 1 # power down sequence SKIP_A57_L1_FLUSH_PWR_DWN := 1 +# Disable the PSCI platform compatibility layer +ENABLE_PLAT_COMPAT := 0 + include plat/arm/board/common/board_css.mk include plat/arm/common/arm_common.mk include plat/arm/soc/common/soc_css.mk diff --git a/plat/arm/board/juno/tsp/tsp-juno.mk b/plat/arm/board/juno/tsp/tsp-juno.mk index 55b031afd6..bb67012f37 100644 --- a/plat/arm/board/juno/tsp/tsp-juno.mk +++ b/plat/arm/board/juno/tsp/tsp-juno.mk @@ -28,4 +28,6 @@ # POSSIBILITY OF SUCH DAMAGE. # +BL32_SOURCES += plat/arm/css/common/css_topology.c + include plat/arm/common/tsp/arm_tsp.mk diff --git a/plat/arm/common/aarch64/arm_helpers.S b/plat/arm/common/aarch64/arm_helpers.S index 25b67f3f50..daf08faa9d 100644 --- a/plat/arm/common/aarch64/arm_helpers.S +++ b/plat/arm/common/aarch64/arm_helpers.S @@ -30,11 +30,37 @@ #include #include - + .weak plat_arm_calc_core_pos + .weak plat_my_core_pos .globl plat_crash_console_init .globl plat_crash_console_putc + /* ----------------------------------------------------- + * unsigned int plat_my_core_pos(void) + * This function uses the plat_arm_calc_core_pos() + * definition to get the index of the calling CPU. + * ----------------------------------------------------- + */ +func plat_my_core_pos + mrs x0, mpidr_el1 + b plat_arm_calc_core_pos +endfunc plat_my_core_pos + + /* ----------------------------------------------------- + * unsigned int plat_arm_calc_core_pos(uint64_t mpidr) + * Helper function to calculate the core position. + * With this function: CorePos = (ClusterId * 4) + + * CoreId + * ----------------------------------------------------- + */ +func plat_arm_calc_core_pos + and x1, x0, #MPIDR_CPU_MASK + and x0, x0, #MPIDR_CLUSTER_MASK + add x0, x1, x0, LSR #6 + ret +endfunc plat_arm_calc_core_pos + /* --------------------------------------------- * int plat_crash_console_init(void) * Function to initialize the crash console diff --git a/plat/arm/common/arm_bl31_setup.c b/plat/arm/common/arm_bl31_setup.c index 7e8856bad9..3fda2ef853 100644 --- a/plat/arm/common/arm_bl31_setup.c +++ b/plat/arm/common/arm_bl31_setup.c @@ -226,9 +226,6 @@ void arm_bl31_platform_setup(void) /* Initialize power controller before setting up topology */ plat_arm_pwrc_setup(); - - /* Topologies are best known to the platform. */ - plat_arm_topology_setup(); } void bl31_platform_setup(void) diff --git a/plat/arm/common/arm_common.mk b/plat/arm/common/arm_common.mk index bcc3833f99..eb2f7507a9 100644 --- a/plat/arm/common/arm_common.mk +++ b/plat/arm/common/arm_common.mk @@ -83,7 +83,8 @@ BL31_SOURCES += drivers/arm/cci/cci.c \ plat/arm/common/arm_security.c \ plat/arm/common/arm_topology.c \ plat/common/plat_gic.c \ - plat/common/aarch64/platform_mp_stack.S + plat/common/aarch64/platform_mp_stack.S \ + plat/common/aarch64/plat_psci_common.c ifneq (${TRUSTED_BOARD_BOOT},0) diff --git a/plat/arm/common/arm_pm.c b/plat/arm/common/arm_pm.c index 67c2b7398b..3f473097d6 100644 --- a/plat/arm/common/arm_pm.c +++ b/plat/arm/common/arm_pm.c @@ -33,49 +33,37 @@ #include #include - -/******************************************************************************* - * ARM standard platform utility function which is used to determine if any - * platform actions should be performed for the specified affinity instance - * given its state. Nothing needs to be done if the 'state' is not off or if - * this is not the highest affinity level which will enter the 'state'. - ******************************************************************************/ -int32_t arm_do_affinst_actions(unsigned int afflvl, unsigned int state) -{ - unsigned int max_phys_off_afflvl; - - assert(afflvl <= MPIDR_AFFLVL1); - - if (state != PSCI_STATE_OFF) - return -EAGAIN; - - /* - * Find the highest affinity level which will be suspended and postpone - * all the platform specific actions until that level is hit. - */ - max_phys_off_afflvl = psci_get_max_phys_off_afflvl(); - assert(max_phys_off_afflvl != PSCI_INVALID_DATA); - if (afflvl != max_phys_off_afflvl) - return -EAGAIN; - - return 0; -} - /******************************************************************************* * ARM standard platform handler called to check the validity of the power state * parameter. ******************************************************************************/ -int arm_validate_power_state(unsigned int power_state) +int arm_validate_power_state(unsigned int power_state, + psci_power_state_t *req_state) { + int pstate = psci_get_pstate_type(power_state); + int pwr_lvl = psci_get_pstate_pwrlvl(power_state); + int i; + + assert(req_state); + + if (pwr_lvl > PLAT_MAX_PWR_LVL) + return PSCI_E_INVALID_PARAMS; + /* Sanity check the requested state */ - if (psci_get_pstate_type(power_state) == PSTATE_TYPE_STANDBY) { + if (pstate == PSTATE_TYPE_STANDBY) { /* - * It's possible to enter standby only on affinity level 0 - * (i.e. a CPU on ARM standard platforms). - * Ignore any other affinity level. + * It's possible to enter standby only on power level 0 + * Ignore any other power level. */ - if (psci_get_pstate_afflvl(power_state) != MPIDR_AFFLVL0) + if (pwr_lvl != ARM_PWR_LVL0) return PSCI_E_INVALID_PARAMS; + + req_state->pwr_domain_state[ARM_PWR_LVL0] = + ARM_LOCAL_STATE_RET; + } else { + for (i = ARM_PWR_LVL0; i <= pwr_lvl; i++) + req_state->pwr_domain_state[i] = + ARM_LOCAL_STATE_OFF; } /* diff --git a/plat/arm/common/arm_topology.c b/plat/arm/common/arm_topology.c index 94faa9f282..cb0bb9c9c3 100644 --- a/plat/arm/common/arm_topology.c +++ b/plat/arm/common/arm_topology.c @@ -30,35 +30,49 @@ #include #include +#include #include -/* - * Weak definitions use fixed topology. Strong definitions could make topology - * configurable - */ -#pragma weak plat_get_aff_count -#pragma weak plat_get_aff_state -#pragma weak plat_arm_topology_setup +#define get_arm_cluster_core_count(mpidr)\ + (((mpidr) & 0x100) ? PLAT_ARM_CLUSTER1_CORE_COUNT :\ + PLAT_ARM_CLUSTER0_CORE_COUNT) + +/* The power domain tree descriptor which need to be exported by ARM platforms */ +extern const unsigned char arm_power_domain_tree_desc[]; -unsigned int plat_get_aff_count(unsigned int aff_lvl, unsigned long mpidr) +/******************************************************************************* + * This function returns the ARM default topology tree information. + ******************************************************************************/ +const unsigned char *plat_get_power_domain_tree_desc(void) { - /* Report 1 (absent) instance at levels higher that the cluster level */ - if (aff_lvl > MPIDR_AFFLVL1) - return 1; - - if (aff_lvl == MPIDR_AFFLVL1) - return ARM_CLUSTER_COUNT; - - return mpidr & 0x100 ? PLAT_ARM_CLUSTER1_CORE_COUNT : - PLAT_ARM_CLUSTER0_CORE_COUNT; + return arm_power_domain_tree_desc; } -unsigned int plat_get_aff_state(unsigned int aff_lvl, unsigned long mpidr) +/******************************************************************************* + * This function validates an MPIDR by checking whether it falls within the + * acceptable bounds. An error code (-1) is returned if an incorrect mpidr + * is passed. + ******************************************************************************/ +int arm_check_mpidr(u_register_t mpidr) { - return aff_lvl <= MPIDR_AFFLVL1 ? PSCI_AFF_PRESENT : PSCI_AFF_ABSENT; -} + unsigned int cluster_id, cpu_id; -void plat_arm_topology_setup(void) -{ + mpidr &= MPIDR_AFFINITY_MASK; + + if (mpidr & ~(MPIDR_CLUSTER_MASK | MPIDR_CPU_MASK)) + return -1; + + cluster_id = (mpidr >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK; + cpu_id = (mpidr >> MPIDR_AFF0_SHIFT) & MPIDR_AFFLVL_MASK; + + if (cluster_id >= ARM_CLUSTER_COUNT) + return -1; + + /* Validate cpu_id by checking whether it represents a CPU in + one of the two clusters present on the platform. */ + if (cpu_id >= get_arm_cluster_core_count(mpidr)) + return -1; + + return 0; } diff --git a/plat/arm/common/tsp/arm_tsp.mk b/plat/arm/common/tsp/arm_tsp.mk index fc6bd935eb..f285f58576 100644 --- a/plat/arm/common/tsp/arm_tsp.mk +++ b/plat/arm/common/tsp/arm_tsp.mk @@ -31,6 +31,7 @@ # TSP source files common to ARM standard platforms BL32_SOURCES += drivers/arm/gic/arm_gic.c \ drivers/arm/gic/gic_v2.c \ + plat/arm/common/arm_topology.c \ plat/arm/common/tsp/arm_tsp_setup.c \ plat/common/aarch64/platform_mp_stack.S \ plat/common/plat_gic.c diff --git a/plat/arm/css/common/aarch64/css_helpers.S b/plat/arm/css/common/aarch64/css_helpers.S index 39032782c9..a8c558b674 100644 --- a/plat/arm/css/common/aarch64/css_helpers.S +++ b/plat/arm/css/common/aarch64/css_helpers.S @@ -33,11 +33,10 @@ #include .weak plat_secondary_cold_boot_setup - .weak platform_get_entrypoint + .weak plat_get_my_entrypoint .weak platform_mem_init - .globl platform_get_core_pos - .weak platform_is_primary_cpu - + .globl plat_arm_calc_core_pos + .weak plat_is_my_cpu_primary /* ----------------------------------------------------- * void plat_secondary_cold_boot_setup (void); @@ -55,10 +54,10 @@ cb_panic: endfunc plat_secondary_cold_boot_setup /* ----------------------------------------------------- - * void platform_get_entrypoint (unsigned int mpid); + * unsigned long plat_get_my_entrypoint (void); * * Main job of this routine is to distinguish between - * a cold and warm boot. + * a cold and warm boot on the current CPU. * On a cold boot the secondaries first wait for the * platform to be initialized after which they are * hotplugged in. The primary proceeds to perform the @@ -69,28 +68,30 @@ endfunc plat_secondary_cold_boot_setup * TODO: Not a good idea to save lr in a temp reg * ----------------------------------------------------- */ -func platform_get_entrypoint +func plat_get_my_entrypoint mov x9, x30 // lr - bl platform_get_core_pos + bl plat_my_core_pos ldr x1, =TRUSTED_MAILBOXES_BASE lsl x0, x0, #TRUSTED_MAILBOX_SHIFT ldr x0, [x1, x0] ret x9 -endfunc platform_get_entrypoint +endfunc plat_get_my_entrypoint - /* - * Override the default implementation to swap the cluster order. - * This is necessary in order to match the format of the boot - * information passed by the SCP and read in platform_is_primary_cpu - * below. + /* ----------------------------------------------------------- + * unsigned int plat_arm_calc_core_pos(uint64_t mpidr) + * Function to calculate the core position by + * swapping the cluster order. This is necessary in order to + * match the format of the boot information passed by the SCP + * and read in platform_is_primary_cpu below. + * ----------------------------------------------------------- */ -func platform_get_core_pos +func plat_arm_calc_core_pos and x1, x0, #MPIDR_CPU_MASK and x0, x0, #MPIDR_CLUSTER_MASK eor x0, x0, #(1 << MPIDR_AFFINITY_BITS) // swap cluster order add x0, x1, x0, LSR #6 ret -endfunc platform_get_core_pos +endfunc plat_arm_calc_core_pos /* ----------------------------------------------------- * void platform_mem_init(void); @@ -104,19 +105,19 @@ func platform_mem_init endfunc platform_mem_init /* ----------------------------------------------------- - * unsigned int platform_is_primary_cpu (unsigned int mpid); + * unsigned int plat_is_my_cpu_primary (void); * - * Given the mpidr say whether this cpu is the primary + * Find out whether the current cpu is the primary * cpu (applicable ony after a cold boot) * ----------------------------------------------------- */ -func platform_is_primary_cpu +func plat_is_my_cpu_primary mov x9, x30 - bl platform_get_core_pos + bl plat_my_core_pos ldr x1, =SCP_BOOT_CFG_ADDR ldr x1, [x1] ubfx x1, x1, #PRIMARY_CPU_SHIFT, #PRIMARY_CPU_BIT_WIDTH cmp x0, x1 cset x0, eq ret x9 -endfunc platform_is_primary_cpu +endfunc plat_is_my_cpu_primary diff --git a/plat/arm/css/common/css_common.mk b/plat/arm/css/common/css_common.mk index 1b0404b761..d5c2fcb1d4 100644 --- a/plat/arm/css/common/css_common.mk +++ b/plat/arm/css/common/css_common.mk @@ -44,7 +44,8 @@ BL2_SOURCES += plat/arm/css/common/css_bl2_setup.c \ BL31_SOURCES += plat/arm/css/common/css_mhu.c \ plat/arm/css/common/css_pm.c \ - plat/arm/css/common/css_scpi.c + plat/arm/css/common/css_scpi.c \ + plat/arm/css/common/css_topology.c ifneq (${RESET_TO_BL31},0) diff --git a/plat/arm/css/common/css_pm.c b/plat/arm/css/common/css_pm.c index 7b0282eee4..55b1703942 100644 --- a/plat/arm/css/common/css_pm.c +++ b/plat/arm/css/common/css_pm.c @@ -41,6 +41,8 @@ #include #include "css_scpi.h" +unsigned long wakeup_address; + /******************************************************************************* * Private function to program the mailbox for a cpu before it is released * from reset. @@ -50,32 +52,27 @@ static void css_program_mailbox(uint64_t mpidr, uint64_t address) uint64_t linear_id; uint64_t mbox; - linear_id = platform_get_core_pos(mpidr); + linear_id = plat_arm_calc_core_pos(mpidr); mbox = TRUSTED_MAILBOXES_BASE + (linear_id << TRUSTED_MAILBOX_SHIFT); *((uint64_t *) mbox) = address; flush_dcache_range(mbox, sizeof(mbox)); } /******************************************************************************* - * Handler called when an affinity instance is about to be turned on. The + * Handler called when a power domain is about to be turned on. The * level and mpidr determine the affinity instance. ******************************************************************************/ -int32_t css_affinst_on(uint64_t mpidr, - uint64_t sec_entrypoint, - uint32_t afflvl, - uint32_t state) +int css_pwr_domain_on(u_register_t mpidr) { /* - * SCP takes care of powering up higher affinity levels so we + * SCP takes care of powering up parent power domains so we * only need to care about level 0 */ - if (afflvl != MPIDR_AFFLVL0) - return PSCI_E_SUCCESS; /* * Setup mailbox with address for CPU entrypoint when it next powers up */ - css_program_mailbox(mpidr, sec_entrypoint); + css_program_mailbox(mpidr, wakeup_address); scpi_set_css_power_state(mpidr, scpi_power_on, scpi_power_on, scpi_power_on); @@ -84,29 +81,22 @@ int32_t css_affinst_on(uint64_t mpidr, } /******************************************************************************* - * Handler called when an affinity instance has just been powered on after - * being turned off earlier. The level and mpidr determine the affinity - * instance. The 'state' arg. allows the platform to decide whether the cluster - * was turned off prior to wakeup and do what's necessary to setup it up - * correctly. + * Handler called when a power level has just been powered on after + * being turned off earlier. The target_state encodes the low power state that + * each level has woken up from. ******************************************************************************/ -void css_affinst_on_finish(uint32_t afflvl, uint32_t state) +void css_pwr_domain_on_finish(const psci_power_state_t *target_state) { - unsigned long mpidr; - - /* Determine if any platform actions need to be executed. */ - if (arm_do_affinst_actions(afflvl, state) == -EAGAIN) - return; - - /* Get the mpidr for this cpu */ - mpidr = read_mpidr_el1(); + assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == + ARM_LOCAL_STATE_OFF); /* * Perform the common cluster specific operations i.e enable coherency * if this cluster was off. */ - if (afflvl != MPIDR_AFFLVL0) - cci_enable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(mpidr)); + if (target_state->pwr_domain_state[ARM_PWR_LVL1] == + ARM_LOCAL_STATE_OFF) + cci_enable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(read_mpidr_el1())); /* Enable the gic cpu interface */ arm_gic_cpuif_setup(); @@ -115,16 +105,16 @@ void css_affinst_on_finish(uint32_t afflvl, uint32_t state) arm_gic_pcpu_distif_setup(); /* Clear the mailbox for this cpu. */ - css_program_mailbox(mpidr, 0); + css_program_mailbox(read_mpidr_el1(), 0); } /******************************************************************************* * Common function called while turning a cpu off or suspending it. It is called * from css_off() or css_suspend() when these functions in turn are called for - * the highest affinity level which will be powered down. It performs the - * actions common to the OFF and SUSPEND calls. + * power domain at the highest power level which will be powered down. It + * performs the actions common to the OFF and SUSPEND calls. ******************************************************************************/ -static void css_power_down_common(uint32_t afflvl) +static void css_power_down_common(const psci_power_state_t *target_state) { uint32_t cluster_state = scpi_power_on; @@ -132,7 +122,8 @@ static void css_power_down_common(uint32_t afflvl) arm_gic_cpuif_deactivate(); /* Cluster is to be turned off, so disable coherency */ - if (afflvl > MPIDR_AFFLVL0) { + if (target_state->pwr_domain_state[ARM_PWR_LVL1] == + ARM_LOCAL_STATE_OFF) { cci_disable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(read_mpidr())); cluster_state = scpi_power_off; } @@ -148,64 +139,60 @@ static void css_power_down_common(uint32_t afflvl) } /******************************************************************************* - * Handler called when an affinity instance is about to be turned off. The - * level and mpidr determine the affinity instance. The 'state' arg. allows the - * platform to decide whether the cluster is being turned off and take - * appropriate actions. - * - * CAUTION: There is no guarantee that caches will remain turned on across calls - * to this function as each affinity level is dealt with. So do not write & read - * global variables across calls. It will be wise to do flush a write to the - * global to prevent unpredictable results. + * Handler called when a power domain is about to be turned off. The + * target_state encodes the power state that each level should transition to. ******************************************************************************/ -static void css_affinst_off(uint32_t afflvl, uint32_t state) +static void css_pwr_domain_off(const psci_power_state_t *target_state) { - /* Determine if any platform actions need to be executed */ - if (arm_do_affinst_actions(afflvl, state) == -EAGAIN) - return; + assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == + ARM_LOCAL_STATE_OFF); - css_power_down_common(afflvl); + css_power_down_common(target_state); } /******************************************************************************* - * Handler called when an affinity instance is about to be suspended. The - * level and mpidr determine the affinity instance. The 'state' arg. allows the - * platform to decide whether the cluster is being turned off and take apt - * actions. The 'sec_entrypoint' determines the address in BL3-1 from where - * execution should resume. - * - * CAUTION: There is no guarantee that caches will remain turned on across calls - * to this function as each affinity level is dealt with. So do not write & read - * global variables across calls. It will be wise to do flush a write to the - * global to prevent unpredictable results. + * Handler called when a power domain is about to be suspended. The + * target_state encodes the power state that each level should transition to. ******************************************************************************/ -static void css_affinst_suspend(uint64_t sec_entrypoint, - uint32_t afflvl, - uint32_t state) +static void css_pwr_domain_suspend(const psci_power_state_t *target_state) { - /* Determine if any platform actions need to be executed */ - if (arm_do_affinst_actions(afflvl, state) == -EAGAIN) + /* + * Juno has retention only at cpu level. Just return + * as nothing is to be done for retention. + */ + if (target_state->pwr_domain_state[ARM_PWR_LVL0] == + ARM_LOCAL_STATE_RET) return; + assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == + ARM_LOCAL_STATE_OFF); + /* * Setup mailbox with address for CPU entrypoint when it next powers up. */ - css_program_mailbox(read_mpidr_el1(), sec_entrypoint); + css_program_mailbox(read_mpidr_el1(), wakeup_address); - css_power_down_common(afflvl); + css_power_down_common(target_state); } /******************************************************************************* - * Handler called when an affinity instance has just been powered on after - * having been suspended earlier. The level and mpidr determine the affinity - * instance. + * Handler called when a power domain has just been powered on after + * having been suspended earlier. The target_state encodes the low power state + * that each level has woken up from. * TODO: At the moment we reuse the on finisher and reinitialize the secure * context. Need to implement a separate suspend finisher. ******************************************************************************/ -static void css_affinst_suspend_finish(uint32_t afflvl, - uint32_t state) +static void css_pwr_domain_suspend_finish( + const psci_power_state_t *target_state) { - css_affinst_on_finish(afflvl, state); + /* + * Return as nothing is to be done on waking up from retention. + */ + if (target_state->pwr_domain_state[ARM_PWR_LVL0] == + ARM_LOCAL_STATE_RET) + return; + + css_pwr_domain_on_finish(target_state); } /******************************************************************************* @@ -244,12 +231,14 @@ static void __dead2 css_system_reset(void) } /******************************************************************************* - * Handler called when an affinity instance is about to enter standby. + * Handler called when the CPU power domain is about to enter standby. ******************************************************************************/ -void css_affinst_standby(unsigned int power_state) +void css_cpu_standby(plat_local_state_t cpu_state) { unsigned int scr; + assert(cpu_state == ARM_LOCAL_STATE_RET); + scr = read_scr_el3(); /* Enable PhysicalIRQ bit for NS world to wake the CPU */ write_scr_el3(scr | SCR_IRQ_BIT); @@ -267,23 +256,28 @@ void css_affinst_standby(unsigned int power_state) /******************************************************************************* * Export the platform handlers to enable psci to invoke them ******************************************************************************/ -static const plat_pm_ops_t css_ops = { - .affinst_on = css_affinst_on, - .affinst_on_finish = css_affinst_on_finish, - .affinst_off = css_affinst_off, - .affinst_standby = css_affinst_standby, - .affinst_suspend = css_affinst_suspend, - .affinst_suspend_finish = css_affinst_suspend_finish, +static const plat_psci_ops_t css_ops = { + .pwr_domain_on = css_pwr_domain_on, + .pwr_domain_on_finish = css_pwr_domain_on_finish, + .pwr_domain_off = css_pwr_domain_off, + .cpu_standby = css_cpu_standby, + .pwr_domain_suspend = css_pwr_domain_suspend, + .pwr_domain_suspend_finish = css_pwr_domain_suspend_finish, .system_off = css_system_off, .system_reset = css_system_reset, .validate_power_state = arm_validate_power_state }; /******************************************************************************* - * Export the platform specific power ops. + * Export the platform specific psci ops. ******************************************************************************/ -int32_t platform_setup_pm(const plat_pm_ops_t **plat_ops) +int plat_setup_psci_ops(uintptr_t sec_entrypoint, + const plat_psci_ops_t **psci_ops) { - *plat_ops = &css_ops; + *psci_ops = &css_ops; + + wakeup_address = sec_entrypoint; + flush_dcache_range((unsigned long)&wakeup_address, + sizeof(wakeup_address)); return 0; } diff --git a/plat/arm/css/common/css_topology.c b/plat/arm/css/common/css_topology.c new file mode 100644 index 0000000000..381e786b92 --- /dev/null +++ b/plat/arm/css/common/css_topology.c @@ -0,0 +1,70 @@ +/* + * 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 + +/* + * On ARM platforms, by default the cluster power level is treated as the + * highest. The first entry in the power domain descriptor specifies the + * number of cluster power domains i.e. 2. + */ +#define CSS_PWR_DOMAINS_AT_MAX_PWR_LVL ARM_CLUSTER_COUNT + +/* + * The CSS power domain tree descriptor. The cluster power domains are + * arranged so that when the PSCI generic code creates the power domain tree, + * the indices of the CPU power domain nodes it allocates match the linear + * indices returned by plat_core_pos_by_mpidr() i.e. + * CLUSTER1 CPUs are allocated indices from 0 to 3 and the higher indices for + * CLUSTER0 CPUs. + */ +const unsigned char arm_power_domain_tree_desc[] = { + /* No of root nodes */ + CSS_PWR_DOMAINS_AT_MAX_PWR_LVL, + /* No of children for the first node */ + PLAT_ARM_CLUSTER1_CORE_COUNT, + /* No of children for the second node */ + PLAT_ARM_CLUSTER0_CORE_COUNT +}; + + +/****************************************************************************** + * This function implements a part of the critical interface between the psci + * generic layer and the platform that allows the former to query the platform + * to convert an MPIDR to a unique linear index. An error code (-1) is + * returned in case the MPIDR is invalid. + *****************************************************************************/ +int plat_core_pos_by_mpidr(u_register_t mpidr) +{ + if (arm_check_mpidr(mpidr) == 0) + return plat_arm_calc_core_pos(mpidr); + + return -1; +} From 2204afded5cf9557ef1bb934fd15a74b9fb42244 Mon Sep 17 00:00:00 2001 From: Soby Mathew Date: Thu, 16 Apr 2015 14:49:09 +0100 Subject: [PATCH 13/20] PSCI: Demonstrate support for composite power states This patch adds support to the Juno and FVP ports for composite power states with both the original and extended state-id power-state formats. Both the platform ports use the recommended state-id encoding as specified in Section 6.5 of the PSCI specification (ARM DEN 0022C). The platform build flag ARM_RECOM_STATE_ID_ENC is used to include this support. By default, to maintain backwards compatibility, the original power state parameter format is used and the state-id field is expected to be zero. Change-Id: Ie721b961957eaecaca5bf417a30952fe0627ef10 --- docs/user-guide.md | 8 +++++ include/plat/arm/common/plat_arm.h | 29 ++++++++++++++++++ plat/arm/board/fvp/fvp_pm.c | 21 +++++++++++++ plat/arm/common/arm_common.mk | 17 +++++++++++ plat/arm/common/arm_pm.c | 49 ++++++++++++++++++++++++++++++ plat/arm/css/common/css_pm.c | 21 +++++++++++++ 6 files changed, 145 insertions(+) diff --git a/docs/user-guide.md b/docs/user-guide.md index 5be3c3dc1a..b9b69b36bf 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -394,6 +394,14 @@ map is explained in the [Firmware Design]. this option, `arm_rotprivk_rsa.pem` must be specified as `ROT_KEY` when creating the certificates. +* `ARM_RECOM_STATE_ID_ENC`: The PSCI1.0 specification recommends an encoding + for the construction of composite state-ID in the power-state parameter. + The existing PSCI clients currently do not support this encoding of + State-ID yet. Hence this flag is used to configure whether to use the + recommended State-ID encoding or not. The default value of this flag is 0, + in which case the platform is configured to expect NULL in the State-ID + field of power-state parameter. + #### ARM CSS platform specific build options * `CSS_DETECT_PRE_1_7_0_SCP`: Boolean flag to detect SCP version diff --git a/include/plat/arm/common/plat_arm.h b/include/plat/arm/common/plat_arm.h index 29f1c9053c..56b5eda573 100644 --- a/include/plat/arm/common/plat_arm.h +++ b/include/plat/arm/common/plat_arm.h @@ -148,6 +148,35 @@ CASSERT(PLAT_PCPU_DATA_SIZE == sizeof(arm_cpu_data_t), #endif /* IMAGE_BL31 */ +#if ARM_RECOM_STATE_ID_ENC +/* + * Macros used to parse state information from State-ID if it is using the + * recommended encoding for State-ID. + */ +#define ARM_LOCAL_PSTATE_WIDTH 4 +#define ARM_LOCAL_PSTATE_MASK ((1 << ARM_LOCAL_PSTATE_WIDTH) - 1) + +/* Macros to construct the composite power state */ + +/* Make composite power state parameter till power level 0 */ +#if PSCI_EXTENDED_STATE_ID + +#define arm_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type) \ + (((lvl0_state) << PSTATE_ID_SHIFT) | ((type) << PSTATE_TYPE_SHIFT)) +#else +#define arm_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type) \ + (((lvl0_state) << PSTATE_ID_SHIFT) | \ + ((pwr_lvl) << PSTATE_PWR_LVL_SHIFT) | \ + ((type) << PSTATE_TYPE_SHIFT)) +#endif /* __PSCI_EXTENDED_STATE_ID__ */ + +/* Make composite power state parameter till power level 1 */ +#define arm_make_pwrstate_lvl1(lvl1_state, lvl0_state, pwr_lvl, type) \ + (((lvl1_state) << ARM_LOCAL_PSTATE_WIDTH) | \ + arm_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type)) + +#endif /* __ARM_RECOM_STATE_ID_ENC__ */ + /* CCI utility functions */ void arm_cci_init(void); diff --git a/plat/arm/board/fvp/fvp_pm.c b/plat/arm/board/fvp/fvp_pm.c index c59ffd1969..56d6502925 100644 --- a/plat/arm/board/fvp/fvp_pm.c +++ b/plat/arm/board/fvp/fvp_pm.c @@ -49,6 +49,27 @@ typedef volatile struct mailbox { unsigned long value __aligned(CACHE_WRITEBACK_GRANULE); } mailbox_t; +#if ARM_RECOM_STATE_ID_ENC +/* + * The table storing the valid idle power states. Ensure that the + * array entries are populated in ascending order of state-id to + * enable us to use binary search during power state validation. + * The table must be terminated by a NULL entry. + */ +const unsigned int arm_pm_idle_states[] = { + /* State-id - 0x01 */ + arm_make_pwrstate_lvl1(ARM_LOCAL_STATE_RUN, ARM_LOCAL_STATE_RET, + ARM_PWR_LVL0, PSTATE_TYPE_STANDBY), + /* State-id - 0x02 */ + arm_make_pwrstate_lvl1(ARM_LOCAL_STATE_RUN, ARM_LOCAL_STATE_OFF, + ARM_PWR_LVL0, PSTATE_TYPE_POWERDOWN), + /* State-id - 0x22 */ + arm_make_pwrstate_lvl1(ARM_LOCAL_STATE_OFF, ARM_LOCAL_STATE_OFF, + ARM_PWR_LVL1, PSTATE_TYPE_POWERDOWN), + 0, +}; +#endif + /******************************************************************************* * Private FVP function to program the mailbox for a cpu before it is released * from reset. diff --git a/plat/arm/common/arm_common.mk b/plat/arm/common/arm_common.mk index eb2f7507a9..12346194d0 100644 --- a/plat/arm/common/arm_common.mk +++ b/plat/arm/common/arm_common.mk @@ -46,6 +46,23 @@ endif # Process flags $(eval $(call add_define,ARM_TSP_RAM_LOCATION_ID)) +# For the original power-state parameter format, the State-ID can be encoded +# according to the recommended encoding or zero. This flag determines which +# State-ID encoding to be parsed. +ARM_RECOM_STATE_ID_ENC := 0 + +# If the PSCI_EXTENDED_STATE_ID is set, then the recommended state ID need to +# be used. Else throw a build error. +ifeq (${PSCI_EXTENDED_STATE_ID}, 1) + ifeq (${ARM_RECOM_STATE_ID_ENC}, 0) + $(error "Incompatible STATE_ID build option specified") + endif +endif + +# Process ARM_RECOM_STATE_ID_ENC flag +$(eval $(call assert_boolean,ARM_RECOM_STATE_ID_ENC)) +$(eval $(call add_define,ARM_RECOM_STATE_ID_ENC)) + PLAT_INCLUDES += -Iinclude/common/tbbr \ -Iinclude/plat/arm/common \ -Iinclude/plat/arm/common/aarch64 diff --git a/plat/arm/common/arm_pm.c b/plat/arm/common/arm_pm.c index 3f473097d6..71fbf9f11f 100644 --- a/plat/arm/common/arm_pm.c +++ b/plat/arm/common/arm_pm.c @@ -31,8 +31,15 @@ #include #include #include +#include #include +#if ARM_RECOM_STATE_ID_ENC +extern unsigned int arm_pm_idle_states[]; +#endif /* __ARM_RECOM_STATE_ID_ENC__ */ + + +#if !ARM_RECOM_STATE_ID_ENC /******************************************************************************* * ARM standard platform handler called to check the validity of the power state * parameter. @@ -74,3 +81,45 @@ int arm_validate_power_state(unsigned int power_state, return PSCI_E_SUCCESS; } + +#else +/******************************************************************************* + * ARM standard platform handler called to check the validity of the power + * state parameter. The power state parameter has to be a composite power + * state. + ******************************************************************************/ +int arm_validate_power_state(unsigned int power_state, + psci_power_state_t *req_state) +{ + unsigned int state_id; + int i; + + assert(req_state); + + /* + * Currently we are using a linear search for finding the matching + * entry in the idle power state array. This can be made a binary + * search if the number of entries justify the additional complexity. + */ + for (i = 0; !!arm_pm_idle_states[i]; i++) { + if (power_state == arm_pm_idle_states[i]) + break; + } + + /* Return error if entry not found in the idle state array */ + if (!arm_pm_idle_states[i]) + return PSCI_E_INVALID_PARAMS; + + i = 0; + state_id = psci_get_pstate_id(power_state); + + /* Parse the State ID and populate the state info parameter */ + while (state_id) { + req_state->pwr_domain_state[i++] = state_id & + ARM_LOCAL_PSTATE_MASK; + state_id >>= ARM_LOCAL_PSTATE_WIDTH; + } + + return PSCI_E_SUCCESS; +} +#endif /* __ARM_RECOM_STATE_ID_ENC__ */ diff --git a/plat/arm/css/common/css_pm.c b/plat/arm/css/common/css_pm.c index 55b1703942..49c364d5c2 100644 --- a/plat/arm/css/common/css_pm.c +++ b/plat/arm/css/common/css_pm.c @@ -43,6 +43,27 @@ unsigned long wakeup_address; +#if ARM_RECOM_STATE_ID_ENC +/* + * The table storing the valid idle power states. Ensure that the + * array entries are populated in ascending order of state-id to + * enable us to use binary search during power state validation. + * The table must be terminated by a NULL entry. + */ +const unsigned int arm_pm_idle_states[] = { + /* State-id - 0x01 */ + arm_make_pwrstate_lvl1(ARM_LOCAL_STATE_RUN, ARM_LOCAL_STATE_RET, + ARM_PWR_LVL0, PSTATE_TYPE_STANDBY), + /* State-id - 0x02 */ + arm_make_pwrstate_lvl1(ARM_LOCAL_STATE_RUN, ARM_LOCAL_STATE_OFF, + ARM_PWR_LVL0, PSTATE_TYPE_POWERDOWN), + /* State-id - 0x22 */ + arm_make_pwrstate_lvl1(ARM_LOCAL_STATE_OFF, ARM_LOCAL_STATE_OFF, + ARM_PWR_LVL1, PSTATE_TYPE_POWERDOWN), + 0, +}; +#endif + /******************************************************************************* * Private function to program the mailbox for a cpu before it is released * from reset. From 804040d106184a93a9fe188743d1d8a8571dea71 Mon Sep 17 00:00:00 2001 From: Sandrine Bailleux Date: Fri, 10 Jul 2015 16:49:31 +0100 Subject: [PATCH 14/20] PSCI: Use a single mailbox for warm reset for FVP and Juno Since there is a unique warm reset entry point, the FVP and Juno port can use a single mailbox instead of maintaining one per core. The mailbox gets programmed only once when plat_setup_psci_ops() is invoked during PSCI initialization. This means mailbox is not zeroed out during wakeup. Change-Id: Ieba032a90b43650f970f197340ebb0ce5548d432 --- docs/porting-guide.md | 3 - include/plat/arm/css/common/css_def.h | 3 +- plat/arm/board/fvp/aarch64/fvp_helpers.S | 80 +++++++++++------------ plat/arm/board/fvp/fvp_def.h | 1 - plat/arm/board/fvp/fvp_pm.c | 31 ++------- plat/arm/css/common/aarch64/css_helpers.S | 30 ++++----- plat/arm/css/common/css_pm.c | 32 ++------- 7 files changed, 64 insertions(+), 116 deletions(-) diff --git a/docs/porting-guide.md b/docs/porting-guide.md index 0f10fd46ab..81cbe1be06 100644 --- a/docs/porting-guide.md +++ b/docs/porting-guide.md @@ -468,9 +468,6 @@ return value indicates that the CPU is the primary CPU. This function is called before any access to data is made by the firmware, in order to carry out any essential memory initialization. -The ARM FVP port uses this function to initialize the mailbox memory used for -providing the warm-boot entry-point addresses. - ### Function: plat_get_rotpk_info() diff --git a/include/plat/arm/css/common/css_def.h b/include/plat/arm/css/common/css_def.h index 268438ff69..e3dd2b0f50 100644 --- a/include/plat/arm/css/common/css_def.h +++ b/include/plat/arm/css/common/css_def.h @@ -39,8 +39,7 @@ *************************************************************************/ #define MHU_PAYLOAD_CACHED 0 -#define TRUSTED_MAILBOXES_BASE ARM_TRUSTED_SRAM_BASE -#define TRUSTED_MAILBOX_SHIFT 4 +#define TRUSTED_MAILBOX_BASE ARM_TRUSTED_SRAM_BASE #define NSROM_BASE 0x1f000000 #define NSROM_SIZE 0x00001000 diff --git a/plat/arm/board/fvp/aarch64/fvp_helpers.S b/plat/arm/board/fvp/aarch64/fvp_helpers.S index 2787ee674d..9cf3c73c6b 100644 --- a/plat/arm/board/fvp/aarch64/fvp_helpers.S +++ b/plat/arm/board/fvp/aarch64/fvp_helpers.S @@ -96,29 +96,30 @@ cb_panic: b cb_panic endfunc plat_secondary_cold_boot_setup - - /* ----------------------------------------------------- + /* --------------------------------------------------------------------- * unsigned long plat_get_my_entrypoint (void); * - * Main job of this routine is to distinguish between - * a cold and warm boot on the current CPU. - * On a cold boot the secondaries first wait for the - * platform to be initialized after which they are - * hotplugged in. The primary proceeds to perform the - * platform initialization. - * On a warm boot, each cpu jumps to the address in its - * mailbox. + * Main job of this routine is to distinguish between a cold and warm + * boot. On FVP, this information can be queried from the power + * controller. The Power Control SYS Status Register (PSYSR) indicates + * the wake-up reason for the CPU. + * + * For a cold boot, return 0. + * For a warm boot, read the mailbox and return the address it contains. * - * TODO: Not a good idea to save lr in a temp reg * TODO: PSYSR is a common register and should be * accessed using locks. Since its not possible * to use locks immediately after a cold reset * we are relying on the fact that after a cold * reset all cpus will read the same WK field - * ----------------------------------------------------- + * --------------------------------------------------------------------- */ func plat_get_my_entrypoint - mov x9, x30 // lr + /* --------------------------------------------------------------------- + * When bit PSYSR.WK indicates either "Wake by PPONR" or "Wake by GIC + * WakeRequest signal" then it is a warm boot. + * --------------------------------------------------------------------- + */ mrs x2, mpidr_el1 ldr x1, =PWRC_BASE str w2, [x1, #PSYSR_OFF] @@ -128,46 +129,41 @@ func plat_get_my_entrypoint beq warm_reset cmp w2, #WKUP_GICREQ beq warm_reset + + /* Cold reset */ mov x0, #0 - b exit + ret + warm_reset: - /* --------------------------------------------- - * A per-cpu mailbox is maintained in the tru- - * sted DRAM. Its flushed out of the caches - * after every update using normal memory so - * its safe to read it here with SO attributes - * --------------------------------------------- + /* --------------------------------------------------------------------- + * A mailbox is maintained in the trusted SRAM. It is flushed out of the + * caches after every update using normal memory so it is safe to read + * it here with SO attributes. + * --------------------------------------------------------------------- */ - ldr x10, =MBOX_BASE - bl plat_my_core_pos - lsl x0, x0, #ARM_CACHE_WRITEBACK_SHIFT - ldr x0, [x10, x0] + mov_imm x0, MBOX_BASE + ldr x0, [x0] cbz x0, _panic -exit: - ret x9 -_panic: b _panic + ret + + /* --------------------------------------------------------------------- + * The power controller indicates this is a warm reset but the mailbox + * is empty. This should never happen! + * --------------------------------------------------------------------- + */ +_panic: + b _panic endfunc plat_get_my_entrypoint - /* ----------------------------------------------------- + /* --------------------------------------------------------------------- * void platform_mem_init (void); * - * Zero out the mailbox registers in the shared memory. - * The mmu is turned off right now and only the primary can - * ever execute this code. Secondaries will read the - * mailboxes using SO accesses. In short, BL31 will - * update the mailboxes after mapping the tzdram as - * normal memory. It will flush its copy after update. - * BL1 will always read the mailboxes with the MMU off - * ----------------------------------------------------- + * Nothing to do on FVP, the Trusted SRAM is available straight away + * after reset. + * --------------------------------------------------------------------- */ func platform_mem_init - ldr x0, =MBOX_BASE - mov w1, #PLATFORM_CORE_COUNT -loop: - str xzr, [x0], #CACHE_WRITEBACK_GRANULE - subs w1, w1, #1 - b.gt loop ret endfunc platform_mem_init diff --git a/plat/arm/board/fvp/fvp_def.h b/plat/arm/board/fvp/fvp_def.h index 842a287b5e..692948150b 100644 --- a/plat/arm/board/fvp/fvp_def.h +++ b/plat/arm/board/fvp/fvp_def.h @@ -139,7 +139,6 @@ /* Entrypoint mailboxes */ #define MBOX_BASE ARM_SHARED_RAM_BASE -#define MBOX_SIZE 0x200 #endif /* __FVP_DEF_H__ */ diff --git a/plat/arm/board/fvp/fvp_pm.c b/plat/arm/board/fvp/fvp_pm.c index 56d6502925..8be51054b2 100644 --- a/plat/arm/board/fvp/fvp_pm.c +++ b/plat/arm/board/fvp/fvp_pm.c @@ -43,11 +43,6 @@ #include "fvp_def.h" #include "fvp_private.h" -unsigned long wakeup_address; - -typedef volatile struct mailbox { - unsigned long value __aligned(CACHE_WRITEBACK_GRANULE); -} mailbox_t; #if ARM_RECOM_STATE_ID_ENC /* @@ -74,16 +69,11 @@ const unsigned int arm_pm_idle_states[] = { * Private FVP function to program the mailbox for a cpu before it is released * from reset. ******************************************************************************/ -static void fvp_program_mailbox(uint64_t mpidr, uint64_t address) +static void fvp_program_mailbox(uintptr_t address) { - uint64_t linear_id; - mailbox_t *fvp_mboxes; - - linear_id = plat_arm_calc_core_pos(mpidr); - fvp_mboxes = (mailbox_t *)MBOX_BASE; - fvp_mboxes[linear_id].value = address; - flush_dcache_range((unsigned long) &fvp_mboxes[linear_id], - sizeof(unsigned long)); + uintptr_t *mailbox = (void *) MBOX_BASE; + *mailbox = address; + flush_dcache_range((uintptr_t) mailbox, sizeof(*mailbox)); } /******************************************************************************* @@ -150,9 +140,7 @@ int fvp_pwr_domain_on(u_register_t mpidr) psysr = fvp_pwrc_read_psysr(mpidr); } while (psysr & PSYSR_AFF_L0); - fvp_program_mailbox(mpidr, wakeup_address); fvp_pwrc_write_pponr(mpidr); - return rc; } @@ -200,9 +188,6 @@ void fvp_pwr_domain_suspend(const psci_power_state_t *target_state) /* Get the mpidr for this cpu */ mpidr = read_mpidr_el1(); - /* Program the jump address for the this cpu */ - fvp_program_mailbox(mpidr, wakeup_address); - /* Program the power controller to enable wakeup interrupts. */ fvp_pwrc_set_wen(mpidr); @@ -254,9 +239,6 @@ void fvp_pwr_domain_on_finish(const psci_power_state_t *target_state) */ fvp_pwrc_clr_wen(mpidr); - /* Zero the jump address in the mailbox for this cpu */ - fvp_program_mailbox(mpidr, 0); - /* Enable the gic cpu interface */ arm_gic_cpuif_setup(); @@ -332,9 +314,8 @@ int plat_setup_psci_ops(uintptr_t sec_entrypoint, const plat_psci_ops_t **psci_ops) { *psci_ops = &fvp_plat_psci_ops; - wakeup_address = sec_entrypoint; - flush_dcache_range((unsigned long)&wakeup_address, - sizeof(wakeup_address)); + /* Program the jump address */ + fvp_program_mailbox(sec_entrypoint); return 0; } diff --git a/plat/arm/css/common/aarch64/css_helpers.S b/plat/arm/css/common/aarch64/css_helpers.S index a8c558b674..478d5cf2ba 100644 --- a/plat/arm/css/common/aarch64/css_helpers.S +++ b/plat/arm/css/common/aarch64/css_helpers.S @@ -53,28 +53,24 @@ cb_panic: b cb_panic endfunc plat_secondary_cold_boot_setup - /* ----------------------------------------------------- + /* --------------------------------------------------------------------- * unsigned long plat_get_my_entrypoint (void); * - * Main job of this routine is to distinguish between - * a cold and warm boot on the current CPU. - * On a cold boot the secondaries first wait for the - * platform to be initialized after which they are - * hotplugged in. The primary proceeds to perform the - * platform initialization. - * On a warm boot, each cpu jumps to the address in its - * mailbox. + * Main job of this routine is to distinguish between a cold and a warm + * boot. On CSS platforms, this distinction is based on the contents of + * the Trusted Mailbox. It is initialised to zero by the SCP before the + * AP cores are released from reset. Therefore, a zero mailbox means + * it's a cold reset. * - * TODO: Not a good idea to save lr in a temp reg - * ----------------------------------------------------- + * This functions returns the contents of the mailbox, i.e.: + * - 0 for a cold boot; + * - the warm boot entrypoint for a warm boot. + * --------------------------------------------------------------------- */ func plat_get_my_entrypoint - mov x9, x30 // lr - bl plat_my_core_pos - ldr x1, =TRUSTED_MAILBOXES_BASE - lsl x0, x0, #TRUSTED_MAILBOX_SHIFT - ldr x0, [x1, x0] - ret x9 + mov_imm x0, TRUSTED_MAILBOX_BASE + ldr x0, [x0] + ret endfunc plat_get_my_entrypoint /* ----------------------------------------------------------- diff --git a/plat/arm/css/common/css_pm.c b/plat/arm/css/common/css_pm.c index 49c364d5c2..435ed2aa66 100644 --- a/plat/arm/css/common/css_pm.c +++ b/plat/arm/css/common/css_pm.c @@ -41,7 +41,6 @@ #include #include "css_scpi.h" -unsigned long wakeup_address; #if ARM_RECOM_STATE_ID_ENC /* @@ -68,15 +67,11 @@ const unsigned int arm_pm_idle_states[] = { * Private function to program the mailbox for a cpu before it is released * from reset. ******************************************************************************/ -static void css_program_mailbox(uint64_t mpidr, uint64_t address) +static void css_program_mailbox(uintptr_t address) { - uint64_t linear_id; - uint64_t mbox; - - linear_id = plat_arm_calc_core_pos(mpidr); - mbox = TRUSTED_MAILBOXES_BASE + (linear_id << TRUSTED_MAILBOX_SHIFT); - *((uint64_t *) mbox) = address; - flush_dcache_range(mbox, sizeof(mbox)); + uintptr_t *mailbox = (void *) TRUSTED_MAILBOX_BASE; + *mailbox = address; + flush_dcache_range((uintptr_t) mailbox, sizeof(*mailbox)); } /******************************************************************************* @@ -89,12 +84,6 @@ int css_pwr_domain_on(u_register_t mpidr) * SCP takes care of powering up parent power domains so we * only need to care about level 0 */ - - /* - * Setup mailbox with address for CPU entrypoint when it next powers up - */ - css_program_mailbox(mpidr, wakeup_address); - scpi_set_css_power_state(mpidr, scpi_power_on, scpi_power_on, scpi_power_on); @@ -124,9 +113,6 @@ void css_pwr_domain_on_finish(const psci_power_state_t *target_state) /* todo: Is this setup only needed after a cold boot? */ arm_gic_pcpu_distif_setup(); - - /* Clear the mailbox for this cpu. */ - css_program_mailbox(read_mpidr_el1(), 0); } /******************************************************************************* @@ -188,11 +174,6 @@ static void css_pwr_domain_suspend(const psci_power_state_t *target_state) assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == ARM_LOCAL_STATE_OFF); - /* - * Setup mailbox with address for CPU entrypoint when it next powers up. - */ - css_program_mailbox(read_mpidr_el1(), wakeup_address); - css_power_down_common(target_state); } @@ -297,8 +278,7 @@ int plat_setup_psci_ops(uintptr_t sec_entrypoint, { *psci_ops = &css_ops; - wakeup_address = sec_entrypoint; - flush_dcache_range((unsigned long)&wakeup_address, - sizeof(wakeup_address)); + /* Setup mailbox with entry point. */ + css_program_mailbox(sec_entrypoint); return 0; } From a6bd5ffbb0b8e4c767190a69ed07f3db0a8052d8 Mon Sep 17 00:00:00 2001 From: Sandrine Bailleux Date: Fri, 10 Jul 2015 17:33:26 +0100 Subject: [PATCH 15/20] PSCI: Pool platform_mem_init() in common ARM platforms code Now that the FVP mailbox is no longer zeroed, the function platform_mem_init() does nothing both on FVP and on Juno. Therefore, this patch pools it as the default implementation on ARM platforms. Change-Id: I007220f4531f15e8b602c3368a1129a5e3a38d91 --- plat/arm/board/fvp/aarch64/fvp_helpers.S | 13 ------------- plat/arm/common/aarch64/arm_helpers.S | 10 ++++++++++ plat/arm/css/common/aarch64/css_helpers.S | 12 ------------ 3 files changed, 10 insertions(+), 25 deletions(-) diff --git a/plat/arm/board/fvp/aarch64/fvp_helpers.S b/plat/arm/board/fvp/aarch64/fvp_helpers.S index 9cf3c73c6b..ec5ec8eff9 100644 --- a/plat/arm/board/fvp/aarch64/fvp_helpers.S +++ b/plat/arm/board/fvp/aarch64/fvp_helpers.S @@ -38,7 +38,6 @@ .globl plat_secondary_cold_boot_setup .globl plat_get_my_entrypoint - .globl platform_mem_init .globl plat_is_my_cpu_primary .macro fvp_choose_gicmmap param1, param2, x_tmp, w_tmp, res @@ -156,18 +155,6 @@ _panic: endfunc plat_get_my_entrypoint - /* --------------------------------------------------------------------- - * void platform_mem_init (void); - * - * Nothing to do on FVP, the Trusted SRAM is available straight away - * after reset. - * --------------------------------------------------------------------- - */ -func platform_mem_init - ret -endfunc platform_mem_init - - func plat_is_my_cpu_primary mrs x0, mpidr_el1 and x0, x0, #(MPIDR_CLUSTER_MASK | MPIDR_CPU_MASK) diff --git a/plat/arm/common/aarch64/arm_helpers.S b/plat/arm/common/aarch64/arm_helpers.S index daf08faa9d..87179daead 100644 --- a/plat/arm/common/aarch64/arm_helpers.S +++ b/plat/arm/common/aarch64/arm_helpers.S @@ -34,6 +34,7 @@ .weak plat_my_core_pos .globl plat_crash_console_init .globl plat_crash_console_putc + .globl platform_mem_init /* ----------------------------------------------------- @@ -86,3 +87,12 @@ func plat_crash_console_putc mov_imm x1, PLAT_ARM_CRASH_UART_BASE b console_core_putc endfunc plat_crash_console_putc + + /* --------------------------------------------------------------------- + * We don't need to carry out any memory initialization on ARM + * platforms. The Secure RAM is accessible straight away. + * --------------------------------------------------------------------- + */ +func platform_mem_init + ret +endfunc platform_mem_init diff --git a/plat/arm/css/common/aarch64/css_helpers.S b/plat/arm/css/common/aarch64/css_helpers.S index 478d5cf2ba..5d5bf863c0 100644 --- a/plat/arm/css/common/aarch64/css_helpers.S +++ b/plat/arm/css/common/aarch64/css_helpers.S @@ -34,7 +34,6 @@ .weak plat_secondary_cold_boot_setup .weak plat_get_my_entrypoint - .weak platform_mem_init .globl plat_arm_calc_core_pos .weak plat_is_my_cpu_primary @@ -89,17 +88,6 @@ func plat_arm_calc_core_pos ret endfunc plat_arm_calc_core_pos - /* ----------------------------------------------------- - * void platform_mem_init(void); - * - * We don't need to carry out any memory initialization - * on CSS platforms. The Secure RAM is accessible straight away. - * ----------------------------------------------------- - */ -func platform_mem_init - ret -endfunc platform_mem_init - /* ----------------------------------------------------- * unsigned int plat_is_my_cpu_primary (void); * From fd650ff61b80a2155002def233ffddb439e3c071 Mon Sep 17 00:00:00 2001 From: Soby Mathew Date: Wed, 8 Jul 2015 21:45:46 +0100 Subject: [PATCH 16/20] PSCI: Migrate SPDs and TSP to the new platform and framework API The new PSCI frameworks mandates that the platform APIs and the various frameworks in Trusted Firmware migrate away from MPIDR based core identification to one based on core index. Deprecated versions of the old APIs are still present to provide compatibility but their implementations are not optimal. This patch migrates the various SPDs exisiting within Trusted Firmware tree and TSP to the new APIs. Change-Id: Ifc37e7071c5769b5ded21d0b6a071c8c4cab7836 --- bl32/tsp/aarch64/tsp_entrypoint.S | 8 ++--- bl32/tsp/tsp_interrupt.c | 21 +++++------ bl32/tsp/tsp_main.c | 60 ++++++++++++++----------------- bl32/tsp/tsp_timer.c | 6 ++-- services/spd/opteed/opteed_main.c | 17 ++++----- services/spd/opteed/opteed_pm.c | 22 +++++------- services/spd/tlkd/tlkd_main.c | 3 +- services/spd/tspd/tspd_main.c | 21 +++++------ services/spd/tspd/tspd_pm.c | 24 +++++-------- 9 files changed, 74 insertions(+), 108 deletions(-) diff --git a/bl32/tsp/aarch64/tsp_entrypoint.S b/bl32/tsp/aarch64/tsp_entrypoint.S index 5b989e3de7..4e8da7454f 100644 --- a/bl32/tsp/aarch64/tsp_entrypoint.S +++ b/bl32/tsp/aarch64/tsp_entrypoint.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-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: @@ -122,8 +122,7 @@ func tsp_entrypoint * primary cpu is running at the moment. * -------------------------------------------- */ - mrs x0, mpidr_el1 - bl platform_set_stack + bl plat_set_my_stack /* --------------------------------------------- * Perform early platform setup & platform @@ -248,8 +247,7 @@ func tsp_cpu_on_entry * enabled. * -------------------------------------------- */ - mrs x0, mpidr_el1 - bl platform_set_stack + bl plat_set_my_stack /* -------------------------------------------- * Enable the MMU with the DCache disabled. It diff --git a/bl32/tsp/tsp_interrupt.c b/bl32/tsp/tsp_interrupt.c index 9abe9baa0d..139642d094 100644 --- a/bl32/tsp/tsp_interrupt.c +++ b/bl32/tsp/tsp_interrupt.c @@ -49,8 +49,7 @@ ******************************************************************************/ void tsp_update_sync_fiq_stats(uint32_t type, uint64_t elr_el3) { - uint64_t mpidr = read_mpidr(); - uint32_t linear_id = platform_get_core_pos(mpidr); + uint32_t linear_id = plat_my_core_pos(); tsp_stats[linear_id].sync_fiq_count++; if (type == TSP_HANDLE_FIQ_AND_RETURN) @@ -59,9 +58,9 @@ void tsp_update_sync_fiq_stats(uint32_t type, uint64_t elr_el3) #if LOG_LEVEL >= LOG_LEVEL_VERBOSE spin_lock(&console_lock); VERBOSE("TSP: cpu 0x%lx sync fiq request from 0x%lx\n", - mpidr, elr_el3); + read_mpidr(), elr_el3); VERBOSE("TSP: cpu 0x%lx: %d sync fiq requests, %d sync fiq returns\n", - mpidr, + read_mpidr(), tsp_stats[linear_id].sync_fiq_count, tsp_stats[linear_id].sync_fiq_ret_count); spin_unlock(&console_lock); @@ -77,8 +76,7 @@ void tsp_update_sync_fiq_stats(uint32_t type, uint64_t elr_el3) ******************************************************************************/ int32_t tsp_fiq_handler(void) { - uint64_t mpidr = read_mpidr(); - uint32_t linear_id = platform_get_core_pos(mpidr), id; + uint32_t linear_id = plat_my_core_pos(), id; /* * Get the highest priority pending interrupt id and see if it is the @@ -105,9 +103,9 @@ int32_t tsp_fiq_handler(void) #if LOG_LEVEL >= LOG_LEVEL_VERBOSE spin_lock(&console_lock); VERBOSE("TSP: cpu 0x%lx handled fiq %d\n", - mpidr, id); + read_mpidr(), id); VERBOSE("TSP: cpu 0x%lx: %d fiq requests\n", - mpidr, tsp_stats[linear_id].fiq_count); + read_mpidr(), tsp_stats[linear_id].fiq_count); spin_unlock(&console_lock); #endif return 0; @@ -115,15 +113,14 @@ int32_t tsp_fiq_handler(void) int32_t tsp_irq_received(void) { - uint64_t mpidr = read_mpidr(); - uint32_t linear_id = platform_get_core_pos(mpidr); + uint32_t linear_id = plat_my_core_pos(); tsp_stats[linear_id].irq_count++; #if LOG_LEVEL >= LOG_LEVEL_VERBOSE spin_lock(&console_lock); - VERBOSE("TSP: cpu 0x%lx received irq\n", mpidr); + VERBOSE("TSP: cpu 0x%lx received irq\n", read_mpidr()); VERBOSE("TSP: cpu 0x%lx: %d irq requests\n", - mpidr, tsp_stats[linear_id].irq_count); + read_mpidr(), tsp_stats[linear_id].irq_count); spin_unlock(&console_lock); #endif return TSP_PREEMPTED; diff --git a/bl32/tsp/tsp_main.c b/bl32/tsp/tsp_main.c index 2a4ce4f9d4..b002addfba 100644 --- a/bl32/tsp/tsp_main.c +++ b/bl32/tsp/tsp_main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-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: @@ -72,7 +72,6 @@ static tsp_args_t *set_smc_args(uint64_t arg0, uint64_t arg6, uint64_t arg7) { - uint64_t mpidr = read_mpidr(); uint32_t linear_id; tsp_args_t *pcpu_smc_args; @@ -80,7 +79,7 @@ static tsp_args_t *set_smc_args(uint64_t arg0, * Return to Secure Monitor by raising an SMC. The results of the * service are passed as an arguments to the SMC */ - linear_id = platform_get_core_pos(mpidr); + linear_id = plat_my_core_pos(); pcpu_smc_args = &tsp_smc_args[linear_id]; write_sp_arg(pcpu_smc_args, TSP_ARG0, arg0); write_sp_arg(pcpu_smc_args, TSP_ARG1, arg1); @@ -107,8 +106,7 @@ uint64_t tsp_main(void) INFO("TSP: Total memory size : 0x%lx bytes\n", BL32_TOTAL_LIMIT - BL32_TOTAL_BASE); - uint64_t mpidr = read_mpidr(); - uint32_t linear_id = platform_get_core_pos(mpidr); + uint32_t linear_id = plat_my_core_pos(); /* Initialize the platform */ tsp_platform_setup(); @@ -123,7 +121,8 @@ uint64_t tsp_main(void) #if LOG_LEVEL >= LOG_LEVEL_INFO spin_lock(&console_lock); - INFO("TSP: cpu 0x%lx: %d smcs, %d erets %d cpu on requests\n", mpidr, + INFO("TSP: cpu 0x%lx: %d smcs, %d erets %d cpu on requests\n", + read_mpidr(), tsp_stats[linear_id].smc_count, tsp_stats[linear_id].eret_count, tsp_stats[linear_id].cpu_on_count); @@ -139,8 +138,7 @@ uint64_t tsp_main(void) ******************************************************************************/ tsp_args_t *tsp_cpu_on_main(void) { - uint64_t mpidr = read_mpidr(); - uint32_t linear_id = platform_get_core_pos(mpidr); + uint32_t linear_id = plat_my_core_pos(); /* Initialize secure/applications state here */ tsp_generic_timer_start(); @@ -152,8 +150,9 @@ tsp_args_t *tsp_cpu_on_main(void) #if LOG_LEVEL >= LOG_LEVEL_INFO spin_lock(&console_lock); - INFO("TSP: cpu 0x%lx turned on\n", mpidr); - INFO("TSP: cpu 0x%lx: %d smcs, %d erets %d cpu on requests\n", mpidr, + INFO("TSP: cpu 0x%lx turned on\n", read_mpidr()); + INFO("TSP: cpu 0x%lx: %d smcs, %d erets %d cpu on requests\n", + read_mpidr(), tsp_stats[linear_id].smc_count, tsp_stats[linear_id].eret_count, tsp_stats[linear_id].cpu_on_count); @@ -176,8 +175,7 @@ tsp_args_t *tsp_cpu_off_main(uint64_t arg0, uint64_t arg6, uint64_t arg7) { - uint64_t mpidr = read_mpidr(); - uint32_t linear_id = platform_get_core_pos(mpidr); + uint32_t linear_id = plat_my_core_pos(); /* * This cpu is being turned off, so disable the timer to prevent the @@ -193,8 +191,9 @@ tsp_args_t *tsp_cpu_off_main(uint64_t arg0, #if LOG_LEVEL >= LOG_LEVEL_INFO spin_lock(&console_lock); - INFO("TSP: cpu 0x%lx off request\n", mpidr); - INFO("TSP: cpu 0x%lx: %d smcs, %d erets %d cpu off requests\n", mpidr, + INFO("TSP: cpu 0x%lx off request\n", read_mpidr()); + INFO("TSP: cpu 0x%lx: %d smcs, %d erets %d cpu off requests\n", + read_mpidr(), tsp_stats[linear_id].smc_count, tsp_stats[linear_id].eret_count, tsp_stats[linear_id].cpu_off_count); @@ -219,8 +218,7 @@ tsp_args_t *tsp_cpu_suspend_main(uint64_t arg0, uint64_t arg6, uint64_t arg7) { - uint64_t mpidr = read_mpidr(); - uint32_t linear_id = platform_get_core_pos(mpidr); + uint32_t linear_id = plat_my_core_pos(); /* * Save the time context and disable it to prevent the secure timer @@ -237,7 +235,7 @@ tsp_args_t *tsp_cpu_suspend_main(uint64_t arg0, #if LOG_LEVEL >= LOG_LEVEL_INFO spin_lock(&console_lock); INFO("TSP: cpu 0x%lx: %d smcs, %d erets %d cpu suspend requests\n", - mpidr, + read_mpidr(), tsp_stats[linear_id].smc_count, tsp_stats[linear_id].eret_count, tsp_stats[linear_id].cpu_suspend_count); @@ -262,8 +260,7 @@ tsp_args_t *tsp_cpu_resume_main(uint64_t suspend_level, uint64_t arg6, uint64_t arg7) { - uint64_t mpidr = read_mpidr(); - uint32_t linear_id = platform_get_core_pos(mpidr); + uint32_t linear_id = plat_my_core_pos(); /* Restore the generic timer context */ tsp_generic_timer_restore(); @@ -276,9 +273,9 @@ tsp_args_t *tsp_cpu_resume_main(uint64_t suspend_level, #if LOG_LEVEL >= LOG_LEVEL_INFO spin_lock(&console_lock); INFO("TSP: cpu 0x%lx resumed. suspend level %ld\n", - mpidr, suspend_level); + read_mpidr(), suspend_level); INFO("TSP: cpu 0x%lx: %d smcs, %d erets %d cpu suspend requests\n", - mpidr, + read_mpidr(), tsp_stats[linear_id].smc_count, tsp_stats[linear_id].eret_count, tsp_stats[linear_id].cpu_suspend_count); @@ -301,8 +298,7 @@ tsp_args_t *tsp_system_off_main(uint64_t arg0, uint64_t arg6, uint64_t arg7) { - uint64_t mpidr = read_mpidr(); - uint32_t linear_id = platform_get_core_pos(mpidr); + uint32_t linear_id = plat_my_core_pos(); /* Update this cpu's statistics */ tsp_stats[linear_id].smc_count++; @@ -310,8 +306,8 @@ tsp_args_t *tsp_system_off_main(uint64_t arg0, #if LOG_LEVEL >= LOG_LEVEL_INFO spin_lock(&console_lock); - INFO("TSP: cpu 0x%lx SYSTEM_OFF request\n", mpidr); - INFO("TSP: cpu 0x%lx: %d smcs, %d erets requests\n", mpidr, + INFO("TSP: cpu 0x%lx SYSTEM_OFF request\n", read_mpidr()); + INFO("TSP: cpu 0x%lx: %d smcs, %d erets requests\n", read_mpidr(), tsp_stats[linear_id].smc_count, tsp_stats[linear_id].eret_count); spin_unlock(&console_lock); @@ -334,8 +330,7 @@ tsp_args_t *tsp_system_reset_main(uint64_t arg0, uint64_t arg6, uint64_t arg7) { - uint64_t mpidr = read_mpidr(); - uint32_t linear_id = platform_get_core_pos(mpidr); + uint32_t linear_id = plat_my_core_pos(); /* Update this cpu's statistics */ tsp_stats[linear_id].smc_count++; @@ -343,8 +338,8 @@ tsp_args_t *tsp_system_reset_main(uint64_t arg0, #if LOG_LEVEL >= LOG_LEVEL_INFO spin_lock(&console_lock); - INFO("TSP: cpu 0x%lx SYSTEM_RESET request\n", mpidr); - INFO("TSP: cpu 0x%lx: %d smcs, %d erets requests\n", mpidr, + INFO("TSP: cpu 0x%lx SYSTEM_RESET request\n", read_mpidr()); + INFO("TSP: cpu 0x%lx: %d smcs, %d erets requests\n", read_mpidr(), tsp_stats[linear_id].smc_count, tsp_stats[linear_id].eret_count); spin_unlock(&console_lock); @@ -371,17 +366,16 @@ tsp_args_t *tsp_smc_handler(uint64_t func, { uint64_t results[2]; uint64_t service_args[2]; - uint64_t mpidr = read_mpidr(); - uint32_t linear_id = platform_get_core_pos(mpidr); + uint32_t linear_id = plat_my_core_pos(); /* Update this cpu's statistics */ tsp_stats[linear_id].smc_count++; tsp_stats[linear_id].eret_count++; - INFO("TSP: cpu 0x%lx received %s smc 0x%lx\n", mpidr, + INFO("TSP: cpu 0x%lx received %s smc 0x%lx\n", read_mpidr(), ((func >> 31) & 1) == 1 ? "fast" : "standard", func); - INFO("TSP: cpu 0x%lx: %d smcs, %d erets\n", mpidr, + INFO("TSP: cpu 0x%lx: %d smcs, %d erets\n", read_mpidr(), tsp_stats[linear_id].smc_count, tsp_stats[linear_id].eret_count); diff --git a/bl32/tsp/tsp_timer.c b/bl32/tsp/tsp_timer.c index f196021d07..7ca8734018 100644 --- a/bl32/tsp/tsp_timer.c +++ b/bl32/tsp/tsp_timer.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2014-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: @@ -92,7 +92,7 @@ void tsp_generic_timer_stop(void) ******************************************************************************/ void tsp_generic_timer_save(void) { - uint32_t linear_id = platform_get_core_pos(read_mpidr()); + uint32_t linear_id = plat_my_core_pos(); pcpu_timer_context[linear_id].cval = read_cntps_cval_el1(); pcpu_timer_context[linear_id].ctl = read_cntps_ctl_el1(); @@ -105,7 +105,7 @@ void tsp_generic_timer_save(void) ******************************************************************************/ void tsp_generic_timer_restore(void) { - uint32_t linear_id = platform_get_core_pos(read_mpidr()); + uint32_t linear_id = plat_my_core_pos(); write_cntps_cval_el1(pcpu_timer_context[linear_id].cval); write_cntps_ctl_el1(pcpu_timer_context[linear_id].ctl); diff --git a/services/spd/opteed/opteed_main.c b/services/spd/opteed/opteed_main.c index 5c5144fb94..fefc8a75e8 100644 --- a/services/spd/opteed/opteed_main.c +++ b/services/spd/opteed/opteed_main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-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: @@ -80,7 +80,6 @@ static uint64_t opteed_sel1_interrupt_handler(uint32_t id, void *cookie) { uint32_t linear_id; - uint64_t mpidr; optee_context_t *optee_ctx; /* Check the security state when the exception was generated */ @@ -92,14 +91,13 @@ static uint64_t opteed_sel1_interrupt_handler(uint32_t id, #endif /* Sanity check the pointer to this cpu's context */ - mpidr = read_mpidr(); assert(handle == cm_get_context(NON_SECURE)); /* Save the non-secure context before entering the OPTEE */ cm_el1_sysregs_context_save(NON_SECURE); /* Get a reference to this cpu's OPTEE context */ - linear_id = platform_get_core_pos(mpidr); + linear_id = plat_my_core_pos(); optee_ctx = &opteed_sp_context[linear_id]; assert(&optee_ctx->cpu_ctx == cm_get_context(SECURE)); @@ -125,10 +123,9 @@ static uint64_t opteed_sel1_interrupt_handler(uint32_t id, int32_t opteed_setup(void) { entry_point_info_t *optee_ep_info; - uint64_t mpidr = read_mpidr(); uint32_t linear_id; - linear_id = platform_get_core_pos(mpidr); + linear_id = plat_my_core_pos(); /* * Get information about the Secure Payload (BL32) image. Its @@ -182,8 +179,7 @@ int32_t opteed_setup(void) ******************************************************************************/ static int32_t opteed_init(void) { - uint64_t mpidr = read_mpidr(); - uint32_t linear_id = platform_get_core_pos(mpidr); + uint32_t linear_id = plat_my_core_pos(); optee_context_t *optee_ctx = &opteed_sp_context[linear_id]; entry_point_info_t *optee_entry_point; uint64_t rc; @@ -195,7 +191,7 @@ static int32_t opteed_init(void) optee_entry_point = bl31_plat_get_next_image_ep_info(SECURE); assert(optee_entry_point); - cm_init_context(mpidr, optee_entry_point); + cm_init_my_context(optee_entry_point); /* * Arrange for an entry into OPTEE. It will be returned via @@ -226,8 +222,7 @@ uint64_t opteed_smc_handler(uint32_t smc_fid, uint64_t flags) { cpu_context_t *ns_cpu_context; - unsigned long mpidr = read_mpidr(); - uint32_t linear_id = platform_get_core_pos(mpidr); + uint32_t linear_id = plat_my_core_pos(); optee_context_t *optee_ctx = &opteed_sp_context[linear_id]; uint64_t rc; diff --git a/services/spd/opteed/opteed_pm.c b/services/spd/opteed/opteed_pm.c index 37419ec77c..50994d0112 100644 --- a/services/spd/opteed/opteed_pm.c +++ b/services/spd/opteed/opteed_pm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-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: @@ -51,8 +51,7 @@ static void opteed_cpu_on_handler(uint64_t target_cpu) static int32_t opteed_cpu_off_handler(uint64_t unused) { int32_t rc = 0; - uint64_t mpidr = read_mpidr(); - uint32_t linear_id = platform_get_core_pos(mpidr); + uint32_t linear_id = plat_my_core_pos(); optee_context_t *optee_ctx = &opteed_sp_context[linear_id]; assert(optee_vectors); @@ -85,8 +84,7 @@ static int32_t opteed_cpu_off_handler(uint64_t unused) static void opteed_cpu_suspend_handler(uint64_t unused) { int32_t rc = 0; - uint64_t mpidr = read_mpidr(); - uint32_t linear_id = platform_get_core_pos(mpidr); + uint32_t linear_id = plat_my_core_pos(); optee_context_t *optee_ctx = &opteed_sp_context[linear_id]; assert(optee_vectors); @@ -116,8 +114,7 @@ static void opteed_cpu_suspend_handler(uint64_t unused) static void opteed_cpu_on_finish_handler(uint64_t unused) { int32_t rc = 0; - uint64_t mpidr = read_mpidr(); - uint32_t linear_id = platform_get_core_pos(mpidr); + uint32_t linear_id = plat_my_core_pos(); optee_context_t *optee_ctx = &opteed_sp_context[linear_id]; entry_point_info_t optee_on_entrypoint; @@ -129,7 +126,7 @@ static void opteed_cpu_on_finish_handler(uint64_t unused) optee_ctx); /* Initialise this cpu's secure context */ - cm_init_context(mpidr, &optee_on_entrypoint); + cm_init_my_context(&optee_on_entrypoint); /* Enter OPTEE */ rc = opteed_synchronous_sp_entry(optee_ctx); @@ -153,8 +150,7 @@ static void opteed_cpu_on_finish_handler(uint64_t unused) static void opteed_cpu_suspend_finish_handler(uint64_t suspend_level) { int32_t rc = 0; - uint64_t mpidr = read_mpidr(); - uint32_t linear_id = platform_get_core_pos(mpidr); + uint32_t linear_id = plat_my_core_pos(); optee_context_t *optee_ctx = &opteed_sp_context[linear_id]; assert(optee_vectors); @@ -193,8 +189,7 @@ static int32_t opteed_cpu_migrate_info(uint64_t *resident_cpu) ******************************************************************************/ static void opteed_system_off(void) { - uint64_t mpidr = read_mpidr(); - uint32_t linear_id = platform_get_core_pos(mpidr); + uint32_t linear_id = plat_my_core_pos(); optee_context_t *optee_ctx = &opteed_sp_context[linear_id]; assert(optee_vectors); @@ -214,8 +209,7 @@ static void opteed_system_off(void) ******************************************************************************/ static void opteed_system_reset(void) { - uint64_t mpidr = read_mpidr(); - uint32_t linear_id = platform_get_core_pos(mpidr); + uint32_t linear_id = plat_my_core_pos(); optee_context_t *optee_ctx = &opteed_sp_context[linear_id]; assert(optee_vectors); diff --git a/services/spd/tlkd/tlkd_main.c b/services/spd/tlkd/tlkd_main.c index 3532bebe58..58a60464d6 100644 --- a/services/spd/tlkd/tlkd_main.c +++ b/services/spd/tlkd/tlkd_main.c @@ -121,7 +121,6 @@ int32_t tlkd_setup(void) ******************************************************************************/ int32_t tlkd_init(void) { - uint64_t mpidr = read_mpidr(); entry_point_info_t *tlk_entry_point; /* @@ -131,7 +130,7 @@ int32_t tlkd_init(void) tlk_entry_point = bl31_plat_get_next_image_ep_info(SECURE); assert(tlk_entry_point); - cm_init_context(mpidr, tlk_entry_point); + cm_init_my_context(tlk_entry_point); /* * Arrange for an entry into the test secure payload. diff --git a/services/spd/tspd/tspd_main.c b/services/spd/tspd/tspd_main.c index ee17483e1d..b8b67fadc8 100644 --- a/services/spd/tspd/tspd_main.c +++ b/services/spd/tspd/tspd_main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-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: @@ -101,7 +101,6 @@ static uint64_t tspd_sel1_interrupt_handler(uint32_t id, void *cookie) { uint32_t linear_id; - uint64_t mpidr; tsp_context_t *tsp_ctx; /* Check the security state when the exception was generated */ @@ -113,14 +112,13 @@ static uint64_t tspd_sel1_interrupt_handler(uint32_t id, #endif /* Sanity check the pointer to this cpu's context */ - mpidr = read_mpidr(); assert(handle == cm_get_context(NON_SECURE)); /* Save the non-secure context before entering the TSP */ cm_el1_sysregs_context_save(NON_SECURE); /* Get a reference to this cpu's TSP context */ - linear_id = platform_get_core_pos(mpidr); + linear_id = plat_my_core_pos(); tsp_ctx = &tspd_sp_context[linear_id]; assert(&tsp_ctx->cpu_ctx == cm_get_context(SECURE)); @@ -197,10 +195,9 @@ static uint64_t tspd_ns_interrupt_handler(uint32_t id, int32_t tspd_setup(void) { entry_point_info_t *tsp_ep_info; - uint64_t mpidr = read_mpidr(); uint32_t linear_id; - linear_id = platform_get_core_pos(mpidr); + linear_id = plat_my_core_pos(); /* * Get information about the Secure Payload (BL32) image. Its @@ -256,8 +253,7 @@ int32_t tspd_setup(void) ******************************************************************************/ int32_t tspd_init(void) { - uint64_t mpidr = read_mpidr(); - uint32_t linear_id = platform_get_core_pos(mpidr); + uint32_t linear_id = plat_my_core_pos(); tsp_context_t *tsp_ctx = &tspd_sp_context[linear_id]; entry_point_info_t *tsp_entry_point; uint64_t rc; @@ -269,7 +265,7 @@ int32_t tspd_init(void) tsp_entry_point = bl31_plat_get_next_image_ep_info(SECURE); assert(tsp_entry_point); - cm_init_context(mpidr, tsp_entry_point); + cm_init_my_context(tsp_entry_point); /* * Arrange for an entry into the test secure payload. It will be @@ -300,8 +296,7 @@ uint64_t tspd_smc_handler(uint32_t smc_fid, uint64_t flags) { cpu_context_t *ns_cpu_context; - unsigned long mpidr = read_mpidr(); - uint32_t linear_id = platform_get_core_pos(mpidr), ns; + uint32_t linear_id = plat_my_core_pos(), ns; tsp_context_t *tsp_ctx = &tspd_sp_context[linear_id]; uint64_t rc; #if TSP_INIT_ASYNC @@ -453,7 +448,7 @@ uint64_t tspd_smc_handler(uint32_t smc_fid, /* * Disable the interrupt NS locally since it will be enabled globally - * within cm_init_context. + * within cm_init_my_context. */ disable_intr_rm_local(INTR_TYPE_NS, SECURE); #endif @@ -471,7 +466,7 @@ uint64_t tspd_smc_handler(uint32_t smc_fid, assert(NON_SECURE == GET_SECURITY_STATE(next_image_info->h.attr)); - cm_init_context(read_mpidr_el1(), next_image_info); + cm_init_my_context(next_image_info); cm_prepare_el3_exit(NON_SECURE); SMC_RET0(cm_get_context(NON_SECURE)); #else diff --git a/services/spd/tspd/tspd_pm.c b/services/spd/tspd/tspd_pm.c index 009ff5f4d2..bc9eb76593 100644 --- a/services/spd/tspd/tspd_pm.c +++ b/services/spd/tspd/tspd_pm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-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: @@ -52,8 +52,7 @@ static void tspd_cpu_on_handler(uint64_t target_cpu) static int32_t tspd_cpu_off_handler(uint64_t unused) { int32_t rc = 0; - uint64_t mpidr = read_mpidr(); - uint32_t linear_id = platform_get_core_pos(mpidr); + uint32_t linear_id = plat_my_core_pos(); tsp_context_t *tsp_ctx = &tspd_sp_context[linear_id]; assert(tsp_vectors); @@ -86,8 +85,7 @@ static int32_t tspd_cpu_off_handler(uint64_t unused) static void tspd_cpu_suspend_handler(uint64_t unused) { int32_t rc = 0; - uint64_t mpidr = read_mpidr(); - uint32_t linear_id = platform_get_core_pos(mpidr); + uint32_t linear_id = plat_my_core_pos(); tsp_context_t *tsp_ctx = &tspd_sp_context[linear_id]; assert(tsp_vectors); @@ -117,8 +115,7 @@ static void tspd_cpu_suspend_handler(uint64_t unused) static void tspd_cpu_on_finish_handler(uint64_t unused) { int32_t rc = 0; - uint64_t mpidr = read_mpidr(); - uint32_t linear_id = platform_get_core_pos(mpidr); + uint32_t linear_id = plat_my_core_pos(); tsp_context_t *tsp_ctx = &tspd_sp_context[linear_id]; entry_point_info_t tsp_on_entrypoint; @@ -131,12 +128,12 @@ static void tspd_cpu_on_finish_handler(uint64_t unused) tsp_ctx); /* Initialise this cpu's secure context */ - cm_init_context(mpidr, &tsp_on_entrypoint); + cm_init_my_context(&tsp_on_entrypoint); #if TSPD_ROUTE_IRQ_TO_EL3 /* * Disable the NS interrupt locally since it will be enabled globally - * within cm_init_context. + * within cm_init_my_context. */ disable_intr_rm_local(INTR_TYPE_NS, SECURE); #endif @@ -163,8 +160,7 @@ static void tspd_cpu_on_finish_handler(uint64_t unused) static void tspd_cpu_suspend_finish_handler(uint64_t suspend_level) { int32_t rc = 0; - uint64_t mpidr = read_mpidr(); - uint32_t linear_id = platform_get_core_pos(mpidr); + uint32_t linear_id = plat_my_core_pos(); tsp_context_t *tsp_ctx = &tspd_sp_context[linear_id]; assert(tsp_vectors); @@ -203,8 +199,7 @@ static int32_t tspd_cpu_migrate_info(uint64_t *resident_cpu) ******************************************************************************/ static void tspd_system_off(void) { - uint64_t mpidr = read_mpidr(); - uint32_t linear_id = platform_get_core_pos(mpidr); + uint32_t linear_id = plat_my_core_pos(); tsp_context_t *tsp_ctx = &tspd_sp_context[linear_id]; assert(tsp_vectors); @@ -224,8 +219,7 @@ static void tspd_system_off(void) ******************************************************************************/ static void tspd_system_reset(void) { - uint64_t mpidr = read_mpidr(); - uint32_t linear_id = platform_get_core_pos(mpidr); + uint32_t linear_id = plat_my_core_pos(); tsp_context_t *tsp_ctx = &tspd_sp_context[linear_id]; assert(tsp_vectors); From 617540d860f24c324fbbf418388fb65c0ec83242 Mon Sep 17 00:00:00 2001 From: Soby Mathew Date: Wed, 15 Jul 2015 12:13:26 +0100 Subject: [PATCH 17/20] PSCI: Fix the return code for invalid entrypoint As per PSCI1.0 specification, the error code to be returned when an invalid non secure entrypoint address is specified by the PSCI client for CPU_SUSPEND, CPU_ON or SYSTEM_SUSPEND must be PSCI_E_INVALID_ADDRESS. The current PSCI implementation returned PSCI_E_INVAL_PARAMS. This patch rectifies this error and also implements a common helper function to validate the entrypoint information to be used across these PSCI API implementations. Change-Id: I52d697d236c8bf0cd3297da4008c8e8c2399b170 --- include/bl31/services/psci.h | 1 + services/std_svc/psci/psci_common.c | 30 +++++++++++++++-- services/std_svc/psci/psci_main.c | 49 +++------------------------- services/std_svc/psci/psci_private.h | 2 +- 4 files changed, 35 insertions(+), 47 deletions(-) diff --git a/include/bl31/services/psci.h b/include/bl31/services/psci.h index c9b3f8d2c7..f6fd4872c1 100644 --- a/include/bl31/services/psci.h +++ b/include/bl31/services/psci.h @@ -165,6 +165,7 @@ #define PSCI_E_INTERN_FAIL -6 #define PSCI_E_NOT_PRESENT -7 #define PSCI_E_DISABLED -8 +#define PSCI_E_INVALID_ADDRESS -9 #define PSCI_INVALID_MPIDR ~(0ULL) diff --git a/services/std_svc/psci/psci_common.c b/services/std_svc/psci/psci_common.c index 7f1a5fd0d8..f810ddfac5 100644 --- a/services/std_svc/psci/psci_common.c +++ b/services/std_svc/psci/psci_common.c @@ -589,7 +589,7 @@ int psci_validate_mpidr(unsigned long mpidr) * This function determines the full entrypoint information for the requested * PSCI entrypoint on power on/resume and returns it. ******************************************************************************/ -int psci_get_ns_ep_info(entry_point_info_t *ep, +static int psci_get_ns_ep_info(entry_point_info_t *ep, uint64_t entrypoint, uint64_t context_id) { uint32_t ep_attr, mode, sctlr, daif, ee; @@ -621,7 +621,7 @@ int psci_get_ns_ep_info(entry_point_info_t *ep, * aarch64 EL */ if (entrypoint & 0x1) - return PSCI_E_INVALID_PARAMS; + return PSCI_E_INVALID_ADDRESS; mode = ns_scr_el3 & SCR_HCE_BIT ? MODE_EL2 : MODE_EL1; @@ -642,6 +642,32 @@ int psci_get_ns_ep_info(entry_point_info_t *ep, return PSCI_E_SUCCESS; } +/******************************************************************************* + * This function validates the entrypoint with the platform layer if the + * appropriate pm_ops hook is exported by the platform and returns the + * 'entry_point_info'. + ******************************************************************************/ +int psci_validate_entry_point(entry_point_info_t *ep, + uint64_t entrypoint, uint64_t context_id) +{ + int rc; + + /* Validate the entrypoint using platform psci_ops */ + if (psci_plat_pm_ops->validate_ns_entrypoint) { + rc = psci_plat_pm_ops->validate_ns_entrypoint(entrypoint); + if (rc != PSCI_E_SUCCESS) + return PSCI_E_INVALID_ADDRESS; + } + + /* + * Verify and derive the re-entry information for + * the non-secure world from the non-secure state from + * where this call originated. + */ + rc = psci_get_ns_ep_info(ep, entrypoint, context_id); + return rc; +} + /******************************************************************************* * Generic handler which is called when a cpu is physically powered on. It * traverses the node information and finds the highest power level powered diff --git a/services/std_svc/psci/psci_main.c b/services/std_svc/psci/psci_main.c index f024291022..6d3af20480 100644 --- a/services/std_svc/psci/psci_main.c +++ b/services/std_svc/psci/psci_main.c @@ -55,21 +55,8 @@ int psci_cpu_on(unsigned long target_cpu, if (rc != PSCI_E_SUCCESS) return PSCI_E_INVALID_PARAMS; - /* Validate the entrypoint using platform pm_ops */ - if (psci_plat_pm_ops->validate_ns_entrypoint) { - rc = psci_plat_pm_ops->validate_ns_entrypoint(entrypoint); - if (rc != PSCI_E_SUCCESS) { - assert(rc == PSCI_E_INVALID_PARAMS); - return PSCI_E_INVALID_PARAMS; - } - } - - /* - * Verify and derive the re-entry information for - * the non-secure world from the non-secure state from - * where this call originated. - */ - rc = psci_get_ns_ep_info(&ep, entrypoint, context_id); + /* Validate the entry point and get the entry_point_info */ + rc = psci_validate_entry_point(&ep, entrypoint, context_id); if (rc != PSCI_E_SUCCESS) return rc; @@ -141,20 +128,7 @@ int psci_cpu_suspend(unsigned int power_state, * point and program entry information. */ if (is_power_down_state) { - if (psci_plat_pm_ops->validate_ns_entrypoint) { - rc = psci_plat_pm_ops->validate_ns_entrypoint(entrypoint); - if (rc != PSCI_E_SUCCESS) { - assert(rc == PSCI_E_INVALID_PARAMS); - return rc; - } - } - - /* - * Verify and derive the re-entry information for - * the non-secure world from the non-secure state from - * where this call originated. - */ - rc = psci_get_ns_ep_info(&ep, entrypoint, context_id); + rc = psci_validate_entry_point(&ep, entrypoint, context_id); if (rc != PSCI_E_SUCCESS) return rc; } @@ -180,25 +154,12 @@ int psci_system_suspend(unsigned long entrypoint, psci_power_state_t state_info; entry_point_info_t ep; - /* Validate the entrypoint using platform pm_ops */ - if (psci_plat_pm_ops->validate_ns_entrypoint) { - rc = psci_plat_pm_ops->validate_ns_entrypoint(entrypoint); - if (rc != PSCI_E_SUCCESS) { - assert(rc == PSCI_E_INVALID_PARAMS); - return rc; - } - } - /* Check if the current CPU is the last ON CPU in the system */ if (!psci_is_last_on_cpu()) return PSCI_E_DENIED; - /* - * Verify and derive the re-entry information for - * the non-secure world from the non-secure state from - * where this call originated. - */ - rc = psci_get_ns_ep_info(&ep, entrypoint, context_id); + /* Validate the entry point and get the entry_point_info */ + rc = psci_validate_entry_point(&ep, entrypoint, context_id); if (rc != PSCI_E_SUCCESS) return rc; diff --git a/services/std_svc/psci/psci_private.h b/services/std_svc/psci/psci_private.h index e2e32c7962..5345ee30fd 100644 --- a/services/std_svc/psci/psci_private.h +++ b/services/std_svc/psci/psci_private.h @@ -189,7 +189,7 @@ void psci_query_sys_suspend_pwrstate(psci_power_state_t *state_info); int psci_validate_mpidr(unsigned long mpidr); void psci_init_req_local_pwr_states(void); void psci_power_up_finish(void); -int psci_get_ns_ep_info(entry_point_info_t *ep, +int psci_validate_entry_point(entry_point_info_t *ep, uint64_t entrypoint, uint64_t context_id); void psci_get_parent_pwr_domain_nodes(unsigned int cpu_idx, int end_lvl, From f9e858b1f7b27d8e0b89cc7e12e7a90755d0dd00 Mon Sep 17 00:00:00 2001 From: Soby Mathew Date: Wed, 15 Jul 2015 13:36:24 +0100 Subject: [PATCH 18/20] PSCI: Validate non secure entrypoint on ARM platforms This patch implements the platform power managment handler to verify non secure entrypoint for ARM platforms. The handler ensures that the entry point specified by the normal world during CPU_SUSPEND, CPU_ON or SYSTEM_SUSPEND PSCI API is a valid address within the non secure DRAM. Change-Id: I4795452df99f67a24682b22f0e0967175c1de429 --- include/plat/arm/common/plat_arm.h | 1 + plat/arm/board/fvp/fvp_pm.c | 3 ++- plat/arm/common/arm_pm.c | 21 +++++++++++++++++++++ plat/arm/css/common/css_pm.c | 3 ++- 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/include/plat/arm/common/plat_arm.h b/include/plat/arm/common/plat_arm.h index 56b5eda573..823212cb3a 100644 --- a/include/plat/arm/common/plat_arm.h +++ b/include/plat/arm/common/plat_arm.h @@ -190,6 +190,7 @@ void arm_tzc_setup(void); /* PM utility functions */ int arm_validate_power_state(unsigned int power_state, psci_power_state_t *req_state); +int arm_validate_ns_entrypoint(uintptr_t entrypoint); /* Topology utility function */ int arm_check_mpidr(u_register_t mpidr); diff --git a/plat/arm/board/fvp/fvp_pm.c b/plat/arm/board/fvp/fvp_pm.c index 8be51054b2..9d6ab9ce6f 100644 --- a/plat/arm/board/fvp/fvp_pm.c +++ b/plat/arm/board/fvp/fvp_pm.c @@ -304,7 +304,8 @@ static const plat_psci_ops_t fvp_plat_psci_ops = { .pwr_domain_suspend_finish = fvp_pwr_domain_suspend_finish, .system_off = fvp_system_off, .system_reset = fvp_system_reset, - .validate_power_state = arm_validate_power_state + .validate_power_state = arm_validate_power_state, + .validate_ns_entrypoint = arm_validate_ns_entrypoint }; /******************************************************************************* diff --git a/plat/arm/common/arm_pm.c b/plat/arm/common/arm_pm.c index 71fbf9f11f..b2251700f0 100644 --- a/plat/arm/common/arm_pm.c +++ b/plat/arm/common/arm_pm.c @@ -29,6 +29,7 @@ */ #include +#include #include #include #include @@ -123,3 +124,23 @@ int arm_validate_power_state(unsigned int power_state, return PSCI_E_SUCCESS; } #endif /* __ARM_RECOM_STATE_ID_ENC__ */ + +/******************************************************************************* + * ARM standard platform handler called to check the validity of the non secure + * entrypoint. + ******************************************************************************/ +int arm_validate_ns_entrypoint(uintptr_t entrypoint) +{ + /* + * Check if the non secure entrypoint lies within the non + * secure DRAM. + */ + if ((entrypoint >= ARM_NS_DRAM1_BASE) && (entrypoint < + (ARM_NS_DRAM1_BASE + ARM_NS_DRAM1_SIZE))) + return PSCI_E_SUCCESS; + if ((entrypoint >= ARM_DRAM2_BASE) && (entrypoint < + (ARM_DRAM2_BASE + ARM_DRAM2_SIZE))) + return PSCI_E_SUCCESS; + + return PSCI_E_INVALID_ADDRESS; +} diff --git a/plat/arm/css/common/css_pm.c b/plat/arm/css/common/css_pm.c index 435ed2aa66..cc64bf8bfc 100644 --- a/plat/arm/css/common/css_pm.c +++ b/plat/arm/css/common/css_pm.c @@ -267,7 +267,8 @@ static const plat_psci_ops_t css_ops = { .pwr_domain_suspend_finish = css_pwr_domain_suspend_finish, .system_off = css_system_off, .system_reset = css_system_reset, - .validate_power_state = arm_validate_power_state + .validate_power_state = arm_validate_power_state, + .validate_ns_entrypoint = arm_validate_ns_entrypoint }; /******************************************************************************* From 58523c076a4bd766fccd207c493cf1b918fda9db Mon Sep 17 00:00:00 2001 From: Soby Mathew Date: Mon, 8 Jun 2015 12:32:50 +0100 Subject: [PATCH 19/20] PSCI: Add documentation and fix plat_is_my_cpu_primary() This patch adds the necessary documentation updates to porting_guide.md for the changes in the platform interface mandated as a result of the new PSCI Topology and power state management frameworks. It also adds a new document `platform-migration-guide.md` to aid the migration of existing platform ports to the new API. The patch fixes the implementation and callers of plat_is_my_cpu_primary() to use w0 as the return parameter as implied by the function signature rather than x0 which was used previously. Change-Id: Ic11e73019188c8ba2bd64c47e1729ff5acdcdd5b --- docs/diagrams/psci-suspend-sequence.png | Bin 0 -> 427800 bytes docs/platform-migration-guide.md | 574 ++++++++++++++++++++++ docs/porting-guide.md | 441 ++++++++++------- docs/user-guide.md | 8 +- include/common/el3_common_macros.S | 2 +- plat/arm/board/fvp/aarch64/fvp_helpers.S | 10 +- plat/arm/css/common/aarch64/css_helpers.S | 4 +- 7 files changed, 842 insertions(+), 197 deletions(-) create mode 100644 docs/diagrams/psci-suspend-sequence.png create mode 100644 docs/platform-migration-guide.md diff --git a/docs/diagrams/psci-suspend-sequence.png b/docs/diagrams/psci-suspend-sequence.png new file mode 100644 index 0000000000000000000000000000000000000000..1703ea68ab158365effc0403beb88f02448dd421 GIT binary patch literal 427800 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 z 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.EZkPQzsY8gs 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%0DBK;+o~jKeB7ixx#3G|C2z=&0^higR9qZ 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%0A(UbU!Kc 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�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

U2dfetTI@gbAy%-NB`9(RbXbpLNz_sc{w; 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 bKT&w=XU{A#Bz=&feypvjr%`e3&g1_8h`-qZ literal 0 HcmV?d00001 diff --git a/docs/platform-migration-guide.md b/docs/platform-migration-guide.md new file mode 100644 index 0000000000..806fa19d45 --- /dev/null +++ b/docs/platform-migration-guide.md @@ -0,0 +1,574 @@ +Guide to migrate to new Platform porting interface +================================================== + +Contents +-------- + +1. [Introduction](#1--introduction) +2. [Platform API modification due to PSCI framework changes](#2--platform-api-modification-due-to-psci-framework-changes) + * [Power domain topology framework platform API modifications](#21-power-domain-topology-framework-platform-api-modifications) + * [Composite power state framework platform API modifications](#22-composite-power-state-framework-platform-api-modifications) + * [Miscellaneous modifications](#23-miscellaneous-modifications) +3. [Compatibility layer](#3--compatibility-layer) +4. [Deprecated Platform API](#4--deprecated-platform-api) + +- - - - - - - - - - - - - - - - - - + + +1. Introduction +---------------- + +The PSCI implementation in Trusted Firmware has undergone a redesign because of +three requirements that the PSCI 1.0 specification introduced : + +* Removing the framework assumption about the structure of the MPIDR, and + its relation to the power topology enables support for deeper and more + complex hierarchies. + +* Reworking the power state coordination implementation in the framework + to support the more detailed PSCI 1.0 requirements and reduce platform + port complexity + +* Enable the use of the extended power_state parameter and the larger StateID + field + +The PSCI 1.0 implementation introduces new frameworks to fulfill the above +requirements. These framework changes mean that the platform porting API must +also be modified. This document is a guide to assist migration of the existing +platform ports to the new platform API. + +This document describes the new platform API and compares it with the +deprecated API. It also describes the compatibility layer that enables the +existing platform ports to work with the PSCI 1.0 implementation. The +deprecated platform API is documented for reference. + + +2. Platform API modification due to PSCI framework changes +----------------------------------------------------------- + +This section describes changes to the platform APIs. + + +2.1 Power domain topology framework platform API modifications +-------------------------------------------------------------- + +This removes the assumption in the PSCI implementation that MPIDR +based affinity instances map directly to power domains. A power domain, as +described in section 4.2 of [PSCI], could contain a core or a logical group +of cores (a cluster) which share some state on which power management +operations can be performed. The existing affinity instance based APIs +`plat_get_aff_count()` and `plat_get_aff_count()` are deprecated. The new +platform interfaces that are introduced for this framework are: + +* `plat_core_pos_by_mpidr()` +* `plat_my_core_pos()` +* `plat_get_power_domain_tree_desc()` + +`plat_my_core_pos()` and `plat_core_pos_by_mpidr()` are mandatory +and are meant to replace the existing `platform_get_core_pos()` API. +The description of these APIs can be found in the [Porting Guide][my_core_pos]. +These are used by the power domain topology framework such that: + +1. The generic PSCI code does not generate MPIDRs or use them to query the + platform about the number of power domains at a particular power level. The + `plat_get_power_domain_tree_desc()` provides a description of the power + domain tree on the SoC through a pointer to the byte array containing the + power domain topology tree description data structure. + +2. The linear indices returned by `plat_core_pos_by_mpidr()` and + `plat_my_core_pos()` are used to retrieve core power domain nodes from + the power domain tree. These core indices are unique for a core and it is a + number between `0` and `PLATFORM_CORE_COUNT - 1`. The platform can choose + to implement a static mapping between `MPIDR` and core index or implement + a dynamic mapping, choosing to skip the unavailable/unused cores to compact + the core indices. + +In addition, the platforms must define the macros `PLAT_NUM_PWR_DOMAINS` and +`PLAT_MAX_PWR_LVL` which replace the macros `PLAT_NUM_AFFS` and +`PLATFORM_MAX_AFFLVL` respectively. On platforms where the affinity instances +correspond to power domains, the values of new macros remain the same as the +old ones. + +More details on the power domain topology description and its platform +interface can be found in [psci pd tree]. + + +2.2 Composite power state framework platform API modifications +-------------------------------------------------------------- + +The state-ID field in the power-state parameter of a CPU_SUSPEND call can be +used to describe the composite power states specific to a platform. The existing +PSCI state coordination had the limitation that it operates on a run/off +granularity of power states and it did not interpret the state-ID field. This +was acceptable as the specification requirement in PSCI 0.2. The framework's +approach to coordination only requires maintaining a reference +count of the number of cores that have requested the cluster to remain powered. + +In the PSCI 1.0 specification, this approach is non optimal. If composite +power states are used, the PSCI implementation cannot make global +decisions about state coordination required because it does not understand the +platform specific states. + +The PSCI 1.0 implementation now defines a generic representation of the +power-state parameter : + + typedef struct psci_power_state { + plat_local_state_t pwr_domain_state[PLAT_MAX_PWR_LVL + 1]; + } psci_power_state_t; + + +`pwr_domain_state` is an array where each index corresponds to a power level. +Each entry in the array contains the local power state the power domain at +that power level could enter. The meaning of the local power state value is +platform defined, and can vary between levels in a single platform. The PSCI +implementation constraints the values only so that it can classify the state +as RUN, RETENTION or OFF as required by the specification: + +1. Zero means RUN + +2. All OFF state values at all levels must be higher than all + RETENTION state values at all levels + +The platform is required to define the macros `PLAT_MAX_RET_STATE` and +`PLAT_MAX_OFF_STATE` to the framework. The requirement for these macros can +be found in the [Porting Guide]. + +The PSCI 1.0 implementation adds support to involve the platform in state +coordination. This enables the platform to decide the final target state. +During a request to place a power domain in a low power state, the platform +is passed an array of requested `plat_local_state_t` for that power domain by +each core within it through the `plat_get_target_pwr_state()` API. This API +coordinates amongst these requested states to determine a target +`plat_local_state_t` for that power domain. A default weak implementation of +this API is provided in the platform layer which returns the minimum of the +requested local states back to the PSCI state coordination. More details +of `plat_get_target_pwr_state()` API can be found in the +[Porting Guide][get_target_pwr_state]. + +The PSCI Generic implementation expects platform ports to populate the handlers +for the `plat_psci_ops` structure which is declared as : + + typedef struct plat_psci_ops { + void (*cpu_standby)(plat_local_state_t cpu_state); + int (*pwr_domain_on)(u_register_t mpidr); + void (*pwr_domain_off)(const psci_power_state_t *target_state); + void (*pwr_domain_suspend)(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, + psci_power_state_t *req_state); + int (*validate_ns_entrypoint)(unsigned long ns_entrypoint); + void (*get_sys_suspend_power_state)( + psci_power_state_t *req_state); + } plat_psci_ops_t; + +The description of these handlers can be found in the [Porting Guide][psci_ops]. +The previous `plat_pm_ops` structure is deprecated. Compared with the previous +handlers, the major differences are: + +* Difference in parameters + +The PSCI 1.0 implementation depends on the `validate_power_state` handler to +convert the power-state parameter (possibly encoding a composite power state) +passed in a PSCI `CPU_SUSPEND` to the `psci_power_state` format. + +The `plat_psci_ops` handlers, `pwr_domain_off` and `pwr_domain_suspend`, are +passed the target local state for each affected power domain. The platform +must execute operations specific to these target states. Similarly, +`pwr_domain_on_finish` and `pwr_domain_suspend_finish` are passed the local +states of the affected power domains before wakeup. The platform +must execute actions to restore these power domains from these specific +local states. + +* Difference in invocation + +Whereas the power management handlers in `plat_pm_ops` used to be invoked +for each affinity level till the target affinity level, the new handlers +are only invoked once. The `target_state` encodes the target low power +state or the low power state woken up from for each affected power domain. + +* Difference in semantics + +Although the previous `suspend` handlers could be used for power down as well +as retention at different affinity levels, the new handlers make this support +explicit. The `pwr_domain_suspend` can be used to specify powerdown and +retention at various power domain levels subject to the conditions mentioned +in section 4.2.1 of [PSCI] + +Unlike the previous `standby` handler, the `cpu_standby()` handler is only used +as a fast path for placing a core power domain into a standby or retention +state. + +The below diagram shows the sequence of a PSCI SUSPEND call and the interaction +with the platform layer depicting the exchange of data between PSCI Generic +layer and the platform layer. + +![Image 1](diagrams/psci-suspend-sequence.png?raw=true) + +Refer [plat/arm/board/fvp/fvp_pm.c] for the implementation details of +these handlers for the FVP. The commit b6df6ccbc88cc14592f5e603ef580d3cbf4733c3 +demonstrates the migration of ARM reference platforms to the new platform API. + + +2.3 Miscellaneous modifications +------------------------------- + +In addition to the framework changes, unification of warm reset entry points on +wakeup from low power modes has led to a change in the platform API. In the +earlier implementation, the warm reset entry used to be programmed into the +mailboxes by the 'ON' and 'SUSPEND' power management hooks. In the PSCI 1.0 +implementation, this information is not required, because it can figure that +out by querying affinity info state whether to execute the 'suspend_finisher` +or 'on_finisher'. + +As a result, the warm reset entry point must be programmed only once. The +`plat_setup_psci_ops()` API takes the secure entry point as an +additional parameter to enable the platforms to configure their mailbox. The +plat_psci_ops handlers `pwr_domain_on` and `pwr_domain_suspend` no longer take +the warm reset entry point as a parameter. + +Also, some platform APIs which took `MPIDR` as an argument were only ever +invoked to perform actions specific to the caller core which makes the argument +redundant. Therefore the platform APIs `plat_get_my_entrypoint()`, +`plat_is_my_cpu_primary()`, `plat_set_my_stack()` and +`plat_get_my_stack()` are defined which are meant to be invoked only for +operations on the current caller core instead of `platform_get_entrypoint()`, +`platform_is_primary_cpu()`, `platform_set_stack()` and `platform_get_stack()`. + + +3. Compatibility layer +---------------------- + +To ease the migration of the platform ports to the new porting interface, +a compatibility layer is introduced that essentially implements a glue layer +between the old platform API and the new API. The build flag +`ENABLE_PLAT_COMPAT` (enabled by default), specifies whether to enable this +layer or not. A platform port which has migrated to the new API can disable +this flag within the platform specific makefile. + +The compatibility layer works on the assumption that the onus of +state coordination, in case multiple low power states are supported, +is with the platform. The generic PSCI implementation only takes into +account whether the suspend request is power down or not. This corresponds +with the behavior of the PSCI implementation before the introduction of +new frameworks. Also, it assumes that the affinity levels of the platform +correspond directly to the power domain levels. + +The compatibility layer dynamically constructs the new topology +description array by querying the platform using `plat_get_aff_count()` +and `plat_get_aff_count()` APIs. The linear index returned by +`platform_get_core_pos()` is used as the core index for the cores. The +higher level (non-core) power domain nodes must know the cores contained +within its domain. It does so by storing the core index of first core +within it and number of core indexes following it. This means that core +indices returned by `platform_get_core_pos()` for cores within a particular +power domain must be consecutive. We expect that this is the case for most +platform ports including ARM reference platforms. + +The old PSCI helpers like `psci_get_suspend_powerstate()`, +`psci_get_suspend_stateid()`, `psci_get_suspend_stateid_by_mpidr()`, +`psci_get_max_phys_off_afflvl()` and `psci_get_suspend_afflvl()` are also +implemented for the compatibility layer. This allows the existing +platform ports to work with the new PSCI frameworks without significant +rework. + + +4. Deprecated Platform API +--------------------------- + +This section documents the deprecated platform porting API. + +## Common mandatory modifications + +The mandatory macros to be defined by the platform port in `platform_def.h` + +* **#define : PLATFORM_NUM_AFFS** + + Defines the total number of nodes in the affinity hierarchy at all affinity + levels used by the platform. + +* **#define : PLATFORM_MAX_AFFLVL** + + Defines the maximum affinity level that the power management operations + should apply to. ARMv8-A has support for four affinity levels. It is likely + that hardware will implement fewer affinity levels. This macro allows the + PSCI implementation to consider only those affinity levels in the system + that the platform implements. For example, the Base AEM FVP implements two + clusters with a configurable number of cores. It reports the maximum + affinity level as 1, resulting in PSCI power control up to the cluster + level. + +The following functions must be implemented by the platform port to enable +the reset vector code to perform the required tasks. + +### Function : platform_get_entrypoint() [mandatory] + + Argument : unsigned long + Return : unsigned long + +This function is called with the `SCTLR.M` and `SCTLR.C` bits disabled. The core +is identified by its `MPIDR`, which is passed as the argument. The function is +responsible for distinguishing between a warm and cold reset using platform- +specific means. If it is a warm reset, it returns the entrypoint into the +BL3-1 image that the core must jump to. If it is a cold reset, this function +must return zero. + +This function is also responsible for implementing a platform-specific mechanism +to handle the condition where the core has been warm reset but there is no +entrypoint to jump to. + +This function does not follow the Procedure Call Standard used by the +Application Binary Interface for the ARM 64-bit architecture. The caller should +not assume that callee saved registers are preserved across a call to this +function. + +### Function : platform_is_primary_cpu() [mandatory] + + Argument : unsigned long + Return : unsigned int + +This function identifies a core by its `MPIDR`, which is passed as the argument, +to determine whether this core is the primary core or a secondary core. A return +value of zero indicates that the core is not the primary core, while a non-zero +return value indicates that the core is the primary core. + +## Common optional modifications + +### Function : platform_get_core_pos() + + Argument : unsigned long + Return : int + +A platform may need to convert the `MPIDR` of a core to an absolute number, which +can be used as a core-specific linear index into blocks of memory (for example +while allocating per-core stacks). This routine contains a simple mechanism +to perform this conversion, using the assumption that each cluster contains a +maximum of four cores: + + linear index = cpu_id + (cluster_id * 4) + + cpu_id = 8-bit value in MPIDR at affinity level 0 + cluster_id = 8-bit value in MPIDR at affinity level 1 + + +### Function : platform_set_stack() + + Argument : unsigned long + Return : void + +This function sets the current stack pointer to the normal memory stack that +has been allocated for the core specified by MPIDR. For BL images that only +require a stack for the primary core the parameter is ignored. The size of +the stack allocated to each core is specified by the platform defined constant +`PLATFORM_STACK_SIZE`. + +Common implementations of this function for the UP and MP BL images are +provided in [plat/common/aarch64/platform_up_stack.S] and +[plat/common/aarch64/platform_mp_stack.S] + + +### Function : platform_get_stack() + + Argument : unsigned long + Return : unsigned long + +This function returns the base address of the normal memory stack that +has been allocated for the core specificed by MPIDR. For BL images that only +require a stack for the primary core the parameter is ignored. The size of +the stack allocated to each core is specified by the platform defined constant +`PLATFORM_STACK_SIZE`. + +Common implementations of this function for the UP and MP BL images are +provided in [plat/common/aarch64/platform_up_stack.S] and +[plat/common/aarch64/platform_mp_stack.S] + + +## Modifications for Power State Coordination Interface (in BL3-1) + +The following functions must be implemented to initialize PSCI functionality in +the ARM Trusted Firmware. + + +### Function : plat_get_aff_count() [mandatory] + + Argument : unsigned int, unsigned long + Return : unsigned int + +This function may execute with the MMU and data caches enabled if the platform +port does the necessary initializations in `bl31_plat_arch_setup()`. It is only +called by the primary core. + +This function is called by the PSCI initialization code to detect the system +topology. Its purpose is to return the number of affinity instances implemented +at a given `affinity level` (specified by the first argument) and a given +`MPIDR` (specified by the second argument). For example, on a dual-cluster +system where first cluster implements two cores and the second cluster +implements four cores, a call to this function with an `MPIDR` corresponding +to the first cluster (`0x0`) and affinity level 0, would return 2. A call +to this function with an `MPIDR` corresponding to the second cluster (`0x100`) +and affinity level 0, would return 4. + + +### Function : plat_get_aff_state() [mandatory] + + Argument : unsigned int, unsigned long + Return : unsigned int + +This function may execute with the MMU and data caches enabled if the platform +port does the necessary initializations in `bl31_plat_arch_setup()`. It is only +called by the primary core. + +This function is called by the PSCI initialization code. Its purpose is to +return the state of an affinity instance. The affinity instance is determined by +the affinity ID at a given `affinity level` (specified by the first argument) +and an `MPIDR` (specified by the second argument). The state can be one of +`PSCI_AFF_PRESENT` or `PSCI_AFF_ABSENT`. The latter state is used to cater for +system topologies where certain affinity instances are unimplemented. For +example, consider a platform that implements a single cluster with four cores and +another core implemented directly on the interconnect with the cluster. The +`MPIDR`s of the cluster would range from `0x0-0x3`. The `MPIDR` of the single +core is 0x100 to indicate that it does not belong to cluster 0. Cluster 1 +is missing but needs to be accounted for to reach this single core in the +topology tree. Therefore it is marked as `PSCI_AFF_ABSENT`. + + +### Function : platform_setup_pm() [mandatory] + + Argument : const plat_pm_ops ** + Return : int + +This function may execute with the MMU and data caches enabled if the platform +port does the necessary initializations in `bl31_plat_arch_setup()`. It is only +called by the primary core. + +This function is called by PSCI initialization code. Its purpose is to export +handler routines for platform-specific power management actions by populating +the passed pointer with a pointer to the private `plat_pm_ops` structure of +BL3-1. + +A description of each member of this structure is given below. A platform port +is expected to implement these handlers if the corresponding PSCI operation +is to be supported and these handlers are expected to succeed if the return +type is `void`. + +#### plat_pm_ops.affinst_standby() + +Perform the platform-specific setup to enter the standby state indicated by the +passed argument. The generic code expects the handler to succeed. + +#### plat_pm_ops.affinst_on() + +Perform the platform specific setup to power on an affinity instance, specified +by the `MPIDR` (first argument) and `affinity level` (third argument). The +`state` (fourth argument) contains the current state of that affinity instance +(ON or OFF). This is useful to determine whether any action must be taken. For +example, while powering on a core, the cluster that contains this core might +already be in the ON state. The platform decides what actions must be taken to +transition from the current state to the target state (indicated by the power +management operation). The generic code expects the platform to return +E_SUCCESS on success or E_INTERN_FAIL for any failure. + +#### plat_pm_ops.affinst_off() + +Perform the platform specific setup to power off an affinity instance of the +calling core. It is called by the PSCI `CPU_OFF` API implementation. + +The `affinity level` (first argument) and `state` (second argument) have +a similar meaning as described in the `affinst_on()` operation. They +identify the affinity instance on which the call is made and its +current state. This gives the platform port an indication of the +state transition it must make to perform the requested action. For example, if +the calling core is the last powered on core in the cluster, after powering down +affinity level 0 (the core), the platform port should power down affinity +level 1 (the cluster) as well. The generic code expects the handler to succeed. + +#### plat_pm_ops.affinst_suspend() + +Perform the platform specific setup to power off an affinity instance of the +calling core. It is called by the PSCI `CPU_SUSPEND` API and `SYSTEM_SUSPEND` +API implementation + +The `affinity level` (second argument) and `state` (third argument) have a +similar meaning as described in the `affinst_on()` operation. They are used to +identify the affinity instance on which the call is made and its current state. +This gives the platform port an indication of the state transition it must +make to perform the requested action. For example, if the calling core is the +last powered on core in the cluster, after powering down affinity level 0 +(the core), the platform port should power down affinity level 1 (the cluster) +as well. + +The difference between turning an affinity instance off and suspending it +is that in the former case, the affinity instance is expected to re-initialize +its state when it is next powered on (see `affinst_on_finish()`). In the latter +case, the affinity instance is expected to save enough state so that it can +resume execution by restoring this state when it is powered on (see +`affinst_suspend_finish()`).The generic code expects the handler to succeed. + +#### plat_pm_ops.affinst_on_finish() + +This function is called by the PSCI implementation after the calling core is +powered on and released from reset in response to an earlier PSCI `CPU_ON` call. +It performs the platform-specific setup required to initialize enough state for +this core to enter the Normal world and also provide secure runtime firmware +services. + +The `affinity level` (first argument) and `state` (second argument) have a +similar meaning as described in the previous operations. The generic code +expects the handler to succeed. + +#### plat_pm_ops.affinst_suspend_finish() + +This function is called by the PSCI implementation after the calling core is +powered on and released from reset in response to an asynchronous wakeup +event, for example a timer interrupt that was programmed by the core during the +`CPU_SUSPEND` call or `SYSTEM_SUSPEND` call. It performs the platform-specific +setup required to restore the saved state for this core to resume execution +in the Normal world and also provide secure runtime firmware services. + +The `affinity level` (first argument) and `state` (second argument) have a +similar meaning as described in the previous operations. The generic code +expects the platform to succeed. + +#### plat_pm_ops.validate_power_state() + +This function is called by the PSCI implementation during the `CPU_SUSPEND` +call to validate the `power_state` parameter of the PSCI API. If the +`power_state` is known to be invalid, the platform must return +PSCI_E_INVALID_PARAMS as an error, which is propagated back to the Normal +world PSCI client. + +#### plat_pm_ops.validate_ns_entrypoint() + +This function is called by the PSCI implementation during the `CPU_SUSPEND`, +`SYSTEM_SUSPEND` and `CPU_ON` calls to validate the Non-secure `entry_point` +parameter passed by the Normal world. If the `entry_point` is known to be +invalid, the platform must return PSCI_E_INVALID_PARAMS as an error, which is +propagated back to the Normal world PSCI client. + +#### plat_pm_ops.get_sys_suspend_power_state() + +This function is called by the PSCI implementation during the `SYSTEM_SUSPEND` +call to return the `power_state` parameter. This allows the platform to encode +the appropriate State-ID field within the `power_state` parameter which can be +utilized in `affinst_suspend()` to suspend to system affinity level. The +`power_state` parameter should be in the same format as specified by the +PSCI specification for the CPU_SUSPEND API. + +- - - - - - - - - - - - - - - - - - - - - - - - - - + +_Copyright (c) 2015, ARM Limited and Contributors. All rights reserved._ + + +[Porting Guide]: porting-guide.md +[Power Domain Topology Design]: psci-pd-tree.md +[PSCI]: http://infocenter.arm.com/help/topic/com.arm.doc.den0022c/DEN0022C_Power_State_Coordination_Interface.pdf +[psci pd tree]: psci-pd-tree.md +[my_core_pos]: porting-guide.md#function--plat_my_core_pos +[get_target_pwr_state]: porting-guide.md#function--plat_get_target_pwr_state-optional +[psci_ops]: porting-guide.md#function--plat_setup_psci_ops-mandatory +[plat/arm/board/fvp/fvp_pm.c]: ../plat/arm/board/fvp/fvp_pm.c +[plat/common/aarch64/platform_mp_stack.S]: ../plat/common/aarch64/platform_mp_stack.S +[plat/common/aarch64/platform_up_stack.S]: ../plat/common/aarch64/platform_up_stack.S diff --git a/docs/porting-guide.md b/docs/porting-guide.md index 81cbe1be06..6846ddfe2c 100644 --- a/docs/porting-guide.md +++ b/docs/porting-guide.md @@ -8,7 +8,8 @@ Contents 2. [Common Modifications](#2--common-modifications) * [Common mandatory modifications](#21-common-mandatory-modifications) * [Handling reset](#22-handling-reset) - * [Common optional modifications](#23-common-optional-modifications) + * [Common mandatory modifications](#23-common-mandatory-modifications) + * [Common optional modifications](#24-common-optional-modifications) 3. [Boot Loader stage specific modifications](#3--modifications-specific-to-a-boot-loader-stage) * [Boot Loader stage 1 (BL1)](#31-boot-loader-stage-1-bl1) * [Boot Loader stage 2 (BL2)](#32-boot-loader-stage-2-bl2) @@ -25,6 +26,10 @@ Contents 1. Introduction ---------------- +Please note that this document has been updated for the new platform API +as required by the PSCI v1.0 implementation. Please refer to the +[Migration Guide] for the previous platform API. + Porting the ARM Trusted Firmware to a new platform involves making some mandatory and optional modifications for both the cold and warm boot paths. Modifications consist of: @@ -139,21 +144,39 @@ platform port to define additional platform porting constants in Defines the total number of CPUs implemented by the platform across all clusters in the system. -* **#define : PLATFORM_NUM_AFFS** +* **#define : PLAT_NUM_PWR_DOMAINS** - Defines the total number of nodes in the affinity heirarchy at all affinity - levels used by the platform. + Defines the total number of nodes in the power domain topology + tree at all the power domain levels used by the platform. + This macro is used by the PSCI implementation to allocate + data structures to represent power domain topology. -* **#define : PLATFORM_MAX_AFFLVL** +* **#define : PLAT_MAX_PWR_LVL** - Defines the maximum affinity level that the power management operations - should apply to. ARMv8-A has support for 4 affinity levels. It is likely - that hardware will implement fewer affinity levels. This macro allows the - PSCI implementation to consider only those affinity levels in the system - that the platform implements. For example, the Base AEM FVP implements two - clusters with a configurable number of CPUs. It reports the maximum - affinity level as 1, resulting in PSCI power control up to the cluster - level. + Defines the maximum power domain level that the power management operations + should apply to. More often, but not always, the power domain level + corresponds to affinity level. This macro allows the PSCI implementation + to know the highest power domain level that it should consider for power + management operations in the system that the platform implements. For + example, the Base AEM FVP implements two clusters with a configurable + number of CPUs and it reports the maximum power domain level as 1. + +* **#define : PLAT_MAX_OFF_STATE** + + Defines the local power state corresponding to the deepest power down + possible at every power domain level in the platform. The local power + states for each level may be sparsely allocated between 0 and this value + with 0 being reserved for the RUN state. The PSCI implementation uses this + value to initialize the local power states of the power domain nodes and + to specify the requested power state for a PSCI_CPU_OFF call. + +* **#define : PLAT_MAX_RET_STATE** + + Defines the local power state corresponding to the deepest retention state + possible at every power domain level in the platform. This macro should be + a value less than PLAT_MAX_OFF_STATE and greater than 0. It is used by the + PSCI implementation to distuiguish between retention and power down local + power states within PSCI_CPU_SUSPEND call. * **#define : BL1_RO_BASE** @@ -408,21 +431,17 @@ The following functions need to be implemented by the platform port to enable reset vector code to perform the above tasks. -### Function : platform_get_entrypoint() [mandatory] +### Function : plat_get_my_entrypoint() [mandatory when PROGRAMMABLE_RESET_ADDRESS == 0] - Argument : unsigned long - Return : unsigned int + Argument : void + Return : unsigned long -This function is called with the `SCTLR.M` and `SCTLR.C` bits disabled. The CPU -is identified by its `MPIDR`, which is passed as the argument. The function is -responsible for distinguishing between a warm and cold reset using platform- -specific means. If it's a warm reset then it returns the entrypoint into the -BL3-1 image that the CPU must jump to. If it's a cold reset then this function -must return zero. - -This function is also responsible for implementing a platform-specific mechanism -to handle the condition where the CPU has been warm reset but there is no -entrypoint to jump to. +This function is called with the called with the MMU and caches disabled +(`SCTLR_EL3.M` = 0 and `SCTLR_EL3.C` = 0). The function is responsible for +distinguishing between a warm and cold reset for the current CPU using +platform-specific means. If it's a warm reset, then it returns the warm +reset entrypoint point provided to `plat_setup_psci_ops()` during +BL3-1 initialization. If it's a cold reset then this function must return zero. This function does not follow the Procedure Call Standard used by the Application Binary Interface for the ARM 64-bit architecture. The caller should @@ -431,11 +450,16 @@ function. This function fulfills requirement 1 and 3 listed above. +Note that for platforms that support programming the reset address, it is +expected that a CPU will start executing code directly at the right address, +both on a cold and warm reset. In this case, there is no need to identify the +type of reset nor to query the warm reset entrypoint. Therefore, implementing +this function is not required on such platforms. + ### Function : plat_secondary_cold_boot_setup() [mandatory] Argument : void - Return : void This function is called with the MMU and data caches disabled. It is responsible for placing the executing secondary CPU in a platform-specific state until the @@ -449,15 +473,15 @@ requires them. This function fulfills requirement 2 above. -### Function : platform_is_primary_cpu() [mandatory] +### Function : plat_is_my_cpu_primary() [mandatory] - Argument : unsigned long + Argument : void Return : unsigned int -This function identifies a CPU by its `MPIDR`, which is passed as the argument, -to determine whether this CPU is the primary CPU or a secondary CPU. A return -value of zero indicates that the CPU is not the primary CPU, while a non-zero -return value indicates that the CPU is the primary CPU. +This function identifies whether the current CPU is the primary CPU or a +secondary CPU. A return value of zero indicates that the CPU is not the +primary CPU, while a non-zero return value indicates that the CPU is the +primary CPU. ### Function : platform_mem_init() [mandatory] @@ -501,58 +525,75 @@ retrieved from the platform. The function also reports extra information related to the ROTPK in the flags parameter. +2.3 Common mandatory modifications +--------------------------------- -2.3 Common optional modifications +The following functions are mandatory functions which need to be implemented +by the platform port. + +### Function : plat_my_core_pos() + + Argument : void + Return : unsigned int + +This funtion returns the index of the calling CPU which is used as a +CPU-specific linear index into blocks of memory (for example while allocating +per-CPU stacks). This function will be invoked very early in the +initialization sequence which mandates that this function should be +implemented in assembly and should not rely on the avalability of a C +runtime environment. + +This function plays a crucial role in the power domain topology framework in +PSCI and details of this can be found in [Power Domain Topology Design]. + +### Function : plat_core_pos_by_mpidr() + + Argument : u_register_t + Return : int + +This function validates the `MPIDR` of a CPU and converts it to an index, +which can be used as a CPU-specific linear index into blocks of memory. In +case the `MPIDR` is invalid, this function returns -1. This function will only +be invoked by BL3-1 after the power domain topology is initialized and can +utilize the C runtime environment. For further details about how ARM Trusted +Firmware represents the power domain topology and how this relates to the +linear CPU index, please refer [Power Domain Topology Design]. + + + +2.4 Common optional modifications --------------------------------- The following are helper functions implemented by the firmware that perform common platform-specific tasks. A platform may choose to override these definitions. +### Function : plat_set_my_stack() -### Function : platform_get_core_pos() - - Argument : unsigned long - Return : int - -A platform may need to convert the `MPIDR` of a CPU to an absolute number, which -can be used as a CPU-specific linear index into blocks of memory (for example -while allocating per-CPU stacks). This routine contains a simple mechanism -to perform this conversion, using the assumption that each cluster contains a -maximum of 4 CPUs: - - linear index = cpu_id + (cluster_id * 4) - - cpu_id = 8-bit value in MPIDR at affinity level 0 - cluster_id = 8-bit value in MPIDR at affinity level 1 - - -### Function : platform_set_stack() - - Argument : unsigned long + Argument : void Return : void This function sets the current stack pointer to the normal memory stack that -has been allocated for the CPU specificed by MPIDR. For BL images that only -require a stack for the primary CPU the parameter is ignored. The size of -the stack allocated to each CPU is specified by the platform defined constant -`PLATFORM_STACK_SIZE`. +has been allocated for the current CPU. For BL images that only require a +stack for the primary CPU, the UP version of the function is used. The size +of the stack allocated to each CPU is specified by the platform defined +constant `PLATFORM_STACK_SIZE`. Common implementations of this function for the UP and MP BL images are provided in [plat/common/aarch64/platform_up_stack.S] and [plat/common/aarch64/platform_mp_stack.S] -### Function : platform_get_stack() +### Function : plat_get_my_stack() - Argument : unsigned long + Argument : void Return : unsigned long This function returns the base address of the normal memory stack that -has been allocated for the CPU specificed by MPIDR. For BL images that only -require a stack for the primary CPU the parameter is ignored. The size of -the stack allocated to each CPU is specified by the platform defined constant -`PLATFORM_STACK_SIZE`. +has been allocated for the current CPU. For BL images that only require a +stack for the primary CPU, the UP version of the function is used. The size +of the stack allocated to each CPU is specified by the platform defined +constant `PLATFORM_STACK_SIZE`. Common implementations of this function for the UP and MP BL images are provided in [plat/common/aarch64/platform_up_stack.S] and @@ -1113,147 +1154,159 @@ modes table. ------------------------------------------------ The ARM Trusted Firmware's implementation of the PSCI API is based around the -concept of an _affinity instance_. Each _affinity instance_ can be uniquely -identified in a system by a CPU ID (the processor `MPIDR` is used in the PSCI -interface) and an _affinity level_. A processing element (for example, a -CPU) is at level 0. If the CPUs in the system are described in a tree where the -node above a CPU is a logical grouping of CPUs that share some state, then -affinity level 1 is that group of CPUs (for example, a cluster), and affinity -level 2 is a group of clusters (for example, the system). The implementation -assumes that the affinity level 1 ID can be computed from the affinity level 0 -ID (for example, a unique cluster ID can be computed from the CPU ID). The -current implementation computes this on the basis of the recommended use of -`MPIDR` affinity fields in the ARM Architecture Reference Manual. +concept of a _power domain_. A _power domain_ is a CPU or a logical group of +CPUs which share some state on which power management operations can be +performed as specified by [PSCI]. Each CPU in the system is assigned a cpu +index which is a unique number between `0` and `PLATFORM_CORE_COUNT - 1`. +The _power domains_ are arranged in a hierarchial tree structure and +each _power domain_ can be identified in a system by the cpu index of any CPU +that is part of that domain and a _power domain level_. A processing element +(for example, a CPU) is at level 0. If the _power domain_ node above a CPU is +a logical grouping of CPUs that share some state, then level 1 is that group +of CPUs (for example, a cluster), and level 2 is a group of clusters +(for example, the system). More details on the power domain topology and its +organization can be found in [Power Domain Topology Design]. BL3-1's platform initialization code exports a pointer to the platform-specific power management operations required for the PSCI implementation to function -correctly. This information is populated in the `plat_pm_ops` structure. The -PSCI implementation calls members of the `plat_pm_ops` structure for performing -power management operations for each affinity instance. For example, the target -CPU is specified by its `MPIDR` in a PSCI `CPU_ON` call. The `affinst_on()` -handler (if present) is called for each affinity instance as the PSCI -implementation powers up each affinity level implemented in the `MPIDR` (for -example, CPU, cluster and system). +correctly. This information is populated in the `plat_psci_ops` structure. The +PSCI implementation calls members of the `plat_psci_ops` structure for performing +power management operations on the power domains. For example, the target +CPU is specified by its `MPIDR` in a PSCI `CPU_ON` call. The `pwr_domain_on()` +handler (if present) is called for the CPU power domain. + +The `power-state` parameter of a PSCI `CPU_SUSPEND` call can be used to +describe composite power states specific to a platform. The PSCI implementation +defines a generic representation of the power-state parameter viz which is an +array of local power states where each index corresponds to a power domain +level. Each entry contains the local power state the power domain at that power +level could enter. It depends on the `validate_power_state()` handler to +convert the power-state parameter (possibly encoding a composite power state) +passed in a PSCI `CPU_SUSPEND` call to this representation. The following functions must be implemented to initialize PSCI functionality in the ARM Trusted Firmware. -### Function : plat_get_aff_count() [mandatory] +### Function : plat_get_target_pwr_state() [optional] - Argument : unsigned int, unsigned long - Return : unsigned int + Argument : unsigned int, const plat_local_state_t *, unsigned int + Return : plat_local_state_t -This function may execute with the MMU and data caches enabled if the platform -port does the necessary initializations in `bl31_plat_arch_setup()`. It is only -called by the primary CPU. +The PSCI generic code uses this function to let the platform participate in +state coordination during a power management operation. The function is passed +a pointer to an array of platform specific local power state `states` (second +argument) which contains the requested power state for each CPU at a particular +power domain level `lvl` (first argument) within the power domain. The function +is expected to traverse this array of upto `ncpus` (third argument) and return +a coordinated target power state by the comparing all the requested power +states. The target power state should not be deeper than any of the requested +power states. -This function is called by the PSCI initialization code to detect the system -topology. Its purpose is to return the number of affinity instances implemented -at a given `affinity level` (specified by the first argument) and a given -`MPIDR` (specified by the second argument). For example, on a dual-cluster -system where first cluster implements 2 CPUs and the second cluster implements 4 -CPUs, a call to this function with an `MPIDR` corresponding to the first cluster -(`0x0`) and affinity level 0, would return 2. A call to this function with an -`MPIDR` corresponding to the second cluster (`0x100`) and affinity level 0, -would return 4. +A weak definition of this API is provided by default wherein it assumes +that the platform assigns a local state value in order of increasing depth +of the power state i.e. for two power states X & Y, if X < Y +then X represents a shallower power state than Y. As a result, the +coordinated target local power state for a power domain will be the minimum +of the requested local power state values. -### Function : plat_get_aff_state() [mandatory] +### Function : plat_get_power_domain_tree_desc() [mandatory] - Argument : unsigned int, unsigned long - Return : unsigned int + Argument : void + Return : const unsigned char * -This function may execute with the MMU and data caches enabled if the platform -port does the necessary initializations in `bl31_plat_arch_setup()`. It is only -called by the primary CPU. - -This function is called by the PSCI initialization code. Its purpose is to -return the state of an affinity instance. The affinity instance is determined by -the affinity ID at a given `affinity level` (specified by the first argument) -and an `MPIDR` (specified by the second argument). The state can be one of -`PSCI_AFF_PRESENT` or `PSCI_AFF_ABSENT`. The latter state is used to cater for -system topologies where certain affinity instances are unimplemented. For -example, consider a platform that implements a single cluster with 4 CPUs and -another CPU implemented directly on the interconnect with the cluster. The -`MPIDR`s of the cluster would range from `0x0-0x3`. The `MPIDR` of the single -CPU would be 0x100 to indicate that it does not belong to cluster 0. Cluster 1 -is missing but needs to be accounted for to reach this single CPU in the -topology tree. Hence it is marked as `PSCI_AFF_ABSENT`. +This function returns a pointer to the byte array containing the power domain +topology tree description. The format and method to construct this array are +described in [Power Domain Topology Design]. The BL3-1 PSCI initilization code +requires this array to be described by the platform, either statically or +dynamically, to initialize the power domain topology tree. In case the array +is populated dynamically, then plat_core_pos_by_mpidr() and +plat_my_core_pos() should also be implemented suitably so that the topology +tree description matches the CPU indices returned by these APIs. These APIs +together form the platform interface for the PSCI topology framework. -### Function : platform_setup_pm() [mandatory] +## Function : plat_setup_psci_ops() [mandatory] - Argument : const plat_pm_ops ** + Argument : uintptr_t, const plat_psci_ops ** Return : int This function may execute with the MMU and data caches enabled if the platform port does the necessary initializations in `bl31_plat_arch_setup()`. It is only called by the primary CPU. -This function is called by PSCI initialization code. Its purpose is to export -handler routines for platform-specific power management actions by populating -the passed pointer with a pointer to BL3-1's private `plat_pm_ops` structure. +This function is called by PSCI initialization code. Its purpose is to let +the platform layer know about the warm boot entrypoint through the +`sec_entrypoint` (first argument) and to export handler routines for +platform-specific psci power management actions by populating the passed +pointer with a pointer to BL3-1's private `plat_psci_ops` structure. A description of each member of this structure is given below. Please refer to the ARM FVP specific implementation of these handlers in -[plat/arm/board/fvp/fvp_pm.c] as an example. A platform port is expected to -implement these handlers if the corresponding PSCI operation is to be supported -and these handlers are expected to succeed if the return type is `void`. +[plat/arm/board/fvp/fvp_pm.c] as an example. For each PSCI function that the +platform wants to support, the associated operation or operations in this +structure must be provided and implemented (Refer section 4 of +[Firmware Design] for the PSCI API supported in Trusted Firmware). To disable +a PSCI function in a platform port, the operation should be removed from this +structure instead of providing an empty implementation. -#### plat_pm_ops.affinst_standby() +#### plat_psci_ops.cpu_standby() -Perform the platform-specific setup to enter the standby state indicated by the -passed argument. The generic code expects the handler to succeed. +Perform the platform-specific actions to enter the standby state for a cpu +indicated by the passed argument. This provides a fast path for CPU standby +wherein overheads of PSCI state management and lock acquistion is avoided. +For this handler to be invoked by the PSCI `CPU_SUSPEND` API implementation, +the suspend state type specified in the `power-state` parameter should be +STANDBY and the target power domain level specified should be the CPU. The +handler should put the CPU into a low power retention state (usually by +issuing a wfi instruction) and ensure that it can be woken up from that +state by a normal interrupt. The generic code expects the handler to succeed. -#### plat_pm_ops.affinst_on() +#### plat_psci_ops.pwr_domain_on() -Perform the platform specific setup to power on an affinity instance, specified -by the `MPIDR` (first argument) and `affinity level` (third argument). The -`state` (fourth argument) contains the current state of that affinity instance -(ON or OFF). This is useful to determine whether any action must be taken. For -example, while powering on a CPU, the cluster that contains this CPU might -already be in the ON state. The platform decides what actions must be taken to -transition from the current state to the target state (indicated by the power -management operation). The generic code expects the platform to return -E_SUCCESS on success or E_INTERN_FAIL for any failure. +Perform the platform specific actions to power on a CPU, specified +by the `MPIDR` (first argument). The generic code expects the platform to +return PSCI_E_SUCCESS on success or PSCI_E_INTERN_FAIL for any failure. -#### plat_pm_ops.affinst_off() +#### plat_psci_ops.pwr_domain_off() -Perform the platform specific setup to power off an affinity instance of the -calling CPU. It is called by the PSCI `CPU_OFF` API implementation. +Perform the platform specific actions to prepare to power off the calling CPU +and its higher parent power domain levels as indicated by the `target_state` +(first argument). It is called by the PSCI `CPU_OFF` API implementation. -The `affinity level` (first argument) and `state` (second argument) have -a similar meaning as described in the `affinst_on()` operation. They are -used to identify the affinity instance on which the call is made and its -current state. This gives the platform port an indication of the -state transition it must make to perform the requested action. For example, if -the calling CPU is the last powered on CPU in the cluster, after powering down -affinity level 0 (CPU), the platform port should power down affinity level 1 -(the cluster) as well. The generic code expects the handler to succeed. +The `target_state` encodes the platform coordinated target local power states +for the CPU power domain and its parent power domain levels. The handler +needs to perform power management operation corresponding to the local state +at each power level. -#### plat_pm_ops.affinst_suspend() +For this handler, the local power state for the CPU power domain will be a +power down state where as it could be either power down, retention or run state +for the higher power domain levels depending on the result of state +coordination. The generic code expects the handler to succeed. -Perform the platform specific setup to power off an affinity instance of the -calling CPU. It is called by the PSCI `CPU_SUSPEND` API and `SYSTEM_SUSPEND` -API implementation +#### plat_psci_ops.pwr_domain_suspend() -The `affinity level` (second argument) and `state` (third argument) have a -similar meaning as described in the `affinst_on()` operation. They are used to -identify the affinity instance on which the call is made and its current state. -This gives the platform port an indication of the state transition it must -make to perform the requested action. For example, if the calling CPU is the -last powered on CPU in the cluster, after powering down affinity level 0 (CPU), -the platform port should power down affinity level 1 (the cluster) as well. +Perform the platform specific actions to prepare to suspend the calling +CPU and its higher parent power domain levels as indicated by the +`target_state` (first argument). It is called by the PSCI `CPU_SUSPEND` +API implementation. -The difference between turning an affinity instance off versus suspending it -is that in the former case, the affinity instance is expected to re-initialize -its state when its next powered on (see `affinst_on_finish()`). In the latter -case, the affinity instance is expected to save enough state so that it can +The `target_state` has a similar meaning as described in +the `pwr_domain_off()` operation. It encodes the platform coordinated +target local power states for the CPU power domain and its parent +power domain levels. The handler needs to perform power management operation +corresponding to the local state at each power level. The generic code +expects the handler to succeed. + +The difference between turning a power domain off versus suspending it +is that in the former case, the power domain is expected to re-initialize +its state when it is next powered on (see `pwr_domain_on_finish()`). In the +latter case, the power domain is expected to save enough state so that it can resume execution by restoring this state when its powered on (see -`affinst_suspend_finish()`).The generic code expects the handler to succeed. +`pwr_domain_suspend_finish()`). -#### plat_pm_ops.affinst_on_finish() +#### plat_psci_ops.pwr_domain_on_finish() This function is called by the PSCI implementation after the calling CPU is powered on and released from reset in response to an earlier PSCI `CPU_ON` call. @@ -1261,11 +1314,12 @@ It performs the platform-specific setup required to initialize enough state for this CPU to enter the normal world and also provide secure runtime firmware services. -The `affinity level` (first argument) and `state` (second argument) have a -similar meaning as described in the previous operations. The generic code -expects the handler to succeed. +The `target_state` (first argument) is the prior state of the power domains +immediately before the CPU was turned on. It indicates which power domains +above the CPU might require initialization due to having previously been in +low power states. The generic code expects the handler to succeed. -#### plat_pm_ops.affinst_suspend_finish() +#### plat_psci_ops.pwr_domain_suspend_finish() This function is called by the PSCI implementation after the calling CPU is powered on and released from reset in response to an asynchronous wakeup @@ -1274,40 +1328,36 @@ event, for example a timer interrupt that was programmed by the CPU during the setup required to restore the saved state for this CPU to resume execution in the normal world and also provide secure runtime firmware services. -The `affinity level` (first argument) and `state` (second argument) have a -similar meaning as described in the previous operations. The generic code -expects the platform to succeed. +The `target_state` (first argument) has a similar meaning as described in +the `pwr_domain_on_finish()` operation. The generic code expects the platform +to succeed. -#### plat_pm_ops.validate_power_state() +#### plat_psci_ops.validate_power_state() This function is called by the PSCI implementation during the `CPU_SUSPEND` -call to validate the `power_state` parameter of the PSCI API. If the -`power_state` is known to be invalid, the platform must return -PSCI_E_INVALID_PARAMS as error, which is propagated back to the normal -world PSCI client. +call to validate the `power_state` parameter of the PSCI API and if valid, +populate it in `req_state` (second argument) array as power domain level +specific local states. If the `power_state` is invalid, the platform must +return PSCI_E_INVALID_PARAMS as error, which is propagated back to the +normal world PSCI client. -#### plat_pm_ops.validate_ns_entrypoint() +#### plat_psci_ops.validate_ns_entrypoint() This function is called by the PSCI implementation during the `CPU_SUSPEND`, `SYSTEM_SUSPEND` and `CPU_ON` calls to validate the non-secure `entry_point` -parameter passed by the normal world. If the `entry_point` is known to be -invalid, the platform must return PSCI_E_INVALID_PARAMS as error, which is +parameter passed by the normal world. If the `entry_point` is invalid, +the platform must return PSCI_E_INVALID_ADDRESS as error, which is propagated back to the normal world PSCI client. -#### plat_pm_ops.get_sys_suspend_power_state() +#### plat_psci_ops.get_sys_suspend_power_state() This function is called by the PSCI implementation during the `SYSTEM_SUSPEND` -call to return the `power_state` parameter. This allows the platform to encode -the appropriate State-ID field within the `power_state` parameter which can be -utilized in `affinst_suspend()` to suspend to system affinity level. The -`power_state` parameter should be in the same format as specified by the -PSCI specification for the CPU_SUSPEND API. +call to get the `req_state` parameter from platform which encodes the power +domain level specific local states to suspend to system affinity level. The +`req_state` will be utilized to do the PSCI state coordination and +`pwr_domain_suspend()` will be invoked with the coordinated target state to +enter system suspend. -BL3-1 platform initialization code must also detect the system topology and -the state of each affinity instance in the topology. This information is -critical for the PSCI runtime service to function correctly. More details are -provided in the description of the `plat_get_aff_count()` and -`plat_get_aff_state()` functions above. 3.4 Interrupt Management framework (in BL3-1) ---------------------------------------------- @@ -1475,6 +1525,12 @@ register x0. 4. Build flags --------------- +* **ENABLE_PLAT_COMPAT** + All the platforms ports conforming to this API specification should define + the build flag `ENABLE_PLAT_COMPAT` to 0 as the compatibility layer should + be disabled. For more details on compatibility layer, refer + [Migration Guide]. + There are some build flags which can be defined by the platform to control inclusion or exclusion of certain BL stages from the FIP image. These flags need to be defined in the platform makefile which will get included by the @@ -1589,6 +1645,9 @@ _Copyright (c) 2013-2015, ARM Limited and Contributors. All rights reserved._ [User Guide]: user-guide.md [FreeBSD]: http://www.freebsd.org [Firmware Design]: firmware-design.md +[Power Domain Topology Design]: psci-pd-tree.md +[PSCI]: http://infocenter.arm.com/help/topic/com.arm.doc.den0022c/DEN0022C_Power_State_Coordination_Interface.pdf +[Migration Guide]: platform-migration-guide.md [plat/common/aarch64/platform_mp_stack.S]: ../plat/common/aarch64/platform_mp_stack.S [plat/common/aarch64/platform_up_stack.S]: ../plat/common/aarch64/platform_up_stack.S diff --git a/docs/user-guide.md b/docs/user-guide.md index b9b69b36bf..8f271cf0a1 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -351,7 +351,12 @@ performed. * `PROGRAMMABLE_RESET_ADDRESS`: This option indicates whether the reset vector address can be programmed or is fixed on the platform. It can take - either 0 (fixed) or 1 (programmable). Default is 0. + either 0 (fixed) or 1 (programmable). Default is 0. If the platform has a + programmable reset address, it is expected that a CPU will start executing + code directly at the right address, both on a cold and warm reset. In this + case, there is no need to identify the entrypoint on boot and this has + implication for `plat_get_my_entrypoint()` platform porting interface. + (see the [Porting Guide] for details) * `PSCI_EXTENDED_STATE_ID`: As per PSCI1.0 Specification, there are 2 formats possible for the PSCI power-state parameter viz original and extended @@ -1092,4 +1097,5 @@ _Copyright (c) 2013-2015, ARM Limited and Contributors. All rights reserved._ [Juno Software Guide]: http://community.arm.com/docs/DOC-8396 [DS-5]: http://www.arm.com/products/tools/software-tools/ds-5/index.php [mbedTLS Repository]: https://github.com/ARMmbed/mbedtls.git +[Porting Guide]: ./porting-guide.md [Trusted Board Boot]: trusted-board-boot.md diff --git a/include/common/el3_common_macros.S b/include/common/el3_common_macros.S index 3b96081594..7946e728c5 100644 --- a/include/common/el3_common_macros.S +++ b/include/common/el3_common_macros.S @@ -181,7 +181,7 @@ * ------------------------------------------------------------- */ bl plat_is_my_cpu_primary - cbnz x0, do_primary_cold_boot + cbnz w0, do_primary_cold_boot /* This is a cold boot on a secondary CPU */ bl plat_secondary_cold_boot_setup diff --git a/plat/arm/board/fvp/aarch64/fvp_helpers.S b/plat/arm/board/fvp/aarch64/fvp_helpers.S index ec5ec8eff9..42e5b70c7f 100644 --- a/plat/arm/board/fvp/aarch64/fvp_helpers.S +++ b/plat/arm/board/fvp/aarch64/fvp_helpers.S @@ -154,11 +154,17 @@ _panic: b _panic endfunc plat_get_my_entrypoint - + /* ----------------------------------------------------- + * unsigned int plat_is_my_cpu_primary (void); + * + * Find out whether the current cpu is the primary + * cpu. + * ----------------------------------------------------- + */ func plat_is_my_cpu_primary mrs x0, mpidr_el1 and x0, x0, #(MPIDR_CLUSTER_MASK | MPIDR_CPU_MASK) cmp x0, #FVP_PRIMARY_CPU - cset x0, eq + cset w0, eq ret endfunc plat_is_my_cpu_primary diff --git a/plat/arm/css/common/aarch64/css_helpers.S b/plat/arm/css/common/aarch64/css_helpers.S index 5d5bf863c0..05bd864709 100644 --- a/plat/arm/css/common/aarch64/css_helpers.S +++ b/plat/arm/css/common/aarch64/css_helpers.S @@ -77,7 +77,7 @@ endfunc plat_get_my_entrypoint * Function to calculate the core position by * swapping the cluster order. This is necessary in order to * match the format of the boot information passed by the SCP - * and read in platform_is_primary_cpu below. + * and read in plat_is_my_cpu_primary below. * ----------------------------------------------------------- */ func plat_arm_calc_core_pos @@ -102,6 +102,6 @@ func plat_is_my_cpu_primary ldr x1, [x1] ubfx x1, x1, #PRIMARY_CPU_SHIFT, #PRIMARY_CPU_BIT_WIDTH cmp x0, x1 - cset x0, eq + cset w0, eq ret x9 endfunc plat_is_my_cpu_primary From 9d070b9928b874700395ca48780ce2c88b70e588 Mon Sep 17 00:00:00 2001 From: Soby Mathew Date: Wed, 29 Jul 2015 17:05:03 +0100 Subject: [PATCH 20/20] PSCI: Rework generic code to conform to coding guidelines This patch reworks the PSCI generic implementation to conform to ARM Trusted Firmware coding guidelines as described here: https://github.com/ARM-software/arm-trusted-firmware/wiki This patch also reviews the use of signed data types within PSCI Generic code and replaces them with their unsigned counterparts wherever they are not appropriate. The PSCI_INVALID_DATA macro which was defined to -1 is now replaced with PSCI_INVALID_PWR_LVL macro which is defined to PLAT_MAX_PWR_LVL + 1. Change-Id: Iaea422d0e46fc314e0b173c2b4c16e0d56b2515a --- include/bl31/services/psci.h | 30 +++++++---- include/bl31/services/psci_compat.h | 4 ++ services/std_svc/psci/psci_common.c | 78 ++++++++++++++-------------- services/std_svc/psci/psci_helpers.S | 4 +- services/std_svc/psci/psci_main.c | 28 +++++----- services/std_svc/psci/psci_off.c | 2 +- services/std_svc/psci/psci_on.c | 6 +-- services/std_svc/psci/psci_private.h | 28 +++++----- services/std_svc/psci/psci_setup.c | 22 ++++---- services/std_svc/psci/psci_suspend.c | 12 ++--- 10 files changed, 115 insertions(+), 99 deletions(-) diff --git a/include/bl31/services/psci.h b/include/bl31/services/psci.h index f6fd4872c1..004dd61461 100644 --- a/include/bl31/services/psci.h +++ b/include/bl31/services/psci.h @@ -167,7 +167,7 @@ #define PSCI_E_DISABLED -8 #define PSCI_E_INVALID_ADDRESS -9 -#define PSCI_INVALID_MPIDR ~(0ULL) +#define PSCI_INVALID_MPIDR ~((u_register_t)0) #ifndef __ASSEMBLY__ @@ -188,7 +188,7 @@ typedef enum { /* * Macro to represent invalid affinity level within PSCI. */ -#define PSCI_INVALID_DATA -1 +#define PSCI_INVALID_PWR_LVL (PLAT_MAX_PWR_LVL + 1) /* * Type for representing the local power state at a particular level. @@ -242,11 +242,13 @@ typedef struct psci_power_state { typedef struct psci_cpu_data { /* 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; + unsigned char target_pwrlvl; + /* The local power state of this CPU */ plat_local_state_t local_state; #if !USE_COHERENT_MEM @@ -270,7 +272,7 @@ typedef struct plat_psci_ops { void (*system_reset)(void) __dead2; int (*validate_power_state)(unsigned int power_state, psci_power_state_t *req_state); - int (*validate_ns_entrypoint)(unsigned long ns_entrypoint); + int (*validate_ns_entrypoint)(uintptr_t ns_entrypoint); void (*get_sys_suspend_power_state)( psci_power_state_t *req_state); } plat_psci_ops_t; @@ -297,17 +299,23 @@ typedef struct spd_pm_ops { * Function & Data prototypes ******************************************************************************/ unsigned int psci_version(void); -int psci_affinity_info(unsigned long, unsigned int); -int psci_migrate(unsigned long); +int psci_cpu_on(u_register_t target_cpu, + uintptr_t entrypoint, + u_register_t context_id); +int psci_cpu_suspend(unsigned int power_state, + uintptr_t entrypoint, + u_register_t context_id); +int psci_system_suspend(uintptr_t entrypoint, u_register_t context_id); +int psci_cpu_off(void); +int psci_affinity_info(u_register_t target_affinity, + unsigned int lowest_affinity_level); +int psci_migrate(u_register_t target_cpu); int psci_migrate_info_type(void); long psci_migrate_info_up_cpu(void); -int psci_cpu_on(unsigned long, - unsigned long, - unsigned long); +int psci_features(unsigned int psci_fid); void __dead2 psci_power_down_wfi(void); void psci_entrypoint(void); void psci_register_spd_pm_hook(const spd_pm_ops_t *); - uint64_t psci_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, @@ -318,7 +326,7 @@ uint64_t psci_smc_handler(uint32_t smc_fid, uint64_t flags); /* PSCI setup function */ -int32_t psci_setup(void); +int psci_setup(void); #endif /*__ASSEMBLY__*/ diff --git a/include/bl31/services/psci_compat.h b/include/bl31/services/psci_compat.h index cc80ae3030..24bd8dccaa 100644 --- a/include/bl31/services/psci_compat.h +++ b/include/bl31/services/psci_compat.h @@ -65,6 +65,10 @@ #define PLAT_MAX_RET_STATE 1 #define PLAT_MAX_OFF_STATE 2 +/* + * Macro to represent invalid affinity level within PSCI. + */ +#define PSCI_INVALID_DATA -1 #define psci_get_pstate_afflvl(pstate) psci_get_pstate_pwrlvl(pstate) diff --git a/services/std_svc/psci/psci_common.c b/services/std_svc/psci/psci_common.c index f810ddfac5..e12df04b4b 100644 --- a/services/std_svc/psci/psci_common.c +++ b/services/std_svc/psci/psci_common.c @@ -189,9 +189,9 @@ unsigned int psci_is_last_on_cpu(void) * been physically powered up. It is expected to be called immediately after * reset from assembler code. ******************************************************************************/ -static int get_power_on_target_pwrlvl(void) +static unsigned int get_power_on_target_pwrlvl(void) { - int pwrlvl; + unsigned int pwrlvl; /* * Assume that this cpu was suspended and retrieve its target power @@ -200,7 +200,7 @@ static int get_power_on_target_pwrlvl(void) * cpu can be turned off to. */ pwrlvl = psci_get_suspend_pwrlvl(); - if (pwrlvl == PSCI_INVALID_DATA) + if (pwrlvl == PSCI_INVALID_PWR_LVL) pwrlvl = PLAT_MAX_PWR_LVL; return pwrlvl; } @@ -236,8 +236,8 @@ void psci_init_req_local_pwr_states(void) * target state for this power domain during psci state coordination. An * assertion is added to prevent us from accessing the CPU power level. *****************************************************************************/ -static plat_local_state_t *psci_get_req_local_pwr_states(int pwrlvl, - int cpu_idx) +static plat_local_state_t *psci_get_req_local_pwr_states(unsigned int pwrlvl, + unsigned int cpu_idx) { assert(pwrlvl > PSCI_CPU_PWR_LVL); @@ -250,11 +250,10 @@ static plat_local_state_t *psci_get_req_local_pwr_states(int pwrlvl, * function will be called after a cpu is powered on to find the local state * each power domain has emerged from. *****************************************************************************/ -static void psci_get_target_local_pwr_states(uint32_t end_pwrlvl, +static void psci_get_target_local_pwr_states(unsigned int end_pwrlvl, psci_power_state_t *target_state) { - int lvl; - unsigned int parent_idx; + unsigned int parent_idx, lvl; plat_local_state_t *pd_state = target_state->pwr_domain_state; pd_state[PSCI_CPU_PWR_LVL] = psci_get_cpu_local_state(); @@ -270,7 +269,7 @@ static void psci_get_target_local_pwr_states(uint32_t end_pwrlvl, * code runs before caches are enabled. */ flush_dcache_range( - (uint64_t)&psci_non_cpu_pd_nodes[parent_idx], + (uintptr_t) &psci_non_cpu_pd_nodes[parent_idx], sizeof(psci_non_cpu_pd_nodes[parent_idx])); #endif pd_state[lvl] = psci_non_cpu_pd_nodes[parent_idx].local_state; @@ -288,11 +287,10 @@ static void psci_get_target_local_pwr_states(uint32_t end_pwrlvl, * enter. This function will be called after coordination of requested power * states has been done for each power level. *****************************************************************************/ -static void psci_set_target_local_pwr_states(uint32_t end_pwrlvl, +static void psci_set_target_local_pwr_states(unsigned int end_pwrlvl, const psci_power_state_t *target_state) { - int lvl; - unsigned int parent_idx; + unsigned int parent_idx, lvl; const plat_local_state_t *pd_state = target_state->pwr_domain_state; psci_set_cpu_local_state(pd_state[PSCI_CPU_PWR_LVL]); @@ -310,8 +308,8 @@ static void psci_set_target_local_pwr_states(uint32_t end_pwrlvl, psci_non_cpu_pd_nodes[parent_idx].local_state = pd_state[lvl]; #if !USE_COHERENT_MEM flush_dcache_range( - (uint64_t)&psci_non_cpu_pd_nodes[parent_idx], - sizeof(psci_non_cpu_pd_nodes[parent_idx])); + (uintptr_t)&psci_non_cpu_pd_nodes[parent_idx], + sizeof(psci_non_cpu_pd_nodes[parent_idx])); #endif parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node; } @@ -322,7 +320,7 @@ static void psci_set_target_local_pwr_states(uint32_t end_pwrlvl, * PSCI helper function to get the parent nodes corresponding to a cpu_index. ******************************************************************************/ void psci_get_parent_pwr_domain_nodes(unsigned int cpu_idx, - int end_lvl, + unsigned int end_lvl, unsigned int node_index[]) { unsigned int parent_node = psci_cpu_pd_nodes[cpu_idx].parent_node; @@ -339,10 +337,9 @@ void psci_get_parent_pwr_domain_nodes(unsigned int cpu_idx, * affinity info state, target power state and requested power state for the * current CPU and all its ancestor power domains to RUN. *****************************************************************************/ -void psci_set_pwr_domains_to_run(uint32_t end_pwrlvl) +void psci_set_pwr_domains_to_run(unsigned int end_pwrlvl) { - int lvl; - unsigned int parent_idx, cpu_idx = plat_my_core_pos(); + unsigned int parent_idx, cpu_idx = plat_my_core_pos(), lvl; parent_idx = psci_cpu_pd_nodes[cpu_idx].parent_node; /* Reset the local_state to RUN for the non cpu power domains. */ @@ -351,7 +348,7 @@ void psci_set_pwr_domains_to_run(uint32_t end_pwrlvl) PSCI_LOCAL_STATE_RUN; #if !USE_COHERENT_MEM flush_dcache_range( - (uint64_t)&psci_non_cpu_pd_nodes[parent_idx], + (uintptr_t) &psci_non_cpu_pd_nodes[parent_idx], sizeof(psci_non_cpu_pd_nodes[parent_idx])); #endif psci_set_req_local_pwr_state(lvl, @@ -387,7 +384,8 @@ void psci_set_pwr_domains_to_run(uint32_t end_pwrlvl) * This function will only be invoked with data cache enabled and while * powering down a core. *****************************************************************************/ -void psci_do_state_coordination(int end_pwrlvl, psci_power_state_t *state_info) +void psci_do_state_coordination(unsigned int end_pwrlvl, + psci_power_state_t *state_info) { unsigned int lvl, parent_idx, cpu_idx = plat_my_core_pos(); unsigned int start_idx, ncpus; @@ -463,7 +461,7 @@ int psci_validate_suspend_req(const psci_power_state_t *state_info, /* Find the target suspend power level */ target_lvl = psci_find_target_suspend_lvl(state_info); - if (target_lvl == PSCI_INVALID_DATA) + if (target_lvl == PSCI_INVALID_PWR_LVL) return PSCI_E_INVALID_PARAMS; /* All power domain levels are in a RUN state to begin with */ @@ -489,7 +487,7 @@ int psci_validate_suspend_req(const psci_power_state_t *state_info, max_off_lvl = psci_find_max_off_lvl(state_info); /* The target_lvl is either equal to the max_off_lvl or max_retn_lvl */ - max_retn_lvl = PSCI_INVALID_DATA; + max_retn_lvl = PSCI_INVALID_PWR_LVL; if (target_lvl != max_off_lvl) max_retn_lvl = target_lvl; @@ -498,8 +496,8 @@ int psci_validate_suspend_req(const psci_power_state_t *state_info, * has to be invalid and max retention level has to be a valid power * level. */ - if (!is_power_down_state && (max_off_lvl != PSCI_INVALID_DATA || - max_retn_lvl == PSCI_INVALID_DATA)) + if (!is_power_down_state && (max_off_lvl != PSCI_INVALID_PWR_LVL || + max_retn_lvl == PSCI_INVALID_PWR_LVL)) return PSCI_E_INVALID_PARAMS; return PSCI_E_SUCCESS; @@ -518,7 +516,7 @@ unsigned int psci_find_max_off_lvl(const psci_power_state_t *state_info) return i; } - return PSCI_INVALID_DATA; + return PSCI_INVALID_PWR_LVL; } /****************************************************************************** @@ -534,7 +532,7 @@ unsigned int psci_find_target_suspend_lvl(const psci_power_state_t *state_info) return i; } - return PSCI_INVALID_DATA; + return PSCI_INVALID_PWR_LVL; } /******************************************************************************* @@ -542,10 +540,11 @@ unsigned int psci_find_target_suspend_lvl(const psci_power_state_t *state_info) * tree that the operation should be applied to. It picks up locks in order of * increasing power domain level in the range specified. ******************************************************************************/ -void psci_acquire_pwr_domain_locks(int end_pwrlvl, unsigned int cpu_idx) +void psci_acquire_pwr_domain_locks(unsigned int end_pwrlvl, + unsigned int cpu_idx) { unsigned int parent_idx = psci_cpu_pd_nodes[cpu_idx].parent_node; - int level; + unsigned int level; /* No locking required for level 0. Hence start locking from level 1 */ for (level = PSCI_CPU_PWR_LVL + 1; level <= end_pwrlvl; level++) { @@ -559,7 +558,8 @@ void psci_acquire_pwr_domain_locks(int end_pwrlvl, unsigned int cpu_idx) * tree that the operation should be applied to. It releases the locks in order * of decreasing power domain level in the range specified. ******************************************************************************/ -void psci_release_pwr_domain_locks(int end_pwrlvl, unsigned int cpu_idx) +void psci_release_pwr_domain_locks(unsigned int end_pwrlvl, + unsigned int cpu_idx) { unsigned int parent_idx, parent_nodes[PLAT_MAX_PWR_LVL] = {0}; int level; @@ -577,7 +577,7 @@ void psci_release_pwr_domain_locks(int end_pwrlvl, unsigned int cpu_idx) /******************************************************************************* * Simple routine to determine whether a mpidr is valid or not. ******************************************************************************/ -int psci_validate_mpidr(unsigned long mpidr) +int psci_validate_mpidr(u_register_t mpidr) { if (plat_core_pos_by_mpidr(mpidr) < 0) return PSCI_E_INVALID_PARAMS; @@ -590,11 +590,13 @@ int psci_validate_mpidr(unsigned long mpidr) * PSCI entrypoint on power on/resume and returns it. ******************************************************************************/ static int psci_get_ns_ep_info(entry_point_info_t *ep, - uint64_t entrypoint, uint64_t context_id) + uintptr_t entrypoint, + u_register_t context_id) { - uint32_t ep_attr, mode, sctlr, daif, ee; - uint32_t ns_scr_el3 = read_scr_el3(); - uint32_t ns_sctlr_el1 = read_sctlr_el1(); + unsigned long ep_attr, sctlr; + unsigned int daif, ee, mode; + unsigned long ns_scr_el3 = read_scr_el3(); + unsigned long ns_sctlr_el1 = read_sctlr_el1(); sctlr = ns_scr_el3 & SCR_HCE_BIT ? read_sctlr_el2() : ns_sctlr_el1; ee = 0; @@ -648,7 +650,8 @@ static int psci_get_ns_ep_info(entry_point_info_t *ep, * 'entry_point_info'. ******************************************************************************/ int psci_validate_entry_point(entry_point_info_t *ep, - uint64_t entrypoint, uint64_t context_id) + uintptr_t entrypoint, + u_register_t context_id) { int rc; @@ -679,9 +682,8 @@ int psci_validate_entry_point(entry_point_info_t *ep, ******************************************************************************/ void psci_power_up_finish(void) { - unsigned int cpu_idx = plat_my_core_pos(); + unsigned int end_pwrlvl, cpu_idx = plat_my_core_pos(); psci_power_state_t state_info = { {PSCI_LOCAL_STATE_RUN} }; - int end_pwrlvl; /* * Verify that we have been explicitly turned ON or resumed from @@ -764,7 +766,7 @@ void psci_register_spd_pm_hook(const spd_pm_ops_t *pm) * is resident through the mpidr parameter. Else the value of the parameter on * return is undefined. ******************************************************************************/ -int psci_spd_migrate_info(uint64_t *mpidr) +int psci_spd_migrate_info(u_register_t *mpidr) { int rc; diff --git a/services/std_svc/psci/psci_helpers.S b/services/std_svc/psci/psci_helpers.S index bbfa5d5d7b..6ccf943c00 100644 --- a/services/std_svc/psci/psci_helpers.S +++ b/services/std_svc/psci/psci_helpers.S @@ -37,7 +37,7 @@ .globl psci_do_pwrup_cache_maintenance /* ----------------------------------------------------------------------- - * void psci_do_pwrdown_cache_maintenance(uint32_t power level); + * void psci_do_pwrdown_cache_maintenance(unsigned int power level); * * This function performs cache maintenance for the specified power * level. The levels of cache affected are determined by the power @@ -66,7 +66,7 @@ func psci_do_pwrdown_cache_maintenance * platform. * --------------------------------------------- */ - cmp x0, #PSCI_CPU_PWR_LVL + cmp w0, #PSCI_CPU_PWR_LVL b.eq do_core_pwr_dwn bl prepare_cluster_pwr_dwn b do_stack_maintenance diff --git a/services/std_svc/psci/psci_main.c b/services/std_svc/psci/psci_main.c index 6d3af20480..e6cd3a3a4d 100644 --- a/services/std_svc/psci/psci_main.c +++ b/services/std_svc/psci/psci_main.c @@ -41,9 +41,9 @@ /******************************************************************************* * PSCI frontend api for servicing SMCs. Described in the PSCI spec. ******************************************************************************/ -int psci_cpu_on(unsigned long target_cpu, - unsigned long entrypoint, - unsigned long context_id) +int psci_cpu_on(u_register_t target_cpu, + uintptr_t entrypoint, + u_register_t context_id) { int rc; @@ -77,8 +77,8 @@ unsigned int psci_version(void) } int psci_cpu_suspend(unsigned int power_state, - unsigned long entrypoint, - unsigned long context_id) + uintptr_t entrypoint, + u_register_t context_id) { int rc; unsigned int target_pwrlvl, is_power_down_state; @@ -147,8 +147,8 @@ int psci_cpu_suspend(unsigned int power_state, return PSCI_E_SUCCESS; } -int psci_system_suspend(unsigned long entrypoint, - unsigned long context_id) + +int psci_system_suspend(uintptr_t entrypoint, u_register_t context_id) { int rc; psci_power_state_t state_info; @@ -188,7 +188,7 @@ int psci_system_suspend(unsigned long entrypoint, int psci_cpu_off(void) { int rc; - int target_pwrlvl = PLAT_MAX_PWR_LVL; + unsigned int target_pwrlvl = PLAT_MAX_PWR_LVL; /* * Do what is needed to power off this CPU and possible higher power @@ -206,7 +206,7 @@ int psci_cpu_off(void) return rc; } -int psci_affinity_info(unsigned long target_affinity, +int psci_affinity_info(u_register_t target_affinity, unsigned int lowest_affinity_level) { unsigned int target_idx; @@ -223,10 +223,10 @@ int psci_affinity_info(unsigned long target_affinity, return psci_get_aff_info_state_by_idx(target_idx); } -int psci_migrate(unsigned long target_cpu) +int psci_migrate(u_register_t target_cpu) { int rc; - unsigned long resident_cpu_mpidr; + u_register_t resident_cpu_mpidr; rc = psci_spd_migrate_info(&resident_cpu_mpidr); if (rc != PSCI_TOS_UP_MIG_CAP) @@ -255,14 +255,14 @@ int psci_migrate(unsigned long target_cpu) int psci_migrate_info_type(void) { - unsigned long resident_cpu_mpidr; + u_register_t resident_cpu_mpidr; return psci_spd_migrate_info(&resident_cpu_mpidr); } long psci_migrate_info_up_cpu(void) { - unsigned long resident_cpu_mpidr; + u_register_t resident_cpu_mpidr; int rc; /* @@ -278,7 +278,7 @@ long psci_migrate_info_up_cpu(void) int psci_features(unsigned int psci_fid) { - uint32_t local_caps = psci_caps; + unsigned int local_caps = psci_caps; /* Check if it is a 64 bit function */ if (((psci_fid >> FUNCID_CC_SHIFT) & FUNCID_CC_MASK) == SMC_64) diff --git a/services/std_svc/psci/psci_off.c b/services/std_svc/psci/psci_off.c index 28fa52c032..f565ffb7ef 100644 --- a/services/std_svc/psci/psci_off.c +++ b/services/std_svc/psci/psci_off.c @@ -60,7 +60,7 @@ static void psci_set_power_off_state(psci_power_state_t *state_info) * interconnect level if the cpu is the last in the cluster and also the * program the power controller. ******************************************************************************/ -int psci_do_cpu_off(int end_pwrlvl) +int psci_do_cpu_off(unsigned int end_pwrlvl) { int rc, idx = plat_my_core_pos(); psci_power_state_t state_info; diff --git a/services/std_svc/psci/psci_on.c b/services/std_svc/psci/psci_on.c index d68198f2e2..cf1a782a51 100644 --- a/services/std_svc/psci/psci_on.c +++ b/services/std_svc/psci/psci_on.c @@ -84,9 +84,9 @@ static void psci_set_aff_info_state_by_idx(unsigned int cpu_idx, * The state of all the relevant power domains are changed after calling the * platform handler as it can return error. ******************************************************************************/ -int psci_cpu_on_start(unsigned long target_cpu, +int psci_cpu_on_start(u_register_t target_cpu, entry_point_info_t *ep, - int end_pwrlvl) + unsigned int end_pwrlvl) { int rc; unsigned int target_idx = plat_core_pos_by_mpidr(target_cpu); @@ -130,7 +130,7 @@ int psci_cpu_on_start(unsigned long target_cpu, * of the target cpu to allow it to perform the necessary * steps to power on. */ - rc = psci_plat_pm_ops->pwr_domain_on((u_register_t)target_cpu); + rc = psci_plat_pm_ops->pwr_domain_on(target_cpu); assert(rc == PSCI_E_SUCCESS || rc == PSCI_E_INTERN_FAIL); if (rc == PSCI_E_SUCCESS) diff --git a/services/std_svc/psci/psci_private.h b/services/std_svc/psci/psci_private.h index 5345ee30fd..9b55d9f395 100644 --- a/services/std_svc/psci/psci_private.h +++ b/services/std_svc/psci/psci_private.h @@ -149,7 +149,7 @@ typedef struct non_cpu_pwr_domain_node { } non_cpu_pd_node_t; typedef struct cpu_pwr_domain_node { - unsigned long mpidr; + u_register_t mpidr; /* * Index of the parent power domain node. @@ -172,7 +172,7 @@ typedef struct cpu_pwr_domain_node { extern const plat_psci_ops_t *psci_plat_pm_ops; extern non_cpu_pd_node_t psci_non_cpu_pd_nodes[PSCI_NUM_NON_CPU_PWR_DOMAINS]; extern cpu_pd_node_t psci_cpu_pd_nodes[PLATFORM_CORE_COUNT]; -extern uint32_t psci_caps; +extern unsigned int psci_caps; /******************************************************************************* * SPD's power management hooks registered with PSCI @@ -186,43 +186,43 @@ extern const spd_pm_ops_t *psci_spd_pm; int psci_validate_power_state(unsigned int power_state, psci_power_state_t *state_info); void psci_query_sys_suspend_pwrstate(psci_power_state_t *state_info); -int psci_validate_mpidr(unsigned long mpidr); +int psci_validate_mpidr(u_register_t mpidr); void psci_init_req_local_pwr_states(void); void psci_power_up_finish(void); int psci_validate_entry_point(entry_point_info_t *ep, - uint64_t entrypoint, uint64_t context_id); + uintptr_t entrypoint, u_register_t context_id); void psci_get_parent_pwr_domain_nodes(unsigned int cpu_idx, - int end_lvl, + unsigned int end_lvl, unsigned int node_index[]); -void psci_do_state_coordination(int end_pwrlvl, +void psci_do_state_coordination(unsigned int end_pwrlvl, psci_power_state_t *state_info); -void psci_acquire_pwr_domain_locks(int end_pwrlvl, +void psci_acquire_pwr_domain_locks(unsigned int end_pwrlvl, unsigned int cpu_idx); -void psci_release_pwr_domain_locks(int end_pwrlvl, +void psci_release_pwr_domain_locks(unsigned int end_pwrlvl, unsigned int cpu_idx); int psci_validate_suspend_req(const psci_power_state_t *state_info, unsigned int is_power_down_state_req); unsigned int psci_find_max_off_lvl(const psci_power_state_t *state_info); unsigned int psci_find_target_suspend_lvl(const psci_power_state_t *state_info); -void psci_set_pwr_domains_to_run(uint32_t end_pwrlvl); +void psci_set_pwr_domains_to_run(unsigned int end_pwrlvl); void psci_print_power_domain_map(void); unsigned int psci_is_last_on_cpu(void); -int psci_spd_migrate_info(uint64_t *mpidr); +int psci_spd_migrate_info(u_register_t *mpidr); /* Private exported functions from psci_on.c */ int psci_cpu_on_start(unsigned long target_cpu, entry_point_info_t *ep, - int end_pwrlvl); + unsigned int end_pwrlvl); void psci_cpu_on_finish(unsigned int cpu_idx, psci_power_state_t *state_info); /* Private exported functions from psci_cpu_off.c */ -int psci_do_cpu_off(int end_pwrlvl); +int psci_do_cpu_off(unsigned int end_pwrlvl); /* Private exported functions from psci_pwrlvl_suspend.c */ void psci_cpu_suspend_start(entry_point_info_t *ep, - int end_pwrlvl, + unsigned int end_pwrlvl, psci_power_state_t *state_info, unsigned int is_power_down_state_req); @@ -230,7 +230,7 @@ void psci_cpu_suspend_finish(unsigned int cpu_idx, psci_power_state_t *state_info); /* Private exported functions from psci_helpers.S */ -void psci_do_pwrdown_cache_maintenance(uint32_t pwr_level); +void psci_do_pwrdown_cache_maintenance(unsigned int pwr_level); void psci_do_pwrup_cache_maintenance(void); /* Private exported functions from psci_system_off.c */ diff --git a/services/std_svc/psci/psci_setup.c b/services/std_svc/psci/psci_setup.c index ce4da9599f..94fe630c99 100644 --- a/services/std_svc/psci/psci_setup.c +++ b/services/std_svc/psci/psci_setup.c @@ -49,13 +49,15 @@ static cpu_context_t psci_ns_context[PLATFORM_CORE_COUNT]; /****************************************************************************** * Define the psci capability variable. *****************************************************************************/ -uint32_t psci_caps; +unsigned int psci_caps; /******************************************************************************* * Function which initializes the 'psci_non_cpu_pd_nodes' or the * 'psci_cpu_pd_nodes' corresponding to the power level. ******************************************************************************/ -static void psci_init_pwr_domain_node(int node_idx, int parent_idx, int level) +static void psci_init_pwr_domain_node(unsigned int node_idx, + unsigned int parent_idx, + unsigned int level) { if (level > PSCI_CPU_PWR_LVL) { psci_non_cpu_pd_nodes[node_idx].level = level; @@ -78,12 +80,12 @@ static void psci_init_pwr_domain_node(int node_idx, int parent_idx, int level) svc_cpu_data->aff_info_state = AFF_STATE_OFF; /* Invalidate the suspend level for the cpu */ - svc_cpu_data->target_pwrlvl = PSCI_INVALID_DATA; + svc_cpu_data->target_pwrlvl = PSCI_INVALID_PWR_LVL; /* Set the power state to OFF state */ svc_cpu_data->local_state = PLAT_MAX_OFF_STATE; - flush_dcache_range((uint64_t)svc_cpu_data, + flush_dcache_range((uintptr_t)svc_cpu_data, sizeof(*svc_cpu_data)); cm_set_context_by_index(node_idx, @@ -103,9 +105,9 @@ static void psci_init_pwr_domain_node(int node_idx, int parent_idx, int level) *******************************************************************************/ static void psci_update_pwrlvl_limits(void) { - int cpu_idx, j; + int j; unsigned int nodes_idx[PLAT_MAX_PWR_LVL] = {0}; - unsigned int temp_index[PLAT_MAX_PWR_LVL]; + unsigned int temp_index[PLAT_MAX_PWR_LVL], cpu_idx; for (cpu_idx = 0; cpu_idx < PLATFORM_CORE_COUNT; cpu_idx++) { psci_get_parent_pwr_domain_nodes(cpu_idx, @@ -182,7 +184,7 @@ static void populate_power_domain_tree(const unsigned char *topology) #if !USE_COHERENT_MEM /* Flush the non CPU power domain data to memory */ - flush_dcache_range((uint64_t) &psci_non_cpu_pd_nodes, + flush_dcache_range((uintptr_t) &psci_non_cpu_pd_nodes, sizeof(psci_non_cpu_pd_nodes)); #endif } @@ -208,7 +210,7 @@ static void populate_power_domain_tree(const unsigned char *topology) * | CPU 0 | CPU 1 | CPU 2 | CPU 3 | * ------------------------------------------------ ******************************************************************************/ -int32_t psci_setup(void) +int psci_setup(void) { const unsigned char *topology_tree; @@ -230,11 +232,11 @@ int32_t psci_setup(void) * The psci_non_cpu_pd_nodes only needs flushing when it's not allocated in * coherent memory. */ - flush_dcache_range((uint64_t) &psci_non_cpu_pd_nodes, + flush_dcache_range((uintptr_t) &psci_non_cpu_pd_nodes, sizeof(psci_non_cpu_pd_nodes)); #endif - flush_dcache_range((uint64_t) &psci_cpu_pd_nodes, + flush_dcache_range((uintptr_t) &psci_cpu_pd_nodes, sizeof(psci_cpu_pd_nodes)); psci_init_req_local_pwr_states(); diff --git a/services/std_svc/psci/psci_suspend.c b/services/std_svc/psci/psci_suspend.c index 71e477842e..a158e36f6c 100644 --- a/services/std_svc/psci/psci_suspend.c +++ b/services/std_svc/psci/psci_suspend.c @@ -72,7 +72,7 @@ static void psci_suspend_to_standby_finisher(unsigned int cpu_idx, * This function does generic and platform specific suspend to power down * operations. ******************************************************************************/ -static void psci_suspend_to_pwrdown_start(int end_pwrlvl, +static void psci_suspend_to_pwrdown_start(unsigned int end_pwrlvl, entry_point_info_t *ep, psci_power_state_t *state_info) { @@ -127,7 +127,7 @@ static void psci_suspend_to_pwrdown_start(int end_pwrlvl, * not possible to undo any of the actions taken beyond that point. ******************************************************************************/ void psci_cpu_suspend_start(entry_point_info_t *ep, - int end_pwrlvl, + unsigned int end_pwrlvl, psci_power_state_t *state_info, unsigned int is_power_down_state) { @@ -212,8 +212,8 @@ exit: void psci_cpu_suspend_finish(unsigned int cpu_idx, psci_power_state_t *state_info) { - int32_t suspend_level; - uint64_t counter_freq; + unsigned long long counter_freq; + unsigned int suspend_level; /* Ensure we have been woken up from a suspended state */ assert(psci_get_aff_info_state() == AFF_STATE_ON && is_local_state_off(\ @@ -246,12 +246,12 @@ void psci_cpu_suspend_finish(unsigned int cpu_idx, */ if (psci_spd_pm && psci_spd_pm->svc_suspend) { suspend_level = psci_get_suspend_pwrlvl(); - assert (suspend_level != PSCI_INVALID_DATA); + assert (suspend_level != PSCI_INVALID_PWR_LVL); psci_spd_pm->svc_suspend_finish(suspend_level); } /* Invalidate the suspend level for the cpu */ - psci_set_suspend_pwrlvl(PSCI_INVALID_DATA); + psci_set_suspend_pwrlvl(PSCI_INVALID_PWR_LVL); /* * Generic management: Now we just need to retrieve the