1#![doc = include_str!(concat!(env!("OUT_DIR"), "/esp_phy_config_table.md"))]
15#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
17#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
18#![no_std]
19
20mod fmt;
22
23#[cfg(esp32)]
24use esp_hal::time::{Duration, Instant};
25use esp_hal::{
26 clock::{ModemClockController, PhyClockGuard},
27 rtc_cntl::{SocResetReason, reset_reason},
28 system::Cpu,
29};
30use esp_sync::{NonReentrantMutex, RawMutex};
31use esp_wifi_sys::include::*;
32
33mod common_adapter;
34mod phy_init_data;
35
36pub(crate) mod private {
37 pub trait Sealed {}
38}
39
40pub const PHY_CALIBRATION_DATA_LENGTH: usize =
42 core::mem::size_of::<esp_wifi_sys::include::esp_phy_calibration_data_t>();
43
44pub type PhyCalibrationData = [u8; PHY_CALIBRATION_DATA_LENGTH];
46
47#[cfg(phy_backed_up_digital_register_count_is_set)]
48type PhyDigRegsBackup =
49 [u32; esp_metadata_generated::property!("phy.backed_up_digital_register_count")];
50
51#[cfg(esp32)]
52pub type MacTimeUpdateCb = fn(Duration);
58
59static ESP_PHY_LOCK: RawMutex = RawMutex::new();
60
61struct PhyState {
63 ref_count: usize,
65 calibration_data: Option<PhyCalibrationData>,
70 calibrated: bool,
72
73 #[cfg(phy_backed_up_digital_register_count_is_set)]
74 phy_digital_register_backup: Option<PhyDigRegsBackup>,
76
77 #[cfg(esp32)]
79 phy_clock_state_transition_timestamp: Instant,
81 #[cfg(esp32)]
82 mac_clock_delta_since_last_call: Duration,
84 #[cfg(esp32)]
85 mac_time_update_cb: Option<MacTimeUpdateCb>,
87}
88
89impl PhyState {
90 pub const fn new() -> Self {
92 Self {
93 ref_count: 0,
94 calibration_data: None,
95 calibrated: false,
96
97 #[cfg(phy_backed_up_digital_register_count_is_set)]
98 phy_digital_register_backup: None,
99
100 #[cfg(esp32)]
101 phy_clock_state_transition_timestamp: Instant::EPOCH,
102 #[cfg(esp32)]
103 mac_clock_delta_since_last_call: Duration::ZERO,
104 #[cfg(esp32)]
105 mac_time_update_cb: None,
106 }
107 }
108
109 pub fn calibration_data(&mut self) -> &mut PhyCalibrationData {
113 self.calibration_data
114 .get_or_insert([0u8; PHY_CALIBRATION_DATA_LENGTH])
115 }
116
117 fn calibrate(&mut self) {
119 #[cfg(esp32s2)]
120 unsafe {
121 use esp_hal::efuse::Efuse;
122 phy_eco_version_sel(Efuse::major_chip_version());
123 }
124 #[cfg(all(
132 phy_enable_usb,
133 any(soc_has_usb0, soc_has_usb_device),
134 not(any(esp32s2, esp32h2))
135 ))]
136 unsafe {
137 unsafe extern "C" {
138 fn phy_bbpll_en_usb(param: bool);
139 }
140 phy_bbpll_en_usb(true);
141 }
142
143 let calibration_data_available = self.calibration_data.is_some();
144 let calibration_mode = if calibration_data_available {
145 if cfg!(phy_skip_calibration_after_deep_sleep)
149 && reset_reason(Cpu::current()) == Some(SocResetReason::CoreDeepSleep)
150 {
151 esp_phy_calibration_mode_t_PHY_RF_CAL_NONE
152 } else if cfg!(phy_full_calibration) {
153 esp_phy_calibration_mode_t_PHY_RF_CAL_FULL
154 } else {
155 esp_phy_calibration_mode_t_PHY_RF_CAL_PARTIAL
156 }
157 } else {
158 esp_phy_calibration_mode_t_PHY_RF_CAL_FULL
159 };
160 let init_data = &phy_init_data::PHY_INIT_DATA_DEFAULT;
161 unsafe {
162 register_chipv7_phy(
163 init_data,
164 self.calibration_data() as *mut PhyCalibrationData as *mut _,
165 calibration_mode,
166 );
167 }
168 self.calibrated = true;
169 }
170
171 #[cfg(phy_backed_up_digital_register_count_is_set)]
172 fn backup_digital_regs(&mut self) {
174 unsafe {
175 phy_dig_reg_backup(
176 true,
177 self.phy_digital_register_backup.get_or_insert_default() as *mut u32,
178 );
179 }
180 }
181
182 #[cfg(phy_backed_up_digital_register_count_is_set)]
183 fn restore_digital_regs(&mut self) {
187 unsafe {
188 phy_dig_reg_backup(
189 false,
190 self.phy_digital_register_backup
191 .as_mut()
192 .expect("Can't restore digital PHY registers from backup, without a backup.")
193 as *mut u32,
194 );
195 self.phy_digital_register_backup = None;
196 }
197 }
198
199 pub fn increase_ref_count(&mut self) {
203 if self.ref_count == 0 {
204 #[cfg(esp32)]
205 {
206 let now = Instant::now();
207 let delta = now - self.phy_clock_state_transition_timestamp;
208 self.phy_clock_state_transition_timestamp = now;
209 self.mac_clock_delta_since_last_call += delta;
210 }
211 if self.calibrated {
212 unsafe {
213 phy_wakeup_init();
214 }
215 #[cfg(phy_backed_up_digital_register_count_is_set)]
216 self.restore_digital_regs();
217 } else {
218 self.calibrate();
219 self.calibrated = true;
220 }
221 }
222 #[cfg(esp32)]
223 if let Some(cb) = self.mac_time_update_cb {
224 (cb)(self.mac_clock_delta_since_last_call);
225 self.mac_clock_delta_since_last_call = Duration::ZERO;
226 }
227
228 self.ref_count += 1;
229 }
230
231 pub fn decrease_ref_count(&mut self) {
238 self.ref_count = self
239 .ref_count
240 .checked_sub(1)
241 .expect("PHY init ref count dropped below zero.");
242 if self.ref_count == 0 {
243 #[cfg(phy_backed_up_digital_register_count_is_set)]
244 self.backup_digital_regs();
245 unsafe {
246 phy_close_rf();
248
249 #[cfg(not(esp32))]
251 phy_xpd_tsens();
252 }
253 #[cfg(esp32)]
254 {
255 self.phy_clock_state_transition_timestamp = Instant::now();
256 }
257 }
261 }
262}
263
264static PHY_STATE: NonReentrantMutex<PhyState> = NonReentrantMutex::new(PhyState::new());
266
267#[derive(Debug)]
273pub struct PhyInitGuard<'d> {
274 _phy_clock_guard: PhyClockGuard<'d>,
275}
276
277impl PhyInitGuard<'_> {
278 #[inline]
279 pub fn release(self) {}
283}
284
285impl Drop for PhyInitGuard<'_> {
286 fn drop(&mut self) {
287 PHY_STATE.with(|phy_state| phy_state.decrease_ref_count());
288 }
289}
290
291pub trait PhyController<'d>: private::Sealed + ModemClockController<'d> {
293 fn enable_phy(&self) -> PhyInitGuard<'d> {
294 let _phy_clock_guard = self.enable_phy_clock();
297
298 PHY_STATE.with(|phy_state| phy_state.increase_ref_count());
299
300 PhyInitGuard { _phy_clock_guard }
301 }
302
303 fn decrease_phy_ref_count(&self) {
312 PHY_STATE.with(|phy_state| phy_state.decrease_ref_count());
313 self.decrease_phy_clock_ref_count();
314 }
315}
316macro_rules! impl_phy_controller {
317 ($feature_gate:ident, $peripheral:tt) => {
318 #[cfg($feature_gate)]
319 impl private::Sealed for esp_hal::peripherals::$peripheral<'_> {}
320
321 #[cfg($feature_gate)]
322 impl<'d> PhyController<'d> for esp_hal::peripherals::$peripheral<'d> {}
323 };
324}
325impl_phy_controller!(wifi, WIFI);
326impl_phy_controller!(bt, BT);
327impl_phy_controller!(ieee802154, IEEE802154);
328
329#[cfg(esp32)]
330pub trait MacTimeExt {
332 fn set_mac_time_update_cb(&self, mac_time_update_cb: MacTimeUpdateCb) {
336 PHY_STATE.with(|phy_state| phy_state.mac_time_update_cb = Some(mac_time_update_cb));
337 }
338}
339#[cfg(esp32)]
340impl MacTimeExt for esp_hal::peripherals::WIFI<'_> {}
341
342#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
343#[cfg_attr(feature = "defmt", derive(defmt::Format))]
344pub struct CalibrationDataAlreadySetError;
346
347#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
348#[cfg_attr(feature = "defmt", derive(defmt::Format))]
349pub struct NoCalibrationDataError;
351
352pub fn set_phy_calibration_data(
354 calibration_data: &PhyCalibrationData,
355) -> Result<(), CalibrationDataAlreadySetError> {
356 PHY_STATE.with(|phy_state| {
357 if phy_state.calibration_data.is_some() {
358 Err(CalibrationDataAlreadySetError)
359 } else {
360 phy_state.calibration_data = Some(*calibration_data);
361 Ok(())
362 }
363 })
364}
365
366pub fn backup_phy_calibration_data(
368 buffer: &mut PhyCalibrationData,
369) -> Result<(), NoCalibrationDataError> {
370 PHY_STATE.with(|phy_state| {
371 phy_state
372 .calibration_data
373 .as_mut()
374 .ok_or(NoCalibrationDataError)
375 .map(|calibration_data| buffer.copy_from_slice(calibration_data.as_slice()))
376 })
377}