xref: /arm-trusted-firmware/lib/psci/psci_stat.c (revision 91f16700b400a8c0651d24a598fc48ee2997a0d7)
1*91f16700Schasinglulu /*
2*91f16700Schasinglulu  * Copyright (c) 2016-2019, Arm Limited and Contributors. All rights reserved.
3*91f16700Schasinglulu  *
4*91f16700Schasinglulu  * SPDX-License-Identifier: BSD-3-Clause
5*91f16700Schasinglulu  */
6*91f16700Schasinglulu 
7*91f16700Schasinglulu #include <assert.h>
8*91f16700Schasinglulu 
9*91f16700Schasinglulu #include <platform_def.h>
10*91f16700Schasinglulu 
11*91f16700Schasinglulu #include <common/debug.h>
12*91f16700Schasinglulu #include <plat/common/platform.h>
13*91f16700Schasinglulu 
14*91f16700Schasinglulu #include "psci_private.h"
15*91f16700Schasinglulu 
16*91f16700Schasinglulu #ifndef PLAT_MAX_PWR_LVL_STATES
17*91f16700Schasinglulu #define PLAT_MAX_PWR_LVL_STATES		2U
18*91f16700Schasinglulu #endif
19*91f16700Schasinglulu 
20*91f16700Schasinglulu /* Following structure is used for PSCI STAT */
21*91f16700Schasinglulu typedef struct psci_stat {
22*91f16700Schasinglulu 	u_register_t residency;
23*91f16700Schasinglulu 	u_register_t count;
24*91f16700Schasinglulu } psci_stat_t;
25*91f16700Schasinglulu 
26*91f16700Schasinglulu /*
27*91f16700Schasinglulu  * Following is used to keep track of the last cpu
28*91f16700Schasinglulu  * that goes to power down in non cpu power domains.
29*91f16700Schasinglulu  */
30*91f16700Schasinglulu static int last_cpu_in_non_cpu_pd[PSCI_NUM_NON_CPU_PWR_DOMAINS] = {
31*91f16700Schasinglulu 		[0 ... PSCI_NUM_NON_CPU_PWR_DOMAINS - 1U] = -1};
32*91f16700Schasinglulu 
33*91f16700Schasinglulu /*
34*91f16700Schasinglulu  * Following are used to store PSCI STAT values for
35*91f16700Schasinglulu  * CPU and non CPU power domains.
36*91f16700Schasinglulu  */
37*91f16700Schasinglulu static psci_stat_t psci_cpu_stat[PLATFORM_CORE_COUNT]
38*91f16700Schasinglulu 				[PLAT_MAX_PWR_LVL_STATES];
39*91f16700Schasinglulu static psci_stat_t psci_non_cpu_stat[PSCI_NUM_NON_CPU_PWR_DOMAINS]
40*91f16700Schasinglulu 				[PLAT_MAX_PWR_LVL_STATES];
41*91f16700Schasinglulu 
42*91f16700Schasinglulu /*
43*91f16700Schasinglulu  * This functions returns the index into the `psci_stat_t` array given the
44*91f16700Schasinglulu  * local power state and power domain level. If the platform implements the
45*91f16700Schasinglulu  * `get_pwr_lvl_state_idx` pm hook, then that will be used to return the index.
46*91f16700Schasinglulu  */
47*91f16700Schasinglulu static int get_stat_idx(plat_local_state_t local_state, unsigned int pwr_lvl)
48*91f16700Schasinglulu {
49*91f16700Schasinglulu 	int idx;
50*91f16700Schasinglulu 
51*91f16700Schasinglulu 	if (psci_plat_pm_ops->get_pwr_lvl_state_idx == NULL) {
52*91f16700Schasinglulu 		assert(PLAT_MAX_PWR_LVL_STATES == 2U);
53*91f16700Schasinglulu 		if (is_local_state_retn(local_state) != 0)
54*91f16700Schasinglulu 			return 0;
55*91f16700Schasinglulu 
56*91f16700Schasinglulu 		assert(is_local_state_off(local_state) != 0);
57*91f16700Schasinglulu 		return 1;
58*91f16700Schasinglulu 	}
59*91f16700Schasinglulu 
60*91f16700Schasinglulu 	idx = psci_plat_pm_ops->get_pwr_lvl_state_idx(local_state, pwr_lvl);
61*91f16700Schasinglulu 	assert((idx >= 0) && (idx < (int) PLAT_MAX_PWR_LVL_STATES));
62*91f16700Schasinglulu 	return idx;
63*91f16700Schasinglulu }
64*91f16700Schasinglulu 
65*91f16700Schasinglulu /*******************************************************************************
66*91f16700Schasinglulu  * This function is passed the target local power states for each power
67*91f16700Schasinglulu  * domain (state_info) between the current CPU domain and its ancestors until
68*91f16700Schasinglulu  * the target power level (end_pwrlvl).
69*91f16700Schasinglulu  *
70*91f16700Schasinglulu  * Then, for each level (apart from the CPU level) until the 'end_pwrlvl', it
71*91f16700Schasinglulu  * updates the `last_cpu_in_non_cpu_pd[]` with last power down cpu id.
72*91f16700Schasinglulu  *
73*91f16700Schasinglulu  * This function will only be invoked with data cache enabled and while
74*91f16700Schasinglulu  * powering down a core.
75*91f16700Schasinglulu  ******************************************************************************/
76*91f16700Schasinglulu void psci_stats_update_pwr_down(unsigned int end_pwrlvl,
77*91f16700Schasinglulu 			const psci_power_state_t *state_info)
78*91f16700Schasinglulu {
79*91f16700Schasinglulu 	unsigned int lvl, parent_idx;
80*91f16700Schasinglulu 	unsigned int cpu_idx = plat_my_core_pos();
81*91f16700Schasinglulu 
82*91f16700Schasinglulu 	assert(end_pwrlvl <= PLAT_MAX_PWR_LVL);
83*91f16700Schasinglulu 	assert(state_info != NULL);
84*91f16700Schasinglulu 
85*91f16700Schasinglulu 	parent_idx = psci_cpu_pd_nodes[cpu_idx].parent_node;
86*91f16700Schasinglulu 
87*91f16700Schasinglulu 	for (lvl = PSCI_CPU_PWR_LVL + 1U; lvl <= end_pwrlvl; lvl++) {
88*91f16700Schasinglulu 
89*91f16700Schasinglulu 		/* Break early if the target power state is RUN */
90*91f16700Schasinglulu 		if (is_local_state_run(state_info->pwr_domain_state[lvl]) != 0)
91*91f16700Schasinglulu 			break;
92*91f16700Schasinglulu 
93*91f16700Schasinglulu 		/*
94*91f16700Schasinglulu 		 * The power domain is entering a low power state, so this is
95*91f16700Schasinglulu 		 * the last CPU for this power domain
96*91f16700Schasinglulu 		 */
97*91f16700Schasinglulu 		last_cpu_in_non_cpu_pd[parent_idx] = (int)cpu_idx;
98*91f16700Schasinglulu 
99*91f16700Schasinglulu 		parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node;
100*91f16700Schasinglulu 	}
101*91f16700Schasinglulu 
102*91f16700Schasinglulu }
103*91f16700Schasinglulu 
104*91f16700Schasinglulu /*******************************************************************************
105*91f16700Schasinglulu  * This function updates the PSCI STATS(residency time and count) for CPU
106*91f16700Schasinglulu  * and NON-CPU power domains.
107*91f16700Schasinglulu  * It is called with caches enabled and locks acquired(for NON-CPU domain)
108*91f16700Schasinglulu  ******************************************************************************/
109*91f16700Schasinglulu void psci_stats_update_pwr_up(unsigned int end_pwrlvl,
110*91f16700Schasinglulu 			const psci_power_state_t *state_info)
111*91f16700Schasinglulu {
112*91f16700Schasinglulu 	unsigned int lvl, parent_idx;
113*91f16700Schasinglulu 	unsigned int cpu_idx = plat_my_core_pos();
114*91f16700Schasinglulu 	int stat_idx;
115*91f16700Schasinglulu 	plat_local_state_t local_state;
116*91f16700Schasinglulu 	u_register_t residency;
117*91f16700Schasinglulu 
118*91f16700Schasinglulu 	assert(end_pwrlvl <= PLAT_MAX_PWR_LVL);
119*91f16700Schasinglulu 	assert(state_info != NULL);
120*91f16700Schasinglulu 
121*91f16700Schasinglulu 	/* Get the index into the stats array */
122*91f16700Schasinglulu 	local_state = state_info->pwr_domain_state[PSCI_CPU_PWR_LVL];
123*91f16700Schasinglulu 	stat_idx = get_stat_idx(local_state, PSCI_CPU_PWR_LVL);
124*91f16700Schasinglulu 
125*91f16700Schasinglulu 	/* Call into platform interface to calculate residency. */
126*91f16700Schasinglulu 	residency = plat_psci_stat_get_residency(PSCI_CPU_PWR_LVL,
127*91f16700Schasinglulu 	    state_info, cpu_idx);
128*91f16700Schasinglulu 
129*91f16700Schasinglulu 	/* Update CPU stats. */
130*91f16700Schasinglulu 	psci_cpu_stat[cpu_idx][stat_idx].residency += residency;
131*91f16700Schasinglulu 	psci_cpu_stat[cpu_idx][stat_idx].count++;
132*91f16700Schasinglulu 
133*91f16700Schasinglulu 	/*
134*91f16700Schasinglulu 	 * Check what power domains above CPU were off
135*91f16700Schasinglulu 	 * prior to this CPU powering on.
136*91f16700Schasinglulu 	 */
137*91f16700Schasinglulu 	parent_idx = psci_cpu_pd_nodes[cpu_idx].parent_node;
138*91f16700Schasinglulu 	/* Return early if this is the first power up. */
139*91f16700Schasinglulu 	if (last_cpu_in_non_cpu_pd[parent_idx] == -1)
140*91f16700Schasinglulu 		return;
141*91f16700Schasinglulu 
142*91f16700Schasinglulu 	for (lvl = PSCI_CPU_PWR_LVL + 1U; lvl <= end_pwrlvl; lvl++) {
143*91f16700Schasinglulu 		local_state = state_info->pwr_domain_state[lvl];
144*91f16700Schasinglulu 		if (is_local_state_run(local_state) != 0) {
145*91f16700Schasinglulu 			/* Break early */
146*91f16700Schasinglulu 			break;
147*91f16700Schasinglulu 		}
148*91f16700Schasinglulu 
149*91f16700Schasinglulu 		assert(last_cpu_in_non_cpu_pd[parent_idx] != -1);
150*91f16700Schasinglulu 
151*91f16700Schasinglulu 		/* Call into platform interface to calculate residency. */
152*91f16700Schasinglulu 		residency = plat_psci_stat_get_residency(lvl, state_info,
153*91f16700Schasinglulu 			(unsigned int)last_cpu_in_non_cpu_pd[parent_idx]);
154*91f16700Schasinglulu 
155*91f16700Schasinglulu 		/* Initialize back to reset value */
156*91f16700Schasinglulu 		last_cpu_in_non_cpu_pd[parent_idx] = -1;
157*91f16700Schasinglulu 
158*91f16700Schasinglulu 		/* Get the index into the stats array */
159*91f16700Schasinglulu 		stat_idx = get_stat_idx(local_state, lvl);
160*91f16700Schasinglulu 
161*91f16700Schasinglulu 		/* Update non cpu stats */
162*91f16700Schasinglulu 		psci_non_cpu_stat[parent_idx][stat_idx].residency += residency;
163*91f16700Schasinglulu 		psci_non_cpu_stat[parent_idx][stat_idx].count++;
164*91f16700Schasinglulu 
165*91f16700Schasinglulu 		parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node;
166*91f16700Schasinglulu 	}
167*91f16700Schasinglulu 
168*91f16700Schasinglulu }
169*91f16700Schasinglulu 
170*91f16700Schasinglulu /*******************************************************************************
171*91f16700Schasinglulu  * This function returns the appropriate count and residency time of the
172*91f16700Schasinglulu  * local state for the highest power level expressed in the `power_state`
173*91f16700Schasinglulu  * for the node represented by `target_cpu`.
174*91f16700Schasinglulu  ******************************************************************************/
175*91f16700Schasinglulu static int psci_get_stat(u_register_t target_cpu, unsigned int power_state,
176*91f16700Schasinglulu 			 psci_stat_t *psci_stat)
177*91f16700Schasinglulu {
178*91f16700Schasinglulu 	int rc;
179*91f16700Schasinglulu 	unsigned int pwrlvl, lvl, parent_idx, target_idx;
180*91f16700Schasinglulu 	int stat_idx;
181*91f16700Schasinglulu 	psci_power_state_t state_info = { {PSCI_LOCAL_STATE_RUN} };
182*91f16700Schasinglulu 	plat_local_state_t local_state;
183*91f16700Schasinglulu 
184*91f16700Schasinglulu 	/* Determine the cpu index */
185*91f16700Schasinglulu 	target_idx = (unsigned int) plat_core_pos_by_mpidr(target_cpu);
186*91f16700Schasinglulu 
187*91f16700Schasinglulu 	/* Validate the power_state parameter */
188*91f16700Schasinglulu 	if (psci_plat_pm_ops->translate_power_state_by_mpidr == NULL)
189*91f16700Schasinglulu 		rc = psci_validate_power_state(power_state, &state_info);
190*91f16700Schasinglulu 	else
191*91f16700Schasinglulu 		rc = psci_plat_pm_ops->translate_power_state_by_mpidr(
192*91f16700Schasinglulu 				target_cpu, power_state, &state_info);
193*91f16700Schasinglulu 
194*91f16700Schasinglulu 	if (rc != PSCI_E_SUCCESS)
195*91f16700Schasinglulu 		return PSCI_E_INVALID_PARAMS;
196*91f16700Schasinglulu 
197*91f16700Schasinglulu 	/* Find the highest power level */
198*91f16700Schasinglulu 	pwrlvl = psci_find_target_suspend_lvl(&state_info);
199*91f16700Schasinglulu 	if (pwrlvl == PSCI_INVALID_PWR_LVL) {
200*91f16700Schasinglulu 		ERROR("Invalid target power level for PSCI statistics operation\n");
201*91f16700Schasinglulu 		panic();
202*91f16700Schasinglulu 	}
203*91f16700Schasinglulu 
204*91f16700Schasinglulu 	/* Get the index into the stats array */
205*91f16700Schasinglulu 	local_state = state_info.pwr_domain_state[pwrlvl];
206*91f16700Schasinglulu 	stat_idx = get_stat_idx(local_state, pwrlvl);
207*91f16700Schasinglulu 
208*91f16700Schasinglulu 	if (pwrlvl > PSCI_CPU_PWR_LVL) {
209*91f16700Schasinglulu 		/* Get the power domain index */
210*91f16700Schasinglulu 		parent_idx = SPECULATION_SAFE_VALUE(psci_cpu_pd_nodes[target_idx].parent_node);
211*91f16700Schasinglulu 		for (lvl = PSCI_CPU_PWR_LVL + 1U; lvl < pwrlvl; lvl++)
212*91f16700Schasinglulu 			parent_idx = SPECULATION_SAFE_VALUE(psci_non_cpu_pd_nodes[parent_idx].parent_node);
213*91f16700Schasinglulu 
214*91f16700Schasinglulu 		/* Get the non cpu power domain stats */
215*91f16700Schasinglulu 		*psci_stat = psci_non_cpu_stat[parent_idx][stat_idx];
216*91f16700Schasinglulu 	} else {
217*91f16700Schasinglulu 		/* Get the cpu power domain stats */
218*91f16700Schasinglulu 		*psci_stat = psci_cpu_stat[target_idx][stat_idx];
219*91f16700Schasinglulu 	}
220*91f16700Schasinglulu 
221*91f16700Schasinglulu 	return PSCI_E_SUCCESS;
222*91f16700Schasinglulu }
223*91f16700Schasinglulu 
224*91f16700Schasinglulu /* This is the top level function for PSCI_STAT_RESIDENCY SMC. */
225*91f16700Schasinglulu u_register_t psci_stat_residency(u_register_t target_cpu,
226*91f16700Schasinglulu 		unsigned int power_state)
227*91f16700Schasinglulu {
228*91f16700Schasinglulu 	psci_stat_t psci_stat;
229*91f16700Schasinglulu 
230*91f16700Schasinglulu 	/* Validate the target cpu */
231*91f16700Schasinglulu 	if (!is_valid_mpidr(target_cpu))
232*91f16700Schasinglulu 		return 0;
233*91f16700Schasinglulu 
234*91f16700Schasinglulu 	int rc = psci_get_stat(target_cpu, power_state, &psci_stat);
235*91f16700Schasinglulu 
236*91f16700Schasinglulu 	if (rc == PSCI_E_SUCCESS)
237*91f16700Schasinglulu 		return psci_stat.residency;
238*91f16700Schasinglulu 	else
239*91f16700Schasinglulu 		return 0;
240*91f16700Schasinglulu }
241*91f16700Schasinglulu 
242*91f16700Schasinglulu /* This is the top level function for PSCI_STAT_COUNT SMC. */
243*91f16700Schasinglulu u_register_t psci_stat_count(u_register_t target_cpu,
244*91f16700Schasinglulu 	unsigned int power_state)
245*91f16700Schasinglulu {
246*91f16700Schasinglulu 	psci_stat_t psci_stat;
247*91f16700Schasinglulu 
248*91f16700Schasinglulu 	/* Validate the target cpu */
249*91f16700Schasinglulu 	if (!is_valid_mpidr(target_cpu))
250*91f16700Schasinglulu 		return 0;
251*91f16700Schasinglulu 
252*91f16700Schasinglulu 	int rc = psci_get_stat(target_cpu, power_state, &psci_stat);
253*91f16700Schasinglulu 
254*91f16700Schasinglulu 	if (rc == PSCI_E_SUCCESS)
255*91f16700Schasinglulu 		return psci_stat.count;
256*91f16700Schasinglulu 	else
257*91f16700Schasinglulu 		return 0;
258*91f16700Schasinglulu }
259