/* * Copyright (c) 2016-2018, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #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 1000000U /* 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_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. * 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 != NULL); 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 != NULL); 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 != NULL); assert(last_cpu_idx <= PLATFORM_CORE_COUNT); if (lvl == PSCI_CPU_PWR_LVL) assert((unsigned int)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; }