diff options
-rw-r--r-- | Makefile | 5 | ||||
-rw-r--r-- | docs/porting-guide.md | 50 | ||||
-rw-r--r-- | docs/user-guide.md | 5 | ||||
-rw-r--r-- | include/plat/common/platform.h | 7 | ||||
-rw-r--r-- | lib/psci/psci_common.c | 12 | ||||
-rw-r--r-- | lib/psci/psci_main.c | 17 | ||||
-rw-r--r-- | lib/psci/psci_off.c | 10 | ||||
-rw-r--r-- | lib/psci/psci_private.h | 15 | ||||
-rw-r--r-- | lib/psci/psci_stat.c | 65 | ||||
-rw-r--r-- | lib/psci/psci_suspend.c | 10 | ||||
-rw-r--r-- | plat/arm/common/arm_common.mk | 3 | ||||
-rw-r--r-- | plat/common/plat_psci_common.c | 119 |
12 files changed, 198 insertions, 120 deletions
@@ -346,11 +346,6 @@ ifneq (${GENERATE_COT},0) endif endif -# Make sure PMF is enabled if PSCI STAT is enabled. -ifeq (${ENABLE_PSCI_STAT},1) -ENABLE_PMF := 1 -endif - ifneq (${FIP_ALIGN},0) FIP_ARGS += --align ${FIP_ALIGN} endif diff --git a/docs/porting-guide.md b/docs/porting-guide.md index e8486f12..a5e59667 100644 --- a/docs/porting-guide.md +++ b/docs/porting-guide.md @@ -1707,10 +1707,56 @@ 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. +The following functions form part of platform port of PSCI functionality. +### Function : plat_psci_stat_accounting_start() [optional] + + Argument : const psci_power_state_t * + Return : void + +This is an optional hook that platforms can implement for residency statistics +accounting before entering a low power state. The `pwr_domain_state` field of +`state_info` (first argument) can be inspected if stat accounting is done +differently at CPU level versus higher levels. As an example, if the element at +index 0 (CPU power level) in the `pwr_domain_state` array indicates a power down +state, special hardware logic may be programmed in order to keep track of the +residency statistics. For higher levels (array indices > 0), the residency +statistics could be tracked in software using PMF. If `ENABLE_PMF` is set, the +default implementation will use PMF to capture timestamps. + +### Function : plat_psci_stat_accounting_stop() [optional] + + Argument : const psci_power_state_t * + Return : void + +This is an optional hook that platforms can implement for residency statistics +accounting after exiting from a low power state. The `pwr_domain_state` field +of `state_info` (first argument) can be inspected if stat accounting is done +differently at CPU level versus higher levels. As an example, if the element at +index 0 (CPU power level) in the `pwr_domain_state` array indicates a power down +state, special hardware logic may be programmed in order to keep track of the +residency statistics. For higher levels (array indices > 0), the residency +statistics could be tracked in software using PMF. If `ENABLE_PMF` is set, the +default implementation will use PMF to capture timestamps. + +### Function : plat_psci_stat_get_residency() [optional] + + Argument : unsigned int, const psci_power_state_t *, int + Return : u_register_t + +This is an optional interface that is is invoked after resuming from a low power +state and provides the time spent resident in that low power state by the power +domain at a particular power domain level. When a CPU wakes up from suspend, +all its parent power domain levels are also woken up. The generic PSCI code +invokes this function for each parent power domain that is resumed and it +identified by the `lvl` (first argument) parameter. The `state_info` (second +argument) describes the low power state that the power domain has resumed from. +The current CPU is the first CPU in the power domain to resume from the low +power state and the `last_cpu_idx` (third parameter) is the index of the last +CPU in the power domain to suspend and may be needed to calculate the residency +for that power domain. + ### Function : plat_get_target_pwr_state() [optional] Argument : unsigned int, const plat_local_state_t *, unsigned int diff --git a/docs/user-guide.md b/docs/user-guide.md index ebdb5a25..265061c4 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -274,8 +274,9 @@ performed. * `ENABLE_PSCI_STAT`: Boolean option to enable support for optional PSCI functions `PSCI_STAT_RESIDENCY` and `PSCI_STAT_COUNT`. Default is 0. - Enabling this option enables the `ENABLE_PMF` build option as well. - The PMF is used for collecting the statistics. + In the absence of an alternate stat collection backend, `ENABLE_PMF` must + be enabled. If `ENABLE_PMF` is set, the residency statistics are tracked in + software. * `ENABLE_RUNTIME_INSTRUMENTATION`: Boolean option to enable runtime instrumentation which injects timestamp collection points into diff --git a/include/plat/common/platform.h b/include/plat/common/platform.h index f904292b..73bb6431 100644 --- a/include/plat/common/platform.h +++ b/include/plat/common/platform.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2017, 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: @@ -254,6 +254,11 @@ const unsigned char *plat_get_power_domain_tree_desc(void); /******************************************************************************* * Optional PSCI functions (BL31). ******************************************************************************/ +void plat_psci_stat_accounting_start(const psci_power_state_t *state_info); +void plat_psci_stat_accounting_stop(const psci_power_state_t *state_info); +u_register_t plat_psci_stat_get_residency(unsigned int lvl, + const psci_power_state_t *state_info, + int last_cpu_index); plat_local_state_t plat_get_target_pwr_state(unsigned int lvl, const plat_local_state_t *states, unsigned int ncpu); diff --git a/lib/psci/psci_common.c b/lib/psci/psci_common.c index 68cdd6eb..822329e6 100644 --- a/lib/psci/psci_common.c +++ b/lib/psci/psci_common.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2017, 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: @@ -760,13 +760,7 @@ void psci_warmboot_entrypoint(void) cpu_idx); #if ENABLE_PSCI_STAT - /* - * Capture power up time-stamp. - * No cache maintenance is required as caches are off - * and writes are direct to the main memory. - */ - PMF_CAPTURE_TIMESTAMP(psci_svc, PSCI_STAT_ID_EXIT_LOW_PWR, - PMF_NO_CACHE_MAINT); + plat_psci_stat_accounting_stop(&state_info); #endif psci_get_target_local_pwr_states(end_pwrlvl, &state_info); @@ -801,7 +795,7 @@ void psci_warmboot_entrypoint(void) * Since caches are now enabled, it's necessary to do cache * maintenance before reading that same data. */ - psci_stats_update_pwr_up(end_pwrlvl, &state_info, PMF_CACHE_MAINT); + psci_stats_update_pwr_up(end_pwrlvl, &state_info); #endif /* diff --git a/lib/psci/psci_main.c b/lib/psci/psci_main.c index 0a3a60ac..5e166b52 100644 --- a/lib/psci/psci_main.c +++ b/lib/psci/psci_main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2017, 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: @@ -117,13 +117,7 @@ int psci_cpu_suspend(unsigned int power_state, psci_set_cpu_local_state(cpu_pd_state); #if ENABLE_PSCI_STAT - /* - * Capture time-stamp before CPU standby - * No cache maintenance is needed as caches - * are ON through out the CPU standby operation. - */ - PMF_CAPTURE_TIMESTAMP(psci_svc, PSCI_STAT_ID_ENTER_LOW_PWR, - PMF_NO_CACHE_MAINT); + plat_psci_stat_accounting_start(&state_info); #endif #if ENABLE_RUNTIME_INSTRUMENTATION @@ -144,13 +138,10 @@ int psci_cpu_suspend(unsigned int power_state, #endif #if ENABLE_PSCI_STAT - /* Capture time-stamp after CPU standby */ - PMF_CAPTURE_TIMESTAMP(psci_svc, PSCI_STAT_ID_EXIT_LOW_PWR, - PMF_NO_CACHE_MAINT); + plat_psci_stat_accounting_stop(&state_info); /* Update PSCI stats */ - psci_stats_update_pwr_up(PSCI_CPU_PWR_LVL, &state_info, - PMF_NO_CACHE_MAINT); + psci_stats_update_pwr_up(PSCI_CPU_PWR_LVL, &state_info); #endif return PSCI_E_SUCCESS; diff --git a/lib/psci/psci_off.c b/lib/psci/psci_off.c index 897bf319..394aaa3b 100644 --- a/lib/psci/psci_off.c +++ b/lib/psci/psci_off.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2017, 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: @@ -137,13 +137,7 @@ int psci_do_cpu_off(unsigned int end_pwrlvl) psci_plat_pm_ops->pwr_domain_off(&state_info); #if ENABLE_PSCI_STAT - /* - * Capture time-stamp while entering low power state. - * No cache maintenance needed because caches are off - * and writes are direct to main memory. - */ - PMF_CAPTURE_TIMESTAMP(psci_svc, PSCI_STAT_ID_ENTER_LOW_PWR, - PMF_NO_CACHE_MAINT); + plat_psci_stat_accounting_start(&state_info); #endif exit: diff --git a/lib/psci/psci_private.h b/lib/psci/psci_private.h index 781b3b52..ca8291e4 100644 --- a/lib/psci/psci_private.h +++ b/lib/psci/psci_private.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2017, 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,7 +35,6 @@ #include <bakery_lock.h> #include <bl_common.h> #include <cpu_data.h> -#include <pmf.h> #include <psci.h> #include <spinlock.h> @@ -106,15 +105,6 @@ #define is_cpu_standby_req(is_power_down_state, retn_lvl) \ (((!(is_power_down_state)) && ((retn_lvl) == 0)) ? 1 : 0) -/* Following are used as ID's to capture time-stamp */ -#define PSCI_STAT_ID_ENTER_LOW_PWR 0 -#define PSCI_STAT_ID_EXIT_LOW_PWR 1 -#define PSCI_STAT_TOTAL_IDS 2 - -/* Declare PMF service functions for PSCI */ -PMF_DECLARE_CAPTURE_TIMESTAMP(psci_svc) -PMF_DECLARE_GET_TIMESTAMP(psci_svc) - /******************************************************************************* * 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 @@ -246,8 +236,7 @@ void __dead2 psci_system_reset(void); void psci_stats_update_pwr_down(unsigned int end_pwrlvl, const psci_power_state_t *state_info); void psci_stats_update_pwr_up(unsigned int end_pwrlvl, - const psci_power_state_t *state_info, - unsigned int flags); + const psci_power_state_t *state_info); u_register_t psci_stat_residency(u_register_t target_cpu, unsigned int power_state); u_register_t psci_stat_count(u_register_t target_cpu, diff --git a/lib/psci/psci_stat.c b/lib/psci/psci_stat.c index ecbe592b..d8034a5d 100644 --- a/lib/psci/psci_stat.c +++ b/lib/psci/psci_stat.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2017, 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,9 +38,6 @@ #define PLAT_MAX_PWR_LVL_STATES 2 #endif -/* Ticks elapsed in one second by a signal of 1 MHz */ -#define MHZ_TICKS_PER_SEC 1000000 - /* Following structure is used for PSCI STAT */ typedef struct psci_stat { u_register_t residency; @@ -62,27 +59,6 @@ static psci_stat_t psci_cpu_stat[PLATFORM_CORE_COUNT] static psci_stat_t psci_non_cpu_stat[PSCI_NUM_NON_CPU_PWR_DOMAINS] [PLAT_MAX_PWR_LVL_STATES]; -/* Register PMF PSCI service */ -PMF_REGISTER_SERVICE(psci_svc, PMF_PSCI_STAT_SVC_ID, - PSCI_STAT_TOTAL_IDS, PMF_STORE_ENABLE) - -/* The divisor to use to convert raw timestamp into microseconds */ -u_register_t residency_div; - -/* - * This macro calculates the stats residency in microseconds, - * taking in account the wrap around condition. - */ -#define calc_stat_residency(_pwrupts, _pwrdnts, _res) \ - do { \ - if (_pwrupts < _pwrdnts) \ - _res = UINT64_MAX - _pwrdnts + _pwrupts;\ - else \ - _res = _pwrupts - _pwrdnts; \ - /* Convert timestamp into microseconds */ \ - _res = _res/residency_div; \ - } while (0) - /* * This functions returns the index into the `psci_stat_t` array given the * local power state and power domain level. If the platform implements the @@ -150,44 +126,23 @@ void psci_stats_update_pwr_down(unsigned int end_pwrlvl, * It is called with caches enabled and locks acquired(for NON-CPU domain) ******************************************************************************/ void psci_stats_update_pwr_up(unsigned int end_pwrlvl, - const psci_power_state_t *state_info, - unsigned int flags) + const psci_power_state_t *state_info) { int parent_idx, cpu_idx = plat_my_core_pos(); int lvl, stat_idx; plat_local_state_t local_state; - unsigned long long pwrup_ts = 0, pwrdn_ts = 0; u_register_t residency; assert(end_pwrlvl <= PLAT_MAX_PWR_LVL); assert(state_info); - /* Initialize the residency divisor if not already initialized */ - if (!residency_div) { - /* Pre-calculate divisor so that it can be directly used to - convert time-stamp into microseconds */ - residency_div = read_cntfrq_el0() / MHZ_TICKS_PER_SEC; - assert(residency_div); - } - - /* Get power down time-stamp for current CPU */ - PMF_GET_TIMESTAMP_BY_INDEX(psci_svc, PSCI_STAT_ID_ENTER_LOW_PWR, - cpu_idx, flags, pwrdn_ts); - - /* In the case of 1st power on just return */ - if (!pwrdn_ts) - return; - - /* Get power up time-stamp for current CPU */ - PMF_GET_TIMESTAMP_BY_INDEX(psci_svc, PSCI_STAT_ID_EXIT_LOW_PWR, - cpu_idx, flags, pwrup_ts); - /* Get the index into the stats array */ local_state = state_info->pwr_domain_state[PSCI_CPU_PWR_LVL]; stat_idx = get_stat_idx(local_state, PSCI_CPU_PWR_LVL); - /* Calculate stats residency */ - calc_stat_residency(pwrup_ts, pwrdn_ts, residency); + /* Call into platform interface to calculate residency. */ + residency = plat_psci_stat_get_residency(PSCI_CPU_PWR_LVL, + state_info, cpu_idx); /* Update CPU stats. */ psci_cpu_stat[cpu_idx][stat_idx].residency += residency; @@ -207,10 +162,9 @@ void psci_stats_update_pwr_up(unsigned int end_pwrlvl, assert(last_cpu_in_non_cpu_pd[parent_idx] != -1); - /* Get power down time-stamp for last CPU */ - PMF_GET_TIMESTAMP_BY_INDEX(psci_svc, PSCI_STAT_ID_ENTER_LOW_PWR, - last_cpu_in_non_cpu_pd[parent_idx], - flags, pwrdn_ts); + /* Call into platform interface to calculate residency. */ + residency = plat_psci_stat_get_residency(lvl, state_info, + last_cpu_in_non_cpu_pd[parent_idx]); /* Initialize back to reset value */ last_cpu_in_non_cpu_pd[parent_idx] = -1; @@ -218,9 +172,6 @@ void psci_stats_update_pwr_up(unsigned int end_pwrlvl, /* Get the index into the stats array */ stat_idx = get_stat_idx(local_state, lvl); - /* Calculate stats residency */ - calc_stat_residency(pwrup_ts, pwrdn_ts, residency); - /* Update non cpu stats */ psci_non_cpu_stat[parent_idx][stat_idx].residency += residency; psci_non_cpu_stat[parent_idx][stat_idx].count++; diff --git a/lib/psci/psci_suspend.c b/lib/psci/psci_suspend.c index dc2ab774..5c8c3e26 100644 --- a/lib/psci/psci_suspend.c +++ b/lib/psci/psci_suspend.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2017, 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: @@ -211,13 +211,7 @@ void psci_cpu_suspend_start(entry_point_info_t *ep, psci_plat_pm_ops->pwr_domain_suspend(state_info); #if ENABLE_PSCI_STAT - /* - * Capture time-stamp while entering low power state. - * No cache maintenance needed because caches are off - * and writes are direct to main memory. - */ - PMF_CAPTURE_TIMESTAMP(psci_svc, PSCI_STAT_ID_ENTER_LOW_PWR, - PMF_NO_CACHE_MAINT); + plat_psci_stat_accounting_start(state_info); #endif exit: diff --git a/plat/arm/common/arm_common.mk b/plat/arm/common/arm_common.mk index c2f28f98..4628a43d 100644 --- a/plat/arm/common/arm_common.mk +++ b/plat/arm/common/arm_common.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2015-2016, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2015-2017, 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,6 +92,7 @@ $(eval $(call add_define,ARM_BL31_IN_DRAM)) # Enable PSCI_STAT_COUNT/RESIDENCY APIs on ARM platforms ENABLE_PSCI_STAT := 1 +ENABLE_PMF := 1 # On ARM platforms, separate the code and read-only data sections to allow # mapping the former as executable and the latter as execute-never. diff --git a/plat/common/plat_psci_common.c b/plat/common/plat_psci_common.c index 3eb6886e..0e00faaf 100644 --- a/plat/common/plat_psci_common.c +++ b/plat/common/plat_psci_common.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2017, 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,8 +31,125 @@ #include <arch.h> #include <assert.h> #include <platform.h> +#include <pmf.h> #include <psci.h> +#if ENABLE_PSCI_STAT && ENABLE_PMF +#pragma weak plat_psci_stat_accounting_start +#pragma weak plat_psci_stat_accounting_stop +#pragma weak plat_psci_stat_get_residency + +/* Ticks elapsed in one second by a signal of 1 MHz */ +#define MHZ_TICKS_PER_SEC 1000000 + +/* Following are used as ID's to capture time-stamp */ +#define PSCI_STAT_ID_ENTER_LOW_PWR 0 +#define PSCI_STAT_ID_EXIT_LOW_PWR 1 +#define PSCI_STAT_TOTAL_IDS 2 + +PMF_REGISTER_SERVICE(psci_svc, PMF_PSCI_STAT_SVC_ID, PSCI_STAT_TOTAL_IDS, + PMF_STORE_ENABLE) + +/* + * This function calculates the stats residency in microseconds, + * taking in account the wrap around condition. + */ +static u_register_t calc_stat_residency(unsigned long long pwrupts, + unsigned long long pwrdnts) +{ + /* The divisor to use to convert raw timestamp into microseconds. */ + u_register_t residency_div; + u_register_t res; + + /* + * Calculate divisor so that it can be directly used to + * convert time-stamp into microseconds. + */ + residency_div = read_cntfrq_el0() / MHZ_TICKS_PER_SEC; + assert(residency_div); + + if (pwrupts < pwrdnts) + res = UINT64_MAX - pwrdnts + pwrupts; + else + res = pwrupts - pwrdnts; + + return res / residency_div; +} + +/* + * Capture timestamp before entering a low power state. + * No cache maintenance is required when capturing the timestamp. + * Cache maintenance may be needed when reading these timestamps. + */ +void plat_psci_stat_accounting_start( + __unused const psci_power_state_t *state_info) +{ + assert(state_info); + PMF_CAPTURE_TIMESTAMP(psci_svc, PSCI_STAT_ID_ENTER_LOW_PWR, + PMF_NO_CACHE_MAINT); +} + +/* + * Capture timestamp after exiting a low power state. + * No cache maintenance is required when capturing the timestamp. + * Cache maintenance may be needed when reading these timestamps. + */ +void plat_psci_stat_accounting_stop( + __unused const psci_power_state_t *state_info) +{ + assert(state_info); + PMF_CAPTURE_TIMESTAMP(psci_svc, PSCI_STAT_ID_EXIT_LOW_PWR, + PMF_NO_CACHE_MAINT); +} + +/* + * Calculate the residency for the given level and power state + * information. + */ +u_register_t plat_psci_stat_get_residency(unsigned int lvl, + const psci_power_state_t *state_info, + int last_cpu_idx) +{ + plat_local_state_t state; + unsigned long long pwrup_ts = 0, pwrdn_ts = 0; + unsigned int pmf_flags; + + assert(lvl >= PSCI_CPU_PWR_LVL && lvl <= PLAT_MAX_PWR_LVL); + assert(state_info); + assert(last_cpu_idx >= 0 && last_cpu_idx <= PLATFORM_CORE_COUNT); + + if (lvl == PSCI_CPU_PWR_LVL) + assert(last_cpu_idx == plat_my_core_pos()); + + /* + * If power down is requested, then timestamp capture will + * be with caches OFF. Hence we have to do cache maintenance + * when reading the timestamp. + */ + state = state_info->pwr_domain_state[PSCI_CPU_PWR_LVL]; + if (is_local_state_off(state)) { + pmf_flags = PMF_CACHE_MAINT; + } else { + assert(is_local_state_retn(state)); + pmf_flags = PMF_NO_CACHE_MAINT; + } + + PMF_GET_TIMESTAMP_BY_INDEX(psci_svc, + PSCI_STAT_ID_ENTER_LOW_PWR, + last_cpu_idx, + pmf_flags, + pwrdn_ts); + + PMF_GET_TIMESTAMP_BY_INDEX(psci_svc, + PSCI_STAT_ID_EXIT_LOW_PWR, + plat_my_core_pos(), + pmf_flags, + pwrup_ts); + + return calc_stat_residency(pwrup_ts, pwrdn_ts); +} +#endif /* ENABLE_PSCI_STAT && ENABLE_PMF */ + /* * The PSCI generic code uses this API to let the platform participate in state * coordination during a power management operation. It compares the platform |