/** @file
Copyright (c) 2004 - 2014, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials are licensed and made available under
the terms and conditions of the BSD License that 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.
Module Name:
Platform.c
Abstract:
This is a generic template for a child of the IchSmm driver.
--*/
#include "SmmPlatform.h"
#include <Protocol/CpuIo2.h>
//
// Local variables
//
typedef struct {
UINT8 Device;
UINT8 Function;
} EFI_PCI_BUS_MASTER;
EFI_PCI_BUS_MASTER mPciBm[] = {
{ PCI_DEVICE_NUMBER_PCH_PCIE_ROOT_PORTS, PCI_FUNCTION_NUMBER_PCH_PCIE_ROOT_PORT_1 },
{ PCI_DEVICE_NUMBER_PCH_PCIE_ROOT_PORTS, PCI_FUNCTION_NUMBER_PCH_PCIE_ROOT_PORT_2 },
{ PCI_DEVICE_NUMBER_PCH_PCIE_ROOT_PORTS, PCI_FUNCTION_NUMBER_PCH_PCIE_ROOT_PORT_3 },
{ PCI_DEVICE_NUMBER_PCH_PCIE_ROOT_PORTS, PCI_FUNCTION_NUMBER_PCH_PCIE_ROOT_PORT_4 },
{ PCI_DEVICE_NUMBER_PCH_USB, PCI_FUNCTION_NUMBER_PCH_EHCI }
};
UINT16 mAcpiBaseAddr;
SYSTEM_CONFIGURATION mSystemConfiguration;
EFI_SMM_VARIABLE_PROTOCOL *mSmmVariable;
EFI_GLOBAL_NVS_AREA_PROTOCOL *mGlobalNvsAreaPtr;
UINT16 mPM1_SaveState16;
UINT32 mGPE_SaveState32;
BOOLEAN mSetSmmVariableProtocolSmiAllowed = TRUE;
//
// Variables. Need to initialize this from Setup
//
BOOLEAN mWakeOnLanS5Variable;
BOOLEAN mWakeOnRtcVariable;
UINT8 mWakeupDay;
UINT8 mWakeupHour;
UINT8 mWakeupMinute;
UINT8 mWakeupSecond;
//
// Use an enum. 0 is Stay Off, 1 is Last State, 2 is Stay On
//
UINT8 mAcLossVariable;
static
UINT8 mTco1Sources[] = {
IchnNmi
};
UINTN
DevicePathSize (
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
);
VOID
S4S5ProgClock();
EFI_STATUS
InitRuntimeScriptTable (
IN EFI_SYSTEM_TABLE *SystemTable
);
VOID
S5SleepWakeOnRtcCallBack (
IN EFI_HANDLE DispatchHandle,
IN EFI_SMM_SX_DISPATCH_CONTEXT *DispatchContext
);
VOID
EnableS5WakeOnRtc();
UINT8
HexToBcd(
UINT8 HexValue
);
UINT8
BcdToHex(
IN UINT8 BcdValue
);
VOID
CpuSmmSxWorkAround(
);
/**
Initializes the SMM Handler Driver
@param ImageHandle
@param SystemTable
@retval None
**/
EFI_STATUS
EFIAPI
InitializePlatformSmm (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
UINT8 Index;
EFI_HANDLE Handle;
EFI_SMM_POWER_BUTTON_DISPATCH_CONTEXT PowerButtonContext;
EFI_SMM_POWER_BUTTON_DISPATCH_PROTOCOL *PowerButtonDispatch;
EFI_SMM_ICHN_DISPATCH_CONTEXT IchnContext;
EFI_SMM_ICHN_DISPATCH_PROTOCOL *IchnDispatch;
EFI_SMM_SX_DISPATCH_PROTOCOL *SxDispatch;
EFI_SMM_SX_DISPATCH_CONTEXT EntryDispatchContext;
EFI_SMM_SW_DISPATCH_PROTOCOL *SwDispatch;
EFI_SMM_SW_DISPATCH_CONTEXT SwContext;
UINTN VarSize;
EFI_BOOT_MODE BootMode;
Handle = NULL;
//
// Locate the Global NVS Protocol.
//
Status = gBS->LocateProtocol (
&gEfiGlobalNvsAreaProtocolGuid,
NULL,
(void **)&mGlobalNvsAreaPtr
);
ASSERT_EFI_ERROR (Status);
//
// Get the ACPI Base Address
//
mAcpiBaseAddr = PchLpcPciCfg16( R_PCH_LPC_ACPI_BASE ) & B_PCH_LPC_ACPI_BASE_BAR;
VarSize = sizeof(SYSTEM_CONFIGURATION);
Status = SystemTable->RuntimeServices->GetVariable(
L"Setup",
&gEfiSetupVariableGuid,
NULL,
&VarSize,
&mSystemConfiguration
);
if (EFI_ERROR (Status) || VarSize != sizeof(SYSTEM_CONFIGURATION)) {
//The setup variable is corrupted
VarSize = sizeof(SYSTEM_CONFIGURATION);
Status = SystemTable->RuntimeServices->GetVariable(
L"SetupRecovery",
&gEfiSetupVariableGuid,
NULL,
&VarSize,
&mSystemConfiguration
);
ASSERT_EFI_ERROR (Status);
}
if (!EFI_ERROR(Status)) {
mAcLossVariable = mSystemConfiguration.StateAfterG3;
//
// If LAN is disabled, WOL function should be disabled too.
//
if (mSystemConfiguration.Lan == 0x01){
mWakeOnLanS5Variable = mSystemConfiguration.WakeOnLanS5;
} else {
mWakeOnLanS5Variable = FALSE;
}
mWakeOnRtcVariable = mSystemConfiguration.WakeOnRtcS5;
}
BootMode = GetBootModeHob ();
//
// Get the Power Button protocol
//
Status = gBS->LocateProtocol(
&gEfiSmmPowerButtonDispatchProtocolGuid,
NULL,
(void **)&PowerButtonDispatch
);
ASSERT_EFI_ERROR(Status);
if (BootMode != BOOT_ON_FLASH_UPDATE) {
//
// Register for the power button event
//
PowerButtonContext.Phase = PowerButtonEntry;
Status = PowerButtonDispatch->Register(
PowerButtonDispatch,
PowerButtonCallback,
&PowerButtonContext,
&Handle
);
ASSERT_EFI_ERROR(Status);
}
//
// Get the Sx dispatch protocol
//
Status = gBS->LocateProtocol (
&gEfiSmmSxDispatchProtocolGuid,
NULL,
(void **)&SxDispatch
);
ASSERT_EFI_ERROR(Status);
//
// Register entry phase call back function
//
EntryDispatchContext.Type = SxS3;
EntryDispatchContext.Phase = SxEntry;
Status = SxDispatch->Register (
SxDispatch,
(EFI_SMM_SX_DISPATCH)SxSleepEntryCallBack,
&EntryDispatchContext,
&Handle
);
EntryDispatchContext.Type = SxS4;
Status = SxDispatch->Register (
SxDispatch,
S4S5CallBack,
&EntryDispatchContext,
&Handle
);
ASSERT_EFI_ERROR(Status);
EntryDispatchContext.Type = SxS5;
Status = SxDispatch->Register (
SxDispatch,
S4S5CallBack,
&EntryDispatchContext,
&Handle
);
ASSERT_EFI_ERROR(Status);
Status = SxDispatch->Register (
SxDispatch,
S5SleepAcLossCallBack,
&EntryDispatchContext,
&Handle
);
ASSERT_EFI_ERROR(Status);
//
// Get the Sw dispatch protocol
//
Status = gBS->LocateProtocol (
&gEfiSmmSwDispatchProtocolGuid,
NULL,
(void **)&SwDispatch
);
ASSERT_EFI_ERROR(Status);
//
// Register ACPI enable handler
//
SwContext.SwSmiInputValue = ACPI_ENABLE;
Status = SwDispatch->Register (
SwDispatch,
EnableAcpiCallback,
&SwContext,
&Handle
);
ASSERT_EFI_ERROR(Status);
//
// Register ACPI disable handler
//
SwContext.SwSmiInputValue = ACPI_DISABLE;
Status = SwDispatch->Register (
SwDispatch,
DisableAcpiCallback,
&SwContext,
&Handle
);
ASSERT_EFI_ERROR(Status);
//
// Register for SmmReadyToBootCallback
//
SwContext.SwSmiInputValue = SMI_SET_SMMVARIABLE_PROTOCOL;
Status = SwDispatch->Register(
SwDispatch,
SmmReadyToBootCallback,
&SwContext,
&Handle
);
ASSERT_EFI_ERROR(Status);
//
// Get the ICHn protocol
//
Status = gBS->LocateProtocol(
&gEfiSmmIchnDispatchProtocolGuid,
NULL,
(void **)&IchnDispatch
);
ASSERT_EFI_ERROR(Status);
//
// Register for the events that may happen that we do not care.
// This is true for SMI related to TCO since TCO is enabled by BIOS WP
//
for (Index = 0; Index < sizeof(mTco1Sources)/sizeof(UINT8); Index++) {
IchnContext.Type = mTco1Sources[Index];
Status = IchnDispatch->Register(
IchnDispatch,
(EFI_SMM_ICHN_DISPATCH)DummyTco1Callback,
&IchnContext,
&Handle
);
ASSERT_EFI_ERROR( Status );
}
//
// Lock TCO_EN bit.
//
IoWrite16( mAcpiBaseAddr + R_PCH_TCO_CNT, IoRead16( mAcpiBaseAddr + R_PCH_TCO_CNT ) | B_PCH_TCO_CNT_LOCK );
//
// Set to power on from G3 dependent on WOL instead of AC Loss variable in order to support WOL from G3 feature.
//
//
// Set wake from G3 dependent on AC Loss variable and Wake On LAN variable.
// This is because no matter how, if WOL enabled or AC Loss variable not disabled, the board needs to wake from G3 to program the LAN WOL settings.
// This needs to be done after LAN enable/disable so that the PWR_FLR state clear not impacted the WOL from G3 feature.
//
if (mAcLossVariable != 0x00) {
SetAfterG3On (TRUE);
} else {
SetAfterG3On (FALSE);
}
return EFI_SUCCESS;
}
VOID
EFIAPI
SmmReadyToBootCallback (
IN EFI_HANDLE DispatchHandle,
IN EFI_SMM_SW_DISPATCH_CONTEXT *DispatchContext
)
{
EFI_STATUS Status;
if (mSetSmmVariableProtocolSmiAllowed)
{
//
// It is okay to use gBS->LocateProtocol here because
// we are still in trusted execution.
//
Status = gBS->LocateProtocol(
&gEfiSmmVariableProtocolGuid,
NULL,
(void **)&mSmmVariable
);
ASSERT_EFI_ERROR(Status);
//
// mSetSmmVariableProtocolSmiAllowed will prevent this function from
// being executed more than 1 time.
//
mSetSmmVariableProtocolSmiAllowed = FALSE;
}
}
/**
@param DispatchHandle The handle of this callback, obtained when registering
@param DispatchContext The predefined context which contained sleep type and phase
@retval EFI_SUCCESS Operation successfully performed
**/
EFI_STATUS
EFIAPI
SxSleepEntryCallBack (
IN EFI_HANDLE DispatchHandle,
IN EFI_SMM_SX_DISPATCH_CONTEXT *DispatchContext
)
{
EFI_STATUS Status;
Status = SaveRuntimeScriptTable ();
if (EFI_ERROR(Status)) {
return Status;
}
//
// Workaround for S3 wake hang if C State is enabled
//
CpuSmmSxWorkAround();
return EFI_SUCCESS;
}
VOID
CpuSmmSxWorkAround(
)
{
UINT64 MsrValue;
MsrValue = AsmReadMsr64 (0xE2);
if (MsrValue & BIT15) {
return;
}
if (MsrValue & BIT10) {
MsrValue &= ~BIT10;
AsmWriteMsr64 (0xE2, MsrValue);
}
}
VOID
ClearP2PBusMaster(
)
{
UINT8 Command;
UINT8 Index;
for (Index = 0; Index < sizeof(mPciBm)/sizeof(EFI_PCI_BUS_MASTER); Index++) {
Command = MmioRead8 (
MmPciAddress (0,
DEFAULT_PCI_BUS_NUMBER_PCH,
mPciBm[Index].Device,
mPciBm[Index].Function,
PCI_COMMAND_OFFSET
)
);
Command &= ~EFI_PCI_COMMAND_BUS_MASTER;
MmioWrite8 (
MmPciAddress (0,
DEFAULT_PCI_BUS_NUMBER_PCH,
mPciBm[Index].Device,
mPciBm[Index].Function,
PCI_COMMAND_OFFSET
),
Command
);
}
}
/**
Set the AC Loss to turn on or off.
**/
VOID
SetAfterG3On (
BOOLEAN Enable
)
{
UINT8 PmCon1;
//
// ICH handling portion
//
PmCon1 = MmioRead8 ( PMC_BASE_ADDRESS + R_PCH_PMC_GEN_PMCON_1 );
PmCon1 &= ~B_PCH_PMC_GEN_PMCON_AFTERG3_EN;
if (Enable) {
PmCon1 |= B_PCH_PMC_GEN_PMCON_AFTERG3_EN;
}
MmioWrite8 (PMC_BASE_ADDRESS + R_PCH_PMC_GEN_PMCON_1, PmCon1);
}
/**
When a power button event happens, it shuts off the machine
**/
VOID
EFIAPI
PowerButtonCallback (
IN EFI_HANDLE DispatchHandle,
IN EFI_SMM_POWER_BUTTON_DISPATCH_CONTEXT *DispatchContext
)
{
//
// Check what the state to return to after AC Loss. If Last State, then
// set it to Off.
//
UINT16 data16;
if (mWakeOnRtcVariable) {
EnableS5WakeOnRtc();
}
if (mAcLossVariable == 1) {
SetAfterG3On (TRUE);
}
ClearP2PBusMaster();
//
// Program clock chip
//
S4S5ProgClock();
data16 = (UINT16)(IoRead16(mAcpiBaseAddr + R_PCH_ACPI_GPE0a_EN));
data16 &= B_PCH_ACPI_GPE0a_EN_PCI_EXP;
//
// Clear Sleep SMI Status
//
IoWrite16 (mAcpiBaseAddr + R_PCH_SMI_STS,
(UINT16)(IoRead16 (mAcpiBaseAddr + R_PCH_SMI_STS) | B_PCH_SMI_STS_ON_SLP_EN));
//
// Clear Sleep Type Enable
//
IoWrite16 (mAcpiBaseAddr + R_PCH_SMI_EN,
(UINT16)(IoRead16 (mAcpiBaseAddr + R_PCH_SMI_EN) & (~B_PCH_SMI_EN_ON_SLP_EN)));
//
// Clear Power Button Status
//
IoWrite16(mAcpiBaseAddr + R_PCH_ACPI_PM1_STS, B_PCH_ACPI_PM1_STS_PWRBTN);
//
// Shut it off now!
//
IoWrite16(mAcpiBaseAddr + R_PCH_ACPI_PM1_CNT, V_PCH_ACPI_PM1_CNT_S5);
IoWrite16(mAcpiBaseAddr + R_PCH_ACPI_PM1_CNT, B_PCH_ACPI_PM1_CNT_SLP_EN | V_PCH_ACPI_PM1_CNT_S5);
//
// Should not return
//
CpuDeadLoop();
}
/**
@param DispatchHandle - The handle of this callback, obtained when registering
@param DispatchContext - The predefined context which contained sleep type and phase
**/
VOID
EFIAPI
S5SleepAcLossCallBack (
IN EFI_HANDLE DispatchHandle,
IN EFI_SMM_SX_DISPATCH_CONTEXT *DispatchContext
)
{
//
// Check what the state to return to after AC Loss. If Last State, then
// set it to Off.
//
if (mAcLossVariable == 1) {
SetAfterG3On (TRUE);
}
}
/**
@param DispatchHandle The handle of this callback, obtained when registering
@param DispatchContext The predefined context which contained sleep type and phase
@retval Clears the Save State bit in the clock.
**/
VOID
EFIAPI
S4S5CallBack (
IN EFI_HANDLE DispatchHandle,
IN EFI_SMM_SX_DISPATCH_CONTEXT *DispatchContext
)
{
UINT32 Data32;
//
// Enable/Disable USB Charging
//
if (mSystemConfiguration.UsbCharging == 0x01) {
Data32 = IoRead32 (GPIO_BASE_ADDRESS + R_PCH_GPIO_SC_LVL);
Data32 |= BIT8;
IoWrite32(GPIO_BASE_ADDRESS + R_PCH_GPIO_SC_LVL, Data32);
}
}
VOID
S4S5ProgClock()
{
}
/**
SMI handler to enable ACPI mode
Dispatched on reads from APM port with value 0xA0
Disables the SW SMI Timer.
ACPI events are disabled and ACPI event status is cleared.
SCI mode is then enabled.
Disable SW SMI Timer
Clear all ACPI event status and disable all ACPI events
Disable PM sources except power button
Clear status bits
Disable GPE0 sources
Clear status bits
Disable GPE1 sources
Clear status bits
Guarantee day-of-month alarm is invalid (ACPI 5.0 Section 4.8.2.4 "Real Time Clock Alarm")
Enable SCI
@param DispatchHandle - EFI Handle
@param DispatchContext - Pointer to the EFI_SMM_SW_DISPATCH_CONTEXT
@retval Nothing
**/
VOID
EFIAPI
EnableAcpiCallback (
IN EFI_HANDLE DispatchHandle,
IN EFI_SMM_SW_DISPATCH_CONTEXT *DispatchContext
)
{
UINT32 SmiEn;
UINT16 Pm1Cnt;
UINT16 wordValue;
UINT32 RegData32;
//
// Disable SW SMI Timer
//
SmiEn = IoRead32(mAcpiBaseAddr + R_PCH_SMI_EN);
SmiEn &= ~B_PCH_SMI_STS_SWSMI_TMR;
IoWrite32(mAcpiBaseAddr + R_PCH_SMI_EN, SmiEn);
wordValue = IoRead16(mAcpiBaseAddr + R_PCH_ACPI_PM1_STS);
if(wordValue & B_PCH_ACPI_PM1_STS_WAK) {
IoWrite32((mAcpiBaseAddr + R_PCH_ACPI_GPE0a_EN), 0x0000);
IoWrite32((mAcpiBaseAddr + R_PCH_ACPI_GPE0a_STS), 0xffffffff);
}
else {
mPM1_SaveState16 = IoRead16(mAcpiBaseAddr + R_PCH_ACPI_PM1_EN);
//
// Disable PM sources except power button
//
// power button is enabled only for PCAT. Disabled it on Tablet platform
//
IoWrite16(mAcpiBaseAddr + R_PCH_ACPI_PM1_EN, B_PCH_ACPI_PM1_EN_PWRBTN);
IoWrite16(mAcpiBaseAddr + R_PCH_ACPI_PM1_STS, 0xffff);
mGPE_SaveState32 = IoRead16(mAcpiBaseAddr + R_PCH_ACPI_GPE0a_EN);
IoWrite32(mAcpiBaseAddr + R_PCH_ACPI_GPE0a_EN, 0x0000);
IoWrite32(mAcpiBaseAddr + R_PCH_ACPI_GPE0a_STS, 0xffffffff);
}
//
// Guarantee day-of-month alarm is invalid (ACPI 5.0 Section 4.8.2.4 "Real Time Clock Alarm")
// Clear Status D reg VM bit, Date of month Alarm to make Data in CMOS RAM is no longer Valid
//
IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_REGISTER_D);
IoWrite8 (PCAT_RTC_DATA_REGISTER, 0x0);
RegData32 = IoRead32(ACPI_BASE_ADDRESS + R_PCH_ALT_GP_SMI_EN);
RegData32 &= ~(BIT7);
IoWrite32((ACPI_BASE_ADDRESS + R_PCH_ALT_GP_SMI_EN), RegData32);
//
// Enable SCI
//
Pm1Cnt = IoRead16(mAcpiBaseAddr + R_PCH_ACPI_PM1_CNT);
Pm1Cnt |= B_PCH_ACPI_PM1_CNT_SCI_EN;
IoWrite16(mAcpiBaseAddr + R_PCH_ACPI_PM1_CNT, Pm1Cnt);
}
/**
SMI handler to disable ACPI mode
Dispatched on reads from APM port with value 0xA1
ACPI events are disabled and ACPI event status is cleared.
SCI mode is then disabled.
Clear all ACPI event status and disable all ACPI events
Disable PM sources except power button
Clear status bits
Disable GPE0 sources
Clear status bits
Disable GPE1 sources
Clear status bits
Disable SCI
@param DispatchHandle - EFI Handle
@param DispatchContext - Pointer to the EFI_SMM_SW_DISPATCH_CONTEXT
@retval Nothing
**/
VOID
EFIAPI
DisableAcpiCallback (
IN EFI_HANDLE DispatchHandle,
IN EFI_SMM_SW_DISPATCH_CONTEXT *DispatchContext
)
{
UINT16 Pm1Cnt;
IoWrite16(mAcpiBaseAddr + R_PCH_ACPI_PM1_STS, 0xffff);
IoWrite16(mAcpiBaseAddr + R_PCH_ACPI_PM1_EN, mPM1_SaveState16);
IoWrite32(mAcpiBaseAddr + R_PCH_ACPI_GPE0a_STS, 0xffffffff);
IoWrite32(mAcpiBaseAddr + R_PCH_ACPI_GPE0a_EN, mGPE_SaveState32);
//
// Disable SCI
//
Pm1Cnt = IoRead16(mAcpiBaseAddr + R_PCH_ACPI_PM1_CNT);
Pm1Cnt &= ~B_PCH_ACPI_PM1_CNT_SCI_EN;
IoWrite16(mAcpiBaseAddr + R_PCH_ACPI_PM1_CNT, Pm1Cnt);
}
/**
When an unknown event happen.
@retval None
**/
VOID
DummyTco1Callback (
IN EFI_HANDLE DispatchHandle,
IN EFI_SMM_ICHN_DISPATCH_CONTEXT *DispatchContext
)
{
}
UINTN
DevicePathSize (
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
)
{
EFI_DEVICE_PATH_PROTOCOL *Start;
if (DevicePath == NULL) {
return 0;
}
//
// Search for the end of the device path structure
//
Start = DevicePath;
while (!IsDevicePathEnd (DevicePath)) {
DevicePath = NextDevicePathNode (DevicePath);
}
//
// Compute the size and add back in the size of the end device path structure
//
return ((UINTN)DevicePath - (UINTN)Start) + sizeof(EFI_DEVICE_PATH_PROTOCOL);
}
/**
@param DispatchHandle The handle of this callback, obtained when registering
@param DispatchContext The predefined context which contained sleep type and phase
**/
VOID
S5SleepWakeOnRtcCallBack (
IN EFI_HANDLE DispatchHandle,
IN EFI_SMM_SX_DISPATCH_CONTEXT *DispatchContext
)
{
EnableS5WakeOnRtc();
}
/**
@retval 1. Check Alarm interrupt is not set.
2. Clear Alarm interrupt.
2. Set RTC wake up date and time.
2. Enable RTC wake up alarm.
3. Enable ICH PM1 EN Bit 10(RTC_EN)
**/
VOID
EnableS5WakeOnRtc()
{
UINT8 CmosData;
UINTN i;
EFI_STATUS Status;
UINTN VarSize;
//
// make sure EFI_SMM_VARIABLE_PROTOCOL is available
//
if (!mSmmVariable) {
return;
}
VarSize = sizeof(SYSTEM_CONFIGURATION);
//
// read the variable into the buffer
//
Status = mSmmVariable->SmmGetVariable(
L"Setup",
&gEfiSetupVariableGuid,
NULL,
&VarSize,
&mSystemConfiguration
);
if (EFI_ERROR(Status) || VarSize != sizeof(SYSTEM_CONFIGURATION)) {
//The setup variable is corrupted
VarSize = sizeof(SYSTEM_CONFIGURATION);
Status = mSmmVariable->SmmGetVariable(
L"SetupRecovery",
&gEfiSetupVariableGuid,
NULL,
&VarSize,
&mSystemConfiguration
);
ASSERT_EFI_ERROR (Status);
}
if (!mSystemConfiguration.WakeOnRtcS5) {
return;
}
mWakeupDay = HexToBcd((UINT8)mSystemConfiguration.RTCWakeupDate);
mWakeupHour = HexToBcd((UINT8)mSystemConfiguration.RTCWakeupTimeHour);
mWakeupMinute = HexToBcd((UINT8)mSystemConfiguration.RTCWakeupTimeMinute);
mWakeupSecond = HexToBcd((UINT8)mSystemConfiguration.RTCWakeupTimeSecond);
//
// Check RTC alarm interrupt is enabled. If enabled, someone already
// grabbed RTC alarm. Just return.
//
IoWrite8(PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_REGISTER_B);
if(IoRead8(PCAT_RTC_DATA_REGISTER) & B_RTC_ALARM_INT_ENABLE){
return;
}
//
// Set Date
//
IoWrite8(PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_REGISTER_D);
CmosData = IoRead8(PCAT_RTC_DATA_REGISTER);
CmosData &= ~(B_RTC_DATE_ALARM_MASK);
CmosData |= mWakeupDay ;
for(i = 0 ; i < 0xffff ; i++){
IoWrite8(PCAT_RTC_DATA_REGISTER, CmosData);
SmmStall(1);
if(((CmosData = IoRead8(PCAT_RTC_DATA_REGISTER)) & B_RTC_DATE_ALARM_MASK)
== mWakeupDay){
break;
}
}
//
// Set Second
//
IoWrite8(PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_SECOND_ALARM);
for(i = 0 ; i < 0xffff ; i++){
IoWrite8(PCAT_RTC_DATA_REGISTER, mWakeupSecond);
SmmStall(1);
if(IoRead8(PCAT_RTC_DATA_REGISTER) == mWakeupSecond){
break;
}
}
//
// Set Minute
//
IoWrite8(PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_MINUTE_ALARM);
for(i = 0 ; i < 0xffff ; i++){
IoWrite8(PCAT_RTC_DATA_REGISTER, mWakeupMinute);
SmmStall(1);
if(IoRead8(PCAT_RTC_DATA_REGISTER) == mWakeupMinute){
break;
}
}
//
// Set Hour
//
IoWrite8(PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_HOUR_ALARM);
for(i = 0 ; i < 0xffff ; i++){
IoWrite8(PCAT_RTC_DATA_REGISTER, mWakeupHour);
SmmStall(1);
if(IoRead8(PCAT_RTC_DATA_REGISTER) == mWakeupHour){
break;
}
}
//
// Wait for UIP to arm RTC alarm
//
IoWrite8(PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_REGISTER_A);
while (IoRead8(PCAT_RTC_DATA_REGISTER) & 0x80);
//
// Read RTC register 0C to clear pending RTC interrupts
//
IoWrite8(PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_REGISTER_C);
IoRead8(PCAT_RTC_DATA_REGISTER);
//
// Enable RTC Alarm Interrupt
//
IoWrite8(PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_REGISTER_B);
IoWrite8(PCAT_RTC_DATA_REGISTER, IoRead8(PCAT_RTC_DATA_REGISTER) | B_RTC_ALARM_INT_ENABLE);
//
// Clear ICH RTC Status
//
IoWrite16(mAcpiBaseAddr + R_PCH_ACPI_PM1_STS, B_PCH_ACPI_PM1_STS_RTC);
//
// Enable ICH RTC event
//
IoWrite16(mAcpiBaseAddr + R_PCH_ACPI_PM1_EN,
(UINT16)(IoRead16(mAcpiBaseAddr + R_PCH_ACPI_PM1_EN) | B_PCH_ACPI_PM1_EN_RTC));
}
UINT8
HexToBcd(
IN UINT8 HexValue
)
{
UINTN HighByte;
UINTN LowByte;
HighByte = (UINTN)HexValue / 10;
LowByte = (UINTN)HexValue % 10;
return ((UINT8)(LowByte + (HighByte << 4)));
}
UINT8
BcdToHex(
IN UINT8 BcdValue
)
{
UINTN HighByte;
UINTN LowByte;
HighByte = (UINTN)((BcdValue >> 4) * 10);
LowByte = (UINTN)(BcdValue & 0x0F);
return ((UINT8)(LowByte + HighByte));
}