blob: d8034a5d7eeff112112370b7d603de4b8c7531bc [file] [log] [blame]
Yatharth Kochar241ec6c2016-05-09 18:26:35 +01001/*
dp-arm66abfbe2017-01-31 13:01:04 +00002 * Copyright (c) 2016-2017, ARM Limited and Contributors. All rights reserved.
Yatharth Kochar241ec6c2016-05-09 18:26:35 +01003 *
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
Yatharth Kochar241ec6c2016-05-09 18:26:35 +010041/* Following structure is used for PSCI STAT */
42typedef struct psci_stat {
43 u_register_t residency;
44 u_register_t count;
45} psci_stat_t;
46
47/*
48 * Following is used to keep track of the last cpu
49 * that goes to power down in non cpu power domains.
50 */
51static int last_cpu_in_non_cpu_pd[PSCI_NUM_NON_CPU_PWR_DOMAINS] = {-1};
52
53/*
54 * Following are used to store PSCI STAT values for
55 * CPU and non CPU power domains.
56 */
57static psci_stat_t psci_cpu_stat[PLATFORM_CORE_COUNT]
58 [PLAT_MAX_PWR_LVL_STATES];
59static psci_stat_t psci_non_cpu_stat[PSCI_NUM_NON_CPU_PWR_DOMAINS]
60 [PLAT_MAX_PWR_LVL_STATES];
61
Yatharth Kochar241ec6c2016-05-09 18:26:35 +010062/*
Yatharth Kochar241ec6c2016-05-09 18:26:35 +010063 * This functions returns the index into the `psci_stat_t` array given the
64 * local power state and power domain level. If the platform implements the
65 * `get_pwr_lvl_state_idx` pm hook, then that will be used to return the index.
66 */
67static int get_stat_idx(plat_local_state_t local_state, int pwr_lvl)
68{
69 int idx;
70
71 if (psci_plat_pm_ops->get_pwr_lvl_state_idx == NULL) {
72 assert(PLAT_MAX_PWR_LVL_STATES == 2);
73 if (is_local_state_retn(local_state))
74 return 0;
75
76 assert(is_local_state_off(local_state));
77 return 1;
78 }
79
80 idx = psci_plat_pm_ops->get_pwr_lvl_state_idx(local_state, pwr_lvl);
81 assert((idx >= 0) && (idx < PLAT_MAX_PWR_LVL_STATES));
82 return idx;
83}
84
85/*******************************************************************************
86 * This function is passed the target local power states for each power
87 * domain (state_info) between the current CPU domain and its ancestors until
88 * the target power level (end_pwrlvl).
89 *
90 * Then, for each level (apart from the CPU level) until the 'end_pwrlvl', it
91 * updates the `last_cpu_in_non_cpu_pd[]` with last power down cpu id.
92 *
93 * This function will only be invoked with data cache enabled and while
94 * powering down a core.
95 ******************************************************************************/
96void psci_stats_update_pwr_down(unsigned int end_pwrlvl,
97 const psci_power_state_t *state_info)
98{
99 int lvl, parent_idx, cpu_idx = plat_my_core_pos();
100
101 assert(end_pwrlvl <= PLAT_MAX_PWR_LVL);
102 assert(state_info);
103
104 parent_idx = psci_cpu_pd_nodes[cpu_idx].parent_node;
105
106 for (lvl = PSCI_CPU_PWR_LVL + 1; lvl <= end_pwrlvl; lvl++) {
107
108 /* Break early if the target power state is RUN */
109 if (is_local_state_run(state_info->pwr_domain_state[lvl]))
110 break;
111
112 /*
113 * The power domain is entering a low power state, so this is
114 * the last CPU for this power domain
115 */
116 last_cpu_in_non_cpu_pd[parent_idx] = cpu_idx;
117
118 parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node;
119 }
120
121}
122
123/*******************************************************************************
124 * This function updates the PSCI STATS(residency time and count) for CPU
125 * and NON-CPU power domains.
126 * It is called with caches enabled and locks acquired(for NON-CPU domain)
127 ******************************************************************************/
128void psci_stats_update_pwr_up(unsigned int end_pwrlvl,
dp-arm66abfbe2017-01-31 13:01:04 +0000129 const psci_power_state_t *state_info)
Yatharth Kochar241ec6c2016-05-09 18:26:35 +0100130{
131 int parent_idx, cpu_idx = plat_my_core_pos();
132 int lvl, stat_idx;
133 plat_local_state_t local_state;
Yatharth Kochar241ec6c2016-05-09 18:26:35 +0100134 u_register_t residency;
135
136 assert(end_pwrlvl <= PLAT_MAX_PWR_LVL);
137 assert(state_info);
138
Yatharth Kochar241ec6c2016-05-09 18:26:35 +0100139 /* Get the index into the stats array */
140 local_state = state_info->pwr_domain_state[PSCI_CPU_PWR_LVL];
141 stat_idx = get_stat_idx(local_state, PSCI_CPU_PWR_LVL);
142
dp-arm66abfbe2017-01-31 13:01:04 +0000143 /* Call into platform interface to calculate residency. */
144 residency = plat_psci_stat_get_residency(PSCI_CPU_PWR_LVL,
145 state_info, cpu_idx);
Yatharth Kochar241ec6c2016-05-09 18:26:35 +0100146
147 /* Update CPU stats. */
148 psci_cpu_stat[cpu_idx][stat_idx].residency += residency;
149 psci_cpu_stat[cpu_idx][stat_idx].count++;
150
151 /*
152 * Check what power domains above CPU were off
153 * prior to this CPU powering on.
154 */
155 parent_idx = psci_cpu_pd_nodes[cpu_idx].parent_node;
156 for (lvl = PSCI_CPU_PWR_LVL + 1; lvl <= end_pwrlvl; lvl++) {
157 local_state = state_info->pwr_domain_state[lvl];
158 if (is_local_state_run(local_state)) {
159 /* Break early */
160 break;
161 }
162
163 assert(last_cpu_in_non_cpu_pd[parent_idx] != -1);
164
dp-arm66abfbe2017-01-31 13:01:04 +0000165 /* Call into platform interface to calculate residency. */
166 residency = plat_psci_stat_get_residency(lvl, state_info,
167 last_cpu_in_non_cpu_pd[parent_idx]);
Yatharth Kochar241ec6c2016-05-09 18:26:35 +0100168
169 /* Initialize back to reset value */
170 last_cpu_in_non_cpu_pd[parent_idx] = -1;
171
172 /* Get the index into the stats array */
173 stat_idx = get_stat_idx(local_state, lvl);
174
Yatharth Kochar241ec6c2016-05-09 18:26:35 +0100175 /* Update non cpu stats */
176 psci_non_cpu_stat[parent_idx][stat_idx].residency += residency;
177 psci_non_cpu_stat[parent_idx][stat_idx].count++;
178
179 parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node;
180 }
181
182}
183
184/*******************************************************************************
185 * This function returns the appropriate count and residency time of the
186 * local state for the highest power level expressed in the `power_state`
187 * for the node represented by `target_cpu`.
188 ******************************************************************************/
189int psci_get_stat(u_register_t target_cpu, unsigned int power_state,
190 psci_stat_t *psci_stat)
191{
192 int rc, pwrlvl, lvl, parent_idx, stat_idx, target_idx;
193 psci_power_state_t state_info = { {PSCI_LOCAL_STATE_RUN} };
194 plat_local_state_t local_state;
195
196 /* Validate the target_cpu parameter and determine the cpu index */
197 target_idx = plat_core_pos_by_mpidr(target_cpu);
198 if (target_idx == -1)
199 return PSCI_E_INVALID_PARAMS;
200
201 /* Validate the power_state parameter */
202 if (!psci_plat_pm_ops->translate_power_state_by_mpidr)
203 rc = psci_validate_power_state(power_state, &state_info);
204 else
205 rc = psci_plat_pm_ops->translate_power_state_by_mpidr(
206 target_cpu, power_state, &state_info);
207
208 if (rc != PSCI_E_SUCCESS)
209 return PSCI_E_INVALID_PARAMS;
210
211 /* Find the highest power level */
212 pwrlvl = psci_find_target_suspend_lvl(&state_info);
Sandrine Bailleuxf9f3bbf2016-06-22 16:35:01 +0100213 if (pwrlvl == PSCI_INVALID_PWR_LVL) {
214 ERROR("Invalid target power level for PSCI statistics operation\n");
215 panic();
216 }
Yatharth Kochar241ec6c2016-05-09 18:26:35 +0100217
218 /* Get the index into the stats array */
219 local_state = state_info.pwr_domain_state[pwrlvl];
220 stat_idx = get_stat_idx(local_state, pwrlvl);
221
222 if (pwrlvl > PSCI_CPU_PWR_LVL) {
223 /* Get the power domain index */
224 parent_idx = psci_cpu_pd_nodes[target_idx].parent_node;
225 for (lvl = PSCI_CPU_PWR_LVL + 1; lvl < pwrlvl; lvl++)
226 parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node;
227
228 /* Get the non cpu power domain stats */
229 *psci_stat = psci_non_cpu_stat[parent_idx][stat_idx];
230 } else {
231 /* Get the cpu power domain stats */
232 *psci_stat = psci_cpu_stat[target_idx][stat_idx];
233 }
234
235 return PSCI_E_SUCCESS;
236}
237
238/* This is the top level function for PSCI_STAT_RESIDENCY SMC. */
239u_register_t psci_stat_residency(u_register_t target_cpu,
240 unsigned int power_state)
241{
242 psci_stat_t psci_stat;
243
244 int rc = psci_get_stat(target_cpu, power_state, &psci_stat);
245 if (rc == PSCI_E_SUCCESS)
246 return psci_stat.residency;
247 else
248 return 0;
249}
250
251/* This is the top level function for PSCI_STAT_COUNT SMC. */
252u_register_t psci_stat_count(u_register_t target_cpu,
253 unsigned int power_state)
254{
255 psci_stat_t psci_stat;
256
257 int rc = psci_get_stat(target_cpu, power_state, &psci_stat);
258 if (rc == PSCI_E_SUCCESS)
259 return psci_stat.count;
260 else
261 return 0;
262}