1 /** @file 2 Simple PC RTC 3 4 Copyright (c) 2007, Intel Corporation. All rights reserved.<BR> 5 Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR> 6 Copyright (c) 2014, ARM Ltd. All rights reserved. 7 8 This program and the accompanying materials 9 are licensed and made available under the terms and conditions of the BSD License 10 which accompanies this distribution. The full text of the license may be found at 11 http://opensource.org/licenses/bsd-license.php 12 13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 14 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 15 16 17 **/ 18 19 20 21 typedef struct { 22 EFI_LOCK RtcLock; 23 UINT16 SavedTimeZone; 24 UINT8 Daylight; 25 } PC_RTC_GLOBALS; 26 27 #define PCAT_RTC_ADDRESS_REGISTER 0x70 28 #define PCAT_RTC_DATA_REGISTER 0x71 29 30 // 31 // Dallas DS12C887 Real Time Clock 32 // 33 #define RTC_ADDRESS_SECONDS 0 // R/W Range 0..59 34 #define RTC_ADDRESS_SECONDS_ALARM 1 // R/W Range 0..59 35 #define RTC_ADDRESS_MINUTES 2 // R/W Range 0..59 36 #define RTC_ADDRESS_MINUTES_ALARM 3 // R/W Range 0..59 37 #define RTC_ADDRESS_HOURS 4 // R/W Range 1..12 or 0..23 Bit 7 is AM/PM 38 #define RTC_ADDRESS_HOURS_ALARM 5 // R/W Range 1..12 or 0..23 Bit 7 is AM/PM 39 #define RTC_ADDRESS_DAY_OF_THE_WEEK 6 // R/W Range 1..7 40 #define RTC_ADDRESS_DAY_OF_THE_MONTH 7 // R/W Range 1..31 41 #define RTC_ADDRESS_MONTH 8 // R/W Range 1..12 42 #define RTC_ADDRESS_YEAR 9 // R/W Range 0..99 43 #define RTC_ADDRESS_REGISTER_A 10 // R/W[0..6] R0[7] 44 #define RTC_ADDRESS_REGISTER_B 11 // R/W 45 #define RTC_ADDRESS_REGISTER_C 12 // RO 46 #define RTC_ADDRESS_REGISTER_D 13 // RO 47 #define RTC_ADDRESS_CENTURY 50 // R/W Range 19..20 Bit 8 is R/W 48 // 49 // Date and time initial values. 50 // They are used if the RTC values are invalid during driver initialization 51 // 52 #define RTC_INIT_SECOND 0 53 #define RTC_INIT_MINUTE 0 54 #define RTC_INIT_HOUR 0 55 #define RTC_INIT_DAY 1 56 #define RTC_INIT_MONTH 1 57 #define RTC_INIT_YEAR 2001 58 59 // 60 // Register initial values 61 // 62 #define RTC_INIT_REGISTER_A 0x26 63 #define RTC_INIT_REGISTER_B 0x02 64 #define RTC_INIT_REGISTER_D 0x0 65 66 #pragma pack(1) 67 // 68 // Register A 69 // 70 typedef struct { 71 UINT8 RS : 4; // Rate Selection Bits 72 UINT8 DV : 3; // Divisor 73 UINT8 UIP : 1; // Update in progress 74 } RTC_REGISTER_A_BITS; 75 76 typedef union { 77 RTC_REGISTER_A_BITS Bits; 78 UINT8 Data; 79 } RTC_REGISTER_A; 80 81 // 82 // Register B 83 // 84 typedef struct { 85 UINT8 DSE : 1; // 0 - Daylight saving disabled 1 - Daylight savings enabled 86 UINT8 MIL : 1; // 0 - 12 hour mode 1 - 24 hour mode 87 UINT8 DM : 1; // 0 - BCD Format 1 - Binary Format 88 UINT8 SQWE : 1; // 0 - Disable SQWE output 1 - Enable SQWE output 89 UINT8 UIE : 1; // 0 - Update INT disabled 1 - Update INT enabled 90 UINT8 AIE : 1; // 0 - Alarm INT disabled 1 - Alarm INT Enabled 91 UINT8 PIE : 1; // 0 - Periodic INT disabled 1 - Periodic INT Enabled 92 UINT8 SET : 1; // 0 - Normal operation. 1 - Updates inhibited 93 } RTC_REGISTER_B_BITS; 94 95 typedef union { 96 RTC_REGISTER_B_BITS Bits; 97 UINT8 Data; 98 } RTC_REGISTER_B; 99 100 // 101 // Register C 102 // 103 typedef struct { 104 UINT8 Reserved : 4; // Read as zero. Can not be written. 105 UINT8 UF : 1; // Update End Interrupt Flag 106 UINT8 AF : 1; // Alarm Interrupt Flag 107 UINT8 PF : 1; // Periodic Interrupt Flag 108 UINT8 IRQF : 1; // Iterrupt Request Flag = PF & PIE | AF & AIE | UF & UIE 109 } RTC_REGISTER_C_BITS; 110 111 typedef union { 112 RTC_REGISTER_C_BITS Bits; 113 UINT8 Data; 114 } RTC_REGISTER_C; 115 116 // 117 // Register D 118 // 119 typedef struct { 120 UINT8 Reserved : 7; // Read as zero. Can not be written. 121 UINT8 VRT : 1; // Valid RAM and Time 122 } RTC_REGISTER_D_BITS; 123 124 typedef union { 125 RTC_REGISTER_D_BITS Bits; 126 UINT8 Data; 127 } RTC_REGISTER_D; 128 129 #pragma pack() 130 131 PC_RTC_GLOBALS mRtc; 132 133 BOOLEAN 134 IsLeapYear ( 135 IN EFI_TIME *Time 136 ) 137 { 138 if (Time->Year % 4 == 0) { 139 if (Time->Year % 100 == 0) { 140 if (Time->Year % 400 == 0) { 141 return TRUE; 142 } else { 143 return FALSE; 144 } 145 } else { 146 return TRUE; 147 } 148 } else { 149 return FALSE; 150 } 151 } 152 153 154 const INTN mDayOfMonth[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 155 156 BOOLEAN 157 DayValid ( 158 IN EFI_TIME *Time 159 ) 160 { 161 if (Time->Day < 1 || 162 Time->Day > mDayOfMonth[Time->Month - 1] || 163 (Time->Month == 2 && (!IsLeapYear (Time) && Time->Day > 28)) 164 ) { 165 return FALSE; 166 } 167 168 return TRUE; 169 } 170 171 172 UINT8 173 DecimaltoBcd ( 174 IN UINT8 DecValue 175 ) 176 { 177 UINTN High; 178 UINTN Low; 179 180 High = DecValue / 10; 181 Low = DecValue - (High * 10); 182 183 return (UINT8) (Low + (High << 4)); 184 } 185 186 UINT8 187 BcdToDecimal ( 188 IN UINT8 BcdValue 189 ) 190 { 191 UINTN High; 192 UINTN Low; 193 194 High = BcdValue >> 4; 195 Low = BcdValue - (High << 4); 196 197 return (UINT8) (Low + (High * 10)); 198 } 199 200 201 202 203 VOID 204 ConvertEfiTimeToRtcTime ( 205 IN EFI_TIME *Time, 206 IN RTC_REGISTER_B RegisterB, 207 IN UINT8 *Century 208 ) 209 { 210 BOOLEAN PM; 211 212 PM = TRUE; 213 // 214 // Adjust hour field if RTC in in 12 hour mode 215 // 216 if (RegisterB.Bits.MIL == 0) { 217 if (Time->Hour < 12) { 218 PM = FALSE; 219 } 220 221 if (Time->Hour >= 13) { 222 Time->Hour = (UINT8) (Time->Hour - 12); 223 } else if (Time->Hour == 0) { 224 Time->Hour = 12; 225 } 226 } 227 // 228 // Set the Time/Date/Daylight Savings values. 229 // 230 *Century = DecimaltoBcd ((UINT8) (Time->Year / 100)); 231 232 Time->Year = (UINT16) (Time->Year % 100); 233 234 if (RegisterB.Bits.DM == 0) { 235 Time->Year = DecimaltoBcd ((UINT8) Time->Year); 236 Time->Month = DecimaltoBcd (Time->Month); 237 Time->Day = DecimaltoBcd (Time->Day); 238 Time->Hour = DecimaltoBcd (Time->Hour); 239 Time->Minute = DecimaltoBcd (Time->Minute); 240 Time->Second = DecimaltoBcd (Time->Second); 241 } 242 // 243 // If we are in 12 hour mode and PM is set, then set bit 7 of the Hour field. 244 // 245 if (RegisterB.Bits.MIL == 0 && PM) { 246 Time->Hour = (UINT8) (Time->Hour | 0x80); 247 } 248 } 249 250 /** 251 Check the validity of all the fields of a data structure of type EFI_TIME 252 253 @param[in] Time Pointer to a data structure of type EFI_TIME that defines a date and time 254 255 @retval EFI_SUCCESS All date and time fields are valid 256 @retval EFI_INVALID_PARAMETER At least one date or time field is not valid 257 **/ 258 EFI_STATUS 259 RtcTimeFieldsValid ( 260 IN EFI_TIME *Time 261 ) 262 { 263 if ((Time->Year < 1998 ) || 264 (Time->Year > 2099 ) || 265 (Time->Month < 1 ) || 266 (Time->Month > 12 ) || 267 (!DayValid (Time)) || 268 (Time->Hour > 23 ) || 269 (Time->Minute > 59 ) || 270 (Time->Second > 59 ) || 271 (Time->Nanosecond > 999999999) || 272 ((Time->TimeZone != EFI_UNSPECIFIED_TIMEZONE) && 273 ((Time->TimeZone < -1440) || 274 (Time->TimeZone > 1440 ) ) ) || 275 (Time->Daylight & (~(EFI_TIME_ADJUST_DAYLIGHT | 276 EFI_TIME_IN_DAYLIGHT ))) 277 ) { 278 return EFI_INVALID_PARAMETER; 279 } 280 281 return EFI_SUCCESS; 282 } 283 284 UINT8 285 RtcRead ( 286 IN UINT8 Address 287 ) 288 { 289 IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, (UINT8) (Address | (UINT8) (IoRead8 (PCAT_RTC_ADDRESS_REGISTER) & 0x80))); 290 return IoRead8 (PCAT_RTC_DATA_REGISTER); 291 } 292 293 VOID 294 RtcWrite ( 295 IN UINT8 Address, 296 IN UINT8 Data 297 ) 298 { 299 IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, (UINT8) (Address | (UINT8) (IoRead8 (PCAT_RTC_ADDRESS_REGISTER) & 0x80))); 300 IoWrite8 (PCAT_RTC_DATA_REGISTER, Data); 301 } 302 303 304 EFI_STATUS 305 RtcTestCenturyRegister ( 306 VOID 307 ) 308 { 309 UINT8 Century; 310 UINT8 Temp; 311 312 Century = RtcRead (RTC_ADDRESS_CENTURY); 313 // 314 // RtcWrite (RTC_ADDRESS_CENTURY, 0x00); 315 // 316 Temp = (UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f); 317 RtcWrite (RTC_ADDRESS_CENTURY, Century); 318 if (Temp == 0x19 || Temp == 0x20) { 319 return EFI_SUCCESS; 320 } 321 322 return EFI_DEVICE_ERROR; 323 } 324 325 VOID 326 ConvertRtcTimeToEfiTime ( 327 IN EFI_TIME *Time, 328 IN RTC_REGISTER_B RegisterB 329 ) 330 { 331 BOOLEAN PM; 332 333 if ((Time->Hour) & 0x80) { 334 PM = TRUE; 335 } else { 336 PM = FALSE; 337 } 338 339 Time->Hour = (UINT8) (Time->Hour & 0x7f); 340 341 if (RegisterB.Bits.DM == 0) { 342 Time->Year = BcdToDecimal ((UINT8) Time->Year); 343 Time->Month = BcdToDecimal (Time->Month); 344 Time->Day = BcdToDecimal (Time->Day); 345 Time->Hour = BcdToDecimal (Time->Hour); 346 Time->Minute = BcdToDecimal (Time->Minute); 347 Time->Second = BcdToDecimal (Time->Second); 348 } 349 // 350 // If time is in 12 hour format, convert it to 24 hour format 351 // 352 if (RegisterB.Bits.MIL == 0) { 353 if (PM && Time->Hour < 12) { 354 Time->Hour = (UINT8) (Time->Hour + 12); 355 } 356 357 if (!PM && Time->Hour == 12) { 358 Time->Hour = 0; 359 } 360 } 361 362 Time->Nanosecond = 0; 363 Time->TimeZone = EFI_UNSPECIFIED_TIMEZONE; 364 Time->Daylight = 0; 365 } 366 367 EFI_STATUS 368 RtcWaitToUpdate ( 369 UINTN Timeout 370 ) 371 { 372 RTC_REGISTER_A RegisterA; 373 RTC_REGISTER_D RegisterD; 374 375 // 376 // See if the RTC is functioning correctly 377 // 378 RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D); 379 380 if (RegisterD.Bits.VRT == 0) { 381 return EFI_DEVICE_ERROR; 382 } 383 // 384 // Wait for up to 0.1 seconds for the RTC to be ready. 385 // 386 Timeout = (Timeout / 10) + 1; 387 RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A); 388 while (RegisterA.Bits.UIP == 1 && Timeout > 0) { 389 MicroSecondDelay (10); 390 RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A); 391 Timeout--; 392 } 393 394 RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D); 395 if (Timeout == 0 || RegisterD.Bits.VRT == 0) { 396 return EFI_DEVICE_ERROR; 397 } 398 399 return EFI_SUCCESS; 400 } 401 402 EFI_STATUS 403 LibGetTime ( 404 OUT EFI_TIME *Time, 405 OUT EFI_TIME_CAPABILITIES *Capabilities 406 ) 407 { 408 EFI_STATUS Status; 409 RTC_REGISTER_B RegisterB; 410 UINT8 Century; 411 UINTN BufferSize; 412 413 // 414 // Check parameters for null pointer 415 // 416 if (Time == NULL) { 417 return EFI_INVALID_PARAMETER; 418 419 } 420 // 421 // Acquire RTC Lock to make access to RTC atomic 422 // 423 EfiAcquireLock (&mRtc.RtcLock); 424 425 // 426 // Wait for up to 0.1 seconds for the RTC to be updated 427 // 428 Status = RtcWaitToUpdate (100000); 429 if (EFI_ERROR (Status)) { 430 EfiReleaseLock (&mRtc.RtcLock); 431 return Status; 432 } 433 // 434 // Read Register B 435 // 436 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B); 437 438 // 439 // Get the Time/Date/Daylight Savings values. 440 // 441 Time->Second = RtcRead (RTC_ADDRESS_SECONDS); 442 Time->Minute = RtcRead (RTC_ADDRESS_MINUTES); 443 Time->Hour = RtcRead (RTC_ADDRESS_HOURS); 444 Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH); 445 Time->Month = RtcRead (RTC_ADDRESS_MONTH); 446 Time->Year = RtcRead (RTC_ADDRESS_YEAR); 447 448 ConvertRtcTimeToEfiTime (Time, RegisterB); 449 450 if (RtcTestCenturyRegister () == EFI_SUCCESS) { 451 Century = BcdToDecimal ((UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f)); 452 } else { 453 Century = BcdToDecimal (RtcRead (RTC_ADDRESS_CENTURY)); 454 } 455 456 Time->Year = (UINT16) (Century * 100 + Time->Year); 457 458 // 459 // Release RTC Lock. 460 // 461 EfiReleaseLock (&mRtc.RtcLock); 462 463 // 464 // Get the variable that containts the TimeZone and Daylight fields 465 // 466 Time->TimeZone = mRtc.SavedTimeZone; 467 Time->Daylight = mRtc.Daylight; 468 469 BufferSize = sizeof (INT16) + sizeof (UINT8); 470 471 // 472 // Make sure all field values are in correct range 473 // 474 Status = RtcTimeFieldsValid (Time); 475 if (EFI_ERROR (Status)) { 476 return EFI_DEVICE_ERROR; 477 } 478 // 479 // Fill in Capabilities if it was passed in 480 // 481 if (Capabilities) { 482 Capabilities->Resolution = 1; 483 // 484 // 1 hertz 485 // 486 Capabilities->Accuracy = 50000000; 487 // 488 // 50 ppm 489 // 490 Capabilities->SetsToZero = FALSE; 491 } 492 493 return EFI_SUCCESS; 494 } 495 496 497 498 EFI_STATUS 499 LibSetTime ( 500 IN EFI_TIME *Time 501 ) 502 { 503 EFI_STATUS Status; 504 EFI_TIME RtcTime; 505 RTC_REGISTER_B RegisterB; 506 UINT8 Century; 507 508 if (Time == NULL) { 509 return EFI_INVALID_PARAMETER; 510 } 511 // 512 // Make sure that the time fields are valid 513 // 514 Status = RtcTimeFieldsValid (Time); 515 if (EFI_ERROR (Status)) { 516 return Status; 517 } 518 519 CopyMem (&RtcTime, Time, sizeof (EFI_TIME)); 520 521 // 522 // Acquire RTC Lock to make access to RTC atomic 523 // 524 EfiAcquireLock (&mRtc.RtcLock); 525 526 // 527 // Wait for up to 0.1 seconds for the RTC to be updated 528 // 529 Status = RtcWaitToUpdate (100000); 530 if (EFI_ERROR (Status)) { 531 EfiReleaseLock (&mRtc.RtcLock); 532 return Status; 533 } 534 // 535 // Read Register B, and inhibit updates of the RTC 536 // 537 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B); 538 RegisterB.Bits.SET = 1; 539 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data); 540 541 ConvertEfiTimeToRtcTime (&RtcTime, RegisterB, &Century); 542 543 RtcWrite (RTC_ADDRESS_SECONDS, RtcTime.Second); 544 RtcWrite (RTC_ADDRESS_MINUTES, RtcTime.Minute); 545 RtcWrite (RTC_ADDRESS_HOURS, RtcTime.Hour); 546 RtcWrite (RTC_ADDRESS_DAY_OF_THE_MONTH, RtcTime.Day); 547 RtcWrite (RTC_ADDRESS_MONTH, RtcTime.Month); 548 RtcWrite (RTC_ADDRESS_YEAR, (UINT8) RtcTime.Year); 549 if (RtcTestCenturyRegister () == EFI_SUCCESS) { 550 Century = (UINT8) ((Century & 0x7f) | (RtcRead (RTC_ADDRESS_CENTURY) & 0x80)); 551 } 552 553 RtcWrite (RTC_ADDRESS_CENTURY, Century); 554 555 // 556 // Allow updates of the RTC registers 557 // 558 RegisterB.Bits.SET = 0; 559 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data); 560 561 // 562 // Release RTC Lock. 563 // 564 EfiReleaseLock (&mRtc.RtcLock); 565 566 // 567 // Set the variable that containts the TimeZone and Daylight fields 568 // 569 mRtc.SavedTimeZone = Time->TimeZone; 570 mRtc.Daylight = Time->Daylight; 571 return Status; 572 } 573 574 EFI_STATUS 575 libGetWakeupTime ( 576 OUT BOOLEAN *Enabled, 577 OUT BOOLEAN *Pending, 578 OUT EFI_TIME *Time 579 ) 580 { 581 EFI_STATUS Status; 582 RTC_REGISTER_B RegisterB; 583 RTC_REGISTER_C RegisterC; 584 UINT8 Century; 585 586 // 587 // Check paramters for null pointers 588 // 589 if ((Enabled == NULL) || (Pending == NULL) || (Time == NULL)) { 590 return EFI_INVALID_PARAMETER; 591 592 } 593 // 594 // Acquire RTC Lock to make access to RTC atomic 595 // 596 EfiAcquireLock (&mRtc.RtcLock); 597 598 // 599 // Wait for up to 0.1 seconds for the RTC to be updated 600 // 601 Status = RtcWaitToUpdate (100000); 602 if (EFI_ERROR (Status)) { 603 EfiReleaseLock (&mRtc.RtcLock); 604 return EFI_DEVICE_ERROR; 605 } 606 // 607 // Read Register B and Register C 608 // 609 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B); 610 RegisterC.Data = RtcRead (RTC_ADDRESS_REGISTER_C); 611 612 // 613 // Get the Time/Date/Daylight Savings values. 614 // 615 *Enabled = RegisterB.Bits.AIE; 616 if (*Enabled) { 617 Time->Second = RtcRead (RTC_ADDRESS_SECONDS_ALARM); 618 Time->Minute = RtcRead (RTC_ADDRESS_MINUTES_ALARM); 619 Time->Hour = RtcRead (RTC_ADDRESS_HOURS_ALARM); 620 Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH); 621 Time->Month = RtcRead (RTC_ADDRESS_MONTH); 622 Time->Year = RtcRead (RTC_ADDRESS_YEAR); 623 } else { 624 Time->Second = 0; 625 Time->Minute = 0; 626 Time->Hour = 0; 627 Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH); 628 Time->Month = RtcRead (RTC_ADDRESS_MONTH); 629 Time->Year = RtcRead (RTC_ADDRESS_YEAR); 630 } 631 632 ConvertRtcTimeToEfiTime (Time, RegisterB); 633 634 if (RtcTestCenturyRegister () == EFI_SUCCESS) { 635 Century = BcdToDecimal ((UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f)); 636 } else { 637 Century = BcdToDecimal (RtcRead (RTC_ADDRESS_CENTURY)); 638 } 639 640 Time->Year = (UINT16) (Century * 100 + Time->Year); 641 642 // 643 // Release RTC Lock. 644 // 645 EfiReleaseLock (&mRtc.RtcLock); 646 647 // 648 // Make sure all field values are in correct range 649 // 650 Status = RtcTimeFieldsValid (Time); 651 if (EFI_ERROR (Status)) { 652 return EFI_DEVICE_ERROR; 653 } 654 655 *Pending = RegisterC.Bits.AF; 656 657 return EFI_SUCCESS; 658 } 659 660 EFI_STATUS 661 LibSetWakeupTime ( 662 IN BOOLEAN Enabled, 663 OUT EFI_TIME *Time 664 ) 665 { 666 EFI_STATUS Status; 667 EFI_TIME RtcTime; 668 RTC_REGISTER_B RegisterB; 669 UINT8 Century; 670 EFI_TIME_CAPABILITIES Capabilities; 671 672 if (Enabled) { 673 674 if (Time == NULL) { 675 return EFI_INVALID_PARAMETER; 676 } 677 // 678 // Make sure that the time fields are valid 679 // 680 Status = RtcTimeFieldsValid (Time); 681 if (EFI_ERROR (Status)) { 682 return EFI_INVALID_PARAMETER; 683 } 684 // 685 // Just support set alarm time within 24 hours 686 // 687 LibGetTime (&RtcTime, &Capabilities); 688 if (Time->Year != RtcTime.Year || 689 Time->Month != RtcTime.Month || 690 (Time->Day != RtcTime.Day && Time->Day != (RtcTime.Day + 1)) 691 ) { 692 return EFI_UNSUPPORTED; 693 } 694 // 695 // Make a local copy of the time and date 696 // 697 CopyMem (&RtcTime, Time, sizeof (EFI_TIME)); 698 699 } 700 // 701 // Acquire RTC Lock to make access to RTC atomic 702 // 703 EfiAcquireLock (&mRtc.RtcLock); 704 705 // 706 // Wait for up to 0.1 seconds for the RTC to be updated 707 // 708 Status = RtcWaitToUpdate (100000); 709 if (EFI_ERROR (Status)) { 710 EfiReleaseLock (&mRtc.RtcLock); 711 return EFI_DEVICE_ERROR; 712 } 713 // 714 // Read Register B, and inhibit updates of the RTC 715 // 716 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B); 717 718 RegisterB.Bits.SET = 1; 719 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data); 720 721 if (Enabled) { 722 ConvertEfiTimeToRtcTime (&RtcTime, RegisterB, &Century); 723 724 // 725 // Set RTC alarm time 726 // 727 RtcWrite (RTC_ADDRESS_SECONDS_ALARM, RtcTime.Second); 728 RtcWrite (RTC_ADDRESS_MINUTES_ALARM, RtcTime.Minute); 729 RtcWrite (RTC_ADDRESS_HOURS_ALARM, RtcTime.Hour); 730 731 RegisterB.Bits.AIE = 1; 732 733 } else { 734 RegisterB.Bits.AIE = 0; 735 } 736 // 737 // Allow updates of the RTC registers 738 // 739 RegisterB.Bits.SET = 0; 740 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data); 741 742 // 743 // Release RTC Lock. 744 // 745 EfiReleaseLock (&mRtc.RtcLock); 746 747 return EFI_SUCCESS; 748 } 749 750 751 752 VOID 753 LibRtcVirtualAddressChangeEvent ( 754 VOID 755 ) 756 { 757 } 758 759 760 VOID 761 LibRtcInitialize ( 762 VOID 763 ) 764 { 765 EFI_STATUS Status; 766 RTC_REGISTER_A RegisterA; 767 RTC_REGISTER_B RegisterB; 768 RTC_REGISTER_C RegisterC; 769 RTC_REGISTER_D RegisterD; 770 UINT8 Century; 771 EFI_TIME Time; 772 773 // 774 // Acquire RTC Lock to make access to RTC atomic 775 // 776 EfiAcquireLock (&mRtc.RtcLock); 777 778 // 779 // Initialize RTC Register 780 // 781 // Make sure Division Chain is properly configured, 782 // or RTC clock won't "tick" -- time won't increment 783 // 784 RegisterA.Data = RTC_INIT_REGISTER_A; 785 RtcWrite (RTC_ADDRESS_REGISTER_A, RegisterA.Data); 786 787 // 788 // Read Register B 789 // 790 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B); 791 792 // 793 // Clear RTC flag register 794 // 795 RegisterC.Data = RtcRead (RTC_ADDRESS_REGISTER_C); 796 797 // 798 // Clear RTC register D 799 // 800 RegisterD.Data = RTC_INIT_REGISTER_D; 801 RtcWrite (RTC_ADDRESS_REGISTER_D, RegisterD.Data); 802 803 // 804 // Wait for up to 0.1 seconds for the RTC to be updated 805 // 806 Status = RtcWaitToUpdate (100000); 807 if (EFI_ERROR (Status)) { 808 EfiReleaseLock (&mRtc.RtcLock); 809 return; 810 } 811 812 // 813 // Get the Time/Date/Daylight Savings values. 814 // 815 Time.Second = RtcRead (RTC_ADDRESS_SECONDS); 816 Time.Minute = RtcRead (RTC_ADDRESS_MINUTES); 817 Time.Hour = RtcRead (RTC_ADDRESS_HOURS); 818 Time.Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH); 819 Time.Month = RtcRead (RTC_ADDRESS_MONTH); 820 Time.Year = RtcRead (RTC_ADDRESS_YEAR); 821 822 ConvertRtcTimeToEfiTime (&Time, RegisterB); 823 824 if (RtcTestCenturyRegister () == EFI_SUCCESS) { 825 Century = BcdToDecimal ((UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f)); 826 } else { 827 Century = BcdToDecimal (RtcRead (RTC_ADDRESS_CENTURY)); 828 } 829 830 Time.Year = (UINT16) (Century * 100 + Time.Year); 831 832 // 833 // Set RTC configuration after get original time 834 // 835 RtcWrite (RTC_ADDRESS_REGISTER_B, RTC_INIT_REGISTER_B); 836 837 // 838 // Release RTC Lock. 839 // 840 EfiReleaseLock (&mRtc.RtcLock); 841 842 // 843 // Validate time fields 844 // 845 Status = RtcTimeFieldsValid (&Time); 846 if (EFI_ERROR (Status)) { 847 Time.Second = RTC_INIT_SECOND; 848 Time.Minute = RTC_INIT_MINUTE; 849 Time.Hour = RTC_INIT_HOUR; 850 Time.Day = RTC_INIT_DAY; 851 Time.Month = RTC_INIT_MONTH; 852 Time.Year = RTC_INIT_YEAR; 853 } 854 // 855 // Reset time value according to new RTC configuration 856 // 857 LibSetTime (&Time); 858 859 return; 860 } 861 862 863