aboutsummaryrefslogtreecommitdiff
path: root/embassy-mcxa/src/rtc.rs
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-mcxa/src/rtc.rs')
-rw-r--r--embassy-mcxa/src/rtc.rs453
1 files changed, 453 insertions, 0 deletions
diff --git a/embassy-mcxa/src/rtc.rs b/embassy-mcxa/src/rtc.rs
new file mode 100644
index 000000000..f975d9c9f
--- /dev/null
+++ b/embassy-mcxa/src/rtc.rs
@@ -0,0 +1,453 @@
1//! RTC DateTime driver.
2use core::marker::PhantomData;
3
4use embassy_hal_internal::{Peri, PeripheralType};
5use maitake_sync::WaitCell;
6
7use crate::clocks::with_clocks;
8use crate::interrupt::typelevel::{Handler, Interrupt};
9use crate::pac;
10use crate::pac::rtc0::cr::Um;
11
12type Regs = pac::rtc0::RegisterBlock;
13
14/// Global wait cell for alarm notifications
15static WAKER: WaitCell = WaitCell::new();
16
17/// RTC interrupt handler.
18pub struct InterruptHandler<I: Instance> {
19 _phantom: PhantomData<I>,
20}
21
22/// Trait for RTC peripheral instances
23pub trait Instance: PeripheralType {
24 type Interrupt: Interrupt;
25 fn ptr() -> *const Regs;
26}
27
28/// Token for RTC0
29pub type Rtc0 = crate::peripherals::RTC0;
30impl Instance for crate::peripherals::RTC0 {
31 type Interrupt = crate::interrupt::typelevel::RTC;
32 #[inline(always)]
33 fn ptr() -> *const Regs {
34 pac::Rtc0::ptr()
35 }
36}
37
38/// Number of days in a standard year
39const DAYS_IN_A_YEAR: u32 = 365;
40/// Number of seconds in a day
41const SECONDS_IN_A_DAY: u32 = 86400;
42/// Number of seconds in an hour
43const SECONDS_IN_A_HOUR: u32 = 3600;
44/// Number of seconds in a minute
45const SECONDS_IN_A_MINUTE: u32 = 60;
46/// Unix epoch start year
47const YEAR_RANGE_START: u16 = 1970;
48
49/// Date and time structure for RTC operations
50#[derive(Debug, Clone, Copy)]
51pub struct RtcDateTime {
52 pub year: u16,
53 pub month: u8,
54 pub day: u8,
55 pub hour: u8,
56 pub minute: u8,
57 pub second: u8,
58}
59#[derive(Copy, Clone)]
60pub struct RtcConfig {
61 #[allow(dead_code)]
62 wakeup_select: bool,
63 update_mode: Um,
64 #[allow(dead_code)]
65 supervisor_access: bool,
66 compensation_interval: u8,
67 compensation_time: u8,
68}
69
70/// RTC interrupt enable flags
71#[derive(Copy, Clone)]
72pub struct RtcInterruptEnable;
73impl RtcInterruptEnable {
74 pub const RTC_TIME_INVALID_INTERRUPT_ENABLE: u32 = 1 << 0;
75 pub const RTC_TIME_OVERFLOW_INTERRUPT_ENABLE: u32 = 1 << 1;
76 pub const RTC_ALARM_INTERRUPT_ENABLE: u32 = 1 << 2;
77 pub const RTC_SECONDS_INTERRUPT_ENABLE: u32 = 1 << 4;
78}
79
80/// Converts a DateTime structure to Unix timestamp (seconds since 1970-01-01)
81///
82/// # Arguments
83///
84/// * `datetime` - The date and time to convert
85///
86/// # Returns
87///
88/// Unix timestamp as u32
89///
90/// # Note
91///
92/// This function handles leap years correctly.
93pub fn convert_datetime_to_seconds(datetime: &RtcDateTime) -> u32 {
94 let month_days: [u16; 13] = [0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
95
96 let mut seconds = (datetime.year as u32 - 1970) * DAYS_IN_A_YEAR;
97 seconds += (datetime.year as u32 / 4) - (1970 / 4);
98 seconds += month_days[datetime.month as usize] as u32;
99 seconds += datetime.day as u32 - 1;
100
101 if (datetime.year & 3 == 0) && (datetime.month <= 2) {
102 seconds -= 1;
103 }
104
105 seconds = seconds * SECONDS_IN_A_DAY
106 + (datetime.hour as u32 * SECONDS_IN_A_HOUR)
107 + (datetime.minute as u32 * SECONDS_IN_A_MINUTE)
108 + datetime.second as u32;
109
110 seconds
111}
112
113/// Converts Unix timestamp to DateTime structure
114///
115/// # Arguments
116///
117/// * `seconds` - Unix timestamp (seconds since 1970-01-01)
118///
119/// # Returns
120///
121/// RtcDateTime structure with the converted date and time
122///
123/// # Note
124///
125/// This function handles leap years correctly.
126pub fn convert_seconds_to_datetime(seconds: u32) -> RtcDateTime {
127 let mut seconds_remaining = seconds;
128 let mut days = seconds_remaining / SECONDS_IN_A_DAY + 1;
129 seconds_remaining %= SECONDS_IN_A_DAY;
130
131 let hour = (seconds_remaining / SECONDS_IN_A_HOUR) as u8;
132 seconds_remaining %= SECONDS_IN_A_HOUR;
133 let minute = (seconds_remaining / SECONDS_IN_A_MINUTE) as u8;
134 let second = (seconds_remaining % SECONDS_IN_A_MINUTE) as u8;
135
136 let mut year = YEAR_RANGE_START;
137 let mut days_in_year = DAYS_IN_A_YEAR;
138
139 while days > days_in_year {
140 days -= days_in_year;
141 year += 1;
142
143 days_in_year = if year.is_multiple_of(4) {
144 DAYS_IN_A_YEAR + 1
145 } else {
146 DAYS_IN_A_YEAR
147 };
148 }
149
150 let days_per_month = [
151 31,
152 if year.is_multiple_of(4) { 29 } else { 28 },
153 31,
154 30,
155 31,
156 30,
157 31,
158 31,
159 30,
160 31,
161 30,
162 31,
163 ];
164
165 let mut month = 1;
166 for (m, month_days) in days_per_month.iter().enumerate() {
167 let m = m + 1;
168 if days <= *month_days as u32 {
169 month = m;
170 break;
171 } else {
172 days -= *month_days as u32;
173 }
174 }
175
176 let day = days as u8;
177
178 RtcDateTime {
179 year,
180 month: month as u8,
181 day,
182 hour,
183 minute,
184 second,
185 }
186}
187
188/// Returns default RTC configuration
189///
190/// # Returns
191///
192/// RtcConfig with sensible default values:
193/// - No wakeup selection
194/// - Update mode 0 (immediate updates)
195/// - No supervisor access restriction
196/// - No compensation
197pub fn get_default_config() -> RtcConfig {
198 RtcConfig {
199 wakeup_select: false,
200 update_mode: Um::Um0,
201 supervisor_access: false,
202 compensation_interval: 0,
203 compensation_time: 0,
204 }
205}
206/// Minimal RTC handle for a specific instance I (store the zero-sized token like embassy)
207pub struct Rtc<'a, I: Instance> {
208 _inst: core::marker::PhantomData<&'a mut I>,
209}
210
211impl<'a, I: Instance> Rtc<'a, I> {
212 /// Create a new instance of the real time clock.
213 pub fn new(
214 _inst: Peri<'a, I>,
215 _irq: impl crate::interrupt::typelevel::Binding<I::Interrupt, InterruptHandler<I>> + 'a,
216 config: RtcConfig,
217 ) -> Self {
218 let rtc = unsafe { &*I::ptr() };
219
220 // The RTC is NOT gated by the MRCC, but we DO need to make sure the 16k clock
221 // on the vsys domain is active
222 let clocks = with_clocks(|c| c.clk_16k_vsys.clone());
223 match clocks {
224 None => panic!("Clocks have not been initialized"),
225 Some(None) => panic!("Clocks initialized, but clk_16k_vsys not active"),
226 Some(Some(_)) => {}
227 }
228
229 // RTC reset
230 rtc.cr().modify(|_, w| w.swr().set_bit());
231 rtc.cr().modify(|_, w| w.swr().clear_bit());
232 rtc.tsr().write(|w| unsafe { w.bits(1) });
233
234 rtc.cr().modify(|_, w| w.um().variant(config.update_mode));
235
236 rtc.tcr().modify(|_, w| unsafe {
237 w.cir()
238 .bits(config.compensation_interval)
239 .tcr()
240 .bits(config.compensation_time)
241 });
242
243 // Enable RTC interrupt
244 I::Interrupt::unpend();
245 unsafe { I::Interrupt::enable() };
246
247 Self {
248 _inst: core::marker::PhantomData,
249 }
250 }
251
252 /// Set the current date and time
253 ///
254 /// # Arguments
255 ///
256 /// * `datetime` - The date and time to set
257 ///
258 /// # Note
259 ///
260 /// The datetime is converted to Unix timestamp and written to the time seconds register.
261 pub fn set_datetime(&self, datetime: RtcDateTime) {
262 let rtc = unsafe { &*I::ptr() };
263 let seconds = convert_datetime_to_seconds(&datetime);
264 rtc.tsr().write(|w| unsafe { w.bits(seconds) });
265 }
266
267 /// Get the current date and time
268 ///
269 /// # Returns
270 ///
271 /// Current date and time as RtcDateTime
272 ///
273 /// # Note
274 ///
275 /// Reads the current Unix timestamp from the time seconds register and converts it.
276 pub fn get_datetime(&self) -> RtcDateTime {
277 let rtc = unsafe { &*I::ptr() };
278 let seconds = rtc.tsr().read().bits();
279 convert_seconds_to_datetime(seconds)
280 }
281
282 /// Set the alarm date and time
283 ///
284 /// # Arguments
285 ///
286 /// * `alarm` - The date and time when the alarm should trigger
287 ///
288 /// # Note
289 ///
290 /// This function:
291 /// - Clears any existing alarm by writing 0 to the alarm register
292 /// - Waits for the clear operation to complete
293 /// - Sets the new alarm time
294 /// - Waits for the write operation to complete
295 /// - Uses timeouts to prevent infinite loops
296 /// - Enables the alarm interrupt after setting
297 pub fn set_alarm(&self, alarm: RtcDateTime) {
298 let rtc = unsafe { &*I::ptr() };
299 let seconds = convert_datetime_to_seconds(&alarm);
300
301 rtc.tar().write(|w| unsafe { w.bits(0) });
302 let mut timeout = 10000;
303 while rtc.tar().read().bits() != 0 && timeout > 0 {
304 timeout -= 1;
305 }
306
307 rtc.tar().write(|w| unsafe { w.bits(seconds) });
308
309 let mut timeout = 10000;
310 while rtc.tar().read().bits() != seconds && timeout > 0 {
311 timeout -= 1;
312 }
313
314 self.set_interrupt(RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE);
315 }
316
317 /// Get the current alarm date and time
318 ///
319 /// # Returns
320 ///
321 /// Alarm date and time as RtcDateTime
322 ///
323 /// # Note
324 ///
325 /// Reads the alarm timestamp from the time alarm register and converts it.
326 pub fn get_alarm(&self) -> RtcDateTime {
327 let rtc = unsafe { &*I::ptr() };
328 let alarm_seconds = rtc.tar().read().bits();
329 convert_seconds_to_datetime(alarm_seconds)
330 }
331
332 /// Start the RTC time counter
333 ///
334 /// # Note
335 ///
336 /// Sets the Time Counter Enable (TCE) bit in the status register.
337 pub fn start(&self) {
338 let rtc = unsafe { &*I::ptr() };
339 rtc.sr().modify(|_, w| w.tce().set_bit());
340 }
341
342 /// Stop the RTC time counter
343 ///
344 /// # Note
345 ///
346 /// Clears the Time Counter Enable (TCE) bit in the status register.
347 pub fn stop(&self) {
348 let rtc = unsafe { &*I::ptr() };
349 rtc.sr().modify(|_, w| w.tce().clear_bit());
350 }
351
352 /// Enable specific RTC interrupts
353 ///
354 /// # Arguments
355 ///
356 /// * `mask` - Bitmask of interrupts to enable (use RtcInterruptEnable constants)
357 ///
358 /// # Note
359 ///
360 /// This function enables the specified interrupt types and resets the alarm occurred flag.
361 /// Available interrupts:
362 /// - Time Invalid Interrupt
363 /// - Time Overflow Interrupt
364 /// - Alarm Interrupt
365 /// - Seconds Interrupt
366 pub fn set_interrupt(&self, mask: u32) {
367 let rtc = unsafe { &*I::ptr() };
368
369 if (RtcInterruptEnable::RTC_TIME_INVALID_INTERRUPT_ENABLE & mask) != 0 {
370 rtc.ier().modify(|_, w| w.tiie().tiie_1());
371 }
372 if (RtcInterruptEnable::RTC_TIME_OVERFLOW_INTERRUPT_ENABLE & mask) != 0 {
373 rtc.ier().modify(|_, w| w.toie().toie_1());
374 }
375 if (RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE & mask) != 0 {
376 rtc.ier().modify(|_, w| w.taie().taie_1());
377 }
378 if (RtcInterruptEnable::RTC_SECONDS_INTERRUPT_ENABLE & mask) != 0 {
379 rtc.ier().modify(|_, w| w.tsie().tsie_1());
380 }
381 }
382
383 /// Disable specific RTC interrupts
384 ///
385 /// # Arguments
386 ///
387 /// * `mask` - Bitmask of interrupts to disable (use RtcInterruptEnable constants)
388 ///
389 /// # Note
390 ///
391 /// This function disables the specified interrupt types.
392 pub fn disable_interrupt(&self, mask: u32) {
393 let rtc = unsafe { &*I::ptr() };
394
395 if (RtcInterruptEnable::RTC_TIME_INVALID_INTERRUPT_ENABLE & mask) != 0 {
396 rtc.ier().modify(|_, w| w.tiie().tiie_0());
397 }
398 if (RtcInterruptEnable::RTC_TIME_OVERFLOW_INTERRUPT_ENABLE & mask) != 0 {
399 rtc.ier().modify(|_, w| w.toie().toie_0());
400 }
401 if (RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE & mask) != 0 {
402 rtc.ier().modify(|_, w| w.taie().taie_0());
403 }
404 if (RtcInterruptEnable::RTC_SECONDS_INTERRUPT_ENABLE & mask) != 0 {
405 rtc.ier().modify(|_, w| w.tsie().tsie_0());
406 }
407 }
408
409 /// Clear the alarm interrupt flag
410 ///
411 /// # Note
412 ///
413 /// This function clears the Time Alarm Interrupt Enable bit.
414 pub fn clear_alarm_flag(&self) {
415 let rtc = unsafe { &*I::ptr() };
416 rtc.ier().modify(|_, w| w.taie().clear_bit());
417 }
418
419 /// Wait for an RTC alarm to trigger.
420 ///
421 /// # Arguments
422 ///
423 /// * `alarm` - The date and time when the alarm should trigger
424 /// This function will wait until the RTC alarm is triggered.
425 /// If no alarm is scheduled, it will wait indefinitely until one is scheduled and triggered.
426 pub async fn wait_for_alarm(&mut self, alarm: RtcDateTime) {
427 let wait = WAKER.subscribe().await;
428
429 self.set_alarm(alarm);
430 self.start();
431
432 // REVISIT: propagate error?
433 let _ = wait.await;
434
435 // Clear the interrupt and disable the alarm after waking up
436 self.disable_interrupt(RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE);
437 }
438}
439
440/// RTC interrupt handler
441///
442/// This struct implements the interrupt handler for RTC events.
443impl<T: Instance> Handler<T::Interrupt> for InterruptHandler<T> {
444 unsafe fn on_interrupt() {
445 let rtc = &*pac::Rtc0::ptr();
446 // Check if this is actually a time alarm interrupt
447 let sr = rtc.sr().read();
448 if sr.taf().bit_is_set() {
449 rtc.ier().modify(|_, w| w.taie().clear_bit());
450 WAKER.wake();
451 }
452 }
453}