Home | History | Annotate | Download | only in HalRuntimeServicesExampleLib
      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