1 /*
2 * Copyright (c) 2021 Bestechnic (Shanghai) Co., Ltd. All rights reserved.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include "plat_types.h"
16 #include "hal_sysfreq.h"
17 #include "hal_location.h"
18 #include "hal_timer.h"
19 #include "hal_trace.h"
20 #include "cmsis.h"
21
22 #if !(defined(ROM_BUILD) || defined(CHIP_SUBSYS_SENS) || \
23 (defined(CHIP_SUBSYS_BTH) && !defined(BTH_AS_MAIN_MCU)) || \
24 defined(ARM_CMSE))
25 #define PMU_CTRL_ENABLE
26 #endif
27
28 #ifdef PMU_CTRL_ENABLE
29 #include "pmu.h"
30 #endif
31
32 static uint32_t BOOT_BSS_LOC sysfreq_bundle[(HAL_SYSFREQ_USER_QTY + 3) / 4];
33
34 static uint8_t * const sysfreq_per_user = (uint8_t *)&sysfreq_bundle[0];
35
36 static enum HAL_SYSFREQ_USER_T BOOT_DATA_LOC top_user = HAL_SYSFREQ_USER_QTY;
37
38 static enum HAL_CMU_FREQ_T BOOT_DATA_LOC min_sysfreq = HAL_CMU_FREQ_32K;
39
40 #ifdef SYSFREQ_STATS
41 static uint32_t BOOT_BSS_LOC last_check_time;
42 static uint32_t BOOT_BSS_LOC sysfreq_intvl[HAL_SYSFREQ_USER_QTY][2];
43 #endif
44
45 #ifdef SYSFREQ_STATS
46 BOOT_TEXT_SRAM_LOC
47 #endif
hal_sysfreq_revise_freq(enum HAL_CMU_FREQ_T freq)48 static enum HAL_CMU_FREQ_T hal_sysfreq_revise_freq(enum HAL_CMU_FREQ_T freq)
49 {
50 if (freq == HAL_CMU_FREQ_32K) {
51 freq = HAL_CMU_FREQ_26M;
52 }
53 return (freq > min_sysfreq) ? freq : min_sysfreq;
54 }
55
hal_sysfreq_set_min_freq(enum HAL_CMU_FREQ_T freq)56 void hal_sysfreq_set_min_freq(enum HAL_CMU_FREQ_T freq)
57 {
58 uint32_t lock;
59
60 lock = int_lock();
61
62 if (min_sysfreq < freq) {
63 min_sysfreq = freq;
64 if (min_sysfreq > hal_sysfreq_get()) {
65 hal_cmu_sys_set_freq(min_sysfreq);
66 }
67 }
68
69 int_unlock(lock);
70 }
71
hal_sysfreq_req(enum HAL_SYSFREQ_USER_T user,enum HAL_CMU_FREQ_T freq)72 int hal_sysfreq_req(enum HAL_SYSFREQ_USER_T user, enum HAL_CMU_FREQ_T freq)
73 {
74 uint32_t lock;
75 enum HAL_CMU_FREQ_T cur_freq;
76 enum HAL_CMU_FREQ_T real_freq;
77 enum HAL_CMU_FREQ_T real_cur_freq;
78 int i;
79
80 if (user >= HAL_SYSFREQ_USER_QTY) {
81 return 1;
82 }
83 if (freq >= HAL_CMU_FREQ_QTY) {
84 return 2;
85 }
86
87 lock = int_lock();
88
89 cur_freq = hal_sysfreq_get();
90
91 sysfreq_per_user[user] = freq;
92
93 if (freq == cur_freq) {
94 top_user = user;
95 } else if (freq > cur_freq) {
96 top_user = user;
97 real_freq = hal_sysfreq_revise_freq(freq);
98 real_cur_freq = hal_sysfreq_revise_freq(cur_freq);
99 // It is possible that revised freq <= revised cur_freq (e.g., when cur_freq=32K)
100 if (real_freq != real_cur_freq) {
101 #ifdef PMU_CTRL_ENABLE
102 pmu_sys_freq_config(real_freq);
103 #endif
104 #ifdef ULTRA_LOW_POWER
105 // Enable PLL if required
106 hal_cmu_low_freq_mode_disable(real_cur_freq, real_freq);
107 #endif
108 hal_cmu_sys_set_freq(real_freq);
109 }
110 #ifdef SYSFREQ_STATS
111 if (real_freq != real_cur_freq || cur_freq == HAL_CMU_FREQ_32K) {
112 uint32_t idx = (cur_freq == HAL_CMU_FREQ_32K) ? cur_freq : real_cur_freq;
113 uint32_t cur_time = hal_sys_timer_get();
114 sysfreq_intvl[idx][0] += cur_time - last_check_time;
115 last_check_time = cur_time;
116 }
117 #endif
118 } else /* if (freq < cur_freq) */ {
119 if (top_user == user || top_user == HAL_SYSFREQ_USER_QTY) {
120 if (top_user == user) {
121 freq = sysfreq_per_user[0];
122 user = 0;
123 for (i = 1; i < HAL_SYSFREQ_USER_QTY; i++) {
124 if (freq < sysfreq_per_user[i]) {
125 freq = sysfreq_per_user[i];
126 user = i;
127 }
128 }
129 }
130 top_user = user;
131 if (freq != cur_freq) {
132 real_freq = hal_sysfreq_revise_freq(freq);
133 real_cur_freq = hal_sysfreq_revise_freq(cur_freq);
134 // It is possible that revised freq >= revised cur_freq (e.g., when freq=32K)
135 if (real_freq != real_cur_freq) {
136 hal_cmu_sys_set_freq(real_freq);
137 #ifdef ULTRA_LOW_POWER
138 // Disable PLL if capable
139 hal_cmu_low_freq_mode_enable(real_cur_freq, real_freq);
140 #endif
141 #ifdef PMU_CTRL_ENABLE
142 pmu_sys_freq_config(real_freq);
143 #endif
144 }
145 #ifdef SYSFREQ_STATS
146 if (real_freq != real_cur_freq || freq == HAL_CMU_FREQ_32K) {
147 uint32_t cur_time = hal_sys_timer_get();
148 sysfreq_intvl[real_cur_freq][0] += cur_time - last_check_time;
149 last_check_time = cur_time;
150 }
151 #endif
152 }
153 }
154 }
155
156 int_unlock(lock);
157
158 return 0;
159 }
160
hal_sysfreq_get(void)161 enum HAL_CMU_FREQ_T hal_sysfreq_get(void)
162 {
163 if (top_user < HAL_SYSFREQ_USER_QTY) {
164 return sysfreq_per_user[top_user];
165 } else {
166 return hal_cmu_sys_get_freq();
167 }
168 }
169
hal_sysfreq_get_hw_freq(void)170 enum HAL_CMU_FREQ_T hal_sysfreq_get_hw_freq(void)
171 {
172 if (top_user < HAL_SYSFREQ_USER_QTY) {
173 return hal_sysfreq_revise_freq(sysfreq_per_user[top_user]);
174 } else {
175 return hal_cmu_sys_get_freq();
176 }
177 }
178
hal_sysfreq_busy(void)179 int hal_sysfreq_busy(void)
180 {
181 int i;
182
183 for (i = 0; i < ARRAY_SIZE(sysfreq_bundle); i++) {
184 if (sysfreq_bundle[i] != 0) {
185 return 1;
186 }
187 }
188
189 return 0;
190 }
191
hal_sysfreq_print_user_freq(void)192 void hal_sysfreq_print_user_freq(void)
193 {
194 int i;
195
196 TRACE(0, "SYSFREQ USER FREQ:");
197 for (i = 0; i < HAL_SYSFREQ_USER_QTY; i++) {
198 if (sysfreq_per_user[i] != 0) {
199 TRACE(TR_ATTR_NO_TS,"\t[%2u] f=%2u", i, sysfreq_per_user[i]);
200 }
201 }
202 TRACE(TR_ATTR_NO_TS,"\ttop_user=%2u", top_user);
203 }
204
205 #ifdef SYSFREQ_STATS
206 SRAM_TEXT_LOC
hal_sysfreq_get_freq_stats_index(void)207 static uint32_t hal_sysfreq_get_freq_stats_index(void)
208 {
209 enum HAL_CMU_FREQ_T cur_freq;
210 uint32_t idx;
211
212 if (top_user >= HAL_SYSFREQ_USER_QTY) {
213 return HAL_CMU_FREQ_26M;
214 }
215 cur_freq = sysfreq_per_user[top_user];
216 if (cur_freq == HAL_CMU_FREQ_32K) {
217 idx = cur_freq;
218 } else {
219 idx = hal_sysfreq_revise_freq(cur_freq);
220 }
221
222 return idx;
223 }
224
225 SRAM_TEXT_LOC
hal_sysfreq_add_freq_time(int idle,uint32_t cur_time)226 void hal_sysfreq_add_freq_time(int idle, uint32_t cur_time)
227 {
228 uint32_t idx;
229
230 idx = hal_sysfreq_get_freq_stats_index();
231 sysfreq_intvl[idx][idle ? 1 : 0] += cur_time - last_check_time;
232 last_check_time = cur_time;
233 }
234
hal_sysfreq_print_freq_stats(void)235 void hal_sysfreq_print_freq_stats(void)
236 {
237 uint32_t total_intvl;
238 uint32_t lock;
239 uint32_t idx;
240 uint32_t cur_time;
241 uint32_t i;
242 uint32_t ratio[2];
243
244 total_intvl = 0;
245
246 lock = int_lock();
247
248 idx = hal_sysfreq_get_freq_stats_index();
249 cur_time = hal_sys_timer_get();
250 sysfreq_intvl[idx][0] += cur_time - last_check_time;
251
252 for (i = 0; i < ARRAY_SIZE(sysfreq_intvl); i++) {
253 total_intvl += sysfreq_intvl[i][0] + sysfreq_intvl[i][1];
254 }
255
256 TRACE(0, "SYSFREQ FREQ STATS:");
257
258 if (total_intvl) {
259 for (i = 0; i < ARRAY_SIZE(sysfreq_intvl); i++) {
260 if (sysfreq_intvl[i][0] || sysfreq_intvl[i][1]) {
261 ratio[0] = sysfreq_intvl[i][0] * 100 / total_intvl;
262 ratio[1] = sysfreq_intvl[i][1] * 100 / total_intvl;
263 TRACE(TR_ATTR_NO_TS, "\t[%2u]: B=(%5u ms %2u%%) I=(%5u ms %2u%%)", i,
264 TICKS_TO_MS(sysfreq_intvl[i][0]), ratio[0],
265 TICKS_TO_MS(sysfreq_intvl[i][1]), ratio[1]);
266 }
267 }
268 }
269
270 // Reset intervals
271 for (i = 0; i < ARRAY_SIZE(sysfreq_intvl); i++) {
272 sysfreq_intvl[i][0] = 0;
273 sysfreq_intvl[i][1] = 0;
274 }
275 last_check_time = cur_time;
276
277 int_unlock(lock);
278 }
279 #endif
280