RTC-4513 real-time clock chip, used by Tengai Makyou Zero
Bit 7 indicates status, 0=busy and 1=ready; hardcode to 1
10pub const STATUS_BYTE: u8 = 0x80;
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] 13enum HourType { 14 #[default] 15 Am, 16 Pm, 17} 18 19impl HourType { 20 #[must_use] 21 const fn toggle(self) -> Self { 22 match self { 23 Self::Am => Self::Pm, 24 Self::Pm => Self::Am, 25 } 26 } 27 28 const fn from_bit(bit: bool) -> Self { 29 if bit { Self::Pm } else { Self::Am } 30 } 31 32 fn to_bit(self) -> bool { 33 self == Self::Pm 34 } 35} 36 37impl Display for HourType { 38 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 39 match self { 40 Self::Am => write!(f, "AM"), 41 Self::Pm => write!(f, "PM"), 42 } 43 } 44} 45 46#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] 47enum ClockHours { 48 #[default] 49 Twelve, 50 TwentyFour, 51} 52 53impl ClockHours { 54 fn from_bit(bit: bool) -> Self { 55 if bit { Self::TwentyFour } else { Self::Twelve } 56 } 57 58 fn to_bit(self) -> bool { 59 self == Self::TwentyFour 60 } 61} 62 63impl Display for ClockHours { 64 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 65 match self { 66 Self::Twelve => write!(f, "12-hour"), 67 Self::TwentyFour => write!(f, "24-hour"), 68 } 69 } 70} 71 72#[derive(Debug, Clone, Encode, Decode)] 73struct RtcTime { 74 last_update_nanos: u128, 75 nanos: u32, 76 seconds: u8, 77 minutes: u8, 78 hours: u8, 79 hour_type: HourType, 80 day: u8, 81 month: u8, 82 year: u8, 83 day_of_week: u8, 84 clock_hours: ClockHours, 85 calendar_enabled: bool, 86} 87 88impl RtcTime { 89 fn new(now_nanos: u128) -> Self { 90 Self { 91 last_update_nanos: now_nanos, 92 nanos: 0, 93 seconds: 0, 94 minutes: 0, 95 hours: 0, 96 hour_type: HourType::default(), 97 day: 0, 98 month: 0, 99 year: 0, 100 day_of_week: 0, 101 clock_hours: ClockHours::default(), 102 calendar_enabled: false, 103 } 104 } 105 106 fn increment_seconds(&mut self) { 107 self.seconds += 1; 108 if self.seconds >= 60 { 109 self.seconds = 0; 110 self.increment_minutes(); 111 } 112 113 log::trace!("Incremented seconds, new time: {self:#?}"); 114 } 115 116 fn increment_minutes(&mut self) { 117 self.minutes += 1; 118 if self.minutes >= 60 { 119 self.minutes = 0; 120 self.increment_hours(); 121 } 122 } 123 124 fn increment_hours(&mut self) { 125 self.hours += 1; 126 127 match self.clock_hours { 128 ClockHours::TwentyFour => { 129 if self.hours >= 24 { 130 self.hours = 0; 131 self.increment_day(); 132 } 133 } 134 ClockHours::Twelve => match self.hours.cmp(&12) { 135 Ordering::Equal => { 136 self.hour_type = self.hour_type.toggle(); 137 if self.hour_type == HourType::Am { 138 self.increment_day(); 139 } 140 } 141 Ordering::Greater => { 142 self.hours = 1; 143 } 144 Ordering::Less => {} 145 }, 146 } 147 } 148 149 fn increment_day(&mut self) { 150 if !self.calendar_enabled { 151 self.day_of_week = (self.day_of_week + 1) % 7; 152 return; 153 } 154 155 self.day += 1; 156 self.day_of_week = (self.day_of_week + 1) % 7; 157 158 if self.day > timeutils::days_in_month(self.month, self.year) { 159 self.day = 1; 160 self.increment_month(); 161 } 162 } 163 164 fn increment_month(&mut self) { 165 self.month += 1; 166 if self.month > 12 { 167 self.month = 1; 168 self.increment_year(); 169 } 170 } 171 172 fn increment_year(&mut self) { 173 // This chip only stores years in the range 0-99 174 self.year = (self.year + 1) % 100; 175 } 176} 177 178#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] 179enum Command { 180 #[default] 181 None, 182 Read, 183 Write, 184} 185 186#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] 187enum InterruptDuty { 188 #[default] 189 FixedTime, 190 UntilAck, 191} 192 193impl InterruptDuty { 194 fn from_bit(bit: bool) -> Self { 195 if bit { Self::UntilAck } else { Self::FixedTime } 196 } 197 198 fn to_bit(self) -> bool { 199 self == Self::UntilAck 200 } 201} 202 203#[derive(Debug, Clone, Encode, Decode)] 204pub struct Rtc4513 { 205 command: Command, 206 register: Option<u8>, 207 time: RtcTime, 208 selected: bool, 209 wrapped: bool, 210 paused: bool, 211 pending_seconds_increment: bool, 212 stopped: bool, 213 reset: bool, 214 time_lost: bool, 215 irq: bool, 216 irq_enabled: bool, 217 irq_rate_bits: u8, 218 irq_rate_nanos: u128, 219 last_irq_nanos: u128, 220 irq_duty: InterruptDuty, 221 last_day_write_high: u8, 222 last_month_write_high: u8, 223} 224 225impl Rtc4513 { 226 pub fn new() -> Self { 227 let now_nanos = timeutils::current_time_nanos(); 228 Self { 229 command: Command::default(), 230 register: None, 231 time: RtcTime::new(now_nanos), 232 selected: false, 233 wrapped: false, 234 paused: false, 235 pending_seconds_increment: false, 236 stopped: false, 237 reset: false, 238 time_lost: true, 239 irq: false, 240 irq_enabled: true, 241 irq_rate_bits: 0, 242 irq_rate_nanos: irq_rate_nanos_from_bits(0), 243 last_irq_nanos: now_nanos, 244 irq_duty: InterruptDuty::default(), 245 last_day_write_high: 0, 246 last_month_write_high: 0, 247 } 248 } 249 250 pub fn read_chip_select(&self) -> u8 { 251 log::trace!("Chip select read"); 252 self.selected.into() 253 } 254 255 pub fn write_chip_select(&mut self, value: u8) { 256 log::trace!("Chip select write {value:02X}"); 257 258 let prev_selected = self.selected; 259 self.selected = value.bit(0); 260 261 if prev_selected && !self.selected { 262 self.wrapped = false; 263 self.reset = false; 264 self.command = Command::None; 265 self.register = None; 266 } 267 } 268 269 pub fn read_data_port(&mut self) -> u8 { 270 if !self.selected || self.command != Command::Read { 271 return 0; 272 } 273 274 self.update_time(); 275 276 let Some(register) = self.register else { return 0 }; 277 278 let byte = self.read_register(register); 279 log::trace!( 280 "RTC data port read, current register is {:X?}, returning {byte:X}", 281 self.register 282 ); 283 284 self.register = Some((register + 1) & 0x0F); 285 byte 286 } 287 288 pub fn write_data_port(&mut self, value: u8) { 289 if !self.selected { 290 return; 291 } 292 293 self.update_time(); 294 295 log::trace!( 296 "RTC data port write {value:X}, current register is {:X?} and command is {:?}", 297 self.register, 298 self.command 299 ); 300 301 match (self.command, self.register) { 302 (Command::None, _) => { 303 // First write since select: set command ($03 = write, $0C = read) 304 self.command = match value { 305 0x03 => Command::Write, 306 0x0C => Command::Read, 307 _ => Command::None, 308 }; 309 } 310 (_, None) => { 311 // Second write since select: set register index 312 self.register = Some(value & 0x0F); 313 } 314 (Command::Write, Some(register)) => { 315 // Write to selected register and increment register index 316 self.write_register(register, value); 317 self.register = Some((register + 1) & 0x0F); 318 } 319 (Command::Read, Some(_)) => {} 320 } 321 } 322 323 fn read_register(&mut self, register: u8) -> u8 { 324 match register { 325 0x0 => self.read_seconds_low(), 326 0x1 => self.read_seconds_high(), 327 0x2 => self.read_minutes_low(), 328 0x3 => self.read_minutes_high(), 329 0x4 => self.read_hours_low(), 330 0x5 => self.read_hours_high(), 331 0x6 => self.read_day_low(), 332 0x7 => self.read_day_high(), 333 0x8 => self.read_month_low(), 334 0x9 => self.read_month_high(), 335 0xA => self.read_year_low(), 336 0xB => self.read_year_high(), 337 0xC => self.read_day_of_week(), 338 0xD => self.read_control_1(), 339 0xE => self.read_control_2(), 340 0xF => self.read_control_3(), 341 _ => panic!("Invalid RTC-4513 register: {register:02X}"), 342 } 343 } 344 345 fn write_register(&mut self, register: u8, value: u8) { 346 match register { 347 0x0 => self.write_seconds_low(value), 348 0x1 => self.write_seconds_high(value), 349 0x2 => self.write_minutes_low(value), 350 0x3 => self.write_minutes_high(value), 351 0x4 => self.write_hours_low(value), 352 0x5 => self.write_hours_high(value), 353 0x6 => self.write_day_low(value), 354 0x7 => self.write_day_high(value), 355 0x8 => self.write_month_low(value), 356 0x9 => self.write_month_high(value), 357 0xA => self.write_year_low(value), 358 0xB => self.write_year_high(value), 359 0xC => self.write_day_of_week(value), 360 0xD => self.write_control_1(value), 361 0xE => self.write_control_2(value), 362 0xF => self.write_control_3(value), 363 _ => panic!("Invalid RTC-4513 register: {register:02X}"), 364 } 365 } 366 367 fn read_seconds_low(&self) -> u8 { 368 self.time.seconds % 10 369 } 370 371 fn read_seconds_high(&self) -> u8 { 372 let lost = u8::from(self.time_lost) << 3; 373 lost | (self.time.seconds / 10) 374 } 375 376 fn write_seconds_low(&mut self, value: u8) { 377 self.time.seconds = self.time.seconds / 10 * 10 + (value & 0x0F); 378 log::trace!(" seconds={}", self.time.seconds); 379 } 380 381 fn write_seconds_high(&mut self, value: u8) { 382 self.time.seconds = 10 * (value & 0x07) + (self.time.seconds % 10); 383 self.time_lost = value.bit(3); 384 log::trace!(" seconds={}, time_lost={}", self.time.seconds, self.time_lost); 385 } 386 387 fn read_minutes_low(&self) -> u8 { 388 self.time.minutes % 10 389 } 390 391 fn read_minutes_high(&self) -> u8 { 392 let wrapped = u8::from(self.wrapped) << 3; 393 wrapped | (self.time.minutes / 10) 394 } 395 396 fn write_minutes_low(&mut self, value: u8) { 397 self.time.minutes = self.time.minutes / 10 * 10 + (value & 0x0F); 398 log::trace!(" minutes={}", self.time.minutes); 399 } 400 401 fn write_minutes_high(&mut self, value: u8) { 402 self.time.minutes = 10 * (value & 0x07) + (self.time.minutes % 10); 403 log::trace!(" minutes={}", self.time.minutes); 404 } 405 406 fn read_hours_low(&self) -> u8 { 407 self.time.hours % 10 408 } 409 410 fn read_hours_high(&self) -> u8 { 411 let wrapped = u8::from(self.wrapped) << 3; 412 let am_pm_bit = u8::from(self.time.hour_type.to_bit()) << 2; 413 wrapped | am_pm_bit | (self.time.hours / 10) 414 } 415 416 fn write_hours_low(&mut self, value: u8) { 417 self.time.hours = self.time.hours / 10 * 10 + (value & 0x0F); 418 log::trace!(" hours={}", self.time.hours); 419 } 420 421 fn write_hours_high(&mut self, value: u8) { 422 let mask = match self.time.clock_hours { 423 ClockHours::TwentyFour => 0x03, 424 ClockHours::Twelve => 0x01, 425 }; 426 self.time.hours = 10 * (value & mask) + (self.time.hours % 10); 427 428 if self.time.clock_hours == ClockHours::Twelve { 429 self.time.hour_type = HourType::from_bit(value.bit(2)); 430 } 431 432 log::trace!(" hours={}, hour_type={}", self.time.hours, self.time.hour_type); 433 } 434 435 fn read_day_low(&self) -> u8 { 436 self.time.day % 10 437 } 438 439 fn read_day_high(&self) -> u8 { 440 let wrapped = u8::from(self.wrapped) << 3; 441 let ram_bit = self.last_day_write_high & 0x04; 442 wrapped | ram_bit | (self.time.day / 10) 443 } 444 445 fn write_day_low(&mut self, value: u8) { 446 self.time.day = self.time.day / 10 * 10 + (value & 0x0F); 447 log::trace!(" day={}", self.time.day); 448 } 449 450 fn write_day_high(&mut self, value: u8) { 451 self.time.day = 10 * (value & 0x03) + (self.time.day % 10); 452 self.last_day_write_high = value & 0x0F; 453 log::trace!(" day={}", self.time.day); 454 } 455 456 fn read_month_low(&self) -> u8 { 457 self.time.month % 10 458 } 459 460 fn read_month_high(&self) -> u8 { 461 let wrapped = u8::from(self.wrapped) << 3; 462 let ram_bits = self.last_month_write_high & 0x06; 463 wrapped | ram_bits | (self.time.month / 10) 464 } 465 466 fn write_month_low(&mut self, value: u8) { 467 self.time.month = self.time.month / 10 * 10 + (value & 0x0F); 468 log::trace!(" month={}", self.time.month); 469 } 470 471 fn write_month_high(&mut self, value: u8) { 472 self.time.month = 10 * (value & 0x01) + (self.time.month % 10); 473 self.last_month_write_high = value & 0x0F; 474 log::trace!(" month={}", self.time.month); 475 } 476 477 fn read_year_low(&self) -> u8 { 478 self.time.year % 10 479 } 480 481 fn read_year_high(&self) -> u8 { 482 self.time.year / 10 483 } 484 485 fn write_year_low(&mut self, value: u8) { 486 self.time.year = self.time.year / 10 * 10 + (value & 0x0F); 487 log::trace!(" year={}", self.time.year); 488 } 489 490 fn write_year_high(&mut self, value: u8) { 491 self.time.year = 10 * (value & 0x0F) + (self.time.year % 10); 492 log::trace!(" year={}", self.time.year); 493 } 494 495 fn read_day_of_week(&self) -> u8 { 496 let wrapped = u8::from(self.wrapped) << 3; 497 wrapped | self.time.day_of_week 498 } 499 500 fn write_day_of_week(&mut self, value: u8) { 501 self.time.day_of_week = value & 0x07; 502 log::trace!(" day_of_week={}", self.time.day_of_week); 503 } 504 505 fn read_control_1(&mut self) -> u8 { 506 let irq = self.irq; 507 self.irq = false; 508 509 u8::from(self.paused) 510 | (u8::from(self.time.calendar_enabled) << 1) 511 | (u8::from(irq && self.irq_enabled) << 2) 512 } 513 514 fn write_control_1(&mut self, value: u8) { 515 // HOLD: Pause clock while set 516 self.paused = value.bit(0); 517 518 // CAL/HW: Calendar enabled (day/month/year) 519 self.time.calendar_enabled = value.bit(1); 520 521 // 30ADJ: Clear seconds, and increment minutes if seconds was >= 30 522 if value.bit(3) { 523 let prev_seconds = self.time.seconds; 524 self.time.seconds = 0; 525 526 if prev_seconds >= 30 { 527 self.time.increment_minutes(); 528 } 529 } 530 531 if !self.paused && self.pending_seconds_increment { 532 self.pending_seconds_increment = false; 533 self.time.increment_seconds(); 534 } 535 536 log::trace!( 537 " HOLD={}, CAL/HW={}, 30ADJ={}", 538 self.paused, 539 self.time.calendar_enabled, 540 value.bit(3) 541 ); 542 } 543 544 fn read_control_2(&self) -> u8 { 545 u8::from(!self.irq_enabled) 546 | (u8::from(self.irq_duty.to_bit()) << 1) 547 | (self.irq_rate_bits << 2) 548 } 549 550 fn write_control_2(&mut self, value: u8) { 551 // MASK: Mask interrupts 552 self.irq_enabled = !value.bit(0); 553 554 // DUTY: Interrupt duty 555 self.irq_duty = InterruptDuty::from_bit(value.bit(1)); 556 557 // RATE: Interrupt rate 558 self.irq_rate_bits = (value >> 2) & 0x03; 559 self.irq_rate_nanos = irq_rate_nanos_from_bits(self.irq_rate_bits); 560 561 log::trace!( 562 " MASK={}, DUTY={:?}, RATE={}", 563 value.bit(0), 564 self.irq_duty, 565 (value >> 2) & 0x03 566 ); 567 } 568 569 fn read_control_3(&self) -> u8 { 570 u8::from(self.reset) 571 | (u8::from(self.stopped) << 1) 572 | (u8::from(self.time.clock_hours.to_bit()) << 2) 573 } 574 575 fn write_control_3(&mut self, value: u8) { 576 // RESET: Clear seconds and stop clock (automatically cleared on chip deselect) 577 self.reset = value.bit(0); 578 if self.reset { 579 self.time.seconds = 0; 580 self.pending_seconds_increment = false; 581 } 582 583 // STOP: Stop clock 584 self.stopped = value.bit(1); 585 586 // 24/12: Select 12-hour clock vs. 24-hour clock 587 self.time.clock_hours = ClockHours::from_bit(value.bit(2)); 588 if self.time.clock_hours == ClockHours::TwentyFour { 589 self.time.hour_type = HourType::from_bit(false); 590 } 591 592 log::trace!( 593 " RESET={}, STOP={}, 24/12={}", 594 self.reset, 595 self.stopped, 596 self.time.clock_hours 597 ); 598 } 599 600 fn update_time(&mut self) { 601 if self.stopped || self.reset { 602 self.time.last_update_nanos = timeutils::current_time_nanos(); 603 return; 604 } 605 606 let now_nanos = timeutils::current_time_nanos(); 607 let elapsed = now_nanos.saturating_sub(self.time.last_update_nanos); 608 let new_time_nanos = u128::from(self.time.nanos) + elapsed; 609 self.time.nanos = (new_time_nanos % 1_000_000_000) as u32; 610 self.time.last_update_nanos = now_nanos; 611 612 for _ in 0..new_time_nanos / 1_000_000_000 { 613 if self.paused { 614 self.pending_seconds_increment = true; 615 break; 616 } 617 618 self.time.increment_seconds(); 619 if self.command != Command::None { 620 self.wrapped = true; 621 } 622 } 623 624 if now_nanos - self.last_irq_nanos >= self.irq_rate_nanos { 625 if self.irq_enabled { 626 log::trace!("Flagging IRQ; now = {now_nanos}, last_irq = {}", self.last_irq_nanos); 627 self.irq = true; 628 } 629 630 while self.last_irq_nanos + self.irq_rate_nanos <= now_nanos { 631 self.last_irq_nanos += self.irq_rate_nanos; 632 } 633 } 634 635 // Interrupts with fixed duty expire after 7.8ms 636 if self.irq_duty == InterruptDuty::FixedTime && now_nanos - self.last_irq_nanos >= 7_800_000 637 { 638 self.irq = false; 639 } 640 } 641} 642 643impl Default for Rtc4513 { 644 fn default() -> Self { 645 Self::new() 646 } 647} 648 649fn irq_rate_nanos_from_bits(rate: u8) -> u128 { 650 match rate { 651 // 64 times per second 652 0 => 1_000_000_000 / 64, 653 // Once per second 654 1 => 1_000_000_000, 655 // Once per minute 656 2 => 60 * 1_000_000_000, 657 // Once per hour 658 3 => 60 * 60 * 1_000_000_000, 659 _ => panic!("Invalid RTC-4513 interrupt rate: {rate}"), 660 } 661}