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