strat9_kernel/hardware/timer/
rtc.rs1#![allow(dead_code)]
11
12use crate::arch::x86_64::io::{inb, outb};
13use core::sync::atomic::{AtomicBool, AtomicU64, Ordering};
14use spin::Mutex;
15
16const CMOS_ADDR_PORT: u16 = 0x70;
18const CMOS_DATA_PORT: u16 = 0x71;
19
20const CMOS_REG_SECOND: u8 = 0x00;
22const CMOS_REG_MINUTE: u8 = 0x02;
23const CMOS_REG_HOUR: u8 = 0x04;
24const CMOS_REG_WEEKDAY: u8 = 0x06;
25const CMOS_REG_DAY: u8 = 0x07;
26const CMOS_REG_MONTH: u8 = 0x08;
27const CMOS_REG_YEAR: u8 = 0x09;
28const CMOS_REG_STATUS_A: u8 = 0x0A;
29const CMOS_REG_STATUS_B: u8 = 0x0B;
30const CMOS_REG_STATUS_C: u8 = 0x0C;
31
32const STATUS_A_UIP: u8 = 0x80; const STATUS_B_PIE: u8 = 0x40; const STATUS_B_UIE: u8 = 0x10; const STATUS_B_DM: u8 = 0x04; const STATUS_B_24H: u8 = 0x02; const RTC_FREQ_1024: u8 = 6; const RTC_FREQ_256: u8 = 8; const RTC_FREQ_64: u8 = 10; const RTC_FREQ_16: u8 = 12; const RTC_FREQ_4: u8 = 14; const RTC_FREQ_1: u8 = 15; const CENTURY_REG_CANDIDATES: &[u8] = &[0x32, 0x37];
51
52#[derive(Clone, Copy, Debug)]
54pub struct RtcDateTime {
55 pub second: u8,
56 pub minute: u8,
57 pub hour: u8,
58 pub weekday: u8, pub day: u8,
60 pub month: u8,
61 pub year: u16,
62 pub century: u16,
63}
64
65impl RtcDateTime {
66 pub fn new() -> Self {
68 Self {
69 second: 0,
70 minute: 0,
71 hour: 0,
72 weekday: 0,
73 day: 0,
74 month: 0,
75 year: 2000,
76 century: 20,
77 }
78 }
79
80 pub fn to_timestamp(&self) -> u64 {
82 let mut year = self.year as i64;
83 let mut month = self.month as i64;
84 let day = self.day as i64;
85 let hour = self.hour as i64;
86 let minute = self.minute as i64;
87 let second = self.second as i64;
88
89 if month <= 2 {
91 year -= 1;
92 month += 12;
93 }
94
95 let days =
97 365 * year + year / 4 - year / 100 + year / 400 + (153 * (month - 3) + 2) / 5 + day
98 - 1
99 - 719468;
100
101 (days * 86400 + hour * 3600 + minute * 60 + second) as u64
103 }
104
105 pub fn to_string(&self) -> [u8; 19] {
107 let mut buf = [0u8; 19];
108 let year = self.year;
109
110 buf[0] = b'0' + ((year / 1000) % 10) as u8;
112 buf[1] = b'0' + ((year / 100) % 10) as u8;
113 buf[2] = b'0' + ((year / 10) % 10) as u8;
114 buf[3] = b'0' + (year % 10) as u8;
115 buf[4] = b'-';
116 buf[5] = b'0' + (self.month / 10);
117 buf[6] = b'0' + (self.month % 10);
118 buf[7] = b'-';
119 buf[8] = b'0' + (self.day / 10);
120 buf[9] = b'0' + (self.day % 10);
121 buf[10] = b'T';
122 buf[11] = b'0' + (self.hour / 10);
123 buf[12] = b'0' + (self.hour % 10);
124 buf[13] = b':';
125 buf[14] = b'0' + (self.minute / 10);
126 buf[15] = b'0' + (self.minute % 10);
127 buf[16] = b':';
128 buf[17] = b'0' + (self.second / 10);
129 buf[18] = b'0' + (self.second % 10);
130
131 buf
132 }
133}
134
135pub struct RtcDriver {
137 cmos_century_reg: u8,
138 use_binary: bool,
139 last_update_tick: AtomicU64,
140}
141
142unsafe impl Send for RtcDriver {}
143unsafe impl Sync for RtcDriver {}
144
145static RTC_DRIVER: Mutex<Option<RtcDriver>> = Mutex::new(None);
146static RTC_INITIALIZED: AtomicBool = AtomicBool::new(false);
147static RTC_LAST_TICK: AtomicU64 = AtomicU64::new(0);
148
149fn cmos_read(reg: u8) -> u8 {
151 unsafe {
152 outb(CMOS_ADDR_PORT, reg | 0x80); inb(CMOS_DATA_PORT)
154 }
155}
156
157fn cmos_write(reg: u8, value: u8) {
159 unsafe {
160 outb(CMOS_ADDR_PORT, reg | 0x80); outb(CMOS_DATA_PORT, value);
162 }
163}
164
165fn bcd_to_binary(bcd: u8) -> u8 {
167 (bcd & 0x0F) + ((bcd / 16) * 10)
168}
169
170fn detect_century_register(use_binary: bool) -> u8 {
172 if let Some(fadt) = crate::acpi::get_fadt() {
173 let reg = fadt.century;
174 if reg != 0 {
175 let raw = cmos_read(reg);
176 let val = if use_binary { raw } else { bcd_to_binary(raw) };
177 if (19..=21).contains(&val) {
178 return reg;
179 }
180 }
181 }
182
183 for ® in CENTURY_REG_CANDIDATES {
184 let raw = cmos_read(reg);
185 let val = if use_binary { raw } else { bcd_to_binary(raw) };
186 if (19..=21).contains(&val) {
187 return reg;
188 }
189 }
190
191 0
192}
193
194fn is_update_in_progress() -> bool {
196 cmos_read(CMOS_REG_STATUS_A) & STATUS_A_UIP != 0
197}
198
199fn read_rtc_time() -> RtcDateTime {
201 #[derive(Clone, Copy, PartialEq, Eq)]
202 struct RawRtc {
203 second: u8,
204 minute: u8,
205 hour: u8,
206 weekday: u8,
207 day: u8,
208 month: u8,
209 year: u8,
210 century: u8,
211 status_b: u8,
212 }
213
214 fn read_raw_once(century_reg: u8) -> RawRtc {
216 RawRtc {
217 second: cmos_read(CMOS_REG_SECOND),
218 minute: cmos_read(CMOS_REG_MINUTE),
219 hour: cmos_read(CMOS_REG_HOUR),
220 weekday: cmos_read(CMOS_REG_WEEKDAY),
221 day: cmos_read(CMOS_REG_DAY),
222 month: cmos_read(CMOS_REG_MONTH),
223 year: cmos_read(CMOS_REG_YEAR),
224 century: if century_reg != 0 {
225 cmos_read(century_reg)
226 } else {
227 0
228 },
229 status_b: cmos_read(CMOS_REG_STATUS_B),
230 }
231 }
232
233 let (century_reg, default_binary) = {
234 let driver = RTC_DRIVER.lock();
235 if let Some(ref drv) = *driver {
236 (drv.cmos_century_reg, drv.use_binary)
237 } else {
238 (0, false)
239 }
240 };
241
242 let mut raw = loop {
243 while is_update_in_progress() {
244 core::hint::spin_loop();
245 }
246 let a = read_raw_once(century_reg);
247 while is_update_in_progress() {
248 core::hint::spin_loop();
249 }
250 let b = read_raw_once(century_reg);
251 if a == b {
252 break b;
253 }
254 };
255
256 let use_binary = if raw.status_b == 0 {
257 default_binary
258 } else {
259 (raw.status_b & STATUS_B_DM) != 0
260 };
261 let pm_bit = raw.hour & 0x80;
262 if !use_binary {
263 raw.second = bcd_to_binary(raw.second);
264 raw.minute = bcd_to_binary(raw.minute);
265 raw.hour = bcd_to_binary(raw.hour & 0x7F);
266 raw.weekday = bcd_to_binary(raw.weekday);
267 raw.day = bcd_to_binary(raw.day);
268 raw.month = bcd_to_binary(raw.month);
269 raw.year = bcd_to_binary(raw.year);
270 if raw.century != 0 {
271 raw.century = bcd_to_binary(raw.century);
272 }
273 }
274
275 let is_24h = (raw.status_b & STATUS_B_24H) != 0;
276 if !is_24h {
277 let pm = pm_bit != 0;
278 raw.hour &= 0x7F;
279 if pm {
280 if raw.hour != 12 {
281 raw.hour = raw.hour.saturating_add(12);
282 }
283 } else if raw.hour == 12 {
284 raw.hour = 0;
285 }
286 }
287
288 let full_year = if raw.century != 0 {
289 (raw.century as u16) * 100 + raw.year as u16
290 } else if raw.year >= 70 {
291 1900 + raw.year as u16
292 } else {
293 2000 + raw.year as u16
294 };
295
296 RtcDateTime {
297 second: raw.second,
298 minute: raw.minute,
299 hour: raw.hour,
300 weekday: raw.weekday.saturating_sub(1),
301 day: raw.day,
302 month: raw.month,
303 year: full_year,
304 century: (full_year / 100) as u16,
305 }
306}
307
308pub fn init() -> Result<(), &'static str> {
310 log::info!("[RTC] Initializing RTC...");
311
312 let status_b = cmos_read(CMOS_REG_STATUS_B);
314 let use_binary = (status_b & STATUS_B_DM) != 0;
315
316 let cmos_century_reg = detect_century_register(use_binary);
317
318 let driver = RtcDriver {
319 cmos_century_reg,
320 use_binary,
321 last_update_tick: AtomicU64::new(0),
322 };
323
324 *RTC_DRIVER.lock() = Some(driver);
325
326 let mut status_b = cmos_read(CMOS_REG_STATUS_B);
330 status_b |= STATUS_B_UIE;
331 cmos_write(CMOS_REG_STATUS_B, status_b);
332
333 let _ = cmos_read(CMOS_REG_STATUS_C);
335
336 RTC_INITIALIZED.store(true, Ordering::SeqCst);
337
338 let time = RtcDriver::get_time();
339 log::info!(
340 "[RTC] Initialized: {:04}-{:02}-{:02} {:02}:{:02}:{:02}",
341 time.year,
342 time.month,
343 time.day,
344 time.hour,
345 time.minute,
346 time.second
347 );
348
349 Ok(())
350}
351
352impl RtcDriver {
353 pub fn get_time() -> RtcDateTime {
355 read_rtc_time()
356 }
357
358 pub fn get_timestamp() -> u64 {
360 Self::get_time().to_timestamp()
361 }
362
363 pub fn last_update_tick() -> u64 {
365 RTC_LAST_TICK.load(Ordering::Relaxed)
366 }
367}
368
369pub fn rtc_interrupt_handler() {
371 let _status_c = cmos_read(CMOS_REG_STATUS_C);
373
374 RTC_LAST_TICK.fetch_add(1, Ordering::Relaxed);
376
377 let driver = RTC_DRIVER.lock();
379 if let Some(ref drv) = *driver {
380 drv.last_update_tick.fetch_add(1, Ordering::Relaxed);
381 }
382}
383
384pub fn is_available() -> bool {
386 RTC_INITIALIZED.load(Ordering::Relaxed)
387}
388
389pub fn get_datetime() -> RtcDateTime {
391 RtcDriver::get_time()
392}
393
394pub fn get_timestamp() -> u64 {
396 RtcDriver::get_timestamp()
397}
398
399pub fn set_periodic_frequency(freq_hz: u16) {
401 let rate = match freq_hz {
404 1024 => RTC_FREQ_1024,
405 256 => RTC_FREQ_256,
406 64 => RTC_FREQ_64,
407 16 => RTC_FREQ_16,
408 4 => RTC_FREQ_4,
409 1 => RTC_FREQ_1,
410 _ => {
411 log::warn!(
412 "[RTC] Unsupported periodic frequency {} Hz, falling back to 1 Hz",
413 freq_hz
414 );
415 RTC_FREQ_1
416 }
417 };
418
419 let mut status_a = cmos_read(CMOS_REG_STATUS_A);
420 status_a = (status_a & 0xF0) | (rate & 0x0F);
421 cmos_write(CMOS_REG_STATUS_A, status_a);
422}
423
424pub fn set_periodic_interrupt(enable: bool) {
426 let mut status_b = cmos_read(CMOS_REG_STATUS_B);
427 if enable {
428 status_b |= STATUS_B_PIE;
429 } else {
430 status_b &= !STATUS_B_PIE;
431 }
432 cmos_write(CMOS_REG_STATUS_B, status_b);
433}
434
435pub fn uptime_secs() -> u64 {
437 RTC_LAST_TICK.load(Ordering::Relaxed)
438}