diff options
| author | 1-rafael-1 <[email protected]> | 2025-05-12 21:33:47 +0200 |
|---|---|---|
| committer | 1-rafael-1 <[email protected]> | 2025-05-12 21:33:47 +0200 |
| commit | 79e452922a6b467f2e8547a6b28698ed5f409705 (patch) | |
| tree | 3c9344145e0eb8cfdd35549af940ef04cf25b3c3 /embassy-rp | |
| parent | 133500167ca53cfbc5e9268356753bc0e3f8c209 (diff) | |
Add ClockError enum and update system_freq to return Result for error handling
Diffstat (limited to 'embassy-rp')
| -rw-r--r-- | embassy-rp/src/clocks.rs | 66 |
1 files changed, 47 insertions, 19 deletions
diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index bcd08c204..5872ef789 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs | |||
| @@ -82,6 +82,18 @@ use crate::{pac, reset, Peri}; | |||
| 82 | // be very useful until we have runtime clock reconfiguration. once this | 82 | // be very useful until we have runtime clock reconfiguration. once this |
| 83 | // happens we can resurrect the commented-out gpin bits. | 83 | // happens we can resurrect the commented-out gpin bits. |
| 84 | 84 | ||
| 85 | /// Clock error types. | ||
| 86 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 87 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 88 | pub enum ClockError { | ||
| 89 | /// PLL failed to lock within the timeout period. | ||
| 90 | PllLockTimedOut, | ||
| 91 | /// Could not find valid PLL parameters for system clock. | ||
| 92 | InvalidPllParameters, | ||
| 93 | /// Reading the core voltage failed due to an unexpected value in the register. | ||
| 94 | UnexpectedCoreVoltageRead, | ||
| 95 | } | ||
| 96 | |||
| 85 | struct Clocks { | 97 | struct Clocks { |
| 86 | xosc: AtomicU32, | 98 | xosc: AtomicU32, |
| 87 | sys: AtomicU32, | 99 | sys: AtomicU32, |
| @@ -144,8 +156,9 @@ pub enum PeriClkSrc { | |||
| 144 | /// **Note**: For RP235x the maximum voltage is 1.30V, unless unlocked by setting unless the voltage limit | 156 | /// **Note**: For RP235x the maximum voltage is 1.30V, unless unlocked by setting unless the voltage limit |
| 145 | /// is disabled using the disable_voltage_limit field in the vreg_ctrl register. For lack of practical use at this | 157 | /// is disabled using the disable_voltage_limit field in the vreg_ctrl register. For lack of practical use at this |
| 146 | /// point in time, this is not implemented here. So the maximum voltage in this enum is 1.30V for now. | 158 | /// point in time, this is not implemented here. So the maximum voltage in this enum is 1.30V for now. |
| 147 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
| 148 | #[repr(u8)] | 159 | #[repr(u8)] |
| 160 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
| 161 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 149 | pub enum CoreVoltage { | 162 | pub enum CoreVoltage { |
| 150 | // RP2040 voltage levels | 163 | // RP2040 voltage levels |
| 151 | #[cfg(feature = "rp2040")] | 164 | #[cfg(feature = "rp2040")] |
| @@ -468,7 +481,7 @@ impl ClockConfig { | |||
| 468 | /// # Returns | 481 | /// # Returns |
| 469 | /// | 482 | /// |
| 470 | /// A ClockConfig configured to achieve the requested system frequency using the | 483 | /// A ClockConfig configured to achieve the requested system frequency using the |
| 471 | /// the usual 12Mhz crystal, or panic if no valid parameters can be found. | 484 | /// the usual 12Mhz crystal, or an error if no valid parameters can be found. |
| 472 | /// | 485 | /// |
| 473 | /// # Note on core voltage: | 486 | /// # Note on core voltage: |
| 474 | /// | 487 | /// |
| @@ -482,7 +495,11 @@ impl ClockConfig { | |||
| 482 | /// **For RP235x**: | 495 | /// **For RP235x**: |
| 483 | /// At this point in time there is no official manufacturer endorsement for running the chip on other core voltages and/or other clock speeds than the defaults. | 496 | /// At this point in time there is no official manufacturer endorsement for running the chip on other core voltages and/or other clock speeds than the defaults. |
| 484 | /// Using this function is experimental and may not work as expected or even damage the chip. | 497 | /// Using this function is experimental and may not work as expected or even damage the chip. |
| 485 | pub fn system_freq(hz: u32) -> Self { | 498 | /// |
| 499 | /// # Returns | ||
| 500 | /// | ||
| 501 | /// A Result containing either the configured ClockConfig or a ClockError. | ||
| 502 | pub fn system_freq(hz: u32) -> Result<Self, ClockError> { | ||
| 486 | // Start with the standard configuration from crystal() | 503 | // Start with the standard configuration from crystal() |
| 487 | const DEFAULT_CRYSTAL_HZ: u32 = 12_000_000; | 504 | const DEFAULT_CRYSTAL_HZ: u32 = 12_000_000; |
| 488 | let mut config = Self::crystal(DEFAULT_CRYSTAL_HZ); | 505 | let mut config = Self::crystal(DEFAULT_CRYSTAL_HZ); |
| @@ -491,16 +508,15 @@ impl ClockConfig { | |||
| 491 | // (which is what crystal() configures by default) | 508 | // (which is what crystal() configures by default) |
| 492 | #[cfg(feature = "rp2040")] | 509 | #[cfg(feature = "rp2040")] |
| 493 | if hz == 125_000_000 { | 510 | if hz == 125_000_000 { |
| 494 | return config; | 511 | return Ok(config); |
| 495 | } | 512 | } |
| 496 | #[cfg(feature = "_rp235x")] | 513 | #[cfg(feature = "_rp235x")] |
| 497 | if hz == 150_000_000 { | 514 | if hz == 150_000_000 { |
| 498 | return config; | 515 | return Ok(config); |
| 499 | } | 516 | } |
| 500 | 517 | ||
| 501 | // Find optimal PLL parameters for the requested frequency | 518 | // Find optimal PLL parameters for the requested frequency |
| 502 | let sys_pll_params = find_pll_params(DEFAULT_CRYSTAL_HZ, hz) | 519 | let sys_pll_params = find_pll_params(DEFAULT_CRYSTAL_HZ, hz).ok_or(ClockError::InvalidPllParameters)?; |
| 503 | .unwrap_or_else(|| panic!("Could not find valid PLL parameters for system clock")); | ||
| 504 | 520 | ||
| 505 | // Replace the sys_pll configuration with our custom parameters | 521 | // Replace the sys_pll configuration with our custom parameters |
| 506 | if let Some(xosc) = &mut config.xosc { | 522 | if let Some(xosc) = &mut config.xosc { |
| @@ -525,7 +541,7 @@ impl ClockConfig { | |||
| 525 | }; | 541 | }; |
| 526 | } | 542 | } |
| 527 | 543 | ||
| 528 | config | 544 | Ok(config) |
| 529 | } | 545 | } |
| 530 | 546 | ||
| 531 | /// Configure with manual PLL settings for full control over system clock | 547 | /// Configure with manual PLL settings for full control over system clock |
| @@ -620,6 +636,7 @@ impl ClockConfig { | |||
| 620 | #[repr(u16)] | 636 | #[repr(u16)] |
| 621 | #[non_exhaustive] | 637 | #[non_exhaustive] |
| 622 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | 638 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
| 639 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 623 | pub enum RoscRange { | 640 | pub enum RoscRange { |
| 624 | /// Low range. | 641 | /// Low range. |
| 625 | Low = pac::rosc::vals::FreqRange::LOW.0, | 642 | Low = pac::rosc::vals::FreqRange::LOW.0, |
| @@ -726,6 +743,7 @@ pub struct RefClkConfig { | |||
| 726 | /// Reference clock source. | 743 | /// Reference clock source. |
| 727 | #[non_exhaustive] | 744 | #[non_exhaustive] |
| 728 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | 745 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
| 746 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 729 | pub enum RefClkSrc { | 747 | pub enum RefClkSrc { |
| 730 | /// XOSC. | 748 | /// XOSC. |
| 731 | Xosc, | 749 | Xosc, |
| @@ -741,6 +759,7 @@ pub enum RefClkSrc { | |||
| 741 | /// SYS clock source. | 759 | /// SYS clock source. |
| 742 | #[non_exhaustive] | 760 | #[non_exhaustive] |
| 743 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | 761 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
| 762 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 744 | pub enum SysClkSrc { | 763 | pub enum SysClkSrc { |
| 745 | /// REF. | 764 | /// REF. |
| 746 | Ref, | 765 | Ref, |
| @@ -779,6 +798,7 @@ pub struct SysClkConfig { | |||
| 779 | #[repr(u8)] | 798 | #[repr(u8)] |
| 780 | #[non_exhaustive] | 799 | #[non_exhaustive] |
| 781 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | 800 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
| 801 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 782 | pub enum UsbClkSrc { | 802 | pub enum UsbClkSrc { |
| 783 | /// PLL USB. | 803 | /// PLL USB. |
| 784 | PllUsb = ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB as _, | 804 | PllUsb = ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB as _, |
| @@ -807,6 +827,7 @@ pub struct UsbClkConfig { | |||
| 807 | #[repr(u8)] | 827 | #[repr(u8)] |
| 808 | #[non_exhaustive] | 828 | #[non_exhaustive] |
| 809 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | 829 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
| 830 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 810 | pub enum AdcClkSrc { | 831 | pub enum AdcClkSrc { |
| 811 | /// PLL USB. | 832 | /// PLL USB. |
| 812 | PllUsb = ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB as _, | 833 | PllUsb = ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB as _, |
| @@ -835,6 +856,7 @@ pub struct AdcClkConfig { | |||
| 835 | #[repr(u8)] | 856 | #[repr(u8)] |
| 836 | #[non_exhaustive] | 857 | #[non_exhaustive] |
| 837 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | 858 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
| 859 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 838 | #[cfg(feature = "rp2040")] | 860 | #[cfg(feature = "rp2040")] |
| 839 | pub enum RtcClkSrc { | 861 | pub enum RtcClkSrc { |
| 840 | /// PLL USB. | 862 | /// PLL USB. |
| @@ -1084,14 +1106,20 @@ pub(crate) unsafe fn init(config: ClockConfig) { | |||
| 1084 | let pll_sys_freq = match config.sys_pll { | 1106 | let pll_sys_freq = match config.sys_pll { |
| 1085 | Some(sys_pll_config) => match configure_pll(pac::PLL_SYS, config.hz, sys_pll_config) { | 1107 | Some(sys_pll_config) => match configure_pll(pac::PLL_SYS, config.hz, sys_pll_config) { |
| 1086 | Ok(freq) => freq, | 1108 | Ok(freq) => freq, |
| 1109 | #[cfg(feature = "defmt")] | ||
| 1087 | Err(e) => panic!("Failed to configure PLL_SYS: {}", e), | 1110 | Err(e) => panic!("Failed to configure PLL_SYS: {}", e), |
| 1111 | #[cfg(not(feature = "defmt"))] | ||
| 1112 | Err(_e) => panic!("Failed to configure PLL_SYS"), | ||
| 1088 | }, | 1113 | }, |
| 1089 | None => 0, | 1114 | None => 0, |
| 1090 | }; | 1115 | }; |
| 1091 | let pll_usb_freq = match config.usb_pll { | 1116 | let pll_usb_freq = match config.usb_pll { |
| 1092 | Some(usb_pll_config) => match configure_pll(pac::PLL_USB, config.hz, usb_pll_config) { | 1117 | Some(usb_pll_config) => match configure_pll(pac::PLL_USB, config.hz, usb_pll_config) { |
| 1093 | Ok(freq) => freq, | 1118 | Ok(freq) => freq, |
| 1119 | #[cfg(feature = "defmt")] | ||
| 1094 | Err(e) => panic!("Failed to configure PLL_USB: {}", e), | 1120 | Err(e) => panic!("Failed to configure PLL_USB: {}", e), |
| 1121 | #[cfg(not(feature = "defmt"))] | ||
| 1122 | Err(_e) => panic!("Failed to configure PLL_USB"), | ||
| 1095 | }, | 1123 | }, |
| 1096 | None => 0, | 1124 | None => 0, |
| 1097 | }; | 1125 | }; |
| @@ -1401,7 +1429,7 @@ pub fn clk_rtc_freq() -> u16 { | |||
| 1401 | /// | 1429 | /// |
| 1402 | /// Returns the current core voltage or an error if the voltage register | 1430 | /// Returns the current core voltage or an error if the voltage register |
| 1403 | /// contains an unknown value. | 1431 | /// contains an unknown value. |
| 1404 | pub fn core_voltage() -> Result<CoreVoltage, &'static str> { | 1432 | pub fn core_voltage() -> Result<CoreVoltage, ClockError> { |
| 1405 | #[cfg(feature = "rp2040")] | 1433 | #[cfg(feature = "rp2040")] |
| 1406 | { | 1434 | { |
| 1407 | let vreg = pac::VREG_AND_CHIP_RESET; | 1435 | let vreg = pac::VREG_AND_CHIP_RESET; |
| @@ -1418,7 +1446,7 @@ pub fn core_voltage() -> Result<CoreVoltage, &'static str> { | |||
| 1418 | 0b1101 => Ok(CoreVoltage::V1_20), | 1446 | 0b1101 => Ok(CoreVoltage::V1_20), |
| 1419 | 0b1110 => Ok(CoreVoltage::V1_25), | 1447 | 0b1110 => Ok(CoreVoltage::V1_25), |
| 1420 | 0b1111 => Ok(CoreVoltage::V1_30), | 1448 | 0b1111 => Ok(CoreVoltage::V1_30), |
| 1421 | _ => Err("Unexpected value in register"), | 1449 | _ => Err(ClockError::UnexpectedCoreVoltageRead), |
| 1422 | } | 1450 | } |
| 1423 | } | 1451 | } |
| 1424 | 1452 | ||
| @@ -1443,7 +1471,7 @@ pub fn core_voltage() -> Result<CoreVoltage, &'static str> { | |||
| 1443 | 0b01101 => Ok(CoreVoltage::V1_20), | 1471 | 0b01101 => Ok(CoreVoltage::V1_20), |
| 1444 | 0b01110 => Ok(CoreVoltage::V1_25), | 1472 | 0b01110 => Ok(CoreVoltage::V1_25), |
| 1445 | 0b01111 => Ok(CoreVoltage::V1_30), | 1473 | 0b01111 => Ok(CoreVoltage::V1_30), |
| 1446 | _ => Err("Unexpected value in register"), | 1474 | _ => Err(ClockError::UnexpectedCoreVoltageRead), |
| 1447 | // see CoreVoltage: we do not support setting Voltages higher than 1.30V at this point | 1475 | // see CoreVoltage: we do not support setting Voltages higher than 1.30V at this point |
| 1448 | } | 1476 | } |
| 1449 | } | 1477 | } |
| @@ -1461,7 +1489,7 @@ fn start_xosc(crystal_hz: u32, delay_multiplier: u32) { | |||
| 1461 | 1489 | ||
| 1462 | /// PLL (Phase-Locked Loop) configuration | 1490 | /// PLL (Phase-Locked Loop) configuration |
| 1463 | #[inline(always)] | 1491 | #[inline(always)] |
| 1464 | fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> Result<u32, &'static str> { | 1492 | fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> Result<u32, ClockError> { |
| 1465 | // Calculate reference frequency | 1493 | // Calculate reference frequency |
| 1466 | let ref_freq = input_freq / config.refdiv as u32; | 1494 | let ref_freq = input_freq / config.refdiv as u32; |
| 1467 | 1495 | ||
| @@ -1532,7 +1560,7 @@ fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> Result | |||
| 1532 | timeout -= 1; | 1560 | timeout -= 1; |
| 1533 | if timeout == 0 { | 1561 | if timeout == 0 { |
| 1534 | // PLL failed to lock, return 0 to indicate failure | 1562 | // PLL failed to lock, return 0 to indicate failure |
| 1535 | return Err("PLL failed to lock"); | 1563 | return Err(ClockError::PllLockTimedOut); |
| 1536 | } | 1564 | } |
| 1537 | } | 1565 | } |
| 1538 | 1566 | ||
| @@ -2103,21 +2131,21 @@ mod tests { | |||
| 2103 | { | 2131 | { |
| 2104 | // Test automatic voltage scaling based on frequency | 2132 | // Test automatic voltage scaling based on frequency |
| 2105 | // Under 133 MHz should use default voltage (V1_10) | 2133 | // Under 133 MHz should use default voltage (V1_10) |
| 2106 | let config = ClockConfig::system_freq(125_000_000); | 2134 | let config = ClockConfig::system_freq(125_000_000).unwrap(); |
| 2107 | assert_eq!(config.core_voltage, CoreVoltage::V1_10); | 2135 | assert_eq!(config.core_voltage, CoreVoltage::V1_10); |
| 2108 | 2136 | ||
| 2109 | // 133-200 MHz should use V1_15 | 2137 | // 133-200 MHz should use V1_15 |
| 2110 | let config = ClockConfig::system_freq(150_000_000); | 2138 | let config = ClockConfig::system_freq(150_000_000).unwrap(); |
| 2111 | assert_eq!(config.core_voltage, CoreVoltage::V1_15); | 2139 | assert_eq!(config.core_voltage, CoreVoltage::V1_15); |
| 2112 | let config = ClockConfig::system_freq(200_000_000); | 2140 | let config = ClockConfig::system_freq(200_000_000).unwrap(); |
| 2113 | assert_eq!(config.core_voltage, CoreVoltage::V1_15); | 2141 | assert_eq!(config.core_voltage, CoreVoltage::V1_15); |
| 2114 | 2142 | ||
| 2115 | // Above 200 MHz should use V1_25 | 2143 | // Above 200 MHz should use V1_15 |
| 2116 | let config = ClockConfig::system_freq(250_000_000); | 2144 | let config = ClockConfig::system_freq(250_000_000).unwrap(); |
| 2117 | assert_eq!(config.core_voltage, CoreVoltage::V1_15); | 2145 | assert_eq!(config.core_voltage, CoreVoltage::V1_15); |
| 2118 | 2146 | ||
| 2119 | // Below 125 MHz should use V1_10 | 2147 | // Below 125 MHz should use V1_10 |
| 2120 | let config = ClockConfig::system_freq(100_000_000); | 2148 | let config = ClockConfig::system_freq(100_000_000).unwrap(); |
| 2121 | assert_eq!(config.core_voltage, CoreVoltage::V1_10); | 2149 | assert_eq!(config.core_voltage, CoreVoltage::V1_10); |
| 2122 | } | 2150 | } |
| 2123 | } | 2151 | } |
