/** @file
System reset Library Services. This library class provides a set of
methods to reset whole system with manipulate QNC.
Copyright (c) 2013-2016 Intel Corporation.
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.
**/
#include <Base.h>
#include <IntelQNCBase.h>
#include <QNCAccess.h>
#include <Uefi/UefiBaseType.h>
#include <Library/ResetSystemLib.h>
#include <Library/BaseLib.h>
#include <Library/IoLib.h>
#include <Library/PcdLib.h>
#include <Library/CpuLib.h>
#include <Library/QNCAccessLib.h>
//
// Amount of time (seconds) before RTC alarm fires
// This must be < BCD_BASE
//
#define PLATFORM_WAKE_SECONDS_BUFFER 0x06
//
// RTC 'seconds' above which we will not read to avoid potential rollover
//
#define PLATFORM_RTC_ROLLOVER_LIMIT 0x47
//
// BCD is base 10
//
#define BCD_BASE 0x0A
#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
/**
Wait for an RTC update to happen
**/
VOID
EFIAPI
WaitForRTCUpdate (
VOID
)
{
UINT8 Data8;
IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_REGISTER_A);
Data8 = IoRead8 (PCAT_RTC_DATA_REGISTER);
if ((Data8 & BIT7) == BIT7) {
while ((Data8 & BIT7) == BIT7) {
IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_REGISTER_A);
Data8 = IoRead8 (PCAT_RTC_DATA_REGISTER);
}
} else {
while ((Data8 & BIT7) == 0) {
IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_REGISTER_A);
Data8 = IoRead8 (PCAT_RTC_DATA_REGISTER);
}
while ((Data8 & BIT7) == BIT7) {
IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_REGISTER_A);
Data8 = IoRead8 (PCAT_RTC_DATA_REGISTER);
}
}
}
/**
Calling this function causes a system-wide reset. This sets
all circuitry within the system to its initial state. This type of reset
is asynchronous to system operation and operates without regard to
cycle boundaries.
System reset should not return, if it returns, it means the system does
not support cold reset.
**/
VOID
EFIAPI
ResetCold (
VOID
)
{
//
// Reference to QuarkNcSocId BWG
// Setting bit 1 will generate a warm reset, driving only RSTRDY# low
//
IoWrite8 (RST_CNT, B_RST_CNT_COLD_RST);
}
/**
Calling this function causes a system-wide initialization. The processors
are set to their initial state, and pending cycles are not corrupted.
System reset should not return, if it returns, it means the system does
not support warm reset.
**/
VOID
EFIAPI
ResetWarm (
VOID
)
{
//
// Reference to QuarkNcSocId BWG
// Setting bit 1 will generate a warm reset, driving only RSTRDY# low
//
IoWrite8 (RST_CNT, B_RST_CNT_WARM_RST);
}
/**
Calling this function causes the system to enter a power state equivalent
to the ACPI G2/S5 or G3 states.
System shutdown should not return, if it returns, it means the system does
not support shut down reset.
**/
VOID
EFIAPI
ResetShutdown (
VOID
)
{
//
// Reference to QuarkNcSocId BWG
// Disable RTC Alarm : (RTC Enable at PM1BLK + 02h[10]))
//
IoWrite16 (PcdGet16 (PcdPm1blkIoBaseAddress) + R_QNC_PM1BLK_PM1E, 0);
//
// Firstly, GPE0_EN should be disabled to
// avoid any GPI waking up the system from S5
//
IoWrite32 ((UINT16)(LpcPciCfg32 (R_QNC_LPC_GPE0BLK) & 0xFFFF) + R_QNC_GPE0BLK_GPE0E, 0);
//
// Reference to QuarkNcSocId BWG
// Disable Resume Well GPIO : (GPIO bits in GPIOBASE + 34h[8:0])
//
IoWrite32 (PcdGet16 (PcdGbaIoBaseAddress) + R_QNC_GPIO_RGGPE_RESUME_WELL, 0);
//
// No power button status bit to clear for our platform, go to next step.
//
//
// Finally, transform system into S5 sleep state
//
IoAndThenOr32 (PcdGet16 (PcdPm1blkIoBaseAddress) + R_QNC_PM1BLK_PM1C, 0xffffc3ff, B_QNC_PM1BLK_PM1C_SLPEN | V_S5);
}
/**
Calling this function causes the system to enter a power state for capsule
update.
Reset update should not return, if it returns, it means the system does
not support capsule update.
**/
VOID
EFIAPI
EnterS3WithImmediateWake (
VOID
)
{
UINT8 Data8;
UINT16 Data16;
UINT32 Data32;
UINTN Eflags;
UINTN RegCr0;
EFI_TIME EfiTime;
UINT32 SmiEnSave;
Eflags = AsmReadEflags ();
if ( (Eflags & 0x200) ) {
DisableInterrupts ();
}
//
// Write all cache data to memory because processor will lost power
//
AsmWbinvd();
RegCr0 = AsmReadCr0();
AsmWriteCr0 (RegCr0 | 0x060000000);
SmiEnSave = QNCPortRead (QUARK_NC_HOST_BRIDGE_SB_PORT_ID, QNC_MSG_FSBIC_REG_HMISC);
QNCPortWrite (QUARK_NC_HOST_BRIDGE_SB_PORT_ID, QNC_MSG_FSBIC_REG_HMISC, (SmiEnSave & ~SMI_EN));
//
// Pogram RTC alarm for immediate WAKE
//
//
// Disable SMI sources
//
IoWrite16 (PcdGet16 (PcdGpe0blkIoBaseAddress) + R_QNC_GPE0BLK_SMIE, 0);
//
// Disable RTC alarm interrupt
//
IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_REGISTER_B);
Data8 = IoRead8 (PCAT_RTC_DATA_REGISTER);
IoWrite8 (PCAT_RTC_DATA_REGISTER, (Data8 & ~BIT5));
//
// Clear RTC alarm if already set
//
IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_REGISTER_C);
Data8 = IoRead8 (PCAT_RTC_DATA_REGISTER); // Read clears alarm status
//
// Disable all WAKE events
//
IoWrite16 (PcdGet16 (PcdPm1blkIoBaseAddress) + R_QNC_PM1BLK_PM1E, B_QNC_PM1BLK_PM1E_PWAKED);
//
// Clear all WAKE status bits
//
IoWrite16 (PcdGet16 (PcdPm1blkIoBaseAddress) + R_QNC_PM1BLK_PM1S, B_QNC_PM1BLK_PM1S_ALL);
//
// Avoid RTC rollover
//
do {
WaitForRTCUpdate();
IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_SECONDS);
EfiTime.Second = IoRead8 (PCAT_RTC_DATA_REGISTER);
} while (EfiTime.Second > PLATFORM_RTC_ROLLOVER_LIMIT);
//
// Read RTC time
//
IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_HOURS);
EfiTime.Hour = IoRead8 (PCAT_RTC_DATA_REGISTER);
IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_MINUTES);
EfiTime.Minute = IoRead8 (PCAT_RTC_DATA_REGISTER);
IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_SECONDS);
EfiTime.Second = IoRead8 (PCAT_RTC_DATA_REGISTER);
//
// Set RTC alarm
//
//
// Add PLATFORM_WAKE_SECONDS_BUFFER to current EfiTime.Second
// The maths is to allow for the fact we are adding to a BCD number and require the answer to be BCD (EfiTime.Second)
//
if ((BCD_BASE - (EfiTime.Second & 0x0F)) <= PLATFORM_WAKE_SECONDS_BUFFER) {
Data8 = (((EfiTime.Second & 0xF0) + 0x10) + (PLATFORM_WAKE_SECONDS_BUFFER - (BCD_BASE - (EfiTime.Second & 0x0F))));
} else {
Data8 = EfiTime.Second + PLATFORM_WAKE_SECONDS_BUFFER;
}
IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_HOURS_ALARM);
IoWrite8 (PCAT_RTC_DATA_REGISTER, EfiTime.Hour);
IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_MINUTES_ALARM);
IoWrite8 (PCAT_RTC_DATA_REGISTER, EfiTime.Minute);
IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_SECONDS_ALARM);
IoWrite8 (PCAT_RTC_DATA_REGISTER, Data8);
//
// Enable RTC alarm interrupt
//
IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_REGISTER_B);
Data8 = IoRead8 (PCAT_RTC_DATA_REGISTER);
IoWrite8 (PCAT_RTC_DATA_REGISTER, (Data8 | BIT5));
//
// Enable RTC alarm as WAKE event
//
Data16 = IoRead16 (PcdGet16 (PcdPm1blkIoBaseAddress) + R_QNC_PM1BLK_PM1E);
IoWrite16 (PcdGet16 (PcdPm1blkIoBaseAddress) + R_QNC_PM1BLK_PM1E, (Data16 | B_QNC_PM1BLK_PM1E_RTC));
//
// Enter S3
//
Data32 = IoRead32 (PcdGet16 (PcdPm1blkIoBaseAddress) + R_QNC_PM1BLK_PM1C);
Data32 = (UINT32) ((Data32 & 0xffffc3fe) | V_S3 | B_QNC_PM1BLK_PM1C_SCIEN);
IoWrite32 (PcdGet16 (PcdPm1blkIoBaseAddress) + R_QNC_PM1BLK_PM1C, Data32);
Data32 = Data32 | B_QNC_PM1BLK_PM1C_SLPEN;
IoWrite32 (PcdGet16 (PcdPm1blkIoBaseAddress) + R_QNC_PM1BLK_PM1C, Data32);
//
// Enable Interrupt if it's enabled before
//
if ( (Eflags & 0x200) ) {
EnableInterrupts ();
}
}
/**
This function causes a systemwide reset. The exact type of the reset is
defined by the EFI_GUID that follows the Null-terminated Unicode string passed
into ResetData. If the platform does not recognize the EFI_GUID in ResetData
the platform must pick a supported reset type to perform.The platform may
optionally log the parameters from any non-normal reset that occurs.
@param[in] DataSize The size, in bytes, of ResetData.
@param[in] ResetData The data buffer starts with a Null-terminated string,
followed by the EFI_GUID.
**/
VOID
EFIAPI
ResetPlatformSpecific (
IN UINTN DataSize,
IN VOID *ResetData
)
{
ResetCold ();
}