1 /* 2 * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are met: 6 * 7 * Redistributions of source code must retain the above copyright notice, this 8 * list of conditions and the following disclaimer. 9 * 10 * Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 14 * Neither the name of ARM nor the names of its contributors may be used 15 * to endorse or promote products derived from this software without specific 16 * prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include <assert.h> 32 #include <debug.h> 33 #include <platform.h> 34 #include <platform_def.h> 35 #include "psci_private.h" 36 37 #ifndef PLAT_MAX_PWR_LVL_STATES 38 #define PLAT_MAX_PWR_LVL_STATES 2 39 #endif 40 41 /* Ticks elapsed in one second by a signal of 1 MHz */ 42 #define MHZ_TICKS_PER_SEC 1000000 43 44 /* Following structure is used for PSCI STAT */ 45 typedef struct psci_stat { 46 u_register_t residency; 47 u_register_t count; 48 } psci_stat_t; 49 50 /* 51 * Following is used to keep track of the last cpu 52 * that goes to power down in non cpu power domains. 53 */ 54 static int last_cpu_in_non_cpu_pd[PSCI_NUM_NON_CPU_PWR_DOMAINS] = {-1}; 55 56 /* 57 * Following are used to store PSCI STAT values for 58 * CPU and non CPU power domains. 59 */ 60 static psci_stat_t psci_cpu_stat[PLATFORM_CORE_COUNT] 61 [PLAT_MAX_PWR_LVL_STATES]; 62 static psci_stat_t psci_non_cpu_stat[PSCI_NUM_NON_CPU_PWR_DOMAINS] 63 [PLAT_MAX_PWR_LVL_STATES]; 64 65 /* Register PMF PSCI service */ 66 PMF_REGISTER_SERVICE(psci_svc, PMF_PSCI_STAT_SVC_ID, 67 PSCI_STAT_TOTAL_IDS, PMF_STORE_ENABLE) 68 69 /* The divisor to use to convert raw timestamp into microseconds */ 70 u_register_t residency_div; 71 72 /* 73 * This macro calculates the stats residency in microseconds, 74 * taking in account the wrap around condition. 75 */ 76 #define calc_stat_residency(_pwrupts, _pwrdnts, _res) \ 77 do { \ 78 if (_pwrupts < _pwrdnts) \ 79 _res = UINT64_MAX - _pwrdnts + _pwrupts;\ 80 else \ 81 _res = _pwrupts - _pwrdnts; \ 82 /* Convert timestamp into microseconds */ \ 83 _res = _res/residency_div; \ 84 } while (0) 85 86 /* 87 * This functions returns the index into the `psci_stat_t` array given the 88 * local power state and power domain level. If the platform implements the 89 * `get_pwr_lvl_state_idx` pm hook, then that will be used to return the index. 90 */ 91 static int get_stat_idx(plat_local_state_t local_state, int pwr_lvl) 92 { 93 int idx; 94 95 if (psci_plat_pm_ops->get_pwr_lvl_state_idx == NULL) { 96 assert(PLAT_MAX_PWR_LVL_STATES == 2); 97 if (is_local_state_retn(local_state)) 98 return 0; 99 100 assert(is_local_state_off(local_state)); 101 return 1; 102 } 103 104 idx = psci_plat_pm_ops->get_pwr_lvl_state_idx(local_state, pwr_lvl); 105 assert((idx >= 0) && (idx < PLAT_MAX_PWR_LVL_STATES)); 106 return idx; 107 } 108 109 /******************************************************************************* 110 * This function is passed the target local power states for each power 111 * domain (state_info) between the current CPU domain and its ancestors until 112 * the target power level (end_pwrlvl). 113 * 114 * Then, for each level (apart from the CPU level) until the 'end_pwrlvl', it 115 * updates the `last_cpu_in_non_cpu_pd[]` with last power down cpu id. 116 * 117 * This function will only be invoked with data cache enabled and while 118 * powering down a core. 119 ******************************************************************************/ 120 void psci_stats_update_pwr_down(unsigned int end_pwrlvl, 121 const psci_power_state_t *state_info) 122 { 123 int lvl, parent_idx, cpu_idx = plat_my_core_pos(); 124 125 assert(end_pwrlvl <= PLAT_MAX_PWR_LVL); 126 assert(state_info); 127 128 parent_idx = psci_cpu_pd_nodes[cpu_idx].parent_node; 129 130 for (lvl = PSCI_CPU_PWR_LVL + 1; lvl <= end_pwrlvl; lvl++) { 131 132 /* Break early if the target power state is RUN */ 133 if (is_local_state_run(state_info->pwr_domain_state[lvl])) 134 break; 135 136 /* 137 * The power domain is entering a low power state, so this is 138 * the last CPU for this power domain 139 */ 140 last_cpu_in_non_cpu_pd[parent_idx] = cpu_idx; 141 142 parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node; 143 } 144 145 } 146 147 /******************************************************************************* 148 * This function updates the PSCI STATS(residency time and count) for CPU 149 * and NON-CPU power domains. 150 * It is called with caches enabled and locks acquired(for NON-CPU domain) 151 ******************************************************************************/ 152 void psci_stats_update_pwr_up(unsigned int end_pwrlvl, 153 const psci_power_state_t *state_info, 154 unsigned int flags) 155 { 156 int parent_idx, cpu_idx = plat_my_core_pos(); 157 int lvl, stat_idx; 158 plat_local_state_t local_state; 159 unsigned long long pwrup_ts = 0, pwrdn_ts = 0; 160 u_register_t residency; 161 162 assert(end_pwrlvl <= PLAT_MAX_PWR_LVL); 163 assert(state_info); 164 165 /* Initialize the residency divisor if not already initialized */ 166 if (!residency_div) { 167 /* Pre-calculate divisor so that it can be directly used to 168 convert time-stamp into microseconds */ 169 residency_div = read_cntfrq_el0() / MHZ_TICKS_PER_SEC; 170 assert(residency_div); 171 } 172 173 /* Get power down time-stamp for current CPU */ 174 PMF_GET_TIMESTAMP_BY_INDEX(psci_svc, PSCI_STAT_ID_ENTER_LOW_PWR, 175 cpu_idx, flags, pwrdn_ts); 176 177 /* In the case of 1st power on just return */ 178 if (!pwrdn_ts) 179 return; 180 181 /* Get power up time-stamp for current CPU */ 182 PMF_GET_TIMESTAMP_BY_INDEX(psci_svc, PSCI_STAT_ID_EXIT_LOW_PWR, 183 cpu_idx, flags, pwrup_ts); 184 185 /* Get the index into the stats array */ 186 local_state = state_info->pwr_domain_state[PSCI_CPU_PWR_LVL]; 187 stat_idx = get_stat_idx(local_state, PSCI_CPU_PWR_LVL); 188 189 /* Calculate stats residency */ 190 calc_stat_residency(pwrup_ts, pwrdn_ts, residency); 191 192 /* Update CPU stats. */ 193 psci_cpu_stat[cpu_idx][stat_idx].residency += residency; 194 psci_cpu_stat[cpu_idx][stat_idx].count++; 195 196 /* 197 * Check what power domains above CPU were off 198 * prior to this CPU powering on. 199 */ 200 parent_idx = psci_cpu_pd_nodes[cpu_idx].parent_node; 201 for (lvl = PSCI_CPU_PWR_LVL + 1; lvl <= end_pwrlvl; lvl++) { 202 local_state = state_info->pwr_domain_state[lvl]; 203 if (is_local_state_run(local_state)) { 204 /* Break early */ 205 break; 206 } 207 208 assert(last_cpu_in_non_cpu_pd[parent_idx] != -1); 209 210 /* Get power down time-stamp for last CPU */ 211 PMF_GET_TIMESTAMP_BY_INDEX(psci_svc, PSCI_STAT_ID_ENTER_LOW_PWR, 212 last_cpu_in_non_cpu_pd[parent_idx], 213 flags, pwrdn_ts); 214 215 /* Initialize back to reset value */ 216 last_cpu_in_non_cpu_pd[parent_idx] = -1; 217 218 /* Get the index into the stats array */ 219 stat_idx = get_stat_idx(local_state, lvl); 220 221 /* Calculate stats residency */ 222 calc_stat_residency(pwrup_ts, pwrdn_ts, residency); 223 224 /* Update non cpu stats */ 225 psci_non_cpu_stat[parent_idx][stat_idx].residency += residency; 226 psci_non_cpu_stat[parent_idx][stat_idx].count++; 227 228 parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node; 229 } 230 231 } 232 233 /******************************************************************************* 234 * This function returns the appropriate count and residency time of the 235 * local state for the highest power level expressed in the `power_state` 236 * for the node represented by `target_cpu`. 237 ******************************************************************************/ 238 int psci_get_stat(u_register_t target_cpu, unsigned int power_state, 239 psci_stat_t *psci_stat) 240 { 241 int rc, pwrlvl, lvl, parent_idx, stat_idx, target_idx; 242 psci_power_state_t state_info = { {PSCI_LOCAL_STATE_RUN} }; 243 plat_local_state_t local_state; 244 245 /* Validate the target_cpu parameter and determine the cpu index */ 246 target_idx = plat_core_pos_by_mpidr(target_cpu); 247 if (target_idx == -1) 248 return PSCI_E_INVALID_PARAMS; 249 250 /* Validate the power_state parameter */ 251 if (!psci_plat_pm_ops->translate_power_state_by_mpidr) 252 rc = psci_validate_power_state(power_state, &state_info); 253 else 254 rc = psci_plat_pm_ops->translate_power_state_by_mpidr( 255 target_cpu, power_state, &state_info); 256 257 if (rc != PSCI_E_SUCCESS) 258 return PSCI_E_INVALID_PARAMS; 259 260 /* Find the highest power level */ 261 pwrlvl = psci_find_target_suspend_lvl(&state_info); 262 if (pwrlvl == PSCI_INVALID_PWR_LVL) { 263 ERROR("Invalid target power level for PSCI statistics operation\n"); 264 panic(); 265 } 266 267 /* Get the index into the stats array */ 268 local_state = state_info.pwr_domain_state[pwrlvl]; 269 stat_idx = get_stat_idx(local_state, pwrlvl); 270 271 if (pwrlvl > PSCI_CPU_PWR_LVL) { 272 /* Get the power domain index */ 273 parent_idx = psci_cpu_pd_nodes[target_idx].parent_node; 274 for (lvl = PSCI_CPU_PWR_LVL + 1; lvl < pwrlvl; lvl++) 275 parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node; 276 277 /* Get the non cpu power domain stats */ 278 *psci_stat = psci_non_cpu_stat[parent_idx][stat_idx]; 279 } else { 280 /* Get the cpu power domain stats */ 281 *psci_stat = psci_cpu_stat[target_idx][stat_idx]; 282 } 283 284 return PSCI_E_SUCCESS; 285 } 286 287 /* This is the top level function for PSCI_STAT_RESIDENCY SMC. */ 288 u_register_t psci_stat_residency(u_register_t target_cpu, 289 unsigned int power_state) 290 { 291 psci_stat_t psci_stat; 292 293 int rc = psci_get_stat(target_cpu, power_state, &psci_stat); 294 if (rc == PSCI_E_SUCCESS) 295 return psci_stat.residency; 296 else 297 return 0; 298 } 299 300 /* This is the top level function for PSCI_STAT_COUNT SMC. */ 301 u_register_t psci_stat_count(u_register_t target_cpu, 302 unsigned int power_state) 303 { 304 psci_stat_t psci_stat; 305 306 int rc = psci_get_stat(target_cpu, power_state, &psci_stat); 307 if (rc == PSCI_E_SUCCESS) 308 return psci_stat.count; 309 else 310 return 0; 311 } 312