diff options
Diffstat (limited to 'plat/common/plat_psci_common.c')
-rw-r--r-- | plat/common/plat_psci_common.c | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/plat/common/plat_psci_common.c b/plat/common/plat_psci_common.c new file mode 100644 index 0000000..c32e59f --- /dev/null +++ b/plat/common/plat_psci_common.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2016-2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020, NVIDIA Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> + +#include <arch.h> +#include <lib/pmf/pmf.h> +#include <lib/psci/psci.h> +#include <lib/utils_def.h> +#include <plat/common/platform.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 + +/* Maximum time-stamp value read from architectural counters */ +#ifdef __aarch64__ +#define MAX_TS UINT64_MAX +#else +#define MAX_TS UINT32_MAX +#endif + +/* 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_DECLARE_CAPTURE_TIMESTAMP(psci_svc) +PMF_DECLARE_GET_TIMESTAMP(psci_svc) +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 > 0U); + + if (pwrupts < pwrdnts) + res = MAX_TS - pwrdnts + pwrupts; + else + res = pwrupts - pwrdnts; + + return res / residency_div; +} + +/* + * Capture timestamp before entering a low power state. + * 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 != NULL); + PMF_CAPTURE_TIMESTAMP(psci_svc, PSCI_STAT_ID_ENTER_LOW_PWR, + PMF_CACHE_MAINT); +} + +/* + * Capture timestamp after exiting a low power state. + * 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 != NULL); + PMF_CAPTURE_TIMESTAMP(psci_svc, PSCI_STAT_ID_EXIT_LOW_PWR, + PMF_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, + unsigned 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 != NULL); + assert(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) != 0) { + pmf_flags = PMF_CACHE_MAINT; + } else { + assert(is_local_state_retn(state) == 1); + 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 + * 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; + const plat_local_state_t *st = states; + unsigned int n = ncpu; + + assert(ncpu > 0U); + + do { + temp = *st; + st++; + if (temp < target) + target = temp; + n--; + } while (n > 0U); + + return target; +} |