469 lines
15 KiB
Rust
469 lines
15 KiB
Rust
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)]
|
||
#[allow(non_camel_case_types)]
|
||
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.
|
||
HSI,
|
||
/// Use PLL1 as the system clock.
|
||
PLL1_R(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: PllSource,
|
||
/// 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 HSI / 1 * 10 / 1 = 160 MHz
|
||
pub const fn hsi_160mhz() -> Self {
|
||
PllConfig {
|
||
source: PllSource::HSI,
|
||
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: PllSource::MSIS(Msirange::RANGE_48MHZ),
|
||
m: Pllm::DIV3,
|
||
n: Plln::MUL10,
|
||
r: Plldiv::DIV1,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[derive(Clone, Copy)]
|
||
pub enum PllSource {
|
||
/// 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.
|
||
HSI,
|
||
}
|
||
|
||
impl Into<Pllsrc> for PllSource {
|
||
fn into(self) -> Pllsrc {
|
||
match self {
|
||
PllSource::MSIS(..) => Pllsrc::MSIS,
|
||
PllSource::HSE(..) => Pllsrc::HSE,
|
||
PllSource::HSI => Pllsrc::HSI,
|
||
}
|
||
}
|
||
}
|
||
|
||
impl Into<Sw> for ClockSrc {
|
||
fn into(self) -> Sw {
|
||
match self {
|
||
ClockSrc::MSI(..) => Sw::MSIS,
|
||
ClockSrc::HSE(..) => Sw::HSE,
|
||
ClockSrc::HSI => Sw::HSI,
|
||
ClockSrc::PLL1_R(..) => 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: Option<super::Hsi48Config>,
|
||
/// 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_hsi(&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::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: Some(Default::default()),
|
||
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::HSI => config.init_hsi(),
|
||
ClockSrc::PLL1_R(pll) => {
|
||
// Configure the PLL source
|
||
let source_clk = match pll.source {
|
||
PllSource::MSIS(range) => config.init_msis(range),
|
||
PllSource::HSE(hertz) => config.init_hse(hertz),
|
||
PllSource::HSI => config.init_hsi(),
|
||
};
|
||
|
||
// 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 PLL1_R 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
|
||
}
|
||
};
|
||
|
||
let _hsi48 = config.hsi48.map(super::init_hsi48);
|
||
|
||
// 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),
|
||
}
|
||
}
|