/** @file Copyright (c) 2020, Hisilicon Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "RX8900RealTimeClock.h" extern I2C_DEVICE gRtcDevice; STATIC BOOLEAN mRX8900Initialized = FALSE; STATIC CONST CHAR16 mTimeZoneVariableName[] = L"RX8900RtcTimeZone"; STATIC CONST CHAR16 mDaylightVariableName[] = L"RX8900RtcDaylight"; EFI_STATUS InitializeRX8900 ( VOID ) { EFI_STATUS Status; unsigned char writeTemp; Status = SwitchRtcI2cChannelAndLock (); if (EFI_ERROR (Status)) { goto EXIT; } Status = I2CInit (gRtcDevice.Socket, gRtcDevice.Port, Normal); if (EFI_ERROR (Status)) { goto EXIT; } writeTemp = RX8900_VDETOFF_SWOFF; Status = I2CWrite (&gRtcDevice, RX8900_REGADDR_BACKUP_FUN, 1, &writeTemp); if (EFI_ERROR (Status)) { goto EXIT; } writeTemp = TEMPERATURE_COMPENSATION_2S; Status = I2CWrite (&gRtcDevice, RX8900_REGADDR_CONTRLREG, 1, &writeTemp); if (EFI_ERROR (Status)) { goto EXIT; } writeTemp = OUTPUT_FREQUENCY_32768; Status = I2CWrite (&gRtcDevice, RX8900_REGADDR_EXTENSIONREG, 1, &writeTemp); if (EFI_ERROR (Status)) { goto EXIT; } writeTemp = FLAG_REG_DEFAULT; Status = I2CWrite (&gRtcDevice, RX8900_REGADDR_FLAGREG, 1, &writeTemp); if (EFI_ERROR (Status)) { goto EXIT; } writeTemp = RX8900_RAM_REG_DEFAULT; Status = I2CWrite (&gRtcDevice, RX8900_REGADDR_RAM, 1, &writeTemp); if (EFI_ERROR (Status)) { goto EXIT; } mRX8900Initialized = TRUE; EXIT: ReleaseOwnershipOfRtc (); return Status; } STATIC INT16 GetTimeZone ( VOID ) { EFI_STATUS Status; INT16 TimeZone; UINTN Size; TimeZone = EFI_UNSPECIFIED_TIMEZONE; Size = sizeof (TimeZone); Status = EfiGetVariable ( (CHAR16 *)mTimeZoneVariableName, &gEfiCallerIdGuid, NULL, &Size, (VOID *)&TimeZone ); if (EFI_ERROR (Status)) { TimeZone = EFI_UNSPECIFIED_TIMEZONE; // The time zone variable does not exist in non-volatile storage, so create it. Status = EfiSetVariable ( (CHAR16 *)mTimeZoneVariableName, &gEfiCallerIdGuid, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, Size, (VOID *)&TimeZone ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_INFO, "Failed to save %s variable, Status = %r\n", mTimeZoneVariableName, Status)); } } else { // Check TimeZone bounds: -1440 to 1440 or 2047 if ((TimeZone < -EFI_TIMEOFFSET_TIMEZONE) || (TimeZone > EFI_TIMEOFFSET_TIMEZONE)) { TimeZone = EFI_UNSPECIFIED_TIMEZONE; } } return TimeZone; } STATIC UINT8 GetDayLight ( VOID ) { EFI_STATUS Status; UINT8 DayLight; UINTN Size; DayLight = 0; // Get the current daylight information from non-volatile storage Size = sizeof (DayLight); Status = EfiGetVariable ( (CHAR16 *)mDaylightVariableName, &gEfiCallerIdGuid, NULL, &Size, (VOID *)&DayLight ); if (EFI_ERROR (Status)) { DayLight = 0; // The daylight variable does not exist in non-volatile storage, so create it. Status = EfiSetVariable ( (CHAR16 *)mDaylightVariableName, &gEfiCallerIdGuid, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, Size, (VOID *)&DayLight ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_INFO, "Failed to save %s variable, Status = %r\n", mDaylightVariableName, Status)); } } return DayLight; } EFI_STATUS EFIAPI LibGetTime ( OUT EFI_TIME *Time, OUT EFI_TIME_CAPABILITIES *Capabilities ) { EFI_STATUS Status; UINT8 Temp[7] = {0}; UINT16 BaseYear = 2000; UINTN EpochSeconds; UINT8 TryCount = 0; // Ensure Time is a valid pointer if (Time == NULL) { return EFI_INVALID_PARAMETER; } // Initialize the hardware if not already done if (!mRX8900Initialized) { Status = InitializeRX8900 (); if (EFI_ERROR (Status)) { return EFI_NOT_READY; } } Status = SwitchRtcI2cChannelAndLock (); if (EFI_ERROR (Status)) { ReleaseOwnershipOfRtc (); return Status; } do { Status = I2CRead (&gRtcDevice, RX8900_REGADDR_SECONDS, 7, Temp); if (EFI_ERROR (Status)) { Status = EFI_DEVICE_ERROR; goto Err; } Time->Second = BcdToDecimal8 (Temp[0]); Time->Minute = BcdToDecimal8 (Temp[1]); Time->Hour = BcdToDecimal8 (Temp[2]); Time->Day = BcdToDecimal8 (Temp[4]); Time->Month = BcdToDecimal8 (Temp[5]); Time->Year = BaseYear + BcdToDecimal8 (Temp[6]); Time->Nanosecond = 0; EpochSeconds = EfiTimeToEpoch (Time); Time->TimeZone = GetTimeZone (); // Adjust for the correct time zone if (Time->TimeZone != EFI_UNSPECIFIED_TIMEZONE) { EpochSeconds += Time->TimeZone * SEC_PER_MIN; } Time->Daylight = GetDayLight (); // Adjust for the correct period if ((Time->Daylight & EFI_TIME_IN_DAYLIGHT) == EFI_TIME_IN_DAYLIGHT) { // Convert to adjusted time, i.e. spring forwards one hour EpochSeconds += SEC_PER_HOUR; } // Convert from internal 32-bit time to UEFI time EpochToEfiTime (EpochSeconds, Time); if ((!IsTimeValid (Time)) || ((Time->Year - BaseYear) > 99) || (Time->Year < 2000)) { DEBUG ((DEBUG_INFO, "LibGetTime: %d-%d-%d %d-%d-%d EpochSeconds:%llx is invalid time!\n", Time->Second, Time->Minute, Time->Hour, Time->Day, Time->Month, Time->Year, EpochSeconds)); Status = EFI_DEVICE_ERROR; } Err: TryCount++; } while ((TryCount < 3) && (EFI_ERROR (Status))); ReleaseOwnershipOfRtc (); return Status; } STATIC EFI_STATUS SetTimeToRX8900 ( IN EFI_TIME *Time ) { EFI_STATUS Status; UINT8 Temp; (VOID)MicroSecondDelay (RTC_DELAY_1000_MICROSECOND); Temp = DecimalToBcd8 (Time->Second); Status = I2CWrite (&gRtcDevice, RX8900_REGADDR_SECONDS, 1, &Temp); if(EFI_ERROR (Status)) { Status = EFI_DEVICE_ERROR; return Status; } Temp = DecimalToBcd8 (Time->Minute); (VOID)MicroSecondDelay (RTC_DELAY_1000_MICROSECOND); Status = I2CWrite (&gRtcDevice, RX8900_REGADDR_MIUTES, 1, &Temp); if(EFI_ERROR (Status)) { Status = EFI_DEVICE_ERROR; return Status; } Temp = DecimalToBcd8 (Time->Hour); (VOID)MicroSecondDelay (RTC_DELAY_1000_MICROSECOND); Status = I2CWrite (&gRtcDevice, RX8900_REGADDR_HOURS, 1, &Temp); if(EFI_ERROR (Status)) { Status = EFI_DEVICE_ERROR; return Status; } Temp = DecimalToBcd8 (Time->Day); (VOID)MicroSecondDelay (RTC_DELAY_1000_MICROSECOND); Status = I2CWrite (&gRtcDevice, RX8900_REGADDR_DATE, 1, &Temp); if(EFI_ERROR (Status)) { Status = EFI_DEVICE_ERROR; return Status; } Temp = DecimalToBcd8 (Time->Month); (VOID)MicroSecondDelay (RTC_DELAY_1000_MICROSECOND); Status = I2CWrite (&gRtcDevice, RX8900_REGADDR_MONTH, 1, &Temp); if(EFI_ERROR (Status)) { Status = EFI_DEVICE_ERROR; return Status; } Time->Year= Time->Year % 100; Temp = Time->Year; Temp = DecimalToBcd8 (Temp); (VOID)MicroSecondDelay (RTC_DELAY_1000_MICROSECOND); Status = I2CWrite (&gRtcDevice, RX8900_REGADDR_YEAR, 1, &Temp); if(EFI_ERROR (Status)) { Status = EFI_DEVICE_ERROR; return Status; } return Status; } EFI_STATUS EFIAPI LibSetTime ( IN EFI_TIME *Time ) { EFI_STATUS Status; UINTN EpochSeconds; // Initialize the hardware if not already done if (!mRX8900Initialized) { Status = InitializeRX8900 (); if (EFI_ERROR (Status)) { goto EXIT; } } Status = SwitchRtcI2cChannelAndLock (); if (EFI_ERROR (Status)) { goto EXIT; } if(!IsTimeValid(Time)){ return EFI_INVALID_PARAMETER; } EpochSeconds = EfiTimeToEpoch (Time); // Adjust for the correct time zone, i.e. convert to UTC time zone if (Time->TimeZone != EFI_UNSPECIFIED_TIMEZONE) { EpochSeconds -= Time->TimeZone * SEC_PER_MIN; } // Adjust for the correct period if ((Time->Daylight & EFI_TIME_IN_DAYLIGHT) == EFI_TIME_IN_DAYLIGHT) { // Convert to un-adjusted time, i.e. fall back one hour EpochSeconds -= SEC_PER_HOUR; } EpochToEfiTime (EpochSeconds, Time); Status = SetTimeToRX8900 (Time); if (EFI_ERROR (Status)) { goto EXIT; } // Save the current time zone information into non-volatile storage Status = EfiSetVariable ( (CHAR16 *)mTimeZoneVariableName, &gEfiCallerIdGuid, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, sizeof (Time->TimeZone), (VOID *)&(Time->TimeZone) ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_INFO, "LibSetTime: Failed to save %s variable, Status = %r\n", mTimeZoneVariableName, Status)); goto EXIT; } // Save the current daylight information into non-volatile storage Status = EfiSetVariable ( (CHAR16 *)mDaylightVariableName, &gEfiCallerIdGuid, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, sizeof(Time->Daylight), (VOID *)&(Time->Daylight) ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_INFO, "LibSetTime: Failed to save %s variable, Status = %r\n", mDaylightVariableName, Status)); goto EXIT; } EXIT: ReleaseOwnershipOfRtc (); return Status; } EFI_STATUS EFIAPI LibGetWakeupTime ( OUT BOOLEAN *Enabled, OUT BOOLEAN *Pending, OUT EFI_TIME *Time ) { // Not a required feature return EFI_UNSUPPORTED; } EFI_STATUS EFIAPI LibSetWakeupTime ( IN BOOLEAN Enabled, OUT EFI_TIME *Time ) { // Not a required feature return EFI_UNSUPPORTED; } EFI_STATUS EFIAPI LibRtcInitialize ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; EFI_HANDLE Handle; EFI_TIME EfiTime; // Setup the setters and getters gRT->GetTime = LibGetTime; gRT->SetTime = LibSetTime; gRT->GetWakeupTime = LibGetWakeupTime; gRT->SetWakeupTime = LibSetWakeupTime; Status = gRT->GetTime (&EfiTime, NULL); if (EFI_ERROR (Status) || (EfiTime.Year < 2000) || (EfiTime.Year > 2099) || (!IsTimeValid (&EfiTime))) { EfiTime.Year = 2000; EfiTime.Month = 1; EfiTime.Day = 1; EfiTime.Hour = 0; EfiTime.Minute = 0; EfiTime.Second = 0; EfiTime.Nanosecond = 0; EfiTime.Daylight = 0; EfiTime.TimeZone = EFI_UNSPECIFIED_TIMEZONE; Status = gRT->SetTime (&EfiTime); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "SetTime Status : %r\n", Status)); } } Handle = NULL; Status = gBS->InstallMultipleProtocolInterfaces ( &Handle, &gEfiRealTimeClockArchProtocolGuid, NULL, NULL ); return Status; } VOID EFIAPI LibRtcVirtualNotifyEvent ( IN EFI_EVENT Event, IN VOID *Context ) { return; }