• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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