diff options
Diffstat (limited to 'arch/x86')
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; } |