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