diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 1354ccd27..67439fda3 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -1,51 +1,96 @@ +use core::marker::PhantomData; +use core::sync::atomic::{AtomicU16, AtomicU32, Ordering}; + use embassy_hal_common::{into_ref, PeripheralRef}; use pac::clocks::vals::*; +use crate::gpio::sealed::Pin; +use crate::gpio::AnyPin; use crate::{pac, reset, Peripheral}; -// TODO fix terrible use of global here -static mut XIN_HZ: u32 = 0; +// NOTE: all gpin handling is commented out for future reference. +// gpin is not usually safe to use during the boot init() call, so it won't +// be very useful until we have runtime clock reconfiguration. once this +// happens we can resurrect the commented-out gpin bits. +struct Clocks { + xosc: AtomicU32, + sys: AtomicU32, + reference: AtomicU32, + pll_sys: AtomicU32, + pll_usb: AtomicU32, + usb: AtomicU32, + adc: AtomicU32, + // gpin0: AtomicU32, + // gpin1: AtomicU32, + rosc: AtomicU32, + peri: AtomicU32, + rtc: AtomicU16, +} -pub use rp_pac::clocks::vals::{ - ClkAdcCtrlAuxsrc as AdcAuxsrc, ClkGpoutCtrlAuxsrc as GpoutSrc, ClkPeriCtrlAuxsrc as PeriClkAuxsrc, - ClkRefCtrlAuxsrc as RefAuxsrc, ClkRtcCtrlAuxsrc as RtcAuxsrc, ClkSysCtrlAuxsrc as SysAuxsrc, - ClkUsbCtrlAuxsrc as UsbAuxsrc, +static CLOCKS: Clocks = Clocks { + xosc: AtomicU32::new(0), + sys: AtomicU32::new(0), + reference: AtomicU32::new(0), + pll_sys: AtomicU32::new(0), + pll_usb: AtomicU32::new(0), + usb: AtomicU32::new(0), + adc: AtomicU32::new(0), + // gpin0: AtomicU32::new(0), + // gpin1: AtomicU32::new(0), + rosc: AtomicU32::new(0), + peri: AtomicU32::new(0), + rtc: AtomicU16::new(0), }; +#[repr(u8)] +#[non_exhaustive] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum PeriClkSrc { + Sys = ClkPeriCtrlAuxsrc::CLK_SYS.0, + PllSys = ClkPeriCtrlAuxsrc::CLKSRC_PLL_SYS.0, + PllUsb = ClkPeriCtrlAuxsrc::CLKSRC_PLL_USB.0, + Rosc = ClkPeriCtrlAuxsrc::ROSC_CLKSRC_PH.0, + Xosc = ClkPeriCtrlAuxsrc::XOSC_CLKSRC.0, + // Gpin0 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN0.0, + // Gpin1 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN1.0, +} + #[non_exhaustive] pub struct ClockConfig { pub rosc: Option, pub xosc: Option, pub ref_clk: RefClkConfig, pub sys_clk: SysClkConfig, - pub peri_clk_src: Option, + pub peri_clk_src: Option, pub usb_clk: Option, pub adc_clk: Option, pub rtc_clk: Option, + // gpin0: Option<(u32, Gpin<'static, AnyPin>)>, + // gpin1: Option<(u32, Gpin<'static, AnyPin>)>, } impl ClockConfig { pub fn crystal(crystal_hz: u32) -> Self { Self { rosc: Some(RoscConfig { - range: pac::rosc::vals::FreqRange::MEDIUM, + hz: 6_500_000, + range: RoscRange::Medium, drive_strength: [0; 8], div: 16, }), xosc: Some(XoscConfig { hz: crystal_hz, - clock_type: ExternalClock::Crystal, sys_pll: Some(PllConfig { refdiv: 1, - vco_freq: 1500_000_000, + fbdiv: 125, post_div1: 6, post_div2: 2, }), usb_pll: Some(PllConfig { refdiv: 1, - vco_freq: 480_000_000, - post_div1: 5, - post_div2: 2, + fbdiv: 120, + post_div1: 6, + post_div2: 5, }), }), ref_clk: RefClkConfig { @@ -53,34 +98,40 @@ impl ClockConfig { div: 1, }, sys_clk: SysClkConfig { - src: SysClkSrc::Aux(ClkSysCtrlAuxsrc::CLKSRC_PLL_SYS), + src: SysClkSrc::PllSys, div_int: 1, div_frac: 0, }, - peri_clk_src: Some(ClkPeriCtrlAuxsrc::CLK_SYS), + peri_clk_src: Some(PeriClkSrc::Sys), + // CLK USB = PLL USB (48MHz) / 1 = 48MHz usb_clk: Some(UsbClkConfig { - src: ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB, + src: UsbClkSrc::PllUsb, div: 1, phase: 0, }), + // CLK ADC = PLL USB (48MHZ) / 1 = 48MHz adc_clk: Some(AdcClkConfig { - src: ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB, + src: AdcClkSrc::PllUsb, div: 1, phase: 0, }), + // CLK RTC = PLL USB (48MHz) / 1024 = 46875Hz rtc_clk: Some(RtcClkConfig { - src: ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB, + src: RtcClkSrc::PllUsb, div_int: 1024, div_frac: 0, phase: 0, }), + // gpin0: None, + // gpin1: None, } } pub fn rosc() -> Self { Self { rosc: Some(RoscConfig { - range: pac::rosc::vals::FreqRange::HIGH, + hz: 140_000_000, + range: RoscRange::High, drive_strength: [0; 8], div: 1, }), @@ -90,65 +141,104 @@ impl ClockConfig { div: 1, }, sys_clk: SysClkConfig { - src: SysClkSrc::Aux(ClkSysCtrlAuxsrc::ROSC_CLKSRC), + src: SysClkSrc::Rosc, div_int: 1, div_frac: 0, }, - peri_clk_src: Some(ClkPeriCtrlAuxsrc::ROSC_CLKSRC_PH), + peri_clk_src: Some(PeriClkSrc::Rosc), usb_clk: None, + // CLK ADC = ROSC (140MHz) / 3 ≅ 48MHz adc_clk: Some(AdcClkConfig { - src: ClkAdcCtrlAuxsrc::ROSC_CLKSRC_PH, - div: 1, + src: AdcClkSrc::Rosc, + div: 3, phase: 0, }), + // CLK RTC = ROSC (140MHz) / 2986.667969 ≅ 46875Hz rtc_clk: Some(RtcClkConfig { - src: ClkRtcCtrlAuxsrc::ROSC_CLKSRC_PH, - div_int: 1024, - div_frac: 0, + src: RtcClkSrc::Rosc, + div_int: 2986, + div_frac: 171, phase: 0, }), + // gpin0: None, + // gpin1: None, } } + + // pub fn bind_gpin(&mut self, gpin: Gpin<'static, P>, hz: u32) { + // match P::NR { + // 0 => self.gpin0 = Some((hz, gpin.map_into())), + // 1 => self.gpin1 = Some((hz, gpin.map_into())), + // _ => unreachable!(), + // } + // // pin is now provisionally bound. if the config is applied it must be forgotten, + // // or Gpin::drop will deconfigure the clock input. + // } +} + +#[repr(u16)] +#[non_exhaustive] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum RoscRange { + Low = pac::rosc::vals::FreqRange::LOW.0, + Medium = pac::rosc::vals::FreqRange::MEDIUM.0, + High = pac::rosc::vals::FreqRange::HIGH.0, + TooHigh = pac::rosc::vals::FreqRange::TOOHIGH.0, } pub struct RoscConfig { - pub range: pac::rosc::vals::FreqRange, + /// Final frequency of the oscillator, after the divider has been applied. + /// The oscillator has a nominal frequency of 6.5MHz at medium range with + /// divider 16 and all drive strengths set to 0, other values should be + /// measured in situ. + pub hz: u32, + pub range: RoscRange, pub drive_strength: [u8; 8], pub div: u16, } pub struct XoscConfig { pub hz: u32, - pub clock_type: ExternalClock, pub sys_pll: Option, pub usb_pll: Option, } pub struct PllConfig { - pub refdiv: u32, - pub vco_freq: u32, + pub refdiv: u8, + pub fbdiv: u16, pub post_div1: u8, pub post_div2: u8, } -pub enum ExternalClock { - Crystal, - Clock, -} pub struct RefClkConfig { pub src: RefClkSrc, pub div: u8, } +#[non_exhaustive] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum RefClkSrc { + // main sources Xosc, Rosc, - Aux(ClkRefCtrlAuxsrc), + // aux sources + PllUsb, + // Gpin0, + // Gpin1, } +#[non_exhaustive] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum SysClkSrc { + // main sources Ref, - Aux(ClkSysCtrlAuxsrc), + // aux sources + PllSys, + PllUsb, + Rosc, + Xosc, + // Gpin0, + // Gpin1, } pub struct SysClkConfig { @@ -157,20 +247,56 @@ pub struct SysClkConfig { pub div_frac: u8, } +#[repr(u8)] +#[non_exhaustive] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum UsbClkSrc { + PllUsb = ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB.0, + PllSys = ClkUsbCtrlAuxsrc::CLKSRC_PLL_SYS.0, + Rosc = ClkUsbCtrlAuxsrc::ROSC_CLKSRC_PH.0, + Xosc = ClkUsbCtrlAuxsrc::XOSC_CLKSRC.0, + // Gpin0 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN0.0, + // Gpin1 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN1.0, +} + pub struct UsbClkConfig { - pub src: ClkUsbCtrlAuxsrc, + pub src: UsbClkSrc, pub div: u8, pub phase: u8, } +#[repr(u8)] +#[non_exhaustive] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum AdcClkSrc { + PllUsb = ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB.0, + PllSys = ClkAdcCtrlAuxsrc::CLKSRC_PLL_SYS.0, + Rosc = ClkAdcCtrlAuxsrc::ROSC_CLKSRC_PH.0, + Xosc = ClkAdcCtrlAuxsrc::XOSC_CLKSRC.0, + // Gpin0 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN0.0, + // Gpin1 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN1.0, +} + pub struct AdcClkConfig { - pub src: ClkAdcCtrlAuxsrc, + pub src: AdcClkSrc, pub div: u8, pub phase: u8, } +#[repr(u8)] +#[non_exhaustive] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum RtcClkSrc { + PllUsb = ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB.0, + PllSys = ClkRtcCtrlAuxsrc::CLKSRC_PLL_SYS.0, + Rosc = ClkRtcCtrlAuxsrc::ROSC_CLKSRC_PH.0, + Xosc = ClkRtcCtrlAuxsrc::XOSC_CLKSRC.0, + // Gpin0 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN0.0, + // Gpin1 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN1.0, +} + pub struct RtcClkConfig { - pub src: ClkRtcCtrlAuxsrc, + pub src: RtcClkSrc, pub div_int: u32, pub div_frac: u8, pub phase: u8, @@ -184,6 +310,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { // - USB, SYSCFG (breaks usb-to-swd on core1) let mut peris = reset::ALL_PERIPHERALS; peris.set_io_qspi(false); + // peris.set_io_bank0(false); // might be suicide if we're clocked from gpin peris.set_pads_qspi(false); peris.set_pll_sys(false); peris.set_pll_usb(false); @@ -203,84 +330,108 @@ pub(crate) unsafe fn init(config: ClockConfig) { c.clk_ref_ctrl().modify(|w| w.set_src(ClkRefCtrlSrc::ROSC_CLKSRC_PH)); while c.clk_ref_selected().read() != 1 {} - if let Some(config) = config.rosc { - configure_rosc(config); - } + // Reset the PLLs + let mut peris = reset::Peripherals(0); + peris.set_pll_sys(true); + peris.set_pll_usb(true); + reset::reset(peris); + reset::unreset_wait(peris); - if let Some(config) = config.xosc { - XIN_HZ = config.hz; + // let gpin0_freq = config.gpin0.map_or(0, |p| { + // core::mem::forget(p.1); + // p.0 + // }); + // CLOCKS.gpin0.store(gpin0_freq, Ordering::Relaxed); + // let gpin1_freq = config.gpin1.map_or(0, |p| { + // core::mem::forget(p.1); + // p.0 + // }); + // CLOCKS.gpin1.store(gpin1_freq, Ordering::Relaxed); - pac::WATCHDOG.tick().write(|w| { - w.set_cycles((config.hz / 1_000_000) as u16); - w.set_enable(true); - }); + let rosc_freq = match config.rosc { + Some(config) => configure_rosc(config), + None => 0, + }; + CLOCKS.rosc.store(rosc_freq, Ordering::Relaxed); - // start XOSC - match config.clock_type { - ExternalClock::Crystal => start_xosc(config.hz), - // TODO The datasheet says the xosc needs to be put into a bypass mode to use an - // external clock, but is mum about how to do that. - ExternalClock::Clock => todo!(), - } + let (xosc_freq, pll_sys_freq, pll_usb_freq) = match config.xosc { + Some(config) => { + // start XOSC + // datasheet mentions support for clock inputs into XIN, but doesn't go into + // how this is achieved. pico-sdk doesn't support this at all. + start_xosc(config.hz); - if let Some(sys_pll_config) = config.sys_pll { - configure_pll(pac::PLL_SYS, config.hz, sys_pll_config); - } - if let Some(usb_pll_config) = config.usb_pll { - configure_pll(pac::PLL_USB, config.hz, usb_pll_config); - } - } + let pll_sys_freq = match config.sys_pll { + Some(sys_pll_config) => configure_pll(pac::PLL_SYS, config.hz, sys_pll_config), + None => 0, + }; + let pll_usb_freq = match config.usb_pll { + Some(usb_pll_config) => configure_pll(pac::PLL_USB, config.hz, usb_pll_config), + None => 0, + }; - match config.ref_clk.src { - RefClkSrc::Xosc => { - c.clk_ref_ctrl().write(|w| { - w.set_src(ClkRefCtrlSrc::XOSC_CLKSRC); - }); - while c.clk_ref_selected().read() != 1 << ClkRefCtrlSrc::XOSC_CLKSRC.0 {} + (config.hz, pll_sys_freq, pll_usb_freq) } - RefClkSrc::Rosc => { - c.clk_ref_ctrl().write(|w| { - w.set_src(ClkRefCtrlSrc::ROSC_CLKSRC_PH); - }); - while c.clk_ref_selected().read() != 1 << ClkRefCtrlSrc::ROSC_CLKSRC_PH.0 {} + None => (0, 0, 0), + }; + CLOCKS.xosc.store(xosc_freq, Ordering::Relaxed); + CLOCKS.pll_sys.store(pll_sys_freq, Ordering::Relaxed); + CLOCKS.pll_usb.store(pll_usb_freq, Ordering::Relaxed); + + let (ref_src, ref_aux, clk_ref_freq) = { + use {ClkRefCtrlAuxsrc as Aux, ClkRefCtrlSrc as Src}; + let div = config.ref_clk.div as u32; + assert!(div >= 1 && div <= 4); + match config.ref_clk.src { + RefClkSrc::Xosc => (Src::XOSC_CLKSRC, Aux::CLKSRC_PLL_USB, xosc_freq / div), + RefClkSrc::Rosc => (Src::ROSC_CLKSRC_PH, Aux::CLKSRC_PLL_USB, rosc_freq / div), + RefClkSrc::PllUsb => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_PLL_USB, pll_usb_freq / div), + // RefClkSrc::Gpin0 => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_GPIN0, gpin0_freq / div), + // RefClkSrc::Gpin1 => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_GPIN1, gpin1_freq / div), } - RefClkSrc::Aux(src) => { - c.clk_ref_ctrl().write(|w| { - w.set_auxsrc(src); - w.set_src(ClkRefCtrlSrc::CLKSRC_CLK_REF_AUX); - }); - while c.clk_ref_selected().read() != 1 << ClkRefCtrlSrc::CLKSRC_CLK_REF_AUX.0 {} - } - } + }; + assert!(clk_ref_freq != 0); + CLOCKS.reference.store(clk_ref_freq, Ordering::Relaxed); + c.clk_ref_ctrl().write(|w| { + w.set_src(ref_src); + w.set_auxsrc(ref_aux); + }); + while c.clk_ref_selected().read() != 1 << ref_src.0 {} c.clk_ref_div().write(|w| { w.set_int(config.ref_clk.div); }); pac::WATCHDOG.tick().write(|w| { - w.set_cycles((clk_ref_freq() / 1_000_000) as u16); + w.set_cycles((clk_ref_freq / 1_000_000) as u16); w.set_enable(true); }); - match config.sys_clk.src { - SysClkSrc::Ref => { - c.clk_sys_ctrl().write(|w| { - w.set_src(ClkSysCtrlSrc::CLK_REF); - }); - while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLK_REF.0 {} - } - SysClkSrc::Aux(src) => { - c.clk_sys_ctrl().write(|w| { - w.set_src(ClkSysCtrlSrc::CLK_REF); - }); - while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLK_REF.0 {} - - c.clk_sys_ctrl().write(|w| { - w.set_auxsrc(src); - w.set_src(ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX); - }); - while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX.0 {} - } + let (sys_src, sys_aux, clk_sys_freq) = { + use {ClkSysCtrlAuxsrc as Aux, ClkSysCtrlSrc as Src}; + let (src, aux, freq) = match config.sys_clk.src { + SysClkSrc::Ref => (Src::CLK_REF, Aux::CLKSRC_PLL_SYS, clk_ref_freq), + SysClkSrc::PllSys => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_SYS, pll_sys_freq), + SysClkSrc::PllUsb => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_USB, pll_usb_freq), + SysClkSrc::Rosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::ROSC_CLKSRC, rosc_freq), + SysClkSrc::Xosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::XOSC_CLKSRC, xosc_freq), + // SysClkSrc::Gpin0 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN0, gpin0_freq), + // SysClkSrc::Gpin1 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN1, gpin1_freq), + }; + assert!(config.sys_clk.div_int <= 0x1000000); + let div = config.sys_clk.div_int as u64 * 256 + config.sys_clk.div_frac as u64; + (src, aux, ((freq as u64 * 256) / div) as u32) + }; + assert!(clk_sys_freq != 0); + CLOCKS.sys.store(clk_sys_freq, Ordering::Relaxed); + if sys_src != ClkSysCtrlSrc::CLK_REF { + c.clk_sys_ctrl().write(|w| w.set_src(ClkSysCtrlSrc::CLK_REF)); + while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLK_REF.0 {} } + c.clk_sys_ctrl().write(|w| { + w.set_auxsrc(sys_aux); + w.set_src(sys_src); + }); + while c.clk_sys_selected().read() != 1 << sys_src.0 {} c.clk_sys_div().write(|w| { w.set_int(config.sys_clk.div_int); w.set_frac(config.sys_clk.div_frac); @@ -291,44 +442,74 @@ pub(crate) unsafe fn init(config: ClockConfig) { if let Some(src) = config.peri_clk_src { c.clk_peri_ctrl().write(|w| { w.set_enable(true); - w.set_auxsrc(src); + w.set_auxsrc(ClkPeriCtrlAuxsrc(src as _)); }); + let peri_freq = match src { + PeriClkSrc::Sys => clk_sys_freq, + PeriClkSrc::PllSys => pll_sys_freq, + PeriClkSrc::PllUsb => pll_usb_freq, + PeriClkSrc::Rosc => rosc_freq, + PeriClkSrc::Xosc => xosc_freq, + // PeriClkSrc::Gpin0 => gpin0_freq, + // PeriClkSrc::Gpin1 => gpin1_freq, + }; + assert!(peri_freq != 0); + CLOCKS.peri.store(peri_freq, Ordering::Relaxed); } else { peris.set_spi0(false); peris.set_spi1(false); peris.set_uart0(false); peris.set_uart1(false); + CLOCKS.peri.store(0, Ordering::Relaxed); } if let Some(conf) = config.usb_clk { - // CLK USB = PLL USB (48MHz) / 1 = 48MHz c.clk_usb_div().write(|w| w.set_int(conf.div)); c.clk_usb_ctrl().write(|w| { w.set_phase(conf.phase); w.set_enable(true); - w.set_auxsrc(conf.src); + w.set_auxsrc(ClkUsbCtrlAuxsrc(conf.src as _)); }); + let usb_freq = match conf.src { + UsbClkSrc::PllUsb => pll_usb_freq, + UsbClkSrc::PllSys => pll_sys_freq, + UsbClkSrc::Rosc => rosc_freq, + UsbClkSrc::Xosc => xosc_freq, + // UsbClkSrc::Gpin0 => gpin0_freq, + // UsbClkSrc::Gpin1 => gpin1_freq, + }; + assert!(usb_freq != 0); + assert!(conf.div >= 1 && conf.div <= 4); + CLOCKS.usb.store(usb_freq / conf.div as u32, Ordering::Relaxed); } else { peris.set_usbctrl(false); + CLOCKS.usb.store(0, Ordering::Relaxed); } if let Some(conf) = config.adc_clk { - // CLK ADC = PLL USB (48MHZ) / 1 = 48MHz c.clk_adc_div().write(|w| w.set_int(conf.div)); c.clk_adc_ctrl().write(|w| { w.set_phase(conf.phase); w.set_enable(true); - w.set_auxsrc(conf.src); + w.set_auxsrc(ClkAdcCtrlAuxsrc(conf.src as _)); }); + let adc_in_freq = match conf.src { + AdcClkSrc::PllUsb => pll_usb_freq, + AdcClkSrc::PllSys => pll_sys_freq, + AdcClkSrc::Rosc => rosc_freq, + AdcClkSrc::Xosc => xosc_freq, + // AdcClkSrc::Gpin0 => gpin0_freq, + // AdcClkSrc::Gpin1 => gpin1_freq, + }; + assert!(adc_in_freq != 0); + assert!(conf.div >= 1 && conf.div <= 4); + CLOCKS.adc.store(adc_in_freq / conf.div as u32, Ordering::Relaxed); } else { peris.set_adc(false); + CLOCKS.adc.store(0, Ordering::Relaxed); } if let Some(conf) = config.rtc_clk { - // CLK RTC = PLL USB (48MHz) / 1024 = 46875Hz - c.clk_rtc_ctrl().modify(|w| { - w.set_enable(false); - }); c.clk_rtc_div().write(|w| { w.set_int(conf.div_int); w.set_frac(conf.div_frac); @@ -336,17 +517,32 @@ pub(crate) unsafe fn init(config: ClockConfig) { c.clk_rtc_ctrl().write(|w| { w.set_phase(conf.phase); w.set_enable(true); - w.set_auxsrc(conf.src); + w.set_auxsrc(ClkRtcCtrlAuxsrc(conf.src as _)); }); + let rtc_in_freq = match conf.src { + RtcClkSrc::PllUsb => pll_usb_freq, + RtcClkSrc::PllSys => pll_sys_freq, + RtcClkSrc::Rosc => rosc_freq, + RtcClkSrc::Xosc => xosc_freq, + // RtcClkSrc::Gpin0 => gpin0_freq, + // RtcClkSrc::Gpin1 => gpin1_freq, + }; + assert!(rtc_in_freq != 0); + assert!(config.sys_clk.div_int <= 0x1000000); + CLOCKS.rtc.store( + ((rtc_in_freq as u64 * 256) / (conf.div_int as u64 * 256 + conf.div_frac as u64)) as u16, + Ordering::Relaxed, + ); } else { peris.set_rtc(false); + CLOCKS.rtc.store(0, Ordering::Relaxed); } // Peripheral clocks should now all be running reset::unreset_wait(peris); } -unsafe fn configure_rosc(config: RoscConfig) { +unsafe fn configure_rosc(config: RoscConfig) -> u32 { let p = pac::ROSC; p.freqa().write(|w| { @@ -371,195 +567,57 @@ unsafe fn configure_rosc(config: RoscConfig) { p.ctrl().write(|w| { w.set_enable(pac::rosc::vals::Enable::ENABLE); - w.set_freq_range(config.range); + w.set_freq_range(pac::rosc::vals::FreqRange(config.range as u16)); }); + + config.hz } -pub fn estimate_rosc_freq() -> u32 { - let p = pac::ROSC; - - let base = match unsafe { p.ctrl().read().freq_range() } { - pac::rosc::vals::FreqRange::LOW => 84_000_000, - pac::rosc::vals::FreqRange::MEDIUM => 104_000_000, - pac::rosc::vals::FreqRange::HIGH => 140_000_000, - pac::rosc::vals::FreqRange::TOOHIGH => 208_000_000, - _ => unreachable!(), - }; - let mut div = unsafe { p.div().read().0 - pac::rosc::vals::Div::PASS.0 as u32 }; - if div == 0 { - div = 32 - } - - base / div +pub fn rosc_freq() -> u32 { + CLOCKS.rosc.load(Ordering::Relaxed) } pub fn xosc_freq() -> u32 { - unsafe { XIN_HZ } + CLOCKS.xosc.load(Ordering::Relaxed) } -pub fn gpin0_freq() -> u32 { - todo!() -} -pub fn gpin1_freq() -> u32 { - todo!() -} +// pub fn gpin0_freq() -> u32 { +// CLOCKS.gpin0.load(Ordering::Relaxed) +// } +// pub fn gpin1_freq() -> u32 { +// CLOCKS.gpin1.load(Ordering::Relaxed) +// } pub fn pll_sys_freq() -> u32 { - let p = pac::PLL_SYS; - - let input_freq = xosc_freq(); - let cs = unsafe { p.cs().read() }; - - let refdiv = cs.refdiv() as u32; - let fbdiv = unsafe { p.fbdiv_int().read().fbdiv_int() } as u32; - let (postdiv1, postdiv2) = unsafe { - let prim = p.prim().read(); - (prim.postdiv1() as u32, prim.postdiv2() as u32) - }; - - (((input_freq / refdiv) * fbdiv) / postdiv1) / postdiv2 + CLOCKS.pll_sys.load(Ordering::Relaxed) } pub fn pll_usb_freq() -> u32 { - let p = pac::PLL_USB; - - let input_freq = xosc_freq(); - let cs = unsafe { p.cs().read() }; - - let refdiv = cs.refdiv() as u32; - let fbdiv = unsafe { p.fbdiv_int().read().fbdiv_int() } as u32; - let (postdiv1, postdiv2) = unsafe { - let prim = p.prim().read(); - (prim.postdiv1() as u32, prim.postdiv2() as u32) - }; - - (((input_freq / refdiv) * fbdiv) / postdiv1) / postdiv2 + CLOCKS.pll_usb.load(Ordering::Relaxed) } pub fn clk_sys_freq() -> u32 { - let c = pac::CLOCKS; - let ctrl = unsafe { c.clk_sys_ctrl().read() }; - - let base = match ctrl.src() { - ClkSysCtrlSrc::CLK_REF => clk_ref_freq(), - ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX => match ctrl.auxsrc() { - ClkSysCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), - ClkSysCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), - ClkSysCtrlAuxsrc::ROSC_CLKSRC => estimate_rosc_freq(), - ClkSysCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(), - ClkSysCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), - ClkSysCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), - _ => unreachable!(), - }, - _ => unreachable!(), - }; - - let div = unsafe { c.clk_sys_div().read() }; - let int = if div.int() == 0 { 65536 } else { div.int() }; - // TODO handle fractional clock div - let _frac = div.frac(); - - base / int + CLOCKS.sys.load(Ordering::Relaxed) } pub fn clk_ref_freq() -> u32 { - let c = pac::CLOCKS; - let ctrl = unsafe { c.clk_ref_ctrl().read() }; - - let base = match ctrl.src() { - ClkRefCtrlSrc::ROSC_CLKSRC_PH => estimate_rosc_freq(), - ClkRefCtrlSrc::XOSC_CLKSRC => xosc_freq(), - ClkRefCtrlSrc::CLKSRC_CLK_REF_AUX => match ctrl.auxsrc() { - ClkRefCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), - ClkRefCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), - ClkRefCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), - _ => unreachable!(), - }, - _ => unreachable!(), - }; - - let div = unsafe { c.clk_ref_div().read() }; - let int = if div.int() == 0 { 4 } else { div.int() as u32 }; - - base / int + CLOCKS.reference.load(Ordering::Relaxed) } pub fn clk_peri_freq() -> u32 { - let c = pac::CLOCKS; - let src = unsafe { c.clk_peri_ctrl().read().auxsrc() }; - - match src { - ClkPeriCtrlAuxsrc::CLK_SYS => clk_sys_freq(), - ClkPeriCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), - ClkPeriCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), - ClkPeriCtrlAuxsrc::ROSC_CLKSRC_PH => estimate_rosc_freq(), - ClkPeriCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(), - ClkPeriCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), - ClkPeriCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), - _ => unreachable!(), - } + CLOCKS.peri.load(Ordering::Relaxed) } pub fn clk_usb_freq() -> u32 { - let c = pac::CLOCKS; - let ctrl = unsafe { c.clk_usb_ctrl().read() }; - - let base = match ctrl.auxsrc() { - ClkUsbCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), - ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), - ClkUsbCtrlAuxsrc::ROSC_CLKSRC_PH => estimate_rosc_freq(), - ClkUsbCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(), - ClkUsbCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), - ClkUsbCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), - _ => unreachable!(), - }; - - let div = unsafe { c.clk_ref_div().read() }; - let int = if div.int() == 0 { 4 } else { div.int() as u32 }; - - base / int + CLOCKS.usb.load(Ordering::Relaxed) } pub fn clk_adc_freq() -> u32 { - let c = pac::CLOCKS; - let ctrl = unsafe { c.clk_adc_ctrl().read() }; - - let base = match ctrl.auxsrc() { - ClkAdcCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), - ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), - ClkAdcCtrlAuxsrc::ROSC_CLKSRC_PH => estimate_rosc_freq(), - ClkAdcCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(), - ClkAdcCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), - ClkAdcCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), - _ => unreachable!(), - }; - - let div = unsafe { c.clk_adc_div().read() }; - let int = if div.int() == 0 { 4 } else { div.int() as u32 }; - - base / int + CLOCKS.adc.load(Ordering::Relaxed) } -pub fn clk_rtc_freq() -> u32 { - let c = pac::CLOCKS; - let src = unsafe { c.clk_rtc_ctrl().read().auxsrc() }; - - let base = match src { - ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), - ClkRtcCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), - ClkRtcCtrlAuxsrc::ROSC_CLKSRC_PH => estimate_rosc_freq(), - ClkRtcCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(), - ClkRtcCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), - ClkRtcCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), - _ => unreachable!(), - }; - - let div = unsafe { c.clk_rtc_div().read() }; - let int = if div.int() == 0 { 65536 } else { div.int() }; - // TODO handle fractional clock div - let _frac = div.frac(); - - base / int +pub fn clk_rtc_freq() -> u16 { + CLOCKS.rtc.load(Ordering::Relaxed) } unsafe fn start_xosc(crystal_hz: u32) { @@ -576,72 +634,56 @@ unsafe fn start_xosc(crystal_hz: u32) { while !pac::XOSC.status().read().stable() {} } -unsafe fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) { - let ref_freq = input_freq / config.refdiv; - - let fbdiv = config.vco_freq / ref_freq; - assert!(fbdiv >= 16 && fbdiv <= 320); +#[inline(always)] +unsafe fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> u32 { + let ref_freq = input_freq / config.refdiv as u32; + assert!(config.fbdiv >= 16 && config.fbdiv <= 320); assert!(config.post_div1 >= 1 && config.post_div1 <= 7); assert!(config.post_div2 >= 1 && config.post_div2 <= 7); - assert!(config.post_div2 <= config.post_div1); - assert!(ref_freq <= (config.vco_freq / 16)); - - // do not disrupt PLL that is already correctly configured and operating - let cs = p.cs().read(); - let prim = p.prim().read(); - if cs.lock() - && cs.refdiv() == config.refdiv as u8 - && p.fbdiv_int().read().fbdiv_int() == fbdiv as u16 - && prim.postdiv1() == config.post_div1 - && prim.postdiv2() == config.post_div2 - { - return; - } - - // Reset it - let mut peris = reset::Peripherals(0); - match p { - pac::PLL_SYS => peris.set_pll_sys(true), - pac::PLL_USB => peris.set_pll_usb(true), - _ => unreachable!(), - } - reset::reset(peris); - reset::unreset_wait(peris); + assert!(config.refdiv >= 1 && config.refdiv <= 63); + assert!(ref_freq >= 5_000_000 && ref_freq <= 800_000_000); + let vco_freq = ref_freq.saturating_mul(config.fbdiv as u32); + assert!(vco_freq >= 750_000_000 && vco_freq <= 1800_000_000); // Load VCO-related dividers before starting VCO p.cs().write(|w| w.set_refdiv(config.refdiv as _)); - p.fbdiv_int().write(|w| w.set_fbdiv_int(fbdiv as _)); + p.fbdiv_int().write(|w| w.set_fbdiv_int(config.fbdiv)); // Turn on PLL - p.pwr().modify(|w| { + let pwr = p.pwr().write(|w| { + w.set_dsmpd(true); // "nothing is achieved by setting this low" w.set_pd(false); w.set_vcopd(false); w.set_postdivpd(true); + *w }); // Wait for PLL to lock while !p.cs().read().lock() {} - // Wait for PLL to lock + // Set post-dividers p.prim().write(|w| { w.set_postdiv1(config.post_div1); w.set_postdiv2(config.post_div2); }); // Turn on post divider - p.pwr().modify(|w| w.set_postdivpd(false)); + p.pwr().write(|w| { + *w = pwr; + w.set_postdivpd(false); + }); + + vco_freq / ((config.post_div1 * config.post_div2) as u32) } pub trait GpinPin: crate::gpio::Pin { - fn number(&self) -> usize; + const NR: usize; } macro_rules! impl_gpinpin { ($name:ident, $pin_num:expr, $gpin_num:expr) => { impl GpinPin for crate::peripherals::$name { - fn number(&self) -> usize { - $gpin_num - } + const NR: usize = $gpin_num; } }; } @@ -649,23 +691,31 @@ macro_rules! impl_gpinpin { impl_gpinpin!(PIN_20, 20, 0); impl_gpinpin!(PIN_22, 22, 1); -pub struct Gpin<'d, T: GpinPin> { - gpin: PeripheralRef<'d, T>, +pub struct Gpin<'d, T: Pin> { + gpin: PeripheralRef<'d, AnyPin>, + _phantom: PhantomData, } -impl<'d, T: GpinPin> Gpin<'d, T> { - pub fn new(gpin: impl Peripheral

