X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=PcAtChipsetPkg%2FPcatRealTimeClockRuntimeDxe%2FPcRtc.c;h=52af17941786ef81c3911512ee64551724e67209;hp=a77009a21765299e6e9160ed7993367652a21071;hb=HEAD;hpb=0e991a2f87e6146a203dc7ed138a77a1b9796c74 diff --git a/PcAtChipsetPkg/PcatRealTimeClockRuntimeDxe/PcRtc.c b/PcAtChipsetPkg/PcatRealTimeClockRuntimeDxe/PcRtc.c index a77009a217..9242a2e826 100644 --- a/PcAtChipsetPkg/PcatRealTimeClockRuntimeDxe/PcRtc.c +++ b/PcAtChipsetPkg/PcatRealTimeClockRuntimeDxe/PcRtc.c @@ -1,22 +1,32 @@ /** @file RTC Architectural Protocol GUID as defined in DxeCis 0.96. -Copyright (c) 2006 - 2014, Intel Corporation. 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 +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+Copyright (c) 2017, AMD Inc. All rights reserved.
+Copyright (c) 2018 - 2020, ARM Limited. All rights reserved.
-THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "PcRtc.h" +extern UINTN mRtcIndexRegister; +extern UINTN mRtcTargetRegister; + +// +// Days of month. +// +UINTN mDayOfMonth[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +// +// The name of NV variable to store the timezone and daylight saving information. +// +CHAR16 mTimeZoneVariableName[] = L"RTC"; + /** Compare the Hour, Minute and Second of the From time and the To time. - + Only compare H/M/S in EFI_TIME and ignore other fields here. @param From the first time @@ -28,8 +38,8 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ INTN CompareHMS ( - IN EFI_TIME *From, - IN EFI_TIME *To + IN EFI_TIME *From, + IN EFI_TIME *To ); /** @@ -43,43 +53,137 @@ CompareHMS ( **/ BOOLEAN IsWithinOneDay ( - IN EFI_TIME *From, - IN EFI_TIME *To + IN EFI_TIME *From, + IN EFI_TIME *To ); +/** + Read RTC content through its registers using IO access. + + @param Address Address offset of RTC. It is recommended to use + macros such as RTC_ADDRESS_SECONDS. + + @return The data of UINT8 type read from RTC. +**/ +STATIC +UINT8 +IoRtcRead ( + IN UINTN Address + ) +{ + IoWrite8 ( + PcdGet8 (PcdRtcIndexRegister), + (UINT8)(Address | (UINT8)(IoRead8 (PcdGet8 (PcdRtcIndexRegister)) & 0x80)) + ); + return IoRead8 (PcdGet8 (PcdRtcTargetRegister)); +} + +/** + Write RTC through its registers using IO access. + + @param Address Address offset of RTC. It is recommended to use + macros such as RTC_ADDRESS_SECONDS. + @param Data The content you want to write into RTC. + +**/ +STATIC +VOID +IoRtcWrite ( + IN UINTN Address, + IN UINT8 Data + ) +{ + IoWrite8 ( + PcdGet8 (PcdRtcIndexRegister), + (UINT8)(Address | (UINT8)(IoRead8 (PcdGet8 (PcdRtcIndexRegister)) & 0x80)) + ); + IoWrite8 (PcdGet8 (PcdRtcTargetRegister), Data); +} + +/** + Read RTC content through its registers using MMIO access. + + @param Address Address offset of RTC. It is recommended to use + macros such as RTC_ADDRESS_SECONDS. + + @return The data of UINT8 type read from RTC. +**/ +STATIC +UINT8 +MmioRtcRead ( + IN UINTN Address + ) +{ + MmioWrite8 ( + mRtcIndexRegister, + (UINT8)(Address | (UINT8)(MmioRead8 (mRtcIndexRegister) & 0x80)) + ); + return MmioRead8 (mRtcTargetRegister); +} + +/** + Write RTC through its registers using MMIO access. + + @param Address Address offset of RTC. It is recommended to use + macros such as RTC_ADDRESS_SECONDS. + @param Data The content you want to write into RTC. + +**/ +STATIC +VOID +MmioRtcWrite ( + IN UINTN Address, + IN UINT8 Data + ) +{ + MmioWrite8 ( + mRtcIndexRegister, + (UINT8)(Address | (UINT8)(MmioRead8 (mRtcIndexRegister) & 0x80)) + ); + MmioWrite8 (mRtcTargetRegister, Data); +} + /** Read RTC content through its registers. - @param Address Address offset of RTC. It is recommended to use macros such as - RTC_ADDRESS_SECONDS. + @param Address Address offset of RTC. It is recommended to use + macros such as RTC_ADDRESS_SECONDS. @return The data of UINT8 type read from RTC. **/ +STATIC UINT8 RtcRead ( - IN UINT8 Address + IN UINTN Address ) { - IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, (UINT8) (Address | (UINT8) (IoRead8 (PCAT_RTC_ADDRESS_REGISTER) & 0x80))); - return IoRead8 (PCAT_RTC_DATA_REGISTER); + if (FeaturePcdGet (PcdRtcUseMmio)) { + return MmioRtcRead (Address); + } + + return IoRtcRead (Address); } /** Write RTC through its registers. - @param Address Address offset of RTC. It is recommended to use macros such as - RTC_ADDRESS_SECONDS. - @param Data The content you want to write into RTC. + @param Address Address offset of RTC. It is recommended to use + macros such as RTC_ADDRESS_SECONDS. + @param Data The content you want to write into RTC. **/ +STATIC VOID RtcWrite ( - IN UINT8 Address, - IN UINT8 Data + IN UINTN Address, + IN UINT8 Data ) { - IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, (UINT8) (Address | (UINT8) (IoRead8 (PCAT_RTC_ADDRESS_REGISTER) & 0x80))); - IoWrite8 (PCAT_RTC_DATA_REGISTER, Data); + if (FeaturePcdGet (PcdRtcUseMmio)) { + MmioRtcWrite (Address, Data); + } else { + IoRtcWrite (Address, Data); + } } /** @@ -100,10 +204,11 @@ PcRtcInit ( RTC_REGISTER_A RegisterA; RTC_REGISTER_B RegisterB; RTC_REGISTER_D RegisterD; - UINT8 Century; EFI_TIME Time; UINTN DataSize; UINT32 TimerVar; + BOOLEAN Enabled; + BOOLEAN Pending; // // Acquire RTC Lock to make access to RTC atomic @@ -111,13 +216,14 @@ PcRtcInit ( if (!EfiAtRuntime ()) { EfiAcquireLock (&Global->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; + RegisterA.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterA); RtcWrite (RTC_ADDRESS_REGISTER_A, RegisterA.Data); // @@ -133,7 +239,7 @@ PcRtcInit ( // // Clear RTC register D // - RegisterD.Data = RTC_INIT_REGISTER_D; + RegisterD.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterD); RtcWrite (RTC_ADDRESS_REGISTER_D, RegisterD.Data); // @@ -149,8 +255,10 @@ PcRtcInit ( if (!EfiAtRuntime ()) { EfiReleaseLock (&Global->RtcLock); } + return EFI_DEVICE_ERROR; } + // // Get the Time/Date/Daylight Savings values. // @@ -161,48 +269,41 @@ PcRtcInit ( Time.Month = RtcRead (RTC_ADDRESS_MONTH); Time.Year = RtcRead (RTC_ADDRESS_YEAR); - Century = RtcRead (RTC_ADDRESS_CENTURY); - - // - // Set RTC configuration after get original time - // The value of bit AIE should be reserved. - // - RtcWrite (RTC_ADDRESS_REGISTER_B, (UINT8)(RTC_INIT_REGISTER_B | (RegisterB.Data & BIT5))); - // // Release RTC Lock. // if (!EfiAtRuntime ()) { EfiReleaseLock (&Global->RtcLock); } - + // // Get the data of Daylight saving and time zone, if they have been // stored in NV variable during previous boot. // DataSize = sizeof (UINT32); - Status = EfiGetVariable ( - L"RTC", - &gEfiCallerIdGuid, - NULL, - &DataSize, - (VOID *) &TimerVar - ); + Status = EfiGetVariable ( + mTimeZoneVariableName, + &gEfiCallerIdGuid, + NULL, + &DataSize, + &TimerVar + ); if (!EFI_ERROR (Status)) { - Time.TimeZone = (INT16) TimerVar; - Time.Daylight = (UINT8) (TimerVar >> 16); + Time.TimeZone = (INT16)TimerVar; + Time.Daylight = (UINT8)(TimerVar >> 16); } else { Time.TimeZone = EFI_UNSPECIFIED_TIMEZONE; - Time.Daylight = 0; + Time.Daylight = 0; } // // Validate time fields // - Status = ConvertRtcTimeToEfiTime (&Time, Century, RegisterB); + Status = ConvertRtcTimeToEfiTime (&Time, RegisterB); if (!EFI_ERROR (Status)) { Status = RtcTimeFieldsValid (&Time); } + if (EFI_ERROR (Status)) { // // Report Status Code to indicate that the RTC has bad date and time @@ -211,26 +312,122 @@ PcRtcInit ( EFI_ERROR_CODE | EFI_ERROR_MINOR, (EFI_SOFTWARE_DXE_RT_DRIVER | EFI_SW_EC_BAD_DATE_TIME) ); - 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; - Time.Nanosecond = 0; - Time.TimeZone = EFI_UNSPECIFIED_TIMEZONE; - Time.Daylight = 0; + 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 = PcdGet16 (PcdMinimalValidYear); + Time.Nanosecond = 0; + Time.TimeZone = EFI_UNSPECIFIED_TIMEZONE; + Time.Daylight = 0; } + // + // Set RTC configuration after get original time + // The value of bit AIE should be reserved. + // + RegisterB.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterB) | (RegisterB.Data & BIT5); + RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data); + // // Reset time value according to new RTC configuration // Status = PcRtcSetTime (&Time, Global); - if(!EFI_ERROR (Status)) { + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + // + // Reset wakeup time value to valid state when wakeup alarm is disabled and wakeup time is invalid. + // Global variable has already had valid SavedTimeZone and Daylight, + // so we can use them to get and set wakeup time. + // + Status = PcRtcGetWakeupTime (&Enabled, &Pending, &Time, Global); + if ((Enabled) || (!EFI_ERROR (Status))) { return EFI_SUCCESS; - } else { + } + + // + // When wakeup time is disabled and invalid, reset wakeup time register to valid state + // but keep wakeup alarm disabled. + // + 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 = PcdGet16 (PcdMinimalValidYear); + Time.Nanosecond = 0; + Time.TimeZone = Global->SavedTimeZone; + Time.Daylight = Global->Daylight; + + // + // Acquire RTC Lock to make access to RTC atomic + // + if (!EfiAtRuntime ()) { + EfiAcquireLock (&Global->RtcLock); + } + + // + // Wait for up to 0.1 seconds for the RTC to be updated + // + Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout)); + if (EFI_ERROR (Status)) { + if (!EfiAtRuntime ()) { + EfiReleaseLock (&Global->RtcLock); + } + + return EFI_DEVICE_ERROR; + } + + ConvertEfiTimeToRtcTime (&Time, RegisterB); + + // + // Set the Y/M/D info to variable as it has no corresponding hw registers. + // + Status = EfiSetVariable ( + L"RTCALARM", + &gEfiCallerIdGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, + sizeof (Time), + &Time + ); + if (EFI_ERROR (Status)) { + if (!EfiAtRuntime ()) { + EfiReleaseLock (&Global->RtcLock); + } + return EFI_DEVICE_ERROR; } + + // + // Inhibit updates of the RTC + // + RegisterB.Bits.Set = 1; + RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data); + + // + // Set RTC alarm time registers + // + RtcWrite (RTC_ADDRESS_SECONDS_ALARM, Time.Second); + RtcWrite (RTC_ADDRESS_MINUTES_ALARM, Time.Minute); + RtcWrite (RTC_ADDRESS_HOURS_ALARM, Time.Hour); + + // + // Allow updates of the RTC registers + // + RegisterB.Bits.Set = 0; + RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data); + + // + // Release RTC Lock. + // + if (!EfiAtRuntime ()) { + EfiReleaseLock (&Global->RtcLock); + } + + return EFI_SUCCESS; } /** @@ -250,37 +447,39 @@ PcRtcInit ( EFI_STATUS PcRtcGetTime ( OUT EFI_TIME *Time, - OUT EFI_TIME_CAPABILITIES *Capabilities, OPTIONAL + OUT EFI_TIME_CAPABILITIES *Capabilities OPTIONAL, IN PC_RTC_MODULE_GLOBALS *Global ) { EFI_STATUS Status; RTC_REGISTER_B RegisterB; - UINT8 Century; // // Check parameters for null pointer // if (Time == NULL) { return EFI_INVALID_PARAMETER; - } + // // Acquire RTC Lock to make access to RTC atomic // if (!EfiAtRuntime ()) { EfiAcquireLock (&Global->RtcLock); } + // // Wait for up to 0.1 seconds for the RTC to be updated // Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout)); if (EFI_ERROR (Status)) { - if (!EfiAtRuntime ()) { - EfiReleaseLock (&Global->RtcLock); - } + if (!EfiAtRuntime ()) { + EfiReleaseLock (&Global->RtcLock); + } + return Status; } + // // Read Register B // @@ -289,15 +488,13 @@ PcRtcGetTime ( // // 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); + 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); - Century = RtcRead (RTC_ADDRESS_CENTURY); - // // Release RTC Lock. // @@ -308,16 +505,17 @@ PcRtcGetTime ( // // Get the variable that contains the TimeZone and Daylight fields // - Time->TimeZone = Global->SavedTimeZone; - Time->Daylight = Global->Daylight; + Time->TimeZone = Global->SavedTimeZone; + Time->Daylight = Global->Daylight; // // Make sure all field values are in correct range // - Status = ConvertRtcTimeToEfiTime (Time, Century, RegisterB); + Status = ConvertRtcTimeToEfiTime (Time, RegisterB); if (!EFI_ERROR (Status)) { Status = RtcTimeFieldsValid (Time); } + if (EFI_ERROR (Status)) { return EFI_DEVICE_ERROR; } @@ -353,19 +551,19 @@ PcRtcGetTime ( **/ EFI_STATUS PcRtcSetTime ( - IN EFI_TIME *Time, - IN PC_RTC_MODULE_GLOBALS *Global + IN EFI_TIME *Time, + IN PC_RTC_MODULE_GLOBALS *Global ) { EFI_STATUS Status; EFI_TIME RtcTime; RTC_REGISTER_B RegisterB; - UINT8 Century; UINT32 TimerVar; if (Time == NULL) { return EFI_INVALID_PARAMETER; } + // // Make sure that the time fields are valid // @@ -382,52 +580,75 @@ PcRtcSetTime ( if (!EfiAtRuntime ()) { EfiAcquireLock (&Global->RtcLock); } + // // Wait for up to 0.1 seconds for the RTC to be updated // Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout)); if (EFI_ERROR (Status)) { - if (!EfiAtRuntime ()) { - EfiReleaseLock (&Global->RtcLock); - } + if (!EfiAtRuntime ()) { + EfiReleaseLock (&Global->RtcLock); + } + return Status; } - + // // Write timezone and daylight to RTC variable // - TimerVar = Time->Daylight; - TimerVar = (UINT32) ((TimerVar << 16) | (UINT16)(Time->TimeZone)); - Status = EfiSetVariable ( - L"RTC", - &gEfiCallerIdGuid, - EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, - sizeof (TimerVar), - &TimerVar - ); + if ((Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE) && (Time->Daylight == 0)) { + Status = EfiSetVariable ( + mTimeZoneVariableName, + &gEfiCallerIdGuid, + 0, + 0, + NULL + ); + if (Status == EFI_NOT_FOUND) { + Status = EFI_SUCCESS; + } + } else { + TimerVar = Time->Daylight; + TimerVar = (UINT32)((TimerVar << 16) | (UINT16)(Time->TimeZone)); + Status = EfiSetVariable ( + mTimeZoneVariableName, + &gEfiCallerIdGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, + sizeof (TimerVar), + &TimerVar + ); + } + if (EFI_ERROR (Status)) { if (!EfiAtRuntime ()) { EfiReleaseLock (&Global->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; + RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B); + RegisterB.Bits.Set = 1; RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data); - ConvertEfiTimeToRtcTime (&RtcTime, RegisterB, &Century); + // + // Store the century value to RTC before converting to BCD format. + // + if (Global->CenturyRtcAddress != 0) { + RtcWrite (Global->CenturyRtcAddress, DecimalToBcd8 ((UINT8)(RtcTime.Year / 100))); + } + + ConvertEfiTimeToRtcTime (&RtcTime, RegisterB); 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); - RtcWrite (RTC_ADDRESS_CENTURY, Century); + RtcWrite (RTC_ADDRESS_YEAR, (UINT8)RtcTime.Year); // // Allow updates of the RTC registers @@ -441,6 +662,7 @@ PcRtcSetTime ( if (!EfiAtRuntime ()) { EfiReleaseLock (&Global->RtcLock); } + // // Set the variable that contains the TimeZone and Daylight fields // @@ -477,7 +699,6 @@ PcRtcGetWakeupTime ( EFI_STATUS Status; RTC_REGISTER_B RegisterB; RTC_REGISTER_C RegisterC; - UINT8 Century; EFI_TIME RtcTime; UINTN DataSize; @@ -486,29 +707,32 @@ PcRtcGetWakeupTime ( // if ((Enabled == NULL) || (Pending == NULL) || (Time == NULL)) { return EFI_INVALID_PARAMETER; - } + // // Acquire RTC Lock to make access to RTC atomic // if (!EfiAtRuntime ()) { EfiAcquireLock (&Global->RtcLock); } + // // Wait for up to 0.1 seconds for the RTC to be updated // Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout)); if (EFI_ERROR (Status)) { if (!EfiAtRuntime ()) { - EfiReleaseLock (&Global->RtcLock); + EfiReleaseLock (&Global->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); + RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B); + RegisterC.Data = RtcRead (RTC_ADDRESS_REGISTER_C); // // Get the Time/Date/Daylight Savings values. @@ -516,28 +740,26 @@ PcRtcGetWakeupTime ( *Enabled = RegisterB.Bits.Aie; *Pending = RegisterC.Bits.Af; - 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); + 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); Time->TimeZone = Global->SavedTimeZone; Time->Daylight = Global->Daylight; - Century = RtcRead (RTC_ADDRESS_CENTURY); - // // Get the alarm info from variable // DataSize = sizeof (EFI_TIME); - Status = EfiGetVariable ( - L"RTCALARM", - &gEfiCallerIdGuid, - NULL, - &DataSize, - &RtcTime - ); + Status = EfiGetVariable ( + L"RTCALARM", + &gEfiCallerIdGuid, + NULL, + &DataSize, + &RtcTime + ); if (!EFI_ERROR (Status)) { // // The alarm variable exists. In this case, we read variable to get info. @@ -557,10 +779,11 @@ PcRtcGetWakeupTime ( // // Make sure all field values are in correct range // - Status = ConvertRtcTimeToEfiTime (Time, Century, RegisterB); + Status = ConvertRtcTimeToEfiTime (Time, RegisterB); if (!EFI_ERROR (Status)) { Status = RtcTimeFieldsValid (Time); } + if (EFI_ERROR (Status)) { return EFI_DEVICE_ERROR; } @@ -586,23 +809,22 @@ PcRtcGetWakeupTime ( EFI_STATUS PcRtcSetWakeupTime ( IN BOOLEAN Enable, - IN EFI_TIME *Time, OPTIONAL + IN EFI_TIME *Time OPTIONAL, IN PC_RTC_MODULE_GLOBALS *Global ) { - EFI_STATUS Status; - EFI_TIME RtcTime; - RTC_REGISTER_B RegisterB; - UINT8 Century; - EFI_TIME_CAPABILITIES Capabilities; + EFI_STATUS Status; + EFI_TIME RtcTime; + RTC_REGISTER_B RegisterB; + EFI_TIME_CAPABILITIES Capabilities; ZeroMem (&RtcTime, sizeof (RtcTime)); if (Enable) { - if (Time == NULL) { return EFI_INVALID_PARAMETER; } + // // Make sure that the time fields are valid // @@ -610,6 +832,7 @@ PcRtcSetWakeupTime ( if (EFI_ERROR (Status)) { return EFI_INVALID_PARAMETER; } + // // Just support set alarm time within 24 hours // @@ -618,48 +841,53 @@ PcRtcSetWakeupTime ( if (EFI_ERROR (Status)) { return EFI_DEVICE_ERROR; } + if (!IsWithinOneDay (&RtcTime, Time)) { 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 // if (!EfiAtRuntime ()) { EfiAcquireLock (&Global->RtcLock); } + // // Wait for up to 0.1 seconds for the RTC to be updated // Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout)); if (EFI_ERROR (Status)) { if (!EfiAtRuntime ()) { - EfiReleaseLock (&Global->RtcLock); + EfiReleaseLock (&Global->RtcLock); } + return EFI_DEVICE_ERROR; } + // // Read Register B // RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B); if (Enable) { - ConvertEfiTimeToRtcTime (&RtcTime, RegisterB, &Century); + ConvertEfiTimeToRtcTime (&RtcTime, RegisterB); } else { // // if the alarm is disable, record the current setting. // - RtcTime.Second = RtcRead (RTC_ADDRESS_SECONDS_ALARM); - RtcTime.Minute = RtcRead (RTC_ADDRESS_MINUTES_ALARM); - RtcTime.Hour = RtcRead (RTC_ADDRESS_HOURS_ALARM); - RtcTime.Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH); - RtcTime.Month = RtcRead (RTC_ADDRESS_MONTH); - RtcTime.Year = RtcRead (RTC_ADDRESS_YEAR); + RtcTime.Second = RtcRead (RTC_ADDRESS_SECONDS_ALARM); + RtcTime.Minute = RtcRead (RTC_ADDRESS_MINUTES_ALARM); + RtcTime.Hour = RtcRead (RTC_ADDRESS_HOURS_ALARM); + RtcTime.Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH); + RtcTime.Month = RtcRead (RTC_ADDRESS_MONTH); + RtcTime.Year = RtcRead (RTC_ADDRESS_YEAR); RtcTime.TimeZone = Global->SavedTimeZone; RtcTime.Daylight = Global->Daylight; } @@ -678,13 +906,14 @@ PcRtcSetWakeupTime ( if (!EfiAtRuntime ()) { EfiReleaseLock (&Global->RtcLock); } + return EFI_DEVICE_ERROR; } - + // // Inhibit updates of the RTC // - RegisterB.Bits.Set = 1; + RegisterB.Bits.Set = 1; RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data); if (Enable) { @@ -696,10 +925,10 @@ PcRtcSetWakeupTime ( RtcWrite (RTC_ADDRESS_HOURS_ALARM, RtcTime.Hour); RegisterB.Bits.Aie = 1; - } else { RegisterB.Bits.Aie = 0; } + // // Allow updates of the RTC registers // @@ -712,10 +941,10 @@ PcRtcSetWakeupTime ( if (!EfiAtRuntime ()) { EfiReleaseLock (&Global->RtcLock); } + return EFI_SUCCESS; } - /** Checks an 8-bit BCD value, and converts to an 8-bit value if valid. @@ -750,7 +979,6 @@ CheckAndConvertBcd8ToDecimal8 ( @param Time On input, the time data read from RTC to convert On output, the time converted to UEFI format - @param Century Value of century read from RTC. @param RegisterB Value of Register B of RTC, indicating data mode and hour format. @@ -761,52 +989,64 @@ CheckAndConvertBcd8ToDecimal8 ( EFI_STATUS ConvertRtcTimeToEfiTime ( IN OUT EFI_TIME *Time, - IN UINT8 Century, IN RTC_REGISTER_B RegisterB ) { - BOOLEAN IsPM; + BOOLEAN IsPM; + UINT8 Century; - if ((Time->Hour & 0x80) != 0) { - IsPM = TRUE; - } else { - IsPM = FALSE; - } + // IsPM only makes sense for 12-hour format. + if (RegisterB.Bits.Mil == 0) { + if ((Time->Hour & 0x80) != 0) { + IsPM = TRUE; + } else { + IsPM = FALSE; + } - Time->Hour = (UINT8) (Time->Hour & 0x7f); + Time->Hour = (UINT8)(Time->Hour & 0x7f); + } if (RegisterB.Bits.Dm == 0) { - Time->Year = CheckAndConvertBcd8ToDecimal8 ((UINT8) Time->Year); - Time->Month = CheckAndConvertBcd8ToDecimal8 (Time->Month); - Time->Day = CheckAndConvertBcd8ToDecimal8 (Time->Day); - Time->Hour = CheckAndConvertBcd8ToDecimal8 (Time->Hour); - Time->Minute = CheckAndConvertBcd8ToDecimal8 (Time->Minute); - Time->Second = CheckAndConvertBcd8ToDecimal8 (Time->Second); + Time->Year = CheckAndConvertBcd8ToDecimal8 ((UINT8)Time->Year); + Time->Month = CheckAndConvertBcd8ToDecimal8 (Time->Month); + Time->Day = CheckAndConvertBcd8ToDecimal8 (Time->Day); + Time->Hour = CheckAndConvertBcd8ToDecimal8 (Time->Hour); + Time->Minute = CheckAndConvertBcd8ToDecimal8 (Time->Minute); + Time->Second = CheckAndConvertBcd8ToDecimal8 (Time->Second); } - Century = CheckAndConvertBcd8ToDecimal8 (Century); - if (Time->Year == 0xff || Time->Month == 0xff || Time->Day == 0xff || - Time->Hour == 0xff || Time->Minute == 0xff || Time->Second == 0xff || - Century == 0xff) { + if ((Time->Year == 0xff) || (Time->Month == 0xff) || (Time->Day == 0xff) || + (Time->Hour == 0xff) || (Time->Minute == 0xff) || (Time->Second == 0xff)) + { return EFI_INVALID_PARAMETER; } - Time->Year = (UINT16) (Century * 100 + Time->Year); + // + // For minimal/maximum year range [1970, 2069], + // Century is 19 if RTC year >= 70, + // Century is 20 otherwise. + // + Century = (UINT8)(PcdGet16 (PcdMinimalValidYear) / 100); + if (Time->Year < PcdGet16 (PcdMinimalValidYear) % 100) { + Century++; + } + + Time->Year = (UINT16)(Century * 100 + Time->Year); // // If time is in 12 hour format, convert it to 24 hour format // if (RegisterB.Bits.Mil == 0) { - if (IsPM && Time->Hour < 12) { - Time->Hour = (UINT8) (Time->Hour + 12); + if (IsPM && (Time->Hour < 12)) { + Time->Hour = (UINT8)(Time->Hour + 12); } - if (!IsPM && Time->Hour == 12) { + if (!IsPM && (Time->Hour == 12)) { Time->Hour = 0; } } - Time->Nanosecond = 0; + Time->Nanosecond = 0; return EFI_SUCCESS; } @@ -817,11 +1057,11 @@ ConvertRtcTimeToEfiTime ( @param Timeout Tell how long it should take to wait. @retval EFI_DEVICE_ERROR RTC device error. - @retval EFI_SUCCESS RTC is updated and ready. + @retval EFI_SUCCESS RTC is updated and ready. **/ EFI_STATUS RtcWaitToUpdate ( - UINTN Timeout + UINTN Timeout ) { RTC_REGISTER_A RegisterA; @@ -835,11 +1075,12 @@ RtcWaitToUpdate ( 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); + 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); @@ -847,7 +1088,7 @@ RtcWaitToUpdate ( } RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D); - if (Timeout == 0 || RegisterD.Bits.Vrt == 0) { + if ((Timeout == 0) || (RegisterD.Bits.Vrt == 0)) { return EFI_DEVICE_ERROR; } @@ -865,20 +1106,21 @@ RtcWaitToUpdate ( **/ EFI_STATUS RtcTimeFieldsValid ( - IN EFI_TIME *Time + IN EFI_TIME *Time ) { - if (Time->Year < 1998 || - Time->Year > 2099 || - Time->Month < 1 || - Time->Month > 12 || + if ((Time->Year < PcdGet16 (PcdMinimalValidYear)) || + (Time->Year > PcdGet16 (PcdMaximalValidYear)) || + (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))) != 0)) { + (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))) != 0)) + { return EFI_INVALID_PARAMETER; } @@ -898,30 +1140,16 @@ DayValid ( IN EFI_TIME *Time ) { - INTN DayOfMonth[12]; - - DayOfMonth[0] = 31; - DayOfMonth[1] = 29; - DayOfMonth[2] = 31; - DayOfMonth[3] = 30; - DayOfMonth[4] = 31; - DayOfMonth[5] = 30; - DayOfMonth[6] = 31; - DayOfMonth[7] = 31; - DayOfMonth[8] = 30; - DayOfMonth[9] = 31; - DayOfMonth[10] = 30; - DayOfMonth[11] = 31; - // // The validity of Time->Month field should be checked before // - ASSERT (Time->Month >=1); - ASSERT (Time->Month <=12); - if (Time->Day < 1 || - Time->Day > DayOfMonth[Time->Month - 1] || - (Time->Month == 2 && (!IsLeapYear (Time) && Time->Day > 28)) - ) { + ASSERT (Time->Month >= 1); + ASSERT (Time->Month <= 12); + if ((Time->Day < 1) || + (Time->Day > mDayOfMonth[Time->Month - 1]) || + ((Time->Month == 2) && (!IsLeapYear (Time) && (Time->Day > 28))) + ) + { return FALSE; } @@ -938,7 +1166,7 @@ DayValid ( **/ BOOLEAN IsLeapYear ( - IN EFI_TIME *Time + IN EFI_TIME *Time ) { if (Time->Year % 4 == 0) { @@ -957,26 +1185,23 @@ IsLeapYear ( } /** - Converts time from EFI_TIME format defined by UEFI spec to RTC's. + Converts time from EFI_TIME format defined by UEFI spec to RTC format. - This function converts time from EFI_TIME format defined by UEFI spec to RTC's. + This function converts time from EFI_TIME format defined by UEFI spec to RTC format. If data mode of RTC is BCD, then converts EFI_TIME to it. If RTC is in 12-hour format, then converts EFI_TIME to it. @param Time On input, the time data read from UEFI to convert On output, the time converted to RTC format @param RegisterB Value of Register B of RTC, indicating data mode - @param Century It is set according to EFI_TIME Time. - **/ VOID ConvertEfiTimeToRtcTime ( IN OUT EFI_TIME *Time, - IN RTC_REGISTER_B RegisterB, - OUT UINT8 *Century + IN RTC_REGISTER_B RegisterB ) { - BOOLEAN IsPM; + BOOLEAN IsPM; IsPM = TRUE; // @@ -988,37 +1213,37 @@ ConvertEfiTimeToRtcTime ( } if (Time->Hour >= 13) { - Time->Hour = (UINT8) (Time->Hour - 12); + Time->Hour = (UINT8)(Time->Hour - 12); } else if (Time->Hour == 0) { Time->Hour = 12; } } + // - // Set the Time/Date/Daylight Savings values. + // Set the Time/Date values. // - *Century = DecimalToBcd8 ((UINT8) (Time->Year / 100)); - - Time->Year = (UINT16) (Time->Year % 100); + Time->Year = (UINT16)(Time->Year % 100); if (RegisterB.Bits.Dm == 0) { - Time->Year = DecimalToBcd8 ((UINT8) Time->Year); - Time->Month = DecimalToBcd8 (Time->Month); - Time->Day = DecimalToBcd8 (Time->Day); - Time->Hour = DecimalToBcd8 (Time->Hour); - Time->Minute = DecimalToBcd8 (Time->Minute); - Time->Second = DecimalToBcd8 (Time->Second); + Time->Year = DecimalToBcd8 ((UINT8)Time->Year); + Time->Month = DecimalToBcd8 (Time->Month); + Time->Day = DecimalToBcd8 (Time->Day); + Time->Hour = DecimalToBcd8 (Time->Hour); + Time->Minute = DecimalToBcd8 (Time->Minute); + Time->Second = DecimalToBcd8 (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 && IsPM) { - Time->Hour = (UINT8) (Time->Hour | 0x80); + if ((RegisterB.Bits.Mil == 0) && IsPM) { + Time->Hour = (UINT8)(Time->Hour | 0x80); } } /** Compare the Hour, Minute and Second of the From time and the To time. - + Only compare H/M/S in EFI_TIME and ignore other fields here. @param From the first time @@ -1030,13 +1255,14 @@ ConvertEfiTimeToRtcTime ( **/ INTN CompareHMS ( - IN EFI_TIME *From, - IN EFI_TIME *To + IN EFI_TIME *From, + IN EFI_TIME *To ) { if ((From->Hour > To->Hour) || - ((From->Hour == To->Hour) && (From->Minute > To->Minute)) || - ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second > To->Second))) { + ((From->Hour == To->Hour) && (From->Minute > To->Minute)) || + ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second > To->Second))) + { return 1; } else if ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second == To->Second)) { return 0; @@ -1060,51 +1286,37 @@ IsWithinOneDay ( IN EFI_TIME *To ) { - UINT8 DayOfMonth[12]; - BOOLEAN Adjacent; - - DayOfMonth[0] = 31; - DayOfMonth[1] = 29; - DayOfMonth[2] = 31; - DayOfMonth[3] = 30; - DayOfMonth[4] = 31; - DayOfMonth[5] = 30; - DayOfMonth[6] = 31; - DayOfMonth[7] = 31; - DayOfMonth[8] = 30; - DayOfMonth[9] = 31; - DayOfMonth[10] = 30; - DayOfMonth[11] = 31; + BOOLEAN Adjacent; Adjacent = FALSE; // // The validity of From->Month field should be checked before // - ASSERT (From->Month >=1); - ASSERT (From->Month <=12); - + ASSERT (From->Month >= 1); + ASSERT (From->Month <= 12); + if (From->Year == To->Year) { if (From->Month == To->Month) { if ((From->Day + 1) == To->Day) { - if ((CompareHMS(From, To) >= 0)) { + if ((CompareHMS (From, To) >= 0)) { Adjacent = TRUE; } } else if (From->Day == To->Day) { - if ((CompareHMS(From, To) <= 0)) { + if ((CompareHMS (From, To) <= 0)) { Adjacent = TRUE; } } } else if (((From->Month + 1) == To->Month) && (To->Day == 1)) { - if ((From->Month == 2) && !IsLeapYear(From)) { + if ((From->Month == 2) && !IsLeapYear (From)) { if (From->Day == 28) { - if ((CompareHMS(From, To) >= 0)) { + if ((CompareHMS (From, To) >= 0)) { Adjacent = TRUE; } } - } else if (From->Day == DayOfMonth[From->Month - 1]) { - if ((CompareHMS(From, To) >= 0)) { - Adjacent = TRUE; + } else if (From->Day == mDayOfMonth[From->Month - 1]) { + if ((CompareHMS (From, To) >= 0)) { + Adjacent = TRUE; } } } @@ -1112,8 +1324,9 @@ IsWithinOneDay ( (From->Month == 12) && (From->Day == 31) && (To->Month == 1) && - (To->Day == 1)) { - if ((CompareHMS(From, To) >= 0)) { + (To->Day == 1)) + { + if ((CompareHMS (From, To) >= 0)) { Adjacent = TRUE; } } @@ -1121,3 +1334,63 @@ IsWithinOneDay ( return Adjacent; } +/** + Get the century RTC address from the ACPI FADT table. + + @return The century RTC address or 0 if not found. +**/ +UINT8 +GetCenturyRtcAddress ( + VOID + ) +{ + EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt; + + Fadt = (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *)EfiLocateFirstAcpiTable ( + EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE + ); + + if ((Fadt != NULL) && + (Fadt->Century > RTC_ADDRESS_REGISTER_D) && (Fadt->Century < 0x80) + ) + { + return Fadt->Century; + } else { + return 0; + } +} + +/** + Notification function of ACPI Table change. + + This is a notification function registered on ACPI Table change event. + It saves the Century address stored in ACPI FADT table. + + @param Event Event whose notification function is being invoked. + @param Context Pointer to the notification function's context. + +**/ +VOID +EFIAPI +PcRtcAcpiTableChangeCallback ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + EFI_TIME Time; + UINT8 CenturyRtcAddress; + UINT8 Century; + + CenturyRtcAddress = GetCenturyRtcAddress (); + if ((CenturyRtcAddress != 0) && (mModuleGlobal.CenturyRtcAddress != CenturyRtcAddress)) { + mModuleGlobal.CenturyRtcAddress = CenturyRtcAddress; + Status = PcRtcGetTime (&Time, NULL, &mModuleGlobal); + if (!EFI_ERROR (Status)) { + Century = (UINT8)(Time.Year / 100); + Century = DecimalToBcd8 (Century); + DEBUG ((DEBUG_INFO, "PcRtc: Write 0x%x to CMOS location 0x%x\n", Century, mModuleGlobal.CenturyRtcAddress)); + RtcWrite (mModuleGlobal.CenturyRtcAddress, Century); + } + } +}