1 | pub use crate::pac::pwr::vals::Vos as VoltageScale; |
2 | #[cfg (all(peri_usb_otg_hs))] |
3 | pub use crate::pac::rcc::vals::Otghssel; |
4 | pub use crate::pac::rcc::vals::{ |
5 | Hpre as AHBPrescaler, Msirange, Msirange as MSIRange, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul, |
6 | Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, |
7 | }; |
8 | use crate::pac::rcc::vals::{Hseext, Msirgsel, Pllmboost, Pllrge}; |
9 | #[cfg (all(peri_usb_otg_hs))] |
10 | pub use crate::pac::{syscfg::vals::Usbrefcksel, SYSCFG}; |
11 | use crate::pac::{FLASH, PWR, RCC}; |
12 | use crate::rcc::LSI_FREQ; |
13 | use crate::time::Hertz; |
14 | |
15 | /// HSI speed |
16 | pub const HSI_FREQ: Hertz = Hertz(16_000_000); |
17 | |
18 | #[derive (Clone, Copy, Eq, PartialEq)] |
19 | pub enum HseMode { |
20 | /// crystal/ceramic oscillator (HSEBYP=0) |
21 | Oscillator, |
22 | /// external analog clock (low swing) (HSEBYP=1, HSEEXT=0) |
23 | Bypass, |
24 | /// external digital clock (full swing) (HSEBYP=1, HSEEXT=1) |
25 | BypassDigital, |
26 | } |
27 | |
28 | #[derive (Clone, Copy, Eq, PartialEq)] |
29 | pub struct Hse { |
30 | /// HSE frequency. |
31 | pub freq: Hertz, |
32 | /// HSE mode. |
33 | pub mode: HseMode, |
34 | } |
35 | |
36 | #[derive (Clone, Copy)] |
37 | pub struct Pll { |
38 | /// The clock source for the PLL. |
39 | pub source: PllSource, |
40 | /// The PLL pre-divider. |
41 | /// |
42 | /// The clock speed of the `source` divided by `m` must be between 4 and 16 MHz. |
43 | pub prediv: PllPreDiv, |
44 | /// The PLL multiplier. |
45 | /// |
46 | /// The multiplied clock – `source` divided by `m` times `n` – must be between 128 and 544 |
47 | /// MHz. The upper limit may be lower depending on the `Config { voltage_range }`. |
48 | pub mul: PllMul, |
49 | /// The divider for the P output. |
50 | /// |
51 | /// The P output is one of several options |
52 | /// that can be used to feed the SAI/MDF/ADF Clock mux's. |
53 | pub divp: Option<PllDiv>, |
54 | /// The divider for the Q output. |
55 | /// |
56 | /// The Q ouput is one of severals options that can be used to feed the 48MHz clocks |
57 | /// and the OCTOSPI clock. It may also be used on the MDF/ADF clock mux's. |
58 | pub divq: Option<PllDiv>, |
59 | /// The divider for the R output. |
60 | /// |
61 | /// When used to drive the system clock, `source` divided by `m` times `n` divided by `r` |
62 | /// must not exceed 160 MHz. System clocks above 55 MHz require a non-default |
63 | /// `Config { voltage_range }`. |
64 | pub divr: Option<PllDiv>, |
65 | } |
66 | |
67 | #[derive (Clone, Copy)] |
68 | pub struct Config { |
69 | // base clock sources |
70 | pub msis: Option<MSIRange>, |
71 | pub msik: Option<MSIRange>, |
72 | pub hsi: bool, |
73 | pub hse: Option<Hse>, |
74 | pub hsi48: Option<super::Hsi48Config>, |
75 | |
76 | // pll |
77 | pub pll1: Option<Pll>, |
78 | pub pll2: Option<Pll>, |
79 | pub pll3: Option<Pll>, |
80 | |
81 | // sysclk, buses. |
82 | pub sys: Sysclk, |
83 | pub ahb_pre: AHBPrescaler, |
84 | pub apb1_pre: APBPrescaler, |
85 | pub apb2_pre: APBPrescaler, |
86 | pub apb3_pre: APBPrescaler, |
87 | |
88 | /// The voltage range influences the maximum clock frequencies for different parts of the |
89 | /// device. In particular, system clocks exceeding 110 MHz require `RANGE1`, and system clocks |
90 | /// exceeding 55 MHz require at least `RANGE2`. |
91 | /// |
92 | /// See RM0456 § 10.5.4 for a general overview and § 11.4.10 for clock source frequency limits. |
93 | pub voltage_range: VoltageScale, |
94 | pub ls: super::LsConfig, |
95 | |
96 | /// Per-peripheral kernel clock selection muxes |
97 | pub mux: super::mux::ClockMux, |
98 | } |
99 | |
100 | impl Default for Config { |
101 | fn default() -> Self { |
102 | Self { |
103 | msis: Some(Msirange::RANGE_4MHZ), |
104 | msik: Some(Msirange::RANGE_4MHZ), |
105 | hse: None, |
106 | hsi: false, |
107 | hsi48: Some(Default::default()), |
108 | pll1: None, |
109 | pll2: None, |
110 | pll3: None, |
111 | sys: Sysclk::MSIS, |
112 | ahb_pre: AHBPrescaler::DIV1, |
113 | apb1_pre: APBPrescaler::DIV1, |
114 | apb2_pre: APBPrescaler::DIV1, |
115 | apb3_pre: APBPrescaler::DIV1, |
116 | voltage_range: VoltageScale::RANGE1, |
117 | ls: Default::default(), |
118 | mux: Default::default(), |
119 | } |
120 | } |
121 | } |
122 | |
123 | pub(crate) unsafe fn init(config: Config) { |
124 | // Set the requested power mode |
125 | PWR.vosr().modify(|w| w.set_vos(config.voltage_range)); |
126 | while !PWR.vosr().read().vosrdy() {} |
127 | |
128 | let msis = config.msis.map(|range| { |
129 | // Check MSI output per RM0456 § 11.4.10 |
130 | match config.voltage_range { |
131 | VoltageScale::RANGE4 => { |
132 | assert!(msirange_to_hertz(range).0 <= 24_000_000); |
133 | } |
134 | _ => {} |
135 | } |
136 | |
137 | // RM0456 § 11.8.2: spin until MSIS is off or MSIS is ready before setting its range |
138 | loop { |
139 | let cr = RCC.cr().read(); |
140 | if cr.msison() == false || cr.msisrdy() == true { |
141 | break; |
142 | } |
143 | } |
144 | |
145 | RCC.icscr1().modify(|w| { |
146 | w.set_msisrange(range); |
147 | w.set_msirgsel(Msirgsel::ICSCR1); |
148 | }); |
149 | RCC.cr().write(|w| { |
150 | w.set_msipllen(false); |
151 | w.set_msison(true); |
152 | }); |
153 | while !RCC.cr().read().msisrdy() {} |
154 | msirange_to_hertz(range) |
155 | }); |
156 | |
157 | let msik = config.msik.map(|range| { |
158 | // Check MSI output per RM0456 § 11.4.10 |
159 | match config.voltage_range { |
160 | VoltageScale::RANGE4 => { |
161 | assert!(msirange_to_hertz(range).0 <= 24_000_000); |
162 | } |
163 | _ => {} |
164 | } |
165 | |
166 | // RM0456 § 11.8.2: spin until MSIS is off or MSIS is ready before setting its range |
167 | loop { |
168 | let cr = RCC.cr().read(); |
169 | if cr.msikon() == false || cr.msikrdy() == true { |
170 | break; |
171 | } |
172 | } |
173 | |
174 | RCC.icscr1().modify(|w| { |
175 | w.set_msikrange(range); |
176 | w.set_msirgsel(Msirgsel::ICSCR1); |
177 | }); |
178 | RCC.cr().write(|w| { |
179 | w.set_msikon(true); |
180 | }); |
181 | while !RCC.cr().read().msikrdy() {} |
182 | msirange_to_hertz(range) |
183 | }); |
184 | |
185 | let hsi = config.hsi.then(|| { |
186 | RCC.cr().write(|w| w.set_hsion(true)); |
187 | while !RCC.cr().read().hsirdy() {} |
188 | |
189 | HSI_FREQ |
190 | }); |
191 | |
192 | let hse = config.hse.map(|hse| { |
193 | // Check frequency limits per RM456 § 11.4.10 |
194 | match config.voltage_range { |
195 | VoltageScale::RANGE1 | VoltageScale::RANGE2 | VoltageScale::RANGE3 => { |
196 | assert!(hse.freq.0 <= 50_000_000); |
197 | } |
198 | VoltageScale::RANGE4 => { |
199 | assert!(hse.freq.0 <= 25_000_000); |
200 | } |
201 | } |
202 | |
203 | // Enable HSE, and wait for it to stabilize |
204 | RCC.cr().write(|w| { |
205 | w.set_hseon(true); |
206 | w.set_hsebyp(hse.mode != HseMode::Oscillator); |
207 | w.set_hseext(match hse.mode { |
208 | HseMode::Oscillator | HseMode::Bypass => Hseext::ANALOG, |
209 | HseMode::BypassDigital => Hseext::DIGITAL, |
210 | }); |
211 | }); |
212 | while !RCC.cr().read().hserdy() {} |
213 | |
214 | hse.freq |
215 | }); |
216 | |
217 | let hsi48 = config.hsi48.map(super::init_hsi48); |
218 | |
219 | let pll_input = PllInput { hse, hsi, msi: msis }; |
220 | let pll1 = init_pll(PllInstance::Pll1, config.pll1, &pll_input, config.voltage_range); |
221 | let pll2 = init_pll(PllInstance::Pll2, config.pll2, &pll_input, config.voltage_range); |
222 | let pll3 = init_pll(PllInstance::Pll3, config.pll3, &pll_input, config.voltage_range); |
223 | |
224 | let sys_clk = match config.sys { |
225 | Sysclk::HSE => hse.unwrap(), |
226 | Sysclk::HSI => hsi.unwrap(), |
227 | Sysclk::MSIS => msis.unwrap(), |
228 | Sysclk::PLL1_R => pll1.r.unwrap(), |
229 | }; |
230 | |
231 | // Do we need the EPOD booster to reach the target clock speed per § 10.5.4? |
232 | if sys_clk >= Hertz::mhz(55) { |
233 | // Enable the booster |
234 | PWR.vosr().modify(|w| w.set_boosten(true)); |
235 | while !PWR.vosr().read().boostrdy() {} |
236 | } |
237 | |
238 | // The clock source is ready |
239 | // Calculate and set the flash wait states |
240 | let wait_states = match config.voltage_range { |
241 | // VOS 1 range VCORE 1.26V - 1.40V |
242 | VoltageScale::RANGE1 => match sys_clk.0 { |
243 | ..=32_000_000 => 0, |
244 | ..=64_000_000 => 1, |
245 | ..=96_000_000 => 2, |
246 | ..=128_000_000 => 3, |
247 | _ => 4, |
248 | }, |
249 | // VOS 2 range VCORE 1.15V - 1.26V |
250 | VoltageScale::RANGE2 => match sys_clk.0 { |
251 | ..=30_000_000 => 0, |
252 | ..=60_000_000 => 1, |
253 | ..=90_000_000 => 2, |
254 | _ => 3, |
255 | }, |
256 | // VOS 3 range VCORE 1.05V - 1.15V |
257 | VoltageScale::RANGE3 => match sys_clk.0 { |
258 | ..=24_000_000 => 0, |
259 | ..=48_000_000 => 1, |
260 | _ => 2, |
261 | }, |
262 | // VOS 4 range VCORE 0.95V - 1.05V |
263 | VoltageScale::RANGE4 => match sys_clk.0 { |
264 | ..=12_000_000 => 0, |
265 | _ => 1, |
266 | }, |
267 | }; |
268 | FLASH.acr().modify(|w| { |
269 | w.set_latency(wait_states); |
270 | }); |
271 | |
272 | // Switch the system clock source |
273 | RCC.cfgr1().modify(|w| w.set_sw(config.sys)); |
274 | while RCC.cfgr1().read().sws() != config.sys {} |
275 | |
276 | // Configure the bus prescalers |
277 | RCC.cfgr2().modify(|w| { |
278 | w.set_hpre(config.ahb_pre); |
279 | w.set_ppre1(config.apb1_pre); |
280 | w.set_ppre2(config.apb2_pre); |
281 | }); |
282 | RCC.cfgr3().modify(|w| { |
283 | w.set_ppre3(config.apb3_pre); |
284 | }); |
285 | |
286 | let hclk = sys_clk / config.ahb_pre; |
287 | |
288 | let hclk_max = match config.voltage_range { |
289 | VoltageScale::RANGE1 => Hertz::mhz(160), |
290 | VoltageScale::RANGE2 => Hertz::mhz(110), |
291 | VoltageScale::RANGE3 => Hertz::mhz(55), |
292 | VoltageScale::RANGE4 => Hertz::mhz(25), |
293 | }; |
294 | assert!(hclk <= hclk_max); |
295 | |
296 | let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); |
297 | let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre); |
298 | let (pclk3, _) = super::util::calc_pclk(hclk, config.apb3_pre); |
299 | |
300 | let rtc = config.ls.init(); |
301 | |
302 | #[cfg (all(stm32u5, peri_usb_otg_hs))] |
303 | let usb_refck = match config.mux.otghssel { |
304 | Otghssel::HSE => hse, |
305 | Otghssel::HSE_DIV_2 => hse.map(|hse_val| hse_val / 2u8), |
306 | Otghssel::PLL1_P => pll1.p, |
307 | Otghssel::PLL1_P_DIV_2 => pll1.p.map(|pll1p_val| pll1p_val / 2u8), |
308 | }; |
309 | #[cfg (all(stm32u5, peri_usb_otg_hs))] |
310 | let usb_refck_sel = match usb_refck { |
311 | Some(clk_val) => match clk_val { |
312 | Hertz(16_000_000) => Usbrefcksel::MHZ16, |
313 | Hertz(19_200_000) => Usbrefcksel::MHZ19_2, |
314 | Hertz(20_000_000) => Usbrefcksel::MHZ20, |
315 | Hertz(24_000_000) => Usbrefcksel::MHZ24, |
316 | Hertz(26_000_000) => Usbrefcksel::MHZ26, |
317 | Hertz(32_000_000) => Usbrefcksel::MHZ32, |
318 | _ => panic!("cannot select OTG_HS reference clock with source frequency of {} Hz, must be one of 16, 19.2, 20, 24, 26, 32 MHz" , clk_val), |
319 | }, |
320 | None => Usbrefcksel::MHZ24, |
321 | }; |
322 | #[cfg (all(stm32u5, peri_usb_otg_hs))] |
323 | SYSCFG.otghsphycr().modify(|w| { |
324 | w.set_clksel(usb_refck_sel); |
325 | }); |
326 | |
327 | let lse = config.ls.lse.map(|l| l.frequency); |
328 | let lsi = config.ls.lsi.then_some(LSI_FREQ); |
329 | |
330 | config.mux.init(); |
331 | |
332 | set_clocks!( |
333 | sys: Some(sys_clk), |
334 | hclk1: Some(hclk), |
335 | hclk2: Some(hclk), |
336 | hclk3: Some(hclk), |
337 | pclk1: Some(pclk1), |
338 | pclk2: Some(pclk2), |
339 | pclk3: Some(pclk3), |
340 | pclk1_tim: Some(pclk1_tim), |
341 | pclk2_tim: Some(pclk2_tim), |
342 | msik: msik, |
343 | hsi48: hsi48, |
344 | rtc: rtc, |
345 | lse: lse, |
346 | lsi: lsi, |
347 | hse: hse, |
348 | hse_div_2: hse.map(|clk| clk / 2u32), |
349 | hsi: hsi, |
350 | pll1_p: pll1.p, |
351 | pll1_p_div_2: pll1.p.map(|clk| clk / 2u32), |
352 | pll1_q: pll1.q, |
353 | pll1_r: pll1.r, |
354 | pll2_p: pll2.p, |
355 | pll2_q: pll2.q, |
356 | pll2_r: pll2.r, |
357 | pll3_p: pll3.p, |
358 | pll3_q: pll3.q, |
359 | pll3_r: pll3.r, |
360 | |
361 | #[cfg (dsihost)] |
362 | dsi_phy: None, // DSI PLL clock not supported, don't call `RccPeripheral::frequency()` in the drivers |
363 | |
364 | // TODO |
365 | audioclk: None, |
366 | hsi48_div_2: None, |
367 | shsi: None, |
368 | shsi_div_2: None, |
369 | ); |
370 | } |
371 | |
372 | fn msirange_to_hertz(range: Msirange) -> Hertz { |
373 | match range { |
374 | Msirange::RANGE_48MHZ => Hertz(48_000_000), |
375 | Msirange::RANGE_24MHZ => Hertz(24_000_000), |
376 | Msirange::RANGE_16MHZ => Hertz(16_000_000), |
377 | Msirange::RANGE_12MHZ => Hertz(12_000_000), |
378 | Msirange::RANGE_4MHZ => Hertz(4_000_000), |
379 | Msirange::RANGE_2MHZ => Hertz(2_000_000), |
380 | Msirange::RANGE_1_33MHZ => Hertz(1_330_000), |
381 | Msirange::RANGE_1MHZ => Hertz(1_000_000), |
382 | Msirange::RANGE_3_072MHZ => Hertz(3_072_000), |
383 | Msirange::RANGE_1_536MHZ => Hertz(1_536_000), |
384 | Msirange::RANGE_1_024MHZ => Hertz(1_024_000), |
385 | Msirange::RANGE_768KHZ => Hertz(768_000), |
386 | Msirange::RANGE_400KHZ => Hertz(400_000), |
387 | Msirange::RANGE_200KHZ => Hertz(200_000), |
388 | Msirange::RANGE_133KHZ => Hertz(133_000), |
389 | Msirange::RANGE_100KHZ => Hertz(100_000), |
390 | } |
391 | } |
392 | |
393 | pub(super) struct PllInput { |
394 | pub hsi: Option<Hertz>, |
395 | pub hse: Option<Hertz>, |
396 | pub msi: Option<Hertz>, |
397 | } |
398 | |
399 | #[allow (unused)] |
400 | #[derive (Default)] |
401 | pub(super) struct PllOutput { |
402 | pub p: Option<Hertz>, |
403 | pub q: Option<Hertz>, |
404 | pub r: Option<Hertz>, |
405 | } |
406 | |
407 | #[derive (PartialEq, Eq, Clone, Copy)] |
408 | enum PllInstance { |
409 | Pll1 = 0, |
410 | Pll2 = 1, |
411 | Pll3 = 2, |
412 | } |
413 | |
414 | fn pll_enable(instance: PllInstance, enabled: bool) { |
415 | RCC.cr().modify(|w: &mut Cr| w.set_pllon(n:instance as _, val:enabled)); |
416 | while RCC.cr().read().pllrdy(instance as _) != enabled {} |
417 | } |
418 | |
419 | fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput, voltage_range: VoltageScale) -> PllOutput { |
420 | // Disable PLL |
421 | pll_enable(instance, false); |
422 | |
423 | let Some(pll) = config else { return PllOutput::default() }; |
424 | |
425 | let src_freq = match pll.source { |
426 | PllSource::DISABLE => panic!("must not select PLL source as DISABLE" ), |
427 | PllSource::HSE => unwrap!(input.hse), |
428 | PllSource::HSI => unwrap!(input.hsi), |
429 | PllSource::MSIS => unwrap!(input.msi), |
430 | }; |
431 | |
432 | // Calculate the reference clock, which is the source divided by m |
433 | let ref_freq = src_freq / pll.prediv; |
434 | // Check limits per RM0456 § 11.4.6 |
435 | assert!(Hertz::mhz(4) <= ref_freq && ref_freq <= Hertz::mhz(16)); |
436 | |
437 | // Check PLL clocks per RM0456 § 11.4.10 |
438 | let (vco_min, vco_max, out_max) = match voltage_range { |
439 | VoltageScale::RANGE1 => (Hertz::mhz(128), Hertz::mhz(544), Hertz::mhz(208)), |
440 | VoltageScale::RANGE2 => (Hertz::mhz(128), Hertz::mhz(544), Hertz::mhz(110)), |
441 | VoltageScale::RANGE3 => (Hertz::mhz(128), Hertz::mhz(330), Hertz::mhz(55)), |
442 | VoltageScale::RANGE4 => panic!("PLL is unavailable in voltage range 4" ), |
443 | }; |
444 | |
445 | // Calculate the PLL VCO clock |
446 | let vco_freq = ref_freq * pll.mul; |
447 | assert!(vco_freq >= vco_min && vco_freq <= vco_max); |
448 | |
449 | // Calculate output clocks. |
450 | let p = pll.divp.map(|div| vco_freq / div); |
451 | let q = pll.divq.map(|div| vco_freq / div); |
452 | let r = pll.divr.map(|div| vco_freq / div); |
453 | for freq in [p, q, r] { |
454 | if let Some(freq) = freq { |
455 | assert!(freq <= out_max); |
456 | } |
457 | } |
458 | |
459 | let divr = match instance { |
460 | PllInstance::Pll1 => RCC.pll1divr(), |
461 | PllInstance::Pll2 => RCC.pll2divr(), |
462 | PllInstance::Pll3 => RCC.pll3divr(), |
463 | }; |
464 | divr.write(|w| { |
465 | w.set_plln(pll.mul); |
466 | w.set_pllp(pll.divp.unwrap_or(PllDiv::DIV1)); |
467 | w.set_pllq(pll.divq.unwrap_or(PllDiv::DIV1)); |
468 | w.set_pllr(pll.divr.unwrap_or(PllDiv::DIV1)); |
469 | }); |
470 | |
471 | let input_range = match ref_freq.0 { |
472 | ..=8_000_000 => Pllrge::FREQ_4TO8MHZ, |
473 | _ => Pllrge::FREQ_8TO16MHZ, |
474 | }; |
475 | |
476 | macro_rules! write_fields { |
477 | ($w:ident) => { |
478 | $w.set_pllpen(pll.divp.is_some()); |
479 | $w.set_pllqen(pll.divq.is_some()); |
480 | $w.set_pllren(pll.divr.is_some()); |
481 | $w.set_pllm(pll.prediv); |
482 | $w.set_pllsrc(pll.source); |
483 | $w.set_pllrge(input_range); |
484 | }; |
485 | } |
486 | |
487 | match instance { |
488 | PllInstance::Pll1 => RCC.pll1cfgr().write(|w| { |
489 | // § 10.5.4: if we're targeting >= 55 MHz, we must configure PLL1MBOOST to a prescaler |
490 | // value that results in an output between 4 and 16 MHz for the PWR EPOD boost |
491 | if r.unwrap() >= Hertz::mhz(55) { |
492 | // source_clk can be up to 50 MHz, so there's just a few cases: |
493 | let mboost = match src_freq.0 { |
494 | ..=16_000_000 => Pllmboost::DIV1, // Bypass, giving EPOD 4-16 MHz |
495 | ..=32_000_000 => Pllmboost::DIV2, // Divide by 2, giving EPOD 8-16 MHz |
496 | _ => Pllmboost::DIV4, // Divide by 4, giving EPOD 8-12.5 MHz |
497 | }; |
498 | w.set_pllmboost(mboost); |
499 | } |
500 | write_fields!(w); |
501 | }), |
502 | PllInstance::Pll2 => RCC.pll2cfgr().write(|w| { |
503 | write_fields!(w); |
504 | }), |
505 | PllInstance::Pll3 => RCC.pll3cfgr().write(|w| { |
506 | write_fields!(w); |
507 | }), |
508 | } |
509 | |
510 | // Enable PLL |
511 | pll_enable(instance, true); |
512 | |
513 | PllOutput { p, q, r } |
514 | } |
515 | |