embassy/embassy-stm32/src/rcc/u5.rs
2023-10-16 05:35:29 +02:00

471 lines
15 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Msirange, Plldiv, Pllm, Plln, Ppre as APBPrescaler};
use crate::pac::rcc::vals::{Msirgsel, Pllmboost, Pllrge, Pllsrc, Sw};
use crate::pac::{FLASH, PWR, RCC};
use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz;
/// HSI speed
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
pub use crate::pac::pwr::vals::Vos as VoltageScale;
#[derive(Copy, Clone)]
pub enum ClockSrc {
/// Use an internal medium speed oscillator (MSIS) as the system clock.
MSI(Msirange),
/// Use the external high speed clock as the system clock.
///
/// HSE clocks faster than 25 MHz require at least `VoltageScale::RANGE3`, and HSE clocks must
/// never exceed 50 MHz.
HSE(Hertz),
/// Use the 16 MHz internal high speed oscillator as the system clock.
HSI16,
/// Use PLL1 as the system clock.
PLL1R(PllConfig),
}
impl Default for ClockSrc {
fn default() -> Self {
// The default system clock source is MSIS @ 4 MHz, per RM0456 § 11.4.9
ClockSrc::MSI(Msirange::RANGE_4MHZ)
}
}
#[derive(Clone, Copy)]
pub struct PllConfig {
/// The clock source for the PLL.
pub source: PllSrc,
/// The PLL prescaler.
///
/// The clock speed of the `source` divided by `m` must be between 4 and 16 MHz.
pub m: Pllm,
/// The PLL multiplier.
///
/// The multiplied clock `source` divided by `m` times `n` must be between 128 and 544
/// MHz. The upper limit may be lower depending on the `Config { voltage_range }`.
pub n: Plln,
/// The divider for the R output.
///
/// When used to drive the system clock, `source` divided by `m` times `n` divided by `r`
/// must not exceed 160 MHz. System clocks above 55 MHz require a non-default
/// `Config { voltage_range }`.
pub r: Plldiv,
}
impl PllConfig {
/// A configuration for HSI16 / 1 * 10 / 1 = 160 MHz
pub const fn hsi16_160mhz() -> Self {
PllConfig {
source: PllSrc::HSI16,
m: Pllm::DIV1,
n: Plln::MUL10,
r: Plldiv::DIV1,
}
}
/// A configuration for MSIS @ 48 MHz / 3 * 10 / 1 = 160 MHz
pub const fn msis_160mhz() -> Self {
PllConfig {
source: PllSrc::MSIS(Msirange::RANGE_48MHZ),
m: Pllm::DIV3,
n: Plln::MUL10,
r: Plldiv::DIV1,
}
}
}
#[derive(Clone, Copy)]
pub enum PllSrc {
/// Use an internal medium speed oscillator as the PLL source.
MSIS(Msirange),
/// Use the external high speed clock as the system PLL source.
///
/// HSE clocks faster than 25 MHz require at least `VoltageScale::RANGE3`, and HSE clocks must
/// never exceed 50 MHz.
HSE(Hertz),
/// Use the 16 MHz internal high speed oscillator as the PLL source.
HSI16,
}
impl Into<Pllsrc> for PllSrc {
fn into(self) -> Pllsrc {
match self {
PllSrc::MSIS(..) => Pllsrc::MSIS,
PllSrc::HSE(..) => Pllsrc::HSE,
PllSrc::HSI16 => Pllsrc::HSI16,
}
}
}
impl Into<Sw> for ClockSrc {
fn into(self) -> Sw {
match self {
ClockSrc::MSI(..) => Sw::MSIS,
ClockSrc::HSE(..) => Sw::HSE,
ClockSrc::HSI16 => Sw::HSI16,
ClockSrc::PLL1R(..) => Sw::PLL1_R,
}
}
}
pub struct Config {
pub mux: ClockSrc,
pub ahb_pre: AHBPrescaler,
pub apb1_pre: APBPrescaler,
pub apb2_pre: APBPrescaler,
pub apb3_pre: APBPrescaler,
pub hsi48: bool,
/// The voltage range influences the maximum clock frequencies for different parts of the
/// device. In particular, system clocks exceeding 110 MHz require `RANGE1`, and system clocks
/// exceeding 55 MHz require at least `RANGE2`.
///
/// See RM0456 § 10.5.4 for a general overview and § 11.4.10 for clock source frequency limits.
pub voltage_range: VoltageScale,
pub ls: super::LsConfig,
}
impl Config {
unsafe fn init_hsi16(&self) -> Hertz {
RCC.cr().write(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
HSI_FREQ
}
unsafe fn init_hse(&self, frequency: Hertz) -> Hertz {
// Check frequency limits per RM456 § 11.4.10
match self.voltage_range {
VoltageScale::RANGE1 | VoltageScale::RANGE2 | VoltageScale::RANGE3 => {
assert!(frequency.0 <= 50_000_000);
}
VoltageScale::RANGE4 => {
assert!(frequency.0 <= 25_000_000);
}
}
// Enable HSE, and wait for it to stabilize
RCC.cr().write(|w| w.set_hseon(true));
while !RCC.cr().read().hserdy() {}
frequency
}
unsafe fn init_msis(&self, range: Msirange) -> Hertz {
// Check MSI output per RM0456 § 11.4.10
match self.voltage_range {
VoltageScale::RANGE4 => {
assert!(msirange_to_hertz(range).0 <= 24_000_000);
}
_ => {}
}
// RM0456 § 11.8.2: spin until MSIS is off or MSIS is ready before setting its range
loop {
let cr = RCC.cr().read();
if cr.msison() == false || cr.msisrdy() == true {
break;
}
}
RCC.icscr1().modify(|w| {
w.set_msisrange(range);
w.set_msirgsel(Msirgsel::RCC_ICSCR1);
});
RCC.cr().write(|w| {
w.set_msipllen(false);
w.set_msison(true);
});
while !RCC.cr().read().msisrdy() {}
msirange_to_hertz(range)
}
}
impl Default for Config {
fn default() -> Self {
Self {
mux: ClockSrc::default(),
ahb_pre: AHBPrescaler::DIV1,
apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1,
apb3_pre: APBPrescaler::DIV1,
hsi48: true,
voltage_range: VoltageScale::RANGE3,
ls: Default::default(),
}
}
}
pub(crate) unsafe fn init(config: Config) {
// Ensure PWR peripheral clock is enabled
RCC.ahb3enr().modify(|w| {
w.set_pwren(true);
});
RCC.ahb3enr().read(); // synchronize
// Set the requested power mode
PWR.vosr().modify(|w| {
w.set_vos(config.voltage_range);
});
while !PWR.vosr().read().vosrdy() {}
let sys_clk = match config.mux {
ClockSrc::MSI(range) => config.init_msis(range),
ClockSrc::HSE(freq) => config.init_hse(freq),
ClockSrc::HSI16 => config.init_hsi16(),
ClockSrc::PLL1R(pll) => {
// Configure the PLL source
let source_clk = match pll.source {
PllSrc::MSIS(range) => config.init_msis(range),
PllSrc::HSE(hertz) => config.init_hse(hertz),
PllSrc::HSI16 => config.init_hsi16(),
};
// Calculate the reference clock, which is the source divided by m
let reference_clk = source_clk / pll.m;
// Check limits per RM0456 § 11.4.6
assert!(Hertz::mhz(4) <= reference_clk && reference_clk <= Hertz::mhz(16));
// Calculate the PLL1 VCO clock and PLL1 R output clock
let pll1_clk = reference_clk * pll.n;
let pll1r_clk = pll1_clk / pll.r;
// Check system clock per RM0456 § 11.4.9
assert!(pll1r_clk <= Hertz::mhz(160));
// Check PLL clocks per RM0456 § 11.4.10
match config.voltage_range {
VoltageScale::RANGE1 => {
assert!(pll1_clk >= Hertz::mhz(128) && pll1_clk <= Hertz::mhz(544));
assert!(pll1r_clk <= Hertz::mhz(208));
}
VoltageScale::RANGE2 => {
assert!(pll1_clk >= Hertz::mhz(128) && pll1_clk <= Hertz::mhz(544));
assert!(pll1r_clk <= Hertz::mhz(110));
}
VoltageScale::RANGE3 => {
assert!(pll1_clk >= Hertz::mhz(128) && pll1_clk <= Hertz::mhz(330));
assert!(pll1r_clk <= Hertz::mhz(55));
}
VoltageScale::RANGE4 => {
panic!("PLL is unavailable in voltage range 4");
}
}
// § 10.5.4: if we're targeting >= 55 MHz, we must configure PLL1MBOOST to a prescaler
// value that results in an output between 4 and 16 MHz for the PWR EPOD boost
let mboost = if pll1r_clk >= Hertz::mhz(55) {
// source_clk can be up to 50 MHz, so there's just a few cases:
if source_clk > Hertz::mhz(32) {
// Divide by 4, giving EPOD 8-12.5 MHz
Pllmboost::DIV4
} else if source_clk > Hertz::mhz(16) {
// Divide by 2, giving EPOD 8-16 MHz
Pllmboost::DIV2
} else {
// Bypass, giving EPOD 4-16 MHz
Pllmboost::DIV1
}
} else {
// Nothing to do
Pllmboost::DIV1
};
// Disable the PLL, and wait for it to disable
RCC.cr().modify(|w| w.set_pllon(0, false));
while RCC.cr().read().pllrdy(0) {}
// Configure the PLL
RCC.pll1cfgr().write(|w| {
// Configure PLL1 source and prescaler
w.set_pllsrc(pll.source.into());
w.set_pllm(pll.m);
// Configure PLL1 input frequncy range
let input_range = if reference_clk <= Hertz::mhz(8) {
Pllrge::FREQ_4TO8MHZ
} else {
Pllrge::FREQ_8TO16MHZ
};
w.set_pllrge(input_range);
// Set the prescaler for PWR EPOD
w.set_pllmboost(mboost);
// Enable PLL1R output
w.set_pllren(true);
});
// Configure the PLL divisors
RCC.pll1divr().modify(|w| {
// Set the VCO multiplier
w.set_plln(pll.n);
// Set the R output divisor
w.set_pllr(pll.r);
});
// Do we need the EPOD booster to reach the target clock speed per § 10.5.4?
if pll1r_clk >= Hertz::mhz(55) {
// Enable the booster
PWR.vosr().modify(|w| {
w.set_boosten(true);
});
while !PWR.vosr().read().boostrdy() {}
}
// Enable the PLL
RCC.cr().modify(|w| w.set_pllon(0, true));
while !RCC.cr().read().pllrdy(0) {}
pll1r_clk
}
};
if config.hsi48 {
RCC.cr().modify(|w| w.set_hsi48on(true));
while !RCC.cr().read().hsi48rdy() {}
}
// The clock source is ready
// Calculate and set the flash wait states
let wait_states = match config.voltage_range {
// VOS 1 range VCORE 1.26V - 1.40V
VoltageScale::RANGE1 => {
if sys_clk.0 < 32_000_000 {
0
} else if sys_clk.0 < 64_000_000 {
1
} else if sys_clk.0 < 96_000_000 {
2
} else if sys_clk.0 < 128_000_000 {
3
} else {
4
}
}
// VOS 2 range VCORE 1.15V - 1.26V
VoltageScale::RANGE2 => {
if sys_clk.0 < 30_000_000 {
0
} else if sys_clk.0 < 60_000_000 {
1
} else if sys_clk.0 < 90_000_000 {
2
} else {
3
}
}
// VOS 3 range VCORE 1.05V - 1.15V
VoltageScale::RANGE3 => {
if sys_clk.0 < 24_000_000 {
0
} else if sys_clk.0 < 48_000_000 {
1
} else {
2
}
}
// VOS 4 range VCORE 0.95V - 1.05V
VoltageScale::RANGE4 => {
if sys_clk.0 < 12_000_000 {
0
} else {
1
}
}
};
FLASH.acr().modify(|w| {
w.set_latency(wait_states);
});
// Switch the system clock source
RCC.cfgr1().modify(|w| {
w.set_sw(config.mux.into());
});
// RM0456 § 11.4.9 specifies maximum bus frequencies per voltage range, but the maximum bus
// frequency for each voltage range exactly matches the maximum permitted PLL output frequency.
// Given that:
//
// 1. Any bus frequency can never exceed the system clock frequency;
// 2. We checked the PLL output frequency if we're using it as a system clock;
// 3. The maximum HSE frequencies at each voltage range are lower than the bus limits, and
// we checked the HSE frequency if configured as a system clock; and
// 4. The maximum frequencies from the other clock sources are lower than the lowest bus
// frequency limit
//
// ...then we do not need to perform additional bus-related frequency checks.
// Configure the bus prescalers
RCC.cfgr2().modify(|w| {
w.set_hpre(config.ahb_pre);
w.set_ppre1(config.apb1_pre);
w.set_ppre2(config.apb2_pre);
});
RCC.cfgr3().modify(|w| {
w.set_ppre3(config.apb3_pre);
});
let ahb_freq = sys_clk / config.ahb_pre;
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
pre => {
let freq = ahb_freq / pre;
(freq, freq * 2u32)
}
};
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
pre => {
let freq = ahb_freq / pre;
(freq, freq * 2u32)
}
};
let (apb3_freq, _apb3_tim_freq) = match config.apb3_pre {
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
pre => {
let freq = ahb_freq / pre;
(freq, freq * 2u32)
}
};
let rtc = config.ls.init();
set_freqs(Clocks {
sys: sys_clk,
hclk1: ahb_freq,
hclk2: ahb_freq,
hclk3: ahb_freq,
pclk1: apb1_freq,
pclk2: apb2_freq,
pclk3: apb3_freq,
pclk1_tim: apb1_tim_freq,
pclk2_tim: apb2_tim_freq,
rtc,
});
}
fn msirange_to_hertz(range: Msirange) -> Hertz {
match range {
Msirange::RANGE_48MHZ => Hertz(48_000_000),
Msirange::RANGE_24MHZ => Hertz(24_000_000),
Msirange::RANGE_16MHZ => Hertz(16_000_000),
Msirange::RANGE_12MHZ => Hertz(12_000_000),
Msirange::RANGE_4MHZ => Hertz(4_000_000),
Msirange::RANGE_2MHZ => Hertz(2_000_000),
Msirange::RANGE_1_33MHZ => Hertz(1_330_000),
Msirange::RANGE_1MHZ => Hertz(1_000_000),
Msirange::RANGE_3_072MHZ => Hertz(3_072_000),
Msirange::RANGE_1_536MHZ => Hertz(1_536_000),
Msirange::RANGE_1_024MHZ => Hertz(1_024_000),
Msirange::RANGE_768KHZ => Hertz(768_000),
Msirange::RANGE_400KHZ => Hertz(400_000),
Msirange::RANGE_200KHZ => Hertz(200_000),
Msirange::RANGE_133KHZ => Hertz(133_000),
Msirange::RANGE_100KHZ => Hertz(100_000),
}
}