| /** @file | |
| System reset Library Services. This library class provides a set of | |
| methods to reset whole system with manipulate QNC. | |
| Copyright (c) 2013-2015 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 (); | |
| } | |
| } | |