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.rs299
1 files changed, 299 insertions, 0 deletions
diff --git a/embassy-mcxa/src/rtc.rs b/embassy-mcxa/src/rtc.rs
new file mode 100644
index 000000000..b750a97ea
--- /dev/null
+++ b/embassy-mcxa/src/rtc.rs
@@ -0,0 +1,299 @@
1//! RTC DateTime driver.
2use core::sync::atomic::{AtomicBool, Ordering};
3
4use embassy_hal_internal::{Peri, PeripheralType};
5
6use crate::clocks::with_clocks;
7use crate::pac;
8use crate::pac::rtc0::cr::Um;
9
10type Regs = pac::rtc0::RegisterBlock;
11
12static ALARM_TRIGGERED: AtomicBool = AtomicBool::new(false);
13
14// Token-based instance pattern like embassy-imxrt
15pub trait Instance: PeripheralType {
16 fn ptr() -> *const Regs;
17}
18
19/// Token for RTC0
20pub type Rtc0 = crate::peripherals::RTC0;
21impl Instance for crate::peripherals::RTC0 {
22 #[inline(always)]
23 fn ptr() -> *const Regs {
24 pac::Rtc0::ptr()
25 }
26}
27
28const DAYS_IN_A_YEAR: u32 = 365;
29const SECONDS_IN_A_DAY: u32 = 86400;
30const SECONDS_IN_A_HOUR: u32 = 3600;
31const SECONDS_IN_A_MINUTE: u32 = 60;
32const YEAR_RANGE_START: u16 = 1970;
33
34#[derive(Debug, Clone, Copy)]
35pub struct RtcDateTime {
36 pub year: u16,
37 pub month: u8,
38 pub day: u8,
39 pub hour: u8,
40 pub minute: u8,
41 pub second: u8,
42}
43#[derive(Copy, Clone)]
44pub struct RtcConfig {
45 #[allow(dead_code)]
46 wakeup_select: bool,
47 update_mode: Um,
48 #[allow(dead_code)]
49 supervisor_access: bool,
50 compensation_interval: u8,
51 compensation_time: u8,
52}
53
54#[derive(Copy, Clone)]
55pub struct RtcInterruptEnable;
56impl RtcInterruptEnable {
57 pub const RTC_TIME_INVALID_INTERRUPT_ENABLE: u32 = 1 << 0;
58 pub const RTC_TIME_OVERFLOW_INTERRUPT_ENABLE: u32 = 1 << 1;
59 pub const RTC_ALARM_INTERRUPT_ENABLE: u32 = 1 << 2;
60 pub const RTC_SECONDS_INTERRUPT_ENABLE: u32 = 1 << 4;
61}
62
63pub fn convert_datetime_to_seconds(datetime: &RtcDateTime) -> u32 {
64 let month_days: [u16; 13] = [0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
65
66 let mut seconds = (datetime.year as u32 - 1970) * DAYS_IN_A_YEAR;
67 seconds += (datetime.year as u32 / 4) - (1970 / 4);
68 seconds += month_days[datetime.month as usize] as u32;
69 seconds += datetime.day as u32 - 1;
70
71 if (datetime.year & 3 == 0) && (datetime.month <= 2) {
72 seconds -= 1;
73 }
74
75 seconds = seconds * SECONDS_IN_A_DAY
76 + (datetime.hour as u32 * SECONDS_IN_A_HOUR)
77 + (datetime.minute as u32 * SECONDS_IN_A_MINUTE)
78 + datetime.second as u32;
79
80 seconds
81}
82
83pub fn convert_seconds_to_datetime(seconds: u32) -> RtcDateTime {
84 let mut seconds_remaining = seconds;
85 let mut days = seconds_remaining / SECONDS_IN_A_DAY + 1;
86 seconds_remaining %= SECONDS_IN_A_DAY;
87
88 let hour = (seconds_remaining / SECONDS_IN_A_HOUR) as u8;
89 seconds_remaining %= SECONDS_IN_A_HOUR;
90 let minute = (seconds_remaining / SECONDS_IN_A_MINUTE) as u8;
91 let second = (seconds_remaining % SECONDS_IN_A_MINUTE) as u8;
92
93 let mut year = YEAR_RANGE_START;
94 let mut days_in_year = DAYS_IN_A_YEAR;
95
96 while days > days_in_year {
97 days -= days_in_year;
98 year += 1;
99
100 days_in_year = if year.is_multiple_of(4) {
101 DAYS_IN_A_YEAR + 1
102 } else {
103 DAYS_IN_A_YEAR
104 };
105 }
106
107 let days_per_month = [
108 31,
109 if year.is_multiple_of(4) { 29 } else { 28 },
110 31,
111 30,
112 31,
113 30,
114 31,
115 31,
116 30,
117 31,
118 30,
119 31,
120 ];
121
122 let mut month = 1;
123 for (m, month_days) in days_per_month.iter().enumerate() {
124 let m = m + 1;
125 if days <= *month_days as u32 {
126 month = m;
127 break;
128 } else {
129 days -= *month_days as u32;
130 }
131 }
132
133 let day = days as u8;
134
135 RtcDateTime {
136 year,
137 month: month as u8,
138 day,
139 hour,
140 minute,
141 second,
142 }
143}
144
145pub fn get_default_config() -> RtcConfig {
146 RtcConfig {
147 wakeup_select: false,
148 update_mode: Um::Um0,
149 supervisor_access: false,
150 compensation_interval: 0,
151 compensation_time: 0,
152 }
153}
154/// Minimal RTC handle for a specific instance I (store the zero-sized token like embassy)
155pub struct Rtc<'a, I: Instance> {
156 _inst: core::marker::PhantomData<&'a mut I>,
157}
158
159impl<'a, I: Instance> Rtc<'a, I> {
160 /// initialize RTC
161 pub fn new(_inst: Peri<'a, I>, config: RtcConfig) -> Self {
162 let rtc = unsafe { &*I::ptr() };
163
164 // The RTC is NOT gated by the MRCC, but we DO need to make sure the 16k clock
165 // on the vsys domain is active
166 let clocks = with_clocks(|c| c.clk_16k_vsys.clone());
167 match clocks {
168 None => panic!("Clocks have not been initialized"),
169 Some(None) => panic!("Clocks initialized, but clk_16k_vsys not active"),
170 Some(Some(_)) => {}
171 }
172
173 /* RTC reset */
174 rtc.cr().modify(|_, w| w.swr().set_bit());
175 rtc.cr().modify(|_, w| w.swr().clear_bit());
176 rtc.tsr().write(|w| unsafe { w.bits(1) });
177
178 rtc.cr().modify(|_, w| w.um().variant(config.update_mode));
179
180 rtc.tcr().modify(|_, w| unsafe {
181 w.cir()
182 .bits(config.compensation_interval)
183 .tcr()
184 .bits(config.compensation_time)
185 });
186
187 Self {
188 _inst: core::marker::PhantomData,
189 }
190 }
191
192 pub fn set_datetime(&self, datetime: RtcDateTime) {
193 let rtc = unsafe { &*I::ptr() };
194 let seconds = convert_datetime_to_seconds(&datetime);
195 rtc.tsr().write(|w| unsafe { w.bits(seconds) });
196 }
197
198 pub fn get_datetime(&self) -> RtcDateTime {
199 let rtc = unsafe { &*I::ptr() };
200 let seconds = rtc.tsr().read().bits();
201 convert_seconds_to_datetime(seconds)
202 }
203
204 pub fn set_alarm(&self, alarm: RtcDateTime) {
205 let rtc = unsafe { &*I::ptr() };
206 let seconds = convert_datetime_to_seconds(&alarm);
207
208 rtc.tar().write(|w| unsafe { w.bits(0) });
209 let mut timeout = 10000;
210 while rtc.tar().read().bits() != 0 && timeout > 0 {
211 timeout -= 1;
212 }
213
214 rtc.tar().write(|w| unsafe { w.bits(seconds) });
215
216 let mut timeout = 10000;
217 while rtc.tar().read().bits() != seconds && timeout > 0 {
218 timeout -= 1;
219 }
220 }
221
222 pub fn get_alarm(&self) -> RtcDateTime {
223 let rtc = unsafe { &*I::ptr() };
224 let alarm_seconds = rtc.tar().read().bits();
225 convert_seconds_to_datetime(alarm_seconds)
226 }
227
228 pub fn start(&self) {
229 let rtc = unsafe { &*I::ptr() };
230 rtc.sr().modify(|_, w| w.tce().set_bit());
231 }
232
233 pub fn stop(&self) {
234 let rtc = unsafe { &*I::ptr() };
235 rtc.sr().modify(|_, w| w.tce().clear_bit());
236 }
237
238 pub fn set_interrupt(&self, mask: u32) {
239 let rtc = unsafe { &*I::ptr() };
240
241 if (RtcInterruptEnable::RTC_TIME_INVALID_INTERRUPT_ENABLE & mask) != 0 {
242 rtc.ier().modify(|_, w| w.tiie().tiie_1());
243 }
244 if (RtcInterruptEnable::RTC_TIME_OVERFLOW_INTERRUPT_ENABLE & mask) != 0 {
245 rtc.ier().modify(|_, w| w.toie().toie_1());
246 }
247 if (RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE & mask) != 0 {
248 rtc.ier().modify(|_, w| w.taie().taie_1());
249 }
250 if (RtcInterruptEnable::RTC_SECONDS_INTERRUPT_ENABLE & mask) != 0 {
251 rtc.ier().modify(|_, w| w.tsie().tsie_1());
252 }
253
254 ALARM_TRIGGERED.store(false, Ordering::SeqCst);
255 }
256
257 pub fn disable_interrupt(&self, mask: u32) {
258 let rtc = unsafe { &*I::ptr() };
259
260 if (RtcInterruptEnable::RTC_TIME_INVALID_INTERRUPT_ENABLE & mask) != 0 {
261 rtc.ier().modify(|_, w| w.tiie().tiie_0());
262 }
263 if (RtcInterruptEnable::RTC_TIME_OVERFLOW_INTERRUPT_ENABLE & mask) != 0 {
264 rtc.ier().modify(|_, w| w.toie().toie_0());
265 }
266 if (RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE & mask) != 0 {
267 rtc.ier().modify(|_, w| w.taie().taie_0());
268 }
269 if (RtcInterruptEnable::RTC_SECONDS_INTERRUPT_ENABLE & mask) != 0 {
270 rtc.ier().modify(|_, w| w.tsie().tsie_0());
271 }
272 }
273
274 pub fn clear_alarm_flag(&self) {
275 let rtc = unsafe { &*I::ptr() };
276 rtc.ier().modify(|_, w| w.taie().clear_bit());
277 }
278
279 pub fn is_alarm_triggered(&self) -> bool {
280 ALARM_TRIGGERED.load(Ordering::Relaxed)
281 }
282}
283
284pub fn on_interrupt() {
285 let rtc = unsafe { &*pac::Rtc0::ptr() };
286 // Check if this is actually a time alarm interrupt
287 let sr = rtc.sr().read();
288 if sr.taf().bit_is_set() {
289 rtc.ier().modify(|_, w| w.taie().clear_bit());
290 ALARM_TRIGGERED.store(true, Ordering::SeqCst);
291 }
292}
293
294pub struct RtcHandler;
295impl crate::interrupt::typelevel::Handler<crate::interrupt::typelevel::RTC> for RtcHandler {
296 unsafe fn on_interrupt() {
297 on_interrupt();
298 }
299}