summaryrefslogtreecommitdiff
path: root/arch/x86
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86')
-rw-r--r--arch/x86/Kconfig32
-rw-r--r--arch/x86/cpu/Makefile1
-rw-r--r--arch/x86/cpu/baytrail/acpi.c47
-rw-r--r--arch/x86/cpu/baytrail/valleyview.c12
-rw-r--r--arch/x86/cpu/cpu.c30
-rw-r--r--arch/x86/cpu/quark/quark.c10
-rw-r--r--arch/x86/cpu/queensbay/Makefile2
-rw-r--r--arch/x86/cpu/queensbay/topcliff.c20
-rw-r--r--arch/x86/cpu/wakeup.S78
-rw-r--r--arch/x86/dts/bayleybay.dts6
-rw-r--r--arch/x86/dts/baytrail_som-db5800-som-6867.dts6
-rw-r--r--arch/x86/dts/conga-qeval20-qa3-e3845.dts6
-rw-r--r--arch/x86/dts/dfi-bt700.dtsi6
-rw-r--r--arch/x86/dts/minnowmax.dts11
-rw-r--r--arch/x86/include/asm/acpi_s3.h131
-rw-r--r--arch/x86/include/asm/acpi_table.h28
-rw-r--r--arch/x86/include/asm/arch-baytrail/acpi/sleepstates.asl2
-rw-r--r--arch/x86/include/asm/arch-baytrail/iomap.h24
-rw-r--r--arch/x86/include/asm/cmos_layout.h31
-rw-r--r--arch/x86/include/asm/early_cmos.h43
-rw-r--r--arch/x86/include/asm/global_data.h4
-rw-r--r--arch/x86/include/asm/post.h2
-rw-r--r--arch/x86/include/asm/tables.h1
-rw-r--r--arch/x86/include/asm/u-boot-x86.h13
-rw-r--r--arch/x86/lib/Makefile2
-rw-r--r--arch/x86/lib/acpi_s3.c82
-rw-r--r--arch/x86/lib/acpi_table.c84
-rw-r--r--arch/x86/lib/bootm.c9
-rw-r--r--arch/x86/lib/coreboot_table.c7
-rw-r--r--arch/x86/lib/early_cmos.c51
-rw-r--r--arch/x86/lib/fsp/fsp_common.c63
-rw-r--r--arch/x86/lib/fsp/fsp_dram.c12
32 files changed, 802 insertions, 54 deletions
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 9ead3ebccf..0cd981e73e 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -589,6 +589,38 @@ config GENERATE_ACPI_TABLE
endmenu
+config HAVE_ACPI_RESUME
+ bool "Enable ACPI S3 resume"
+ help
+ Select this to enable ACPI S3 resume. S3 is an ACPI-defined sleeping
+ state where all system context is lost except system memory. U-Boot
+ is responsible for restoring the machine state as it was before sleep.
+ It needs restore the memory controller, without overwriting memory
+ which is not marked as reserved. For the peripherals which lose their
+ registers, U-Boot needs to write the original value. When everything
+ is done, U-Boot needs to find out the wakeup vector provided by OSes
+ and jump there.
+
+config S3_VGA_ROM_RUN
+ bool "Re-run VGA option ROMs on S3 resume"
+ depends on HAVE_ACPI_RESUME
+ default y if HAVE_ACPI_RESUME
+ help
+ Execute VGA option ROMs in U-Boot when resuming from S3. Normally
+ this is needed when graphics console is being used in the kernel.
+
+ Turning it off can reduce some resume time, but be aware that your
+ graphics console won't work without VGA options ROMs. Set it to N
+ if your kernel is only on a serial console.
+
+config STACK_SIZE
+ hex
+ depends on HAVE_ACPI_RESUME
+ default 0x1000
+ help
+ Estimated U-Boot's runtime stack size that needs to be reserved
+ during an ACPI S3 resume.
+
config MAX_PIRQ_LINKS
int
default 8
diff --git a/arch/x86/cpu/Makefile b/arch/x86/cpu/Makefile
index 92a9023b0b..e1c84ce097 100644
--- a/arch/x86/cpu/Makefile
+++ b/arch/x86/cpu/Makefile
@@ -45,6 +45,7 @@ ifndef CONFIG_$(SPL_)X86_64
obj-$(CONFIG_SMP) += sipi_vector.o
endif
obj-y += turbo.o
+obj-$(CONFIG_HAVE_ACPI_RESUME) += wakeup.o
ifeq ($(CONFIG_$(SPL_)X86_64),y)
obj-y += x86_64/
diff --git a/arch/x86/cpu/baytrail/acpi.c b/arch/x86/cpu/baytrail/acpi.c
index fa92d8852e..55ed7de781 100644
--- a/arch/x86/cpu/baytrail/acpi.c
+++ b/arch/x86/cpu/baytrail/acpi.c
@@ -8,7 +8,9 @@
#include <cpu.h>
#include <dm.h>
#include <dm/uclass-internal.h>
+#include <asm/acpi_s3.h>
#include <asm/acpi_table.h>
+#include <asm/io.h>
#include <asm/ioapic.h>
#include <asm/mpspec.h>
#include <asm/tables.h>
@@ -187,3 +189,48 @@ void acpi_create_gnvs(struct acpi_global_nvs *gnvs)
else
gnvs->iuart_en = 0;
}
+
+#ifdef CONFIG_HAVE_ACPI_RESUME
+/*
+ * The following two routines are called at a very early stage, even before
+ * FSP 2nd phase API fsp_init() is called. Registers off ACPI_BASE_ADDRESS
+ * and PMC_BASE_ADDRESS are accessed, so we need make sure the base addresses
+ * of these two blocks are programmed by either U-Boot or FSP.
+ *
+ * It has been verified that 1st phase API (see arch/x86/lib/fsp/fsp_car.S)
+ * on Intel BayTrail SoC already initializes these two base addresses so
+ * we are safe to access these registers here.
+ */
+
+enum acpi_sleep_state chipset_prev_sleep_state(void)
+{
+ u32 pm1_sts;
+ u32 pm1_cnt;
+ u32 gen_pmcon1;
+ enum acpi_sleep_state prev_sleep_state = ACPI_S0;
+
+ /* Read Power State */
+ pm1_sts = inw(ACPI_BASE_ADDRESS + PM1_STS);
+ pm1_cnt = inl(ACPI_BASE_ADDRESS + PM1_CNT);
+ gen_pmcon1 = readl(PMC_BASE_ADDRESS + GEN_PMCON1);
+
+ debug("PM1_STS = 0x%x PM1_CNT = 0x%x GEN_PMCON1 = 0x%x\n",
+ pm1_sts, pm1_cnt, gen_pmcon1);
+
+ if (pm1_sts & WAK_STS)
+ prev_sleep_state = acpi_sleep_from_pm1(pm1_cnt);
+
+ if (gen_pmcon1 & (PWR_FLR | SUS_PWR_FLR))
+ prev_sleep_state = ACPI_S5;
+
+ return prev_sleep_state;
+}
+
+void chipset_clear_sleep_state(void)
+{
+ u32 pm1_cnt;
+
+ pm1_cnt = inl(ACPI_BASE_ADDRESS + PM1_CNT);
+ outl(pm1_cnt & ~(SLP_TYP), ACPI_BASE_ADDRESS + PM1_CNT);
+}
+#endif
diff --git a/arch/x86/cpu/baytrail/valleyview.c b/arch/x86/cpu/baytrail/valleyview.c
index 87ba849c1c..c58f6a86a8 100644
--- a/arch/x86/cpu/baytrail/valleyview.c
+++ b/arch/x86/cpu/baytrail/valleyview.c
@@ -11,18 +11,6 @@
#include <asm/mrccache.h>
#include <asm/post.h>
-static struct pci_device_id mmc_supported[] = {
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT_SDIO },
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT_SD },
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT_EMMC2 },
- {},
-};
-
-int cpu_mmc_init(bd_t *bis)
-{
- return pci_mmc_init("ValleyView SDHCI", mmc_supported);
-}
-
#ifndef CONFIG_EFI_APP
int arch_cpu_init(void)
{
diff --git a/arch/x86/cpu/cpu.c b/arch/x86/cpu/cpu.c
index 8fa6953588..e13786efa5 100644
--- a/arch/x86/cpu/cpu.c
+++ b/arch/x86/cpu/cpu.c
@@ -25,6 +25,8 @@
#include <errno.h>
#include <malloc.h>
#include <syscon.h>
+#include <asm/acpi_s3.h>
+#include <asm/acpi_table.h>
#include <asm/control_regs.h>
#include <asm/coreboot_tables.h>
#include <asm/cpu.h>
@@ -179,6 +181,11 @@ int default_print_cpuinfo(void)
cpu_has_64bit() ? "x86_64" : "x86",
cpu_vendor_name(gd->arch.x86_vendor), gd->arch.x86_device);
+#ifdef CONFIG_HAVE_ACPI_RESUME
+ debug("ACPI previous sleep state: %s\n",
+ acpi_ss_string(gd->arch.prev_sleep_state));
+#endif
+
return 0;
}
@@ -198,10 +205,17 @@ __weak void board_final_cleanup(void)
int last_stage_init(void)
{
- write_tables();
-
board_final_cleanup();
+#if CONFIG_HAVE_ACPI_RESUME
+ struct acpi_fadt *fadt = acpi_find_fadt();
+
+ if (fadt != NULL && gd->arch.prev_sleep_state == ACPI_S3)
+ acpi_resume(fadt);
+#endif
+
+ write_tables();
+
return 0;
}
#endif
@@ -264,6 +278,18 @@ int reserve_arch(void)
high_table_reserve();
#endif
+#ifdef CONFIG_HAVE_ACPI_RESUME
+ acpi_s3_reserve();
+
+#ifdef CONFIG_HAVE_FSP
+ /*
+ * Save stack address to CMOS so that at next S3 boot,
+ * we can use it as the stack address for fsp_contiue()
+ */
+ fsp_save_s3_stack();
+#endif /* CONFIG_HAVE_FSP */
+#endif /* CONFIG_HAVE_ACPI_RESUME */
+
return 0;
}
#endif
diff --git a/arch/x86/cpu/quark/quark.c b/arch/x86/cpu/quark/quark.c
index 0c2cea4ee9..c36a5892d5 100644
--- a/arch/x86/cpu/quark/quark.c
+++ b/arch/x86/cpu/quark/quark.c
@@ -16,11 +16,6 @@
#include <asm/arch/msg_port.h>
#include <asm/arch/quark.h>
-static struct pci_device_id mmc_supported[] = {
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QRK_SDIO },
- {},
-};
-
static void quark_setup_mtrr(void)
{
u32 base, mask;
@@ -328,11 +323,6 @@ int arch_early_init_r(void)
return 0;
}
-int cpu_mmc_init(bd_t *bis)
-{
- return pci_mmc_init("Quark SDHCI", mmc_supported);
-}
-
int arch_misc_init(void)
{
#ifdef CONFIG_ENABLE_MRC_CACHE
diff --git a/arch/x86/cpu/queensbay/Makefile b/arch/x86/cpu/queensbay/Makefile
index af3ffad385..c0681995bd 100644
--- a/arch/x86/cpu/queensbay/Makefile
+++ b/arch/x86/cpu/queensbay/Makefile
@@ -5,4 +5,4 @@
#
obj-y += fsp_configs.o irq.o
-obj-y += tnc.o topcliff.o
+obj-y += tnc.o
diff --git a/arch/x86/cpu/queensbay/topcliff.c b/arch/x86/cpu/queensbay/topcliff.c
deleted file mode 100644
index b76dd7de69..0000000000
--- a/arch/x86/cpu/queensbay/topcliff.c
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
- *
- * SPDX-License-Identifier: GPL-2.0+
- */
-
-#include <common.h>
-#include <mmc.h>
-#include <pci_ids.h>
-
-static struct pci_device_id mmc_supported[] = {
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TCF_SDIO_0 },
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TCF_SDIO_1 },
- {},
-};
-
-int cpu_mmc_init(bd_t *bis)
-{
- return pci_mmc_init("Topcliff SDHCI", mmc_supported);
-}
diff --git a/arch/x86/cpu/wakeup.S b/arch/x86/cpu/wakeup.S
new file mode 100644
index 0000000000..066c9b1a55
--- /dev/null
+++ b/arch/x86/cpu/wakeup.S
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * From coreboot src/arch/x86/wakeup.S
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <asm/acpi_s3.h>
+#include <asm/processor.h>
+#include <asm/processor-flags.h>
+
+#define RELOCATED(x) ((x) - __wakeup + WAKEUP_BASE)
+
+#define CODE_SEG (X86_GDT_ENTRY_16BIT_CS * X86_GDT_ENTRY_SIZE)
+#define DATA_SEG (X86_GDT_ENTRY_16BIT_DS * X86_GDT_ENTRY_SIZE)
+
+ .code32
+ .globl __wakeup
+__wakeup:
+ /* First prepare the jmp to the resume vector */
+ mov 0x4(%esp), %eax /* vector */
+ /* last 4 bits of linear addr are taken as offset */
+ andw $0x0f, %ax
+ movw %ax, (__wakeup_offset)
+ mov 0x4(%esp), %eax
+ /* the rest is taken as segment */
+ shr $4, %eax
+ movw %ax, (__wakeup_segment)
+
+ /* Activate the right segment descriptor real mode */
+ ljmp $CODE_SEG, $RELOCATED(1f)
+1:
+ /* 16 bit code from here on... */
+ .code16
+
+ /*
+ * Load the segment registers w/ properly configured segment
+ * descriptors. They will retain these configurations (limits,
+ * writability, etc.) once protected mode is turned off.
+ */
+ mov $DATA_SEG, %ax
+ mov %ax, %ds
+ mov %ax, %es
+ mov %ax, %fs
+ mov %ax, %gs
+ mov %ax, %ss
+
+ /* Turn off protection */
+ movl %cr0, %eax
+ andl $~X86_CR0_PE, %eax
+ movl %eax, %cr0
+
+ /* Now really going into real mode */
+ ljmp $0, $RELOCATED(1f)
+1:
+ movw $0x0, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %ss
+ movw %ax, %fs
+ movw %ax, %gs
+
+ /*
+ * This is a FAR JMP to the OS waking vector.
+ * The C code changes the address to be correct.
+ */
+ .byte 0xea
+
+__wakeup_offset = RELOCATED(.)
+ .word 0x0000
+
+__wakeup_segment = RELOCATED(.)
+ .word 0x0000
+
+ .globl __wakeup_size
+__wakeup_size:
+ .long . - __wakeup
diff --git a/arch/x86/dts/bayleybay.dts b/arch/x86/dts/bayleybay.dts
index 18b310d39e..1ae058d7a9 100644
--- a/arch/x86/dts/bayleybay.dts
+++ b/arch/x86/dts/bayleybay.dts
@@ -189,6 +189,7 @@
u-boot,dm-pre-reloc;
reg = <0 0x20>;
bank-name = "A";
+ use-lvl-write-cache;
};
gpiob {
@@ -196,6 +197,7 @@
u-boot,dm-pre-reloc;
reg = <0x20 0x20>;
bank-name = "B";
+ use-lvl-write-cache;
};
gpioc {
@@ -203,6 +205,7 @@
u-boot,dm-pre-reloc;
reg = <0x40 0x20>;
bank-name = "C";
+ use-lvl-write-cache;
};
gpiod {
@@ -210,6 +213,7 @@
u-boot,dm-pre-reloc;
reg = <0x60 0x20>;
bank-name = "D";
+ use-lvl-write-cache;
};
gpioe {
@@ -217,6 +221,7 @@
u-boot,dm-pre-reloc;
reg = <0x80 0x20>;
bank-name = "E";
+ use-lvl-write-cache;
};
gpiof {
@@ -224,6 +229,7 @@
u-boot,dm-pre-reloc;
reg = <0xA0 0x20>;
bank-name = "F";
+ use-lvl-write-cache;
};
};
};
diff --git a/arch/x86/dts/baytrail_som-db5800-som-6867.dts b/arch/x86/dts/baytrail_som-db5800-som-6867.dts
index e1d81a7283..aa8bfb8651 100644
--- a/arch/x86/dts/baytrail_som-db5800-som-6867.dts
+++ b/arch/x86/dts/baytrail_som-db5800-som-6867.dts
@@ -212,6 +212,7 @@
u-boot,dm-pre-reloc;
reg = <0 0x20>;
bank-name = "A";
+ use-lvl-write-cache;
};
gpiob {
@@ -219,6 +220,7 @@
u-boot,dm-pre-reloc;
reg = <0x20 0x20>;
bank-name = "B";
+ use-lvl-write-cache;
};
gpioc {
@@ -226,6 +228,7 @@
u-boot,dm-pre-reloc;
reg = <0x40 0x20>;
bank-name = "C";
+ use-lvl-write-cache;
};
gpiod {
@@ -233,6 +236,7 @@
u-boot,dm-pre-reloc;
reg = <0x60 0x20>;
bank-name = "D";
+ use-lvl-write-cache;
};
gpioe {
@@ -240,6 +244,7 @@
u-boot,dm-pre-reloc;
reg = <0x80 0x20>;
bank-name = "E";
+ use-lvl-write-cache;
};
gpiof {
@@ -247,6 +252,7 @@
u-boot,dm-pre-reloc;
reg = <0xA0 0x20>;
bank-name = "F";
+ use-lvl-write-cache;
};
};
};
diff --git a/arch/x86/dts/conga-qeval20-qa3-e3845.dts b/arch/x86/dts/conga-qeval20-qa3-e3845.dts
index f0efe908e2..898e9c9b5f 100644
--- a/arch/x86/dts/conga-qeval20-qa3-e3845.dts
+++ b/arch/x86/dts/conga-qeval20-qa3-e3845.dts
@@ -199,6 +199,7 @@
u-boot,dm-pre-reloc;
reg = <0 0x20>;
bank-name = "A";
+ use-lvl-write-cache;
};
gpiob {
@@ -206,6 +207,7 @@
u-boot,dm-pre-reloc;
reg = <0x20 0x20>;
bank-name = "B";
+ use-lvl-write-cache;
};
gpioc {
@@ -213,6 +215,7 @@
u-boot,dm-pre-reloc;
reg = <0x40 0x20>;
bank-name = "C";
+ use-lvl-write-cache;
};
gpiod {
@@ -220,6 +223,7 @@
u-boot,dm-pre-reloc;
reg = <0x60 0x20>;
bank-name = "D";
+ use-lvl-write-cache;
};
gpioe {
@@ -227,6 +231,7 @@
u-boot,dm-pre-reloc;
reg = <0x80 0x20>;
bank-name = "E";
+ use-lvl-write-cache;
};
gpiof {
@@ -234,6 +239,7 @@
u-boot,dm-pre-reloc;
reg = <0xA0 0x20>;
bank-name = "F";
+ use-lvl-write-cache;
};
};
};
diff --git a/arch/x86/dts/dfi-bt700.dtsi b/arch/x86/dts/dfi-bt700.dtsi
index 75ee6ade7c..546981a9ac 100644
--- a/arch/x86/dts/dfi-bt700.dtsi
+++ b/arch/x86/dts/dfi-bt700.dtsi
@@ -201,6 +201,7 @@
u-boot,dm-pre-reloc;
reg = <0 0x20>;
bank-name = "A";
+ use-lvl-write-cache;
};
gpiob {
@@ -208,6 +209,7 @@
u-boot,dm-pre-reloc;
reg = <0x20 0x20>;
bank-name = "B";
+ use-lvl-write-cache;
};
gpioc {
@@ -215,6 +217,7 @@
u-boot,dm-pre-reloc;
reg = <0x40 0x20>;
bank-name = "C";
+ use-lvl-write-cache;
};
gpiod {
@@ -222,6 +225,7 @@
u-boot,dm-pre-reloc;
reg = <0x60 0x20>;
bank-name = "D";
+ use-lvl-write-cache;
};
gpioe {
@@ -229,6 +233,7 @@
u-boot,dm-pre-reloc;
reg = <0x80 0x20>;
bank-name = "E";
+ use-lvl-write-cache;
};
gpiof {
@@ -236,6 +241,7 @@
u-boot,dm-pre-reloc;
reg = <0xA0 0x20>;
bank-name = "F";
+ use-lvl-write-cache;
};
};
};
diff --git a/arch/x86/dts/minnowmax.dts b/arch/x86/dts/minnowmax.dts
index d51318bdf6..af64c6859c 100644
--- a/arch/x86/dts/minnowmax.dts
+++ b/arch/x86/dts/minnowmax.dts
@@ -35,7 +35,6 @@
/* GPIO E0 */
soc_gpio_s5_0@0 {
gpio-offset = <0x80 0>;
- pad-offset = <0x1d0>;
mode-gpio;
output-value = <0>;
direction = <PIN_OUTPUT>;
@@ -44,7 +43,6 @@
/* GPIO E1 */
soc_gpio_s5_1@0 {
gpio-offset = <0x80 1>;
- pad-offset = <0x210>;
mode-gpio;
output-value = <0>;
direction = <PIN_OUTPUT>;
@@ -53,7 +51,6 @@
/* GPIO E2 */
soc_gpio_s5_2@0 {
gpio-offset = <0x80 2>;
- pad-offset = <0x1e0>;
mode-gpio;
output-value = <0>;
direction = <PIN_OUTPUT>;
@@ -61,7 +58,6 @@
pin_usb_host_en0@0 {
gpio-offset = <0x80 8>;
- pad-offset = <0x260>;
mode-gpio;
output-value = <1>;
direction = <PIN_OUTPUT>;
@@ -69,7 +65,6 @@
pin_usb_host_en1@0 {
gpio-offset = <0x80 9>;
- pad-offset = <0x250>;
mode-gpio;
output-value = <1>;
direction = <PIN_OUTPUT>;
@@ -218,6 +213,7 @@
u-boot,dm-pre-reloc;
reg = <0 0x20>;
bank-name = "A";
+ use-lvl-write-cache;
};
gpiob {
@@ -225,6 +221,7 @@
u-boot,dm-pre-reloc;
reg = <0x20 0x20>;
bank-name = "B";
+ use-lvl-write-cache;
};
gpioc {
@@ -232,6 +229,7 @@
u-boot,dm-pre-reloc;
reg = <0x40 0x20>;
bank-name = "C";
+ use-lvl-write-cache;
};
gpiod {
@@ -239,6 +237,7 @@
u-boot,dm-pre-reloc;
reg = <0x60 0x20>;
bank-name = "D";
+ use-lvl-write-cache;
};
gpioe {
@@ -246,6 +245,7 @@
u-boot,dm-pre-reloc;
reg = <0x80 0x20>;
bank-name = "E";
+ use-lvl-write-cache;
};
gpiof {
@@ -253,6 +253,7 @@
u-boot,dm-pre-reloc;
reg = <0xA0 0x20>;
bank-name = "F";
+ use-lvl-write-cache;
};
};
};
diff --git a/arch/x86/include/asm/acpi_s3.h b/arch/x86/include/asm/acpi_s3.h
new file mode 100644
index 0000000000..86aec0abe0
--- /dev/null
+++ b/arch/x86/include/asm/acpi_s3.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef __ASM_ACPI_S3_H__
+#define __ASM_ACPI_S3_H__
+
+#define WAKEUP_BASE 0x600
+
+/* PM1_STATUS register */
+#define WAK_STS (1 << 15)
+#define PCIEXPWAK_STS (1 << 14)
+#define RTC_STS (1 << 10)
+#define SLPBTN_STS (1 << 9)
+#define PWRBTN_STS (1 << 8)
+#define GBL_STS (1 << 5)
+#define BM_STS (1 << 4)
+#define TMR_STS (1 << 0)
+
+/* PM1_CNT register */
+#define SLP_EN (1 << 13)
+#define SLP_TYP_SHIFT 10
+#define SLP_TYP (7 << SLP_TYP_SHIFT)
+#define SLP_TYP_S0 0
+#define SLP_TYP_S1 1
+#define SLP_TYP_S3 5
+#define SLP_TYP_S4 6
+#define SLP_TYP_S5 7
+
+/* Memory size reserved for S3 resume */
+#define S3_RESERVE_SIZE 0x1000
+
+#ifndef __ASSEMBLY__
+
+extern char __wakeup[];
+extern int __wakeup_size;
+
+enum acpi_sleep_state {
+ ACPI_S0,
+ ACPI_S1,
+ ACPI_S2,
+ ACPI_S3,
+ ACPI_S4,
+ ACPI_S5,
+};
+
+/**
+ * acpi_ss_string() - get ACPI-defined sleep state string
+ *
+ * @pm1_cnt: ACPI-defined sleep state
+ * @return: a pointer to the sleep state string.
+ */
+static inline char *acpi_ss_string(enum acpi_sleep_state state)
+{
+ char *ss_string[] = { "S0", "S1", "S2", "S3", "S4", "S5"};
+
+ return ss_string[state];
+}
+
+/**
+ * acpi_sleep_from_pm1() - get ACPI-defined sleep state from PM1_CNT register
+ *
+ * @pm1_cnt: PM1_CNT register value
+ * @return: ACPI-defined sleep state if given valid PM1_CNT register value,
+ * -EINVAL otherwise.
+ */
+static inline enum acpi_sleep_state acpi_sleep_from_pm1(u32 pm1_cnt)
+{
+ switch ((pm1_cnt & SLP_TYP) >> SLP_TYP_SHIFT) {
+ case SLP_TYP_S0:
+ return ACPI_S0;
+ case SLP_TYP_S1:
+ return ACPI_S1;
+ case SLP_TYP_S3:
+ return ACPI_S3;
+ case SLP_TYP_S4:
+ return ACPI_S4;
+ case SLP_TYP_S5:
+ return ACPI_S5;
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * chipset_prev_sleep_state() - Get chipset previous sleep state
+ *
+ * This returns chipset previous sleep state from ACPI registers.
+ * Platform codes must supply this routine in order to support ACPI S3.
+ *
+ * @return ACPI_S0/S1/S2/S3/S4/S5.
+ */
+enum acpi_sleep_state chipset_prev_sleep_state(void);
+
+/**
+ * chipset_clear_sleep_state() - Clear chipset sleep state
+ *
+ * This clears chipset sleep state in ACPI registers.
+ * Platform codes must supply this routine in order to support ACPI S3.
+ */
+void chipset_clear_sleep_state(void);
+
+struct acpi_fadt;
+/**
+ * acpi_resume() - Do ACPI S3 resume
+ *
+ * This calls U-Boot wake up assembly stub and jumps to OS's wake up vector.
+ *
+ * @fadt: FADT table pointer in the ACPI table
+ * @return: Never returns
+ */
+void acpi_resume(struct acpi_fadt *fadt);
+
+/**
+ * acpi_s3_reserve() - Reserve memory for ACPI S3 resume
+ *
+ * This copies memory where real mode interrupt handler stubs reside to the
+ * reserved place on the stack.
+ *
+ * This routine should be called by reserve_arch() before U-Boot is relocated
+ * when ACPI S3 resume is enabled.
+ *
+ * @return: 0 always
+ */
+int acpi_s3_reserve(void);
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __ASM_ACPI_S3_H__ */
diff --git a/arch/x86/include/asm/acpi_table.h b/arch/x86/include/asm/acpi_table.h
index bbd80a1dd9..dd7a946b6c 100644
--- a/arch/x86/include/asm/acpi_table.h
+++ b/arch/x86/include/asm/acpi_table.h
@@ -316,4 +316,32 @@ int acpi_create_madt_lapic_nmi(struct acpi_madt_lapic_nmi *lapic_nmi,
u8 cpu, u16 flags, u8 lint);
u32 acpi_fill_madt(u32 current);
void acpi_create_gnvs(struct acpi_global_nvs *gnvs);
+/**
+ * enter_acpi_mode() - enter into ACPI mode
+ *
+ * This programs the ACPI-defined PM1_CNT register to enable SCI interrupt
+ * so that the whole system swiches to ACPI mode.
+ *
+ * @pm1_cnt: PM1_CNT register I/O address
+ */
+void enter_acpi_mode(int pm1_cnt);
ulong write_acpi_tables(ulong start);
+
+/**
+ * acpi_find_fadt() - find ACPI FADT table in the sytem memory
+ *
+ * This routine parses the ACPI table to locate the ACPI FADT table.
+ *
+ * @return: a pointer to the ACPI FADT table in the system memory
+ */
+struct acpi_fadt *acpi_find_fadt(void);
+
+/**
+ * acpi_find_wakeup_vector() - find OS installed wake up vector address
+ *
+ * This routine parses the ACPI table to locate the wake up vector installed
+ * by the OS previously.
+ *
+ * @return: wake up vector address installed by the OS
+ */
+void *acpi_find_wakeup_vector(struct acpi_fadt *);
diff --git a/arch/x86/include/asm/arch-baytrail/acpi/sleepstates.asl b/arch/x86/include/asm/arch-baytrail/acpi/sleepstates.asl
index eb5ae76186..5600723084 100644
--- a/arch/x86/include/asm/arch-baytrail/acpi/sleepstates.asl
+++ b/arch/x86/include/asm/arch-baytrail/acpi/sleepstates.asl
@@ -8,6 +8,8 @@
*/
Name(\_S0, Package() {0x0, 0x0, 0x0, 0x0})
+#ifdef CONFIG_HAVE_ACPI_RESUME
Name(\_S3, Package() {0x5, 0x0, 0x0, 0x0})
+#endif
Name(\_S4, Package() {0x6, 0x0, 0x0, 0x0})
Name(\_S5, Package() {0x7, 0x0, 0x0, 0x0})
diff --git a/arch/x86/include/asm/arch-baytrail/iomap.h b/arch/x86/include/asm/arch-baytrail/iomap.h
index 62a91051e4..ec4e9d5212 100644
--- a/arch/x86/include/asm/arch-baytrail/iomap.h
+++ b/arch/x86/include/asm/arch-baytrail/iomap.h
@@ -35,6 +35,27 @@
#define PMC_BASE_ADDRESS 0xfed03000
#define PMC_BASE_SIZE 0x400
+#define GEN_PMCON1 0x20
+#define UART_EN (1 << 24)
+#define DISB (1 << 23)
+#define MEM_SR (1 << 21)
+#define SRS (1 << 20)
+#define CTS (1 << 19)
+#define MS4V (1 << 18)
+#define PWR_FLR (1 << 16)
+#define PME_B0_S5_DIS (1 << 15)
+#define SUS_PWR_FLR (1 << 14)
+#define WOL_EN_OVRD (1 << 13)
+#define DIS_SLP_X_STRCH_SUS_UP (1 << 12)
+#define GEN_RST_STS (1 << 9)
+#define RPS (1 << 2)
+#define AFTERG3_EN (1 << 0)
+#define GEN_PMCON2 0x24
+#define SLPSX_STR_POL_LOCK (1 << 18)
+#define BIOS_PCI_EXP_EN (1 << 10)
+#define PWRBTN_LVL (1 << 9)
+#define SMI_LOCK (1 << 4)
+
/* Power Management Unit */
#define PUNIT_BASE_ADDRESS 0xfed05000
#define PUNIT_BASE_SIZE 0x800
@@ -62,6 +83,9 @@
#define ACPI_BASE_ADDRESS 0x0400
#define ACPI_BASE_SIZE 0x80
+#define PM1_STS 0x00
+#define PM1_CNT 0x04
+
#define GPIO_BASE_ADDRESS 0x0500
#define GPIO_BASE_SIZE 0x100
diff --git a/arch/x86/include/asm/cmos_layout.h b/arch/x86/include/asm/cmos_layout.h
new file mode 100644
index 0000000000..0a0a51e72d
--- /dev/null
+++ b/arch/x86/include/asm/cmos_layout.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef __CMOS_LAYOUT_H
+#define __CMOS_LAYOUT_H
+
+/*
+ * The RTC internal registers and RAM is organized as two banks of 128 bytes
+ * each, called the standard and extended banks. The first 14 bytes of the
+ * standard bank contain the RTC time and date information along with four
+ * registers, A - D, that are used for configuration of the RTC. The extended
+ * bank contains a full 128 bytes of battery backed SRAM.
+ *
+ * For simplicity in U-Boot we only support CMOS in the standard bank, and
+ * its base address starts from offset 0x10, which leaves us 112 bytes space.
+ */
+#define CMOS_BASE 0x10
+
+/*
+ * The file records all offsets off CMOS_BASE that is currently used by
+ * U-Boot for various reasons. It is put in such a unified place in order
+ * to be consistent across platforms.
+ */
+
+/* stack address for S3 boot in a FSP configuration, 4 bytes */
+#define CMOS_FSP_STACK_ADDR CMOS_BASE
+
+#endif /* __CMOS_LAYOUT_H */
diff --git a/arch/x86/include/asm/early_cmos.h b/arch/x86/include/asm/early_cmos.h
new file mode 100644
index 0000000000..cd2634d4cd
--- /dev/null
+++ b/arch/x86/include/asm/early_cmos.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef __EARLY_CMOS_H
+#define __EARLY_CMOS_H
+
+/* CMOS actually resides in the RTC SRAM */
+#define CMOS_IO_PORT 0x70
+
+/**
+ * cmos_read8() - Get 8-bit data stored at the given address
+ *
+ * This reads from CMOS for the 8-bit data stored at the given address.
+ *
+ * @addr: RTC SRAM address
+ * @return: 8-bit data stored at the given address
+ */
+u8 cmos_read8(u8 addr);
+
+/**
+ * cmos_read16() - Get 16-bit data stored at the given address
+ *
+ * This reads from CMOS for the 16-bit data stored at the given address.
+ *
+ * @addr: RTC SRAM address
+ * @return: 16-bit data stored at the given address
+ */
+u16 cmos_read16(u8 addr);
+
+/**
+ * cmos_read32() - Get 32-bit data stored at the given address
+ *
+ * This reads from CMOS for the 32-bit data stored at the given address.
+ *
+ * @addr: RTC SRAM address
+ * @return: 32-bit data stored at the given address
+ */
+u32 cmos_read32(u8 addr);
+
+#endif /* __EARLY_CMOS_H */
diff --git a/arch/x86/include/asm/global_data.h b/arch/x86/include/asm/global_data.h
index 4570bc7a4a..93a80fe2b6 100644
--- a/arch/x86/include/asm/global_data.h
+++ b/arch/x86/include/asm/global_data.h
@@ -99,6 +99,10 @@ struct arch_global_data {
u32 high_table_ptr;
u32 high_table_limit;
#endif
+#ifdef CONFIG_HAVE_ACPI_RESUME
+ int prev_sleep_state; /* Previous sleep state ACPI_S0/1../5 */
+ ulong backup_mem; /* Backup memory address for S3 */
+#endif
};
#endif
diff --git a/arch/x86/include/asm/post.h b/arch/x86/include/asm/post.h
index 6b774bdbe8..f627663f31 100644
--- a/arch/x86/include/asm/post.h
+++ b/arch/x86/include/asm/post.h
@@ -31,10 +31,12 @@
#define POST_MRC 0x2f
#define POST_DRAM 0x30
#define POST_LAPIC 0x31
+#define POST_OS_RESUME 0x40
#define POST_RAM_FAILURE 0xea
#define POST_BIST_FAILURE 0xeb
#define POST_CAR_FAILURE 0xec
+#define POST_RESUME_FAILURE 0xed
/* Output a post code using al - value must be 0 to 0xff */
#ifdef __ASSEMBLY__
diff --git a/arch/x86/include/asm/tables.h b/arch/x86/include/asm/tables.h
index d1b2388021..9e8208ba2b 100644
--- a/arch/x86/include/asm/tables.h
+++ b/arch/x86/include/asm/tables.h
@@ -15,6 +15,7 @@
* PIRQ routing table, Multi-Processor table and ACPI table.
*/
#define ROM_TABLE_ADDR 0xf0000
+#define ROM_TABLE_END 0xfffff
#define ROM_TABLE_ALIGN 1024
diff --git a/arch/x86/include/asm/u-boot-x86.h b/arch/x86/include/asm/u-boot-x86.h
index d2d603967e..d55455f2d0 100644
--- a/arch/x86/include/asm/u-boot-x86.h
+++ b/arch/x86/include/asm/u-boot-x86.h
@@ -54,6 +54,19 @@ u32 isa_map_rom(u32 bus_addr, int size);
/* arch/x86/lib/... */
int video_bios_init(void);
+/* arch/x86/lib/fsp/... */
+
+/**
+ * fsp_save_s3_stack() - save stack address to CMOS for next S3 boot
+ *
+ * At the end of pre-relocation phase, save the new stack address
+ * to CMOS and use it as the stack on next S3 boot for fsp_init()
+ * continuation function.
+ *
+ * @return: 0 if OK, -ve on error
+ */
+int fsp_save_s3_stack(void);
+
void board_init_f_r_trampoline(ulong) __attribute__ ((noreturn));
void board_init_f_r(void) __attribute__ ((noreturn));
diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile
index d1ad37af64..fe00d7573f 100644
--- a/arch/x86/lib/Makefile
+++ b/arch/x86/lib/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_CMD_BOOTM) += bootm.o
endif
obj-y += cmd_boot.o
obj-$(CONFIG_SEABIOS) += coreboot_table.o
+obj-y += early_cmos.o
obj-$(CONFIG_EFI) += efi/
obj-y += e820.o
obj-y += gcc.o
@@ -37,6 +38,7 @@ obj-$(CONFIG_INTEL_MID) += scu.o
obj-y += sections.o
obj-y += sfi.o
obj-y += string.o
+obj-$(CONFIG_HAVE_ACPI_RESUME) += acpi_s3.o
ifndef CONFIG_QEMU
obj-$(CONFIG_GENERATE_ACPI_TABLE) += acpi_table.o
endif
diff --git a/arch/x86/lib/acpi_s3.c b/arch/x86/lib/acpi_s3.c
new file mode 100644
index 0000000000..3175da828b
--- /dev/null
+++ b/arch/x86/lib/acpi_s3.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/acpi_s3.h>
+#include <asm/acpi_table.h>
+#include <asm/post.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static void asmlinkage (*acpi_do_wakeup)(void *vector) = (void *)WAKEUP_BASE;
+
+static void acpi_jump_to_wakeup(void *vector)
+{
+ /* Copy wakeup trampoline in place */
+ memcpy((void *)WAKEUP_BASE, __wakeup, __wakeup_size);
+
+ printf("Jumping to OS waking vector %p\n", vector);
+ acpi_do_wakeup(vector);
+}
+
+void acpi_resume(struct acpi_fadt *fadt)
+{
+ void *wake_vec;
+
+ /* Turn on ACPI mode for S3 */
+ enter_acpi_mode(fadt->pm1a_cnt_blk);
+
+ wake_vec = acpi_find_wakeup_vector(fadt);
+
+ /*
+ * Restore the memory content starting from address 0x1000 which is
+ * used for the real mode interrupt handler stubs.
+ */
+ memcpy((void *)0x1000, (const void *)gd->arch.backup_mem,
+ S3_RESERVE_SIZE);
+
+ post_code(POST_OS_RESUME);
+ acpi_jump_to_wakeup(wake_vec);
+}
+
+int acpi_s3_reserve(void)
+{
+ /* adjust stack pointer for ACPI S3 resume backup memory */
+ gd->start_addr_sp -= S3_RESERVE_SIZE;
+ gd->arch.backup_mem = gd->start_addr_sp;
+
+ gd->start_addr_sp &= ~0xf;
+
+ /*
+ * U-Boot sets up the real mode interrupt handler stubs starting from
+ * address 0x1000. In most cases, the first 640K (0x00000 - 0x9ffff)
+ * system memory is reported as system RAM in E820 table to the OS.
+ * (see install_e820_map() implementation for each platform). So OS
+ * can use these memories whatever it wants.
+ *
+ * If U-Boot is in an S3 resume path, care must be taken not to corrupt
+ * these memorie otherwise OS data gets lost. Testing shows that, on
+ * Microsoft Windows 10 on Intel Baytrail its wake up vector happens to
+ * be installed at the same address 0x1000. While on Linux its wake up
+ * vector does not overlap this memory range, but after resume kernel
+ * checks low memory range per config option CONFIG_X86_RESERVE_LOW
+ * which is 64K by default to see whether a memory corruption occurs
+ * during the suspend/resume (it's harmless, but warnings are shown
+ * in the kernel dmesg logs).
+ *
+ * We cannot simply mark the these memory as reserved in E820 table
+ * because such configuration makes GRUB complain: unable to allocate
+ * real mode page. Hence we choose to back up these memories to the
+ * place where we reserved on our stack for our S3 resume work.
+ * Before jumping to OS wake up vector, we need restore the original
+ * content there (see acpi_resume() above).
+ */
+ if (gd->arch.prev_sleep_state == ACPI_S3)
+ memcpy((void *)gd->arch.backup_mem, (const void *)0x1000,
+ S3_RESERVE_SIZE);
+
+ return 0;
+}
diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c
index 355456dc19..01d5b6fff0 100644
--- a/arch/x86/lib/acpi_table.c
+++ b/arch/x86/lib/acpi_table.c
@@ -304,8 +304,10 @@ static void acpi_create_mcfg(struct acpi_mcfg *mcfg)
header->checksum = table_compute_checksum((void *)mcfg, header->length);
}
-static void enter_acpi_mode(int pm1_cnt)
+void enter_acpi_mode(int pm1_cnt)
{
+ u16 val = inw(pm1_cnt);
+
/*
* PM1_CNT register bit0 selects the power management event to be
* either an SCI or SMI interrupt. When this bit is set, then power
@@ -320,7 +322,7 @@ static void enter_acpi_mode(int pm1_cnt)
* system, and expose ourselves to OSPM as working under ACPI mode
* already, turn this bit on.
*/
- outw(PM1_CNT_SCI_EN, pm1_cnt);
+ outw(val | PM1_CNT_SCI_EN, pm1_cnt);
}
/*
@@ -438,3 +440,81 @@ ulong write_acpi_tables(ulong start)
return current;
}
+
+static struct acpi_rsdp *acpi_valid_rsdp(struct acpi_rsdp *rsdp)
+{
+ if (strncmp((char *)rsdp, RSDP_SIG, sizeof(RSDP_SIG) - 1) != 0)
+ return NULL;
+
+ debug("Looking on %p for valid checksum\n", rsdp);
+
+ if (table_compute_checksum((void *)rsdp, 20) != 0)
+ return NULL;
+ debug("acpi rsdp checksum 1 passed\n");
+
+ if ((rsdp->revision > 1) &&
+ (table_compute_checksum((void *)rsdp, rsdp->length) != 0))
+ return NULL;
+ debug("acpi rsdp checksum 2 passed\n");
+
+ return rsdp;
+}
+
+struct acpi_fadt *acpi_find_fadt(void)
+{
+ char *p, *end;
+ struct acpi_rsdp *rsdp = NULL;
+ struct acpi_rsdt *rsdt;
+ struct acpi_fadt *fadt = NULL;
+ int i;
+
+ /* Find RSDP */
+ for (p = (char *)ROM_TABLE_ADDR; p < (char *)ROM_TABLE_END; p += 16) {
+ rsdp = acpi_valid_rsdp((struct acpi_rsdp *)p);
+ if (rsdp)
+ break;
+ }
+
+ if (rsdp == NULL)
+ return NULL;
+
+ debug("RSDP found at %p\n", rsdp);
+ rsdt = (struct acpi_rsdt *)rsdp->rsdt_address;
+
+ end = (char *)rsdt + rsdt->header.length;
+ debug("RSDT found at %p ends at %p\n", rsdt, end);
+
+ for (i = 0; ((char *)&rsdt->entry[i]) < end; i++) {
+ fadt = (struct acpi_fadt *)rsdt->entry[i];
+ if (strncmp((char *)fadt, "FACP", 4) == 0)
+ break;
+ fadt = NULL;
+ }
+
+ if (fadt == NULL)
+ return NULL;
+
+ debug("FADT found at %p\n", fadt);
+ return fadt;
+}
+
+void *acpi_find_wakeup_vector(struct acpi_fadt *fadt)
+{
+ struct acpi_facs *facs;
+ void *wake_vec;
+
+ debug("Trying to find the wakeup vector...\n");
+
+ facs = (struct acpi_facs *)fadt->firmware_ctrl;
+
+ if (facs == NULL) {
+ debug("No FACS found, wake up from S3 not possible.\n");
+ return NULL;
+ }
+
+ debug("FACS found at %p\n", facs);
+ wake_vec = (void *)facs->firmware_waking_vector;
+ debug("OS waking vector is %p\n", wake_vec);
+
+ return wake_vec;
+}
diff --git a/arch/x86/lib/bootm.c b/arch/x86/lib/bootm.c
index 75bab90225..ecd4f4e6c6 100644
--- a/arch/x86/lib/bootm.c
+++ b/arch/x86/lib/bootm.c
@@ -10,6 +10,8 @@
#include <common.h>
#include <command.h>
+#include <dm/device.h>
+#include <dm/root.h>
#include <errno.h>
#include <fdt_support.h>
#include <image.h>
@@ -46,6 +48,13 @@ void bootm_announce_and_cleanup(void)
#ifdef CONFIG_BOOTSTAGE_REPORT
bootstage_report();
#endif
+
+ /*
+ * Call remove function of all devices with a removal flag set.
+ * This may be useful for last-stage operations, like cancelling
+ * of DMA operation or releasing device internal buffers.
+ */
+ dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL);
}
#if defined(CONFIG_OF_LIBFDT) && !defined(CONFIG_OF_NO_KERNEL)
diff --git a/arch/x86/lib/coreboot_table.c b/arch/x86/lib/coreboot_table.c
index ceab3cf5e4..b1b4cd9613 100644
--- a/arch/x86/lib/coreboot_table.c
+++ b/arch/x86/lib/coreboot_table.c
@@ -6,6 +6,7 @@
#include <common.h>
#include <vbe.h>
+#include <asm/acpi_s3.h>
#include <asm/coreboot_tables.h>
#include <asm/e820.h>
@@ -19,7 +20,11 @@ int high_table_reserve(void)
gd->arch.high_table_ptr = gd->start_addr_sp;
/* clear the memory */
- memset((void *)gd->arch.high_table_ptr, 0, CONFIG_HIGH_TABLE_SIZE);
+#ifdef CONFIG_HAVE_ACPI_RESUME
+ if (gd->arch.prev_sleep_state != ACPI_S3)
+#endif
+ memset((void *)gd->arch.high_table_ptr, 0,
+ CONFIG_HIGH_TABLE_SIZE);
gd->start_addr_sp &= ~0xf;
diff --git a/arch/x86/lib/early_cmos.c b/arch/x86/lib/early_cmos.c
new file mode 100644
index 0000000000..fa0b3273a8
--- /dev/null
+++ b/arch/x86/lib/early_cmos.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+/*
+ * This library provides CMOS (inside RTC SRAM) access routines at a very
+ * early stage when driver model is not available yet. Only read access is
+ * provided. The 16-bit/32-bit read are compatible with driver model RTC
+ * uclass write ops, that data is stored in little-endian mode.
+ */
+
+#include <common.h>
+#include <asm/early_cmos.h>
+#include <asm/io.h>
+
+u8 cmos_read8(u8 addr)
+{
+ outb(addr, CMOS_IO_PORT);
+
+ return inb(CMOS_IO_PORT + 1);
+}
+
+u16 cmos_read16(u8 addr)
+{
+ u16 value = 0;
+ u16 data;
+ int i;
+
+ for (i = 0; i < sizeof(value); i++) {
+ data = cmos_read8(addr + i);
+ value |= data << (i << 3);
+ }
+
+ return value;
+}
+
+u32 cmos_read32(u8 addr)
+{
+ u32 value = 0;
+ u32 data;
+ int i;
+
+ for (i = 0; i < sizeof(value); i++) {
+ data = cmos_read8(addr + i);
+ value |= data << (i << 3);
+ }
+
+ return value;
+}
diff --git a/arch/x86/lib/fsp/fsp_common.c b/arch/x86/lib/fsp/fsp_common.c
index 66a388d601..3397bb83ea 100644
--- a/arch/x86/lib/fsp/fsp_common.c
+++ b/arch/x86/lib/fsp/fsp_common.c
@@ -5,7 +5,12 @@
*/
#include <common.h>
+#include <dm.h>
#include <errno.h>
+#include <rtc.h>
+#include <asm/acpi_s3.h>
+#include <asm/cmos_layout.h>
+#include <asm/early_cmos.h>
#include <asm/io.h>
#include <asm/mrccache.h>
#include <asm/post.h>
@@ -75,9 +80,41 @@ static __maybe_unused void *fsp_prepare_mrc_cache(void)
return cache->data;
}
+#ifdef CONFIG_HAVE_ACPI_RESUME
+int fsp_save_s3_stack(void)
+{
+ struct udevice *dev;
+ int ret;
+
+ if (gd->arch.prev_sleep_state == ACPI_S3)
+ return 0;
+
+ ret = uclass_get_device(UCLASS_RTC, 0, &dev);
+ if (ret) {
+ debug("Cannot find RTC: err=%d\n", ret);
+ return -ENODEV;
+ }
+
+ /* Save the stack address to CMOS */
+ ret = rtc_write32(dev, CMOS_FSP_STACK_ADDR, gd->start_addr_sp);
+ if (ret) {
+ debug("Save stack address to CMOS: err=%d\n", ret);
+ return -EIO;
+ }
+
+ return 0;
+}
+#endif
+
int arch_fsp_init(void)
{
void *nvs;
+ int stack = CONFIG_FSP_TEMP_RAM_ADDR;
+ int boot_mode = BOOT_FULL_CONFIG;
+#ifdef CONFIG_HAVE_ACPI_RESUME
+ int prev_sleep_state = chipset_prev_sleep_state();
+ gd->arch.prev_sleep_state = prev_sleep_state;
+#endif
if (!gd->arch.hob_list) {
#ifdef CONFIG_ENABLE_MRC_CACHE
@@ -85,12 +122,36 @@ int arch_fsp_init(void)
#else
nvs = NULL;
#endif
+
+#ifdef CONFIG_HAVE_ACPI_RESUME
+ if (prev_sleep_state == ACPI_S3) {
+ if (nvs == NULL) {
+ /* If waking from S3 and no cache then */
+ debug("No MRC cache found in S3 resume path\n");
+ post_code(POST_RESUME_FAILURE);
+ /* Clear Sleep Type */
+ chipset_clear_sleep_state();
+ /* Reboot */
+ debug("Rebooting..\n");
+ reset_cpu(0);
+ /* Should not reach here.. */
+ panic("Reboot System");
+ }
+
+ /*
+ * DM is not avaiable yet at this point, hence call
+ * CMOS access library which does not depend on DM.
+ */
+ stack = cmos_read32(CMOS_FSP_STACK_ADDR);
+ boot_mode = BOOT_ON_S3_RESUME;
+ }
+#endif
/*
* The first time we enter here, call fsp_init().
* Note the execution does not return to this function,
* instead it jumps to fsp_continue().
*/
- fsp_init(CONFIG_FSP_TEMP_RAM_ADDR, BOOT_FULL_CONFIG, nvs);
+ fsp_init(stack, boot_mode, nvs);
} else {
/*
* The second time we enter here, adjust the size of malloc()
diff --git a/arch/x86/lib/fsp/fsp_dram.c b/arch/x86/lib/fsp/fsp_dram.c
index 8b880cd594..1a7af576d5 100644
--- a/arch/x86/lib/fsp/fsp_dram.c
+++ b/arch/x86/lib/fsp/fsp_dram.c
@@ -92,5 +92,17 @@ unsigned install_e820_map(unsigned max_entries, struct e820entry *entries)
entries[num_entries].type = E820_RESERVED;
num_entries++;
+#ifdef CONFIG_HAVE_ACPI_RESUME
+ /*
+ * Everything between U-Boot's stack and ram top needs to be
+ * reserved in order for ACPI S3 resume to work.
+ */
+ entries[num_entries].addr = gd->start_addr_sp - CONFIG_STACK_SIZE;
+ entries[num_entries].size = gd->ram_top - gd->start_addr_sp + \
+ CONFIG_STACK_SIZE;
+ entries[num_entries].type = E820_RESERVED;
+ num_entries++;
+#endif
+
return num_entries;
}