xref: /rk3399_ARM-atf/plat/common/plat_psci_common.c (revision 05d22c3045e2e972c2262b9ccd6c82cb7545bf83)
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 
68 	return res / residency_div;
69 }
70 
71 /*
72  * Capture timestamp before entering a low power state.
73  * Cache maintenance may be needed when reading these timestamps.
74  */
75 void plat_psci_stat_accounting_start(
76 	__unused const psci_power_state_t *state_info)
77 {
78 	assert(state_info != NULL);
79 	PMF_CAPTURE_TIMESTAMP(psci_svc, PSCI_STAT_ID_ENTER_LOW_PWR,
80 		CACHE_MAINTENANCE_ATTR);
81 }
82 
83 /*
84  * Capture timestamp after exiting a low power state.
85  * Cache maintenance may be needed when reading these timestamps.
86  */
87 void plat_psci_stat_accounting_stop(
88 	__unused const psci_power_state_t *state_info)
89 {
90 	assert(state_info != NULL);
91 	PMF_CAPTURE_TIMESTAMP(psci_svc, PSCI_STAT_ID_EXIT_LOW_PWR,
92 		CACHE_MAINTENANCE_ATTR);
93 }
94 
95 /*
96  * Calculate the residency for the given level and power state
97  * information.
98  */
99 u_register_t plat_psci_stat_get_residency(unsigned int lvl,
100 	const psci_power_state_t *state_info,
101 	unsigned int last_cpu_idx)
102 {
103 	unsigned long long pwrup_ts = 0, pwrdn_ts = 0;
104 	unsigned int pmf_flags;
105 
106 	assert((lvl >= PSCI_CPU_PWR_LVL) && (lvl <= PLAT_MAX_PWR_LVL));
107 	assert(last_cpu_idx <= PLATFORM_CORE_COUNT);
108 
109 	if (lvl == PSCI_CPU_PWR_LVL)
110 		assert(last_cpu_idx == plat_my_core_pos());
111 
112 #if HW_ASSISTED_COHERENCY
113 	/* HW coherency allows for the capture and access to happen with caches
114 	 * ON. So these timestamps don't need cache maintenance */
115 	pmf_flags = PMF_NO_CACHE_MAINT;
116 #else
117 	/*
118 	 * If power down is requested, then timestamp capture will
119 	 * be with caches OFF.  Hence we have to do cache maintenance
120 	 * when reading the timestamp.
121 	 */
122 	plat_local_state_t state;
123 	assert(state_info != NULL);
124 	state = state_info->pwr_domain_state[PSCI_CPU_PWR_LVL];
125 	if (is_local_state_off(state) != 0) {
126 		pmf_flags = PMF_CACHE_MAINT;
127 	} else {
128 		assert(is_local_state_retn(state) == 1);
129 		pmf_flags = PMF_NO_CACHE_MAINT;
130 	}
131 #endif
132 
133 	PMF_GET_TIMESTAMP_BY_INDEX(psci_svc,
134 		PSCI_STAT_ID_ENTER_LOW_PWR,
135 		last_cpu_idx,
136 		pmf_flags,
137 		pwrdn_ts);
138 
139 	PMF_GET_TIMESTAMP_BY_INDEX(psci_svc,
140 		PSCI_STAT_ID_EXIT_LOW_PWR,
141 		plat_my_core_pos(),
142 		pmf_flags,
143 		pwrup_ts);
144 
145 	return calc_stat_residency(pwrup_ts, pwrdn_ts);
146 }
147 #endif /* ENABLE_PSCI_STAT && ENABLE_PMF */
148 
149 /*
150  * The PSCI generic code uses this API to let the platform participate in state
151  * coordination during a power management operation. It compares the platform
152  * specific local power states requested by each cpu for a given power domain
153  * and returns the coordinated target power state that the domain should
154  * enter. A platform assigns a number to a local power state. This default
155  * implementation assumes that the platform assigns these numbers in order of
156  * increasing depth of the power state i.e. for two power states X & Y, if X < Y
157  * then X represents a shallower power state than Y. As a result, the
158  * coordinated target local power state for a power domain will be the minimum
159  * of the requested local power states.
160  */
161 plat_local_state_t plat_get_target_pwr_state(unsigned int lvl,
162 					     const plat_local_state_t *states,
163 					     unsigned int ncpu)
164 {
165 	(void)lvl;
166 	plat_local_state_t target = PLAT_MAX_OFF_STATE, temp;
167 	const plat_local_state_t *st = states;
168 	unsigned int n = ncpu;
169 
170 	assert(ncpu > 0U);
171 
172 	do {
173 		temp = *st;
174 		st++;
175 		if (temp < target) {
176 			target = temp;
177 		}
178 		n--;
179 	} while (n > 0U);
180 
181 	return target;
182 }
183