/** @file | |
Simple PC RTC | |
Copyright (c) 2007, Intel Corporation. All rights reserved.<BR> | |
Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR> | |
Copyright (c) 2014, ARM Ltd. All rights reserved. | |
This program and the accompanying materials | |
are licensed and made available under the terms and conditions of the BSD License | |
which accompanies this distribution. The full text of the license may be found at | |
http://opensource.org/licenses/bsd-license.php | |
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
**/ | |
typedef struct { | |
EFI_LOCK RtcLock; | |
UINT16 SavedTimeZone; | |
UINT8 Daylight; | |
} PC_RTC_GLOBALS; | |
#define PCAT_RTC_ADDRESS_REGISTER 0x70 | |
#define PCAT_RTC_DATA_REGISTER 0x71 | |
// | |
// Dallas DS12C887 Real Time Clock | |
// | |
#define RTC_ADDRESS_SECONDS 0 // R/W Range 0..59 | |
#define RTC_ADDRESS_SECONDS_ALARM 1 // R/W Range 0..59 | |
#define RTC_ADDRESS_MINUTES 2 // R/W Range 0..59 | |
#define RTC_ADDRESS_MINUTES_ALARM 3 // R/W Range 0..59 | |
#define RTC_ADDRESS_HOURS 4 // R/W Range 1..12 or 0..23 Bit 7 is AM/PM | |
#define RTC_ADDRESS_HOURS_ALARM 5 // R/W Range 1..12 or 0..23 Bit 7 is AM/PM | |
#define RTC_ADDRESS_DAY_OF_THE_WEEK 6 // R/W Range 1..7 | |
#define RTC_ADDRESS_DAY_OF_THE_MONTH 7 // R/W Range 1..31 | |
#define RTC_ADDRESS_MONTH 8 // R/W Range 1..12 | |
#define RTC_ADDRESS_YEAR 9 // R/W Range 0..99 | |
#define RTC_ADDRESS_REGISTER_A 10 // R/W[0..6] R0[7] | |
#define RTC_ADDRESS_REGISTER_B 11 // R/W | |
#define RTC_ADDRESS_REGISTER_C 12 // RO | |
#define RTC_ADDRESS_REGISTER_D 13 // RO | |
#define RTC_ADDRESS_CENTURY 50 // R/W Range 19..20 Bit 8 is R/W | |
// | |
// Date and time initial values. | |
// They are used if the RTC values are invalid during driver initialization | |
// | |
#define RTC_INIT_SECOND 0 | |
#define RTC_INIT_MINUTE 0 | |
#define RTC_INIT_HOUR 0 | |
#define RTC_INIT_DAY 1 | |
#define RTC_INIT_MONTH 1 | |
#define RTC_INIT_YEAR 2001 | |
// | |
// Register initial values | |
// | |
#define RTC_INIT_REGISTER_A 0x26 | |
#define RTC_INIT_REGISTER_B 0x02 | |
#define RTC_INIT_REGISTER_D 0x0 | |
#pragma pack(1) | |
// | |
// Register A | |
// | |
typedef struct { | |
UINT8 RS : 4; // Rate Selection Bits | |
UINT8 DV : 3; // Divisor | |
UINT8 UIP : 1; // Update in progress | |
} RTC_REGISTER_A_BITS; | |
typedef union { | |
RTC_REGISTER_A_BITS Bits; | |
UINT8 Data; | |
} RTC_REGISTER_A; | |
// | |
// Register B | |
// | |
typedef struct { | |
UINT8 DSE : 1; // 0 - Daylight saving disabled 1 - Daylight savings enabled | |
UINT8 MIL : 1; // 0 - 12 hour mode 1 - 24 hour mode | |
UINT8 DM : 1; // 0 - BCD Format 1 - Binary Format | |
UINT8 SQWE : 1; // 0 - Disable SQWE output 1 - Enable SQWE output | |
UINT8 UIE : 1; // 0 - Update INT disabled 1 - Update INT enabled | |
UINT8 AIE : 1; // 0 - Alarm INT disabled 1 - Alarm INT Enabled | |
UINT8 PIE : 1; // 0 - Periodic INT disabled 1 - Periodic INT Enabled | |
UINT8 SET : 1; // 0 - Normal operation. 1 - Updates inhibited | |
} RTC_REGISTER_B_BITS; | |
typedef union { | |
RTC_REGISTER_B_BITS Bits; | |
UINT8 Data; | |
} RTC_REGISTER_B; | |
// | |
// Register C | |
// | |
typedef struct { | |
UINT8 Reserved : 4; // Read as zero. Can not be written. | |
UINT8 UF : 1; // Update End Interrupt Flag | |
UINT8 AF : 1; // Alarm Interrupt Flag | |
UINT8 PF : 1; // Periodic Interrupt Flag | |
UINT8 IRQF : 1; // Iterrupt Request Flag = PF & PIE | AF & AIE | UF & UIE | |
} RTC_REGISTER_C_BITS; | |
typedef union { | |
RTC_REGISTER_C_BITS Bits; | |
UINT8 Data; | |
} RTC_REGISTER_C; | |
// | |
// Register D | |
// | |
typedef struct { | |
UINT8 Reserved : 7; // Read as zero. Can not be written. | |
UINT8 VRT : 1; // Valid RAM and Time | |
} RTC_REGISTER_D_BITS; | |
typedef union { | |
RTC_REGISTER_D_BITS Bits; | |
UINT8 Data; | |
} RTC_REGISTER_D; | |
#pragma pack() | |
PC_RTC_GLOBALS mRtc; | |
BOOLEAN | |
IsLeapYear ( | |
IN EFI_TIME *Time | |
) | |
{ | |
if (Time->Year % 4 == 0) { | |
if (Time->Year % 100 == 0) { | |
if (Time->Year % 400 == 0) { | |
return TRUE; | |
} else { | |
return FALSE; | |
} | |
} else { | |
return TRUE; | |
} | |
} else { | |
return FALSE; | |
} | |
} | |
const INTN mDayOfMonth[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; | |
BOOLEAN | |
DayValid ( | |
IN EFI_TIME *Time | |
) | |
{ | |
if (Time->Day < 1 || | |
Time->Day > mDayOfMonth[Time->Month - 1] || | |
(Time->Month == 2 && (!IsLeapYear (Time) && Time->Day > 28)) | |
) { | |
return FALSE; | |
} | |
return TRUE; | |
} | |
UINT8 | |
DecimaltoBcd ( | |
IN UINT8 DecValue | |
) | |
{ | |
UINTN High; | |
UINTN Low; | |
High = DecValue / 10; | |
Low = DecValue - (High * 10); | |
return (UINT8) (Low + (High << 4)); | |
} | |
UINT8 | |
BcdToDecimal ( | |
IN UINT8 BcdValue | |
) | |
{ | |
UINTN High; | |
UINTN Low; | |
High = BcdValue >> 4; | |
Low = BcdValue - (High << 4); | |
return (UINT8) (Low + (High * 10)); | |
} | |
VOID | |
ConvertEfiTimeToRtcTime ( | |
IN EFI_TIME *Time, | |
IN RTC_REGISTER_B RegisterB, | |
IN UINT8 *Century | |
) | |
{ | |
BOOLEAN PM; | |
PM = TRUE; | |
// | |
// Adjust hour field if RTC in in 12 hour mode | |
// | |
if (RegisterB.Bits.MIL == 0) { | |
if (Time->Hour < 12) { | |
PM = FALSE; | |
} | |
if (Time->Hour >= 13) { | |
Time->Hour = (UINT8) (Time->Hour - 12); | |
} else if (Time->Hour == 0) { | |
Time->Hour = 12; | |
} | |
} | |
// | |
// Set the Time/Date/Daylight Savings values. | |
// | |
*Century = DecimaltoBcd ((UINT8) (Time->Year / 100)); | |
Time->Year = (UINT16) (Time->Year % 100); | |
if (RegisterB.Bits.DM == 0) { | |
Time->Year = DecimaltoBcd ((UINT8) Time->Year); | |
Time->Month = DecimaltoBcd (Time->Month); | |
Time->Day = DecimaltoBcd (Time->Day); | |
Time->Hour = DecimaltoBcd (Time->Hour); | |
Time->Minute = DecimaltoBcd (Time->Minute); | |
Time->Second = DecimaltoBcd (Time->Second); | |
} | |
// | |
// If we are in 12 hour mode and PM is set, then set bit 7 of the Hour field. | |
// | |
if (RegisterB.Bits.MIL == 0 && PM) { | |
Time->Hour = (UINT8) (Time->Hour | 0x80); | |
} | |
} | |
/** | |
Check the validity of all the fields of a data structure of type EFI_TIME | |
@param[in] Time Pointer to a data structure of type EFI_TIME that defines a date and time | |
@retval EFI_SUCCESS All date and time fields are valid | |
@retval EFI_INVALID_PARAMETER At least one date or time field is not valid | |
**/ | |
EFI_STATUS | |
RtcTimeFieldsValid ( | |
IN EFI_TIME *Time | |
) | |
{ | |
if ((Time->Year < 1998 ) || | |
(Time->Year > 2099 ) || | |
(Time->Month < 1 ) || | |
(Time->Month > 12 ) || | |
(!DayValid (Time)) || | |
(Time->Hour > 23 ) || | |
(Time->Minute > 59 ) || | |
(Time->Second > 59 ) || | |
(Time->Nanosecond > 999999999) || | |
((Time->TimeZone != EFI_UNSPECIFIED_TIMEZONE) && | |
((Time->TimeZone < -1440) || | |
(Time->TimeZone > 1440 ) ) ) || | |
(Time->Daylight & (~(EFI_TIME_ADJUST_DAYLIGHT | | |
EFI_TIME_IN_DAYLIGHT ))) | |
) { | |
return EFI_INVALID_PARAMETER; | |
} | |
return EFI_SUCCESS; | |
} | |
UINT8 | |
RtcRead ( | |
IN UINT8 Address | |
) | |
{ | |
IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, (UINT8) (Address | (UINT8) (IoRead8 (PCAT_RTC_ADDRESS_REGISTER) & 0x80))); | |
return IoRead8 (PCAT_RTC_DATA_REGISTER); | |
} | |
VOID | |
RtcWrite ( | |
IN UINT8 Address, | |
IN UINT8 Data | |
) | |
{ | |
IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, (UINT8) (Address | (UINT8) (IoRead8 (PCAT_RTC_ADDRESS_REGISTER) & 0x80))); | |
IoWrite8 (PCAT_RTC_DATA_REGISTER, Data); | |
} | |
EFI_STATUS | |
RtcTestCenturyRegister ( | |
VOID | |
) | |
{ | |
UINT8 Century; | |
UINT8 Temp; | |
Century = RtcRead (RTC_ADDRESS_CENTURY); | |
// | |
// RtcWrite (RTC_ADDRESS_CENTURY, 0x00); | |
// | |
Temp = (UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f); | |
RtcWrite (RTC_ADDRESS_CENTURY, Century); | |
if (Temp == 0x19 || Temp == 0x20) { | |
return EFI_SUCCESS; | |
} | |
return EFI_DEVICE_ERROR; | |
} | |
VOID | |
ConvertRtcTimeToEfiTime ( | |
IN EFI_TIME *Time, | |
IN RTC_REGISTER_B RegisterB | |
) | |
{ | |
BOOLEAN PM; | |
if ((Time->Hour) & 0x80) { | |
PM = TRUE; | |
} else { | |
PM = FALSE; | |
} | |
Time->Hour = (UINT8) (Time->Hour & 0x7f); | |
if (RegisterB.Bits.DM == 0) { | |
Time->Year = BcdToDecimal ((UINT8) Time->Year); | |
Time->Month = BcdToDecimal (Time->Month); | |
Time->Day = BcdToDecimal (Time->Day); | |
Time->Hour = BcdToDecimal (Time->Hour); | |
Time->Minute = BcdToDecimal (Time->Minute); | |
Time->Second = BcdToDecimal (Time->Second); | |
} | |
// | |
// If time is in 12 hour format, convert it to 24 hour format | |
// | |
if (RegisterB.Bits.MIL == 0) { | |
if (PM && Time->Hour < 12) { | |
Time->Hour = (UINT8) (Time->Hour + 12); | |
} | |
if (!PM && Time->Hour == 12) { | |
Time->Hour = 0; | |
} | |
} | |
Time->Nanosecond = 0; | |
Time->TimeZone = EFI_UNSPECIFIED_TIMEZONE; | |
Time->Daylight = 0; | |
} | |
EFI_STATUS | |
RtcWaitToUpdate ( | |
UINTN Timeout | |
) | |
{ | |
RTC_REGISTER_A RegisterA; | |
RTC_REGISTER_D RegisterD; | |
// | |
// See if the RTC is functioning correctly | |
// | |
RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D); | |
if (RegisterD.Bits.VRT == 0) { | |
return EFI_DEVICE_ERROR; | |
} | |
// | |
// Wait for up to 0.1 seconds for the RTC to be ready. | |
// | |
Timeout = (Timeout / 10) + 1; | |
RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A); | |
while (RegisterA.Bits.UIP == 1 && Timeout > 0) { | |
MicroSecondDelay (10); | |
RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A); | |
Timeout--; | |
} | |
RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D); | |
if (Timeout == 0 || RegisterD.Bits.VRT == 0) { | |
return EFI_DEVICE_ERROR; | |
} | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
LibGetTime ( | |
OUT EFI_TIME *Time, | |
OUT EFI_TIME_CAPABILITIES *Capabilities | |
) | |
{ | |
EFI_STATUS Status; | |
RTC_REGISTER_B RegisterB; | |
UINT8 Century; | |
UINTN BufferSize; | |
// | |
// Check parameters for null pointer | |
// | |
if (Time == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Acquire RTC Lock to make access to RTC atomic | |
// | |
EfiAcquireLock (&mRtc.RtcLock); | |
// | |
// Wait for up to 0.1 seconds for the RTC to be updated | |
// | |
Status = RtcWaitToUpdate (100000); | |
if (EFI_ERROR (Status)) { | |
EfiReleaseLock (&mRtc.RtcLock); | |
return Status; | |
} | |
// | |
// Read Register B | |
// | |
RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B); | |
// | |
// Get the Time/Date/Daylight Savings values. | |
// | |
Time->Second = RtcRead (RTC_ADDRESS_SECONDS); | |
Time->Minute = RtcRead (RTC_ADDRESS_MINUTES); | |
Time->Hour = RtcRead (RTC_ADDRESS_HOURS); | |
Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH); | |
Time->Month = RtcRead (RTC_ADDRESS_MONTH); | |
Time->Year = RtcRead (RTC_ADDRESS_YEAR); | |
ConvertRtcTimeToEfiTime (Time, RegisterB); | |
if (RtcTestCenturyRegister () == EFI_SUCCESS) { | |
Century = BcdToDecimal ((UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f)); | |
} else { | |
Century = BcdToDecimal (RtcRead (RTC_ADDRESS_CENTURY)); | |
} | |
Time->Year = (UINT16) (Century * 100 + Time->Year); | |
// | |
// Release RTC Lock. | |
// | |
EfiReleaseLock (&mRtc.RtcLock); | |
// | |
// Get the variable that containts the TimeZone and Daylight fields | |
// | |
Time->TimeZone = mRtc.SavedTimeZone; | |
Time->Daylight = mRtc.Daylight; | |
BufferSize = sizeof (INT16) + sizeof (UINT8); | |
// | |
// Make sure all field values are in correct range | |
// | |
Status = RtcTimeFieldsValid (Time); | |
if (EFI_ERROR (Status)) { | |
return EFI_DEVICE_ERROR; | |
} | |
// | |
// Fill in Capabilities if it was passed in | |
// | |
if (Capabilities) { | |
Capabilities->Resolution = 1; | |
// | |
// 1 hertz | |
// | |
Capabilities->Accuracy = 50000000; | |
// | |
// 50 ppm | |
// | |
Capabilities->SetsToZero = FALSE; | |
} | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
LibSetTime ( | |
IN EFI_TIME *Time | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_TIME RtcTime; | |
RTC_REGISTER_B RegisterB; | |
UINT8 Century; | |
if (Time == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Make sure that the time fields are valid | |
// | |
Status = RtcTimeFieldsValid (Time); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
CopyMem (&RtcTime, Time, sizeof (EFI_TIME)); | |
// | |
// Acquire RTC Lock to make access to RTC atomic | |
// | |
EfiAcquireLock (&mRtc.RtcLock); | |
// | |
// Wait for up to 0.1 seconds for the RTC to be updated | |
// | |
Status = RtcWaitToUpdate (100000); | |
if (EFI_ERROR (Status)) { | |
EfiReleaseLock (&mRtc.RtcLock); | |
return Status; | |
} | |
// | |
// Read Register B, and inhibit updates of the RTC | |
// | |
RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B); | |
RegisterB.Bits.SET = 1; | |
RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data); | |
ConvertEfiTimeToRtcTime (&RtcTime, RegisterB, &Century); | |
RtcWrite (RTC_ADDRESS_SECONDS, RtcTime.Second); | |
RtcWrite (RTC_ADDRESS_MINUTES, RtcTime.Minute); | |
RtcWrite (RTC_ADDRESS_HOURS, RtcTime.Hour); | |
RtcWrite (RTC_ADDRESS_DAY_OF_THE_MONTH, RtcTime.Day); | |
RtcWrite (RTC_ADDRESS_MONTH, RtcTime.Month); | |
RtcWrite (RTC_ADDRESS_YEAR, (UINT8) RtcTime.Year); | |
if (RtcTestCenturyRegister () == EFI_SUCCESS) { | |
Century = (UINT8) ((Century & 0x7f) | (RtcRead (RTC_ADDRESS_CENTURY) & 0x80)); | |
} | |
RtcWrite (RTC_ADDRESS_CENTURY, Century); | |
// | |
// Allow updates of the RTC registers | |
// | |
RegisterB.Bits.SET = 0; | |
RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data); | |
// | |
// Release RTC Lock. | |
// | |
EfiReleaseLock (&mRtc.RtcLock); | |
// | |
// Set the variable that containts the TimeZone and Daylight fields | |
// | |
mRtc.SavedTimeZone = Time->TimeZone; | |
mRtc.Daylight = Time->Daylight; | |
return Status; | |
} | |
EFI_STATUS | |
libGetWakeupTime ( | |
OUT BOOLEAN *Enabled, | |
OUT BOOLEAN *Pending, | |
OUT EFI_TIME *Time | |
) | |
{ | |
EFI_STATUS Status; | |
RTC_REGISTER_B RegisterB; | |
RTC_REGISTER_C RegisterC; | |
UINT8 Century; | |
// | |
// Check paramters for null pointers | |
// | |
if ((Enabled == NULL) || (Pending == NULL) || (Time == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Acquire RTC Lock to make access to RTC atomic | |
// | |
EfiAcquireLock (&mRtc.RtcLock); | |
// | |
// Wait for up to 0.1 seconds for the RTC to be updated | |
// | |
Status = RtcWaitToUpdate (100000); | |
if (EFI_ERROR (Status)) { | |
EfiReleaseLock (&mRtc.RtcLock); | |
return EFI_DEVICE_ERROR; | |
} | |
// | |
// Read Register B and Register C | |
// | |
RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B); | |
RegisterC.Data = RtcRead (RTC_ADDRESS_REGISTER_C); | |
// | |
// Get the Time/Date/Daylight Savings values. | |
// | |
*Enabled = RegisterB.Bits.AIE; | |
if (*Enabled) { | |
Time->Second = RtcRead (RTC_ADDRESS_SECONDS_ALARM); | |
Time->Minute = RtcRead (RTC_ADDRESS_MINUTES_ALARM); | |
Time->Hour = RtcRead (RTC_ADDRESS_HOURS_ALARM); | |
Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH); | |
Time->Month = RtcRead (RTC_ADDRESS_MONTH); | |
Time->Year = RtcRead (RTC_ADDRESS_YEAR); | |
} else { | |
Time->Second = 0; | |
Time->Minute = 0; | |
Time->Hour = 0; | |
Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH); | |
Time->Month = RtcRead (RTC_ADDRESS_MONTH); | |
Time->Year = RtcRead (RTC_ADDRESS_YEAR); | |
} | |
ConvertRtcTimeToEfiTime (Time, RegisterB); | |
if (RtcTestCenturyRegister () == EFI_SUCCESS) { | |
Century = BcdToDecimal ((UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f)); | |
} else { | |
Century = BcdToDecimal (RtcRead (RTC_ADDRESS_CENTURY)); | |
} | |
Time->Year = (UINT16) (Century * 100 + Time->Year); | |
// | |
// Release RTC Lock. | |
// | |
EfiReleaseLock (&mRtc.RtcLock); | |
// | |
// Make sure all field values are in correct range | |
// | |
Status = RtcTimeFieldsValid (Time); | |
if (EFI_ERROR (Status)) { | |
return EFI_DEVICE_ERROR; | |
} | |
*Pending = RegisterC.Bits.AF; | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
LibSetWakeupTime ( | |
IN BOOLEAN Enabled, | |
OUT EFI_TIME *Time | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_TIME RtcTime; | |
RTC_REGISTER_B RegisterB; | |
UINT8 Century; | |
EFI_TIME_CAPABILITIES Capabilities; | |
if (Enabled) { | |
if (Time == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Make sure that the time fields are valid | |
// | |
Status = RtcTimeFieldsValid (Time); | |
if (EFI_ERROR (Status)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Just support set alarm time within 24 hours | |
// | |
LibGetTime (&RtcTime, &Capabilities); | |
if (Time->Year != RtcTime.Year || | |
Time->Month != RtcTime.Month || | |
(Time->Day != RtcTime.Day && Time->Day != (RtcTime.Day + 1)) | |
) { | |
return EFI_UNSUPPORTED; | |
} | |
// | |
// Make a local copy of the time and date | |
// | |
CopyMem (&RtcTime, Time, sizeof (EFI_TIME)); | |
} | |
// | |
// Acquire RTC Lock to make access to RTC atomic | |
// | |
EfiAcquireLock (&mRtc.RtcLock); | |
// | |
// Wait for up to 0.1 seconds for the RTC to be updated | |
// | |
Status = RtcWaitToUpdate (100000); | |
if (EFI_ERROR (Status)) { | |
EfiReleaseLock (&mRtc.RtcLock); | |
return EFI_DEVICE_ERROR; | |
} | |
// | |
// Read Register B, and inhibit updates of the RTC | |
// | |
RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B); | |
RegisterB.Bits.SET = 1; | |
RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data); | |
if (Enabled) { | |
ConvertEfiTimeToRtcTime (&RtcTime, RegisterB, &Century); | |
// | |
// Set RTC alarm time | |
// | |
RtcWrite (RTC_ADDRESS_SECONDS_ALARM, RtcTime.Second); | |
RtcWrite (RTC_ADDRESS_MINUTES_ALARM, RtcTime.Minute); | |
RtcWrite (RTC_ADDRESS_HOURS_ALARM, RtcTime.Hour); | |
RegisterB.Bits.AIE = 1; | |
} else { | |
RegisterB.Bits.AIE = 0; | |
} | |
// | |
// Allow updates of the RTC registers | |
// | |
RegisterB.Bits.SET = 0; | |
RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data); | |
// | |
// Release RTC Lock. | |
// | |
EfiReleaseLock (&mRtc.RtcLock); | |
return EFI_SUCCESS; | |
} | |
VOID | |
LibRtcVirtualAddressChangeEvent ( | |
VOID | |
) | |
{ | |
} | |
VOID | |
LibRtcInitialize ( | |
VOID | |
) | |
{ | |
EFI_STATUS Status; | |
RTC_REGISTER_A RegisterA; | |
RTC_REGISTER_B RegisterB; | |
RTC_REGISTER_C RegisterC; | |
RTC_REGISTER_D RegisterD; | |
UINT8 Century; | |
EFI_TIME Time; | |
// | |
// Acquire RTC Lock to make access to RTC atomic | |
// | |
EfiAcquireLock (&mRtc.RtcLock); | |
// | |
// Initialize RTC Register | |
// | |
// Make sure Division Chain is properly configured, | |
// or RTC clock won't "tick" -- time won't increment | |
// | |
RegisterA.Data = RTC_INIT_REGISTER_A; | |
RtcWrite (RTC_ADDRESS_REGISTER_A, RegisterA.Data); | |
// | |
// Read Register B | |
// | |
RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B); | |
// | |
// Clear RTC flag register | |
// | |
RegisterC.Data = RtcRead (RTC_ADDRESS_REGISTER_C); | |
// | |
// Clear RTC register D | |
// | |
RegisterD.Data = RTC_INIT_REGISTER_D; | |
RtcWrite (RTC_ADDRESS_REGISTER_D, RegisterD.Data); | |
// | |
// Wait for up to 0.1 seconds for the RTC to be updated | |
// | |
Status = RtcWaitToUpdate (100000); | |
if (EFI_ERROR (Status)) { | |
EfiReleaseLock (&mRtc.RtcLock); | |
return; | |
} | |
// | |
// Get the Time/Date/Daylight Savings values. | |
// | |
Time.Second = RtcRead (RTC_ADDRESS_SECONDS); | |
Time.Minute = RtcRead (RTC_ADDRESS_MINUTES); | |
Time.Hour = RtcRead (RTC_ADDRESS_HOURS); | |
Time.Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH); | |
Time.Month = RtcRead (RTC_ADDRESS_MONTH); | |
Time.Year = RtcRead (RTC_ADDRESS_YEAR); | |
ConvertRtcTimeToEfiTime (&Time, RegisterB); | |
if (RtcTestCenturyRegister () == EFI_SUCCESS) { | |
Century = BcdToDecimal ((UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f)); | |
} else { | |
Century = BcdToDecimal (RtcRead (RTC_ADDRESS_CENTURY)); | |
} | |
Time.Year = (UINT16) (Century * 100 + Time.Year); | |
// | |
// Set RTC configuration after get original time | |
// | |
RtcWrite (RTC_ADDRESS_REGISTER_B, RTC_INIT_REGISTER_B); | |
// | |
// Release RTC Lock. | |
// | |
EfiReleaseLock (&mRtc.RtcLock); | |
// | |
// Validate time fields | |
// | |
Status = RtcTimeFieldsValid (&Time); | |
if (EFI_ERROR (Status)) { | |
Time.Second = RTC_INIT_SECOND; | |
Time.Minute = RTC_INIT_MINUTE; | |
Time.Hour = RTC_INIT_HOUR; | |
Time.Day = RTC_INIT_DAY; | |
Time.Month = RTC_INIT_MONTH; | |
Time.Year = RTC_INIT_YEAR; | |
} | |
// | |
// Reset time value according to new RTC configuration | |
// | |
LibSetTime (&Time); | |
return; | |
} | |