1#![doc = include_str!(concat!(env!("OUT_DIR"), "/esp_phy_config_table.md"))]
16#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
18#![doc(html_logo_url = "https://docs.espressif.com/projects/rust/esp-rs-grey-bg.svg")]
19#![no_std]
20#![deny(missing_docs)]
21
22mod fmt;
24pub(crate) mod reg_access;
25
26use core::{cell::Cell, marker::PhantomData};
27
28use esp_hal::system::Cpu;
29#[cfg(esp32)]
30use esp_hal::time::{Duration, Instant};
31use esp_sync::{NonReentrantMutex, RawMutex};
32
33static PHY_CLOCK_REF_COUNTER: embassy_sync::blocking_mutex::Mutex<RawMutex, Cell<u8>> =
35 embassy_sync::blocking_mutex::Mutex::new(Cell::new(0));
36
37fn increase_phy_clock_ref_count_internal() {
38 PHY_CLOCK_REF_COUNTER.lock(|phy_clock_ref_counter| {
39 let phy_clock_ref_count = phy_clock_ref_counter.get();
40
41 if phy_clock_ref_count == 0 {
42 phy_clocks::enable_phy(true);
43 }
44 let new_phy_clock_ref_count = unwrap!(
45 phy_clock_ref_count.checked_add(1),
46 "PHY clock ref count overflowed."
47 );
48
49 phy_clock_ref_counter.set(new_phy_clock_ref_count);
50 })
51}
52
53fn decrease_phy_clock_ref_count_internal() {
54 PHY_CLOCK_REF_COUNTER.lock(|phy_clock_ref_counter| {
55 let new_phy_clock_ref_count = unwrap!(
56 phy_clock_ref_counter.get().checked_sub(1),
57 "PHY clock ref count underflowed. Either you forgot a PhyClockGuard, or used PhyController::decrease_phy_clock_ref_count incorrectly."
58 );
59
60 if new_phy_clock_ref_count == 0 {
61 phy_clocks::enable_phy(false);
62 }
63
64 phy_clock_ref_counter.set(new_phy_clock_ref_count);
65 })
66}
67
68#[derive(Debug)]
69pub struct PhyClockGuard<'d> {
75 _phantom: PhantomData<&'d ()>,
76}
77
78impl PhyClockGuard<'_> {
79 #[inline]
80 pub fn release(self) {
84 }
86}
87
88impl Drop for PhyClockGuard<'_> {
89 fn drop(&mut self) {
90 decrease_phy_clock_ref_count_internal();
91 }
92}
93pub(crate) mod sys {
94 #[cfg(esp32)]
95 pub use esp_wifi_sys_esp32::*;
96 #[cfg(esp32c2)]
97 pub use esp_wifi_sys_esp32c2::*;
98 #[cfg(esp32c3)]
99 pub use esp_wifi_sys_esp32c3::*;
100 #[cfg(esp32c5)]
101 pub use esp_wifi_sys_esp32c5::*;
102 #[cfg(esp32c6)]
103 pub use esp_wifi_sys_esp32c6::*;
104 #[cfg(esp32c61)]
105 pub use esp_wifi_sys_esp32c61::*;
106 #[cfg(esp32h2)]
107 pub use esp_wifi_sys_esp32h2::*;
108 #[cfg(esp32s2)]
109 pub use esp_wifi_sys_esp32s2::*;
110 #[cfg(esp32s3)]
111 pub use esp_wifi_sys_esp32s3::*;
112 #[cfg(esp32s31)]
113 pub use esp_wifi_sys_esp32s31::*;
114}
115
116mod common_adapter;
117mod phy_clocks;
118mod phy_init_data;
119
120pub const PHY_CALIBRATION_DATA_LENGTH: usize =
122 core::mem::size_of::<sys::include::esp_phy_calibration_data_t>();
123
124pub type PhyCalibrationData = [u8; PHY_CALIBRATION_DATA_LENGTH];
126
127#[cfg(phy_backed_up_digital_register_count_is_set)]
128type PhyDigRegsBackup =
129 [u32; esp_metadata_generated::property!("phy.backed_up_digital_register_count")];
130
131#[cfg(esp32)]
132pub type MacTimeUpdateCb = fn(Duration);
138
139static ESP_PHY_LOCK: RawMutex = RawMutex::new();
140
141struct PhyState {
143 ref_count: usize,
145 calibration_data: Option<PhyCalibrationData>,
150 calibrated: bool,
152 calibration_result: i32,
154
155 #[cfg(phy_backed_up_digital_register_count_is_set)]
156 phy_digital_register_backup: Option<PhyDigRegsBackup>,
158
159 #[cfg(esp32)]
161 phy_clock_state_transition_timestamp: Instant,
163 #[cfg(esp32)]
164 mac_clock_delta_since_last_call: Duration,
166 #[cfg(esp32)]
167 mac_time_update_cb: Option<MacTimeUpdateCb>,
169}
170
171impl PhyState {
172 pub const fn new() -> Self {
174 Self {
175 ref_count: 0,
176 calibration_data: None,
177 calibrated: false,
178 calibration_result: 0,
179
180 #[cfg(phy_backed_up_digital_register_count_is_set)]
181 phy_digital_register_backup: None,
182
183 #[cfg(esp32)]
184 phy_clock_state_transition_timestamp: Instant::EPOCH,
185 #[cfg(esp32)]
186 mac_clock_delta_since_last_call: Duration::ZERO,
187 #[cfg(esp32)]
188 mac_time_update_cb: None,
189 }
190 }
191
192 pub fn calibration_data(&mut self) -> &mut PhyCalibrationData {
196 self.calibration_data
197 .get_or_insert([0u8; PHY_CALIBRATION_DATA_LENGTH])
198 }
199
200 fn calibrate(&mut self) {
202 #[cfg(esp32s2)]
203 unsafe {
204 sys::include::phy_eco_version_sel(esp_hal::efuse::chip_revision().major);
205 }
206 cfg_select! {
211 esp32c5 => {
212 }
217 phy_combo_module => unsafe {
218 sys::include::phy_init_param_set(1);
219 },
220 _ => {}
221 }
222
223 #[cfg(all(
224 phy_enable_usb,
225 any(soc_has_usb_fs, soc_has_usb_device),
226 not(any(esp32s2, esp32h2))
227 ))]
228 unsafe {
229 unsafe extern "C" {
232 fn phy_bbpll_en_usb(param: bool);
233 }
234 phy_bbpll_en_usb(true);
235 }
236
237 let calibration_data_available = self.calibration_data.is_some();
238 let calibration_mode = if calibration_data_available {
239 if cfg!(phy_skip_calibration_after_deep_sleep) && is_reset_from_deepsleep() {
243 sys::include::esp_phy_calibration_mode_t_PHY_RF_CAL_NONE
244 } else if cfg!(phy_full_calibration) {
245 sys::include::esp_phy_calibration_mode_t_PHY_RF_CAL_FULL
246 } else {
247 sys::include::esp_phy_calibration_mode_t_PHY_RF_CAL_PARTIAL
248 }
249 } else {
250 sys::include::esp_phy_calibration_mode_t_PHY_RF_CAL_FULL
251 };
252 let init_data = &phy_init_data::PHY_INIT_DATA_DEFAULT;
253 unsafe {
254 self.calibration_result = sys::include::register_chipv7_phy(
255 init_data,
256 self.calibration_data() as *mut PhyCalibrationData as *mut _,
257 calibration_mode,
258 );
259 }
260 self.calibrated = true;
261 }
262
263 #[cfg(phy_backed_up_digital_register_count_is_set)]
264 fn backup_digital_regs(&mut self) {
266 unsafe {
267 sys::include::phy_dig_reg_backup(
268 true,
269 self.phy_digital_register_backup.get_or_insert_default() as *mut u32,
270 );
271 }
272 }
273
274 #[cfg(phy_backed_up_digital_register_count_is_set)]
275 fn restore_digital_regs(&mut self) {
279 unsafe {
280 sys::include::phy_dig_reg_backup(
281 false,
282 self.phy_digital_register_backup
283 .as_mut()
284 .expect("Can't restore digital PHY registers from backup, without a backup.")
285 as *mut u32,
286 );
287 self.phy_digital_register_backup = None;
288 }
289 }
290
291 pub fn increase_ref_count(&mut self) {
295 if self.ref_count == 0 {
296 #[cfg(esp32)]
297 {
298 let now = Instant::now();
299 let delta = now - self.phy_clock_state_transition_timestamp;
300 self.phy_clock_state_transition_timestamp = now;
301 self.mac_clock_delta_since_last_call += delta;
302 }
303 if self.calibrated {
304 unsafe {
305 sys::include::phy_wakeup_init();
306 }
307 #[cfg(phy_backed_up_digital_register_count_is_set)]
308 self.restore_digital_regs();
309 } else {
310 self.calibrate();
311 self.calibrated = true;
312 }
313 }
314 #[cfg(esp32)]
315 if let Some(cb) = self.mac_time_update_cb {
316 (cb)(self.mac_clock_delta_since_last_call);
317 self.mac_clock_delta_since_last_call = Duration::ZERO;
318 }
319
320 self.ref_count += 1;
321 }
322
323 pub fn decrease_ref_count(&mut self) {
330 self.ref_count = self
331 .ref_count
332 .checked_sub(1)
333 .expect("PHY init ref count dropped below zero.");
334 if self.ref_count == 0 {
335 #[cfg(phy_backed_up_digital_register_count_is_set)]
336 self.backup_digital_regs();
337 unsafe {
338 sys::include::phy_close_rf();
340
341 #[cfg(not(esp32))]
343 sys::include::phy_xpd_tsens();
344 }
345 #[cfg(esp32)]
346 {
347 self.phy_clock_state_transition_timestamp = Instant::now();
348 }
349 }
353 }
354}
355
356fn is_reset_from_deepsleep() -> bool {
357 #[cfg(any(
359 esp32, esp32c2, esp32c3, esp32c5, esp32c6, esp32c61, esp32h2, esp32s2, esp32s3, esp32s31
360 ))]
361 const CORE_DEEP_SLEEP: u32 = 5;
362
363 unsafe extern "C" {
364 fn rtc_get_reset_reason(cpu_num: u32) -> u32;
365 }
366
367 let reason = unsafe { rtc_get_reset_reason(Cpu::current() as u32) };
368
369 reason == CORE_DEEP_SLEEP
370}
371
372static PHY_STATE: NonReentrantMutex<PhyState> = NonReentrantMutex::new(PhyState::new());
374
375#[derive(Debug)]
381pub struct PhyInitGuard<'d> {
382 _phy_clock_guard: PhyClockGuard<'d>,
383}
384
385impl PhyInitGuard<'_> {
386 #[inline]
387 pub fn release(self) {
391 }
393}
394
395impl Drop for PhyInitGuard<'_> {
396 fn drop(&mut self) {
397 PHY_STATE.with(|phy_state| phy_state.decrease_ref_count());
398 }
399}
400
401pub fn enable_phy<'d>() -> PhyInitGuard<'d> {
407 let _phy_clock_guard = enable_phy_clock();
410
411 PHY_STATE.with(|phy_state| phy_state.increase_ref_count());
412
413 PhyInitGuard { _phy_clock_guard }
414}
415
416pub fn disable_phy() {
420 PHY_STATE.with(|phy_state| phy_state.decrease_ref_count());
421 decrease_phy_clock_ref_count_internal();
425}
426
427pub fn enable_phy_with_wifi_rx() {
432 core::mem::forget(enable_phy());
433 set_wifi_rx_enabled(true);
434}
435
436pub fn disable_phy_with_wifi_rx() {
440 set_wifi_rx_enabled(false);
441 disable_phy();
442}
443
444fn set_wifi_rx_enabled(enabled: bool) {
453 cfg_select! {
454 esp32c5 => {
455 let _ = enabled;
462 }
463 phy_combo_module => unsafe {
464 sys::include::phy_wifi_enable_set(enabled as u8);
465 },
466 _ => {
467 let _ = enabled;
468 }
469 }
470}
471
472pub fn enable_phy_clock<'d>() -> PhyClockGuard<'d> {
476 increase_phy_clock_ref_count_internal();
477 PhyClockGuard {
478 _phantom: PhantomData,
479 }
480}
481
482#[cfg(esp32)]
486pub fn set_mac_time_update_cb(mac_time_update_cb: MacTimeUpdateCb) {
487 PHY_STATE.with(|phy_state| phy_state.mac_time_update_cb = Some(mac_time_update_cb));
488}
489
490#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
491#[cfg_attr(feature = "defmt", derive(defmt::Format))]
492pub struct CalibrationDataAlreadySetError;
494
495#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
496#[cfg_attr(feature = "defmt", derive(defmt::Format))]
497pub struct NoCalibrationDataError;
499
500#[derive(Debug, Clone, Copy)]
502#[cfg_attr(feature = "defmt", derive(defmt::Format))]
503#[non_exhaustive]
504pub enum CalibrationResult {
505 Ok,
507
508 DataCheckFailed,
510}
511
512pub fn set_phy_calibration_data(
514 calibration_data: &PhyCalibrationData,
515) -> Result<(), CalibrationDataAlreadySetError> {
516 PHY_STATE.with(|phy_state| {
517 if phy_state.calibration_data.is_some() {
518 Err(CalibrationDataAlreadySetError)
519 } else {
520 phy_state.calibration_data = Some(*calibration_data);
521 Ok(())
522 }
523 })
524}
525
526pub fn backup_phy_calibration_data(
528 buffer: &mut PhyCalibrationData,
529) -> Result<(), NoCalibrationDataError> {
530 PHY_STATE.with(|phy_state| {
531 phy_state
532 .calibration_data
533 .as_mut()
534 .ok_or(NoCalibrationDataError)
535 .map(|calibration_data| buffer.copy_from_slice(calibration_data.as_slice()))
536 })
537}
538
539pub fn last_calibration_result() -> Option<CalibrationResult> {
544 PHY_STATE.with(|phy_state| {
545 if phy_state.calibrated {
546 Some(
547 if phy_state.calibration_result == sys::include::ESP_CAL_DATA_CHECK_FAIL as i32 {
548 CalibrationResult::DataCheckFailed
549 } else {
550 CalibrationResult::Ok
551 },
552 )
553 } else {
554 None
555 }
556 })
557}