diff options
author | Tom Rini <trini@ti.com> | 2014-11-24 12:00:00 -0500 |
---|---|---|
committer | Tom Rini <trini@ti.com> | 2014-11-24 12:00:00 -0500 |
commit | 746667f1e56bf08d03e66a178df3c4f4f6c806e1 (patch) | |
tree | e42c7fd72cb1ef97a5a05a73b06b3cd2fc118147 /arch/x86/cpu | |
parent | 6c016485a685b5cdac28edb25147311a3e88d51f (diff) | |
parent | fe5b9b447c6eea3873833b1f3ba15c9854aa2ef8 (diff) | |
download | u-boot-imx-746667f1e56bf08d03e66a178df3c4f4f6c806e1.zip u-boot-imx-746667f1e56bf08d03e66a178df3c4f4f6c806e1.tar.gz u-boot-imx-746667f1e56bf08d03e66a178df3c4f4f6c806e1.tar.bz2 |
Merge git://git.denx.de/u-boot-x86
Conflicts:
arch/x86/cpu/Makefile
Signed-off-by: Tom Rini <trini@ti.com>
Diffstat (limited to 'arch/x86/cpu')
-rw-r--r-- | arch/x86/cpu/Makefile | 1 | ||||
-rw-r--r-- | arch/x86/cpu/coreboot/coreboot.c | 42 | ||||
-rw-r--r-- | arch/x86/cpu/coreboot/ipchecksum.c | 2 | ||||
-rw-r--r-- | arch/x86/cpu/coreboot/pci.c | 22 | ||||
-rw-r--r-- | arch/x86/cpu/coreboot/sdram.c | 15 | ||||
-rw-r--r-- | arch/x86/cpu/coreboot/tables.c | 6 | ||||
-rw-r--r-- | arch/x86/cpu/cpu.c | 301 | ||||
-rw-r--r-- | arch/x86/cpu/interrupts.c | 2 | ||||
-rw-r--r-- | arch/x86/cpu/ivybridge/Kconfig | 172 | ||||
-rw-r--r-- | arch/x86/cpu/ivybridge/Makefile | 16 | ||||
-rw-r--r-- | arch/x86/cpu/ivybridge/car.S | 178 | ||||
-rw-r--r-- | arch/x86/cpu/ivybridge/cpu.c | 357 | ||||
-rw-r--r-- | arch/x86/cpu/ivybridge/early_init.c | 145 | ||||
-rw-r--r-- | arch/x86/cpu/ivybridge/early_me.c | 191 | ||||
-rw-r--r-- | arch/x86/cpu/ivybridge/lpc.c | 48 | ||||
-rw-r--r-- | arch/x86/cpu/ivybridge/me_status.c | 195 | ||||
-rw-r--r-- | arch/x86/cpu/ivybridge/microcode_intel.c | 151 | ||||
-rw-r--r-- | arch/x86/cpu/ivybridge/pci.c | 60 | ||||
-rw-r--r-- | arch/x86/cpu/ivybridge/report_platform.c | 89 | ||||
-rw-r--r-- | arch/x86/cpu/ivybridge/sdram.c | 571 | ||||
-rw-r--r-- | arch/x86/cpu/pci.c | 98 | ||||
-rw-r--r-- | arch/x86/cpu/start.S | 40 | ||||
-rw-r--r-- | arch/x86/cpu/start16.S | 18 |
23 files changed, 2595 insertions, 125 deletions
diff --git a/arch/x86/cpu/Makefile b/arch/x86/cpu/Makefile index 8dd7b06..2b9e9b9 100644 --- a/arch/x86/cpu/Makefile +++ b/arch/x86/cpu/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_X86_RESET_VECTOR) += resetvec.o start16.o obj-y += interrupts.o cpu.o call64.o obj-$(CONFIG_SYS_COREBOOT) += coreboot/ +obj-$(CONFIG_PCI) += pci.o diff --git a/arch/x86/cpu/coreboot/coreboot.c b/arch/x86/cpu/coreboot/coreboot.c index e24f13a..2df7288 100644 --- a/arch/x86/cpu/coreboot/coreboot.c +++ b/arch/x86/cpu/coreboot/coreboot.c @@ -13,25 +13,25 @@ #include <ns16550.h> #include <asm/msr.h> #include <asm/cache.h> +#include <asm/cpu.h> #include <asm/io.h> -#include <asm/arch-coreboot/tables.h> -#include <asm/arch-coreboot/sysinfo.h> +#include <asm/arch/tables.h> +#include <asm/arch/sysinfo.h> #include <asm/arch/timestamp.h> DECLARE_GLOBAL_DATA_PTR; -/* - * Miscellaneous platform dependent initializations - */ -int cpu_init_f(void) +int arch_cpu_init(void) { int ret = get_coreboot_info(&lib_sysinfo); - if (ret != 0) + if (ret != 0) { printf("Failed to parse coreboot tables.\n"); + return ret; + } timestamp_init(); - return ret; + return x86_cpu_init_f(); } int board_early_init_f(void) @@ -50,27 +50,9 @@ int board_early_init_r(void) return 0; } -void show_boot_progress(int val) +int print_cpuinfo(void) { -#if MIN_PORT80_KCLOCKS_DELAY - /* - * Scale the time counter reading to avoid using 64 bit arithmetics. - * Can't use get_timer() here becuase it could be not yet - * initialized or even implemented. - */ - if (!gd->arch.tsc_prev) { - gd->arch.tsc_base_kclocks = rdtsc() / 1000; - gd->arch.tsc_prev = 0; - } else { - uint32_t now; - - do { - now = rdtsc() / 1000 - gd->arch.tsc_base_kclocks; - } while (now < (gd->arch.tsc_prev + MIN_PORT80_KCLOCKS_DELAY)); - gd->arch.tsc_prev = now; - } -#endif - outb(val, 0x80); + return default_print_cpuinfo(); } int last_stage_init(void) @@ -98,7 +80,7 @@ int board_eth_init(bd_t *bis) #define MTRRphysBase_MSR(reg) (0x200 + 2 * (reg)) #define MTRRphysMask_MSR(reg) (0x200 + 2 * (reg) + 1) -int board_final_cleanup(void) +void board_final_cleanup(void) { /* Un-cache the ROM so the kernel has one * more MTRR available. @@ -120,8 +102,6 @@ int board_final_cleanup(void) /* Issue SMI to Coreboot to lock down ME and registers */ printf("Finalizing Coreboot\n"); outb(0xcb, 0xb2); - - return 0; } void panic_puts(const char *str) diff --git a/arch/x86/cpu/coreboot/ipchecksum.c b/arch/x86/cpu/coreboot/ipchecksum.c index 57733d8..5f6c009 100644 --- a/arch/x86/cpu/coreboot/ipchecksum.c +++ b/arch/x86/cpu/coreboot/ipchecksum.c @@ -30,7 +30,7 @@ */ #include <compiler.h> -#include <asm/arch-coreboot/ipchecksum.h> +#include <asm/arch/ipchecksum.h> unsigned short ipchksum(const void *vptr, unsigned long nbytes) { diff --git a/arch/x86/cpu/coreboot/pci.c b/arch/x86/cpu/coreboot/pci.c index b35d70c..6a3dd93 100644 --- a/arch/x86/cpu/coreboot/pci.c +++ b/arch/x86/cpu/coreboot/pci.c @@ -13,8 +13,6 @@ #include <pci.h> #include <asm/pci.h> -static struct pci_controller coreboot_hose; - static void config_pci_bridge(struct pci_controller *hose, pci_dev_t dev, struct pci_config_table *table) { @@ -31,19 +29,13 @@ static struct pci_config_table pci_coreboot_config_table[] = { {} }; -void pci_init_board(void) +void board_pci_setup_hose(struct pci_controller *hose) { - coreboot_hose.config_table = pci_coreboot_config_table; - coreboot_hose.first_busno = 0; - coreboot_hose.last_busno = 0; - - pci_set_region(coreboot_hose.regions + 0, 0x0, 0x0, 0xffffffff, - PCI_REGION_MEM); - coreboot_hose.region_count = 1; - - pci_setup_type1(&coreboot_hose); - - pci_register_hose(&coreboot_hose); + hose->config_table = pci_coreboot_config_table; + hose->first_busno = 0; + hose->last_busno = 0; - pci_hose_scan(&coreboot_hose); + pci_set_region(hose->regions + 0, 0x0, 0x0, 0xffffffff, + PCI_REGION_MEM); + hose->region_count = 1; } diff --git a/arch/x86/cpu/coreboot/sdram.c b/arch/x86/cpu/coreboot/sdram.c index 3140b6b..e98a230 100644 --- a/arch/x86/cpu/coreboot/sdram.c +++ b/arch/x86/cpu/coreboot/sdram.c @@ -11,8 +11,10 @@ #include <asm/e820.h> #include <asm/u-boot-x86.h> #include <asm/global_data.h> +#include <asm/init_helpers.h> #include <asm/processor.h> #include <asm/sections.h> +#include <asm/zimage.h> #include <asm/arch/sysinfo.h> #include <asm/arch/tables.h> @@ -79,7 +81,7 @@ ulong board_get_usable_ram_top(ulong total_size) return (ulong)dest_addr; } -int dram_init_f(void) +int dram_init(void) { int i; phys_size_t ram_size = 0; @@ -94,10 +96,11 @@ int dram_init_f(void) gd->ram_size = ram_size; if (ram_size == 0) return -1; - return 0; + + return calculate_relocation_address(); } -int dram_init_banksize(void) +void dram_init_banksize(void) { int i, j; @@ -114,10 +117,4 @@ int dram_init_banksize(void) } } } - return 0; -} - -int dram_init(void) -{ - return dram_init_banksize(); } diff --git a/arch/x86/cpu/coreboot/tables.c b/arch/x86/cpu/coreboot/tables.c index 0d91adc..92b7528 100644 --- a/arch/x86/cpu/coreboot/tables.c +++ b/arch/x86/cpu/coreboot/tables.c @@ -8,9 +8,9 @@ */ #include <common.h> -#include <asm/arch-coreboot/ipchecksum.h> -#include <asm/arch-coreboot/sysinfo.h> -#include <asm/arch-coreboot/tables.h> +#include <asm/arch/ipchecksum.h> +#include <asm/arch/sysinfo.h> +#include <asm/arch/tables.h> /* * This needs to be in the .data section so that it's copied over during diff --git a/arch/x86/cpu/cpu.c b/arch/x86/cpu/cpu.c index 2e25253..b391b7a 100644 --- a/arch/x86/cpu/cpu.c +++ b/arch/x86/cpu/cpu.c @@ -13,6 +13,9 @@ * Sysgo Real-Time Solutions, GmbH <www.elinos.com> * Alex Zuepke <azu@sysgo.de> * + * Part of this file is adapted from coreboot + * src/arch/x86/lib/cpu.c + * * SPDX-License-Identifier: GPL-2.0+ */ @@ -22,11 +25,14 @@ #include <malloc.h> #include <asm/control_regs.h> #include <asm/cpu.h> +#include <asm/post.h> #include <asm/processor.h> #include <asm/processor-flags.h> #include <asm/interrupt.h> #include <linux/compiler.h> +DECLARE_GLOBAL_DATA_PTR; + /* * Constructor for a conventional segment GDT (or LDT) entry * This is a macro so it can be used in initialisers @@ -43,6 +49,52 @@ struct gdt_ptr { u32 ptr; } __packed; +struct cpu_device_id { + unsigned vendor; + unsigned device; +}; + +struct cpuinfo_x86 { + uint8_t x86; /* CPU family */ + uint8_t x86_vendor; /* CPU vendor */ + uint8_t x86_model; + uint8_t x86_mask; +}; + +/* + * List of cpu vendor strings along with their normalized + * id values. + */ +static struct { + int vendor; + const char *name; +} x86_vendors[] = { + { X86_VENDOR_INTEL, "GenuineIntel", }, + { X86_VENDOR_CYRIX, "CyrixInstead", }, + { X86_VENDOR_AMD, "AuthenticAMD", }, + { X86_VENDOR_UMC, "UMC UMC UMC ", }, + { X86_VENDOR_NEXGEN, "NexGenDriven", }, + { X86_VENDOR_CENTAUR, "CentaurHauls", }, + { X86_VENDOR_RISE, "RiseRiseRise", }, + { X86_VENDOR_TRANSMETA, "GenuineTMx86", }, + { X86_VENDOR_TRANSMETA, "TransmetaCPU", }, + { X86_VENDOR_NSC, "Geode by NSC", }, + { X86_VENDOR_SIS, "SiS SiS SiS ", }, +}; + +static const char *const x86_vendor_name[] = { + [X86_VENDOR_INTEL] = "Intel", + [X86_VENDOR_CYRIX] = "Cyrix", + [X86_VENDOR_AMD] = "AMD", + [X86_VENDOR_UMC] = "UMC", + [X86_VENDOR_NEXGEN] = "NexGen", + [X86_VENDOR_CENTAUR] = "Centaur", + [X86_VENDOR_RISE] = "Rise", + [X86_VENDOR_TRANSMETA] = "Transmeta", + [X86_VENDOR_NSC] = "NSC", + [X86_VENDOR_SIS] = "SiS", +}; + static void load_ds(u32 segment) { asm volatile("movl %0, %%ds" : : "r" (segment * X86_GDT_ENTRY_SIZE)); @@ -115,6 +167,129 @@ int __weak x86_cleanup_before_linux(void) return 0; } +/* + * Cyrix CPUs without cpuid or with cpuid not yet enabled can be detected + * by the fact that they preserve the flags across the division of 5/2. + * PII and PPro exhibit this behavior too, but they have cpuid available. + */ + +/* + * Perform the Cyrix 5/2 test. A Cyrix won't change + * the flags, while other 486 chips will. + */ +static inline int test_cyrix_52div(void) +{ + unsigned int test; + + __asm__ __volatile__( + "sahf\n\t" /* clear flags (%eax = 0x0005) */ + "div %b2\n\t" /* divide 5 by 2 */ + "lahf" /* store flags into %ah */ + : "=a" (test) + : "0" (5), "q" (2) + : "cc"); + + /* AH is 0x02 on Cyrix after the divide.. */ + return (unsigned char) (test >> 8) == 0x02; +} + +/* + * Detect a NexGen CPU running without BIOS hypercode new enough + * to have CPUID. (Thanks to Herbert Oppmann) + */ + +static int deep_magic_nexgen_probe(void) +{ + int ret; + + __asm__ __volatile__ ( + " movw $0x5555, %%ax\n" + " xorw %%dx,%%dx\n" + " movw $2, %%cx\n" + " divw %%cx\n" + " movl $0, %%eax\n" + " jnz 1f\n" + " movl $1, %%eax\n" + "1:\n" + : "=a" (ret) : : "cx", "dx"); + return ret; +} + +static bool has_cpuid(void) +{ + return flag_is_changeable_p(X86_EFLAGS_ID); +} + +static int build_vendor_name(char *vendor_name) +{ + struct cpuid_result result; + result = cpuid(0x00000000); + unsigned int *name_as_ints = (unsigned int *)vendor_name; + + name_as_ints[0] = result.ebx; + name_as_ints[1] = result.edx; + name_as_ints[2] = result.ecx; + + return result.eax; +} + +static void identify_cpu(struct cpu_device_id *cpu) +{ + char vendor_name[16]; + int i; + + vendor_name[0] = '\0'; /* Unset */ + cpu->device = 0; /* fix gcc 4.4.4 warning */ + + /* Find the id and vendor_name */ + if (!has_cpuid()) { + /* Its a 486 if we can modify the AC flag */ + if (flag_is_changeable_p(X86_EFLAGS_AC)) + cpu->device = 0x00000400; /* 486 */ + else + cpu->device = 0x00000300; /* 386 */ + if ((cpu->device == 0x00000400) && test_cyrix_52div()) { + memcpy(vendor_name, "CyrixInstead", 13); + /* If we ever care we can enable cpuid here */ + } + /* Detect NexGen with old hypercode */ + else if (deep_magic_nexgen_probe()) + memcpy(vendor_name, "NexGenDriven", 13); + } + if (has_cpuid()) { + int cpuid_level; + + cpuid_level = build_vendor_name(vendor_name); + vendor_name[12] = '\0'; + + /* Intel-defined flags: level 0x00000001 */ + if (cpuid_level >= 0x00000001) { + cpu->device = cpuid_eax(0x00000001); + } else { + /* Have CPUID level 0 only unheard of */ + cpu->device = 0x00000400; + } + } + cpu->vendor = X86_VENDOR_UNKNOWN; + for (i = 0; i < ARRAY_SIZE(x86_vendors); i++) { + if (memcmp(vendor_name, x86_vendors[i].name, 12) == 0) { + cpu->vendor = x86_vendors[i].vendor; + break; + } + } +} + +static inline void get_fms(struct cpuinfo_x86 *c, uint32_t tfms) +{ + c->x86 = (tfms >> 8) & 0xf; + c->x86_model = (tfms >> 4) & 0xf; + c->x86_mask = tfms & 0xf; + if (c->x86 == 0xf) + c->x86 += (tfms >> 20) & 0xff; + if (c->x86 >= 0x6) + c->x86_model += ((tfms >> 16) & 0xF) << 4; +} + int x86_cpu_init_f(void) { const u32 em_rst = ~X86_CR0_EM; @@ -128,9 +303,22 @@ int x86_cpu_init_f(void) "movl %%eax, %%cr0\n" \ : : "i" (em_rst), "i" (mp_ne_set) : "eax"); + /* identify CPU via cpuid and store the decoded info into gd->arch */ + if (has_cpuid()) { + struct cpu_device_id cpu; + struct cpuinfo_x86 c; + + identify_cpu(&cpu); + get_fms(&c, cpu.device); + gd->arch.x86 = c.x86; + gd->arch.x86_vendor = cpu.vendor; + gd->arch.x86_model = c.x86_model; + gd->arch.x86_mask = c.x86_mask; + gd->arch.x86_device = cpu.device; + } + return 0; } -int cpu_init_f(void) __attribute__((weak, alias("x86_cpu_init_f"))); int x86_cpu_init_r(void) { @@ -198,14 +386,13 @@ asm(".globl generate_gpf\n" "generate_gpf:\n" "ljmp $0x70, $0x47114711\n"); -void __reset_cpu(ulong addr) +__weak void reset_cpu(ulong addr) { printf("Resetting using x86 Triple Fault\n"); set_vector(13, generate_gpf); /* general protection fault handler */ set_vector(8, generate_gpf); /* double fault handler */ generate_gpf(); /* start the show */ } -void reset_cpu(ulong addr) __attribute__((weak, alias("__reset_cpu"))); int dcache_status(void) { @@ -279,66 +466,63 @@ void cpu_disable_paging_pae(void) : "eax"); } -static bool has_cpuid(void) +static bool can_detect_long_mode(void) { - unsigned long flag; - - asm volatile("pushf\n" \ - "pop %%eax\n" - "mov %%eax, %%ecx\n" /* ecx = flags */ - "xor %1, %%eax\n" - "push %%eax\n" - "popf\n" /* flags ^= $2 */ - "pushf\n" - "pop %%eax\n" /* eax = flags */ - "push %%ecx\n" - "popf\n" /* flags = ecx */ - "xor %%ecx, %%eax\n" - "mov %%eax, %0" - : "=r" (flag) - : "i" (1 << 21) - : "eax", "ecx", "memory"); + return cpuid_eax(0x80000000) > 0x80000000UL; +} - return flag != 0; +static bool has_long_mode(void) +{ + return cpuid_edx(0x80000001) & (1 << 29) ? true : false; } -static bool can_detect_long_mode(void) +int cpu_has_64bit(void) { - unsigned long flag; + return has_cpuid() && can_detect_long_mode() && + has_long_mode(); +} - asm volatile("mov $0x80000000, %%eax\n" - "cpuid\n" - "mov %%eax, %0" - : "=r" (flag) - : - : "eax", "ebx", "ecx", "edx", "memory"); +const char *cpu_vendor_name(int vendor) +{ + const char *name; + name = "<invalid cpu vendor>"; + if ((vendor < (ARRAY_SIZE(x86_vendor_name))) && + (x86_vendor_name[vendor] != 0)) + name = x86_vendor_name[vendor]; - return flag > 0x80000000UL; + return name; } -static bool has_long_mode(void) +char *cpu_get_name(char *name) { - unsigned long flag; + unsigned int *name_as_ints = (unsigned int *)name; + struct cpuid_result regs; + char *ptr; + int i; - asm volatile("mov $0x80000001, %%eax\n" - "cpuid\n" - "mov %%edx, %0" - : "=r" (flag) - : - : "eax", "ebx", "ecx", "edx", "memory"); + /* This bit adds up to 48 bytes */ + for (i = 0; i < 3; i++) { + regs = cpuid(0x80000002 + i); + name_as_ints[i * 4 + 0] = regs.eax; + name_as_ints[i * 4 + 1] = regs.ebx; + name_as_ints[i * 4 + 2] = regs.ecx; + name_as_ints[i * 4 + 3] = regs.edx; + } + name[CPU_MAX_NAME_LEN - 1] = '\0'; - return flag & (1 << 29) ? true : false; -} + /* Skip leading spaces. */ + ptr = name; + while (*ptr == ' ') + ptr++; -int cpu_has_64bit(void) -{ - return has_cpuid() && can_detect_long_mode() && - has_long_mode(); + return ptr; } -int print_cpuinfo(void) +int default_print_cpuinfo(void) { - printf("CPU: %s\n", cpu_has_64bit() ? "x86_64" : "x86"); + printf("CPU: %s, vendor %s, device %xh\n", + cpu_has_64bit() ? "x86_64" : "x86", + cpu_vendor_name(gd->arch.x86_vendor), gd->arch.x86_device); return 0; } @@ -384,3 +568,26 @@ int cpu_jump_to_64bit(ulong setup_base, ulong target) return -EFAULT; } + +void show_boot_progress(int val) +{ +#if MIN_PORT80_KCLOCKS_DELAY + /* + * Scale the time counter reading to avoid using 64 bit arithmetics. + * Can't use get_timer() here becuase it could be not yet + * initialized or even implemented. + */ + if (!gd->arch.tsc_prev) { + gd->arch.tsc_base_kclocks = rdtsc() / 1000; + gd->arch.tsc_prev = 0; + } else { + uint32_t now; + + do { + now = rdtsc() / 1000 - gd->arch.tsc_base_kclocks; + } while (now < (gd->arch.tsc_prev + MIN_PORT80_KCLOCKS_DELAY)); + gd->arch.tsc_prev = now; + } +#endif + outb(val, POST_PORT); +} diff --git a/arch/x86/cpu/interrupts.c b/arch/x86/cpu/interrupts.c index 6f3d85f..51e2c59 100644 --- a/arch/x86/cpu/interrupts.c +++ b/arch/x86/cpu/interrupts.c @@ -31,7 +31,7 @@ DECLARE_GLOBAL_DATA_PTR; "pushl $"#x"\n" \ "jmp irq_common_entry\n" -void dump_regs(struct irq_regs *regs) +static void dump_regs(struct irq_regs *regs) { unsigned long cr0 = 0L, cr2 = 0L, cr3 = 0L, cr4 = 0L; unsigned long d0, d1, d2, d3, d6, d7; diff --git a/arch/x86/cpu/ivybridge/Kconfig b/arch/x86/cpu/ivybridge/Kconfig new file mode 100644 index 0000000..afca957 --- /dev/null +++ b/arch/x86/cpu/ivybridge/Kconfig @@ -0,0 +1,172 @@ +# +# From Coreboot src/northbridge/intel/sandybridge/Kconfig +# +# Copyright (C) 2010 Google Inc. +# +# SPDX-License-Identifier: GPL-2.0 + + +config NORTHBRIDGE_INTEL_SANDYBRIDGE + bool + select CACHE_MRC_BIN + select CPU_INTEL_MODEL_206AX + +config NORTHBRIDGE_INTEL_IVYBRIDGE + bool + select CACHE_MRC_BIN + select CPU_INTEL_MODEL_306AX + +if NORTHBRIDGE_INTEL_SANDYBRIDGE + +config VGA_BIOS_ID + string + default "8086,0106" + +config CACHE_MRC_SIZE_KB + int + default 256 + +config MRC_CACHE_BASE + hex + default 0xff800000 + +config MRC_CACHE_LOCATION + hex + depends on !CHROMEOS + default 0x1ec000 + +config MRC_CACHE_SIZE + hex + depends on !CHROMEOS + default 0x10000 + +config DCACHE_RAM_BASE + hex + default 0xff7f0000 + +config DCACHE_RAM_SIZE + hex + default 0x10000 + +endif + +if NORTHBRIDGE_INTEL_IVYBRIDGE + +config VGA_BIOS_ID + string + default "8086,0166" + +config EXTERNAL_MRC_BLOB + bool + default n + +config CACHE_MRC_SIZE_KB + int + default 512 + +config MRC_CACHE_BASE + hex + default 0xff800000 + +config MRC_CACHE_LOCATION + hex + depends on !CHROMEOS + default 0x370000 + +config MRC_CACHE_SIZE + hex + depends on !CHROMEOS + default 0x10000 + +config DCACHE_RAM_BASE + hex + default 0xff7e0000 + +config DCACHE_RAM_SIZE + hex + default 0x20000 + +endif + +if NORTHBRIDGE_INTEL_SANDYBRIDGE || NORTHBRIDGE_INTEL_IVYBRIDGE + +config HAVE_MRC + bool "Add a System Agent binary" + help + Select this option to add a System Agent binary to + the resulting U-Boot image. MRC stands for Memory Reference Code. + It is a binary blob which U-Boot uses to set up SDRAM. + + Note: Without this binary U-Boot will not be able to set up its + SDRAM so will not boot. + +config DCACHE_RAM_MRC_VAR_SIZE + hex + default 0x4000 + help + This is the amount of CAR (Cache as RAM) reserved for use by the + memory reference code. This should be set to 16KB (0x4000 hex) + so that MRC has enough space to run. + +config MRC_FILE + string "Intel System Agent path and filename" + depends on HAVE_MRC + default "systemagent-ivybridge.bin" if NORTHBRIDGE_INTEL_IVYBRIDGE + default "systemagent-sandybridge.bin" if NORTHBRIDGE_INTEL_SANDYBRIDGE + help + The path and filename of the file to use as System Agent + binary. + +config CPU_SPECIFIC_OPTIONS + def_bool y + select SMM_TSEG + select ARCH_BOOTBLOCK_X86_32 + select ARCH_ROMSTAGE_X86_32 + select ARCH_RAMSTAGE_X86_32 + select SMP + select SSE2 + select UDELAY_LAPIC + select CPU_MICROCODE_IN_CBFS + select TSC_SYNC_MFENCE + select HAVE_INTEL_ME + select X86_RAMTEST + +config SMM_TSEG_SIZE + hex + default 0x800000 + +config ENABLE_VMX + bool "Enable VMX for virtualization" + default n + help + Virtual Machine Extensions are provided in many x86 CPUs. These + provide various facilities for allowing a host OS to provide an + environment where potentially several guest OSes have only + limited access to the underlying hardware. This is achieved + without resorting to software trapping and/or instruction set + emulation (which would be very slow). + + Intel's implementation of this is called VT-x. This option enables + VT-x this so that the OS that is booted by U-Boot can make use of + these facilities. If this option is not enabled, then the host OS + will be unable to support virtualisation, or it will run very + slowly. + +endif + +config CPU_INTEL_SOCKET_RPGA989 + bool + +if CPU_INTEL_SOCKET_RPGA989 + +config SOCKET_SPECIFIC_OPTIONS # dummy + def_bool y + select MMX + select SSE + select CACHE_AS_RAM + +config CACHE_MRC_BIN + bool + default n + +endif diff --git a/arch/x86/cpu/ivybridge/Makefile b/arch/x86/cpu/ivybridge/Makefile new file mode 100644 index 0000000..721b37e --- /dev/null +++ b/arch/x86/cpu/ivybridge/Makefile @@ -0,0 +1,16 @@ +# +# Copyright (c) 2014 Google, Inc +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y += car.o +obj-y += cpu.o +obj-y += early_init.o +obj-y += early_me.o +obj-y += lpc.o +obj-y += me_status.o +obj-y += microcode_intel.o +obj-y += pci.o +obj-y += report_platform.o +obj-y += sdram.o diff --git a/arch/x86/cpu/ivybridge/car.S b/arch/x86/cpu/ivybridge/car.S new file mode 100644 index 0000000..dca68e4 --- /dev/null +++ b/arch/x86/cpu/ivybridge/car.S @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2014 Google, Inc + * + * From Coreboot file cpu/intel/model_206ax/cache_as_ram.inc + * + * Copyright (C) 2000,2007 Ronald G. Minnich <rminnich@gmail.com> + * Copyright (C) 2005 Tyan (written by Yinghai Lu for Tyan) + * Copyright (C) 2007-2008 coresystems GmbH + * Copyright (C) 2012 Kyösti Mälkki <kyosti.malkki@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <asm/mtrr.h> +#include <asm/post.h> +#include <asm/processor-flags.h> + +#define MTRR_PHYS_BASE_MSR(reg) (0x200 + 2 * (reg)) +#define MTRR_PHYS_MASK_MSR(reg) (0x200 + 2 * (reg) + 1) + +#define CACHE_AS_RAM_SIZE CONFIG_DCACHE_RAM_SIZE +#define CACHE_AS_RAM_BASE CONFIG_DCACHE_RAM_BASE + +/* Cache 4GB - MRC_SIZE_KB for MRC */ +#define CACHE_MRC_BYTES ((CONFIG_CACHE_MRC_SIZE_KB << 10) - 1) +#define CACHE_MRC_BASE (0xFFFFFFFF - CACHE_MRC_BYTES) +#define CACHE_MRC_MASK (~CACHE_MRC_BYTES) + +#define CPU_PHYSMASK_HI (1 << (CONFIG_CPU_ADDR_BITS - 32) - 1) + +#define NOEVICTMOD_MSR 0x2e0 + + /* + * Note: ebp must not be touched in this code as it holds the BIST + * value (built-in self test). We preserve this value until it can + * be written to global_data when CAR is ready for use. + */ +.globl car_init +car_init: + post_code(POST_CAR_START) + + /* Send INIT IPI to all excluding ourself */ + movl $0x000C4500, %eax + movl $0xFEE00300, %esi + movl %eax, (%esi) + + post_code(POST_CAR_SIPI) + /* Zero out all fixed range and variable range MTRRs */ + movl $mtrr_table, %esi + movl $((mtrr_table_end - mtrr_table) / 2), %edi + xorl %eax, %eax + xorl %edx, %edx +clear_mtrrs: + movw (%esi), %bx + movzx %bx, %ecx + wrmsr + add $2, %esi + dec %edi + jnz clear_mtrrs + + post_code(POST_CAR_MTRR) + /* Configure the default memory type to uncacheable */ + movl $MTRRdefType_MSR, %ecx + rdmsr + andl $(~0x00000cff), %eax + wrmsr + + post_code(POST_CAR_UNCACHEABLE) + /* Set Cache-as-RAM base address */ + movl $(MTRR_PHYS_BASE_MSR(0)), %ecx + movl $(CACHE_AS_RAM_BASE | MTRR_TYPE_WRBACK), %eax + xorl %edx, %edx + wrmsr + + post_code(POST_CAR_BASE_ADDRESS) + /* Set Cache-as-RAM mask */ + movl $(MTRR_PHYS_MASK_MSR(0)), %ecx + movl $(~(CACHE_AS_RAM_SIZE - 1) | MTRRphysMaskValid), %eax + movl $CPU_PHYSMASK_HI, %edx + wrmsr + + post_code(POST_CAR_MASK) + + /* Enable MTRR */ + movl $MTRRdefType_MSR, %ecx + rdmsr + orl $MTRRdefTypeEn, %eax + wrmsr + + /* Enable cache (CR0.CD = 0, CR0.NW = 0) */ + movl %cr0, %eax + andl $(~(X86_CR0_CD | X86_CR0_NW)), %eax + invd + movl %eax, %cr0 + + /* enable the 'no eviction' mode */ + movl $NOEVICTMOD_MSR, %ecx + rdmsr + orl $1, %eax + andl $~2, %eax + wrmsr + + /* Clear the cache memory region. This will also fill up the cache */ + movl $CACHE_AS_RAM_BASE, %esi + movl %esi, %edi + movl $(CACHE_AS_RAM_SIZE / 4), %ecx + xorl %eax, %eax + rep stosl + + /* enable the 'no eviction run' state */ + movl $NOEVICTMOD_MSR, %ecx + rdmsr + orl $3, %eax + wrmsr + + post_code(POST_CAR_FILL) + /* Enable Cache-as-RAM mode by disabling cache */ + movl %cr0, %eax + orl $X86_CR0_CD, %eax + movl %eax, %cr0 + + /* Enable cache for our code in Flash because we do XIP here */ + movl $MTRR_PHYS_BASE_MSR(1), %ecx + xorl %edx, %edx + movl $car_init_ret, %eax + andl $(~(CONFIG_XIP_ROM_SIZE - 1)), %eax + orl $MTRR_TYPE_WRPROT, %eax + wrmsr + + movl $MTRR_PHYS_MASK_MSR(1), %ecx + movl $CPU_PHYSMASK_HI, %edx + movl $(~(CONFIG_XIP_ROM_SIZE - 1) | MTRRphysMaskValid), %eax + wrmsr + + post_code(POST_CAR_ROM_CACHE) +#ifdef CONFIG_CACHE_MRC_BIN + /* Enable caching for ram init code to run faster */ + movl $MTRR_PHYS_BASE_MSR(2), %ecx + movl $(CACHE_MRC_BASE | MTRR_TYPE_WRPROT), %eax + xorl %edx, %edx + wrmsr + movl $MTRR_PHYS_MASK_MSR(2), %ecx + movl $(CACHE_MRC_MASK | MTRRphysMaskValid), %eax + movl $CPU_PHYSMASK_HI, %edx + wrmsr +#endif + + post_code(POST_CAR_MRC_CACHE) + /* Enable cache */ + movl %cr0, %eax + andl $(~(X86_CR0_CD | X86_CR0_NW)), %eax + movl %eax, %cr0 + + post_code(POST_CAR_CPU_CACHE) + + /* All CPUs need to be in Wait for SIPI state */ +wait_for_sipi: + movl (%esi), %eax + bt $12, %eax + jc wait_for_sipi + + /* return */ + jmp car_init_ret + +mtrr_table: + /* Fixed MTRRs */ + .word 0x250, 0x258, 0x259 + .word 0x268, 0x269, 0x26A + .word 0x26B, 0x26C, 0x26D + .word 0x26E, 0x26F + /* Variable MTRRs */ + .word 0x200, 0x201, 0x202, 0x203 + .word 0x204, 0x205, 0x206, 0x207 + .word 0x208, 0x209, 0x20A, 0x20B + .word 0x20C, 0x20D, 0x20E, 0x20F + .word 0x210, 0x211, 0x212, 0x213 +mtrr_table_end: diff --git a/arch/x86/cpu/ivybridge/cpu.c b/arch/x86/cpu/ivybridge/cpu.c new file mode 100644 index 0000000..60976db --- /dev/null +++ b/arch/x86/cpu/ivybridge/cpu.c @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2014 Google, Inc + * (C) Copyright 2008 + * Graeme Russ, graeme.russ@gmail.com. + * + * Some portions from coreboot src/mainboard/google/link/romstage.c + * and src/cpu/intel/model_206ax/bootblock.c + * Copyright (C) 2007-2010 coresystems GmbH + * Copyright (C) 2011 Google Inc. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <errno.h> +#include <fdtdec.h> +#include <asm/cpu.h> +#include <asm/io.h> +#include <asm/lapic.h> +#include <asm/msr.h> +#include <asm/mtrr.h> +#include <asm/pci.h> +#include <asm/post.h> +#include <asm/processor.h> +#include <asm/arch/model_206ax.h> +#include <asm/arch/microcode.h> +#include <asm/arch/pch.h> +#include <asm/arch/sandybridge.h> + +DECLARE_GLOBAL_DATA_PTR; + +static void enable_port80_on_lpc(struct pci_controller *hose, pci_dev_t dev) +{ + /* Enable port 80 POST on LPC */ + pci_hose_write_config_dword(hose, dev, PCH_RCBA_BASE, DEFAULT_RCBA | 1); + clrbits_le32(RCB_REG(GCS), 4); +} + +/* + * Enable Prefetching and Caching. + */ +static void enable_spi_prefetch(struct pci_controller *hose, pci_dev_t dev) +{ + u8 reg8; + + pci_hose_read_config_byte(hose, dev, 0xdc, ®8); + reg8 &= ~(3 << 2); + reg8 |= (2 << 2); /* Prefetching and Caching Enabled */ + pci_hose_write_config_byte(hose, dev, 0xdc, reg8); +} + +static void set_var_mtrr( + unsigned reg, unsigned base, unsigned size, unsigned type) + +{ + /* Bit Bit 32-35 of MTRRphysMask should be set to 1 */ + /* FIXME: It only support 4G less range */ + wrmsr(MTRRphysBase_MSR(reg), base | type, 0); + wrmsr(MTRRphysMask_MSR(reg), ~(size - 1) | MTRRphysMaskValid, + (1 << (CONFIG_CPU_ADDR_BITS - 32)) - 1); +} + +static void enable_rom_caching(void) +{ + disable_caches(); + set_var_mtrr(1, 0xffc00000, 4 << 20, MTRR_TYPE_WRPROT); + enable_caches(); + + /* Enable Variable MTRRs */ + wrmsr(MTRRdefType_MSR, 0x800, 0); +} + +static int set_flex_ratio_to_tdp_nominal(void) +{ + msr_t flex_ratio, msr; + u8 nominal_ratio; + + /* Minimum CPU revision for configurable TDP support */ + if (cpuid_eax(1) < IVB_CONFIG_TDP_MIN_CPUID) + return -EINVAL; + + /* Check for Flex Ratio support */ + flex_ratio = msr_read(MSR_FLEX_RATIO); + if (!(flex_ratio.lo & FLEX_RATIO_EN)) + return -EINVAL; + + /* Check for >0 configurable TDPs */ + msr = msr_read(MSR_PLATFORM_INFO); + if (((msr.hi >> 1) & 3) == 0) + return -EINVAL; + + /* Use nominal TDP ratio for flex ratio */ + msr = msr_read(MSR_CONFIG_TDP_NOMINAL); + nominal_ratio = msr.lo & 0xff; + + /* See if flex ratio is already set to nominal TDP ratio */ + if (((flex_ratio.lo >> 8) & 0xff) == nominal_ratio) + return 0; + + /* Set flex ratio to nominal TDP ratio */ + flex_ratio.lo &= ~0xff00; + flex_ratio.lo |= nominal_ratio << 8; + flex_ratio.lo |= FLEX_RATIO_LOCK; + msr_write(MSR_FLEX_RATIO, flex_ratio); + + /* Set flex ratio in soft reset data register bits 11:6 */ + clrsetbits_le32(RCB_REG(SOFT_RESET_DATA), 0x3f << 6, + (nominal_ratio & 0x3f) << 6); + + /* Set soft reset control to use register value */ + setbits_le32(RCB_REG(SOFT_RESET_CTRL), 1); + + /* Issue warm reset, will be "CPU only" due to soft reset data */ + outb(0x0, PORT_RESET); + outb(0x6, PORT_RESET); + cpu_hlt(); + + /* Not reached */ + return -EINVAL; +} + +static void set_spi_speed(void) +{ + u32 fdod; + + /* Observe SPI Descriptor Component Section 0 */ + writel(0x1000, RCB_REG(SPI_DESC_COMP0)); + + /* Extract the1 Write/Erase SPI Frequency from descriptor */ + fdod = readl(RCB_REG(SPI_FREQ_WR_ERA)); + fdod >>= 24; + fdod &= 7; + + /* Set Software Sequence frequency to match */ + clrsetbits_8(RCB_REG(SPI_FREQ_SWSEQ), 7, fdod); +} + +int arch_cpu_init(void) +{ + const void *blob = gd->fdt_blob; + struct pci_controller *hose; + int node; + int ret; + + post_code(POST_CPU_INIT); + timer_set_base(rdtsc()); + + ret = x86_cpu_init_f(); + if (ret) + return ret; + + ret = pci_early_init_hose(&hose); + if (ret) + return ret; + + node = fdtdec_next_compatible(blob, 0, COMPAT_INTEL_LPC); + if (node < 0) + return -ENOENT; + ret = lpc_early_init(gd->fdt_blob, node, PCH_LPC_DEV); + if (ret) + return ret; + + enable_spi_prefetch(hose, PCH_LPC_DEV); + + /* This is already done in start.S, but let's do it in C */ + enable_port80_on_lpc(hose, PCH_LPC_DEV); + + /* already done in car.S */ + if (false) + enable_rom_caching(); + + set_spi_speed(); + + /* + * We should do as little as possible before the serial console is + * up. Perhaps this should move to later. Our next lot of init + * happens in print_cpuinfo() when we have a console + */ + ret = set_flex_ratio_to_tdp_nominal(); + if (ret) + return ret; + + return 0; +} + +static int enable_smbus(void) +{ + pci_dev_t dev; + uint16_t value; + + /* Set the SMBus device statically. */ + dev = PCI_BDF(0x0, 0x1f, 0x3); + + /* Check to make sure we've got the right device. */ + value = pci_read_config16(dev, 0x0); + if (value != 0x8086) { + printf("SMBus controller not found\n"); + return -ENOSYS; + } + + /* Set SMBus I/O base. */ + pci_write_config32(dev, SMB_BASE, + SMBUS_IO_BASE | PCI_BASE_ADDRESS_SPACE_IO); + + /* Set SMBus enable. */ + pci_write_config8(dev, HOSTC, HST_EN); + + /* Set SMBus I/O space enable. */ + pci_write_config16(dev, PCI_COMMAND, PCI_COMMAND_IO); + + /* Disable interrupt generation. */ + outb(0, SMBUS_IO_BASE + SMBHSTCTL); + + /* Clear any lingering errors, so transactions can run. */ + outb(inb(SMBUS_IO_BASE + SMBHSTSTAT), SMBUS_IO_BASE + SMBHSTSTAT); + debug("SMBus controller enabled\n"); + + return 0; +} + +#define PCH_EHCI0_TEMP_BAR0 0xe8000000 +#define PCH_EHCI1_TEMP_BAR0 0xe8000400 +#define PCH_XHCI_TEMP_BAR0 0xe8001000 + +/* + * Setup USB controller MMIO BAR to prevent the reference code from + * resetting the controller. + * + * The BAR will be re-assigned during device enumeration so these are only + * temporary. + * + * This is used to speed up the resume path. + */ +static void enable_usb_bar(void) +{ + pci_dev_t usb0 = PCH_EHCI1_DEV; + pci_dev_t usb1 = PCH_EHCI2_DEV; + pci_dev_t usb3 = PCH_XHCI_DEV; + u32 cmd; + + /* USB Controller 1 */ + pci_write_config32(usb0, PCI_BASE_ADDRESS_0, + PCH_EHCI0_TEMP_BAR0); + cmd = pci_read_config32(usb0, PCI_COMMAND); + cmd |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; + pci_write_config32(usb0, PCI_COMMAND, cmd); + + /* USB Controller 1 */ + pci_write_config32(usb1, PCI_BASE_ADDRESS_0, + PCH_EHCI1_TEMP_BAR0); + cmd = pci_read_config32(usb1, PCI_COMMAND); + cmd |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; + pci_write_config32(usb1, PCI_COMMAND, cmd); + + /* USB3 Controller */ + pci_write_config32(usb3, PCI_BASE_ADDRESS_0, + PCH_XHCI_TEMP_BAR0); + cmd = pci_read_config32(usb3, PCI_COMMAND); + cmd |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; + pci_write_config32(usb3, PCI_COMMAND, cmd); +} + +static int report_bist_failure(void) +{ + if (gd->arch.bist != 0) { + printf("BIST failed: %08x\n", gd->arch.bist); + return -EFAULT; + } + + return 0; +} + +int print_cpuinfo(void) +{ + enum pei_boot_mode_t boot_mode = PEI_BOOT_NONE; + char processor_name[CPU_MAX_NAME_LEN]; + const char *name; + uint32_t pm1_cnt; + uint16_t pm1_sts; + int ret; + + /* Halt if there was a built in self test failure */ + ret = report_bist_failure(); + if (ret) + return ret; + + enable_lapic(); + + ret = microcode_update_intel(); + if (ret && ret != -ENOENT && ret != -EEXIST) + return ret; + + /* Enable upper 128bytes of CMOS */ + writel(1 << 2, RCB_REG(RC)); + + /* TODO: cmos_post_init() */ + if (readl(MCHBAR_REG(SSKPD)) == 0xCAFE) { + debug("soft reset detected\n"); + boot_mode = PEI_BOOT_SOFT_RESET; + + /* System is not happy after keyboard reset... */ + debug("Issuing CF9 warm reset\n"); + outb(0x6, 0xcf9); + cpu_hlt(); + } + + /* Early chipset init required before RAM init can work */ + sandybridge_early_init(SANDYBRIDGE_MOBILE); + + /* Check PM1_STS[15] to see if we are waking from Sx */ + pm1_sts = inw(DEFAULT_PMBASE + PM1_STS); + + /* Read PM1_CNT[12:10] to determine which Sx state */ + pm1_cnt = inl(DEFAULT_PMBASE + PM1_CNT); + + if ((pm1_sts & WAK_STS) && ((pm1_cnt >> 10) & 7) == 5) { +#if CONFIG_HAVE_ACPI_RESUME + debug("Resume from S3 detected.\n"); + boot_mode = PEI_BOOT_RESUME; + /* Clear SLP_TYPE. This will break stage2 but + * we care for that when we get there. + */ + outl(pm1_cnt & ~(7 << 10), DEFAULT_PMBASE + PM1_CNT); +#else + debug("Resume from S3 detected, but disabled.\n"); +#endif + } else { + /* + * TODO: An indication of life might be possible here (e.g. + * keyboard light) + */ + } + post_code(POST_EARLY_INIT); + + /* Enable SPD ROMs and DDR-III DRAM */ + ret = enable_smbus(); + if (ret) + return ret; + + /* Prepare USB controller early in S3 resume */ + if (boot_mode == PEI_BOOT_RESUME) + enable_usb_bar(); + + gd->arch.pei_boot_mode = boot_mode; + + /* TODO: Move this to the board or driver */ + pci_write_config32(PCH_LPC_DEV, GPIO_BASE, DEFAULT_GPIOBASE | 1); + pci_write_config32(PCH_LPC_DEV, GPIO_CNTL, 0x10); + + /* Print processor name */ + name = cpu_get_name(processor_name); + printf("CPU: %s\n", name); + + post_code(POST_CPU_INFO); + + return 0; +} diff --git a/arch/x86/cpu/ivybridge/early_init.c b/arch/x86/cpu/ivybridge/early_init.c new file mode 100644 index 0000000..eb8f613 --- /dev/null +++ b/arch/x86/cpu/ivybridge/early_init.c @@ -0,0 +1,145 @@ +/* + * From Coreboot + * + * Copyright (C) 2007-2010 coresystems GmbH + * Copyright (C) 2011 Google Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/pci.h> +#include <asm/arch/pch.h> +#include <asm/arch/sandybridge.h> + +static void sandybridge_setup_bars(pci_dev_t pch_dev, pci_dev_t lpc_dev) +{ + /* Setting up Southbridge. In the northbridge code. */ + debug("Setting up static southbridge registers\n"); + pci_write_config32(lpc_dev, PCH_RCBA_BASE, DEFAULT_RCBA | 1); + + pci_write_config32(lpc_dev, PMBASE, DEFAULT_PMBASE | 1); + pci_write_config8(lpc_dev, ACPI_CNTL, 0x80); /* Enable ACPI BAR */ + + debug("Disabling watchdog reboot\n"); + setbits_le32(RCB_REG(GCS), 1 >> 5); /* No reset */ + outw(1 << 11, DEFAULT_PMBASE | 0x60 | 0x08); /* halt timer */ + + /* Set up all hardcoded northbridge BARs */ + debug("Setting up static registers\n"); + pci_write_config32(pch_dev, EPBAR, DEFAULT_EPBAR | 1); + pci_write_config32(pch_dev, EPBAR + 4, (0LL + DEFAULT_EPBAR) >> 32); + pci_write_config32(pch_dev, MCHBAR, DEFAULT_MCHBAR | 1); + pci_write_config32(pch_dev, MCHBAR + 4, (0LL + DEFAULT_MCHBAR) >> 32); + /* 64MB - busses 0-63 */ + pci_write_config32(pch_dev, PCIEXBAR, DEFAULT_PCIEXBAR | 5); + pci_write_config32(pch_dev, PCIEXBAR + 4, + (0LL + DEFAULT_PCIEXBAR) >> 32); + pci_write_config32(pch_dev, DMIBAR, DEFAULT_DMIBAR | 1); + pci_write_config32(pch_dev, DMIBAR + 4, (0LL + DEFAULT_DMIBAR) >> 32); + + /* Set C0000-FFFFF to access RAM on both reads and writes */ + pci_write_config8(pch_dev, PAM0, 0x30); + pci_write_config8(pch_dev, PAM1, 0x33); + pci_write_config8(pch_dev, PAM2, 0x33); + pci_write_config8(pch_dev, PAM3, 0x33); + pci_write_config8(pch_dev, PAM4, 0x33); + pci_write_config8(pch_dev, PAM5, 0x33); + pci_write_config8(pch_dev, PAM6, 0x33); +} + +static void sandybridge_setup_graphics(pci_dev_t pch_dev, pci_dev_t video_dev) +{ + u32 reg32; + u16 reg16; + u8 reg8; + + reg16 = pci_read_config16(video_dev, PCI_DEVICE_ID); + switch (reg16) { + case 0x0102: /* GT1 Desktop */ + case 0x0106: /* GT1 Mobile */ + case 0x010a: /* GT1 Server */ + case 0x0112: /* GT2 Desktop */ + case 0x0116: /* GT2 Mobile */ + case 0x0122: /* GT2 Desktop >=1.3GHz */ + case 0x0126: /* GT2 Mobile >=1.3GHz */ + case 0x0156: /* IvyBridge */ + case 0x0166: /* IvyBridge */ + break; + default: + debug("Graphics not supported by this CPU/chipset\n"); + return; + } + + debug("Initialising Graphics\n"); + + /* Setup IGD memory by setting GGC[7:3] = 1 for 32MB */ + reg16 = pci_read_config16(pch_dev, GGC); + reg16 &= ~0x00f8; + reg16 |= 1 << 3; + /* Program GTT memory by setting GGC[9:8] = 2MB */ + reg16 &= ~0x0300; + reg16 |= 2 << 8; + /* Enable VGA decode */ + reg16 &= ~0x0002; + pci_write_config16(pch_dev, GGC, reg16); + + /* Enable 256MB aperture */ + reg8 = pci_read_config8(video_dev, MSAC); + reg8 &= ~0x06; + reg8 |= 0x02; + pci_write_config8(video_dev, MSAC, reg8); + + /* Erratum workarounds */ + reg32 = readl(MCHBAR_REG(0x5f00)); + reg32 |= (1 << 9) | (1 << 10); + writel(reg32, MCHBAR_REG(0x5f00)); + + /* Enable SA Clock Gating */ + reg32 = readl(MCHBAR_REG(0x5f00)); + writel(reg32 | 1, MCHBAR_REG(0x5f00)); + + /* GPU RC6 workaround for sighting 366252 */ + reg32 = readl(MCHBAR_REG(0x5d14)); + reg32 |= (1 << 31); + writel(reg32, MCHBAR_REG(0x5d14)); + + /* VLW */ + reg32 = readl(MCHBAR_REG(0x6120)); + reg32 &= ~(1 << 0); + writel(reg32, MCHBAR_REG(0x6120)); + + reg32 = readl(MCHBAR_REG(0x5418)); + reg32 |= (1 << 4) | (1 << 5); + writel(reg32, MCHBAR_REG(0x5418)); +} + +void sandybridge_early_init(int chipset_type) +{ + pci_dev_t pch_dev = PCH_DEV; + pci_dev_t video_dev = PCH_VIDEO_DEV; + pci_dev_t lpc_dev = PCH_LPC_DEV; + u32 capid0_a; + u8 reg8; + + /* Device ID Override Enable should be done very early */ + capid0_a = pci_read_config32(pch_dev, 0xe4); + if (capid0_a & (1 << 10)) { + reg8 = pci_read_config8(pch_dev, 0xf3); + reg8 &= ~7; /* Clear 2:0 */ + + if (chipset_type == SANDYBRIDGE_MOBILE) + reg8 |= 1; /* Set bit 0 */ + + pci_write_config8(pch_dev, 0xf3, reg8); + } + + /* Setup all BARs required for early PCIe and raminit */ + sandybridge_setup_bars(pch_dev, lpc_dev); + + /* Device Enable */ + pci_write_config32(pch_dev, DEVEN, DEVEN_HOST | DEVEN_IGD); + + sandybridge_setup_graphics(pch_dev, video_dev); +} diff --git a/arch/x86/cpu/ivybridge/early_me.c b/arch/x86/cpu/ivybridge/early_me.c new file mode 100644 index 0000000..b24dea1 --- /dev/null +++ b/arch/x86/cpu/ivybridge/early_me.c @@ -0,0 +1,191 @@ +/* + * From Coreboot src/southbridge/intel/bd82x6x/early_me.c + * + * Copyright (C) 2011 The Chromium OS Authors. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <errno.h> +#include <asm/pci.h> +#include <asm/processor.h> +#include <asm/arch/me.h> +#include <asm/arch/pch.h> +#include <asm/io.h> + +static const char *const me_ack_values[] = { + [ME_HFS_ACK_NO_DID] = "No DID Ack received", + [ME_HFS_ACK_RESET] = "Non-power cycle reset", + [ME_HFS_ACK_PWR_CYCLE] = "Power cycle reset", + [ME_HFS_ACK_S3] = "Go to S3", + [ME_HFS_ACK_S4] = "Go to S4", + [ME_HFS_ACK_S5] = "Go to S5", + [ME_HFS_ACK_GBL_RESET] = "Global Reset", + [ME_HFS_ACK_CONTINUE] = "Continue to boot" +}; + +static inline void pci_read_dword_ptr(void *ptr, int offset) +{ + u32 dword; + + dword = pci_read_config32(PCH_ME_DEV, offset); + memcpy(ptr, &dword, sizeof(dword)); +} + +static inline void pci_write_dword_ptr(void *ptr, int offset) +{ + u32 dword = 0; + memcpy(&dword, ptr, sizeof(dword)); + pci_write_config32(PCH_ME_DEV, offset, dword); +} + +void intel_early_me_status(void) +{ + struct me_hfs hfs; + struct me_gmes gmes; + + pci_read_dword_ptr(&hfs, PCI_ME_HFS); + pci_read_dword_ptr(&gmes, PCI_ME_GMES); + + intel_me_status(&hfs, &gmes); +} + +int intel_early_me_init(void) +{ + int count; + struct me_uma uma; + struct me_hfs hfs; + + debug("Intel ME early init\n"); + + /* Wait for ME UMA SIZE VALID bit to be set */ + for (count = ME_RETRY; count > 0; --count) { + pci_read_dword_ptr(&uma, PCI_ME_UMA); + if (uma.valid) + break; + udelay(ME_DELAY); + } + if (!count) { + printf("ERROR: ME is not ready!\n"); + return -EBUSY; + } + + /* Check for valid firmware */ + pci_read_dword_ptr(&hfs, PCI_ME_HFS); + if (hfs.fpt_bad) { + printf("WARNING: ME has bad firmware\n"); + return -EBADF; + } + + debug("Intel ME firmware is ready\n"); + + return 0; +} + +int intel_early_me_uma_size(void) +{ + struct me_uma uma; + + pci_read_dword_ptr(&uma, PCI_ME_UMA); + if (uma.valid) { + debug("ME: Requested %uMB UMA\n", uma.size); + return uma.size; + } + + debug("ME: Invalid UMA size\n"); + return -EINVAL; +} + +static inline void set_global_reset(int enable) +{ + u32 etr3; + + etr3 = pci_read_config32(PCH_LPC_DEV, ETR3); + + /* Clear CF9 Without Resume Well Reset Enable */ + etr3 &= ~ETR3_CWORWRE; + + /* CF9GR indicates a Global Reset */ + if (enable) + etr3 |= ETR3_CF9GR; + else + etr3 &= ~ETR3_CF9GR; + + pci_write_config32(PCH_LPC_DEV, ETR3, etr3); +} + +int intel_early_me_init_done(u8 status) +{ + u8 reset; + int count; + u32 mebase_l, mebase_h; + struct me_hfs hfs; + struct me_did did = { + .init_done = ME_INIT_DONE, + .status = status + }; + + /* MEBASE from MESEG_BASE[35:20] */ + mebase_l = pci_read_config32(PCH_DEV, PCI_CPU_MEBASE_L); + mebase_h = pci_read_config32(PCH_DEV, PCI_CPU_MEBASE_H); + mebase_h &= 0xf; + did.uma_base = (mebase_l >> 20) | (mebase_h << 12); + + /* Send message to ME */ + debug("ME: Sending Init Done with status: %d, UMA base: 0x%04x\n", + status, did.uma_base); + + pci_write_dword_ptr(&did, PCI_ME_H_GS); + + /* Must wait for ME acknowledgement */ + for (count = ME_RETRY; count > 0; --count) { + pci_read_dword_ptr(&hfs, PCI_ME_HFS); + if (hfs.bios_msg_ack) + break; + udelay(ME_DELAY); + } + if (!count) { + printf("ERROR: ME failed to respond\n"); + return -1; + } + + /* Return the requested BIOS action */ + debug("ME: Requested BIOS Action: %s\n", me_ack_values[hfs.ack_data]); + + /* Check status after acknowledgement */ + intel_early_me_status(); + + reset = 0; + switch (hfs.ack_data) { + case ME_HFS_ACK_CONTINUE: + /* Continue to boot */ + return 0; + case ME_HFS_ACK_RESET: + /* Non-power cycle reset */ + set_global_reset(0); + reset = 0x06; + break; + case ME_HFS_ACK_PWR_CYCLE: + /* Power cycle reset */ + set_global_reset(0); + reset = 0x0e; + break; + case ME_HFS_ACK_GBL_RESET: + /* Global reset */ + set_global_reset(1); + reset = 0x0e; + break; + case ME_HFS_ACK_S3: + case ME_HFS_ACK_S4: + case ME_HFS_ACK_S5: + break; + } + + /* Perform the requested reset */ + if (reset) { + outb(reset, 0xcf9); + cpu_hlt(); + } + return -1; +} diff --git a/arch/x86/cpu/ivybridge/lpc.c b/arch/x86/cpu/ivybridge/lpc.c new file mode 100644 index 0000000..621ef2c --- /dev/null +++ b/arch/x86/cpu/ivybridge/lpc.c @@ -0,0 +1,48 @@ +/* + * From coreboot southbridge/intel/bd82x6x/lpc.c + * + * Copyright (C) 2008-2009 coresystems GmbH + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <errno.h> +#include <fdtdec.h> +#include <pci.h> +#include <asm/pci.h> +#include <asm/arch/pch.h> + +int lpc_early_init(const void *blob, int node, pci_dev_t dev) +{ + struct reg_info { + u32 base; + u32 size; + } values[4], *ptr; + int count; + int i; + + count = fdtdec_get_int_array_count(blob, node, "gen-dec", + (u32 *)values, sizeof(values) / sizeof(u32)); + if (count < 0) + return -EINVAL; + + /* Set COM1/COM2 decode range */ + pci_write_config16(dev, LPC_IO_DEC, 0x0010); + + /* Enable PS/2 Keyboard/Mouse, EC areas and COM1 */ + pci_write_config16(dev, LPC_EN, KBC_LPC_EN | MC_LPC_EN | + GAMEL_LPC_EN | COMA_LPC_EN); + + /* Write all registers but use 0 if we run out of data */ + count = count * sizeof(u32) / sizeof(values[0]); + for (i = 0, ptr = values; i < ARRAY_SIZE(values); i++, ptr++) { + u32 reg = 0; + + if (i < count) + reg = ptr->base | PCI_COMMAND_IO | (ptr->size << 16); + pci_write_config32(dev, LPC_GENX_DEC(i), reg); + } + + return 0; +} diff --git a/arch/x86/cpu/ivybridge/me_status.c b/arch/x86/cpu/ivybridge/me_status.c new file mode 100644 index 0000000..15cf69f --- /dev/null +++ b/arch/x86/cpu/ivybridge/me_status.c @@ -0,0 +1,195 @@ +/* + * From Coreboot src/southbridge/intel/bd82x6x/me_status.c + * + * Copyright (C) 2011 The Chromium OS Authors. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <asm/arch/me.h> + +/* HFS1[3:0] Current Working State Values */ +static const char *const me_cws_values[] = { + [ME_HFS_CWS_RESET] = "Reset", + [ME_HFS_CWS_INIT] = "Initializing", + [ME_HFS_CWS_REC] = "Recovery", + [ME_HFS_CWS_NORMAL] = "Normal", + [ME_HFS_CWS_WAIT] = "Platform Disable Wait", + [ME_HFS_CWS_TRANS] = "OP State Transition", + [ME_HFS_CWS_INVALID] = "Invalid CPU Plugged In" +}; + +/* HFS1[8:6] Current Operation State Values */ +static const char *const me_opstate_values[] = { + [ME_HFS_STATE_PREBOOT] = "Preboot", + [ME_HFS_STATE_M0_UMA] = "M0 with UMA", + [ME_HFS_STATE_M3] = "M3 without UMA", + [ME_HFS_STATE_M0] = "M0 without UMA", + [ME_HFS_STATE_BRINGUP] = "Bring up", + [ME_HFS_STATE_ERROR] = "M0 without UMA but with error" +}; + +/* HFS[19:16] Current Operation Mode Values */ +static const char *const me_opmode_values[] = { + [ME_HFS_MODE_NORMAL] = "Normal", + [ME_HFS_MODE_DEBUG] = "Debug", + [ME_HFS_MODE_DIS] = "Soft Temporary Disable", + [ME_HFS_MODE_OVER_JMPR] = "Security Override via Jumper", + [ME_HFS_MODE_OVER_MEI] = "Security Override via MEI Message" +}; + +/* HFS[15:12] Error Code Values */ +static const char *const me_error_values[] = { + [ME_HFS_ERROR_NONE] = "No Error", + [ME_HFS_ERROR_UNCAT] = "Uncategorized Failure", + [ME_HFS_ERROR_IMAGE] = "Image Failure", + [ME_HFS_ERROR_DEBUG] = "Debug Failure" +}; + +/* GMES[31:28] ME Progress Code */ +static const char *const me_progress_values[] = { + [ME_GMES_PHASE_ROM] = "ROM Phase", + [ME_GMES_PHASE_BUP] = "BUP Phase", + [ME_GMES_PHASE_UKERNEL] = "uKernel Phase", + [ME_GMES_PHASE_POLICY] = "Policy Module", + [ME_GMES_PHASE_MODULE] = "Module Loading", + [ME_GMES_PHASE_UNKNOWN] = "Unknown", + [ME_GMES_PHASE_HOST] = "Host Communication" +}; + +/* GMES[27:24] Power Management Event */ +static const char *const me_pmevent_values[] = { + [0x00] = "Clean Moff->Mx wake", + [0x01] = "Moff->Mx wake after an error", + [0x02] = "Clean global reset", + [0x03] = "Global reset after an error", + [0x04] = "Clean Intel ME reset", + [0x05] = "Intel ME reset due to exception", + [0x06] = "Pseudo-global reset", + [0x07] = "S0/M0->Sx/M3", + [0x08] = "Sx/M3->S0/M0", + [0x09] = "Non-power cycle reset", + [0x0a] = "Power cycle reset through M3", + [0x0b] = "Power cycle reset through Moff", + [0x0c] = "Sx/Mx->Sx/Moff" +}; + +/* Progress Code 0 states */ +static const char *const me_progress_rom_values[] = { + [0x00] = "BEGIN", + [0x06] = "DISABLE" +}; + +/* Progress Code 1 states */ +static const char *const me_progress_bup_values[] = { + [0x00] = "Initialization starts", + [0x01] = "Disable the host wake event", + [0x04] = "Flow determination start process", + [0x08] = "Error reading/matching the VSCC table in the descriptor", + [0x0a] = "Check to see if straps say ME DISABLED", + [0x0b] = "Timeout waiting for PWROK", + [0x0d] = "Possibly handle BUP manufacturing override strap", + [0x11] = "Bringup in M3", + [0x12] = "Bringup in M0", + [0x13] = "Flow detection error", + [0x15] = "M3 clock switching error", + [0x18] = "M3 kernel load", + [0x1c] = "T34 missing - cannot program ICC", + [0x1f] = "Waiting for DID BIOS message", + [0x20] = "Waiting for DID BIOS message failure", + [0x21] = "DID reported an error", + [0x22] = "Enabling UMA", + [0x23] = "Enabling UMA error", + [0x24] = "Sending DID Ack to BIOS", + [0x25] = "Sending DID Ack to BIOS error", + [0x26] = "Switching clocks in M0", + [0x27] = "Switching clocks in M0 error", + [0x28] = "ME in temp disable", + [0x32] = "M0 kernel load", +}; + +/* Progress Code 3 states */ +static const char *const me_progress_policy_values[] = { + [0x00] = "Entery into Policy Module", + [0x03] = "Received S3 entry", + [0x04] = "Received S4 entry", + [0x05] = "Received S5 entry", + [0x06] = "Received UPD entry", + [0x07] = "Received PCR entry", + [0x08] = "Received NPCR entry", + [0x09] = "Received host wake", + [0x0a] = "Received AC<>DC switch", + [0x0b] = "Received DRAM Init Done", + [0x0c] = "VSCC Data not found for flash device", + [0x0d] = "VSCC Table is not valid", + [0x0e] = "Flash Partition Boundary is outside address space", + [0x0f] = "ME cannot access the chipset descriptor region", + [0x10] = "Required VSCC values for flash parts do not match", +}; + +void intel_me_status(struct me_hfs *hfs, struct me_gmes *gmes) +{ + /* Check Current States */ + debug("ME: FW Partition Table : %s\n", + hfs->fpt_bad ? "BAD" : "OK"); + debug("ME: Bringup Loader Failure : %s\n", + hfs->ft_bup_ld_flr ? "YES" : "NO"); + debug("ME: Firmware Init Complete : %s\n", + hfs->fw_init_complete ? "YES" : "NO"); + debug("ME: Manufacturing Mode : %s\n", + hfs->mfg_mode ? "YES" : "NO"); + debug("ME: Boot Options Present : %s\n", + hfs->boot_options_present ? "YES" : "NO"); + debug("ME: Update In Progress : %s\n", + hfs->update_in_progress ? "YES" : "NO"); + debug("ME: Current Working State : %s\n", + me_cws_values[hfs->working_state]); + debug("ME: Current Operation State : %s\n", + me_opstate_values[hfs->operation_state]); + debug("ME: Current Operation Mode : %s\n", + me_opmode_values[hfs->operation_mode]); + debug("ME: Error Code : %s\n", + me_error_values[hfs->error_code]); + debug("ME: Progress Phase : %s\n", + me_progress_values[gmes->progress_code]); + debug("ME: Power Management Event : %s\n", + me_pmevent_values[gmes->current_pmevent]); + + debug("ME: Progress Phase State : "); + switch (gmes->progress_code) { + case ME_GMES_PHASE_ROM: /* ROM Phase */ + debug("%s", me_progress_rom_values[gmes->current_state]); + break; + + case ME_GMES_PHASE_BUP: /* Bringup Phase */ + if (gmes->current_state < ARRAY_SIZE(me_progress_bup_values) && + me_progress_bup_values[gmes->current_state]) + debug("%s", + me_progress_bup_values[gmes->current_state]); + else + debug("0x%02x", gmes->current_state); + break; + + case ME_GMES_PHASE_POLICY: /* Policy Module Phase */ + if (gmes->current_state < + ARRAY_SIZE(me_progress_policy_values) && + me_progress_policy_values[gmes->current_state]) + debug("%s", + me_progress_policy_values[gmes->current_state]); + else + debug("0x%02x", gmes->current_state); + break; + + case ME_GMES_PHASE_HOST: /* Host Communication Phase */ + if (!gmes->current_state) + debug("Host communication established"); + else + debug("0x%02x", gmes->current_state); + break; + + default: + debug("Unknown 0x%02x", gmes->current_state); + } + debug("\n"); +} diff --git a/arch/x86/cpu/ivybridge/microcode_intel.c b/arch/x86/cpu/ivybridge/microcode_intel.c new file mode 100644 index 0000000..8c11a63 --- /dev/null +++ b/arch/x86/cpu/ivybridge/microcode_intel.c @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2014 Google, Inc + * Copyright (C) 2000 Ronald G. Minnich + * + * Microcode update for Intel PIII and later CPUs + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <errno.h> +#include <fdtdec.h> +#include <libfdt.h> +#include <asm/cpu.h> +#include <asm/msr.h> +#include <asm/processor.h> + +/** + * struct microcode_update - standard microcode header from Intel + * + * We read this information out of the device tree and use it to determine + * whether the update is applicable or not. We also use the same structure + * to read information from the CPU. + */ +struct microcode_update { + uint header_version; + uint update_revision; + uint date_code; + uint processor_signature; + uint checksum; + uint loader_revision; + uint processor_flags; + const void *data; + int size; +}; + +static int microcode_decode_node(const void *blob, int node, + struct microcode_update *update) +{ + update->data = fdt_getprop(blob, node, "data", &update->size); + if (!update->data) + return -EINVAL; + + update->header_version = fdtdec_get_int(blob, node, + "intel,header-version", 0); + update->update_revision = fdtdec_get_int(blob, node, + "intel,update-revision", 0); + update->date_code = fdtdec_get_int(blob, node, + "intel,date-code", 0); + update->processor_signature = fdtdec_get_int(blob, node, + "intel.processor-signature", 0); + update->checksum = fdtdec_get_int(blob, node, "intel,checksum", 0); + update->loader_revision = fdtdec_get_int(blob, node, + "loader-revision", 0); + update->processor_flags = fdtdec_get_int(blob, node, + "processor-flags", 0); + + return 0; +} + +static uint32_t microcode_read_rev(void) +{ + /* + * Some Intel CPUs can be very finicky about the CPUID sequence used. + * So this is implemented in assembly so that it works reliably. + */ + uint32_t low, high; + + asm volatile ( + "xorl %%eax, %%eax\n" + "xorl %%edx, %%edx\n" + "movl $0x8b, %%ecx\n" + "wrmsr\n" + "movl $0x01, %%eax\n" + "cpuid\n" + "movl $0x8b, %%ecx\n" + "rdmsr\n" + : /* outputs */ + "=a" (low), "=d" (high) + : /* inputs */ + : /* clobbers */ + "ebx", "ecx" + ); + + return high; +} + +static void microcode_read_cpu(struct microcode_update *cpu) +{ + /* CPUID sets MSR 0x8B iff a microcode update has been loaded. */ + unsigned int x86_model, x86_family; + struct cpuid_result result; + uint32_t low, high; + + wrmsr(0x8b, 0, 0); + result = cpuid(1); + rdmsr(0x8b, low, cpu->update_revision); + x86_model = (result.eax >> 4) & 0x0f; + x86_family = (result.eax >> 8) & 0x0f; + cpu->processor_signature = result.eax; + + cpu->processor_flags = 0; + if ((x86_model >= 5) || (x86_family > 6)) { + rdmsr(0x17, low, high); + cpu->processor_flags = 1 << ((high >> 18) & 7); + } + debug("microcode: sig=%#x pf=%#x revision=%#x\n", + cpu->processor_signature, cpu->processor_flags, + cpu->update_revision); +} + +/* Get a microcode update from the device tree and apply it */ +int microcode_update_intel(void) +{ + struct microcode_update cpu, update; + const void *blob = gd->fdt_blob; + int count; + int node; + int ret; + + microcode_read_cpu(&cpu); + node = 0; + count = 0; + do { + node = fdtdec_next_compatible(blob, node, + COMPAT_INTEL_MICROCODE); + if (node < 0) { + debug("%s: Found %d updates\n", __func__, count); + return count ? 0 : -ENOENT; + } + + ret = microcode_decode_node(blob, node, &update); + if (ret) { + debug("%s: Unable to decode update: %d\n", __func__, + ret); + return ret; + } + if (update.processor_signature == cpu.processor_signature && + (update.processor_flags & cpu.processor_flags)) { + debug("%s: Update already exists\n", __func__); + return -EEXIST; + } + + wrmsr(0x79, (ulong)update.data, 0); + debug("microcode: updated to revision 0x%x date=%04x-%02x-%02x\n", + microcode_read_rev(), update.date_code & 0xffff, + (update.date_code >> 24) & 0xff, + (update.date_code >> 16) & 0xff); + count++; + } while (1); +} diff --git a/arch/x86/cpu/ivybridge/pci.c b/arch/x86/cpu/ivybridge/pci.c new file mode 100644 index 0000000..c1ae658 --- /dev/null +++ b/arch/x86/cpu/ivybridge/pci.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * (C) Copyright 2008,2009 + * Graeme Russ, <graeme.russ@gmail.com> + * + * (C) Copyright 2002 + * Daniel Engström, Omicron Ceti AB, <daniel@omicron.se> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <pci.h> +#include <asm/pci.h> + +static void config_pci_bridge(struct pci_controller *hose, pci_dev_t dev, + struct pci_config_table *table) +{ + u8 secondary; + + hose->read_byte(hose, dev, PCI_SECONDARY_BUS, &secondary); + if (secondary != 0) + pci_hose_scan_bus(hose, secondary); +} + +static struct pci_config_table pci_ivybridge_config_table[] = { + /* vendor, device, class, bus, dev, func */ + { PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_BRIDGE_PCI, + PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, &config_pci_bridge }, + {} +}; + +void board_pci_setup_hose(struct pci_controller *hose) +{ + hose->config_table = pci_ivybridge_config_table; + hose->first_busno = 0; + hose->last_busno = 0; + + /* PCI memory space */ + pci_set_region(hose->regions + 0, + CONFIG_PCI_MEM_BUS, + CONFIG_PCI_MEM_PHYS, + CONFIG_PCI_MEM_SIZE, + PCI_REGION_MEM); + + /* PCI IO space */ + pci_set_region(hose->regions + 1, + CONFIG_PCI_IO_BUS, + CONFIG_PCI_IO_PHYS, + CONFIG_PCI_IO_SIZE, + PCI_REGION_IO); + + pci_set_region(hose->regions + 2, + CONFIG_PCI_PREF_BUS, + CONFIG_PCI_PREF_PHYS, + CONFIG_PCI_PREF_SIZE, + PCI_REGION_PREFETCH); + + hose->region_count = 3; +} diff --git a/arch/x86/cpu/ivybridge/report_platform.c b/arch/x86/cpu/ivybridge/report_platform.c new file mode 100644 index 0000000..69e31b3 --- /dev/null +++ b/arch/x86/cpu/ivybridge/report_platform.c @@ -0,0 +1,89 @@ +/* + * From Coreboot src/northbridge/intel/sandybridge/report_platform.c + * + * Copyright (C) 2012 Google Inc. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <asm/cpu.h> +#include <asm/pci.h> +#include <asm/arch/pch.h> + +static void report_cpu_info(void) +{ + char cpu_string[CPU_MAX_NAME_LEN], *cpu_name; + const char *mode[] = {"NOT ", ""}; + struct cpuid_result cpuidr; + int vt, txt, aes; + u32 index; + + index = 0x80000000; + cpuidr = cpuid(index); + if (cpuidr.eax < 0x80000004) { + strcpy(cpu_string, "Platform info not available"); + cpu_name = cpu_string; + } else { + cpu_name = cpu_get_name(cpu_string); + } + + cpuidr = cpuid(1); + debug("CPU id(%x): %s\n", cpuidr.eax, cpu_name); + aes = (cpuidr.ecx & (1 << 25)) ? 1 : 0; + txt = (cpuidr.ecx & (1 << 6)) ? 1 : 0; + vt = (cpuidr.ecx & (1 << 5)) ? 1 : 0; + debug("AES %ssupported, TXT %ssupported, VT %ssupported\n", + mode[aes], mode[txt], mode[vt]); +} + +/* The PCI id name match comes from Intel document 472178 */ +static struct { + u16 dev_id; + const char *dev_name; +} pch_table[] = { + {0x1E41, "Desktop Sample"}, + {0x1E42, "Mobile Sample"}, + {0x1E43, "SFF Sample"}, + {0x1E44, "Z77"}, + {0x1E45, "H71"}, + {0x1E46, "Z75"}, + {0x1E47, "Q77"}, + {0x1E48, "Q75"}, + {0x1E49, "B75"}, + {0x1E4A, "H77"}, + {0x1E53, "C216"}, + {0x1E55, "QM77"}, + {0x1E56, "QS77"}, + {0x1E58, "UM77"}, + {0x1E57, "HM77"}, + {0x1E59, "HM76"}, + {0x1E5D, "HM75"}, + {0x1E5E, "HM70"}, + {0x1E5F, "NM70"}, +}; + +static void report_pch_info(void) +{ + const char *pch_type = "Unknown"; + int i; + u16 dev_id; + uint8_t rev_id; + + dev_id = pci_read_config16(PCH_LPC_DEV, 2); + for (i = 0; i < ARRAY_SIZE(pch_table); i++) { + if (pch_table[i].dev_id == dev_id) { + pch_type = pch_table[i].dev_name; + break; + } + } + rev_id = pci_read_config8(PCH_LPC_DEV, 8); + debug("PCH type: %s, device id: %x, rev id %x\n", pch_type, dev_id, + rev_id); +} + +void report_platform_info(void) +{ + report_cpu_info(); + report_pch_info(); +} diff --git a/arch/x86/cpu/ivybridge/sdram.c b/arch/x86/cpu/ivybridge/sdram.c new file mode 100644 index 0000000..df2b990 --- /dev/null +++ b/arch/x86/cpu/ivybridge/sdram.c @@ -0,0 +1,571 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * (C) Copyright 2010,2011 + * Graeme Russ, <graeme.russ@gmail.com> + * + * Portions from Coreboot mainboard/google/link/romstage.c + * Copyright (C) 2007-2010 coresystems GmbH + * Copyright (C) 2011 Google Inc. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <errno.h> +#include <fdtdec.h> +#include <malloc.h> +#include <asm/processor.h> +#include <asm/gpio.h> +#include <asm/global_data.h> +#include <asm/pci.h> +#include <asm/arch/me.h> +#include <asm/arch/pei_data.h> +#include <asm/arch/pch.h> +#include <asm/post.h> +#include <asm/arch/sandybridge.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * This function looks for the highest region of memory lower than 4GB which + * has enough space for U-Boot where U-Boot is aligned on a page boundary. + * It overrides the default implementation found elsewhere which simply + * picks the end of ram, wherever that may be. The location of the stack, + * the relocation address, and how far U-Boot is moved by relocation are + * set in the global data structure. + */ +ulong board_get_usable_ram_top(ulong total_size) +{ + struct memory_info *info = &gd->arch.meminfo; + uintptr_t dest_addr = 0; + struct memory_area *largest = NULL; + int i; + + /* Find largest area of memory below 4GB */ + + for (i = 0; i < info->num_areas; i++) { + struct memory_area *area = &info->area[i]; + + if (area->start >= 1ULL << 32) + continue; + if (!largest || area->size > largest->size) + largest = area; + } + + /* If no suitable area was found, return an error. */ + assert(largest); + if (!largest || largest->size < (2 << 20)) + panic("No available memory found for relocation"); + + dest_addr = largest->start + largest->size; + + return (ulong)dest_addr; +} + +void dram_init_banksize(void) +{ + struct memory_info *info = &gd->arch.meminfo; + int num_banks; + int i; + + for (i = 0, num_banks = 0; i < info->num_areas; i++) { + struct memory_area *area = &info->area[i]; + + if (area->start >= 1ULL << 32) + continue; + gd->bd->bi_dram[num_banks].start = area->start; + gd->bd->bi_dram[num_banks].size = area->size; + num_banks++; + } +} + +static const char *const ecc_decoder[] = { + "inactive", + "active on IO", + "disabled on IO", + "active" +}; + +/* + * Dump in the log memory controller configuration as read from the memory + * controller registers. + */ +static void report_memory_config(void) +{ + u32 addr_decoder_common, addr_decode_ch[2]; + int i; + + addr_decoder_common = readl(MCHBAR_REG(0x5000)); + addr_decode_ch[0] = readl(MCHBAR_REG(0x5004)); + addr_decode_ch[1] = readl(MCHBAR_REG(0x5008)); + + debug("memcfg DDR3 clock %d MHz\n", + (readl(MCHBAR_REG(0x5e04)) * 13333 * 2 + 50) / 100); + debug("memcfg channel assignment: A: %d, B % d, C % d\n", + addr_decoder_common & 3, + (addr_decoder_common >> 2) & 3, + (addr_decoder_common >> 4) & 3); + + for (i = 0; i < ARRAY_SIZE(addr_decode_ch); i++) { + u32 ch_conf = addr_decode_ch[i]; + debug("memcfg channel[%d] config (%8.8x):\n", i, ch_conf); + debug(" ECC %s\n", ecc_decoder[(ch_conf >> 24) & 3]); + debug(" enhanced interleave mode %s\n", + ((ch_conf >> 22) & 1) ? "on" : "off"); + debug(" rank interleave %s\n", + ((ch_conf >> 21) & 1) ? "on" : "off"); + debug(" DIMMA %d MB width x%d %s rank%s\n", + ((ch_conf >> 0) & 0xff) * 256, + ((ch_conf >> 19) & 1) ? 16 : 8, + ((ch_conf >> 17) & 1) ? "dual" : "single", + ((ch_conf >> 16) & 1) ? "" : ", selected"); + debug(" DIMMB %d MB width x%d %s rank%s\n", + ((ch_conf >> 8) & 0xff) * 256, + ((ch_conf >> 20) & 1) ? 16 : 8, + ((ch_conf >> 18) & 1) ? "dual" : "single", + ((ch_conf >> 16) & 1) ? ", selected" : ""); + } +} + +static void post_system_agent_init(struct pei_data *pei_data) +{ + /* If PCIe init is skipped, set the PEG clock gating */ + if (!pei_data->pcie_init) + setbits_le32(MCHBAR_REG(0x7010), 1); +} + +static asmlinkage void console_tx_byte(unsigned char byte) +{ +#ifdef DEBUG + putc(byte); +#endif +} + +/** + * Find the PEI executable in the ROM and execute it. + * + * @param pei_data: configuration data for UEFI PEI reference code + */ +int sdram_initialise(struct pei_data *pei_data) +{ + unsigned version; + const char *data; + uint16_t done; + int ret; + + report_platform_info(); + + /* Wait for ME to be ready */ + ret = intel_early_me_init(); + if (ret) + return ret; + ret = intel_early_me_uma_size(); + if (ret < 0) + return ret; + + debug("Starting UEFI PEI System Agent\n"); + + /* If MRC data is not found we cannot continue S3 resume. */ + if (pei_data->boot_mode == PEI_BOOT_RESUME && !pei_data->mrc_input) { + debug("Giving up in sdram_initialize: No MRC data\n"); + outb(0x6, PORT_RESET); + cpu_hlt(); + } + + /* Pass console handler in pei_data */ + pei_data->tx_byte = console_tx_byte; + + debug("PEI data at %p, size %x:\n", pei_data, sizeof(*pei_data)); + + data = (char *)CONFIG_X86_MRC_START; + if (data) { + int rv; + int (*func)(struct pei_data *); + + debug("Calling MRC at %p\n", data); + post_code(POST_PRE_MRC); + func = (int (*)(struct pei_data *))data; + rv = func(pei_data); + post_code(POST_MRC); + if (rv) { + switch (rv) { + case -1: + printf("PEI version mismatch.\n"); + break; + case -2: + printf("Invalid memory frequency.\n"); + break; + default: + printf("MRC returned %x.\n", rv); + } + printf("Nonzero MRC return value.\n"); + return -EFAULT; + } + } else { + printf("UEFI PEI System Agent not found.\n"); + return -ENOSYS; + } + +#if CONFIG_USBDEBUG + /* mrc.bin reconfigures USB, so reinit it to have debug */ + early_usbdebug_init(); +#endif + + version = readl(MCHBAR_REG(0x5034)); + debug("System Agent Version %d.%d.%d Build %d\n", + version >> 24 , (version >> 16) & 0xff, + (version >> 8) & 0xff, version & 0xff); + + /* + * Send ME init done for SandyBridge here. This is done inside the + * SystemAgent binary on IvyBridge + */ + done = pci_read_config32(PCH_DEV, PCI_DEVICE_ID); + done &= BASE_REV_MASK; + if (BASE_REV_SNB == done) + intel_early_me_init_done(ME_INIT_STATUS_SUCCESS); + else + intel_early_me_status(); + + post_system_agent_init(pei_data); + report_memory_config(); + + return 0; +} + +static int copy_spd(struct pei_data *peid) +{ + const int gpio_vector[] = {41, 42, 43, 10, -1}; + int spd_index; + const void *blob = gd->fdt_blob; + int node, spd_node; + int ret, i; + + for (i = 0; ; i++) { + if (gpio_vector[i] == -1) + break; + ret = gpio_requestf(gpio_vector[i], "spd_id%d", i); + if (ret) { + debug("%s: Could not request gpio %d\n", __func__, + gpio_vector[i]); + return ret; + } + } + spd_index = gpio_get_values_as_int(gpio_vector); + debug("spd index %d\n", spd_index); + node = fdtdec_next_compatible(blob, 0, COMPAT_MEMORY_SPD); + if (node < 0) { + printf("SPD data not found.\n"); + return -ENOENT; + } + + for (spd_node = fdt_first_subnode(blob, node); + spd_node > 0; + spd_node = fdt_next_subnode(blob, spd_node)) { + const char *data; + int len; + + if (fdtdec_get_int(blob, spd_node, "reg", -1) != spd_index) + continue; + data = fdt_getprop(blob, spd_node, "data", &len); + if (len < sizeof(peid->spd_data[0])) { + printf("Missing SPD data\n"); + return -EINVAL; + } + + debug("Using SDRAM SPD data for '%s'\n", + fdt_get_name(blob, spd_node, NULL)); + memcpy(peid->spd_data[0], data, sizeof(peid->spd_data[0])); + break; + } + + if (spd_node < 0) { + printf("No SPD data found for index %d\n", spd_index); + return -ENOENT; + } + + return 0; +} + +/** + * add_memory_area() - Add a new usable memory area to our list + * + * Note: @start and @end must not span the first 4GB boundary + * + * @info: Place to store memory info + * @start: Start of this memory area + * @end: End of this memory area + 1 + */ +static int add_memory_area(struct memory_info *info, + uint64_t start, uint64_t end) +{ + struct memory_area *ptr; + + if (info->num_areas == CONFIG_NR_DRAM_BANKS) + return -ENOSPC; + + ptr = &info->area[info->num_areas]; + ptr->start = start; + ptr->size = end - start; + info->total_memory += ptr->size; + if (ptr->start < (1ULL << 32)) + info->total_32bit_memory += ptr->size; + debug("%d: memory %llx size %llx, total now %llx / %llx\n", + info->num_areas, ptr->start, ptr->size, + info->total_32bit_memory, info->total_memory); + info->num_areas++; + + return 0; +} + +/** + * sdram_find() - Find available memory + * + * This is a bit complicated since on x86 there are system memory holes all + * over the place. We create a list of available memory blocks + */ +static int sdram_find(pci_dev_t dev) +{ + struct memory_info *info = &gd->arch.meminfo; + uint32_t tseg_base, uma_size, tolud; + uint64_t tom, me_base, touud; + uint64_t uma_memory_base = 0; + uint64_t uma_memory_size; + unsigned long long tomk; + uint16_t ggc; + + /* Total Memory 2GB example: + * + * 00000000 0000MB-1992MB 1992MB RAM (writeback) + * 7c800000 1992MB-2000MB 8MB TSEG (SMRR) + * 7d000000 2000MB-2002MB 2MB GFX GTT (uncached) + * 7d200000 2002MB-2034MB 32MB GFX UMA (uncached) + * 7f200000 2034MB TOLUD + * 7f800000 2040MB MEBASE + * 7f800000 2040MB-2048MB 8MB ME UMA (uncached) + * 80000000 2048MB TOM + * 100000000 4096MB-4102MB 6MB RAM (writeback) + * + * Total Memory 4GB example: + * + * 00000000 0000MB-2768MB 2768MB RAM (writeback) + * ad000000 2768MB-2776MB 8MB TSEG (SMRR) + * ad800000 2776MB-2778MB 2MB GFX GTT (uncached) + * ada00000 2778MB-2810MB 32MB GFX UMA (uncached) + * afa00000 2810MB TOLUD + * ff800000 4088MB MEBASE + * ff800000 4088MB-4096MB 8MB ME UMA (uncached) + * 100000000 4096MB TOM + * 100000000 4096MB-5374MB 1278MB RAM (writeback) + * 14fe00000 5368MB TOUUD + */ + + /* Top of Upper Usable DRAM, including remap */ + touud = pci_read_config32(dev, TOUUD+4); + touud <<= 32; + touud |= pci_read_config32(dev, TOUUD); + + /* Top of Lower Usable DRAM */ + tolud = pci_read_config32(dev, TOLUD); + + /* Top of Memory - does not account for any UMA */ + tom = pci_read_config32(dev, 0xa4); + tom <<= 32; + tom |= pci_read_config32(dev, 0xa0); + + debug("TOUUD %llx TOLUD %08x TOM %llx\n", touud, tolud, tom); + + /* ME UMA needs excluding if total memory <4GB */ + me_base = pci_read_config32(dev, 0x74); + me_base <<= 32; + me_base |= pci_read_config32(dev, 0x70); + + debug("MEBASE %llx\n", me_base); + + /* TODO: Get rid of all this shifting by 10 bits */ + tomk = tolud >> 10; + if (me_base == tolud) { + /* ME is from MEBASE-TOM */ + uma_size = (tom - me_base) >> 10; + /* Increment TOLUD to account for ME as RAM */ + tolud += uma_size << 10; + /* UMA starts at old TOLUD */ + uma_memory_base = tomk * 1024ULL; + uma_memory_size = uma_size * 1024ULL; + debug("ME UMA base %llx size %uM\n", me_base, uma_size >> 10); + } + + /* Graphics memory comes next */ + ggc = pci_read_config16(dev, GGC); + if (!(ggc & 2)) { + debug("IGD decoded, subtracting "); + + /* Graphics memory */ + uma_size = ((ggc >> 3) & 0x1f) * 32 * 1024ULL; + debug("%uM UMA", uma_size >> 10); + tomk -= uma_size; + uma_memory_base = tomk * 1024ULL; + uma_memory_size += uma_size * 1024ULL; + + /* GTT Graphics Stolen Memory Size (GGMS) */ + uma_size = ((ggc >> 8) & 0x3) * 1024ULL; + tomk -= uma_size; + uma_memory_base = tomk * 1024ULL; + uma_memory_size += uma_size * 1024ULL; + debug(" and %uM GTT\n", uma_size >> 10); + } + + /* Calculate TSEG size from its base which must be below GTT */ + tseg_base = pci_read_config32(dev, 0xb8); + uma_size = (uma_memory_base - tseg_base) >> 10; + tomk -= uma_size; + uma_memory_base = tomk * 1024ULL; + uma_memory_size += uma_size * 1024ULL; + debug("TSEG base 0x%08x size %uM\n", tseg_base, uma_size >> 10); + + debug("Available memory below 4GB: %lluM\n", tomk >> 10); + + /* Report the memory regions */ + add_memory_area(info, 1 << 20, 2 << 28); + add_memory_area(info, (2 << 28) + (2 << 20), 4 << 28); + add_memory_area(info, (4 << 28) + (2 << 20), tseg_base); + add_memory_area(info, 1ULL << 32, touud); + /* + * If >= 4GB installed then memory from TOLUD to 4GB + * is remapped above TOM, TOUUD will account for both + */ + if (touud > (1ULL << 32ULL)) { + debug("Available memory above 4GB: %lluM\n", + (touud >> 20) - 4096); + } + + return 0; +} + +static void rcba_config(void) +{ + /* + * GFX INTA -> PIRQA (MSI) + * D28IP_P3IP WLAN INTA -> PIRQB + * D29IP_E1P EHCI1 INTA -> PIRQD + * D26IP_E2P EHCI2 INTA -> PIRQF + * D31IP_SIP SATA INTA -> PIRQF (MSI) + * D31IP_SMIP SMBUS INTB -> PIRQH + * D31IP_TTIP THRT INTC -> PIRQA + * D27IP_ZIP HDA INTA -> PIRQA (MSI) + * + * TRACKPAD -> PIRQE (Edge Triggered) + * TOUCHSCREEN -> PIRQG (Edge Triggered) + */ + + /* Device interrupt pin register (board specific) */ + writel((INTC << D31IP_TTIP) | (NOINT << D31IP_SIP2) | + (INTB << D31IP_SMIP) | (INTA << D31IP_SIP), RCB_REG(D31IP)); + writel(NOINT << D30IP_PIP, RCB_REG(D30IP)); + writel(INTA << D29IP_E1P, RCB_REG(D29IP)); + writel(INTA << D28IP_P3IP, RCB_REG(D28IP)); + writel(INTA << D27IP_ZIP, RCB_REG(D27IP)); + writel(INTA << D26IP_E2P, RCB_REG(D26IP)); + writel(NOINT << D25IP_LIP, RCB_REG(D25IP)); + writel(NOINT << D22IP_MEI1IP, RCB_REG(D22IP)); + + /* Device interrupt route registers */ + writel(DIR_ROUTE(PIRQB, PIRQH, PIRQA, PIRQC), RCB_REG(D31IR)); + writel(DIR_ROUTE(PIRQD, PIRQE, PIRQF, PIRQG), RCB_REG(D29IR)); + writel(DIR_ROUTE(PIRQB, PIRQC, PIRQD, PIRQE), RCB_REG(D28IR)); + writel(DIR_ROUTE(PIRQA, PIRQH, PIRQA, PIRQB), RCB_REG(D27IR)); + writel(DIR_ROUTE(PIRQF, PIRQE, PIRQG, PIRQH), RCB_REG(D26IR)); + writel(DIR_ROUTE(PIRQA, PIRQB, PIRQC, PIRQD), RCB_REG(D25IR)); + writel(DIR_ROUTE(PIRQA, PIRQB, PIRQC, PIRQD), RCB_REG(D22IR)); + + /* Enable IOAPIC (generic) */ + writew(0x0100, RCB_REG(OIC)); + /* PCH BWG says to read back the IOAPIC enable register */ + (void)readw(RCB_REG(OIC)); + + /* Disable unused devices (board specific) */ + setbits_le32(RCB_REG(FD), PCH_DISABLE_ALWAYS); +} + +int dram_init(void) +{ + struct pei_data pei_data __aligned(8) = { + .pei_version = PEI_VERSION, + .mchbar = DEFAULT_MCHBAR, + .dmibar = DEFAULT_DMIBAR, + .epbar = DEFAULT_EPBAR, + .pciexbar = CONFIG_MMCONF_BASE_ADDRESS, + .smbusbar = SMBUS_IO_BASE, + .wdbbar = 0x4000000, + .wdbsize = 0x1000, + .hpet_address = CONFIG_HPET_ADDRESS, + .rcba = DEFAULT_RCBABASE, + .pmbase = DEFAULT_PMBASE, + .gpiobase = DEFAULT_GPIOBASE, + .thermalbase = 0xfed08000, + .system_type = 0, /* 0 Mobile, 1 Desktop/Server */ + .tseg_size = CONFIG_SMM_TSEG_SIZE, + .ts_addresses = { 0x00, 0x00, 0x00, 0x00 }, + .ec_present = 1, + .ddr3lv_support = 1, + /* + * 0 = leave channel enabled + * 1 = disable dimm 0 on channel + * 2 = disable dimm 1 on channel + * 3 = disable dimm 0+1 on channel + */ + .dimm_channel0_disabled = 2, + .dimm_channel1_disabled = 2, + .max_ddr3_freq = 1600, + .usb_port_config = { + /* + * Empty and onboard Ports 0-7, set to un-used pin + * OC3 + */ + { 0, 3, 0x0000 }, /* P0= Empty */ + { 1, 0, 0x0040 }, /* P1= Left USB 1 (OC0) */ + { 1, 1, 0x0040 }, /* P2= Left USB 2 (OC1) */ + { 1, 3, 0x0040 }, /* P3= SDCARD (no OC) */ + { 0, 3, 0x0000 }, /* P4= Empty */ + { 1, 3, 0x0040 }, /* P5= WWAN (no OC) */ + { 0, 3, 0x0000 }, /* P6= Empty */ + { 0, 3, 0x0000 }, /* P7= Empty */ + /* + * Empty and onboard Ports 8-13, set to un-used pin + * OC4 + */ + { 1, 4, 0x0040 }, /* P8= Camera (no OC) */ + { 1, 4, 0x0040 }, /* P9= Bluetooth (no OC) */ + { 0, 4, 0x0000 }, /* P10= Empty */ + { 0, 4, 0x0000 }, /* P11= Empty */ + { 0, 4, 0x0000 }, /* P12= Empty */ + { 0, 4, 0x0000 }, /* P13= Empty */ + }, + }; + pci_dev_t dev = PCI_BDF(0, 0, 0); + int ret; + + debug("Boot mode %d\n", gd->arch.pei_boot_mode); + debug("mcr_input %p\n", pei_data.mrc_input); + pei_data.boot_mode = gd->arch.pei_boot_mode; + ret = copy_spd(&pei_data); + if (!ret) + ret = sdram_initialise(&pei_data); + if (ret) + return ret; + + rcba_config(); + quick_ram_check(); + + writew(0xCAFE, MCHBAR_REG(SSKPD)); + + post_code(POST_DRAM); + + ret = sdram_find(dev); + if (ret) + return ret; + + gd->ram_size = gd->arch.meminfo.total_32bit_memory; + + return 0; +} diff --git a/arch/x86/cpu/pci.c b/arch/x86/cpu/pci.c new file mode 100644 index 0000000..e399388 --- /dev/null +++ b/arch/x86/cpu/pci.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * (C) Copyright 2008,2009 + * Graeme Russ, <graeme.russ@gmail.com> + * + * (C) Copyright 2002 + * Daniel Engström, Omicron Ceti AB, <daniel@omicron.se> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <errno.h> +#include <malloc.h> +#include <pci.h> +#include <asm/pci.h> + +static struct pci_controller x86_hose; + +int pci_early_init_hose(struct pci_controller **hosep) +{ + struct pci_controller *hose; + + hose = calloc(1, sizeof(struct pci_controller)); + if (!hose) + return -ENOMEM; + + board_pci_setup_hose(hose); + pci_setup_type1(hose); + gd->arch.hose = hose; + *hosep = hose; + + return 0; +} + +void pci_init_board(void) +{ + struct pci_controller *hose = &x86_hose; + + /* Stop using the early hose */ + gd->arch.hose = NULL; + + board_pci_setup_hose(hose); + pci_setup_type1(hose); + pci_register_hose(hose); + + hose->last_busno = pci_hose_scan(hose); +} + +static struct pci_controller *get_hose(void) +{ + if (gd->arch.hose) + return gd->arch.hose; + + return pci_bus_to_hose(0); +} + +unsigned int pci_read_config8(pci_dev_t dev, unsigned where) +{ + uint8_t value; + + pci_hose_read_config_byte(get_hose(), dev, where, &value); + + return value; +} + +unsigned int pci_read_config16(pci_dev_t dev, unsigned where) +{ + uint16_t value; + + pci_hose_read_config_word(get_hose(), dev, where, &value); + + return value; +} + +unsigned int pci_read_config32(pci_dev_t dev, unsigned where) +{ + uint32_t value; + + pci_hose_read_config_dword(get_hose(), dev, where, &value); + + return value; +} + +void pci_write_config8(pci_dev_t dev, unsigned where, unsigned value) +{ + pci_hose_write_config_byte(get_hose(), dev, where, value); +} + +void pci_write_config16(pci_dev_t dev, unsigned where, unsigned value) +{ + pci_hose_write_config_word(get_hose(), dev, where, value); +} + +void pci_write_config32(pci_dev_t dev, unsigned where, unsigned value) +{ + pci_hose_write_config_dword(get_hose(), dev, where, value); +} diff --git a/arch/x86/cpu/start.S b/arch/x86/cpu/start.S index 338bab1..b0d0ac0 100644 --- a/arch/x86/cpu/start.S +++ b/arch/x86/cpu/start.S @@ -13,6 +13,7 @@ #include <config.h> #include <version.h> #include <asm/global_data.h> +#include <asm/post.h> #include <asm/processor.h> #include <asm/processor-flags.h> #include <generated/generic-asm-offsets.h> @@ -49,6 +50,8 @@ _start: */ movw $GD_FLG_COLD_BOOT, %bx 1: + /* Save BIST */ + movl %eax, %ebp /* Load the segement registes to match the gdt loaded in start16.S */ movl $(X86_GDT_ENTRY_32BIT_DS * X86_GDT_ENTRY_SIZE), %eax @@ -65,6 +68,7 @@ _start: jmp early_board_init .globl early_board_init_ret early_board_init_ret: + post_code(POST_START) /* Initialise Cache-As-RAM */ jmp car_init @@ -74,16 +78,29 @@ car_init_ret: * We now have CONFIG_SYS_CAR_SIZE bytes of Cache-As-RAM (or SRAM, * or fully initialised SDRAM - we really don't care which) * starting at CONFIG_SYS_CAR_ADDR to be used as a temporary stack + * and early malloc area. The MRC requires some space at the top. + * + * Stack grows down from top of CAR. We have: + * + * top-> CONFIG_SYS_CAR_ADDR + CONFIG_SYS_CAR_SIZE + * MRC area + * global_data + * x86 global descriptor table + * early malloc area + * stack + * bottom-> CONFIG_SYS_CAR_ADDR */ - - /* Stack grows down from top of CAR */ - movl $(CONFIG_SYS_CAR_ADDR + CONFIG_SYS_CAR_SIZE), %esp + movl $(CONFIG_SYS_CAR_ADDR + CONFIG_SYS_CAR_SIZE - 4), %esp +#ifdef CONFIG_DCACHE_RAM_MRC_VAR_SIZE + subl $CONFIG_DCACHE_RAM_MRC_VAR_SIZE, %esp +#endif /* Reserve space on stack for global data */ subl $GENERATED_GBL_DATA_SIZE, %esp /* Align global data to 16-byte boundary */ andl $0xfffffff0, %esp + post_code(POST_START_STACK) /* Zero the global data since it won't happen later */ xorl %eax, %eax @@ -91,31 +108,36 @@ car_init_ret: movl %esp, %edi rep stosb - /* Setup first parameter to setup_gdt */ + /* Setup first parameter to setup_gdt, pointer to global_data */ movl %esp, %eax /* Reserve space for global descriptor table */ subl $X86_GDT_SIZE, %esp + /* Align temporary global descriptor table to 16-byte boundary */ + andl $0xfffffff0, %esp + movl %esp, %ecx + #if defined(CONFIG_SYS_MALLOC_F_LEN) subl $CONFIG_SYS_MALLOC_F_LEN, %esp movl %eax, %edx addl $GD_MALLOC_BASE, %edx movl %esp, (%edx) #endif - - /* Align temporary global descriptor table to 16-byte boundary */ - andl $0xfffffff0, %esp + /* Store BIST */ + movl %eax, %edx + addl $GD_BIST, %edx + movl %ebp, (%edx) /* Set second parameter to setup_gdt */ - movl %esp, %edx + movl %ecx, %edx /* Setup global descriptor table so gd->xyz works */ call setup_gdt /* Set parameter to board_init_f() to boot flags */ + post_code(POST_START_DONE) xorl %eax, %eax - movw %bx, %ax /* Enter, U-boot! */ call board_init_f diff --git a/arch/x86/cpu/start16.S b/arch/x86/cpu/start16.S index 6968fda..9550502 100644 --- a/arch/x86/cpu/start16.S +++ b/arch/x86/cpu/start16.S @@ -21,18 +21,16 @@ .code16 .globl start16 start16: + /* Save BIST */ + movl %eax, %ecx + /* Set the Cold Boot / Hard Reset flag */ movl $GD_FLG_COLD_BOOT, %ebx - /* - * First we let the BSP do some early initialization - * this code have to map the flash to its final position - */ - jmp board_init16 -.globl board_init16_ret -board_init16_ret: + xorl %eax, %eax + movl %eax, %cr3 /* Invalidate TLB */ - /* Turn of cache (this might require a 486-class CPU) */ + /* Turn off cache (this might require a 486-class CPU) */ movl %cr0, %eax orl $(X86_CR0_NW | X86_CR0_CD), %eax movl %eax, %cr0 @@ -50,9 +48,11 @@ o32 cs lgdt gdt_ptr /* Flush the prefetch queue */ jmp ff ff: - /* Finally jump to the 32bit initialization code */ + + /* Finally restore BIST and jump to the 32bit initialization code */ movw $code32start, %ax movw %ax, %bp + movl %ecx, %eax o32 cs ljmp *(%bp) /* 48-bit far pointer */ |