+ 'd) -> Self { +impl<'d, T: Pin> Gpin<'d, T> { + pub fn new(gpin: impl Peripheral

+ 'd) -> Gpin<'d, P> { into_ref!(gpin); unsafe { gpin.io().ctrl().write(|w| w.set_funcsel(0x08)); } - Self { gpin } + Gpin { + gpin: gpin.map_into(), + _phantom: PhantomData, + } } + + // fn map_into(self) -> Gpin<'d, AnyPin> { + // unsafe { core::mem::transmute(self) } + // } } -impl<'d, T: GpinPin> Drop for Gpin<'d, T> { +impl<'d, T: Pin> Drop for Gpin<'d, T> { fn drop(&mut self) { unsafe { self.gpin @@ -695,6 +745,21 @@ impl_gpoutpin!(PIN_23, 1); impl_gpoutpin!(PIN_24, 2); impl_gpoutpin!(PIN_25, 3); +#[repr(u8)] +pub enum GpoutSrc { + PllSys = ClkGpoutCtrlAuxsrc::CLKSRC_PLL_SYS.0, + // Gpin0 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN0.0, + // Gpin1 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN1.0, + PllUsb = ClkGpoutCtrlAuxsrc::CLKSRC_PLL_USB.0, + Rosc = ClkGpoutCtrlAuxsrc::ROSC_CLKSRC.0, + Xosc = ClkGpoutCtrlAuxsrc::XOSC_CLKSRC.0, + Sys = ClkGpoutCtrlAuxsrc::CLK_SYS.0, + Usb = ClkGpoutCtrlAuxsrc::CLK_USB.0, + Adc = ClkGpoutCtrlAuxsrc::CLK_ADC.0, + Rtc = ClkGpoutCtrlAuxsrc::CLK_RTC.0, + Ref = ClkGpoutCtrlAuxsrc::CLK_REF.0, +} + pub struct Gpout<'d, T: GpoutPin> { gpout: PeripheralRef<'d, T>, } @@ -720,11 +785,11 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { } } - pub fn set_src(&self, src: ClkGpoutCtrlAuxsrc) { + pub fn set_src(&self, src: GpoutSrc) { unsafe { let c = pac::CLOCKS; c.clk_gpout_ctrl(self.gpout.number()).modify(|w| { - w.set_auxsrc(src); + w.set_auxsrc(ClkGpoutCtrlAuxsrc(src as _)); }); } } @@ -753,25 +818,24 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { let base = match src { ClkGpoutCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), - ClkGpoutCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), - ClkGpoutCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), + // ClkGpoutCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), + // ClkGpoutCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), ClkGpoutCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), - ClkGpoutCtrlAuxsrc::ROSC_CLKSRC => estimate_rosc_freq(), + ClkGpoutCtrlAuxsrc::ROSC_CLKSRC => rosc_freq(), ClkGpoutCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(), ClkGpoutCtrlAuxsrc::CLK_SYS => clk_sys_freq(), ClkGpoutCtrlAuxsrc::CLK_USB => clk_usb_freq(), ClkGpoutCtrlAuxsrc::CLK_ADC => clk_adc_freq(), - ClkGpoutCtrlAuxsrc::CLK_RTC => clk_rtc_freq(), + ClkGpoutCtrlAuxsrc::CLK_RTC => clk_rtc_freq() as _, ClkGpoutCtrlAuxsrc::CLK_REF => clk_ref_freq(), _ => unreachable!(), }; let div = unsafe { c.clk_gpout_div(self.gpout.number()).read() }; - let int = if div.int() == 0 { 65536 } else { div.int() }; - // TODO handle fractional clock div - let _frac = div.frac(); + let int = if div.int() == 0 { 65536 } else { div.int() } as u64; + let frac = div.frac() as u64; - base / int + ((base as u64 * 256) / (int * 256 + frac)) as u32 } } diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs index c173909c7..c213ad174 100644 --- a/embassy-rp/src/rtc/mod.rs +++ b/embassy-rp/src/rtc/mod.rs @@ -26,12 +26,7 @@ impl<'d, T: Instance> RealTimeClock<'d, T> { into_ref!(inner); // Set the RTC divider - unsafe { - inner - .regs() - .clkdiv_m1() - .write(|w| w.set_clkdiv_m1(clk_rtc_freq() as u16 - 1)) - }; + unsafe { inner.regs().clkdiv_m1().write(|w| w.set_clkdiv_m1(clk_rtc_freq() - 1)) }; let mut result = Self { inner }; result.set_leap_year_check(true); // should be on by default, make sure this is the case. diff --git a/examples/rp/src/bin/gpout.rs b/examples/rp/src/bin/gpout.rs index 236a653ac..64461fc5f 100644 --- a/examples/rp/src/bin/gpout.rs +++ b/examples/rp/src/bin/gpout.rs @@ -17,14 +17,14 @@ async fn main(_spawner: Spawner) { gpout3.enable(); loop { - gpout3.set_src(clocks::GpoutSrc::CLK_SYS); + gpout3.set_src(clocks::GpoutSrc::Sys); info!( "Pin 25 is now outputing CLK_SYS/1000, should be toggling at {}", gpout3.get_freq() ); Timer::after(Duration::from_secs(2)).await; - gpout3.set_src(clocks::GpoutSrc::CLK_REF); + gpout3.set_src(clocks::GpoutSrc::Ref); info!( "Pin 25 is now outputing CLK_REF/1000, should be toggling at {}", gpout3.get_freq()