diff options
| -rw-r--r-- | embassy-rp/src/clocks.rs | 384 | ||||
| -rw-r--r-- | embassy-rp/src/rtc/mod.rs | 7 |
2 files changed, 184 insertions, 207 deletions
diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 2305a1e34..9e581f105 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs | |||
| @@ -1,10 +1,39 @@ | |||
| 1 | use core::sync::atomic::{AtomicU16, AtomicU32, Ordering}; | ||
| 2 | |||
| 1 | use embassy_hal_common::{into_ref, PeripheralRef}; | 3 | use embassy_hal_common::{into_ref, PeripheralRef}; |
| 2 | use pac::clocks::vals::*; | 4 | use pac::clocks::vals::*; |
| 3 | 5 | ||
| 4 | use crate::{pac, reset, Peripheral}; | 6 | use crate::{pac, reset, Peripheral}; |
| 5 | 7 | ||
| 6 | // TODO fix terrible use of global here | 8 | struct Clocks { |
| 7 | static mut XIN_HZ: u32 = 0; | 9 | xosc: AtomicU32, |
| 10 | sys: AtomicU32, | ||
| 11 | reference: AtomicU32, | ||
| 12 | pll_sys: AtomicU32, | ||
| 13 | pll_usb: AtomicU32, | ||
| 14 | usb: AtomicU32, | ||
| 15 | adc: AtomicU32, | ||
| 16 | gpin0: AtomicU32, | ||
| 17 | gpin1: AtomicU32, | ||
| 18 | rosc: AtomicU32, | ||
| 19 | peri: AtomicU32, | ||
| 20 | rtc: AtomicU16, | ||
| 21 | } | ||
| 22 | |||
| 23 | static CLOCKS: Clocks = Clocks { | ||
| 24 | xosc: AtomicU32::new(0), | ||
| 25 | sys: AtomicU32::new(0), | ||
| 26 | reference: AtomicU32::new(0), | ||
| 27 | pll_sys: AtomicU32::new(0), | ||
| 28 | pll_usb: AtomicU32::new(0), | ||
| 29 | usb: AtomicU32::new(0), | ||
| 30 | adc: AtomicU32::new(0), | ||
| 31 | gpin0: AtomicU32::new(0), | ||
| 32 | gpin1: AtomicU32::new(0), | ||
| 33 | rosc: AtomicU32::new(0), | ||
| 34 | peri: AtomicU32::new(0), | ||
| 35 | rtc: AtomicU16::new(0), | ||
| 36 | }; | ||
| 8 | 37 | ||
| 9 | #[repr(u8)] | 38 | #[repr(u8)] |
| 10 | #[non_exhaustive] | 39 | #[non_exhaustive] |
| @@ -29,12 +58,15 @@ pub struct ClockConfig { | |||
| 29 | pub usb_clk: Option<UsbClkConfig>, | 58 | pub usb_clk: Option<UsbClkConfig>, |
| 30 | pub adc_clk: Option<AdcClkConfig>, | 59 | pub adc_clk: Option<AdcClkConfig>, |
| 31 | pub rtc_clk: Option<RtcClkConfig>, | 60 | pub rtc_clk: Option<RtcClkConfig>, |
| 61 | pub gpin0_hz: Option<u32>, | ||
| 62 | pub gpin1_hz: Option<u32>, | ||
| 32 | } | 63 | } |
| 33 | 64 | ||
| 34 | impl ClockConfig { | 65 | impl ClockConfig { |
| 35 | pub fn crystal(crystal_hz: u32) -> Self { | 66 | pub fn crystal(crystal_hz: u32) -> Self { |
| 36 | Self { | 67 | Self { |
| 37 | rosc: Some(RoscConfig { | 68 | rosc: Some(RoscConfig { |
| 69 | hz: 6_500_000, | ||
| 38 | range: RoscRange::Medium, | 70 | range: RoscRange::Medium, |
| 39 | drive_strength: [0; 8], | 71 | drive_strength: [0; 8], |
| 40 | div: 16, | 72 | div: 16, |
| @@ -83,12 +115,15 @@ impl ClockConfig { | |||
| 83 | div_frac: 0, | 115 | div_frac: 0, |
| 84 | phase: 0, | 116 | phase: 0, |
| 85 | }), | 117 | }), |
| 118 | gpin0_hz: None, | ||
| 119 | gpin1_hz: None, | ||
| 86 | } | 120 | } |
| 87 | } | 121 | } |
| 88 | 122 | ||
| 89 | pub fn rosc() -> Self { | 123 | pub fn rosc() -> Self { |
| 90 | Self { | 124 | Self { |
| 91 | rosc: Some(RoscConfig { | 125 | rosc: Some(RoscConfig { |
| 126 | hz: 140_000_000, | ||
| 92 | range: RoscRange::High, | 127 | range: RoscRange::High, |
| 93 | drive_strength: [0; 8], | 128 | drive_strength: [0; 8], |
| 94 | div: 1, | 129 | div: 1, |
| @@ -118,6 +153,8 @@ impl ClockConfig { | |||
| 118 | div_frac: 171, | 153 | div_frac: 171, |
| 119 | phase: 0, | 154 | phase: 0, |
| 120 | }), | 155 | }), |
| 156 | gpin0_hz: None, | ||
| 157 | gpin1_hz: None, | ||
| 121 | } | 158 | } |
| 122 | } | 159 | } |
| 123 | } | 160 | } |
| @@ -133,6 +170,11 @@ pub enum RoscRange { | |||
| 133 | } | 170 | } |
| 134 | 171 | ||
| 135 | pub struct RoscConfig { | 172 | pub struct RoscConfig { |
| 173 | /// Final frequency of the oscillator, after the divider has been applied. | ||
| 174 | /// The oscillator has a nominal frequency of 6.5MHz at medium range with | ||
| 175 | /// divider 16 and all drive strengths set to 0, other values should be | ||
| 176 | /// measured in situ. | ||
| 177 | pub hz: u32, | ||
| 136 | pub range: RoscRange, | 178 | pub range: RoscRange, |
| 137 | pub drive_strength: [u8; 8], | 179 | pub drive_strength: [u8; 8], |
| 138 | pub div: u16, | 180 | pub div: u16, |
| @@ -145,7 +187,7 @@ pub struct XoscConfig { | |||
| 145 | } | 187 | } |
| 146 | 188 | ||
| 147 | pub struct PllConfig { | 189 | pub struct PllConfig { |
| 148 | pub refdiv: u32, | 190 | pub refdiv: u8, |
| 149 | pub fbdiv: u16, | 191 | pub fbdiv: u16, |
| 150 | pub post_div1: u8, | 192 | pub post_div1: u8, |
| 151 | pub post_div2: u8, | 193 | pub post_div2: u8, |
| @@ -277,41 +319,60 @@ pub(crate) unsafe fn init(config: ClockConfig) { | |||
| 277 | reset::reset(peris); | 319 | reset::reset(peris); |
| 278 | reset::unreset_wait(peris); | 320 | reset::unreset_wait(peris); |
| 279 | 321 | ||
| 280 | if let Some(config) = config.rosc { | 322 | let gpin0_freq = config.gpin0_hz.unwrap_or(0); |
| 281 | configure_rosc(config); | 323 | CLOCKS.gpin0.store(gpin0_freq, Ordering::Relaxed); |
| 282 | } | 324 | let gpin1_freq = config.gpin1_hz.unwrap_or(0); |
| 283 | 325 | CLOCKS.gpin1.store(gpin1_freq, Ordering::Relaxed); | |
| 284 | if let Some(config) = config.xosc { | ||
| 285 | XIN_HZ = config.hz; | ||
| 286 | 326 | ||
| 287 | pac::WATCHDOG.tick().write(|w| { | 327 | let rosc_freq = match config.rosc { |
| 288 | w.set_cycles((config.hz / 1_000_000) as u16); | 328 | Some(config) => configure_rosc(config), |
| 289 | w.set_enable(true); | 329 | None => 0, |
| 290 | }); | 330 | }; |
| 331 | CLOCKS.rosc.store(rosc_freq, Ordering::Relaxed); | ||
| 291 | 332 | ||
| 292 | // start XOSC | 333 | let (xosc_freq, pll_sys_freq, pll_usb_freq) = match config.xosc { |
| 293 | // datasheet mentions support for clock inputs into XIN, but doesn't go into | 334 | Some(config) => { |
| 294 | // how this is achieved. pico-sdk doesn't support this at all. | 335 | pac::WATCHDOG.tick().write(|w| { |
| 295 | start_xosc(config.hz); | 336 | w.set_cycles((config.hz / 1_000_000) as u16); |
| 337 | w.set_enable(true); | ||
| 338 | }); | ||
| 296 | 339 | ||
| 297 | if let Some(sys_pll_config) = config.sys_pll { | 340 | // start XOSC |
| 298 | configure_pll(pac::PLL_SYS, config.hz, sys_pll_config); | 341 | // datasheet mentions support for clock inputs into XIN, but doesn't go into |
| 299 | } | 342 | // how this is achieved. pico-sdk doesn't support this at all. |
| 300 | if let Some(usb_pll_config) = config.usb_pll { | 343 | start_xosc(config.hz); |
| 301 | configure_pll(pac::PLL_USB, config.hz, usb_pll_config); | 344 | |
| 345 | let pll_sys_freq = match config.sys_pll { | ||
| 346 | Some(sys_pll_config) => configure_pll(pac::PLL_SYS, config.hz, sys_pll_config), | ||
| 347 | None => 0, | ||
| 348 | }; | ||
| 349 | let pll_usb_freq = match config.usb_pll { | ||
| 350 | Some(usb_pll_config) => configure_pll(pac::PLL_USB, config.hz, usb_pll_config), | ||
| 351 | None => 0, | ||
| 352 | }; | ||
| 353 | |||
| 354 | (config.hz, pll_sys_freq, pll_usb_freq) | ||
| 302 | } | 355 | } |
| 303 | } | 356 | None => (0, 0, 0), |
| 357 | }; | ||
| 358 | CLOCKS.xosc.store(xosc_freq, Ordering::Relaxed); | ||
| 359 | CLOCKS.pll_sys.store(pll_sys_freq, Ordering::Relaxed); | ||
| 360 | CLOCKS.pll_usb.store(pll_usb_freq, Ordering::Relaxed); | ||
| 304 | 361 | ||
| 305 | let (ref_src, ref_aux) = { | 362 | let (ref_src, ref_aux, clk_ref_freq) = { |
| 306 | use {ClkRefCtrlAuxsrc as Aux, ClkRefCtrlSrc as Src}; | 363 | use {ClkRefCtrlAuxsrc as Aux, ClkRefCtrlSrc as Src}; |
| 364 | let div = config.ref_clk.div as u32; | ||
| 365 | assert!(div >= 1 && div <= 4); | ||
| 307 | match config.ref_clk.src { | 366 | match config.ref_clk.src { |
| 308 | RefClkSrc::Xosc => (Src::XOSC_CLKSRC, Aux::CLKSRC_PLL_USB), | 367 | RefClkSrc::Xosc => (Src::XOSC_CLKSRC, Aux::CLKSRC_PLL_USB, xosc_freq / div), |
| 309 | RefClkSrc::Rosc => (Src::ROSC_CLKSRC_PH, Aux::CLKSRC_PLL_USB), | 368 | RefClkSrc::Rosc => (Src::ROSC_CLKSRC_PH, Aux::CLKSRC_PLL_USB, rosc_freq / div), |
| 310 | RefClkSrc::PllUsb => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_PLL_USB), | 369 | RefClkSrc::PllUsb => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_PLL_USB, pll_usb_freq / div), |
| 311 | RefClkSrc::Gpin0 => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_GPIN0), | 370 | RefClkSrc::Gpin0 => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_GPIN0, gpin0_freq / div), |
| 312 | RefClkSrc::Gpin1 => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_GPIN1), | 371 | RefClkSrc::Gpin1 => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_GPIN1, gpin1_freq / div), |
| 313 | } | 372 | } |
| 314 | }; | 373 | }; |
| 374 | assert!(clk_ref_freq != 0); | ||
| 375 | CLOCKS.reference.store(clk_ref_freq, Ordering::Relaxed); | ||
| 315 | c.clk_ref_ctrl().write(|w| { | 376 | c.clk_ref_ctrl().write(|w| { |
| 316 | w.set_src(ref_src); | 377 | w.set_src(ref_src); |
| 317 | w.set_auxsrc(ref_aux); | 378 | w.set_auxsrc(ref_aux); |
| @@ -322,22 +383,27 @@ pub(crate) unsafe fn init(config: ClockConfig) { | |||
| 322 | }); | 383 | }); |
| 323 | 384 | ||
| 324 | pac::WATCHDOG.tick().write(|w| { | 385 | pac::WATCHDOG.tick().write(|w| { |
| 325 | w.set_cycles((clk_ref_freq() / 1_000_000) as u16); | 386 | w.set_cycles((clk_ref_freq / 1_000_000) as u16); |
| 326 | w.set_enable(true); | 387 | w.set_enable(true); |
| 327 | }); | 388 | }); |
| 328 | 389 | ||
| 329 | let (sys_src, sys_aux) = { | 390 | let (sys_src, sys_aux, clk_sys_freq) = { |
| 330 | use {ClkSysCtrlAuxsrc as Aux, ClkSysCtrlSrc as Src}; | 391 | use {ClkSysCtrlAuxsrc as Aux, ClkSysCtrlSrc as Src}; |
| 331 | match config.sys_clk.src { | 392 | let (src, aux, freq) = match config.sys_clk.src { |
| 332 | SysClkSrc::Ref => (Src::CLK_REF, Aux::CLKSRC_PLL_SYS), | 393 | SysClkSrc::Ref => (Src::CLK_REF, Aux::CLKSRC_PLL_SYS, clk_ref_freq), |
| 333 | SysClkSrc::PllSys => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_SYS), | 394 | SysClkSrc::PllSys => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_SYS, pll_sys_freq), |
| 334 | SysClkSrc::PllUsb => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_USB), | 395 | SysClkSrc::PllUsb => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_USB, pll_usb_freq), |
| 335 | SysClkSrc::Rosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::ROSC_CLKSRC), | 396 | SysClkSrc::Rosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::ROSC_CLKSRC, rosc_freq), |
| 336 | SysClkSrc::Xosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::XOSC_CLKSRC), | 397 | SysClkSrc::Xosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::XOSC_CLKSRC, xosc_freq), |
| 337 | SysClkSrc::Gpin0 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN0), | 398 | SysClkSrc::Gpin0 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN0, gpin0_freq), |
| 338 | SysClkSrc::Gpin1 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN1), | 399 | SysClkSrc::Gpin1 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN1, gpin1_freq), |
| 339 | } | 400 | }; |
| 401 | assert!(config.sys_clk.div_int <= 0x1000000); | ||
| 402 | let div = config.sys_clk.div_int as u64 * 256 + config.sys_clk.div_frac as u64; | ||
| 403 | (src, aux, ((freq as u64 * 256) / div) as u32) | ||
| 340 | }; | 404 | }; |
| 405 | assert!(clk_sys_freq != 0); | ||
| 406 | CLOCKS.sys.store(clk_sys_freq, Ordering::Relaxed); | ||
| 341 | if sys_src != ClkSysCtrlSrc::CLK_REF { | 407 | if sys_src != ClkSysCtrlSrc::CLK_REF { |
| 342 | c.clk_sys_ctrl().write(|w| w.set_src(ClkSysCtrlSrc::CLK_REF)); | 408 | c.clk_sys_ctrl().write(|w| w.set_src(ClkSysCtrlSrc::CLK_REF)); |
| 343 | while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLK_REF.0 {} | 409 | while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLK_REF.0 {} |
| @@ -359,11 +425,23 @@ pub(crate) unsafe fn init(config: ClockConfig) { | |||
| 359 | w.set_enable(true); | 425 | w.set_enable(true); |
| 360 | w.set_auxsrc(ClkPeriCtrlAuxsrc(src as _)); | 426 | w.set_auxsrc(ClkPeriCtrlAuxsrc(src as _)); |
| 361 | }); | 427 | }); |
| 428 | let peri_freq = match src { | ||
| 429 | PeriClkSrc::Sys => clk_sys_freq, | ||
| 430 | PeriClkSrc::PllSys => pll_sys_freq, | ||
| 431 | PeriClkSrc::PllUsb => pll_usb_freq, | ||
| 432 | PeriClkSrc::Rosc => rosc_freq, | ||
| 433 | PeriClkSrc::Xosc => xosc_freq, | ||
| 434 | PeriClkSrc::Gpin0 => gpin0_freq, | ||
| 435 | PeriClkSrc::Gpin1 => gpin1_freq, | ||
| 436 | }; | ||
| 437 | assert!(peri_freq != 0); | ||
| 438 | CLOCKS.peri.store(peri_freq, Ordering::Relaxed); | ||
| 362 | } else { | 439 | } else { |
| 363 | peris.set_spi0(false); | 440 | peris.set_spi0(false); |
| 364 | peris.set_spi1(false); | 441 | peris.set_spi1(false); |
| 365 | peris.set_uart0(false); | 442 | peris.set_uart0(false); |
| 366 | peris.set_uart1(false); | 443 | peris.set_uart1(false); |
| 444 | CLOCKS.peri.store(0, Ordering::Relaxed); | ||
| 367 | } | 445 | } |
| 368 | 446 | ||
| 369 | if let Some(conf) = config.usb_clk { | 447 | if let Some(conf) = config.usb_clk { |
| @@ -373,8 +451,20 @@ pub(crate) unsafe fn init(config: ClockConfig) { | |||
| 373 | w.set_enable(true); | 451 | w.set_enable(true); |
| 374 | w.set_auxsrc(ClkUsbCtrlAuxsrc(conf.src as _)); | 452 | w.set_auxsrc(ClkUsbCtrlAuxsrc(conf.src as _)); |
| 375 | }); | 453 | }); |
| 454 | let usb_freq = match conf.src { | ||
| 455 | UsbClkSrc::PllUsb => pll_usb_freq, | ||
| 456 | UsbClkSrc::PllSys => pll_sys_freq, | ||
| 457 | UsbClkSrc::Rosc => rosc_freq, | ||
| 458 | UsbClkSrc::Xosc => xosc_freq, | ||
| 459 | UsbClkSrc::Gpin0 => gpin0_freq, | ||
| 460 | UsbClkSrc::Gpin1 => gpin1_freq, | ||
| 461 | }; | ||
| 462 | assert!(usb_freq != 0); | ||
| 463 | assert!(conf.div >= 1 && conf.div <= 4); | ||
| 464 | CLOCKS.usb.store(usb_freq / conf.div as u32, Ordering::Relaxed); | ||
| 376 | } else { | 465 | } else { |
| 377 | peris.set_usbctrl(false); | 466 | peris.set_usbctrl(false); |
| 467 | CLOCKS.usb.store(0, Ordering::Relaxed); | ||
| 378 | } | 468 | } |
| 379 | 469 | ||
| 380 | if let Some(conf) = config.adc_clk { | 470 | if let Some(conf) = config.adc_clk { |
| @@ -384,8 +474,20 @@ pub(crate) unsafe fn init(config: ClockConfig) { | |||
| 384 | w.set_enable(true); | 474 | w.set_enable(true); |
| 385 | w.set_auxsrc(ClkAdcCtrlAuxsrc(conf.src as _)); | 475 | w.set_auxsrc(ClkAdcCtrlAuxsrc(conf.src as _)); |
| 386 | }); | 476 | }); |
| 477 | let adc_in_freq = match conf.src { | ||
| 478 | AdcClkSrc::PllUsb => pll_usb_freq, | ||
| 479 | AdcClkSrc::PllSys => pll_sys_freq, | ||
| 480 | AdcClkSrc::Rosc => rosc_freq, | ||
| 481 | AdcClkSrc::Xosc => xosc_freq, | ||
| 482 | AdcClkSrc::Gpin0 => gpin0_freq, | ||
| 483 | AdcClkSrc::Gpin1 => gpin1_freq, | ||
| 484 | }; | ||
| 485 | assert!(adc_in_freq != 0); | ||
| 486 | assert!(conf.div >= 1 && conf.div <= 4); | ||
| 487 | CLOCKS.adc.store(adc_in_freq / conf.div as u32, Ordering::Relaxed); | ||
| 387 | } else { | 488 | } else { |
| 388 | peris.set_adc(false); | 489 | peris.set_adc(false); |
| 490 | CLOCKS.adc.store(0, Ordering::Relaxed); | ||
| 389 | } | 491 | } |
| 390 | 492 | ||
| 391 | if let Some(conf) = config.rtc_clk { | 493 | if let Some(conf) = config.rtc_clk { |
| @@ -401,15 +503,30 @@ pub(crate) unsafe fn init(config: ClockConfig) { | |||
| 401 | w.set_enable(true); | 503 | w.set_enable(true); |
| 402 | w.set_auxsrc(ClkRtcCtrlAuxsrc(conf.src as _)); | 504 | w.set_auxsrc(ClkRtcCtrlAuxsrc(conf.src as _)); |
| 403 | }); | 505 | }); |
| 506 | let rtc_in_freq = match conf.src { | ||
| 507 | RtcClkSrc::PllUsb => pll_usb_freq, | ||
| 508 | RtcClkSrc::PllSys => pll_sys_freq, | ||
| 509 | RtcClkSrc::Rosc => rosc_freq, | ||
| 510 | RtcClkSrc::Xosc => xosc_freq, | ||
| 511 | RtcClkSrc::Gpin0 => gpin0_freq, | ||
| 512 | RtcClkSrc::Gpin1 => gpin1_freq, | ||
| 513 | }; | ||
| 514 | assert!(rtc_in_freq != 0); | ||
| 515 | assert!(config.sys_clk.div_int <= 0x1000000); | ||
| 516 | CLOCKS.rtc.store( | ||
| 517 | ((rtc_in_freq as u64 * 256) / (conf.div_int as u64 * 256 + conf.div_frac as u64)) as u16, | ||
| 518 | Ordering::Relaxed, | ||
| 519 | ); | ||
| 404 | } else { | 520 | } else { |
| 405 | peris.set_rtc(false); | 521 | peris.set_rtc(false); |
| 522 | CLOCKS.rtc.store(0, Ordering::Relaxed); | ||
| 406 | } | 523 | } |
| 407 | 524 | ||
| 408 | // Peripheral clocks should now all be running | 525 | // Peripheral clocks should now all be running |
| 409 | reset::unreset_wait(peris); | 526 | reset::unreset_wait(peris); |
| 410 | } | 527 | } |
| 411 | 528 | ||
| 412 | unsafe fn configure_rosc(config: RoscConfig) { | 529 | unsafe fn configure_rosc(config: RoscConfig) -> u32 { |
| 413 | let p = pac::ROSC; | 530 | let p = pac::ROSC; |
| 414 | 531 | ||
| 415 | p.freqa().write(|w| { | 532 | p.freqa().write(|w| { |
| @@ -436,193 +553,55 @@ unsafe fn configure_rosc(config: RoscConfig) { | |||
| 436 | w.set_enable(pac::rosc::vals::Enable::ENABLE); | 553 | w.set_enable(pac::rosc::vals::Enable::ENABLE); |
| 437 | w.set_freq_range(pac::rosc::vals::FreqRange(config.range as u16)); | 554 | w.set_freq_range(pac::rosc::vals::FreqRange(config.range as u16)); |
| 438 | }); | 555 | }); |
| 439 | } | ||
| 440 | 556 | ||
| 441 | pub fn estimate_rosc_freq() -> u32 { | 557 | config.hz |
| 442 | let p = pac::ROSC; | 558 | } |
| 443 | |||
| 444 | let base = match unsafe { p.ctrl().read().freq_range() } { | ||
| 445 | pac::rosc::vals::FreqRange::LOW => 84_000_000, | ||
| 446 | pac::rosc::vals::FreqRange::MEDIUM => 104_000_000, | ||
| 447 | pac::rosc::vals::FreqRange::HIGH => 140_000_000, | ||
| 448 | pac::rosc::vals::FreqRange::TOOHIGH => 208_000_000, | ||
| 449 | _ => unreachable!(), | ||
| 450 | }; | ||
| 451 | let mut div = unsafe { p.div().read().0 - pac::rosc::vals::Div::PASS.0 as u32 }; | ||
| 452 | if div == 0 { | ||
| 453 | div = 32 | ||
| 454 | } | ||
| 455 | 559 | ||
| 456 | base / div | 560 | pub fn rosc_freq() -> u32 { |
| 561 | CLOCKS.rosc.load(Ordering::Relaxed) | ||
| 457 | } | 562 | } |
| 458 | 563 | ||
| 459 | pub fn xosc_freq() -> u32 { | 564 | pub fn xosc_freq() -> u32 { |
| 460 | unsafe { XIN_HZ } | 565 | CLOCKS.xosc.load(Ordering::Relaxed) |
| 461 | } | 566 | } |
| 462 | 567 | ||
| 463 | pub fn gpin0_freq() -> u32 { | 568 | pub fn gpin0_freq() -> u32 { |
| 464 | todo!() | 569 | CLOCKS.gpin0.load(Ordering::Relaxed) |
| 465 | } | 570 | } |
| 466 | pub fn gpin1_freq() -> u32 { | 571 | pub fn gpin1_freq() -> u32 { |
| 467 | todo!() | 572 | CLOCKS.gpin1.load(Ordering::Relaxed) |
| 468 | } | 573 | } |
| 469 | 574 | ||
| 470 | pub fn pll_sys_freq() -> u32 { | 575 | pub fn pll_sys_freq() -> u32 { |
| 471 | let p = pac::PLL_SYS; | 576 | CLOCKS.pll_sys.load(Ordering::Relaxed) |
| 472 | |||
| 473 | let input_freq = xosc_freq(); | ||
| 474 | let cs = unsafe { p.cs().read() }; | ||
| 475 | |||
| 476 | let refdiv = cs.refdiv() as u32; | ||
| 477 | let fbdiv = unsafe { p.fbdiv_int().read().fbdiv_int() } as u32; | ||
| 478 | let (postdiv1, postdiv2) = unsafe { | ||
| 479 | let prim = p.prim().read(); | ||
| 480 | (prim.postdiv1() as u32, prim.postdiv2() as u32) | ||
| 481 | }; | ||
| 482 | |||
| 483 | (((input_freq / refdiv) * fbdiv) / postdiv1) / postdiv2 | ||
| 484 | } | 577 | } |
| 485 | 578 | ||
| 486 | pub fn pll_usb_freq() -> u32 { | 579 | pub fn pll_usb_freq() -> u32 { |
| 487 | let p = pac::PLL_USB; | 580 | CLOCKS.pll_usb.load(Ordering::Relaxed) |
| 488 | |||
| 489 | let input_freq = xosc_freq(); | ||
| 490 | let cs = unsafe { p.cs().read() }; | ||
| 491 | |||
| 492 | let refdiv = cs.refdiv() as u32; | ||
| 493 | let fbdiv = unsafe { p.fbdiv_int().read().fbdiv_int() } as u32; | ||
| 494 | let (postdiv1, postdiv2) = unsafe { | ||
| 495 | let prim = p.prim().read(); | ||
| 496 | (prim.postdiv1() as u32, prim.postdiv2() as u32) | ||
| 497 | }; | ||
| 498 | |||
| 499 | (((input_freq / refdiv) * fbdiv) / postdiv1) / postdiv2 | ||
| 500 | } | 581 | } |
| 501 | 582 | ||
| 502 | pub fn clk_sys_freq() -> u32 { | 583 | pub fn clk_sys_freq() -> u32 { |
| 503 | let c = pac::CLOCKS; | 584 | CLOCKS.sys.load(Ordering::Relaxed) |
| 504 | let ctrl = unsafe { c.clk_sys_ctrl().read() }; | ||
| 505 | |||
| 506 | let base = match ctrl.src() { | ||
| 507 | ClkSysCtrlSrc::CLK_REF => clk_ref_freq(), | ||
| 508 | ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX => match ctrl.auxsrc() { | ||
| 509 | ClkSysCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), | ||
| 510 | ClkSysCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), | ||
| 511 | ClkSysCtrlAuxsrc::ROSC_CLKSRC => estimate_rosc_freq(), | ||
| 512 | ClkSysCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(), | ||
| 513 | ClkSysCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), | ||
| 514 | ClkSysCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), | ||
| 515 | _ => unreachable!(), | ||
| 516 | }, | ||
| 517 | _ => unreachable!(), | ||
| 518 | }; | ||
| 519 | |||
| 520 | let div = unsafe { c.clk_sys_div().read() }; | ||
| 521 | let int = if div.int() == 0 { 65536 } else { div.int() }; | ||
| 522 | // TODO handle fractional clock div | ||
| 523 | let _frac = div.frac(); | ||
| 524 | |||
| 525 | base / int | ||
| 526 | } | 585 | } |
| 527 | 586 | ||
| 528 | pub fn clk_ref_freq() -> u32 { | 587 | pub fn clk_ref_freq() -> u32 { |
| 529 | let c = pac::CLOCKS; | 588 | CLOCKS.reference.load(Ordering::Relaxed) |
| 530 | let ctrl = unsafe { c.clk_ref_ctrl().read() }; | ||
| 531 | |||
| 532 | let base = match ctrl.src() { | ||
| 533 | ClkRefCtrlSrc::ROSC_CLKSRC_PH => estimate_rosc_freq(), | ||
| 534 | ClkRefCtrlSrc::XOSC_CLKSRC => xosc_freq(), | ||
| 535 | ClkRefCtrlSrc::CLKSRC_CLK_REF_AUX => match ctrl.auxsrc() { | ||
| 536 | ClkRefCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), | ||
| 537 | ClkRefCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), | ||
| 538 | ClkRefCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), | ||
| 539 | _ => unreachable!(), | ||
| 540 | }, | ||
| 541 | _ => unreachable!(), | ||
| 542 | }; | ||
| 543 | |||
| 544 | let div = unsafe { c.clk_ref_div().read() }; | ||
| 545 | let int = if div.int() == 0 { 4 } else { div.int() as u32 }; | ||
| 546 | |||
| 547 | base / int | ||
| 548 | } | 589 | } |
| 549 | 590 | ||
| 550 | pub fn clk_peri_freq() -> u32 { | 591 | pub fn clk_peri_freq() -> u32 { |
| 551 | let c = pac::CLOCKS; | 592 | CLOCKS.peri.load(Ordering::Relaxed) |
| 552 | let src = unsafe { c.clk_peri_ctrl().read().auxsrc() }; | ||
| 553 | |||
| 554 | match src { | ||
| 555 | ClkPeriCtrlAuxsrc::CLK_SYS => clk_sys_freq(), | ||
| 556 | ClkPeriCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), | ||
| 557 | ClkPeriCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), | ||
| 558 | ClkPeriCtrlAuxsrc::ROSC_CLKSRC_PH => estimate_rosc_freq(), | ||
| 559 | ClkPeriCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(), | ||
| 560 | ClkPeriCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), | ||
| 561 | ClkPeriCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), | ||
| 562 | _ => unreachable!(), | ||
| 563 | } | ||
| 564 | } | 593 | } |
| 565 | 594 | ||
| 566 | pub fn clk_usb_freq() -> u32 { | 595 | pub fn clk_usb_freq() -> u32 { |
| 567 | let c = pac::CLOCKS; | 596 | CLOCKS.usb.load(Ordering::Relaxed) |
| 568 | let ctrl = unsafe { c.clk_usb_ctrl().read() }; | ||
| 569 | |||
| 570 | let base = match ctrl.auxsrc() { | ||
| 571 | ClkUsbCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), | ||
| 572 | ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), | ||
| 573 | ClkUsbCtrlAuxsrc::ROSC_CLKSRC_PH => estimate_rosc_freq(), | ||
| 574 | ClkUsbCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(), | ||
| 575 | ClkUsbCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), | ||
| 576 | ClkUsbCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), | ||
| 577 | _ => unreachable!(), | ||
| 578 | }; | ||
| 579 | |||
| 580 | let div = unsafe { c.clk_ref_div().read() }; | ||
| 581 | let int = if div.int() == 0 { 4 } else { div.int() as u32 }; | ||
| 582 | |||
| 583 | base / int | ||
| 584 | } | 597 | } |
| 585 | 598 | ||
| 586 | pub fn clk_adc_freq() -> u32 { | 599 | pub fn clk_adc_freq() -> u32 { |
| 587 | let c = pac::CLOCKS; | 600 | CLOCKS.adc.load(Ordering::Relaxed) |
| 588 | let ctrl = unsafe { c.clk_adc_ctrl().read() }; | ||
| 589 | |||
| 590 | let base = match ctrl.auxsrc() { | ||
| 591 | ClkAdcCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), | ||
| 592 | ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), | ||
| 593 | ClkAdcCtrlAuxsrc::ROSC_CLKSRC_PH => estimate_rosc_freq(), | ||
| 594 | ClkAdcCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(), | ||
| 595 | ClkAdcCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), | ||
| 596 | ClkAdcCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), | ||
| 597 | _ => unreachable!(), | ||
| 598 | }; | ||
| 599 | |||
| 600 | let div = unsafe { c.clk_adc_div().read() }; | ||
| 601 | let int = if div.int() == 0 { 4 } else { div.int() as u32 }; | ||
| 602 | |||
| 603 | base / int | ||
| 604 | } | 601 | } |
| 605 | 602 | ||
| 606 | pub fn clk_rtc_freq() -> u32 { | 603 | pub fn clk_rtc_freq() -> u16 { |
| 607 | let c = pac::CLOCKS; | 604 | CLOCKS.rtc.load(Ordering::Relaxed) |
| 608 | let src = unsafe { c.clk_rtc_ctrl().read().auxsrc() }; | ||
| 609 | |||
| 610 | let base = match src { | ||
| 611 | ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), | ||
| 612 | ClkRtcCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), | ||
| 613 | ClkRtcCtrlAuxsrc::ROSC_CLKSRC_PH => estimate_rosc_freq(), | ||
| 614 | ClkRtcCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(), | ||
| 615 | ClkRtcCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), | ||
| 616 | ClkRtcCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), | ||
| 617 | _ => unreachable!(), | ||
| 618 | }; | ||
| 619 | |||
| 620 | let div = unsafe { c.clk_rtc_div().read() }; | ||
| 621 | let int = if div.int() == 0 { 65536 } else { div.int() }; | ||
| 622 | // TODO handle fractional clock div | ||
| 623 | let _frac = div.frac(); | ||
| 624 | |||
| 625 | base / int | ||
| 626 | } | 605 | } |
| 627 | 606 | ||
| 628 | unsafe fn start_xosc(crystal_hz: u32) { | 607 | unsafe fn start_xosc(crystal_hz: u32) { |
| @@ -640,14 +619,15 @@ unsafe fn start_xosc(crystal_hz: u32) { | |||
| 640 | } | 619 | } |
| 641 | 620 | ||
| 642 | #[inline(always)] | 621 | #[inline(always)] |
| 643 | unsafe fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) { | 622 | unsafe fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> u32 { |
| 644 | let ref_freq = input_freq / config.refdiv; | 623 | let ref_freq = input_freq / config.refdiv as u32; |
| 645 | |||
| 646 | assert!(config.fbdiv >= 16 && config.fbdiv <= 320); | 624 | assert!(config.fbdiv >= 16 && config.fbdiv <= 320); |
| 647 | assert!(config.post_div1 >= 1 && config.post_div1 <= 7); | 625 | assert!(config.post_div1 >= 1 && config.post_div1 <= 7); |
| 648 | assert!(config.post_div2 >= 1 && config.post_div2 <= 7); | 626 | assert!(config.post_div2 >= 1 && config.post_div2 <= 7); |
| 649 | assert!(config.post_div2 <= config.post_div1); | 627 | assert!(config.refdiv >= 1 && config.refdiv <= 63); |
| 650 | assert!(ref_freq >= 5_000_000 && ref_freq <= 800_000_000); | 628 | assert!(ref_freq >= 5_000_000 && ref_freq <= 800_000_000); |
| 629 | let vco_freq = ref_freq.saturating_mul(config.fbdiv as u32); | ||
| 630 | assert!(vco_freq >= 750_000_000 && vco_freq <= 1800_000_000); | ||
| 651 | 631 | ||
| 652 | // Load VCO-related dividers before starting VCO | 632 | // Load VCO-related dividers before starting VCO |
| 653 | p.cs().write(|w| w.set_refdiv(config.refdiv as _)); | 633 | p.cs().write(|w| w.set_refdiv(config.refdiv as _)); |
| @@ -671,6 +651,8 @@ unsafe fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) { | |||
| 671 | 651 | ||
| 672 | // Turn on post divider | 652 | // Turn on post divider |
| 673 | p.pwr().modify(|w| w.set_postdivpd(false)); | 653 | p.pwr().modify(|w| w.set_postdivpd(false)); |
| 654 | |||
| 655 | vco_freq / ((config.post_div1 * config.post_div2) as u32) | ||
| 674 | } | 656 | } |
| 675 | 657 | ||
| 676 | pub trait GpinPin: crate::gpio::Pin { | 658 | pub trait GpinPin: crate::gpio::Pin { |
| @@ -812,12 +794,12 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { | |||
| 812 | ClkGpoutCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), | 794 | ClkGpoutCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), |
| 813 | ClkGpoutCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), | 795 | ClkGpoutCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), |
| 814 | ClkGpoutCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), | 796 | ClkGpoutCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), |
| 815 | ClkGpoutCtrlAuxsrc::ROSC_CLKSRC => estimate_rosc_freq(), | 797 | ClkGpoutCtrlAuxsrc::ROSC_CLKSRC => rosc_freq(), |
| 816 | ClkGpoutCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(), | 798 | ClkGpoutCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(), |
| 817 | ClkGpoutCtrlAuxsrc::CLK_SYS => clk_sys_freq(), | 799 | ClkGpoutCtrlAuxsrc::CLK_SYS => clk_sys_freq(), |
| 818 | ClkGpoutCtrlAuxsrc::CLK_USB => clk_usb_freq(), | 800 | ClkGpoutCtrlAuxsrc::CLK_USB => clk_usb_freq(), |
| 819 | ClkGpoutCtrlAuxsrc::CLK_ADC => clk_adc_freq(), | 801 | ClkGpoutCtrlAuxsrc::CLK_ADC => clk_adc_freq(), |
| 820 | ClkGpoutCtrlAuxsrc::CLK_RTC => clk_rtc_freq(), | 802 | ClkGpoutCtrlAuxsrc::CLK_RTC => clk_rtc_freq() as _, |
| 821 | ClkGpoutCtrlAuxsrc::CLK_REF => clk_ref_freq(), | 803 | ClkGpoutCtrlAuxsrc::CLK_REF => clk_ref_freq(), |
| 822 | _ => unreachable!(), | 804 | _ => unreachable!(), |
| 823 | }; | 805 | }; |
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> { | |||
| 26 | into_ref!(inner); | 26 | into_ref!(inner); |
| 27 | 27 | ||
| 28 | // Set the RTC divider | 28 | // Set the RTC divider |
| 29 | unsafe { | 29 | unsafe { inner.regs().clkdiv_m1().write(|w| w.set_clkdiv_m1(clk_rtc_freq() - 1)) }; |
| 30 | inner | ||
| 31 | .regs() | ||
| 32 | .clkdiv_m1() | ||
| 33 | .write(|w| w.set_clkdiv_m1(clk_rtc_freq() as u16 - 1)) | ||
| 34 | }; | ||
| 35 | 30 | ||
| 36 | let mut result = Self { inner }; | 31 | let mut result = Self { inner }; |
| 37 | result.set_leap_year_check(true); // should be on by default, make sure this is the case. | 32 | result.set_leap_year_check(true); // should be on by default, make sure this is the case. |
