blob: ecbe592b8c5b0d833cf5cdeb9b18c0ab54c1599f [file] [log] [blame]
Yatharth Kochar241ec6c2016-05-09 18:26:35 +01001/*
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 */
45typedef 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 */
54static 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 */
60static psci_stat_t psci_cpu_stat[PLATFORM_CORE_COUNT]
61 [PLAT_MAX_PWR_LVL_STATES];
62static 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 */
66PMF_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 */
70u_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 */
91static 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 ******************************************************************************/
120void 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 ******************************************************************************/
152void 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 ******************************************************************************/
238int 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);
Sandrine Bailleuxf9f3bbf2016-06-22 16:35:01 +0100262 if (pwrlvl == PSCI_INVALID_PWR_LVL) {
263 ERROR("Invalid target power level for PSCI statistics operation\n");
264 panic();
265 }
Yatharth Kochar241ec6c2016-05-09 18:26:35 +0100266
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. */
288u_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. */
301u_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}