From 4372c111d49f1fa0fec0c8645c23a6266617e69d Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Fri, 21 Apr 2017 07:24:28 -0700 Subject: [PATCH 01/29] x86: acpi: Add Kconfig option and header file for ACPI resume This introduces a Kconfig option for ACPI S3 resume, as well as a header file to include anything related to ACPI S3 resume. Signed-off-by: Bin Meng Reviewed-by: Simon Glass Tested-by: Stefan Roese --- arch/x86/Kconfig | 12 +++++++ arch/x86/include/asm/acpi_s3.h | 64 ++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 arch/x86/include/asm/acpi_s3.h diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 9ead3ebccf..b568852406 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -589,6 +589,18 @@ 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 MAX_PIRQ_LINKS int default 8 diff --git a/arch/x86/include/asm/acpi_s3.h b/arch/x86/include/asm/acpi_s3.h new file mode 100644 index 0000000000..78c53ea2fb --- /dev/null +++ b/arch/x86/include/asm/acpi_s3.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2017, Bin Meng + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __ASM_ACPI_S3_H__ +#define __ASM_ACPI_S3_H__ + +/* 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 + +enum acpi_sleep_state { + ACPI_S0, + ACPI_S1, + ACPI_S2, + ACPI_S3, + ACPI_S4, + ACPI_S5, +}; + +/** + * 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; +} + +#endif /* __ASM_ACPI_S3_H__ */ From fcf2fba472ede05df2e7ed5fb141b8caf4f09681 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Fri, 21 Apr 2017 07:24:29 -0700 Subject: [PATCH 02/29] x86: baytrail: acpi: Add APIs for determining/clearing sleep state This adds APIs for determining previous sleep state from ACPI I/O registers, as well as clearing sleep state on BayTrail SoC. Signed-off-by: Bin Meng Reviewed-by: Simon Glass Tested-by: Stefan Roese --- arch/x86/cpu/baytrail/acpi.c | 47 ++++++++++++++++++++++ arch/x86/include/asm/arch-baytrail/iomap.h | 24 +++++++++++ 2 files changed, 71 insertions(+) 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 #include #include +#include #include +#include #include #include #include @@ -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/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 From b7ef3bffffe05002e88419d4a3bfe8a56f937e70 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Fri, 21 Apr 2017 07:24:30 -0700 Subject: [PATCH 03/29] x86: Add post codes for OS resume This adds OS_RESUME (0x40) and RESUME_FAILURE (0xed) post codes. Signed-off-by: Bin Meng Reviewed-by: Simon Glass Tested-by: Stefan Roese --- arch/x86/include/asm/post.h | 2 ++ 1 file changed, 2 insertions(+) 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__ From 1206723b6e597f1aa946b800fc59059db91908bc Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Fri, 21 Apr 2017 07:24:31 -0700 Subject: [PATCH 04/29] x86: fsp: acpi: Pass different boot mode to FSP init When ACPI S3 resume is turned on, we should pass different boot mode to FSP init instead of default BOOT_FULL_CONFIG. Signed-off-by: Bin Meng Reviewed-by: Simon Glass Tested-by: Stefan Roese --- arch/x86/include/asm/acpi_s3.h | 18 ++++++++++++++++++ arch/x86/lib/fsp/fsp_common.c | 26 +++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/arch/x86/include/asm/acpi_s3.h b/arch/x86/include/asm/acpi_s3.h index 78c53ea2fb..ec052112f3 100644 --- a/arch/x86/include/asm/acpi_s3.h +++ b/arch/x86/include/asm/acpi_s3.h @@ -61,4 +61,22 @@ static inline enum acpi_sleep_state acpi_sleep_from_pm1(u32 pm1_cnt) 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); + #endif /* __ASM_ACPI_S3_H__ */ diff --git a/arch/x86/lib/fsp/fsp_common.c b/arch/x86/lib/fsp/fsp_common.c index 66a388d601..cc42cad20b 100644 --- a/arch/x86/lib/fsp/fsp_common.c +++ b/arch/x86/lib/fsp/fsp_common.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -78,6 +79,10 @@ static __maybe_unused void *fsp_prepare_mrc_cache(void) int arch_fsp_init(void) { void *nvs; + int boot_mode = BOOT_FULL_CONFIG; +#ifdef CONFIG_HAVE_ACPI_RESUME + int prev_sleep_state = chipset_prev_sleep_state(); +#endif if (!gd->arch.hob_list) { #ifdef CONFIG_ENABLE_MRC_CACHE @@ -85,12 +90,31 @@ 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"); + } + + 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(CONFIG_FSP_TEMP_RAM_ADDR, boot_mode, nvs); } else { /* * The second time we enter here, adjust the size of malloc() From b727961b0706df57d408fe609cfe098a91626da3 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Fri, 21 Apr 2017 07:24:32 -0700 Subject: [PATCH 05/29] x86: Store and display previous sleep state Add one member in the global data to store previous sleep state, and display the state during boot in print_cpuinfo(). Signed-off-by: Bin Meng Reviewed-by: Simon Glass Tested-by: Stefan Roese --- arch/x86/cpu/cpu.c | 6 ++++++ arch/x86/include/asm/acpi_s3.h | 13 +++++++++++++ arch/x86/include/asm/global_data.h | 3 +++ arch/x86/lib/fsp/fsp_common.c | 1 + 4 files changed, 23 insertions(+) diff --git a/arch/x86/cpu/cpu.c b/arch/x86/cpu/cpu.c index 8fa6953588..7222ec899d 100644 --- a/arch/x86/cpu/cpu.c +++ b/arch/x86/cpu/cpu.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -179,6 +180,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; } diff --git a/arch/x86/include/asm/acpi_s3.h b/arch/x86/include/asm/acpi_s3.h index ec052112f3..10cedde39a 100644 --- a/arch/x86/include/asm/acpi_s3.h +++ b/arch/x86/include/asm/acpi_s3.h @@ -36,6 +36,19 @@ enum acpi_sleep_state { 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 * diff --git a/arch/x86/include/asm/global_data.h b/arch/x86/include/asm/global_data.h index 4570bc7a4a..7d5efea224 100644 --- a/arch/x86/include/asm/global_data.h +++ b/arch/x86/include/asm/global_data.h @@ -99,6 +99,9 @@ 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 */ +#endif }; #endif diff --git a/arch/x86/lib/fsp/fsp_common.c b/arch/x86/lib/fsp/fsp_common.c index cc42cad20b..f2d50acbf4 100644 --- a/arch/x86/lib/fsp/fsp_common.c +++ b/arch/x86/lib/fsp/fsp_common.c @@ -82,6 +82,7 @@ int arch_fsp_init(void) 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) { From e652e1304a9f3b59b3e47005ea19b5b6a3e799d1 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Fri, 21 Apr 2017 07:24:33 -0700 Subject: [PATCH 06/29] x86: baytrail: Conditionally report S3 in the ACPI table When U-Boot is built without ACPI S3 support, it should not report S3 in the ACPI table otherwise when kernel does STR it won't work. Signed-off-by: Bin Meng Reviewed-by: Simon Glass Tested-by: Stefan Roese --- arch/x86/include/asm/arch-baytrail/acpi/sleepstates.asl | 2 ++ arch/x86/include/asm/global_data.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) 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/global_data.h b/arch/x86/include/asm/global_data.h index 7d5efea224..b4ca4731a5 100644 --- a/arch/x86/include/asm/global_data.h +++ b/arch/x86/include/asm/global_data.h @@ -100,7 +100,7 @@ struct arch_global_data { u32 high_table_limit; #endif #ifdef CONFIG_HAVE_ACPI_RESUME - int prev_sleep_state; /* Previous sleep state */ + int prev_sleep_state; /* Previous sleep state ACPI_S0/1../5 */ #endif }; From 7d0d2efef82dcb88030a960aef09290e6e49f771 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Fri, 21 Apr 2017 07:24:34 -0700 Subject: [PATCH 07/29] x86: fsp: Mark memory used by U-Boot as reserved in the E820 table for S3 U-Boot itself as well as everything that is consumed by U-Boot (like heap, stack, dtb, etc) needs to be reserved and reported in the E820 table when S3 resume is on. Signed-off-by: Bin Meng Reviewed-by: Simon Glass Tested-by: Stefan Roese --- arch/x86/Kconfig | 8 ++++++++ arch/x86/lib/fsp/fsp_dram.c | 12 ++++++++++++ 2 files changed, 20 insertions(+) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index b568852406..5322eff7fa 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -601,6 +601,14 @@ config HAVE_ACPI_RESUME is done, U-Boot needs to find out the wakeup vector provided by OSes and jump there. +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/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; } From 2b2d666f9caca189e4014f8e23088b0afc360e22 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Fri, 21 Apr 2017 07:24:35 -0700 Subject: [PATCH 08/29] x86: acpi: Add wake up assembly stub This adds a wake up stub before jumping to OS wake up vector. Signed-off-by: Bin Meng Reviewed-by: Simon Glass Tested-by: Stefan Roese --- arch/x86/cpu/Makefile | 1 + arch/x86/cpu/wakeup.S | 78 ++++++++++++++++++++++++++++++++++ arch/x86/include/asm/acpi_s3.h | 9 ++++ 3 files changed, 88 insertions(+) create mode 100644 arch/x86/cpu/wakeup.S 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/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 + * + * From coreboot src/arch/x86/wakeup.S + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#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/include/asm/acpi_s3.h b/arch/x86/include/asm/acpi_s3.h index 10cedde39a..a06466c01e 100644 --- a/arch/x86/include/asm/acpi_s3.h +++ b/arch/x86/include/asm/acpi_s3.h @@ -7,6 +7,8 @@ #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) @@ -27,6 +29,11 @@ #define SLP_TYP_S4 6 #define SLP_TYP_S5 7 +#ifndef __ASSEMBLY__ + +extern char __wakeup[]; +extern int __wakeup_size; + enum acpi_sleep_state { ACPI_S0, ACPI_S1, @@ -92,4 +99,6 @@ enum acpi_sleep_state chipset_prev_sleep_state(void); */ void chipset_clear_sleep_state(void); +#endif /* __ASSEMBLY__ */ + #endif /* __ASM_ACPI_S3_H__ */ From e76bf38f180b0c9325b17506f1518329d82cbed8 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Fri, 21 Apr 2017 07:24:36 -0700 Subject: [PATCH 09/29] x86: acpi: Add one API to find OS wakeup vector This adds one API acpi_find_wakeup_vector() to locate OS wakeup vector from the ACPI FACS table, to be used in the S3 boot path. Signed-off-by: Bin Meng Reviewed-by: Simon Glass Tested-by: Stefan Roese --- arch/x86/include/asm/acpi_table.h | 10 +++++ arch/x86/include/asm/tables.h | 1 + arch/x86/lib/acpi_table.c | 72 +++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) diff --git a/arch/x86/include/asm/acpi_table.h b/arch/x86/include/asm/acpi_table.h index bbd80a1dd9..20b906c616 100644 --- a/arch/x86/include/asm/acpi_table.h +++ b/arch/x86/include/asm/acpi_table.h @@ -317,3 +317,13 @@ int acpi_create_madt_lapic_nmi(struct acpi_madt_lapic_nmi *lapic_nmi, u32 acpi_fill_madt(u32 current); void acpi_create_gnvs(struct acpi_global_nvs *gnvs); ulong write_acpi_tables(ulong start); + +/** + * 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(void); 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/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index 355456dc19..f1183452f8 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -438,3 +438,75 @@ 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; +} + +void *acpi_find_wakeup_vector(void) +{ + char *p, *end; + struct acpi_rsdp *rsdp = NULL; + struct acpi_rsdt *rsdt; + struct acpi_fadt *fadt = NULL; + struct acpi_facs *facs; + void *wake_vec; + int i; + + debug("Trying to find the wakeup vector...\n"); + + /* 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); + 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; +} From 3a34cae01160d207d50db267585b963cf0a0018d Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Fri, 21 Apr 2017 07:24:37 -0700 Subject: [PATCH 10/29] x86: acpi: Resume OS if resume vector is found In an S3 resume path, U-Boot does everything like a cold boot except in the last_stage_init() it jumps to the OS resume vector. Signed-off-by: Bin Meng Reviewed-by: Simon Glass Tested-by: Stefan Roese --- arch/x86/cpu/cpu.c | 8 ++++++++ arch/x86/include/asm/acpi_s3.h | 10 ++++++++++ arch/x86/lib/Makefile | 1 + arch/x86/lib/acpi_s3.c | 26 ++++++++++++++++++++++++++ 4 files changed, 45 insertions(+) create mode 100644 arch/x86/lib/acpi_s3.c diff --git a/arch/x86/cpu/cpu.c b/arch/x86/cpu/cpu.c index 7222ec899d..f686ad49c9 100644 --- a/arch/x86/cpu/cpu.c +++ b/arch/x86/cpu/cpu.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -204,6 +205,13 @@ __weak void board_final_cleanup(void) int last_stage_init(void) { +#if CONFIG_HAVE_ACPI_RESUME + void *wake_vector = acpi_find_wakeup_vector(); + + if (wake_vector != NULL && gd->arch.prev_sleep_state == ACPI_S3) + acpi_resume(wake_vector); +#endif + write_tables(); board_final_cleanup(); diff --git a/arch/x86/include/asm/acpi_s3.h b/arch/x86/include/asm/acpi_s3.h index a06466c01e..b8d14f470d 100644 --- a/arch/x86/include/asm/acpi_s3.h +++ b/arch/x86/include/asm/acpi_s3.h @@ -99,6 +99,16 @@ enum acpi_sleep_state chipset_prev_sleep_state(void); */ void chipset_clear_sleep_state(void); +/** + * acpi_resume() - Do ACPI S3 resume + * + * This calls U-Boot wake up assembly stub and jumps to OS's wake up vector. + * + * @wake_vec: OS wake up vector + * @return: Never returns + */ +void acpi_resume(void *wake_vec); + #endif /* __ASSEMBLY__ */ #endif /* __ASM_ACPI_S3_H__ */ diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile index d1ad37af64..21f59a0485 100644 --- a/arch/x86/lib/Makefile +++ b/arch/x86/lib/Makefile @@ -37,6 +37,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..f679c06deb --- /dev/null +++ b/arch/x86/lib/acpi_s3.c @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2017, Bin Meng + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +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(void *wake_vec) +{ + post_code(POST_OS_RESUME); + acpi_jump_to_wakeup(wake_vec); +} From 9f1fad1e365f413e4f6f5a608a70be0e6ba95dc8 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Fri, 21 Apr 2017 07:24:38 -0700 Subject: [PATCH 11/29] x86: Add an early CMOS access library This adds a library that provides CMOS (inside RTC SRAM) access at a very early stage when driver model is not available yet. Signed-off-by: Bin Meng Reviewed-by: Simon Glass Tested-by: Stefan Roese --- arch/x86/include/asm/early_cmos.h | 43 ++++++++++++++++++++++++++ arch/x86/lib/Makefile | 1 + arch/x86/lib/early_cmos.c | 51 +++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 arch/x86/include/asm/early_cmos.h create mode 100644 arch/x86/lib/early_cmos.c 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 + * + * 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/lib/Makefile b/arch/x86/lib/Makefile index 21f59a0485..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 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 + * + * 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 +#include +#include + +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; +} From ba65808e7d0699e053c9892983312a11d4e2a30a Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Fri, 21 Apr 2017 07:24:39 -0700 Subject: [PATCH 12/29] x86: fsp: 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. Signed-off-by: Bin Meng Reviewed-by: Simon Glass Tested-by: Stefan Roese --- arch/x86/cpu/cpu.c | 8 +++++++ arch/x86/include/asm/cmos_layout.h | 31 ++++++++++++++++++++++++ arch/x86/include/asm/u-boot-x86.h | 13 ++++++++++ arch/x86/lib/fsp/fsp_common.c | 38 +++++++++++++++++++++++++++++- 4 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 arch/x86/include/asm/cmos_layout.h diff --git a/arch/x86/cpu/cpu.c b/arch/x86/cpu/cpu.c index f686ad49c9..f86eaa9df2 100644 --- a/arch/x86/cpu/cpu.c +++ b/arch/x86/cpu/cpu.c @@ -278,6 +278,14 @@ int reserve_arch(void) high_table_reserve(); #endif +#if defined(CONFIG_HAVE_ACPI_RESUME) && defined(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 + return 0; } #endif 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 + * + * 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/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/fsp/fsp_common.c b/arch/x86/lib/fsp/fsp_common.c index f2d50acbf4..3397bb83ea 100644 --- a/arch/x86/lib/fsp/fsp_common.c +++ b/arch/x86/lib/fsp/fsp_common.c @@ -5,8 +5,12 @@ */ #include +#include #include +#include #include +#include +#include #include #include #include @@ -76,9 +80,36 @@ 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(); @@ -107,6 +138,11 @@ int arch_fsp_init(void) 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 @@ -115,7 +151,7 @@ int arch_fsp_init(void) * Note the execution does not return to this function, * instead it jumps to fsp_continue(). */ - fsp_init(CONFIG_FSP_TEMP_RAM_ADDR, boot_mode, nvs); + fsp_init(stack, boot_mode, nvs); } else { /* * The second time we enter here, adjust the size of malloc() From 95e50dd197cb86641a4ad06a18d5a4c57ea0a6f4 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Fri, 21 Apr 2017 07:24:40 -0700 Subject: [PATCH 13/29] x86: Do not clear high table area for S3 When SeaBIOS is being used, U-Boot reserves a memory area to be used for configuration tables like ACPI. But it should not be cleared otherwise ACPI table will be missing. Signed-off-by: Bin Meng Reviewed-by: Simon Glass Tested-by: Stefan Roese --- arch/x86/lib/coreboot_table.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 #include +#include #include #include @@ -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; From bffd798136deb4622059b5aeb33c375fad554ac7 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Fri, 21 Apr 2017 07:24:41 -0700 Subject: [PATCH 14/29] x86: Adjust board_final_cleanup() order Call board_final_cleanup() before write_tables(), so that anything done in board_final_cleanup() on a normal boot path is also done on an S3 resume path. Signed-off-by: Bin Meng Reviewed-by: Simon Glass Tested-by: Stefan Roese --- arch/x86/cpu/cpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/cpu/cpu.c b/arch/x86/cpu/cpu.c index f86eaa9df2..c9fc7e4ebf 100644 --- a/arch/x86/cpu/cpu.c +++ b/arch/x86/cpu/cpu.c @@ -205,6 +205,8 @@ __weak void board_final_cleanup(void) int last_stage_init(void) { + board_final_cleanup(); + #if CONFIG_HAVE_ACPI_RESUME void *wake_vector = acpi_find_wakeup_vector(); @@ -214,8 +216,6 @@ int last_stage_init(void) write_tables(); - board_final_cleanup(); - return 0; } #endif From b208d1915f1bdba93678b51b87ae214d9e2ca7a3 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Fri, 21 Apr 2017 07:24:42 -0700 Subject: [PATCH 15/29] x86: apci: Change PM1_CNT register access to RMW In enter_acpi_mode() PM1_CNT register is changed to PM1_CNT_SCI_EN directly without preserving its previous value. Update to change the register access to read-modify-write (RMW). Signed-off-by: Bin Meng Reviewed-by: Simon Glass Tested-by: Stefan Roese --- arch/x86/lib/acpi_table.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index f1183452f8..8be8120e3d 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -306,6 +306,8 @@ static void acpi_create_mcfg(struct acpi_mcfg *mcfg) static 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); } /* From 995727850f8e54547bb26285ef9e2aa73a5c71a5 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Fri, 21 Apr 2017 07:24:43 -0700 Subject: [PATCH 16/29] x86: acpi: Make enter_acpi_mode() public enter_acpi_mode() is useful on other boot path like S3 resume, so make it public. Signed-off-by: Bin Meng Reviewed-by: Simon Glass Tested-by: Stefan Roese --- arch/x86/include/asm/acpi_table.h | 9 +++++++++ arch/x86/lib/acpi_table.c | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/arch/x86/include/asm/acpi_table.h b/arch/x86/include/asm/acpi_table.h index 20b906c616..e96409b860 100644 --- a/arch/x86/include/asm/acpi_table.h +++ b/arch/x86/include/asm/acpi_table.h @@ -316,6 +316,15 @@ 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); /** diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index 8be8120e3d..87a71ca492 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -304,7 +304,7 @@ 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); From 0f4e25887dfb3b54b21430340c511f7fd54bc955 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Fri, 21 Apr 2017 07:24:44 -0700 Subject: [PATCH 17/29] x86: acpi: Refactor acpi_resume() To do something more in acpi_resume() like turning on ACPI mode, we need locate ACPI FADT table pointer first. But currently this is done in acpi_find_wakeup_vector(). This changes acpi_resume() signature to accept ACPI FADT pointer as the parameter. A new API acpi_find_fadt() is introduced, and acpi_find_wakeup_vector() is updated to use FADT pointer as the parameter as well. Signed-off-by: Bin Meng Reviewed-by: Simon Glass Tested-by: Stefan Roese --- arch/x86/cpu/cpu.c | 6 +++--- arch/x86/include/asm/acpi_s3.h | 5 +++-- arch/x86/include/asm/acpi_table.h | 11 ++++++++++- arch/x86/lib/acpi_s3.c | 7 ++++++- arch/x86/lib/acpi_table.c | 16 +++++++++++----- 5 files changed, 33 insertions(+), 12 deletions(-) diff --git a/arch/x86/cpu/cpu.c b/arch/x86/cpu/cpu.c index c9fc7e4ebf..dfe624f54a 100644 --- a/arch/x86/cpu/cpu.c +++ b/arch/x86/cpu/cpu.c @@ -208,10 +208,10 @@ int last_stage_init(void) board_final_cleanup(); #if CONFIG_HAVE_ACPI_RESUME - void *wake_vector = acpi_find_wakeup_vector(); + struct acpi_fadt *fadt = acpi_find_fadt(); - if (wake_vector != NULL && gd->arch.prev_sleep_state == ACPI_S3) - acpi_resume(wake_vector); + if (fadt != NULL && gd->arch.prev_sleep_state == ACPI_S3) + acpi_resume(fadt); #endif write_tables(); diff --git a/arch/x86/include/asm/acpi_s3.h b/arch/x86/include/asm/acpi_s3.h index b8d14f470d..1ad20f4fcb 100644 --- a/arch/x86/include/asm/acpi_s3.h +++ b/arch/x86/include/asm/acpi_s3.h @@ -99,15 +99,16 @@ enum acpi_sleep_state chipset_prev_sleep_state(void); */ 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. * - * @wake_vec: OS wake up vector + * @fadt: FADT table pointer in the ACPI table * @return: Never returns */ -void acpi_resume(void *wake_vec); +void acpi_resume(struct acpi_fadt *fadt); #endif /* __ASSEMBLY__ */ diff --git a/arch/x86/include/asm/acpi_table.h b/arch/x86/include/asm/acpi_table.h index e96409b860..dd7a946b6c 100644 --- a/arch/x86/include/asm/acpi_table.h +++ b/arch/x86/include/asm/acpi_table.h @@ -327,6 +327,15 @@ void acpi_create_gnvs(struct acpi_global_nvs *gnvs); 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 * @@ -335,4 +344,4 @@ ulong write_acpi_tables(ulong start); * * @return: wake up vector address installed by the OS */ -void *acpi_find_wakeup_vector(void); +void *acpi_find_wakeup_vector(struct acpi_fadt *); diff --git a/arch/x86/lib/acpi_s3.c b/arch/x86/lib/acpi_s3.c index f679c06deb..e5cc3b0766 100644 --- a/arch/x86/lib/acpi_s3.c +++ b/arch/x86/lib/acpi_s3.c @@ -6,6 +6,7 @@ #include #include +#include #include static void asmlinkage (*acpi_do_wakeup)(void *vector) = (void *)WAKEUP_BASE; @@ -19,8 +20,12 @@ static void acpi_jump_to_wakeup(void *vector) acpi_do_wakeup(vector); } -void acpi_resume(void *wake_vec) +void acpi_resume(struct acpi_fadt *fadt) { + void *wake_vec; + + wake_vec = acpi_find_wakeup_vector(fadt); + post_code(POST_OS_RESUME); acpi_jump_to_wakeup(wake_vec); } diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index 87a71ca492..01d5b6fff0 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -460,18 +460,14 @@ static struct acpi_rsdp *acpi_valid_rsdp(struct acpi_rsdp *rsdp) return rsdp; } -void *acpi_find_wakeup_vector(void) +struct acpi_fadt *acpi_find_fadt(void) { char *p, *end; struct acpi_rsdp *rsdp = NULL; struct acpi_rsdt *rsdt; struct acpi_fadt *fadt = NULL; - struct acpi_facs *facs; - void *wake_vec; int i; - debug("Trying to find the wakeup vector...\n"); - /* Find RSDP */ for (p = (char *)ROM_TABLE_ADDR; p < (char *)ROM_TABLE_END; p += 16) { rsdp = acpi_valid_rsdp((struct acpi_rsdp *)p); @@ -499,6 +495,16 @@ void *acpi_find_wakeup_vector(void) 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) { From 82a5648f5604709ae405b7faf23e68dc700b58d9 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Fri, 21 Apr 2017 07:24:45 -0700 Subject: [PATCH 18/29] x86: acpi: Turn on ACPI mode for S3 Before jumping to OS waking up vector, we need turn on ACPI mode for S3, just like what we do for a normal boot. Signed-off-by: Bin Meng Reviewed-by: Simon Glass Tested-by: Stefan Roese --- arch/x86/lib/acpi_s3.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/x86/lib/acpi_s3.c b/arch/x86/lib/acpi_s3.c index e5cc3b0766..0b62b75908 100644 --- a/arch/x86/lib/acpi_s3.c +++ b/arch/x86/lib/acpi_s3.c @@ -24,6 +24,9 @@ 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); post_code(POST_OS_RESUME); From 68769ebcbc892c85e9a1ffff09bbc6e4877f2d1f Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Fri, 21 Apr 2017 07:24:46 -0700 Subject: [PATCH 19/29] x86: pci: Allow conditionally run VGA rom in S3 Introduce a new CONFIG_S3_VGA_ROM_RUN option so that U-Boot can bypass executing VGA roms in S3. Signed-off-by: Bin Meng Reviewed-by: Simon Glass Tested-by: Stefan Roese --- arch/x86/Kconfig | 12 ++++++++++++ drivers/pci/pci_rom.c | 14 ++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 5322eff7fa..0cd981e73e 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -601,6 +601,18 @@ config HAVE_ACPI_RESUME 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 diff --git a/drivers/pci/pci_rom.c b/drivers/pci/pci_rom.c index 57204c4f3f..75fb093337 100644 --- a/drivers/pci/pci_rom.c +++ b/drivers/pci/pci_rom.c @@ -35,8 +35,22 @@ #include #include +#ifdef CONFIG_X86 +#include +DECLARE_GLOBAL_DATA_PTR; +#endif + __weak bool board_should_run_oprom(struct udevice *dev) { +#if defined(CONFIG_X86) && defined(CONFIG_HAVE_ACPI_RESUME) + if (gd->arch.prev_sleep_state == ACPI_S3) { + if (IS_ENABLED(CONFIG_S3_VGA_ROM_RUN)) + return true; + else + return false; + } +#endif + return true; } From 5ae5aa931042bb4163c6343a3cba65e95d90205a Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Fri, 21 Apr 2017 07:24:47 -0700 Subject: [PATCH 20/29] x86: acpi: Fix Windows S3 resume failure 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. Signed-off-by: Bin Meng Reviewed-by: Simon Glass Tested-by: Stefan Roese --- arch/x86/cpu/cpu.c | 8 +++-- arch/x86/include/asm/acpi_s3.h | 16 ++++++++++ arch/x86/include/asm/global_data.h | 1 + arch/x86/lib/acpi_s3.c | 48 ++++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 2 deletions(-) diff --git a/arch/x86/cpu/cpu.c b/arch/x86/cpu/cpu.c index dfe624f54a..e13786efa5 100644 --- a/arch/x86/cpu/cpu.c +++ b/arch/x86/cpu/cpu.c @@ -278,13 +278,17 @@ int reserve_arch(void) high_table_reserve(); #endif -#if defined(CONFIG_HAVE_ACPI_RESUME) && defined(CONFIG_HAVE_FSP) +#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 +#endif /* CONFIG_HAVE_FSP */ +#endif /* CONFIG_HAVE_ACPI_RESUME */ return 0; } diff --git a/arch/x86/include/asm/acpi_s3.h b/arch/x86/include/asm/acpi_s3.h index 1ad20f4fcb..86aec0abe0 100644 --- a/arch/x86/include/asm/acpi_s3.h +++ b/arch/x86/include/asm/acpi_s3.h @@ -29,6 +29,9 @@ #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[]; @@ -110,6 +113,19 @@ struct acpi_fadt; */ 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/global_data.h b/arch/x86/include/asm/global_data.h index b4ca4731a5..93a80fe2b6 100644 --- a/arch/x86/include/asm/global_data.h +++ b/arch/x86/include/asm/global_data.h @@ -101,6 +101,7 @@ struct arch_global_data { #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 }; diff --git a/arch/x86/lib/acpi_s3.c b/arch/x86/lib/acpi_s3.c index 0b62b75908..3175da828b 100644 --- a/arch/x86/lib/acpi_s3.c +++ b/arch/x86/lib/acpi_s3.c @@ -9,6 +9,8 @@ #include #include +DECLARE_GLOBAL_DATA_PTR; + static void asmlinkage (*acpi_do_wakeup)(void *vector) = (void *)WAKEUP_BASE; static void acpi_jump_to_wakeup(void *vector) @@ -29,6 +31,52 @@ void acpi_resume(struct acpi_fadt *fadt) 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; +} From 4f93c29b54cd719b9f06d5cce47d0a59085a6950 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Fri, 21 Apr 2017 07:24:48 -0700 Subject: [PATCH 21/29] x86: minnowmax: Enable ACPI S3 resume This turns on ACPI S3 resume for minnowmax board. Signed-off-by: Bin Meng Reviewed-by: Simon Glass Tested-by: Stefan Roese --- configs/minnowmax_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/configs/minnowmax_defconfig b/configs/minnowmax_defconfig index 8dac1d72fb..aa50e88422 100644 --- a/configs/minnowmax_defconfig +++ b/configs/minnowmax_defconfig @@ -10,6 +10,7 @@ CONFIG_HAVE_VGA_BIOS=y CONFIG_GENERATE_PIRQ_TABLE=y CONFIG_GENERATE_MP_TABLE=y CONFIG_GENERATE_ACPI_TABLE=y +CONFIG_HAVE_ACPI_RESUME=y CONFIG_SEABIOS=y CONFIG_FIT=y CONFIG_FIT_SIGNATURE=y From 13c9d8482528b466a8c7b42be8baa33bafb08b0b Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Fri, 21 Apr 2017 07:24:49 -0700 Subject: [PATCH 22/29] x86: Document ACPI S3 support Now that we have ACPI S3 support on Intel MinnowMax board, document some generic information of S3 and how to test it. Signed-off-by: Bin Meng Reviewed-by: Simon Glass Tested-by: Stefan Roese --- doc/README.x86 | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/doc/README.x86 b/doc/README.x86 index a38cc1bc6c..c69dc1c511 100644 --- a/doc/README.x86 +++ b/doc/README.x86 @@ -1014,12 +1014,12 @@ compile ACPI DSDT table written in ASL format to AML format. You can get the compiler via "apt-get install iasl" if you are on Ubuntu or download the source from [17] to compile one by yourself. -Current ACPI support in U-Boot is not complete. More features will be added -in the future. The status as of today is: +Current ACPI support in U-Boot is basically complete. More optional features +can be added in the future. The status as of today is: * Support generating RSDT, XSDT, FACS, FADT, MADT, MCFG tables. * Support one static DSDT table only, compiled by Intel ACPI compiler. - * Support S0/S5, reboot and shutdown from OS. + * Support S0/S3/S4/S5, reboot and shutdown from OS. * Support booting a pre-installed Ubuntu distribution via 'zboot' command. * Support installing and booting Ubuntu 14.04 (or above) from U-Boot with the help of SeaBIOS using legacy interface (non-UEFI mode). @@ -1027,9 +1027,6 @@ in the future. The status as of today is: of SeaBIOS using legacy interface (non-UEFI mode). * Support ACPI interrupts with SCI only. -Features not supported so far (to make it a complete ACPI solution): - * S3 (Suspend to RAM), S4 (Suspend to Disk). - Features that are optional: * Dynamic AML bytecodes insertion at run-time. We may need this to support SSDT table generation and DSDT fix up. @@ -1046,6 +1043,21 @@ command from the OS. For other platform boards, ACPI support status can be checked by examining their board defconfig files to see if CONFIG_GENERATE_ACPI_TABLE is set to y. +The S3 sleeping state is a low wake latency sleeping state defined by ACPI +spec where all system context is lost except system memory. To test S3 resume +with a Linux kernel, simply run "echo mem > /sys/power/state" and kernel will +put the board to S3 state where the power is off. So when the power button is +pressed again, U-Boot runs as it does in cold boot and detects the sleeping +state via ACPI register to see if it is S3, if yes it means we are waking up. +U-Boot is responsible for restoring the machine state as it is before sleep. +When everything is done, U-Boot finds out the wakeup vector provided by OSes +and jump there. To determine whether ACPI S3 resume is supported, check to +see if CONFIG_HAVE_ACPI_RESUME is set for that specific board. + +Note for testing S3 resume with Windows, correct graphics driver must be +installed for your platform, otherwise you won't find "Sleep" option in +the "Power" submenu from the Windows start menu. + EFI Support ----------- U-Boot supports booting as a 32-bit or 64-bit EFI payload, e.g. with UEFI. From ddb3ac3c716f56cead695444e65a7ba7b0946555 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Apr 2017 18:38:21 -0600 Subject: [PATCH 23/29] x86: Convert MMC to driver model Convert the pci_mmc driver over to driver model and migrate all x86 boards that use it. Signed-off-by: Simon Glass Reviewed-by: Bin Meng Tested-by: Bin Meng --- arch/Kconfig | 1 + arch/x86/cpu/baytrail/valleyview.c | 12 ----- arch/x86/cpu/quark/quark.c | 10 ---- arch/x86/cpu/queensbay/Makefile | 2 +- arch/x86/cpu/queensbay/topcliff.c | 20 -------- drivers/mmc/pci_mmc.c | 82 +++++++++++++++++++++--------- include/mmc.h | 12 ----- 7 files changed, 60 insertions(+), 79 deletions(-) delete mode 100644 arch/x86/cpu/queensbay/topcliff.c diff --git a/arch/Kconfig b/arch/Kconfig index 826e346f98..2528f50efa 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -85,6 +85,7 @@ config X86 select DM_SPI select DM_SPI_FLASH select USB_EHCI_HCD + select DM_MMC if MMC config XTENSA bool "Xtensa architecture" 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 #include -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/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 #include -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 - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include -#include -#include - -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/drivers/mmc/pci_mmc.c b/drivers/mmc/pci_mmc.c index e39b476834..6db89779ba 100644 --- a/drivers/mmc/pci_mmc.c +++ b/drivers/mmc/pci_mmc.c @@ -6,37 +6,71 @@ */ #include +#include #include #include +#include #include #include -int pci_mmc_init(const char *name, struct pci_device_id *mmc_supported) +struct pci_mmc_plat { + struct mmc_config cfg; + struct mmc mmc; +}; + +struct pci_mmc_priv { + struct sdhci_host host; + void *base; +}; + +static int pci_mmc_probe(struct udevice *dev) { - struct sdhci_host *mmc_host; - u32 iobase; + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct pci_mmc_plat *plat = dev_get_platdata(dev); + struct pci_mmc_priv *priv = dev_get_priv(dev); + struct sdhci_host *host = &priv->host; + u32 ioaddr; int ret; - int i; - for (i = 0; ; i++) { - struct udevice *dev; + dm_pci_read_config32(dev, PCI_BASE_ADDRESS_0, &ioaddr); + host->ioaddr = map_sysmem(ioaddr, 0); + host->name = dev->name; + ret = sdhci_setup_cfg(&plat->cfg, host, 0, 0); + if (ret) + return ret; + host->mmc = &plat->mmc; + host->mmc->priv = &priv->host; + host->mmc->dev = dev; + upriv->mmc = host->mmc; - ret = pci_find_device_id(mmc_supported, i, &dev); - if (ret) - return ret; - mmc_host = malloc(sizeof(struct sdhci_host)); - if (!mmc_host) - return -ENOMEM; - - mmc_host->name = name; - dm_pci_read_config32(dev, PCI_BASE_ADDRESS_0, &iobase); - mmc_host->ioaddr = (void *)(ulong)iobase; - mmc_host->quirks = 0; - mmc_host->max_clk = 0; - ret = add_sdhci(mmc_host, 0, 0); - if (ret) - return ret; - } - - return 0; + return sdhci_probe(dev); } + +static int pci_mmc_bind(struct udevice *dev) +{ + struct pci_mmc_plat *plat = dev_get_platdata(dev); + + return sdhci_bind(dev, &plat->mmc, &plat->cfg); +} + +U_BOOT_DRIVER(pci_mmc) = { + .name = "pci_mmc", + .id = UCLASS_MMC, + .bind = pci_mmc_bind, + .probe = pci_mmc_probe, + .ops = &sdhci_ops, + .priv_auto_alloc_size = sizeof(struct pci_mmc_priv), + .platdata_auto_alloc_size = sizeof(struct pci_mmc_plat), +}; + +static struct pci_device_id mmc_supported[] = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT_SDIO) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT_SD) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT_EMMC2) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QRK_SDIO) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TCF_SDIO_0) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TCF_SDIO_1) }, + {}, +}; + +U_BOOT_PCI_DEVICE(pci_mmc, mmc_supported); diff --git a/include/mmc.h b/include/mmc.h index fad12d608c..8346b0e19e 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -585,18 +585,6 @@ int cpu_mmc_init(bd_t *bis); int mmc_get_env_addr(struct mmc *mmc, int copy, u32 *env_addr); int mmc_get_env_dev(void); -struct pci_device_id; - -/** - * pci_mmc_init() - set up PCI MMC devices - * - * This finds all the matching PCI IDs and sets them up as MMC devices. - * - * @name: Name to use for devices - * @mmc_supported: PCI IDs to search for, terminated by {0, 0} - */ -int pci_mmc_init(const char *name, struct pci_device_id *mmc_supported); - /* Set block count limit because of 16 bit register limit on some hardware*/ #ifndef CONFIG_SYS_MMC_MAX_BLK_COUNT #define CONFIG_SYS_MMC_MAX_BLK_COUNT 65535 From e98856fcff5adc64689c3d5597b0a36d867eec8f Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Mon, 24 Apr 2017 09:48:01 +0200 Subject: [PATCH 24/29] serial: serial-uclass: Use force parameter in stdio_deregister_dev() On my x86 platform I've noticed, that calling dm_uninit() or the new function dm_remove_devices_flags() does not remove the desired device at all. Debugging showed, that the serial uclass returns -EPERM in serial_pre_remove(). This patch sets the force parameter when calling stdio_deregister_dev() resulting in a removal of the device. Signed-off-by: Stefan Roese Cc: Simon Glass Cc: Bin Meng Reviewed-by: Simon Glass --- drivers/serial/serial-uclass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/serial/serial-uclass.c b/drivers/serial/serial-uclass.c index 43c028ebe6..c2b9c5f12f 100644 --- a/drivers/serial/serial-uclass.c +++ b/drivers/serial/serial-uclass.c @@ -349,7 +349,7 @@ static int serial_pre_remove(struct udevice *dev) #if CONFIG_IS_ENABLED(SYS_STDIO_DEREGISTER) struct serial_dev_priv *upriv = dev_get_uclass_priv(dev); - if (stdio_deregister_dev(upriv->sdev, 0)) + if (stdio_deregister_dev(upriv->sdev, true)) return -EPERM; #endif From 426f99fa98c3725fe0ca1eb8438d1215a2c6bd6b Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Mon, 24 Apr 2017 09:48:02 +0200 Subject: [PATCH 25/29] dm: core: Add DM_FLAG_OS_PREPARE flag This new flag can be added to DM device drivers, which need to do some final configuration before U-Boot exits and the OS (e.g. Linux) is started. The remove functions of those drivers will get called at this stage to do these last-stage configuration steps. Signed-off-by: Stefan Roese Reviewed-by: Simon Glass Cc: Bin Meng --- drivers/core/device-remove.c | 16 +++++++++++----- include/dm/device.h | 11 ++++++++++- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/drivers/core/device-remove.c b/drivers/core/device-remove.c index cc0043b990..3c6ab42f7d 100644 --- a/drivers/core/device-remove.c +++ b/drivers/core/device-remove.c @@ -152,6 +152,15 @@ void device_free(struct udevice *dev) devres_release_probe(dev); } +static bool flags_remove(uint flags, uint drv_flags) +{ + if ((flags & DM_REMOVE_NORMAL) || + (flags & (drv_flags & (DM_FLAG_ACTIVE_DMA | DM_FLAG_OS_PREPARE)))) + return true; + + return false; +} + int device_remove(struct udevice *dev, uint flags) { const struct driver *drv; @@ -178,9 +187,7 @@ int device_remove(struct udevice *dev, uint flags) * Remove the device if called with the "normal" remove flag set, * or if the remove flag matches any of the drivers remove flags */ - if (drv->remove && - ((flags & DM_REMOVE_NORMAL) || - (flags & (drv->flags & DM_FLAG_ACTIVE_DMA)))) { + if (drv->remove && flags_remove(flags, drv->flags)) { ret = drv->remove(dev); if (ret) goto err_remove; @@ -194,8 +201,7 @@ int device_remove(struct udevice *dev, uint flags) } } - if ((flags & DM_REMOVE_NORMAL) || - (flags & (drv->flags & DM_FLAG_ACTIVE_DMA))) { + if (flags_remove(flags, drv->flags)) { device_free(dev); dev->seq = -1; diff --git a/include/dm/device.h b/include/dm/device.h index 079ec57003..df02e41df3 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -54,6 +54,12 @@ struct driver_info; */ #define DM_FLAG_ACTIVE_DMA (1 << 9) +/* + * Call driver remove function to do some final configuration, before + * U-Boot exits and the OS is started + */ +#define DM_FLAG_OS_PREPARE (1 << 10) + /* * One or multiple of these flags are passed to device_remove() so that * a selective device removal as specified by the remove-stage and the @@ -66,10 +72,13 @@ enum { /* Remove devices with active DMA */ DM_REMOVE_ACTIVE_DMA = DM_FLAG_ACTIVE_DMA, + /* Remove devices which need some final OS preparation steps */ + DM_REMOVE_OS_PREPARE = DM_FLAG_OS_PREPARE, + /* Add more use cases here */ /* Remove devices with any active flag */ - DM_REMOVE_ACTIVE_ALL = DM_REMOVE_ACTIVE_DMA, + DM_REMOVE_ACTIVE_ALL = DM_REMOVE_ACTIVE_DMA | DM_REMOVE_OS_PREPARE, }; /** From 7025b05415ff97824266ce59b7f7915032ba6403 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Mon, 24 Apr 2017 09:48:03 +0200 Subject: [PATCH 26/29] x86: bootm: Add dm_remove_devices_flags() call to bootm_announce_and_cleanup() This patch adds a call to dm_remove_devices_flags() to bootm_announce_and_cleanup() so that drivers that have one of the removal flags set (e.g. DM_FLAG_ACTIVE_DMA_REMOVE) in their driver struct, may do some last-stage cleanup before the OS is started. Signed-off-by: Stefan Roese Cc: Bin Meng Reviewed-by: Simon Glass --- arch/x86/lib/bootm.c | 9 +++++++++ 1 file changed, 9 insertions(+) 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 #include +#include +#include #include #include #include @@ -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) From 4759dffe23460d39d8e92c01013b00a3587e2112 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Mon, 24 Apr 2017 09:48:04 +0200 Subject: [PATCH 27/29] spi: ich: Configure SPI BIOS parameters for Linux upon U-Boot exit This patch adds a remove function to the Intel ICH SPI driver, that will be called upon U-Boot exit, directly before the OS (Linux) is started. This function takes care of configuring the BIOS registers in the SPI controller (similar to what a "standard" BIOS or coreboot does), so that the Linux MTD device driver is able to correctly read/write to the SPI NOR chip. Without this, the chip is not detected at all. Signed-off-by: Stefan Roese Reviewed-by: Simon Glass Cc: Bin Meng Cc: Jagan Teki --- drivers/spi/ich.c | 18 ++++++++++++++++ drivers/spi/ich.h | 54 +++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 65 insertions(+), 7 deletions(-) diff --git a/drivers/spi/ich.c b/drivers/spi/ich.c index 893fe33b66..bf2e99b5cc 100644 --- a/drivers/spi/ich.c +++ b/drivers/spi/ich.c @@ -617,6 +617,22 @@ static int ich_spi_probe(struct udevice *dev) return 0; } +static int ich_spi_remove(struct udevice *bus) +{ + struct ich_spi_priv *ctlr = dev_get_priv(bus); + + /* + * Configure SPI controller so that the Linux MTD driver can fully + * access the SPI NOR chip + */ + ich_writew(ctlr, SPI_OPPREFIX, ctlr->preop); + ich_writew(ctlr, SPI_OPTYPE, ctlr->optype); + ich_writel(ctlr, SPI_OPMENU_LOWER, ctlr->opmenu); + ich_writel(ctlr, SPI_OPMENU_UPPER, ctlr->opmenu + sizeof(u32)); + + return 0; +} + static int ich_spi_set_speed(struct udevice *bus, uint speed) { struct ich_spi_priv *priv = dev_get_priv(bus); @@ -700,4 +716,6 @@ U_BOOT_DRIVER(ich_spi) = { .priv_auto_alloc_size = sizeof(struct ich_spi_priv), .child_pre_probe = ich_spi_child_pre_probe, .probe = ich_spi_probe, + .remove = ich_spi_remove, + .flags = DM_FLAG_OS_PREPARE, }; diff --git a/drivers/spi/ich.h b/drivers/spi/ich.h index bd0a820809..dcb8a9048f 100644 --- a/drivers/spi/ich.h +++ b/drivers/spi/ich.h @@ -101,13 +101,6 @@ enum { HSFC_FSMIE = 0x8000 }; -enum { - SPI_OPCODE_TYPE_READ_NO_ADDRESS = 0, - SPI_OPCODE_TYPE_WRITE_NO_ADDRESS = 1, - SPI_OPCODE_TYPE_READ_WITH_ADDRESS = 2, - SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS = 3 -}; - enum { ICH_MAX_CMD_LEN = 5, }; @@ -124,8 +117,55 @@ struct spi_trans { uint32_t offset; }; +#define SPI_OPCODE_WRSR 0x01 +#define SPI_OPCODE_PAGE_PROGRAM 0x02 +#define SPI_OPCODE_READ 0x03 +#define SPI_OPCODE_WRDIS 0x04 +#define SPI_OPCODE_RDSR 0x05 #define SPI_OPCODE_WREN 0x06 #define SPI_OPCODE_FAST_READ 0x0b +#define SPI_OPCODE_ERASE_SECT 0x20 +#define SPI_OPCODE_READ_ID 0x9f +#define SPI_OPCODE_ERASE_BLOCK 0xd8 + +#define SPI_OPCODE_TYPE_READ_NO_ADDRESS 0 +#define SPI_OPCODE_TYPE_WRITE_NO_ADDRESS 1 +#define SPI_OPCODE_TYPE_READ_WITH_ADDRESS 2 +#define SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS 3 + +#define SPI_OPMENU_0 SPI_OPCODE_WRSR +#define SPI_OPTYPE_0 SPI_OPCODE_TYPE_WRITE_NO_ADDRESS + +#define SPI_OPMENU_1 SPI_OPCODE_PAGE_PROGRAM +#define SPI_OPTYPE_1 SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS + +#define SPI_OPMENU_2 SPI_OPCODE_READ +#define SPI_OPTYPE_2 SPI_OPCODE_TYPE_READ_WITH_ADDRESS + +#define SPI_OPMENU_3 SPI_OPCODE_RDSR +#define SPI_OPTYPE_3 SPI_OPCODE_TYPE_READ_NO_ADDRESS + +#define SPI_OPMENU_4 SPI_OPCODE_ERASE_SECT +#define SPI_OPTYPE_4 SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS + +#define SPI_OPMENU_5 SPI_OPCODE_READ_ID +#define SPI_OPTYPE_5 SPI_OPCODE_TYPE_READ_NO_ADDRESS + +#define SPI_OPMENU_6 SPI_OPCODE_ERASE_BLOCK +#define SPI_OPTYPE_6 SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS + +#define SPI_OPMENU_7 SPI_OPCODE_FAST_READ +#define SPI_OPTYPE_7 SPI_OPCODE_TYPE_READ_WITH_ADDRESS + +#define SPI_OPPREFIX ((SPI_OPCODE_WREN << 8) | SPI_OPCODE_WREN) +#define SPI_OPTYPE ((SPI_OPTYPE_7 << 14) | (SPI_OPTYPE_6 << 12) | \ + (SPI_OPTYPE_5 << 10) | (SPI_OPTYPE_4 << 8) | \ + (SPI_OPTYPE_3 << 6) | (SPI_OPTYPE_2 << 4) | \ + (SPI_OPTYPE_1 << 2) | (SPI_OPTYPE_0 << 0)) +#define SPI_OPMENU_UPPER ((SPI_OPMENU_7 << 24) | (SPI_OPMENU_6 << 16) | \ + (SPI_OPMENU_5 << 8) | (SPI_OPMENU_4 << 0)) +#define SPI_OPMENU_LOWER ((SPI_OPMENU_3 << 24) | (SPI_OPMENU_2 << 16) | \ + (SPI_OPMENU_1 << 8) | (SPI_OPMENU_0 << 0)) enum ich_version { ICHV_7, From 770ee0174223e824a721f5f164cc8b2bf7473189 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 7 May 2017 19:52:29 -0700 Subject: [PATCH 28/29] x86: ich6_gpio: Add use-lvl-write-cache for I/O access mode Add a device-tree property use-lvl-write-cache that will cause writes to lvl to be cached instead of read from lvl before each write. This is required on some platforms that have the register implemented as dual read/write (such as Baytrail). Prior to this fix the blue USB port on the Minnowboard Max was unusable since USB_HOST_EN0 was set high then immediately set low when USB_HOST_EN1 was written. This also resolves the 'gpio clear | set' command warning like: "Warning: value of pin is still 0" Signed-off-by: George McCollister Signed-off-by: Bin Meng Reviewed-by: Stefan Roese Reviewed-by: Simon Glass --- arch/x86/dts/bayleybay.dts | 6 ++++ arch/x86/dts/baytrail_som-db5800-som-6867.dts | 6 ++++ arch/x86/dts/conga-qeval20-qa3-e3845.dts | 6 ++++ arch/x86/dts/dfi-bt700.dtsi | 6 ++++ arch/x86/dts/minnowmax.dts | 6 ++++ drivers/gpio/intel_ich6_gpio.c | 30 +++++++++++++++---- 6 files changed, 55 insertions(+), 5 deletions(-) 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..bc382a8948 100644 --- a/arch/x86/dts/minnowmax.dts +++ b/arch/x86/dts/minnowmax.dts @@ -218,6 +218,7 @@ u-boot,dm-pre-reloc; reg = <0 0x20>; bank-name = "A"; + use-lvl-write-cache; }; gpiob { @@ -225,6 +226,7 @@ u-boot,dm-pre-reloc; reg = <0x20 0x20>; bank-name = "B"; + use-lvl-write-cache; }; gpioc { @@ -232,6 +234,7 @@ u-boot,dm-pre-reloc; reg = <0x40 0x20>; bank-name = "C"; + use-lvl-write-cache; }; gpiod { @@ -239,6 +242,7 @@ u-boot,dm-pre-reloc; reg = <0x60 0x20>; bank-name = "D"; + use-lvl-write-cache; }; gpioe { @@ -246,6 +250,7 @@ u-boot,dm-pre-reloc; reg = <0x80 0x20>; bank-name = "E"; + use-lvl-write-cache; }; gpiof { @@ -253,6 +258,7 @@ u-boot,dm-pre-reloc; reg = <0xA0 0x20>; bank-name = "F"; + use-lvl-write-cache; }; }; }; diff --git a/drivers/gpio/intel_ich6_gpio.c b/drivers/gpio/intel_ich6_gpio.c index 8b782260bc..0a9eb03fd0 100644 --- a/drivers/gpio/intel_ich6_gpio.c +++ b/drivers/gpio/intel_ich6_gpio.c @@ -46,22 +46,31 @@ struct ich6_bank_priv { uint16_t use_sel; uint16_t io_sel; uint16_t lvl; + u32 lvl_write_cache; + bool use_lvl_write_cache; }; #define GPIO_USESEL_OFFSET(x) (x) #define GPIO_IOSEL_OFFSET(x) (x + 4) #define GPIO_LVL_OFFSET(x) (x + 8) -static int _ich6_gpio_set_value(uint16_t base, unsigned offset, int value) +static int _ich6_gpio_set_value(struct ich6_bank_priv *bank, unsigned offset, + int value) { u32 val; - val = inl(base); + if (bank->use_lvl_write_cache) + val = bank->lvl_write_cache; + else + val = inl(bank->lvl); + if (value) val |= (1UL << offset); else val &= ~(1UL << offset); - outl(val, base); + outl(val, bank->lvl); + if (bank->use_lvl_write_cache) + bank->lvl_write_cache = val; return 0; } @@ -112,6 +121,7 @@ static int ich6_gpio_probe(struct udevice *dev) struct ich6_bank_platdata *plat = dev_get_platdata(dev); struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); struct ich6_bank_priv *bank = dev_get_priv(dev); + const void *prop; uc_priv->gpio_count = GPIO_PER_BANK; uc_priv->bank_name = plat->bank_name; @@ -119,6 +129,14 @@ static int ich6_gpio_probe(struct udevice *dev) bank->io_sel = plat->base_addr + 4; bank->lvl = plat->base_addr + 8; + prop = fdt_getprop(gd->fdt_blob, dev->of_offset, + "use-lvl-write-cache", NULL); + if (prop) + bank->use_lvl_write_cache = true; + else + bank->use_lvl_write_cache = false; + bank->lvl_write_cache = 0; + return 0; } @@ -160,7 +178,7 @@ static int ich6_gpio_direction_output(struct udevice *dev, unsigned offset, if (ret) return ret; - return _ich6_gpio_set_value(bank->lvl, offset, value); + return _ich6_gpio_set_value(bank, offset, value); } static int ich6_gpio_get_value(struct udevice *dev, unsigned offset) @@ -170,6 +188,8 @@ static int ich6_gpio_get_value(struct udevice *dev, unsigned offset) int r; tmplong = inl(bank->lvl); + if (bank->use_lvl_write_cache) + tmplong |= bank->lvl_write_cache; r = (tmplong & (1UL << offset)) ? 1 : 0; return r; } @@ -178,7 +198,7 @@ static int ich6_gpio_set_value(struct udevice *dev, unsigned offset, int value) { struct ich6_bank_priv *bank = dev_get_priv(dev); - return _ich6_gpio_set_value(bank->lvl, offset, value); + return _ich6_gpio_set_value(bank, offset, value); } static int ich6_gpio_get_function(struct udevice *dev, unsigned offset) From c2f17939f4b429c90a453e26927a7c578e5456b5 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 7 May 2017 19:52:30 -0700 Subject: [PATCH 29/29] x86: minnowmax: Remove incorrect pad-offset of several pins Remove 'pad-offset' of soc_gpio_s5_0, soc_gpio_s5_1, soc_gpio_s5_2, pin_usb_host_en0 and pin_usb_host_en1. These offsets are actually wrong. Correct value should be added by 0x2000, but since they are supposed to be 'mode-gpio', 'pad-offset' is not needed at all. Signed-off-by: Bin Meng Reviewed-by: Stefan Roese Reviewed-by: Simon Glass --- arch/x86/dts/minnowmax.dts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/arch/x86/dts/minnowmax.dts b/arch/x86/dts/minnowmax.dts index bc382a8948..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 = ; @@ -44,7 +43,6 @@ /* GPIO E1 */ soc_gpio_s5_1@0 { gpio-offset = <0x80 1>; - pad-offset = <0x210>; mode-gpio; output-value = <0>; direction = ; @@ -53,7 +51,6 @@ /* GPIO E2 */ soc_gpio_s5_2@0 { gpio-offset = <0x80 2>; - pad-offset = <0x1e0>; mode-gpio; output-value = <0>; direction = ; @@ -61,7 +58,6 @@ pin_usb_host_en0@0 { gpio-offset = <0x80 8>; - pad-offset = <0x260>; mode-gpio; output-value = <1>; direction = ; @@ -69,7 +65,6 @@ pin_usb_host_en1@0 { gpio-offset = <0x80 9>; - pad-offset = <0x250>; mode-gpio; output-value = <1>; direction = ;