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;
 | 
						|
}
 | 
						|
 | 
						|
 |