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