1*91f16700Schasinglulu /* 2*91f16700Schasinglulu * Copyright (c) 2016-2020, ARM Limited and Contributors. All rights reserved. 3*91f16700Schasinglulu * Copyright (c) 2020, NVIDIA Corporation. All rights reserved. 4*91f16700Schasinglulu * 5*91f16700Schasinglulu * SPDX-License-Identifier: BSD-3-Clause 6*91f16700Schasinglulu */ 7*91f16700Schasinglulu 8*91f16700Schasinglulu #include <assert.h> 9*91f16700Schasinglulu 10*91f16700Schasinglulu #include <arch.h> 11*91f16700Schasinglulu #include <lib/pmf/pmf.h> 12*91f16700Schasinglulu #include <lib/psci/psci.h> 13*91f16700Schasinglulu #include <lib/utils_def.h> 14*91f16700Schasinglulu #include <plat/common/platform.h> 15*91f16700Schasinglulu 16*91f16700Schasinglulu #if ENABLE_PSCI_STAT && ENABLE_PMF 17*91f16700Schasinglulu #pragma weak plat_psci_stat_accounting_start 18*91f16700Schasinglulu #pragma weak plat_psci_stat_accounting_stop 19*91f16700Schasinglulu #pragma weak plat_psci_stat_get_residency 20*91f16700Schasinglulu 21*91f16700Schasinglulu /* Maximum time-stamp value read from architectural counters */ 22*91f16700Schasinglulu #ifdef __aarch64__ 23*91f16700Schasinglulu #define MAX_TS UINT64_MAX 24*91f16700Schasinglulu #else 25*91f16700Schasinglulu #define MAX_TS UINT32_MAX 26*91f16700Schasinglulu #endif 27*91f16700Schasinglulu 28*91f16700Schasinglulu /* Following are used as ID's to capture time-stamp */ 29*91f16700Schasinglulu #define PSCI_STAT_ID_ENTER_LOW_PWR 0 30*91f16700Schasinglulu #define PSCI_STAT_ID_EXIT_LOW_PWR 1 31*91f16700Schasinglulu #define PSCI_STAT_TOTAL_IDS 2 32*91f16700Schasinglulu 33*91f16700Schasinglulu PMF_DECLARE_CAPTURE_TIMESTAMP(psci_svc) 34*91f16700Schasinglulu PMF_DECLARE_GET_TIMESTAMP(psci_svc) 35*91f16700Schasinglulu PMF_REGISTER_SERVICE(psci_svc, PMF_PSCI_STAT_SVC_ID, PSCI_STAT_TOTAL_IDS, 36*91f16700Schasinglulu PMF_STORE_ENABLE) 37*91f16700Schasinglulu 38*91f16700Schasinglulu /* 39*91f16700Schasinglulu * This function calculates the stats residency in microseconds, 40*91f16700Schasinglulu * taking in account the wrap around condition. 41*91f16700Schasinglulu */ 42*91f16700Schasinglulu static u_register_t calc_stat_residency(unsigned long long pwrupts, 43*91f16700Schasinglulu unsigned long long pwrdnts) 44*91f16700Schasinglulu { 45*91f16700Schasinglulu /* The divisor to use to convert raw timestamp into microseconds. */ 46*91f16700Schasinglulu u_register_t residency_div; 47*91f16700Schasinglulu u_register_t res; 48*91f16700Schasinglulu 49*91f16700Schasinglulu /* 50*91f16700Schasinglulu * Calculate divisor so that it can be directly used to 51*91f16700Schasinglulu * convert time-stamp into microseconds. 52*91f16700Schasinglulu */ 53*91f16700Schasinglulu residency_div = read_cntfrq_el0() / MHZ_TICKS_PER_SEC; 54*91f16700Schasinglulu assert(residency_div > 0U); 55*91f16700Schasinglulu 56*91f16700Schasinglulu if (pwrupts < pwrdnts) 57*91f16700Schasinglulu res = MAX_TS - pwrdnts + pwrupts; 58*91f16700Schasinglulu else 59*91f16700Schasinglulu res = pwrupts - pwrdnts; 60*91f16700Schasinglulu 61*91f16700Schasinglulu return res / residency_div; 62*91f16700Schasinglulu } 63*91f16700Schasinglulu 64*91f16700Schasinglulu /* 65*91f16700Schasinglulu * Capture timestamp before entering a low power state. 66*91f16700Schasinglulu * Cache maintenance may be needed when reading these timestamps. 67*91f16700Schasinglulu */ 68*91f16700Schasinglulu void plat_psci_stat_accounting_start( 69*91f16700Schasinglulu __unused const psci_power_state_t *state_info) 70*91f16700Schasinglulu { 71*91f16700Schasinglulu assert(state_info != NULL); 72*91f16700Schasinglulu PMF_CAPTURE_TIMESTAMP(psci_svc, PSCI_STAT_ID_ENTER_LOW_PWR, 73*91f16700Schasinglulu PMF_CACHE_MAINT); 74*91f16700Schasinglulu } 75*91f16700Schasinglulu 76*91f16700Schasinglulu /* 77*91f16700Schasinglulu * Capture timestamp after exiting a low power state. 78*91f16700Schasinglulu * Cache maintenance may be needed when reading these timestamps. 79*91f16700Schasinglulu */ 80*91f16700Schasinglulu void plat_psci_stat_accounting_stop( 81*91f16700Schasinglulu __unused const psci_power_state_t *state_info) 82*91f16700Schasinglulu { 83*91f16700Schasinglulu assert(state_info != NULL); 84*91f16700Schasinglulu PMF_CAPTURE_TIMESTAMP(psci_svc, PSCI_STAT_ID_EXIT_LOW_PWR, 85*91f16700Schasinglulu PMF_CACHE_MAINT); 86*91f16700Schasinglulu } 87*91f16700Schasinglulu 88*91f16700Schasinglulu /* 89*91f16700Schasinglulu * Calculate the residency for the given level and power state 90*91f16700Schasinglulu * information. 91*91f16700Schasinglulu */ 92*91f16700Schasinglulu u_register_t plat_psci_stat_get_residency(unsigned int lvl, 93*91f16700Schasinglulu const psci_power_state_t *state_info, 94*91f16700Schasinglulu unsigned int last_cpu_idx) 95*91f16700Schasinglulu { 96*91f16700Schasinglulu plat_local_state_t state; 97*91f16700Schasinglulu unsigned long long pwrup_ts = 0, pwrdn_ts = 0; 98*91f16700Schasinglulu unsigned int pmf_flags; 99*91f16700Schasinglulu 100*91f16700Schasinglulu assert((lvl >= PSCI_CPU_PWR_LVL) && (lvl <= PLAT_MAX_PWR_LVL)); 101*91f16700Schasinglulu assert(state_info != NULL); 102*91f16700Schasinglulu assert(last_cpu_idx <= PLATFORM_CORE_COUNT); 103*91f16700Schasinglulu 104*91f16700Schasinglulu if (lvl == PSCI_CPU_PWR_LVL) 105*91f16700Schasinglulu assert(last_cpu_idx == plat_my_core_pos()); 106*91f16700Schasinglulu 107*91f16700Schasinglulu /* 108*91f16700Schasinglulu * If power down is requested, then timestamp capture will 109*91f16700Schasinglulu * be with caches OFF. Hence we have to do cache maintenance 110*91f16700Schasinglulu * when reading the timestamp. 111*91f16700Schasinglulu */ 112*91f16700Schasinglulu state = state_info->pwr_domain_state[PSCI_CPU_PWR_LVL]; 113*91f16700Schasinglulu if (is_local_state_off(state) != 0) { 114*91f16700Schasinglulu pmf_flags = PMF_CACHE_MAINT; 115*91f16700Schasinglulu } else { 116*91f16700Schasinglulu assert(is_local_state_retn(state) == 1); 117*91f16700Schasinglulu pmf_flags = PMF_NO_CACHE_MAINT; 118*91f16700Schasinglulu } 119*91f16700Schasinglulu 120*91f16700Schasinglulu PMF_GET_TIMESTAMP_BY_INDEX(psci_svc, 121*91f16700Schasinglulu PSCI_STAT_ID_ENTER_LOW_PWR, 122*91f16700Schasinglulu last_cpu_idx, 123*91f16700Schasinglulu pmf_flags, 124*91f16700Schasinglulu pwrdn_ts); 125*91f16700Schasinglulu 126*91f16700Schasinglulu PMF_GET_TIMESTAMP_BY_INDEX(psci_svc, 127*91f16700Schasinglulu PSCI_STAT_ID_EXIT_LOW_PWR, 128*91f16700Schasinglulu plat_my_core_pos(), 129*91f16700Schasinglulu pmf_flags, 130*91f16700Schasinglulu pwrup_ts); 131*91f16700Schasinglulu 132*91f16700Schasinglulu return calc_stat_residency(pwrup_ts, pwrdn_ts); 133*91f16700Schasinglulu } 134*91f16700Schasinglulu #endif /* ENABLE_PSCI_STAT && ENABLE_PMF */ 135*91f16700Schasinglulu 136*91f16700Schasinglulu /* 137*91f16700Schasinglulu * The PSCI generic code uses this API to let the platform participate in state 138*91f16700Schasinglulu * coordination during a power management operation. It compares the platform 139*91f16700Schasinglulu * specific local power states requested by each cpu for a given power domain 140*91f16700Schasinglulu * and returns the coordinated target power state that the domain should 141*91f16700Schasinglulu * enter. A platform assigns a number to a local power state. This default 142*91f16700Schasinglulu * implementation assumes that the platform assigns these numbers in order of 143*91f16700Schasinglulu * increasing depth of the power state i.e. for two power states X & Y, if X < Y 144*91f16700Schasinglulu * then X represents a shallower power state than Y. As a result, the 145*91f16700Schasinglulu * coordinated target local power state for a power domain will be the minimum 146*91f16700Schasinglulu * of the requested local power states. 147*91f16700Schasinglulu */ 148*91f16700Schasinglulu plat_local_state_t plat_get_target_pwr_state(unsigned int lvl, 149*91f16700Schasinglulu const plat_local_state_t *states, 150*91f16700Schasinglulu unsigned int ncpu) 151*91f16700Schasinglulu { 152*91f16700Schasinglulu plat_local_state_t target = PLAT_MAX_OFF_STATE, temp; 153*91f16700Schasinglulu const plat_local_state_t *st = states; 154*91f16700Schasinglulu unsigned int n = ncpu; 155*91f16700Schasinglulu 156*91f16700Schasinglulu assert(ncpu > 0U); 157*91f16700Schasinglulu 158*91f16700Schasinglulu do { 159*91f16700Schasinglulu temp = *st; 160*91f16700Schasinglulu st++; 161*91f16700Schasinglulu if (temp < target) 162*91f16700Schasinglulu target = temp; 163*91f16700Schasinglulu n--; 164*91f16700Schasinglulu } while (n > 0U); 165*91f16700Schasinglulu 166*91f16700Schasinglulu return target; 167*91f16700Schasinglulu } 168