Added the header comment block to the function "RtcTimeFieldsValid()" and reworked the alignment of the code. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Ronald Cron <ronald.cron@arm.com> Reviewed-by: Olivier Martin <olivier.martin@arm.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@16011 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			863 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			863 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @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;
 | |
| }
 | |
| 
 | |
| 
 |