1use crate::{dma::EthernetDMA, hal::rcc::Clocks, mac::EthernetMAC, peripherals::ETHERNET_PTP};
6
7mod timestamp;
8pub use timestamp::Timestamp;
9
10#[cfg(all(not(feature = "stm32f1xx-hal"), feature = "async-await"))]
11use {core::task::Poll, futures::task::AtomicWaker};
12
13mod subseconds;
14pub use subseconds::{Subseconds, NANOS_PER_SECOND, SUBSECONDS_PER_SECOND, SUBSECONDS_TO_SECONDS};
15
16mod pps_pin;
17pub use pps_pin::PPSPin;
18
19pub struct EthernetPTP {
50 eth_ptp: ETHERNET_PTP,
51}
52
53impl EthernetPTP {
54 const fn calculate_regs(hclk: u32) -> (Subseconds, u32) {
57 let half_hclk = hclk / 2;
58
59 let stssi = Subseconds::nearest_increment(half_hclk);
62 let half_rate_subsec_increment_hz = stssi.hertz();
63
64 let tsa = ((half_rate_subsec_increment_hz as u64 * u32::MAX as u64) / hclk as u64) as u32;
68 (stssi, tsa)
69 }
70
71 pub(crate) fn new(
72 eth_ptp: ETHERNET_PTP,
73 clocks: Clocks,
74 _dma: &EthernetDMA,
78 ) -> Self {
79 EthernetMAC::mask_timestamp_trigger_interrupt();
81
82 let hclk = clocks.hclk().to_Hz();
83
84 let (stssi, tsa) = Self::calculate_regs(hclk);
85
86 eth_ptp.ptptscr.write(|w| {
88 #[cfg(not(feature = "stm32f1xx-hal"))]
90 let w = w.tssarfe().set_bit();
91
92 w.tse().set_bit().tsfcu().set_bit()
93 });
94
95 eth_ptp
97 .ptpssir
98 .write(|w| unsafe { w.stssi().bits(stssi.raw() as u8) });
99
100 let mut me = Self { eth_ptp };
101
102 me.set_addend(tsa);
103 me.set_time(Timestamp::new_unchecked(false, 0, 0));
104
105 me
106 }
107
108 pub fn subsecond_increment(&self) -> Subseconds {
110 Subseconds::new_unchecked(self.eth_ptp.ptpssir.read().stssi().bits() as u32)
111 }
112
113 pub fn addend(&self) -> u32 {
115 self.eth_ptp.ptptsar.read().bits()
116 }
117
118 #[inline(always)]
120 pub fn set_addend(&mut self, rate: u32) {
121 let ptp = &self.eth_ptp;
122 ptp.ptptsar.write(|w| unsafe { w.bits(rate) });
123
124 #[cfg(feature = "stm32f1xx-hal")]
125 {
126 while ptp.ptptscr.read().tsaru().bit_is_set() {}
127 ptp.ptptscr.modify(|_, w| w.tsaru().set_bit());
128 while ptp.ptptscr.read().tsaru().bit_is_set() {}
129 }
130
131 #[cfg(not(feature = "stm32f1xx-hal"))]
132 {
133 while ptp.ptptscr.read().ttsaru().bit_is_set() {}
134 ptp.ptptscr.modify(|_, w| w.ttsaru().set_bit());
135 while ptp.ptptscr.read().ttsaru().bit_is_set() {}
136 }
137 }
138
139 pub fn set_time(&mut self, time: Timestamp) {
141 let ptp = &self.eth_ptp;
142
143 let seconds = time.seconds();
144 let subseconds = time.subseconds_signed();
145
146 ptp.ptptshur.write(|w| unsafe { w.bits(seconds) });
147 ptp.ptptslur.write(|w| unsafe { w.bits(subseconds) });
148
149 while ptp.ptptscr.read().tssti().bit_is_set() {}
151 ptp.ptptscr.modify(|_, w| w.tssti().set_bit());
152 while ptp.ptptscr.read().tssti().bit_is_set() {}
153 }
154
155 pub fn update_time(&mut self, time: Timestamp) {
160 let ptp = &self.eth_ptp;
161
162 let seconds = time.seconds();
163 let subseconds = time.subseconds_signed();
164
165 ptp.ptptshur.write(|w| unsafe { w.bits(seconds) });
166 ptp.ptptslur.write(|w| unsafe { w.bits(subseconds) });
167
168 let read_status = || {
171 let scr = ptp.ptptscr.read();
172 scr.tsstu().bit_is_set() || scr.tssti().bit_is_set()
173 };
174
175 while read_status() {}
176 ptp.ptptscr.modify(|_, w| w.tsstu().set_bit());
177 while ptp.ptptscr.read().tsstu().bit_is_set() {}
178 }
179
180 pub fn now() -> Timestamp {
182 Self::get_time()
183 }
184
185 pub fn get_time() -> Timestamp {
187 let try_read_time = || {
188 let eth_ptp = unsafe { &*ETHERNET_PTP::ptr() };
190
191 let seconds = eth_ptp.ptptshr.read().bits();
192 let subseconds = eth_ptp.ptptslr.read().bits();
193 let seconds_after = eth_ptp.ptptshr.read().bits();
194
195 if seconds == seconds_after {
196 Ok(Timestamp::from_parts(seconds, subseconds))
197 } else {
198 Err(())
199 }
200 };
201
202 loop {
203 if let Ok(res) = try_read_time() {
204 return res;
205 }
206 }
207 }
208
209 pub fn enable_pps<P>(&mut self, pin: P) -> P::Output
211 where
212 P: PPSPin,
213 {
214 pin.enable()
215 }
216}
217
218#[cfg(not(feature = "stm32f1xx-hal"))]
222impl EthernetPTP {
223 #[cfg(feature = "async-await")]
224 fn waker() -> &'static AtomicWaker {
225 static WAKER: AtomicWaker = AtomicWaker::new();
226 &WAKER
227 }
228
229 fn set_target_time(&mut self, timestamp: Timestamp) {
231 let (high, low) = (timestamp.seconds(), timestamp.subseconds_signed());
232 self.eth_ptp
233 .ptptthr
234 .write(|w| unsafe { w.ttsh().bits(high) });
235 self.eth_ptp
236 .ptpttlr
237 .write(|w| unsafe { w.ttsl().bits(low) });
238 }
239
240 pub fn configure_target_time_interrupt(&mut self, timestamp: Timestamp) {
245 self.set_target_time(timestamp);
246 self.eth_ptp.ptptscr.modify(|_, w| w.tsite().set_bit());
247 EthernetMAC::unmask_timestamp_trigger_interrupt();
248 }
249
250 #[cfg(feature = "async-await")]
252 pub async fn wait_until(&mut self, timestamp: Timestamp) {
253 self.configure_target_time_interrupt(timestamp);
254 core::future::poll_fn(|ctx| {
255 if EthernetPTP::read_and_clear_interrupt_flag() {
256 Poll::Ready(())
257 } else if EthernetPTP::get_time().raw() >= timestamp.raw() {
258 Poll::Ready(())
259 } else {
260 EthernetPTP::waker().register(ctx.waker());
261 Poll::Pending
262 }
263 })
264 .await;
265 }
266
267 #[inline(always)]
268 fn read_and_clear_interrupt_flag() -> bool {
269 let eth_ptp = unsafe { &*ETHERNET_PTP::ptr() };
270 eth_ptp.ptptssr.read().tsttr().bit_is_set()
271 }
272
273 pub fn interrupt_handler() -> bool {
279 let eth_mac = unsafe { &*crate::peripherals::ETHERNET_MAC::ptr() };
281
282 let is_tsint = eth_mac.macsr.read().tsts().bit_is_set();
283 if is_tsint {
284 EthernetMAC::mask_timestamp_trigger_interrupt();
285 }
286
287 #[cfg(feature = "async-await")]
288 if let Some(waker) = EthernetPTP::waker().take() {
289 waker.wake();
290 } else {
291 EthernetPTP::read_and_clear_interrupt_flag();
292 }
293
294 #[cfg(not(feature = "async-await"))]
295 EthernetPTP::read_and_clear_interrupt_flag();
296
297 is_tsint
298 }
299
300 pub fn set_pps_freq(&mut self, pps_freq: u8) {
305 let pps_freq = pps_freq.min(31);
306
307 unsafe {
311 let ptpppscr = self.eth_ptp.ptpppscr.as_ptr() as *mut u32;
312 core::ptr::write_volatile(ptpppscr, pps_freq as u32);
313 }
314 }
315}
316
317#[cfg(all(test, not(target_os = "none")))]
318mod test {
319
320 use super::*;
321
322 #[test]
325 fn hclk_to_regs() {
326 for hclk_hz in (25..180).map(|v| v * 1_000_000) {
327 let (stssi, tsa) = EthernetPTP::calculate_regs(hclk_hz);
328
329 let stssi = stssi.raw() as f64;
330 let tsa = tsa as f64;
331
332 let clock_ratio = (SUBSECONDS_PER_SECOND as f64 / stssi)
334 / (hclk_hz as f64 * (tsa / 0xFFFF_FFFFu32 as f64));
335
336 let ppm = (clock_ratio - 1f64) * 1_000_000f64;
337
338 assert!(ppm <= 0.06, "{} at {}", ppm, hclk_hz);
339 }
340 }
341}