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 return res / residency_div; 68 } 69 70 /* 71 * Capture timestamp before entering a low power state. 72 * Cache maintenance may be needed when reading these timestamps. 73 */ 74 void plat_psci_stat_accounting_start( 75 __unused const psci_power_state_t *state_info) 76 { 77 assert(state_info != NULL); 78 PMF_CAPTURE_TIMESTAMP(psci_svc, PSCI_STAT_ID_ENTER_LOW_PWR, 79 CACHE_MAINTENANCE_ATTR); 80 } 81 82 /* 83 * Capture timestamp after exiting a low power state. 84 * Cache maintenance may be needed when reading these timestamps. 85 */ 86 void plat_psci_stat_accounting_stop( 87 __unused const psci_power_state_t *state_info) 88 { 89 assert(state_info != NULL); 90 PMF_CAPTURE_TIMESTAMP(psci_svc, PSCI_STAT_ID_EXIT_LOW_PWR, 91 CACHE_MAINTENANCE_ATTR); 92 } 93 94 /* 95 * Calculate the residency for the given level and power state 96 * information. 97 */ 98 u_register_t plat_psci_stat_get_residency(unsigned int lvl, 99 const psci_power_state_t *state_info, 100 unsigned int last_cpu_idx) 101 { 102 unsigned long long pwrup_ts = 0, pwrdn_ts = 0; 103 unsigned int pmf_flags; 104 105 assert((lvl >= PSCI_CPU_PWR_LVL) && (lvl <= PLAT_MAX_PWR_LVL)); 106 assert(last_cpu_idx <= PLATFORM_CORE_COUNT); 107 108 if (lvl == PSCI_CPU_PWR_LVL) 109 assert(last_cpu_idx == plat_my_core_pos()); 110 111 #if HW_ASSISTED_COHERENCY 112 /* HW coherency allows for the capture and access to happen with caches 113 * ON. So these timestamps don't need cache maintenance */ 114 pmf_flags = PMF_NO_CACHE_MAINT; 115 #else 116 /* 117 * If power down is requested, then timestamp capture will 118 * be with caches OFF. Hence we have to do cache maintenance 119 * when reading the timestamp. 120 */ 121 plat_local_state_t state; 122 assert(state_info != NULL); 123 state = state_info->pwr_domain_state[PSCI_CPU_PWR_LVL]; 124 if (is_local_state_off(state) != 0) { 125 pmf_flags = PMF_CACHE_MAINT; 126 } else { 127 assert(is_local_state_retn(state) == 1); 128 pmf_flags = PMF_NO_CACHE_MAINT; 129 } 130 #endif 131 132 PMF_GET_TIMESTAMP_BY_INDEX(psci_svc, 133 PSCI_STAT_ID_ENTER_LOW_PWR, 134 last_cpu_idx, 135 pmf_flags, 136 pwrdn_ts); 137 138 PMF_GET_TIMESTAMP_BY_INDEX(psci_svc, 139 PSCI_STAT_ID_EXIT_LOW_PWR, 140 plat_my_core_pos(), 141 pmf_flags, 142 pwrup_ts); 143 144 return calc_stat_residency(pwrup_ts, pwrdn_ts); 145 } 146 #endif /* ENABLE_PSCI_STAT && ENABLE_PMF */ 147 148 /* 149 * The PSCI generic code uses this API to let the platform participate in state 150 * coordination during a power management operation. It compares the platform 151 * specific local power states requested by each cpu for a given power domain 152 * and returns the coordinated target power state that the domain should 153 * enter. A platform assigns a number to a local power state. This default 154 * implementation assumes that the platform assigns these numbers in order of 155 * increasing depth of the power state i.e. for two power states X & Y, if X < Y 156 * then X represents a shallower power state than Y. As a result, the 157 * coordinated target local power state for a power domain will be the minimum 158 * of the requested local power states. 159 */ 160 plat_local_state_t plat_get_target_pwr_state(unsigned int lvl, 161 const plat_local_state_t *states, 162 unsigned int ncpu) 163 { 164 plat_local_state_t target = PLAT_MAX_OFF_STATE, temp; 165 const plat_local_state_t *st = states; 166 unsigned int n = ncpu; 167 168 assert(ncpu > 0U); 169 170 do { 171 temp = *st; 172 st++; 173 if (temp < target) 174 target = temp; 175 n--; 176 } while (n > 0U); 177 178 return target; 179 } 180