aboutsummaryrefslogtreecommitdiff
path: root/embassy-rp
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-rp')
-rw-r--r--embassy-rp/src/clocks.rs256
1 files changed, 103 insertions, 153 deletions
diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs
index 107e499b7..a4cc129ab 100644
--- a/embassy-rp/src/clocks.rs
+++ b/embassy-rp/src/clocks.rs
@@ -168,20 +168,20 @@ pub enum CoreVoltage {
168#[cfg(feature = "rp2040")] 168#[cfg(feature = "rp2040")]
169impl CoreVoltage { 169impl CoreVoltage {
170 /// Get the recommended Brown-Out Detection (BOD) setting for this voltage. 170 /// Get the recommended Brown-Out Detection (BOD) setting for this voltage.
171 /// Sets the BOD threshold to approximately 90% of the core voltage. 171 /// Sets the BOD threshold to approximately 80% of the core voltage.
172 fn recommended_bod(self) -> u8 { 172 fn recommended_bod(self) -> u8 {
173 match self { 173 match self {
174 CoreVoltage::V0_80 => 0b0110, // 0.720V (~90% of 0.80V) 174 CoreVoltage::V0_80 => 0b0100, // 0.645V (~81% of 0.80V)
175 CoreVoltage::V0_85 => 0b0111, // 0.774V (~91% of 0.85V) 175 CoreVoltage::V0_85 => 0b0101, // 0.688V (~81% of 0.85V)
176 CoreVoltage::V0_90 => 0b1000, // 0.817V (~91% of 0.90V) 176 CoreVoltage::V0_90 => 0b0110, // 0.731V (~81% of 0.90V)
177 CoreVoltage::V0_95 => 0b1001, // 0.860V (~91% of 0.95V) 177 CoreVoltage::V0_95 => 0b0111, // 0.774V (~81% of 0.95V)
178 CoreVoltage::V1_00 => 0b1010, // 0.903V (~90% of 1.00V) 178 CoreVoltage::V1_00 => 0b1000, // 0.817V (~82% of 1.00V)
179 CoreVoltage::V1_05 => 0b1011, // 0.946V (~90% of 1.05V) 179 CoreVoltage::V1_05 => 0b1000, // 0.817V (~78% of 1.05V)
180 CoreVoltage::V1_10 => 0b1100, // 0.989V (~90% of 1.10V) 180 CoreVoltage::V1_10 => 0b1001, // 0.860V (~78% of 1.10V)
181 CoreVoltage::V1_15 => 0b1101, // 1.032V (~90% of 1.15V) 181 CoreVoltage::V1_15 => 0b1010, // 0.903V (~79% of 1.15V)
182 CoreVoltage::V1_20 => 0b1110, // 1.075V (~90% of 1.20V) 182 CoreVoltage::V1_20 => 0b1011, // 0.946V (~79% of 1.20V)
183 CoreVoltage::V1_25 => 0b1111, // 1.118V (~89% of 1.25V) 183 CoreVoltage::V1_25 => 0b1100, // 0.989V (~79% of 1.25V)
184 CoreVoltage::V1_30 => 0b1111, // 1.118V (~86% of 1.30V) - using max available threshold 184 CoreVoltage::V1_30 => 0b1101, // 1.032V (~79% of 1.30V)
185 } 185 }
186 } 186 }
187} 187}
@@ -459,11 +459,6 @@ impl ClockConfig {
459 /// ``` 459 /// ```
460 #[cfg(feature = "rp2040")] 460 #[cfg(feature = "rp2040")]
461 pub fn manual_pll(xosc_hz: u32, pll_config: PllConfig, core_voltage: CoreVoltage) -> Self { 461 pub fn manual_pll(xosc_hz: u32, pll_config: PllConfig, core_voltage: CoreVoltage) -> Self {
462 // Calculate the actual output frequency for documentation
463 // let ref_freq = xosc_hz / pll_config.refdiv as u32;
464 // let vco_freq = ref_freq * pll_config.fbdiv as u32;
465 // let sys_freq = vco_freq / ((pll_config.post_div1 * pll_config.post_div2) as u32);
466
467 // Validate PLL parameters 462 // Validate PLL parameters
468 assert!(pll_config.is_valid(xosc_hz), "Invalid PLL parameters"); 463 assert!(pll_config.is_valid(xosc_hz), "Invalid PLL parameters");
469 464
@@ -893,6 +888,30 @@ pub(crate) unsafe fn init(config: ClockConfig) {
893 #[cfg(feature = "_rp235x")] 888 #[cfg(feature = "_rp235x")]
894 while c.clk_ref_selected().read() != pac::clocks::regs::ClkRefSelected(1) {} 889 while c.clk_ref_selected().read() != pac::clocks::regs::ClkRefSelected(1) {}
895 890
891 // Reset the PLLs
892 let mut peris = reset::Peripherals(0);
893 peris.set_pll_sys(true);
894 peris.set_pll_usb(true);
895 reset::reset(peris);
896 reset::unreset_wait(peris);
897
898 // let gpin0_freq = config.gpin0.map_or(0, |p| {
899 // core::mem::forget(p.1);
900 // p.0
901 // });
902 // CLOCKS.gpin0.store(gpin0_freq, Ordering::Relaxed);
903 // let gpin1_freq = config.gpin1.map_or(0, |p| {
904 // core::mem::forget(p.1);
905 // p.0
906 // });
907 // CLOCKS.gpin1.store(gpin1_freq, Ordering::Relaxed);
908
909 let rosc_freq = match config.rosc {
910 Some(config) => configure_rosc(config),
911 None => 0,
912 };
913 CLOCKS.rosc.store(rosc_freq, Ordering::Relaxed);
914
896 // Set Core Voltage (RP2040 only), if we have config for it and we're not using the default 915 // Set Core Voltage (RP2040 only), if we have config for it and we're not using the default
897 #[cfg(feature = "rp2040")] 916 #[cfg(feature = "rp2040")]
898 { 917 {
@@ -901,8 +920,9 @@ pub(crate) unsafe fn init(config: ClockConfig) {
901 let current_vsel = vreg.vreg().read().vsel(); 920 let current_vsel = vreg.vreg().read().vsel();
902 let target_vsel = voltage as u8; 921 let target_vsel = voltage as u8;
903 922
923 // If the target voltage is different from the current one, we need to change it
904 if target_vsel != current_vsel { 924 if target_vsel != current_vsel {
905 // Use modify() instead of write() to preserve the HIZ and EN bits - otherwise we will disable the regulator when changing voltage 925 // Use modify() to preserve the HIZ and EN bits - otherwise we will disable the regulator when changing voltage
906 vreg.vreg().modify(|w| w.set_vsel(target_vsel)); 926 vreg.vreg().modify(|w| w.set_vsel(target_vsel));
907 927
908 // Wait for the voltage to stabilize. Use the provided delay or default based on voltage 928 // Wait for the voltage to stabilize. Use the provided delay or default based on voltage
@@ -914,16 +934,14 @@ pub(crate) unsafe fn init(config: ClockConfig) {
914 } 934 }
915 }); 935 });
916 936
917 // We need a clock that's guaranteed to be running at this point 937 if settling_time_us != 0 {
918 // Use ROSC which should be configured by now 938 // Delay in microseconds, using the ROSC frequency to calculate cycles
919 let rosc_freq_rough = 6_000_000; // Rough ROSC frequency estimate 939 let cycles_per_us = rosc_freq / 1_000_000;
920 let cycles_per_us = rosc_freq_rough / 1_000_000; 940 let delay_cycles = settling_time_us * cycles_per_us;
921 let delay_cycles = settling_time_us * cycles_per_us; 941 cortex_m::asm::delay(delay_cycles);
922 942 }
923 // Wait for voltage to stabilize
924 cortex_m::asm::delay(delay_cycles);
925 943
926 // Only now set the BOD level after voltage has stabilized 944 // Only now set the BOD level. At htis point the voltage is considered stable.
927 vreg.bod().write(|w| { 945 vreg.bod().write(|w| {
928 w.set_vsel(voltage.recommended_bod()); 946 w.set_vsel(voltage.recommended_bod());
929 w.set_en(true); // Enable brownout detection 947 w.set_en(true); // Enable brownout detection
@@ -931,108 +949,64 @@ pub(crate) unsafe fn init(config: ClockConfig) {
931 } 949 }
932 } 950 }
933 951
934 // Configure ROSC first if present 952 let (xosc_freq, pll_sys_freq, pll_usb_freq) = match config.xosc {
935 let rosc_freq = match config.rosc {
936 Some(config) => configure_rosc(config),
937 None => 0,
938 };
939 CLOCKS.rosc.store(rosc_freq, Ordering::Relaxed);
940
941 // Configure XOSC - we'll need this for our temporary stable clock
942 let xosc_freq = match &config.xosc {
943 Some(config) => { 953 Some(config) => {
954 // start XOSC
955 // datasheet mentions support for clock inputs into XIN, but doesn't go into
956 // how this is achieved. pico-sdk doesn't support this at all.
944 start_xosc(config.hz, config.delay_multiplier); 957 start_xosc(config.hz, config.delay_multiplier);
945 config.hz
946 }
947 None => 0,
948 };
949 CLOCKS.xosc.store(xosc_freq, Ordering::Relaxed);
950 958
951 // Setup temporary stable clocks first 959 let pll_sys_freq = match config.sys_pll {
952 // Configure USB PLL for our stable temporary clock 960 Some(sys_pll_config) => match configure_pll(pac::PLL_SYS, config.hz, sys_pll_config) {
953 let pll_usb_freq = match &config.xosc {
954 Some(config) => match &config.usb_pll {
955 Some(pll_usb_config) => {
956 // Reset USB PLL
957 let mut peris = reset::Peripherals(0);
958 peris.set_pll_usb(true);
959 reset::reset(peris);
960 reset::unreset_wait(peris);
961
962 // Configure the USB PLL - this should give us 48MHz
963 let usb_pll_freq = match configure_pll(pac::PLL_USB, xosc_freq, *pll_usb_config) {
964 Ok(freq) => freq, 961 Ok(freq) => freq,
965 Err(_) => { 962 Err(e) => panic!("Failed to configure PLL_SYS: {}", e),
966 panic!("Failed to configure USB PLL"); 963 },
967 } 964 None => 0,
968 }; 965 };
966 let pll_usb_freq = match config.usb_pll {
967 Some(usb_pll_config) => match configure_pll(pac::PLL_USB, config.hz, usb_pll_config) {
968 Ok(freq) => freq,
969 Err(e) => panic!("Failed to configure PLL_USB: {}", e),
970 },
971 None => 0,
972 };
969 973
970 CLOCKS.pll_usb.store(usb_pll_freq, Ordering::Relaxed); 974 (config.hz, pll_sys_freq, pll_usb_freq)
971 usb_pll_freq 975 }
972 } 976 None => (0, 0, 0),
973 None => 0,
974 },
975 None => 0,
976 }; 977 };
977 978
978 // Configure REF clock to use XOSC 979 CLOCKS.xosc.store(xosc_freq, Ordering::Relaxed);
980 CLOCKS.pll_sys.store(pll_sys_freq, Ordering::Relaxed);
981 CLOCKS.pll_usb.store(pll_usb_freq, Ordering::Relaxed);
982
983 let (ref_src, ref_aux, clk_ref_freq) = {
984 use {ClkRefCtrlAuxsrc as Aux, ClkRefCtrlSrc as Src};
985 let div = config.ref_clk.div as u32;
986 assert!(div >= 1 && div <= 4);
987 match config.ref_clk.src {
988 RefClkSrc::Xosc => (Src::XOSC_CLKSRC, Aux::CLKSRC_PLL_USB, xosc_freq / div),
989 RefClkSrc::Rosc => (Src::ROSC_CLKSRC_PH, Aux::CLKSRC_PLL_USB, rosc_freq / div),
990 RefClkSrc::PllUsb => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_PLL_USB, pll_usb_freq / div),
991 // RefClkSrc::Gpin0 => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_GPIN0, gpin0_freq / div),
992 // RefClkSrc::Gpin1 => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_GPIN1, gpin1_freq / div),
993 }
994 };
995 assert!(clk_ref_freq != 0);
996 CLOCKS.reference.store(clk_ref_freq, Ordering::Relaxed);
979 c.clk_ref_ctrl().write(|w| { 997 c.clk_ref_ctrl().write(|w| {
980 w.set_src(ClkRefCtrlSrc::XOSC_CLKSRC); 998 w.set_src(ref_src);
999 w.set_auxsrc(ref_aux);
981 }); 1000 });
982 #[cfg(feature = "rp2040")] 1001 #[cfg(feature = "rp2040")]
983 while c.clk_ref_selected().read() != (1 << ClkRefCtrlSrc::XOSC_CLKSRC as u32) {} 1002 while c.clk_ref_selected().read() != (1 << ref_src as u32) {}
984 #[cfg(feature = "_rp235x")] 1003 #[cfg(feature = "_rp235x")]
985 while c.clk_ref_selected().read() != pac::clocks::regs::ClkRefSelected(1 << ClkRefCtrlSrc::XOSC_CLKSRC as u32) {} 1004 while c.clk_ref_selected().read() != pac::clocks::regs::ClkRefSelected(1 << ref_src as u32) {}
986 1005 c.clk_ref_div().write(|w| {
987 // First switch the system clock to a stable source (USB PLL at 48MHz) 1006 w.set_int(config.ref_clk.div);
988 // This follows the official Pico SDK's approach to ensure stability during reconfiguration
989 c.clk_sys_ctrl().write(|w| {
990 w.set_auxsrc(ClkSysCtrlAuxsrc::CLKSRC_PLL_USB);
991 w.set_src(ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX);
992 }); 1007 });
993 1008
994 #[cfg(feature = "rp2040")] 1009 // Configure tick generation on the 2040.
995 while c.clk_sys_selected().read() != (1 << ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX as u32) {}
996 #[cfg(feature = "_rp235x")]
997 while c.clk_sys_selected().read()
998 != pac::clocks::regs::ClkSysSelected(1 << ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX as u32)
999 {}
1000
1001 // Short delay after switching to USB PLL to ensure stability
1002 cortex_m::asm::delay(100);
1003
1004 // NOW CONFIGURE THE SYSTEM PLL (safely, since we're running from the USB PLL)
1005 let pll_sys_freq = match &config.xosc {
1006 Some(config) => match &config.sys_pll {
1007 Some(sys_pll_config) => {
1008 // Reset SYS PLL
1009 let mut peris = reset::Peripherals(0);
1010 peris.set_pll_sys(true);
1011 reset::reset(peris);
1012 reset::unreset_wait(peris);
1013
1014 // Configure the SYS PLL
1015 let pll_sys_freq = match configure_pll(pac::PLL_SYS, xosc_freq, *sys_pll_config) {
1016 Ok(freq) => freq,
1017 Err(_) => {
1018 panic!("Failed to configure system PLL");
1019 }
1020 };
1021
1022 // Ensure PLL is locked and stable
1023 cortex_m::asm::delay(100);
1024
1025 CLOCKS.pll_sys.store(pll_sys_freq, Ordering::Relaxed);
1026 pll_sys_freq
1027 }
1028 None => 0,
1029 },
1030 None => 0,
1031 };
1032
1033 // Configure tick generation using REF clock
1034 let clk_ref_freq = xosc_freq; // REF clock is now XOSC
1035 CLOCKS.reference.store(clk_ref_freq, Ordering::Relaxed);
1036 #[cfg(feature = "rp2040")] 1010 #[cfg(feature = "rp2040")]
1037 pac::WATCHDOG.tick().write(|w| { 1011 pac::WATCHDOG.tick().write(|w| {
1038 w.set_cycles((clk_ref_freq / 1_000_000) as u16); 1012 w.set_cycles((clk_ref_freq / 1_000_000) as u16);
@@ -1050,8 +1024,6 @@ pub(crate) unsafe fn init(config: ClockConfig) {
1050 pac::TICKS.watchdog_ctrl().write(|w| w.set_enable(true)); 1024 pac::TICKS.watchdog_ctrl().write(|w| w.set_enable(true));
1051 } 1025 }
1052 1026
1053 // NOW SWITCH THE SYSTEM CLOCK TO THE CONFIGURED SOURCE
1054 // The SYS PLL is now stable and we can safely switch to it
1055 let (sys_src, sys_aux, clk_sys_freq) = { 1027 let (sys_src, sys_aux, clk_sys_freq) = {
1056 use {ClkSysCtrlAuxsrc as Aux, ClkSysCtrlSrc as Src}; 1028 use {ClkSysCtrlAuxsrc as Aux, ClkSysCtrlSrc as Src};
1057 let (src, aux, freq) = match config.sys_clk.src { 1029 let (src, aux, freq) = match config.sys_clk.src {
@@ -1068,48 +1040,28 @@ pub(crate) unsafe fn init(config: ClockConfig) {
1068 }; 1040 };
1069 assert!(clk_sys_freq != 0); 1041 assert!(clk_sys_freq != 0);
1070 CLOCKS.sys.store(clk_sys_freq, Ordering::Relaxed); 1042 CLOCKS.sys.store(clk_sys_freq, Ordering::Relaxed);
1071 1043 if sys_src != ClkSysCtrlSrc::CLK_REF {
1072 // Set the divider before changing the source if it's increasing 1044 c.clk_sys_ctrl().write(|w| w.set_src(ClkSysCtrlSrc::CLK_REF));
1073 if config.sys_clk.div_int > 1 || config.sys_clk.div_frac > 0 { 1045 #[cfg(feature = "rp2040")]
1074 c.clk_sys_div().write(|w| { 1046 while c.clk_sys_selected().read() != (1 << ClkSysCtrlSrc::CLK_REF as u32) {}
1075 w.set_int(config.sys_clk.div_int); 1047 #[cfg(feature = "_rp235x")]
1076 w.set_frac(config.sys_clk.div_frac); 1048 while c.clk_sys_selected().read() != pac::clocks::regs::ClkSysSelected(1 << ClkSysCtrlSrc::CLK_REF as u32) {}
1077 });
1078 }
1079
1080 // Configure aux source first if needed
1081 if sys_src == ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX {
1082 c.clk_sys_ctrl().modify(|w| {
1083 w.set_auxsrc(sys_aux);
1084 });
1085 } 1049 }
1086
1087 // Now set the source
1088 c.clk_sys_ctrl().write(|w| { 1050 c.clk_sys_ctrl().write(|w| {
1089 if sys_src == ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX { 1051 w.set_auxsrc(sys_aux);
1090 w.set_auxsrc(sys_aux);
1091 }
1092 w.set_src(sys_src); 1052 w.set_src(sys_src);
1093 }); 1053 });
1094 1054
1095 // Wait for the clock to be selected
1096 #[cfg(feature = "rp2040")] 1055 #[cfg(feature = "rp2040")]
1097 while c.clk_sys_selected().read() != (1 << sys_src as u32) {} 1056 while c.clk_sys_selected().read() != (1 << sys_src as u32) {}
1098 #[cfg(feature = "_rp235x")] 1057 #[cfg(feature = "_rp235x")]
1099 while c.clk_sys_selected().read() != pac::clocks::regs::ClkSysSelected(1 << sys_src as u32) {} 1058 while c.clk_sys_selected().read() != pac::clocks::regs::ClkSysSelected(1 << sys_src as u32) {}
1100 1059
1101 // Short delay after final clock switch to ensure stability 1060 c.clk_sys_div().write(|w| {
1102 cortex_m::asm::delay(100); 1061 w.set_int(config.sys_clk.div_int);
1103 1062 w.set_frac(config.sys_clk.div_frac);
1104 // Set the divider after changing the source if it's decreasing 1063 });
1105 if config.sys_clk.div_int == 1 && config.sys_clk.div_frac == 0 {
1106 c.clk_sys_div().write(|w| {
1107 w.set_int(config.sys_clk.div_int);
1108 w.set_frac(config.sys_clk.div_frac);
1109 });
1110 }
1111 1064
1112 // CONFIGURE PERIPHERAL CLOCK
1113 let mut peris = reset::ALL_PERIPHERALS; 1065 let mut peris = reset::ALL_PERIPHERALS;
1114 1066
1115 if let Some(src) = config.peri_clk_src { 1067 if let Some(src) = config.peri_clk_src {
@@ -1136,7 +1088,6 @@ pub(crate) unsafe fn init(config: ClockConfig) {
1136 CLOCKS.peri.store(0, Ordering::Relaxed); 1088 CLOCKS.peri.store(0, Ordering::Relaxed);
1137 } 1089 }
1138 1090
1139 // CONFIGURE USB CLOCK
1140 if let Some(conf) = config.usb_clk { 1091 if let Some(conf) = config.usb_clk {
1141 c.clk_usb_div().write(|w| w.set_int(conf.div)); 1092 c.clk_usb_div().write(|w| w.set_int(conf.div));
1142 c.clk_usb_ctrl().write(|w| { 1093 c.clk_usb_ctrl().write(|w| {
@@ -1160,7 +1111,6 @@ pub(crate) unsafe fn init(config: ClockConfig) {
1160 CLOCKS.usb.store(0, Ordering::Relaxed); 1111 CLOCKS.usb.store(0, Ordering::Relaxed);
1161 } 1112 }
1162 1113
1163 // CONFIGURE ADC CLOCK
1164 if let Some(conf) = config.adc_clk { 1114 if let Some(conf) = config.adc_clk {
1165 c.clk_adc_div().write(|w| w.set_int(conf.div)); 1115 c.clk_adc_div().write(|w| w.set_int(conf.div));
1166 c.clk_adc_ctrl().write(|w| { 1116 c.clk_adc_ctrl().write(|w| {
@@ -1184,7 +1134,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
1184 CLOCKS.adc.store(0, Ordering::Relaxed); 1134 CLOCKS.adc.store(0, Ordering::Relaxed);
1185 } 1135 }
1186 1136
1187 // CONFIGURE RTC CLOCK 1137 // rp2040 specific clocks
1188 #[cfg(feature = "rp2040")] 1138 #[cfg(feature = "rp2040")]
1189 if let Some(conf) = config.rtc_clk { 1139 if let Some(conf) = config.rtc_clk {
1190 c.clk_rtc_div().write(|w| { 1140 c.clk_rtc_div().write(|w| {
@@ -1393,7 +1343,7 @@ fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> Result
1393 }); 1343 });
1394 1344
1395 // 5. Wait for PLL to lock with a timeout 1345 // 5. Wait for PLL to lock with a timeout
1396 let mut timeout = 1_000_000; // Reasonable timeout value 1346 let mut timeout = 1_000_000;
1397 while !p.cs().read().lock() { 1347 while !p.cs().read().lock() {
1398 timeout -= 1; 1348 timeout -= 1;
1399 if timeout == 0 { 1349 if timeout == 0 {