/** @file\r
RTC Architectural Protocol GUID as defined in DxeCis 0.96.\r
\r
-Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2017, AMD Inc. All rights reserved.<BR>\r
+\r
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
\r
#include "PcRtc.h"\r
\r
+//\r
+// Days of month.\r
+//\r
+UINTN mDayOfMonth[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };\r
+\r
+//\r
+// The name of NV variable to store the timezone and daylight saving information.\r
+//\r
+CHAR16 mTimeZoneVariableName[] = L"RTC";\r
+\r
/**\r
Compare the Hour, Minute and Second of the From time and the To time.\r
- \r
+\r
Only compare H/M/S in EFI_TIME and ignore other fields here.\r
\r
@param From the first time\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
+ IoWrite8 (PcdGet8 (PcdRtcIndexRegister), (UINT8) (Address | (UINT8) (IoRead8 (PcdGet8 (PcdRtcIndexRegister)) & 0x80)));\r
+ return IoRead8 (PcdGet8 (PcdRtcTargetRegister));\r
}\r
\r
/**\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
+ IoWrite8 (PcdGet8 (PcdRtcIndexRegister), (UINT8) (Address | (UINT8) (IoRead8 (PcdGet8 (PcdRtcIndexRegister)) & 0x80)));\r
+ IoWrite8 (PcdGet8 (PcdRtcTargetRegister), Data);\r
}\r
\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
+ RegisterA.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterA);\r
RtcWrite (RTC_ADDRESS_REGISTER_A, RegisterA.Data);\r
\r
//\r
//\r
// Clear RTC register D\r
//\r
- RegisterD.Data = RTC_INIT_REGISTER_D;\r
+ RegisterD.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterD);\r
RtcWrite (RTC_ADDRESS_REGISTER_D, RegisterD.Data);\r
\r
//\r
// Set RTC configuration after get original time\r
// The value of bit AIE should be reserved.\r
//\r
- RtcWrite (RTC_ADDRESS_REGISTER_B, (UINT8)(RTC_INIT_REGISTER_B | (RegisterB.Data & BIT5)));\r
+ RegisterB.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterB) | (RegisterB.Data & BIT5);\r
+ RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
\r
//\r
// Release RTC Lock.\r
if (!EfiAtRuntime ()) {\r
EfiReleaseLock (&Global->RtcLock);\r
}\r
- \r
+\r
//\r
// Get the data of Daylight saving and time zone, if they have been\r
// stored in NV variable during previous boot.\r
//\r
DataSize = sizeof (UINT32);\r
Status = EfiGetVariable (\r
- L"RTC",\r
+ mTimeZoneVariableName,\r
&gEfiCallerIdGuid,\r
NULL,\r
&DataSize,\r
- (VOID *) &TimerVar\r
+ &TimerVar\r
);\r
if (!EFI_ERROR (Status)) {\r
Time.TimeZone = (INT16) TimerVar;\r
Time.Daylight = (UINT8) (TimerVar >> 16);\r
} else {\r
Time.TimeZone = EFI_UNSPECIFIED_TIMEZONE;\r
- Time.Daylight = 0; \r
+ Time.Daylight = 0;\r
}\r
\r
//\r
if (EFI_ERROR (Status)) {\r
return EFI_DEVICE_ERROR;\r
}\r
- \r
+\r
//\r
// Reset wakeup time value to valid state when wakeup alarm is disabled and wakeup time is invalid.\r
// Global variable has already had valid SavedTimeZone and Daylight,\r
if ((Enabled) || (!EFI_ERROR (Status))) {\r
return EFI_SUCCESS;\r
}\r
- \r
+\r
//\r
- // When wakeup time is disabled and invalid, reset wakeup time register to valid state \r
+ // When wakeup time is disabled and invalid, reset wakeup time register to valid state\r
// but keep wakeup alarm disabled.\r
//\r
Time.Second = RTC_INIT_SECOND;\r
}\r
return EFI_DEVICE_ERROR;\r
}\r
- \r
+\r
//\r
// Inhibit updates of the RTC\r
//\r
RegisterB.Bits.Set = 1;\r
RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
- \r
+\r
//\r
// Set RTC alarm time registers\r
//\r
//\r
RegisterB.Bits.Set = 0;\r
RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
- \r
+\r
//\r
// Release RTC Lock.\r
//\r
}\r
return Status;\r
}\r
- \r
+\r
//\r
// Write timezone and daylight to RTC variable\r
//\r
- TimerVar = Time->Daylight;\r
- TimerVar = (UINT32) ((TimerVar << 16) | (UINT16)(Time->TimeZone));\r
- Status = EfiSetVariable (\r
- L"RTC",\r
- &gEfiCallerIdGuid,\r
- EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
- sizeof (TimerVar),\r
- &TimerVar\r
- );\r
+ if ((Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE) && (Time->Daylight == 0)) {\r
+ Status = EfiSetVariable (\r
+ mTimeZoneVariableName,\r
+ &gEfiCallerIdGuid,\r
+ 0,\r
+ 0,\r
+ NULL\r
+ );\r
+ if (Status == EFI_NOT_FOUND) {\r
+ Status = EFI_SUCCESS;\r
+ }\r
+ } else {\r
+ TimerVar = Time->Daylight;\r
+ TimerVar = (UINT32) ((TimerVar << 16) | (UINT16)(Time->TimeZone));\r
+ Status = EfiSetVariable (\r
+ mTimeZoneVariableName,\r
+ &gEfiCallerIdGuid,\r
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
+ sizeof (TimerVar),\r
+ &TimerVar\r
+ );\r
+ }\r
+\r
if (EFI_ERROR (Status)) {\r
if (!EfiAtRuntime ()) {\r
EfiReleaseLock (&Global->RtcLock);\r
RegisterB.Bits.Set = 1;\r
RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
\r
+ //\r
+ // Store the century value to RTC before converting to BCD format.\r
+ //\r
+ if (Global->CenturyRtcAddress != 0) {\r
+ RtcWrite (Global->CenturyRtcAddress, DecimalToBcd8 ((UINT8) (RtcTime.Year / 100)));\r
+ }\r
+\r
ConvertEfiTimeToRtcTime (&RtcTime, RegisterB);\r
\r
RtcWrite (RTC_ADDRESS_SECONDS, RtcTime.Second);\r
}\r
return EFI_DEVICE_ERROR;\r
}\r
- \r
+\r
//\r
// Inhibit updates of the RTC\r
//\r
@param Timeout Tell how long it should take to wait.\r
\r
@retval EFI_DEVICE_ERROR RTC device error.\r
- @retval EFI_SUCCESS RTC is updated and ready. \r
+ @retval EFI_SUCCESS RTC is updated and ready.\r
**/\r
EFI_STATUS\r
RtcWaitToUpdate (\r
IN EFI_TIME *Time\r
)\r
{\r
- INTN DayOfMonth[12];\r
-\r
- DayOfMonth[0] = 31;\r
- DayOfMonth[1] = 29;\r
- DayOfMonth[2] = 31;\r
- DayOfMonth[3] = 30;\r
- DayOfMonth[4] = 31;\r
- DayOfMonth[5] = 30;\r
- DayOfMonth[6] = 31;\r
- DayOfMonth[7] = 31;\r
- DayOfMonth[8] = 30;\r
- DayOfMonth[9] = 31;\r
- DayOfMonth[10] = 30;\r
- DayOfMonth[11] = 31;\r
-\r
//\r
// The validity of Time->Month field should be checked before\r
//\r
ASSERT (Time->Month >=1);\r
ASSERT (Time->Month <=12);\r
if (Time->Day < 1 ||\r
- Time->Day > DayOfMonth[Time->Month - 1] ||\r
+ Time->Day > mDayOfMonth[Time->Month - 1] ||\r
(Time->Month == 2 && (!IsLeapYear (Time) && Time->Day > 28))\r
) {\r
return FALSE;\r
\r
/**\r
Compare the Hour, Minute and Second of the From time and the To time.\r
- \r
+\r
Only compare H/M/S in EFI_TIME and ignore other fields here.\r
\r
@param From the first time\r
IN EFI_TIME *To\r
)\r
{\r
- UINT8 DayOfMonth[12];\r
BOOLEAN Adjacent;\r
\r
- DayOfMonth[0] = 31;\r
- DayOfMonth[1] = 29;\r
- DayOfMonth[2] = 31;\r
- DayOfMonth[3] = 30;\r
- DayOfMonth[4] = 31;\r
- DayOfMonth[5] = 30;\r
- DayOfMonth[6] = 31;\r
- DayOfMonth[7] = 31;\r
- DayOfMonth[8] = 30;\r
- DayOfMonth[9] = 31;\r
- DayOfMonth[10] = 30;\r
- DayOfMonth[11] = 31;\r
-\r
Adjacent = FALSE;\r
\r
//\r
//\r
ASSERT (From->Month >=1);\r
ASSERT (From->Month <=12);\r
- \r
+\r
if (From->Year == To->Year) {\r
if (From->Month == To->Month) {\r
if ((From->Day + 1) == To->Day) {\r
Adjacent = TRUE;\r
}\r
}\r
- } else if (From->Day == DayOfMonth[From->Month - 1]) {\r
+ } else if (From->Day == mDayOfMonth[From->Month - 1]) {\r
if ((CompareHMS(From, To) >= 0)) {\r
Adjacent = TRUE;\r
}\r
return Adjacent;\r
}\r
\r
+/**\r
+ This function find ACPI table with the specified signature in RSDT or XSDT.\r
+\r
+ @param Sdt ACPI RSDT or XSDT.\r
+ @param Signature ACPI table signature.\r
+ @param TablePointerSize Size of table pointer: 4 or 8.\r
+\r
+ @return ACPI table or NULL if not found.\r
+**/\r
+VOID *\r
+ScanTableInSDT (\r
+ IN EFI_ACPI_DESCRIPTION_HEADER *Sdt,\r
+ IN UINT32 Signature,\r
+ IN UINTN TablePointerSize\r
+ )\r
+{\r
+ UINTN Index;\r
+ UINTN EntryCount;\r
+ UINTN EntryBase;\r
+ EFI_ACPI_DESCRIPTION_HEADER *Table;\r
+\r
+ EntryCount = (Sdt->Length - sizeof (EFI_ACPI_DESCRIPTION_HEADER)) / TablePointerSize;\r
+\r
+ EntryBase = (UINTN) (Sdt + 1);\r
+ for (Index = 0; Index < EntryCount; Index++) {\r
+ //\r
+ // When TablePointerSize is 4 while sizeof (VOID *) is 8, make sure the upper 4 bytes are zero.\r
+ //\r
+ Table = 0;\r
+ CopyMem (&Table, (VOID *) (EntryBase + Index * TablePointerSize), TablePointerSize);\r
+\r
+ if (Table == NULL) {\r
+ continue;\r
+ }\r
+\r
+ if (Table->Signature == Signature) {\r
+ return Table;\r
+ }\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r
+/**\r
+ Get the century RTC address from the ACPI FADT table.\r
+\r
+ @return The century RTC address or 0 if not found.\r
+**/\r
+UINT8\r
+GetCenturyRtcAddress (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *Rsdp;\r
+ EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt;\r
+\r
+ Status = EfiGetSystemConfigurationTable (&gEfiAcpiTableGuid, (VOID **) &Rsdp);\r
+ if (EFI_ERROR (Status)) {\r
+ Status = EfiGetSystemConfigurationTable (&gEfiAcpi10TableGuid, (VOID **) &Rsdp);\r
+ }\r
+\r
+ if (EFI_ERROR (Status) || (Rsdp == NULL)) {\r
+ return 0;\r
+ }\r
+\r
+ Fadt = NULL;\r
+\r
+ //\r
+ // Find FADT in XSDT\r
+ //\r
+ if (Rsdp->Revision >= EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER_REVISION && Rsdp->XsdtAddress != 0) {\r
+ Fadt = ScanTableInSDT (\r
+ (EFI_ACPI_DESCRIPTION_HEADER *) (UINTN) Rsdp->XsdtAddress,\r
+ EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE,\r
+ sizeof (UINTN)\r
+ );\r
+ }\r
+\r
+ //\r
+ // Find FADT in RSDT\r
+ //\r
+ if (Fadt == NULL && Rsdp->RsdtAddress != 0) {\r
+ Fadt = ScanTableInSDT (\r
+ (EFI_ACPI_DESCRIPTION_HEADER *) (UINTN) Rsdp->RsdtAddress,\r
+ EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE,\r
+ sizeof (UINT32)\r
+ );\r
+ }\r
+\r
+ if ((Fadt != NULL) &&\r
+ (Fadt->Century > RTC_ADDRESS_REGISTER_D) && (Fadt->Century < 0x80)\r
+ ) {\r
+ return Fadt->Century;\r
+ } else {\r
+ return 0;\r
+ }\r
+}\r
+\r
+/**\r
+ Notification function of ACPI Table change.\r
+\r
+ This is a notification function registered on ACPI Table change event.\r
+ It saves the Century address stored in ACPI FADT table.\r
+\r
+ @param Event Event whose notification function is being invoked.\r
+ @param Context Pointer to the notification function's context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+PcRtcAcpiTableChangeCallback (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_TIME Time;\r
+ UINT8 CenturyRtcAddress;\r
+ UINT8 Century;\r
+\r
+ CenturyRtcAddress = GetCenturyRtcAddress ();\r
+ if ((CenturyRtcAddress != 0) && (mModuleGlobal.CenturyRtcAddress != CenturyRtcAddress)) {\r
+ mModuleGlobal.CenturyRtcAddress = CenturyRtcAddress;\r
+ Status = PcRtcGetTime (&Time, NULL, &mModuleGlobal);\r
+ if (!EFI_ERROR (Status)) {\r
+ Century = (UINT8) (Time.Year / 100);\r
+ Century = DecimalToBcd8 (Century);\r
+ DEBUG ((EFI_D_INFO, "PcRtc: Write 0x%x to CMOS location 0x%x\n", Century, mModuleGlobal.CenturyRtcAddress));\r
+ RtcWrite (mModuleGlobal.CenturyRtcAddress, Century);\r
+ }\r
+ }\r
+}\r