sifli_hal/rcc/
clock_configure.rs1use core::sync::atomic::{compiler_fence, Ordering};
2
3use crate::pac::{HPSYS_RCC, HPSYS_AON, HPSYS_CFG, PMUC};
4use crate::time::Hertz;
5
6use super::{ClkSysSel, ClkPeriSel, UsbSel, TickSel};
7
8#[derive(Debug, Clone, Copy, PartialEq)]
11pub enum ConfigOption<T> {
12 Update(T),
14 Keep,
16}
17
18impl<T> ConfigOption<T> {
19 pub fn new(value: T) -> Self {
21 ConfigOption::Update(value)
22 }
23
24 pub fn keep() -> Self {
26 ConfigOption::Keep
27 }
28
29 pub fn is_update(&self) -> bool {
31 matches!(self, ConfigOption::Update(_))
32 }
33
34 pub fn is_keep(&self) -> bool {
36 matches!(self, ConfigOption::Keep)
37 }
38
39 pub fn apply(self, current: T) -> T {
41 match self {
42 ConfigOption::Update(new_value) => new_value,
43 ConfigOption::Keep => current,
44 }
45 }
46}
47
48pub struct Config {
49 pub hxt48_enable: ConfigOption<bool>,
51 pub hrc48_enable: ConfigOption<bool>,
53 pub dll1: ConfigOption<DllConfig>,
55 pub dll2: ConfigOption<DllConfig>,
58 pub clk_sys_sel: ConfigOption<ClkSysSel>,
60 pub hclk_div: ConfigOption<u8>,
63 pub pclk1_div: ConfigOption<u8>,
66 pub pclk2_div: ConfigOption<u8>,
69
70 pub usb: ConfigOption<UsbConfig>,
72 pub tick: ConfigOption<TickConfig>,
74 pub clk_peri_sel: ConfigOption<ClkPeriSel>,
76}
77
78pub struct DllConfig {
79 pub enable: bool,
81 pub stg: u8,
85 pub div2: bool,
87}
88
89pub struct UsbConfig {
90 pub sel: UsbSel,
92 pub div: u8,
95}
96
97pub struct TickConfig {
98 pub sel: TickSel,
100 pub div: u8,
103}
104
105impl Default for Config {
106 fn default() -> Self {
107 Self {
108 hxt48_enable: ConfigOption::new(true),
109 hrc48_enable: ConfigOption::new(false),
110 dll1: ConfigOption::new(DllConfig { enable: true, stg: 5, div2: false }),
111 dll2: ConfigOption::keep(),
112 clk_sys_sel: ConfigOption::new(ClkSysSel::Dll1),
113 hclk_div: ConfigOption::new(0),
114 pclk1_div: ConfigOption::new(0),
115 pclk2_div: ConfigOption::new(0),
116 usb: ConfigOption::new(UsbConfig { sel: UsbSel::ClkSys, div: 0 }),
117 tick: ConfigOption::new(TickConfig { sel: TickSel::ClkRtc, div: 0 }),
118 clk_peri_sel: ConfigOption::new(ClkPeriSel::Hxt48),
119 }
120 }
121}
122
123impl Config {
124 pub fn new_keep() -> Self {
125 Self {
126 hxt48_enable: ConfigOption::keep(),
127 hrc48_enable: ConfigOption::keep(),
128 dll1: ConfigOption::keep(),
129 dll2: ConfigOption::keep(),
130 clk_sys_sel: ConfigOption::keep(),
131 hclk_div: ConfigOption::keep(),
132 pclk1_div: ConfigOption::keep(),
133 pclk2_div: ConfigOption::keep(),
134 usb: ConfigOption::keep(),
135 tick: ConfigOption::keep(),
136 clk_peri_sel: ConfigOption::keep(),
137 }
138 }
139
140 pub unsafe fn apply(&self) {
152 if let ConfigOption::Update(enable) = self.hxt48_enable {
154 HPSYS_AON.acr().modify(|w| w.set_hxt48_req(enable));
155 while HPSYS_AON.acr().read().hxt48_rdy() != enable {}
156 }
157
158 if let ConfigOption::Update(enable) = self.hrc48_enable {
159 HPSYS_AON.acr().modify(|w| w.set_hrc48_req(enable));
160 while HPSYS_AON.acr().read().hrc48_rdy() != enable {}
161 }
162
163 if let ConfigOption::Update(div) = self.pclk1_div {
164 HPSYS_RCC.cfgr().modify(|w| w.set_pdiv1(div));
165 }
166 if let ConfigOption::Update(div) = self.pclk2_div {
167 HPSYS_RCC.cfgr().modify(|w| w.set_pdiv2(div));
168 }
169
170 if let ConfigOption::Update(usb_cfg) = &self.usb {
172 HPSYS_RCC.csr().modify(|w| w.set_sel_usbc(usb_cfg.sel));
173 HPSYS_RCC.usbcr().modify(|w| w.set_div(usb_cfg.div));
174 }
175
176 if let ConfigOption::Update(tick_cfg) = &self.tick {
178 HPSYS_RCC.csr().modify(|w| w.set_sel_tick(tick_cfg.sel));
179 HPSYS_RCC.cfgr().modify(|w| w.set_tickdiv(tick_cfg.div));
180 }
181
182 if let ConfigOption::Update(sel) = self.clk_peri_sel {
184 HPSYS_RCC.csr().modify(|w| w.set_sel_peri(sel));
185 }
186
187 if self.hclk_is_update() {
189 let hclk_freq = self.get_final_hclk_freq().unwrap();
190 let current_hclk_freq = super::get_hclk_freq().unwrap();
191
192 let config_hclk_fn = || self.config_hclk();
193 crate::pmu::dvfs::config_hcpu_dvfs(current_hclk_freq, hclk_freq, config_hclk_fn);
194 if ConfigOption::Update(ClkSysSel::Dll1) != self.clk_sys_sel {
195 self.config_dll1();
196 }
197 } else {
198 self.config_dll1();
199 }
200
201 self.config_dll2();
203 }
204
205 fn config_hclk(&self) {
206 if let ConfigOption::Update(sel) = self.clk_sys_sel {
208 match sel {
209 ClkSysSel::Hrc48 => if !self.get_final_hrc48_enable() {
210 panic!("clk_sys_sel is Hrc48, but hrc48 is disabled")
211 },
212 ClkSysSel::Hxt48 => if !self.get_final_hxt48_enable() {
213 panic!("clk_sys_sel is Hxt48, but hxt48 is disabled")
214 },
215 ClkSysSel::Dbl96 => todo!(),
216 ClkSysSel::Dll1 => if !self.get_final_dll1_enable() {
217 panic!("clk_sys_sel is dll1, but dll1 is disabled")
218 } else {
219 self.config_dll1();
220 }
221 }
222 HPSYS_RCC.csr().modify(|w| w.set_sel_sys(sel));
223 }
224
225 if let ConfigOption::Update(div) = self.hclk_div {
227 HPSYS_RCC.cfgr().modify(|w| w.set_hdiv(div));
228 }
229 }
230
231 fn config_dll1(&self) {
232 if let ConfigOption::Update(dll1) = &self.dll1 {
233 let old_clk_sys_sel = super::get_clk_sys_source();
234 if old_clk_sys_sel == ClkSysSel::Dll1 {
235 if !dll1.enable {
236 panic!("Disabling DLL1 while it is the current clk_sys source is not allowed");
237 }
238 if super::get_hxt48_freq().is_some() {
240 HPSYS_RCC.csr().modify(|w| w.set_sel_sys(ClkSysSel::Hxt48));
241 } else if super::get_hrc48_freq().is_some() {
242 HPSYS_RCC.csr().modify(|w| w.set_sel_sys(ClkSysSel::Hrc48));
243 } else {
244 panic!();
245 }
246 }
247 if dll1.enable {
248 rcc_assert!(max::DLL.contains(
249 &self.get_final_dll1_freq().unwrap()
250 ));
251
252 PMUC.hxt_cr1().modify(|w| w.set_buf_dll_en(true));
253
254 HPSYS_CFG.cau2_cr().modify(|w| {
255 if !w.hpbg_en() { w.set_hpbg_en(true);
257 }
258 if !w.hpbg_vddpsw_en() {
259 w.set_hpbg_vddpsw_en(true);
260 }
261 });
262 HPSYS_RCC.dllcr(0).modify(|w| w.set_en(false));
263 compiler_fence(Ordering::SeqCst);
264 HPSYS_RCC.dllcr(0).modify(|w| {
266 w.set_en(true);
267 w.set_stg(dll1.stg);
268 w.set_out_div2_en(dll1.div2);
269 });
270 crate::cortex_m_blocking_delay_us(10);
272 while !HPSYS_RCC.dllcr(0).read().ready() {}
273
274 if old_clk_sys_sel == ClkSysSel::Dll1 {
275 HPSYS_RCC.csr().modify(|w| w.set_sel_sys(ClkSysSel::Dll1));
276 }
277 } else {
278 HPSYS_RCC.dllcr(0).modify(|w| w.set_en(false));
280 }
281 }
282 }
283
284 fn config_dll2(&self) {
285 if let ConfigOption::Update(_dll2) = &self.dll2 {
287 todo!("MPI uses DLL2, so we cannot change it simply");
288 }
328 }
329
330 fn get_final_dll1_freq(&self) -> Option<Hertz> {
331 if let ConfigOption::Update(dll1) = &self.dll1 {
332 if dll1.enable {
333 Some(Hertz((dll1.stg + 1) as u32 * 24_000_000 / (dll1.div2 as u32 + 1)))
334 } else {
335 None
336 }
337 } else {
338 super::get_clk_dll1_freq()
339 }
340 }
341
342 fn get_final_clk_sys_freq(&self) -> Option<Hertz> {
355 match self.clk_sys_sel {
356 ConfigOption::Update(ClkSysSel::Hxt48) => Some(Hertz(48_000_000)),
357 ConfigOption::Update(ClkSysSel::Hrc48) => Some(Hertz(48_000_000)),
358 ConfigOption::Update(ClkSysSel::Dll1) => self.get_final_dll1_freq(),
359 ConfigOption::Update(ClkSysSel::Dbl96) => todo!(),
360 ConfigOption::Keep => super::get_clk_sys_freq(),
361 }
362 }
363
364 fn get_final_hclk_freq(&self) -> Option<Hertz> {
365 if self.hclk_is_update() {
366 let hclk_div = match self.hclk_div {
367 ConfigOption::Update(div) => div,
368 ConfigOption::Keep => super::get_hclk_div()
369 };
370 let clk_sys = self.get_final_clk_sys_freq()?;
371 Some(clk_sys / hclk_div as u32)
372
373 } else {
374 super::get_hclk_freq()
375 }
376 }
377
378 fn hclk_is_update(&self) -> bool {
379 if self.hclk_div.is_update() || self.clk_sys_sel.is_update() {
380 return true
381 }
382
383 match super::get_clk_sys_source() {
384 ClkSysSel::Hrc48 => false,
385 ClkSysSel::Hxt48 => false,
386 ClkSysSel::Dbl96 => todo!(),
387 ClkSysSel::Dll1 => self.dll1.is_update()
388 }
389 }
390
391 fn get_final_dll1_enable(&self) -> bool {
392 if let ConfigOption::Update(dll1) = &self.dll1 {
393 dll1.enable
394 } else {
395 super::get_clk_dll1_freq().is_some()
396 }
397 }
398
399 fn get_final_hxt48_enable(&self) -> bool {
400 if let ConfigOption::Update(enable) = self.hxt48_enable {
401 enable
402 } else {
403 super::get_hxt48_freq().is_some()
404 }
405 }
406
407 fn get_final_hrc48_enable(&self) -> bool {
408 if let ConfigOption::Update(enable) = self.hrc48_enable {
409 enable
410 } else {
411 super::get_hrc48_freq().is_some()
412 }
413 }
414}
415
416#[cfg(feature = "sf32lb52x")]
417mod max {
418 use core::ops::RangeInclusive;
419 use crate::time::Hertz;
420
421 pub(crate) const DLL: RangeInclusive<Hertz> = Hertz(24_000_000)..=Hertz(384_000_000);
422}