xref: /arm-trusted-firmware/plat/common/plat_psci_common.c (revision 91f16700b400a8c0651d24a598fc48ee2997a0d7)
1*91f16700Schasinglulu /*
2*91f16700Schasinglulu  * Copyright (c) 2016-2020, ARM Limited and Contributors. All rights reserved.
3*91f16700Schasinglulu  * Copyright (c) 2020, NVIDIA Corporation. All rights reserved.
4*91f16700Schasinglulu  *
5*91f16700Schasinglulu  * SPDX-License-Identifier: BSD-3-Clause
6*91f16700Schasinglulu  */
7*91f16700Schasinglulu 
8*91f16700Schasinglulu #include <assert.h>
9*91f16700Schasinglulu 
10*91f16700Schasinglulu #include <arch.h>
11*91f16700Schasinglulu #include <lib/pmf/pmf.h>
12*91f16700Schasinglulu #include <lib/psci/psci.h>
13*91f16700Schasinglulu #include <lib/utils_def.h>
14*91f16700Schasinglulu #include <plat/common/platform.h>
15*91f16700Schasinglulu 
16*91f16700Schasinglulu #if ENABLE_PSCI_STAT && ENABLE_PMF
17*91f16700Schasinglulu #pragma weak plat_psci_stat_accounting_start
18*91f16700Schasinglulu #pragma weak plat_psci_stat_accounting_stop
19*91f16700Schasinglulu #pragma weak plat_psci_stat_get_residency
20*91f16700Schasinglulu 
21*91f16700Schasinglulu /* Maximum time-stamp value read from architectural counters */
22*91f16700Schasinglulu #ifdef __aarch64__
23*91f16700Schasinglulu #define MAX_TS	UINT64_MAX
24*91f16700Schasinglulu #else
25*91f16700Schasinglulu #define MAX_TS	UINT32_MAX
26*91f16700Schasinglulu #endif
27*91f16700Schasinglulu 
28*91f16700Schasinglulu /* Following are used as ID's to capture time-stamp */
29*91f16700Schasinglulu #define PSCI_STAT_ID_ENTER_LOW_PWR		0
30*91f16700Schasinglulu #define PSCI_STAT_ID_EXIT_LOW_PWR		1
31*91f16700Schasinglulu #define PSCI_STAT_TOTAL_IDS			2
32*91f16700Schasinglulu 
33*91f16700Schasinglulu PMF_DECLARE_CAPTURE_TIMESTAMP(psci_svc)
34*91f16700Schasinglulu PMF_DECLARE_GET_TIMESTAMP(psci_svc)
35*91f16700Schasinglulu PMF_REGISTER_SERVICE(psci_svc, PMF_PSCI_STAT_SVC_ID, PSCI_STAT_TOTAL_IDS,
36*91f16700Schasinglulu 	PMF_STORE_ENABLE)
37*91f16700Schasinglulu 
38*91f16700Schasinglulu /*
39*91f16700Schasinglulu  * This function calculates the stats residency in microseconds,
40*91f16700Schasinglulu  * taking in account the wrap around condition.
41*91f16700Schasinglulu  */
42*91f16700Schasinglulu static u_register_t calc_stat_residency(unsigned long long pwrupts,
43*91f16700Schasinglulu 	unsigned long long pwrdnts)
44*91f16700Schasinglulu {
45*91f16700Schasinglulu 	/* The divisor to use to convert raw timestamp into microseconds. */
46*91f16700Schasinglulu 	u_register_t residency_div;
47*91f16700Schasinglulu 	u_register_t res;
48*91f16700Schasinglulu 
49*91f16700Schasinglulu 	/*
50*91f16700Schasinglulu 	 * Calculate divisor so that it can be directly used to
51*91f16700Schasinglulu 	 * convert time-stamp into microseconds.
52*91f16700Schasinglulu 	 */
53*91f16700Schasinglulu 	residency_div = read_cntfrq_el0() / MHZ_TICKS_PER_SEC;
54*91f16700Schasinglulu 	assert(residency_div > 0U);
55*91f16700Schasinglulu 
56*91f16700Schasinglulu 	if (pwrupts < pwrdnts)
57*91f16700Schasinglulu 		res = MAX_TS - pwrdnts + pwrupts;
58*91f16700Schasinglulu 	else
59*91f16700Schasinglulu 		res = pwrupts - pwrdnts;
60*91f16700Schasinglulu 
61*91f16700Schasinglulu 	return res / residency_div;
62*91f16700Schasinglulu }
63*91f16700Schasinglulu 
64*91f16700Schasinglulu /*
65*91f16700Schasinglulu  * Capture timestamp before entering a low power state.
66*91f16700Schasinglulu  * Cache maintenance may be needed when reading these timestamps.
67*91f16700Schasinglulu  */
68*91f16700Schasinglulu void plat_psci_stat_accounting_start(
69*91f16700Schasinglulu 	__unused const psci_power_state_t *state_info)
70*91f16700Schasinglulu {
71*91f16700Schasinglulu 	assert(state_info != NULL);
72*91f16700Schasinglulu 	PMF_CAPTURE_TIMESTAMP(psci_svc, PSCI_STAT_ID_ENTER_LOW_PWR,
73*91f16700Schasinglulu 		PMF_CACHE_MAINT);
74*91f16700Schasinglulu }
75*91f16700Schasinglulu 
76*91f16700Schasinglulu /*
77*91f16700Schasinglulu  * Capture timestamp after exiting a low power state.
78*91f16700Schasinglulu  * Cache maintenance may be needed when reading these timestamps.
79*91f16700Schasinglulu  */
80*91f16700Schasinglulu void plat_psci_stat_accounting_stop(
81*91f16700Schasinglulu 	__unused const psci_power_state_t *state_info)
82*91f16700Schasinglulu {
83*91f16700Schasinglulu 	assert(state_info != NULL);
84*91f16700Schasinglulu 	PMF_CAPTURE_TIMESTAMP(psci_svc, PSCI_STAT_ID_EXIT_LOW_PWR,
85*91f16700Schasinglulu 		PMF_CACHE_MAINT);
86*91f16700Schasinglulu }
87*91f16700Schasinglulu 
88*91f16700Schasinglulu /*
89*91f16700Schasinglulu  * Calculate the residency for the given level and power state
90*91f16700Schasinglulu  * information.
91*91f16700Schasinglulu  */
92*91f16700Schasinglulu u_register_t plat_psci_stat_get_residency(unsigned int lvl,
93*91f16700Schasinglulu 	const psci_power_state_t *state_info,
94*91f16700Schasinglulu 	unsigned int last_cpu_idx)
95*91f16700Schasinglulu {
96*91f16700Schasinglulu 	plat_local_state_t state;
97*91f16700Schasinglulu 	unsigned long long pwrup_ts = 0, pwrdn_ts = 0;
98*91f16700Schasinglulu 	unsigned int pmf_flags;
99*91f16700Schasinglulu 
100*91f16700Schasinglulu 	assert((lvl >= PSCI_CPU_PWR_LVL) && (lvl <= PLAT_MAX_PWR_LVL));
101*91f16700Schasinglulu 	assert(state_info != NULL);
102*91f16700Schasinglulu 	assert(last_cpu_idx <= PLATFORM_CORE_COUNT);
103*91f16700Schasinglulu 
104*91f16700Schasinglulu 	if (lvl == PSCI_CPU_PWR_LVL)
105*91f16700Schasinglulu 		assert(last_cpu_idx == plat_my_core_pos());
106*91f16700Schasinglulu 
107*91f16700Schasinglulu 	/*
108*91f16700Schasinglulu 	 * If power down is requested, then timestamp capture will
109*91f16700Schasinglulu 	 * be with caches OFF.  Hence we have to do cache maintenance
110*91f16700Schasinglulu 	 * when reading the timestamp.
111*91f16700Schasinglulu 	 */
112*91f16700Schasinglulu 	state = state_info->pwr_domain_state[PSCI_CPU_PWR_LVL];
113*91f16700Schasinglulu 	if (is_local_state_off(state) != 0) {
114*91f16700Schasinglulu 		pmf_flags = PMF_CACHE_MAINT;
115*91f16700Schasinglulu 	} else {
116*91f16700Schasinglulu 		assert(is_local_state_retn(state) == 1);
117*91f16700Schasinglulu 		pmf_flags = PMF_NO_CACHE_MAINT;
118*91f16700Schasinglulu 	}
119*91f16700Schasinglulu 
120*91f16700Schasinglulu 	PMF_GET_TIMESTAMP_BY_INDEX(psci_svc,
121*91f16700Schasinglulu 		PSCI_STAT_ID_ENTER_LOW_PWR,
122*91f16700Schasinglulu 		last_cpu_idx,
123*91f16700Schasinglulu 		pmf_flags,
124*91f16700Schasinglulu 		pwrdn_ts);
125*91f16700Schasinglulu 
126*91f16700Schasinglulu 	PMF_GET_TIMESTAMP_BY_INDEX(psci_svc,
127*91f16700Schasinglulu 		PSCI_STAT_ID_EXIT_LOW_PWR,
128*91f16700Schasinglulu 		plat_my_core_pos(),
129*91f16700Schasinglulu 		pmf_flags,
130*91f16700Schasinglulu 		pwrup_ts);
131*91f16700Schasinglulu 
132*91f16700Schasinglulu 	return calc_stat_residency(pwrup_ts, pwrdn_ts);
133*91f16700Schasinglulu }
134*91f16700Schasinglulu #endif /* ENABLE_PSCI_STAT && ENABLE_PMF */
135*91f16700Schasinglulu 
136*91f16700Schasinglulu /*
137*91f16700Schasinglulu  * The PSCI generic code uses this API to let the platform participate in state
138*91f16700Schasinglulu  * coordination during a power management operation. It compares the platform
139*91f16700Schasinglulu  * specific local power states requested by each cpu for a given power domain
140*91f16700Schasinglulu  * and returns the coordinated target power state that the domain should
141*91f16700Schasinglulu  * enter. A platform assigns a number to a local power state. This default
142*91f16700Schasinglulu  * implementation assumes that the platform assigns these numbers in order of
143*91f16700Schasinglulu  * increasing depth of the power state i.e. for two power states X & Y, if X < Y
144*91f16700Schasinglulu  * then X represents a shallower power state than Y. As a result, the
145*91f16700Schasinglulu  * coordinated target local power state for a power domain will be the minimum
146*91f16700Schasinglulu  * of the requested local power states.
147*91f16700Schasinglulu  */
148*91f16700Schasinglulu plat_local_state_t plat_get_target_pwr_state(unsigned int lvl,
149*91f16700Schasinglulu 					     const plat_local_state_t *states,
150*91f16700Schasinglulu 					     unsigned int ncpu)
151*91f16700Schasinglulu {
152*91f16700Schasinglulu 	plat_local_state_t target = PLAT_MAX_OFF_STATE, temp;
153*91f16700Schasinglulu 	const plat_local_state_t *st = states;
154*91f16700Schasinglulu 	unsigned int n = ncpu;
155*91f16700Schasinglulu 
156*91f16700Schasinglulu 	assert(ncpu > 0U);
157*91f16700Schasinglulu 
158*91f16700Schasinglulu 	do {
159*91f16700Schasinglulu 		temp = *st;
160*91f16700Schasinglulu 		st++;
161*91f16700Schasinglulu 		if (temp < target)
162*91f16700Schasinglulu 			target = temp;
163*91f16700Schasinglulu 		n--;
164*91f16700Schasinglulu 	} while (n > 0U);
165*91f16700Schasinglulu 
166*91f16700Schasinglulu 	return target;
167*91f16700Schasinglulu }
168