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