summaryrefslogtreecommitdiff
path: root/arch/x86/cpu
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86/cpu')
-rw-r--r--arch/x86/cpu/Makefile11
-rw-r--r--arch/x86/cpu/call64.S93
-rw-r--r--arch/x86/cpu/config.mk5
-rw-r--r--arch/x86/cpu/coreboot/Kconfig15
-rw-r--r--arch/x86/cpu/coreboot/Makefile12
-rw-r--r--arch/x86/cpu/coreboot/coreboot.c75
-rw-r--r--arch/x86/cpu/coreboot/ipchecksum.c5
-rw-r--r--arch/x86/cpu/coreboot/pci.c42
-rw-r--r--arch/x86/cpu/coreboot/sdram.c17
-rw-r--r--arch/x86/cpu/coreboot/tables.c6
-rw-r--r--arch/x86/cpu/coreboot/timestamp.c33
-rw-r--r--arch/x86/cpu/cpu.c372
-rw-r--r--arch/x86/cpu/interrupts.c56
-rw-r--r--arch/x86/cpu/ivybridge/Kconfig172
-rw-r--r--arch/x86/cpu/ivybridge/Makefile24
-rw-r--r--arch/x86/cpu/ivybridge/bd82x6x.c146
-rw-r--r--arch/x86/cpu/ivybridge/car.S240
-rw-r--r--arch/x86/cpu/ivybridge/cpu.c333
-rw-r--r--arch/x86/cpu/ivybridge/early_init.c145
-rw-r--r--arch/x86/cpu/ivybridge/early_me.c191
-rw-r--r--arch/x86/cpu/ivybridge/gma.c770
-rw-r--r--arch/x86/cpu/ivybridge/gma.h156
-rw-r--r--arch/x86/cpu/ivybridge/lpc.c569
-rw-r--r--arch/x86/cpu/ivybridge/me_status.c195
-rw-r--r--arch/x86/cpu/ivybridge/microcode_intel.c166
-rw-r--r--arch/x86/cpu/ivybridge/model_206ax.c514
-rw-r--r--arch/x86/cpu/ivybridge/northbridge.c188
-rw-r--r--arch/x86/cpu/ivybridge/pch.c123
-rw-r--r--arch/x86/cpu/ivybridge/pci.c100
-rw-r--r--arch/x86/cpu/ivybridge/report_platform.c89
-rw-r--r--arch/x86/cpu/ivybridge/sata.c225
-rw-r--r--arch/x86/cpu/ivybridge/sdram.c581
-rw-r--r--arch/x86/cpu/ivybridge/usb_ehci.c29
-rw-r--r--arch/x86/cpu/ivybridge/usb_xhci.c32
-rw-r--r--arch/x86/cpu/lapic.c57
-rw-r--r--arch/x86/cpu/mtrr.c81
-rw-r--r--arch/x86/cpu/pci.c113
-rw-r--r--arch/x86/cpu/queensbay/Kconfig79
-rw-r--r--arch/x86/cpu/queensbay/Makefile9
-rw-r--r--arch/x86/cpu/queensbay/fsp_configs.c20
-rw-r--r--arch/x86/cpu/queensbay/fsp_support.c408
-rw-r--r--arch/x86/cpu/queensbay/tnc.c72
-rw-r--r--arch/x86/cpu/queensbay/tnc_car.S124
-rw-r--r--arch/x86/cpu/queensbay/tnc_dram.c81
-rw-r--r--arch/x86/cpu/queensbay/tnc_pci.c61
-rw-r--r--arch/x86/cpu/queensbay/topcliff.c47
-rw-r--r--arch/x86/cpu/start.S65
-rw-r--r--arch/x86/cpu/start16.S18
-rw-r--r--arch/x86/cpu/turbo.c98
-rw-r--r--arch/x86/cpu/u-boot.lds18
50 files changed, 6905 insertions, 176 deletions
diff --git a/arch/x86/cpu/Makefile b/arch/x86/cpu/Makefile
index e7bb3e33d5..62e43c04e5 100644
--- a/arch/x86/cpu/Makefile
+++ b/arch/x86/cpu/Makefile
@@ -10,4 +10,13 @@
extra-y = start.o
obj-$(CONFIG_X86_RESET_VECTOR) += resetvec.o start16.o
-obj-y += interrupts.o cpu.o
+obj-y += interrupts.o cpu.o call64.o
+
+obj-$(CONFIG_SYS_COREBOOT) += coreboot/
+obj-$(CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE) += ivybridge/
+obj-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += ivybridge/
+obj-$(CONFIG_INTEL_QUEENSBAY) += queensbay/
+obj-y += lapic.o
+obj-y += mtrr.o
+obj-$(CONFIG_PCI) += pci.o
+obj-y += turbo.o
diff --git a/arch/x86/cpu/call64.S b/arch/x86/cpu/call64.S
new file mode 100644
index 0000000000..74dd5a89dc
--- /dev/null
+++ b/arch/x86/cpu/call64.S
@@ -0,0 +1,93 @@
+/*
+ * (C) Copyright 2014 Google, Inc
+ * Copyright (C) 1991, 1992, 1993 Linus Torvalds
+ *
+ * Parts of this copied from Linux arch/x86/boot/compressed/head_64.S
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <asm/global_data.h>
+#include <asm/msr-index.h>
+#include <asm/processor-flags.h>
+
+.code32
+.globl cpu_call64
+cpu_call64:
+ /*
+ * cpu_call64(ulong pgtable, ulong setup_base, ulong target)
+ *
+ * eax - pgtable
+ * edx - setup_base
+ * ecx - target
+ */
+ cli
+ push %ecx /* arg2 = target */
+ push %edx /* arg1 = setup_base */
+ mov %eax, %ebx
+
+ /* Load new GDT with the 64bit segments using 32bit descriptor */
+ leal gdt, %eax
+ movl %eax, gdt+2
+ lgdt gdt
+
+ /* Enable PAE mode */
+ movl $(X86_CR4_PAE), %eax
+ movl %eax, %cr4
+
+ /* Enable the boot page tables */
+ leal (%ebx), %eax
+ movl %eax, %cr3
+
+ /* Enable Long mode in EFER (Extended Feature Enable Register) */
+ movl $MSR_EFER, %ecx
+ rdmsr
+ btsl $_EFER_LME, %eax
+ wrmsr
+
+ /* After gdt is loaded */
+ xorl %eax, %eax
+ lldt %ax
+ movl $0x20, %eax
+ ltr %ax
+
+ /*
+ * Setup for the jump to 64bit mode
+ *
+ * When the jump is performed we will be in long mode but
+ * in 32bit compatibility mode with EFER.LME = 1, CS.L = 0, CS.D = 1
+ * (and in turn EFER.LMA = 1). To jump into 64bit mode we use
+ * the new gdt/idt that has __KERNEL_CS with CS.L = 1.
+ * We place all of the values on our mini stack so lret can
+ * used to perform that far jump. See the gdt below.
+ */
+ pop %esi /* setup_base */
+
+ pushl $0x10
+ leal lret_target, %eax
+ pushl %eax
+
+ /* Enter paged protected Mode, activating Long Mode */
+ movl $(X86_CR0_PG | X86_CR0_PE), %eax
+ movl %eax, %cr0
+
+ /* Jump from 32bit compatibility mode into 64bit mode. */
+ lret
+
+code64:
+lret_target:
+ pop %eax /* target */
+ mov %eax, %eax /* Clear bits 63:32 */
+ jmp *%eax /* Jump to the 64-bit target */
+
+ .data
+gdt:
+ .word gdt_end - gdt
+ .long gdt
+ .word 0
+ .quad 0x0000000000000000 /* NULL descriptor */
+ .quad 0x00af9a000000ffff /* __KERNEL_CS */
+ .quad 0x00cf92000000ffff /* __KERNEL_DS */
+ .quad 0x0080890000000000 /* TS descriptor */
+ .quad 0x0000000000000000 /* TS continued */
+gdt_end:
diff --git a/arch/x86/cpu/config.mk b/arch/x86/cpu/config.mk
index f7b01d36e0..84aeaf3edc 100644
--- a/arch/x86/cpu/config.mk
+++ b/arch/x86/cpu/config.mk
@@ -12,5 +12,6 @@ PLATFORM_CPPFLAGS += -D__I386__ -Werror
# DO NOT MODIFY THE FOLLOWING UNLESS YOU REALLY KNOW WHAT YOU ARE DOING!
LDPPFLAGS += -DRESET_SEG_START=0xffff0000
LDPPFLAGS += -DRESET_SEG_SIZE=0x10000
-LDPPFLAGS += -DRESET_VEC_LOC=0xfff0
-LDPPFLAGS += -DSTART_16=0xf800
+LDPPFLAGS += -DRESET_VEC_LOC=0xfffffff0
+LDPPFLAGS += -DSTART_16=$(CONFIG_SYS_X86_START16)
+LDPPFLAGS += -DRESET_BASE="CONFIG_SYS_TEXT_BASE + (CONFIG_SYS_MONITOR_LEN - RESET_SEG_SIZE)"
diff --git a/arch/x86/cpu/coreboot/Kconfig b/arch/x86/cpu/coreboot/Kconfig
new file mode 100644
index 0000000000..e0e3c64506
--- /dev/null
+++ b/arch/x86/cpu/coreboot/Kconfig
@@ -0,0 +1,15 @@
+if TARGET_COREBOOT
+
+config SYS_COREBOOT
+ bool
+ default y
+
+config CBMEM_CONSOLE
+ bool
+ default y
+
+config VIDEO_COREBOOT
+ bool
+ default y
+
+endif
diff --git a/arch/x86/cpu/coreboot/Makefile b/arch/x86/cpu/coreboot/Makefile
index cd0bf4ed31..35e6cdd741 100644
--- a/arch/x86/cpu/coreboot/Makefile
+++ b/arch/x86/cpu/coreboot/Makefile
@@ -13,10 +13,10 @@
# SPDX-License-Identifier: GPL-2.0+
#
-obj-$(CONFIG_SYS_COREBOOT) += car.o
-obj-$(CONFIG_SYS_COREBOOT) += coreboot.o
-obj-$(CONFIG_SYS_COREBOOT) += tables.o
-obj-$(CONFIG_SYS_COREBOOT) += ipchecksum.o
-obj-$(CONFIG_SYS_COREBOOT) += sdram.o
-obj-$(CONFIG_SYS_COREBOOT) += timestamp.o
+obj-y += car.o
+obj-y += coreboot.o
+obj-y += tables.o
+obj-y += ipchecksum.o
+obj-y += sdram.o
+obj-y += timestamp.o
obj-$(CONFIG_PCI) += pci.o
diff --git a/arch/x86/cpu/coreboot/coreboot.c b/arch/x86/cpu/coreboot/coreboot.c
index e24f13afaf..6d06d5af19 100644
--- a/arch/x86/cpu/coreboot/coreboot.c
+++ b/arch/x86/cpu/coreboot/coreboot.c
@@ -13,25 +13,26 @@
#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/mtrr.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)
@@ -39,38 +40,9 @@ int board_early_init_f(void)
return 0;
}
-int board_early_init_r(void)
-{
- /* CPU Speed to 100MHz */
- gd->cpu_clk = 100000000;
-
- /* Crystal is 33.000MHz */
- gd->bus_clk = 33000000;
-
- 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)
@@ -93,12 +65,7 @@ int board_eth_init(bd_t *bis)
return pci_eth_init(bis);
}
-#define MTRR_TYPE_WP 5
-#define MTRRcap_MSR 0xfe
-#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.
@@ -106,22 +73,22 @@ int board_final_cleanup(void)
* Coreboot should have assigned this to the
* top available variable MTRR.
*/
- u8 top_mtrr = (native_read_msr(MTRRcap_MSR) & 0xff) - 1;
- u8 top_type = native_read_msr(MTRRphysBase_MSR(top_mtrr)) & 0xff;
+ u8 top_mtrr = (native_read_msr(MTRR_CAP_MSR) & 0xff) - 1;
+ u8 top_type = native_read_msr(MTRR_PHYS_BASE_MSR(top_mtrr)) & 0xff;
/* Make sure this MTRR is the correct Write-Protected type */
- if (top_type == MTRR_TYPE_WP) {
- disable_caches();
- wrmsrl(MTRRphysBase_MSR(top_mtrr), 0);
- wrmsrl(MTRRphysMask_MSR(top_mtrr), 0);
- enable_caches();
+ if (top_type == MTRR_TYPE_WRPROT) {
+ struct mtrr_state state;
+
+ mtrr_open(&state);
+ wrmsrl(MTRR_PHYS_BASE_MSR(top_mtrr), 0);
+ wrmsrl(MTRR_PHYS_MASK_MSR(top_mtrr), 0);
+ mtrr_close(&state);
}
/* 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 57733d8f0d..3340872a87 100644
--- a/arch/x86/cpu/coreboot/ipchecksum.c
+++ b/arch/x86/cpu/coreboot/ipchecksum.c
@@ -29,8 +29,9 @@
* SUCH DAMAGE.
*/
-#include <compiler.h>
-#include <asm/arch-coreboot/ipchecksum.h>
+#include <linux/types.h>
+#include <linux/compiler.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 33f16a3079..c9983f1588 100644
--- a/arch/x86/cpu/coreboot/pci.c
+++ b/arch/x86/cpu/coreboot/pci.c
@@ -13,14 +13,14 @@
#include <pci.h>
#include <asm/pci.h>
-static struct pci_controller coreboot_hose;
+DECLARE_GLOBAL_DATA_PTR;
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);
- hose->last_busno = max(hose->last_busno, secondary);
+ hose->last_busno = max(hose->last_busno, (int)secondary);
pci_hose_scan_bus(hose, secondary);
}
@@ -31,19 +31,37 @@ 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;
+ hose->config_table = pci_coreboot_config_table;
+ hose->first_busno = 0;
+ hose->last_busno = 0;
- pci_set_region(coreboot_hose.regions + 0, 0x0, 0x0, 0xffffffff,
- PCI_REGION_MEM);
- coreboot_hose.region_count = 1;
+ /* 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_setup_type1(&coreboot_hose);
+ /* 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_register_hose(&coreboot_hose);
+ pci_set_region(hose->regions + 2,
+ CONFIG_PCI_PREF_BUS,
+ CONFIG_PCI_PREF_PHYS,
+ CONFIG_PCI_PREF_SIZE,
+ PCI_REGION_PREFETCH);
- pci_hose_scan(&coreboot_hose);
+ pci_set_region(hose->regions + 3,
+ 0,
+ 0,
+ gd->ram_size,
+ PCI_REGION_MEM | PCI_REGION_SYS_MEMORY);
+
+ hose->region_count = 4;
}
diff --git a/arch/x86/cpu/coreboot/sdram.c b/arch/x86/cpu/coreboot/sdram.c
index 959feaaea3..e98a2302e7 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>
@@ -22,7 +24,7 @@ unsigned install_e820_map(unsigned max_entries, struct e820entry *entries)
{
int i;
- unsigned num_entries = min(lib_sysinfo.n_memranges, max_entries);
+ unsigned num_entries = min((unsigned)lib_sysinfo.n_memranges, max_entries);
if (num_entries < lib_sysinfo.n_memranges) {
printf("Warning: Limiting e820 map to %d entries.\n",
num_entries);
@@ -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 0d91adc5e4..92b75286b1 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/coreboot/timestamp.c b/arch/x86/cpu/coreboot/timestamp.c
index bd3558a021..0edee6bd2c 100644
--- a/arch/x86/cpu/coreboot/timestamp.c
+++ b/arch/x86/cpu/coreboot/timestamp.c
@@ -3,18 +3,7 @@
*
* Copyright (C) 2011 The ChromiumOS Authors. All rights reserved.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
+ * SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
@@ -38,9 +27,27 @@ static struct timestamp_table *ts_table __attribute__((section(".data")));
void timestamp_init(void)
{
+#ifdef CONFIG_SYS_X86_TSC_TIMER
+ uint64_t base_time;
+#endif
+
ts_table = lib_sysinfo.tstamp_table;
#ifdef CONFIG_SYS_X86_TSC_TIMER
- timer_set_base(ts_table->base_time);
+ /*
+ * If coreboot is built with CONFIG_COLLECT_TIMESTAMPS, use the value
+ * of base_time in coreboot's timestamp table as our timer base,
+ * otherwise TSC counter value will be used.
+ *
+ * Sometimes even coreboot is built with CONFIG_COLLECT_TIMESTAMPS,
+ * the value of base_time in the timestamp table is still zero, so
+ * we must exclude this case too (this is currently seen on booting
+ * coreboot in qemu)
+ */
+ if (ts_table && ts_table->base_time)
+ base_time = ts_table->base_time;
+ else
+ base_time = rdtsc();
+ timer_set_base(base_time);
#endif
timestamp_add_now(TS_U_BOOT_INITTED);
}
diff --git a/arch/x86/cpu/cpu.c b/arch/x86/cpu/cpu.c
index 623e3af61f..30e5069698 100644
--- a/arch/x86/cpu/cpu.c
+++ b/arch/x86/cpu/cpu.c
@@ -13,17 +13,26 @@
* 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+
*/
#include <common.h>
#include <command.h>
+#include <errno.h>
+#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
@@ -40,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));
@@ -69,7 +124,7 @@ static void load_gdt(const u64 *boot_gdt, u16 num_entries)
{
struct gdt_ptr gdt;
- gdt.len = (num_entries * 8) - 1;
+ gdt.len = (num_entries * X86_GDT_ENTRY_SIZE) - 1;
gdt.ptr = (u32)boot_gdt;
asm volatile("lgdtl %0\n" : : "m" (gdt));
@@ -89,10 +144,13 @@ void setup_gdt(gd_t *id, u64 *gdt_addr)
(ulong)&id->arch.gd_addr, 0xfffff);
/* 16-bit CS: code, read/execute, 64 kB, base 0 */
- gdt_addr[X86_GDT_ENTRY_16BIT_CS] = GDT_ENTRY(0x109b, 0, 0x0ffff);
+ gdt_addr[X86_GDT_ENTRY_16BIT_CS] = GDT_ENTRY(0x009b, 0, 0x0ffff);
/* 16-bit DS: data, read/write, 64 kB, base 0 */
- gdt_addr[X86_GDT_ENTRY_16BIT_DS] = GDT_ENTRY(0x1093, 0, 0x0ffff);
+ gdt_addr[X86_GDT_ENTRY_16BIT_DS] = GDT_ENTRY(0x0093, 0, 0x0ffff);
+
+ gdt_addr[X86_GDT_ENTRY_16BIT_FLAT_CS] = GDT_ENTRY(0x809b, 0, 0xfffff);
+ gdt_addr[X86_GDT_ENTRY_16BIT_FLAT_DS] = GDT_ENTRY(0x8093, 0, 0xfffff);
load_gdt(gdt_addr, X86_GDT_NUM_ENTRIES);
load_ds(X86_GDT_ENTRY_32BIT_DS);
@@ -112,6 +170,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;
@@ -125,17 +306,22 @@ int x86_cpu_init_f(void)
"movl %%eax, %%cr0\n" \
: : "i" (em_rst), "i" (mp_ne_set) : "eax");
- return 0;
-}
-int cpu_init_f(void) __attribute__((weak, alias("x86_cpu_init_f")));
+ /* 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;
+ }
-int x86_cpu_init_r(void)
-{
- /* Initialize core interrupt and exception functionality of CPU */
- cpu_init_interrupts();
return 0;
}
-int cpu_init_r(void) __attribute__((weak, alias("x86_cpu_init_r")));
void x86_enable_caches(void)
{
@@ -195,14 +381,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)
{
@@ -240,3 +425,164 @@ int icache_status(void)
{
return 1;
}
+
+void cpu_enable_paging_pae(ulong cr3)
+{
+ __asm__ __volatile__(
+ /* Load the page table address */
+ "movl %0, %%cr3\n"
+ /* Enable pae */
+ "movl %%cr4, %%eax\n"
+ "orl $0x00000020, %%eax\n"
+ "movl %%eax, %%cr4\n"
+ /* Enable paging */
+ "movl %%cr0, %%eax\n"
+ "orl $0x80000000, %%eax\n"
+ "movl %%eax, %%cr0\n"
+ :
+ : "r" (cr3)
+ : "eax");
+}
+
+void cpu_disable_paging_pae(void)
+{
+ /* Turn off paging */
+ __asm__ __volatile__ (
+ /* Disable paging */
+ "movl %%cr0, %%eax\n"
+ "andl $0x7fffffff, %%eax\n"
+ "movl %%eax, %%cr0\n"
+ /* Disable pae */
+ "movl %%cr4, %%eax\n"
+ "andl $0xffffffdf, %%eax\n"
+ "movl %%eax, %%cr4\n"
+ :
+ :
+ : "eax");
+}
+
+static bool can_detect_long_mode(void)
+{
+ return cpuid_eax(0x80000000) > 0x80000000UL;
+}
+
+static bool has_long_mode(void)
+{
+ return cpuid_edx(0x80000001) & (1 << 29) ? true : false;
+}
+
+int cpu_has_64bit(void)
+{
+ return has_cpuid() && can_detect_long_mode() &&
+ has_long_mode();
+}
+
+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 name;
+}
+
+char *cpu_get_name(char *name)
+{
+ unsigned int *name_as_ints = (unsigned int *)name;
+ struct cpuid_result regs;
+ char *ptr;
+ int i;
+
+ /* 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';
+
+ /* Skip leading spaces. */
+ ptr = name;
+ while (*ptr == ' ')
+ ptr++;
+
+ return ptr;
+}
+
+int default_print_cpuinfo(void)
+{
+ 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;
+}
+
+#define PAGETABLE_SIZE (6 * 4096)
+
+/**
+ * build_pagetable() - build a flat 4GiB page table structure for 64-bti mode
+ *
+ * @pgtable: Pointer to a 24iKB block of memory
+ */
+static void build_pagetable(uint32_t *pgtable)
+{
+ uint i;
+
+ memset(pgtable, '\0', PAGETABLE_SIZE);
+
+ /* Level 4 needs a single entry */
+ pgtable[0] = (uint32_t)&pgtable[1024] + 7;
+
+ /* Level 3 has one 64-bit entry for each GiB of memory */
+ for (i = 0; i < 4; i++) {
+ pgtable[1024 + i * 2] = (uint32_t)&pgtable[2048] +
+ 0x1000 * i + 7;
+ }
+
+ /* Level 2 has 2048 64-bit entries, each repesenting 2MiB */
+ for (i = 0; i < 2048; i++)
+ pgtable[2048 + i * 2] = 0x183 + (i << 21UL);
+}
+
+int cpu_jump_to_64bit(ulong setup_base, ulong target)
+{
+ uint32_t *pgtable;
+
+ pgtable = memalign(4096, PAGETABLE_SIZE);
+ if (!pgtable)
+ return -ENOMEM;
+
+ build_pagetable(pgtable);
+ cpu_call64((ulong)pgtable, setup_base, target);
+ free(pgtable);
+
+ 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 6f3d85fab0..a21d2a6c50 100644
--- a/arch/x86/cpu/interrupts.c
+++ b/arch/x86/cpu/interrupts.c
@@ -20,6 +20,7 @@
#include <linux/compiler.h>
#include <asm/msr.h>
#include <asm/u-boot-x86.h>
+#include <asm/i8259.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -31,7 +32,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;
@@ -128,9 +129,6 @@ int cpu_init_interrupts(void)
int irq_entry_size = irq_1 - irq_0;
void *irq_entry = (void *)irq_0;
- /* Just in case... */
- disable_interrupts();
-
/* Setup the IDT */
for (i = 0; i < 256; i++) {
idt[i].access = 0x8e;
@@ -146,9 +144,6 @@ int cpu_init_interrupts(void)
load_idt(&idt_ptr);
- /* It is now safe to enable interrupts */
- enable_interrupts();
-
return 0;
}
@@ -172,6 +167,25 @@ int disable_interrupts(void)
return flags & X86_EFLAGS_IF;
}
+int interrupt_init(void)
+{
+ /* Just in case... */
+ disable_interrupts();
+
+#ifdef CONFIG_SYS_PCAT_INTERRUPTS
+ /* Initialize the master/slave i8259 pic */
+ i8259_init();
+#endif
+
+ /* Initialize core interrupt and exception functionality of CPU */
+ cpu_init_interrupts();
+
+ /* It is now safe to enable interrupts */
+ enable_interrupts();
+
+ return 0;
+}
+
/* IRQ Low-Level Service Routine */
void irq_llsr(struct irq_regs *regs)
{
@@ -603,31 +617,3 @@ asm(".globl irq_common_entry\n" \
DECLARE_INTERRUPT(253) \
DECLARE_INTERRUPT(254) \
DECLARE_INTERRUPT(255));
-
-#if defined(CONFIG_INTEL_CORE_ARCH)
-/*
- * Get the number of CPU time counter ticks since it was read first time after
- * restart. This yields a free running counter guaranteed to take almost 6
- * years to wrap around even at 100GHz clock rate.
- */
-u64 get_ticks(void)
-{
- u64 now_tick = rdtsc();
-
- if (!gd->arch.tsc_base)
- gd->arch.tsc_base = now_tick;
-
- return now_tick - gd->arch.tsc_base;
-}
-
-#define PLATFORM_INFO_MSR 0xce
-
-unsigned long get_tbclk(void)
-{
- u32 ratio;
- u64 platform_info = native_read_msr(PLATFORM_INFO_MSR);
-
- ratio = (platform_info >> 8) & 0xff;
- return 100 * 1000 * 1000 * ratio; /* 100MHz times Max Non Turbo ratio */
-}
-#endif
diff --git a/arch/x86/cpu/ivybridge/Kconfig b/arch/x86/cpu/ivybridge/Kconfig
new file mode 100644
index 0000000000..afca9579da
--- /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 0000000000..0c7efaec7c
--- /dev/null
+++ b/arch/x86/cpu/ivybridge/Makefile
@@ -0,0 +1,24 @@
+#
+# Copyright (c) 2014 Google, Inc
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+
+obj-y += bd82x6x.o
+obj-y += car.o
+obj-y += cpu.o
+obj-y += early_init.o
+obj-y += early_me.o
+obj-y += gma.o
+obj-y += lpc.o
+obj-y += me_status.o
+obj-y += model_206ax.o
+obj-y += microcode_intel.o
+obj-y += northbridge.o
+obj-y += pch.o
+obj-y += pci.o
+obj-y += report_platform.o
+obj-y += sata.o
+obj-y += sdram.o
+obj-y += usb_ehci.o
+obj-y += usb_xhci.o
diff --git a/arch/x86/cpu/ivybridge/bd82x6x.c b/arch/x86/cpu/ivybridge/bd82x6x.c
new file mode 100644
index 0000000000..65a17d3e7f
--- /dev/null
+++ b/arch/x86/cpu/ivybridge/bd82x6x.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2014 Google, Inc
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <malloc.h>
+#include <asm/lapic.h>
+#include <asm/pci.h>
+#include <asm/arch/bd82x6x.h>
+#include <asm/arch/model_206ax.h>
+#include <asm/arch/pch.h>
+#include <asm/arch/sandybridge.h>
+
+void bd82x6x_pci_init(pci_dev_t dev)
+{
+ u16 reg16;
+ u8 reg8;
+
+ debug("bd82x6x PCI init.\n");
+ /* Enable Bus Master */
+ reg16 = pci_read_config16(dev, PCI_COMMAND);
+ reg16 |= PCI_COMMAND_MASTER;
+ pci_write_config16(dev, PCI_COMMAND, reg16);
+
+ /* This device has no interrupt */
+ pci_write_config8(dev, INTR, 0xff);
+
+ /* disable parity error response and SERR */
+ reg16 = pci_read_config16(dev, BCTRL);
+ reg16 &= ~(1 << 0);
+ reg16 &= ~(1 << 1);
+ pci_write_config16(dev, BCTRL, reg16);
+
+ /* Master Latency Count must be set to 0x04! */
+ reg8 = pci_read_config8(dev, SMLT);
+ reg8 &= 0x07;
+ reg8 |= (0x04 << 3);
+ pci_write_config8(dev, SMLT, reg8);
+
+ /* Will this improve throughput of bus masters? */
+ pci_write_config8(dev, PCI_MIN_GNT, 0x06);
+
+ /* Clear errors in status registers */
+ reg16 = pci_read_config16(dev, PSTS);
+ /* reg16 |= 0xf900; */
+ pci_write_config16(dev, PSTS, reg16);
+
+ reg16 = pci_read_config16(dev, SECSTS);
+ /* reg16 |= 0xf900; */
+ pci_write_config16(dev, SECSTS, reg16);
+}
+
+#define PCI_BRIDGE_UPDATE_COMMAND
+void bd82x6x_pci_dev_enable_resources(pci_dev_t dev)
+{
+ uint16_t command;
+
+ command = pci_read_config16(dev, PCI_COMMAND);
+ command |= PCI_COMMAND_IO;
+#ifdef PCI_BRIDGE_UPDATE_COMMAND
+ /*
+ * If we write to PCI_COMMAND, on some systems this will cause the
+ * ROM and APICs to become invisible.
+ */
+ debug("%x cmd <- %02x\n", dev, command);
+ pci_write_config16(dev, PCI_COMMAND, command);
+#else
+ printf("%s cmd <- %02x (NOT WRITTEN!)\n", dev_path(dev), command);
+#endif
+}
+
+void bd82x6x_pci_bus_enable_resources(pci_dev_t dev)
+{
+ uint16_t ctrl;
+
+ ctrl = pci_read_config16(dev, PCI_BRIDGE_CONTROL);
+ ctrl |= PCI_COMMAND_IO;
+ ctrl |= PCI_BRIDGE_CTL_VGA;
+ debug("%x bridge ctrl <- %04x\n", dev, ctrl);
+ pci_write_config16(dev, PCI_BRIDGE_CONTROL, ctrl);
+
+ bd82x6x_pci_dev_enable_resources(dev);
+}
+
+int bd82x6x_init_pci_devices(void)
+{
+ const void *blob = gd->fdt_blob;
+ struct pci_controller *hose;
+ struct x86_cpu_priv *cpu;
+ int sata_node, gma_node;
+ int ret;
+
+ hose = pci_bus_to_hose(0);
+ lpc_enable(PCH_LPC_DEV);
+ lpc_init(hose, PCH_LPC_DEV);
+ sata_node = fdtdec_next_compatible(blob, 0,
+ COMPAT_INTEL_PANTHERPOINT_AHCI);
+ if (sata_node < 0) {
+ debug("%s: Cannot find SATA node\n", __func__);
+ return -EINVAL;
+ }
+ bd82x6x_sata_init(PCH_SATA_DEV, blob, sata_node);
+ bd82x6x_usb_ehci_init(PCH_EHCI1_DEV);
+ bd82x6x_usb_ehci_init(PCH_EHCI2_DEV);
+
+ cpu = calloc(1, sizeof(*cpu));
+ if (!cpu)
+ return -ENOMEM;
+ model_206ax_init(cpu);
+
+ gma_node = fdtdec_next_compatible(blob, 0, COMPAT_INTEL_GMA);
+ if (gma_node < 0) {
+ debug("%s: Cannot find GMA node\n", __func__);
+ return -EINVAL;
+ }
+ ret = gma_func0_init(PCH_VIDEO_DEV, pci_bus_to_hose(0), blob,
+ gma_node);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int bd82x6x_init(void)
+{
+ const void *blob = gd->fdt_blob;
+ int sata_node;
+
+ sata_node = fdtdec_next_compatible(blob, 0,
+ COMPAT_INTEL_PANTHERPOINT_AHCI);
+ if (sata_node < 0) {
+ debug("%s: Cannot find SATA node\n", __func__);
+ return -EINVAL;
+ }
+
+ bd82x6x_pci_init(PCH_DEV);
+ bd82x6x_sata_enable(PCH_SATA_DEV, blob, sata_node);
+ northbridge_enable(PCH_DEV);
+ northbridge_init(PCH_DEV);
+
+ return 0;
+}
diff --git a/arch/x86/cpu/ivybridge/car.S b/arch/x86/cpu/ivybridge/car.S
new file mode 100644
index 0000000000..9441666f5a
--- /dev/null
+++ b/arch/x86/cpu/ivybridge/car.S
@@ -0,0 +1,240 @@
+/*
+ * 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/msr-index.h>
+#include <asm/mtrr.h>
+#include <asm/post.h>
+#include <asm/processor-flags.h>
+#include <asm/arch/microcode.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)
+
+ /* TODO: Load microcode later - the 'no eviction' mode breaks this */
+ movl $MSR_IA32_UCODE_WRITE, %ecx
+ xorl %edx, %edx
+ movl $_dt_ucode_base_size, %eax
+ movl (%eax), %eax
+ addl $UCODE_HEADER_LEN, %eax
+ wrmsr
+
+ 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 $MTRR_DEF_TYPE_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) | MTRR_PHYS_MASK_VALID), %eax
+ movl $CPU_PHYSMASK_HI, %edx
+ wrmsr
+
+ post_code(POST_CAR_MASK)
+
+ /* Enable MTRR */
+ movl $MTRR_DEF_TYPE_MSR, %ecx
+ rdmsr
+ orl $MTRR_DEF_TYPE_EN, %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) | MTRR_PHYS_MASK_VALID), %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 | MTRR_PHYS_MASK_VALID), %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
+
+.globl car_uninit
+car_uninit:
+ /* Disable cache */
+ movl %cr0, %eax
+ orl $X86_CR0_CD, %eax
+ movl %eax, %cr0
+
+ /* Disable MTRRs */
+ movl $MTRR_DEF_TYPE_MSR, %ecx
+ rdmsr
+ andl $(~MTRR_DEF_TYPE_EN), %eax
+ wrmsr
+
+ /* Disable the no-eviction run state */
+ movl NOEVICTMOD_MSR, %ecx
+ rdmsr
+ andl $~2, %eax
+ wrmsr
+
+ invd
+
+ /* Disable the no-eviction mode */
+ rdmsr
+ andl $~1, %eax
+ wrmsr
+
+#ifdef CONFIG_CACHE_MRC_BIN
+ /* Clear the MTRR that was used to cache MRC */
+ xorl %eax, %eax
+ xorl %edx, %edx
+ movl $MTRR_PHYS_BASE_MSR(2), %ecx
+ wrmsr
+ movl $MTRR_PHYS_MASK_MSR(2), %ecx
+ wrmsr
+#endif
+
+ /* Enable MTRRs */
+ movl $MTRR_DEF_TYPE_MSR, %ecx
+ rdmsr
+ orl $MTRR_DEF_TYPE_EN, %eax
+ wrmsr
+
+ invd
+
+ 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:
+
+ .align 4
+_dt_ucode_base_size:
+ /* These next two fields are filled in by ifdtool */
+ .long 0 /* microcode base */
+ .long 0 /* microcode size */
diff --git a/arch/x86/cpu/ivybridge/cpu.c b/arch/x86/cpu/ivybridge/cpu.c
new file mode 100644
index 0000000000..e9253100f6
--- /dev/null
+++ b/arch/x86/cpu/ivybridge/cpu.c
@@ -0,0 +1,333 @@
+/*
+ * 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, &reg8);
+ reg8 &= ~(3 << 2);
+ reg8 |= (2 << 2); /* Prefetching and Caching Enabled */
+ pci_hose_write_config_byte(hose, dev, 0xdc, reg8);
+}
+
+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);
+
+ 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) {
+ post_code(POST_BIST_FAILURE);
+ 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)
+ 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 0000000000..eb8f6139fe
--- /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 0000000000..b24dea10b1
--- /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/gma.c b/arch/x86/cpu/ivybridge/gma.c
new file mode 100644
index 0000000000..6cf9654e02
--- /dev/null
+++ b/arch/x86/cpu/ivybridge/gma.c
@@ -0,0 +1,770 @@
+/*
+ * From Coreboot file of the same name
+ *
+ * Copyright (C) 2011 Chromium OS Authors
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <common.h>
+#include <bios_emul.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <pci_rom.h>
+#include <asm/io.h>
+#include <asm/mtrr.h>
+#include <asm/pci.h>
+#include <asm/arch/pch.h>
+#include <asm/arch/sandybridge.h>
+#include <linux/kconfig.h>
+
+struct gt_powermeter {
+ u16 reg;
+ u32 value;
+};
+
+static const struct gt_powermeter snb_pm_gt1[] = {
+ { 0xa200, 0xcc000000 },
+ { 0xa204, 0x07000040 },
+ { 0xa208, 0x0000fe00 },
+ { 0xa20c, 0x00000000 },
+ { 0xa210, 0x17000000 },
+ { 0xa214, 0x00000021 },
+ { 0xa218, 0x0817fe19 },
+ { 0xa21c, 0x00000000 },
+ { 0xa220, 0x00000000 },
+ { 0xa224, 0xcc000000 },
+ { 0xa228, 0x07000040 },
+ { 0xa22c, 0x0000fe00 },
+ { 0xa230, 0x00000000 },
+ { 0xa234, 0x17000000 },
+ { 0xa238, 0x00000021 },
+ { 0xa23c, 0x0817fe19 },
+ { 0xa240, 0x00000000 },
+ { 0xa244, 0x00000000 },
+ { 0xa248, 0x8000421e },
+ { 0 }
+};
+
+static const struct gt_powermeter snb_pm_gt2[] = {
+ { 0xa200, 0x330000a6 },
+ { 0xa204, 0x402d0031 },
+ { 0xa208, 0x00165f83 },
+ { 0xa20c, 0xf1000000 },
+ { 0xa210, 0x00000000 },
+ { 0xa214, 0x00160016 },
+ { 0xa218, 0x002a002b },
+ { 0xa21c, 0x00000000 },
+ { 0xa220, 0x00000000 },
+ { 0xa224, 0x330000a6 },
+ { 0xa228, 0x402d0031 },
+ { 0xa22c, 0x00165f83 },
+ { 0xa230, 0xf1000000 },
+ { 0xa234, 0x00000000 },
+ { 0xa238, 0x00160016 },
+ { 0xa23c, 0x002a002b },
+ { 0xa240, 0x00000000 },
+ { 0xa244, 0x00000000 },
+ { 0xa248, 0x8000421e },
+ { 0 }
+};
+
+static const struct gt_powermeter ivb_pm_gt1[] = {
+ { 0xa800, 0x00000000 },
+ { 0xa804, 0x00021c00 },
+ { 0xa808, 0x00000403 },
+ { 0xa80c, 0x02001700 },
+ { 0xa810, 0x05000200 },
+ { 0xa814, 0x00000000 },
+ { 0xa818, 0x00690500 },
+ { 0xa81c, 0x0000007f },
+ { 0xa820, 0x01002501 },
+ { 0xa824, 0x00000300 },
+ { 0xa828, 0x01000331 },
+ { 0xa82c, 0x0000000c },
+ { 0xa830, 0x00010016 },
+ { 0xa834, 0x01100101 },
+ { 0xa838, 0x00010103 },
+ { 0xa83c, 0x00041300 },
+ { 0xa840, 0x00000b30 },
+ { 0xa844, 0x00000000 },
+ { 0xa848, 0x7f000000 },
+ { 0xa84c, 0x05000008 },
+ { 0xa850, 0x00000001 },
+ { 0xa854, 0x00000004 },
+ { 0xa858, 0x00000007 },
+ { 0xa85c, 0x00000000 },
+ { 0xa860, 0x00010000 },
+ { 0xa248, 0x0000221e },
+ { 0xa900, 0x00000000 },
+ { 0xa904, 0x00001c00 },
+ { 0xa908, 0x00000000 },
+ { 0xa90c, 0x06000000 },
+ { 0xa910, 0x09000200 },
+ { 0xa914, 0x00000000 },
+ { 0xa918, 0x00590000 },
+ { 0xa91c, 0x00000000 },
+ { 0xa920, 0x04002501 },
+ { 0xa924, 0x00000100 },
+ { 0xa928, 0x03000410 },
+ { 0xa92c, 0x00000000 },
+ { 0xa930, 0x00020000 },
+ { 0xa934, 0x02070106 },
+ { 0xa938, 0x00010100 },
+ { 0xa93c, 0x00401c00 },
+ { 0xa940, 0x00000000 },
+ { 0xa944, 0x00000000 },
+ { 0xa948, 0x10000e00 },
+ { 0xa94c, 0x02000004 },
+ { 0xa950, 0x00000001 },
+ { 0xa954, 0x00000004 },
+ { 0xa960, 0x00060000 },
+ { 0xaa3c, 0x00001c00 },
+ { 0xaa54, 0x00000004 },
+ { 0xaa60, 0x00060000 },
+ { 0 }
+};
+
+static const struct gt_powermeter ivb_pm_gt2[] = {
+ { 0xa800, 0x10000000 },
+ { 0xa804, 0x00033800 },
+ { 0xa808, 0x00000902 },
+ { 0xa80c, 0x0c002f00 },
+ { 0xa810, 0x12000400 },
+ { 0xa814, 0x00000000 },
+ { 0xa818, 0x00d20800 },
+ { 0xa81c, 0x00000002 },
+ { 0xa820, 0x03004b02 },
+ { 0xa824, 0x00000600 },
+ { 0xa828, 0x07000773 },
+ { 0xa82c, 0x00000000 },
+ { 0xa830, 0x00010032 },
+ { 0xa834, 0x1520040d },
+ { 0xa838, 0x00020105 },
+ { 0xa83c, 0x00083700 },
+ { 0xa840, 0x0000151d },
+ { 0xa844, 0x00000000 },
+ { 0xa848, 0x20001b00 },
+ { 0xa84c, 0x0a000010 },
+ { 0xa850, 0x00000000 },
+ { 0xa854, 0x00000008 },
+ { 0xa858, 0x00000008 },
+ { 0xa85c, 0x00000000 },
+ { 0xa860, 0x00020000 },
+ { 0xa248, 0x0000221e },
+ { 0xa900, 0x00000000 },
+ { 0xa904, 0x00003500 },
+ { 0xa908, 0x00000000 },
+ { 0xa90c, 0x0c000000 },
+ { 0xa910, 0x12000500 },
+ { 0xa914, 0x00000000 },
+ { 0xa918, 0x00b20000 },
+ { 0xa91c, 0x00000000 },
+ { 0xa920, 0x08004b02 },
+ { 0xa924, 0x00000200 },
+ { 0xa928, 0x07000820 },
+ { 0xa92c, 0x00000000 },
+ { 0xa930, 0x00030000 },
+ { 0xa934, 0x050f020d },
+ { 0xa938, 0x00020300 },
+ { 0xa93c, 0x00903900 },
+ { 0xa940, 0x00000000 },
+ { 0xa944, 0x00000000 },
+ { 0xa948, 0x20001b00 },
+ { 0xa94c, 0x0a000010 },
+ { 0xa950, 0x00000000 },
+ { 0xa954, 0x00000008 },
+ { 0xa960, 0x00110000 },
+ { 0xaa3c, 0x00003900 },
+ { 0xaa54, 0x00000008 },
+ { 0xaa60, 0x00110000 },
+ { 0 }
+};
+
+static const struct gt_powermeter ivb_pm_gt2_17w[] = {
+ { 0xa800, 0x20000000 },
+ { 0xa804, 0x000e3800 },
+ { 0xa808, 0x00000806 },
+ { 0xa80c, 0x0c002f00 },
+ { 0xa810, 0x0c000800 },
+ { 0xa814, 0x00000000 },
+ { 0xa818, 0x00d20d00 },
+ { 0xa81c, 0x000000ff },
+ { 0xa820, 0x03004b02 },
+ { 0xa824, 0x00000600 },
+ { 0xa828, 0x07000773 },
+ { 0xa82c, 0x00000000 },
+ { 0xa830, 0x00020032 },
+ { 0xa834, 0x1520040d },
+ { 0xa838, 0x00020105 },
+ { 0xa83c, 0x00083700 },
+ { 0xa840, 0x000016ff },
+ { 0xa844, 0x00000000 },
+ { 0xa848, 0xff000000 },
+ { 0xa84c, 0x0a000010 },
+ { 0xa850, 0x00000002 },
+ { 0xa854, 0x00000008 },
+ { 0xa858, 0x0000000f },
+ { 0xa85c, 0x00000000 },
+ { 0xa860, 0x00020000 },
+ { 0xa248, 0x0000221e },
+ { 0xa900, 0x00000000 },
+ { 0xa904, 0x00003800 },
+ { 0xa908, 0x00000000 },
+ { 0xa90c, 0x0c000000 },
+ { 0xa910, 0x12000800 },
+ { 0xa914, 0x00000000 },
+ { 0xa918, 0x00b20000 },
+ { 0xa91c, 0x00000000 },
+ { 0xa920, 0x08004b02 },
+ { 0xa924, 0x00000300 },
+ { 0xa928, 0x01000820 },
+ { 0xa92c, 0x00000000 },
+ { 0xa930, 0x00030000 },
+ { 0xa934, 0x15150406 },
+ { 0xa938, 0x00020300 },
+ { 0xa93c, 0x00903900 },
+ { 0xa940, 0x00000000 },
+ { 0xa944, 0x00000000 },
+ { 0xa948, 0x20001b00 },
+ { 0xa94c, 0x0a000010 },
+ { 0xa950, 0x00000000 },
+ { 0xa954, 0x00000008 },
+ { 0xa960, 0x00110000 },
+ { 0xaa3c, 0x00003900 },
+ { 0xaa54, 0x00000008 },
+ { 0xaa60, 0x00110000 },
+ { 0 }
+};
+
+static const struct gt_powermeter ivb_pm_gt2_35w[] = {
+ { 0xa800, 0x00000000 },
+ { 0xa804, 0x00030400 },
+ { 0xa808, 0x00000806 },
+ { 0xa80c, 0x0c002f00 },
+ { 0xa810, 0x0c000300 },
+ { 0xa814, 0x00000000 },
+ { 0xa818, 0x00d20d00 },
+ { 0xa81c, 0x000000ff },
+ { 0xa820, 0x03004b02 },
+ { 0xa824, 0x00000600 },
+ { 0xa828, 0x07000773 },
+ { 0xa82c, 0x00000000 },
+ { 0xa830, 0x00020032 },
+ { 0xa834, 0x1520040d },
+ { 0xa838, 0x00020105 },
+ { 0xa83c, 0x00083700 },
+ { 0xa840, 0x000016ff },
+ { 0xa844, 0x00000000 },
+ { 0xa848, 0xff000000 },
+ { 0xa84c, 0x0a000010 },
+ { 0xa850, 0x00000001 },
+ { 0xa854, 0x00000008 },
+ { 0xa858, 0x00000008 },
+ { 0xa85c, 0x00000000 },
+ { 0xa860, 0x00020000 },
+ { 0xa248, 0x0000221e },
+ { 0xa900, 0x00000000 },
+ { 0xa904, 0x00003800 },
+ { 0xa908, 0x00000000 },
+ { 0xa90c, 0x0c000000 },
+ { 0xa910, 0x12000800 },
+ { 0xa914, 0x00000000 },
+ { 0xa918, 0x00b20000 },
+ { 0xa91c, 0x00000000 },
+ { 0xa920, 0x08004b02 },
+ { 0xa924, 0x00000300 },
+ { 0xa928, 0x01000820 },
+ { 0xa92c, 0x00000000 },
+ { 0xa930, 0x00030000 },
+ { 0xa934, 0x15150406 },
+ { 0xa938, 0x00020300 },
+ { 0xa93c, 0x00903900 },
+ { 0xa940, 0x00000000 },
+ { 0xa944, 0x00000000 },
+ { 0xa948, 0x20001b00 },
+ { 0xa94c, 0x0a000010 },
+ { 0xa950, 0x00000000 },
+ { 0xa954, 0x00000008 },
+ { 0xa960, 0x00110000 },
+ { 0xaa3c, 0x00003900 },
+ { 0xaa54, 0x00000008 },
+ { 0xaa60, 0x00110000 },
+ { 0 }
+};
+
+/*
+ * Some vga option roms are used for several chipsets but they only have one
+ * PCI ID in their header. If we encounter such an option rom, we need to do
+ * the mapping ourselves.
+ */
+
+u32 map_oprom_vendev(u32 vendev)
+{
+ u32 new_vendev = vendev;
+
+ switch (vendev) {
+ case 0x80860102: /* GT1 Desktop */
+ case 0x8086010a: /* GT1 Server */
+ case 0x80860112: /* GT2 Desktop */
+ case 0x80860116: /* GT2 Mobile */
+ case 0x80860122: /* GT2 Desktop >=1.3GHz */
+ case 0x80860126: /* GT2 Mobile >=1.3GHz */
+ case 0x80860156: /* IVB */
+ case 0x80860166: /* IVB */
+ /* Set to GT1 Mobile */
+ new_vendev = 0x80860106;
+ break;
+ }
+
+ return new_vendev;
+}
+
+static inline u32 gtt_read(void *bar, u32 reg)
+{
+ return readl(bar + reg);
+}
+
+static inline void gtt_write(void *bar, u32 reg, u32 data)
+{
+ writel(data, bar + reg);
+}
+
+static void gtt_write_powermeter(void *bar, const struct gt_powermeter *pm)
+{
+ for (; pm && pm->reg; pm++)
+ gtt_write(bar, pm->reg, pm->value);
+}
+
+#define GTT_RETRY 1000
+static int gtt_poll(void *bar, u32 reg, u32 mask, u32 value)
+{
+ unsigned try = GTT_RETRY;
+ u32 data;
+
+ while (try--) {
+ data = gtt_read(bar, reg);
+ if ((data & mask) == value)
+ return 1;
+ udelay(10);
+ }
+
+ printf("GT init timeout\n");
+ return 0;
+}
+
+static int gma_pm_init_pre_vbios(void *gtt_bar)
+{
+ u32 reg32;
+
+ debug("GT Power Management Init, silicon = %#x\n",
+ bridge_silicon_revision());
+
+ if (bridge_silicon_revision() < IVB_STEP_C0) {
+ /* 1: Enable force wake */
+ gtt_write(gtt_bar, 0xa18c, 0x00000001);
+ gtt_poll(gtt_bar, 0x130090, (1 << 0), (1 << 0));
+ } else {
+ gtt_write(gtt_bar, 0xa180, 1 << 5);
+ gtt_write(gtt_bar, 0xa188, 0xffff0001);
+ gtt_poll(gtt_bar, 0x130040, (1 << 0), (1 << 0));
+ }
+
+ if ((bridge_silicon_revision() & BASE_REV_MASK) == BASE_REV_SNB) {
+ /* 1d: Set GTT+0x42004 [15:14]=11 (SnB C1+) */
+ reg32 = gtt_read(gtt_bar, 0x42004);
+ reg32 |= (1 << 14) | (1 << 15);
+ gtt_write(gtt_bar, 0x42004, reg32);
+ }
+
+ if (bridge_silicon_revision() >= IVB_STEP_A0) {
+ /* Display Reset Acknowledge Settings */
+ reg32 = gtt_read(gtt_bar, 0x45010);
+ reg32 |= (1 << 1) | (1 << 0);
+ gtt_write(gtt_bar, 0x45010, reg32);
+ }
+
+ /* 2: Get GT SKU from GTT+0x911c[13] */
+ reg32 = gtt_read(gtt_bar, 0x911c);
+ if ((bridge_silicon_revision() & BASE_REV_MASK) == BASE_REV_SNB) {
+ if (reg32 & (1 << 13)) {
+ debug("SNB GT1 Power Meter Weights\n");
+ gtt_write_powermeter(gtt_bar, snb_pm_gt1);
+ } else {
+ debug("SNB GT2 Power Meter Weights\n");
+ gtt_write_powermeter(gtt_bar, snb_pm_gt2);
+ }
+ } else {
+ u32 unit = readl(MCHBAR_REG(0x5938)) & 0xf;
+
+ if (reg32 & (1 << 13)) {
+ /* GT1 SKU */
+ debug("IVB GT1 Power Meter Weights\n");
+ gtt_write_powermeter(gtt_bar, ivb_pm_gt1);
+ } else {
+ /* GT2 SKU */
+ u32 tdp = readl(MCHBAR_REG(0x5930)) & 0x7fff;
+ tdp /= (1 << unit);
+
+ if (tdp <= 17) {
+ /* <=17W ULV */
+ debug("IVB GT2 17W Power Meter Weights\n");
+ gtt_write_powermeter(gtt_bar, ivb_pm_gt2_17w);
+ } else if ((tdp >= 25) && (tdp <= 35)) {
+ /* 25W-35W */
+ debug("IVB GT2 25W-35W Power Meter Weights\n");
+ gtt_write_powermeter(gtt_bar, ivb_pm_gt2_35w);
+ } else {
+ /* All others */
+ debug("IVB GT2 35W Power Meter Weights\n");
+ gtt_write_powermeter(gtt_bar, ivb_pm_gt2_35w);
+ }
+ }
+ }
+
+ /* 3: Gear ratio map */
+ gtt_write(gtt_bar, 0xa004, 0x00000010);
+
+ /* 4: GFXPAUSE */
+ gtt_write(gtt_bar, 0xa000, 0x00070020);
+
+ /* 5: Dynamic EU trip control */
+ gtt_write(gtt_bar, 0xa080, 0x00000004);
+
+ /* 6: ECO bits */
+ reg32 = gtt_read(gtt_bar, 0xa180);
+ reg32 |= (1 << 26) | (1 << 31);
+ /* (bit 20=1 for SNB step D1+ / IVB A0+) */
+ if (bridge_silicon_revision() >= SNB_STEP_D1)
+ reg32 |= (1 << 20);
+ gtt_write(gtt_bar, 0xa180, reg32);
+
+ /* 6a: for SnB step D2+ only */
+ if (((bridge_silicon_revision() & BASE_REV_MASK) == BASE_REV_SNB) &&
+ (bridge_silicon_revision() >= SNB_STEP_D2)) {
+ reg32 = gtt_read(gtt_bar, 0x9400);
+ reg32 |= (1 << 7);
+ gtt_write(gtt_bar, 0x9400, reg32);
+
+ reg32 = gtt_read(gtt_bar, 0x941c);
+ reg32 &= 0xf;
+ reg32 |= (1 << 1);
+ gtt_write(gtt_bar, 0x941c, reg32);
+ gtt_poll(gtt_bar, 0x941c, (1 << 1), (0 << 1));
+ }
+
+ if ((bridge_silicon_revision() & BASE_REV_MASK) == BASE_REV_IVB) {
+ reg32 = gtt_read(gtt_bar, 0x907c);
+ reg32 |= (1 << 16);
+ gtt_write(gtt_bar, 0x907c, reg32);
+
+ /* 6b: Clocking reset controls */
+ gtt_write(gtt_bar, 0x9424, 0x00000001);
+ } else {
+ /* 6b: Clocking reset controls */
+ gtt_write(gtt_bar, 0x9424, 0x00000000);
+ }
+
+ /* 7 */
+ if (gtt_poll(gtt_bar, 0x138124, (1 << 31), (0 << 31))) {
+ gtt_write(gtt_bar, 0x138128, 0x00000029); /* Mailbox Data */
+ /* Mailbox Cmd for RC6 VID */
+ gtt_write(gtt_bar, 0x138124, 0x80000004);
+ if (gtt_poll(gtt_bar, 0x138124, (1 << 31), (0 << 31)))
+ gtt_write(gtt_bar, 0x138124, 0x8000000a);
+ gtt_poll(gtt_bar, 0x138124, (1 << 31), (0 << 31));
+ }
+
+ /* 8 */
+ gtt_write(gtt_bar, 0xa090, 0x00000000); /* RC Control */
+ gtt_write(gtt_bar, 0xa098, 0x03e80000); /* RC1e Wake Rate Limit */
+ gtt_write(gtt_bar, 0xa09c, 0x0028001e); /* RC6/6p Wake Rate Limit */
+ gtt_write(gtt_bar, 0xa0a0, 0x0000001e); /* RC6pp Wake Rate Limit */
+ gtt_write(gtt_bar, 0xa0a8, 0x0001e848); /* RC Evaluation Interval */
+ gtt_write(gtt_bar, 0xa0ac, 0x00000019); /* RC Idle Hysteresis */
+
+ /* 9 */
+ gtt_write(gtt_bar, 0x2054, 0x0000000a); /* Render Idle Max Count */
+ gtt_write(gtt_bar, 0x12054, 0x0000000a); /* Video Idle Max Count */
+ gtt_write(gtt_bar, 0x22054, 0x0000000a); /* Blitter Idle Max Count */
+
+ /* 10 */
+ gtt_write(gtt_bar, 0xa0b0, 0x00000000); /* Unblock Ack to Busy */
+ gtt_write(gtt_bar, 0xa0b4, 0x000003e8); /* RC1e Threshold */
+ gtt_write(gtt_bar, 0xa0b8, 0x0000c350); /* RC6 Threshold */
+ gtt_write(gtt_bar, 0xa0bc, 0x000186a0); /* RC6p Threshold */
+ gtt_write(gtt_bar, 0xa0c0, 0x0000fa00); /* RC6pp Threshold */
+
+ /* 11 */
+ gtt_write(gtt_bar, 0xa010, 0x000f4240); /* RP Down Timeout */
+ gtt_write(gtt_bar, 0xa014, 0x12060000); /* RP Interrupt Limits */
+ gtt_write(gtt_bar, 0xa02c, 0x00015f90); /* RP Up Threshold */
+ gtt_write(gtt_bar, 0xa030, 0x000186a0); /* RP Down Threshold */
+ gtt_write(gtt_bar, 0xa068, 0x000186a0); /* RP Up EI */
+ gtt_write(gtt_bar, 0xa06c, 0x000493e0); /* RP Down EI */
+ gtt_write(gtt_bar, 0xa070, 0x0000000a); /* RP Idle Hysteresis */
+
+ /* 11a: Enable Render Standby (RC6) */
+ if ((bridge_silicon_revision() & BASE_REV_MASK) == BASE_REV_IVB) {
+ /*
+ * IvyBridge should also support DeepRenderStandby.
+ *
+ * Unfortunately it does not work reliably on all SKUs so
+ * disable it here and it can be enabled by the kernel.
+ */
+ gtt_write(gtt_bar, 0xa090, 0x88040000); /* HW RC Control */
+ } else {
+ gtt_write(gtt_bar, 0xa090, 0x88040000); /* HW RC Control */
+ }
+
+ /* 12: Normal Frequency Request */
+ /* RPNFREQ_VAL comes from MCHBAR 0x5998 23:16 (8 bits!? use 7) */
+ reg32 = readl(MCHBAR_REG(0x5998));
+ reg32 >>= 16;
+ reg32 &= 0xef;
+ reg32 <<= 25;
+ gtt_write(gtt_bar, 0xa008, reg32);
+
+ /* 13: RP Control */
+ gtt_write(gtt_bar, 0xa024, 0x00000592);
+
+ /* 14: Enable PM Interrupts */
+ gtt_write(gtt_bar, 0x4402c, 0x03000076);
+
+ /* Clear 0x6c024 [8:6] */
+ reg32 = gtt_read(gtt_bar, 0x6c024);
+ reg32 &= ~0x000001c0;
+ gtt_write(gtt_bar, 0x6c024, reg32);
+
+ return 0;
+}
+
+int gma_pm_init_post_vbios(void *gtt_bar, const void *blob, int node)
+{
+ u32 reg32, cycle_delay;
+
+ debug("GT Power Management Init (post VBIOS)\n");
+
+ /* 15: Deassert Force Wake */
+ if (bridge_silicon_revision() < IVB_STEP_C0) {
+ gtt_write(gtt_bar, 0xa18c, gtt_read(gtt_bar, 0xa18c) & ~1);
+ gtt_poll(gtt_bar, 0x130090, (1 << 0), (0 << 0));
+ } else {
+ gtt_write(gtt_bar, 0xa188, 0x1fffe);
+ if (gtt_poll(gtt_bar, 0x130040, (1 << 0), (0 << 0))) {
+ gtt_write(gtt_bar, 0xa188,
+ gtt_read(gtt_bar, 0xa188) | 1);
+ }
+ }
+
+ /* 16: SW RC Control */
+ gtt_write(gtt_bar, 0xa094, 0x00060000);
+
+ /* Setup Digital Port Hotplug */
+ reg32 = gtt_read(gtt_bar, 0xc4030);
+ if (!reg32) {
+ u32 dp_hotplug[3];
+
+ if (fdtdec_get_int_array(blob, node, "intel,dp_hotplug",
+ dp_hotplug, ARRAY_SIZE(dp_hotplug)))
+ return -EINVAL;
+
+ reg32 = (dp_hotplug[0] & 0x7) << 2;
+ reg32 |= (dp_hotplug[0] & 0x7) << 10;
+ reg32 |= (dp_hotplug[0] & 0x7) << 18;
+ gtt_write(gtt_bar, 0xc4030, reg32);
+ }
+
+ /* Setup Panel Power On Delays */
+ reg32 = gtt_read(gtt_bar, 0xc7208);
+ if (!reg32) {
+ reg32 = (unsigned)fdtdec_get_int(blob, node,
+ "panel-port-select", 0) << 30;
+ reg32 |= fdtdec_get_int(blob, node, "panel-power-up-delay", 0)
+ << 16;
+ reg32 |= fdtdec_get_int(blob, node,
+ "panel-power-backlight-on-delay", 0);
+ gtt_write(gtt_bar, 0xc7208, reg32);
+ }
+
+ /* Setup Panel Power Off Delays */
+ reg32 = gtt_read(gtt_bar, 0xc720c);
+ if (!reg32) {
+ reg32 = fdtdec_get_int(blob, node, "panel-power-down-delay", 0)
+ << 16;
+ reg32 |= fdtdec_get_int(blob, node,
+ "panel-power-backlight-off-delay", 0);
+ gtt_write(gtt_bar, 0xc720c, reg32);
+ }
+
+ /* Setup Panel Power Cycle Delay */
+ cycle_delay = fdtdec_get_int(blob, node,
+ "intel,panel-power-cycle-delay", 0);
+ if (cycle_delay) {
+ reg32 = gtt_read(gtt_bar, 0xc7210);
+ reg32 &= ~0xff;
+ reg32 |= cycle_delay;
+ gtt_write(gtt_bar, 0xc7210, reg32);
+ }
+
+ /* Enable Backlight if needed */
+ reg32 = fdtdec_get_int(blob, node, "intel,cpu-backlight", 0);
+ if (reg32) {
+ gtt_write(gtt_bar, 0x48250, (1 << 31));
+ gtt_write(gtt_bar, 0x48254, reg32);
+ }
+ reg32 = fdtdec_get_int(blob, node, "intel,pch-backlight", 0);
+ if (reg32) {
+ gtt_write(gtt_bar, 0xc8250, (1 << 31));
+ gtt_write(gtt_bar, 0xc8254, reg32);
+ }
+
+ return 0;
+}
+
+/*
+ * Some vga option roms are used for several chipsets but they only have one
+ * PCI ID in their header. If we encounter such an option rom, we need to do
+ * the mapping ourselves.
+ */
+
+uint32_t board_map_oprom_vendev(uint32_t vendev)
+{
+ switch (vendev) {
+ case 0x80860102: /* GT1 Desktop */
+ case 0x8086010a: /* GT1 Server */
+ case 0x80860112: /* GT2 Desktop */
+ case 0x80860116: /* GT2 Mobile */
+ case 0x80860122: /* GT2 Desktop >=1.3GHz */
+ case 0x80860126: /* GT2 Mobile >=1.3GHz */
+ case 0x80860156: /* IVB */
+ case 0x80860166: /* IVB */
+ return 0x80860106; /* GT1 Mobile */
+ }
+
+ return vendev;
+}
+
+static int int15_handler(void)
+{
+ int res = 0;
+
+ debug("%s: INT15 function %04x!\n", __func__, M.x86.R_AX);
+
+ switch (M.x86.R_AX) {
+ case 0x5f34:
+ /*
+ * Set Panel Fitting Hook:
+ * bit 2 = Graphics Stretching
+ * bit 1 = Text Stretching
+ * bit 0 = Centering (do not set with bit1 or bit2)
+ * 0 = video bios default
+ */
+ M.x86.R_AX = 0x005f;
+ M.x86.R_CL = 0x00; /* Use video bios default */
+ res = 1;
+ break;
+ case 0x5f35:
+ /*
+ * Boot Display Device Hook:
+ * bit 0 = CRT
+ * bit 1 = TV (eDP)
+ * bit 2 = EFP
+ * bit 3 = LFP
+ * bit 4 = CRT2
+ * bit 5 = TV2 (eDP)
+ * bit 6 = EFP2
+ * bit 7 = LFP2
+ */
+ M.x86.R_AX = 0x005f;
+ M.x86.R_CX = 0x0000; /* Use video bios default */
+ res = 1;
+ break;
+ case 0x5f51:
+ /*
+ * Hook to select active LFP configuration:
+ * 00h = No LVDS, VBIOS does not enable LVDS
+ * 01h = Int-LVDS, LFP driven by integrated LVDS decoder
+ * 02h = SVDO-LVDS, LFP driven by SVDO decoder
+ * 03h = eDP, LFP Driven by Int-DisplayPort encoder
+ */
+ M.x86.R_AX = 0x005f;
+ M.x86.R_CX = 0x0003; /* eDP */
+ res = 1;
+ break;
+ case 0x5f70:
+ switch (M.x86.R_CH) {
+ case 0:
+ /* Get Mux */
+ M.x86.R_AX = 0x005f;
+ M.x86.R_CX = 0x0000;
+ res = 1;
+ break;
+ case 1:
+ /* Set Mux */
+ M.x86.R_AX = 0x005f;
+ M.x86.R_CX = 0x0000;
+ res = 1;
+ break;
+ case 2:
+ /* Get SG/Non-SG mode */
+ M.x86.R_AX = 0x005f;
+ M.x86.R_CX = 0x0000;
+ res = 1;
+ break;
+ default:
+ /* Interrupt was not handled */
+ debug("Unknown INT15 5f70 function: 0x%02x\n",
+ M.x86.R_CH);
+ break;
+ }
+ break;
+ case 0x5fac:
+ res = 1;
+ break;
+ default:
+ debug("Unknown INT15 function %04x!\n", M.x86.R_AX);
+ break;
+ }
+ return res;
+}
+
+int gma_func0_init(pci_dev_t dev, struct pci_controller *hose,
+ const void *blob, int node)
+{
+#ifdef CONFIG_VIDEO
+ ulong start;
+#endif
+ void *gtt_bar;
+ ulong base;
+ u32 reg32;
+ int ret;
+
+ /* IGD needs to be Bus Master */
+ reg32 = pci_read_config32(dev, PCI_COMMAND);
+ reg32 |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
+ pci_write_config32(dev, PCI_COMMAND, reg32);
+
+ /* Use write-combining for the graphics memory, 256MB */
+ base = pci_read_bar32(hose, dev, 2);
+ mtrr_add_request(MTRR_TYPE_WRCOMB, base, 256 << 20);
+ mtrr_commit(true);
+
+ gtt_bar = (void *)pci_read_bar32(pci_bus_to_hose(0), dev, 0);
+ debug("GT bar %p\n", gtt_bar);
+ ret = gma_pm_init_pre_vbios(gtt_bar);
+ if (ret)
+ return ret;
+
+#ifdef CONFIG_VIDEO
+ start = get_timer(0);
+ ret = pci_run_vga_bios(dev, int15_handler, false);
+ debug("BIOS ran in %lums\n", get_timer(start));
+#endif
+ /* Post VBIOS init */
+ ret = gma_pm_init_post_vbios(gtt_bar, blob, node);
+ if (ret)
+ return ret;
+
+ return 0;
+}
diff --git a/arch/x86/cpu/ivybridge/gma.h b/arch/x86/cpu/ivybridge/gma.h
new file mode 100644
index 0000000000..e7ec649b80
--- /dev/null
+++ b/arch/x86/cpu/ivybridge/gma.h
@@ -0,0 +1,156 @@
+/*
+ * From Coreboot file of the same name
+ *
+ * Copyright (C) 2012 Chromium OS Authors
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+/* mailbox 0: header */
+__packed struct opregion_header {
+ u8 signature[16];
+ u32 size;
+ u32 version;
+ u8 sbios_version[32];
+ u8 vbios_version[16];
+ u8 driver_version[16];
+ u32 mailboxes;
+ u8 reserved[164];
+};
+
+#define IGD_OPREGION_SIGNATURE "IntelGraphicsMem"
+#define IGD_OPREGION_VERSION 2
+
+#define IGD_MBOX1 (1 << 0)
+#define IGD_MBOX2 (1 << 1)
+#define IGD_MBOX3 (1 << 2)
+#define IGD_MBOX4 (1 << 3)
+#define IGD_MBOX5 (1 << 4)
+
+#define MAILBOXES_MOBILE (IGD_MBOX1 | IGD_MBOX2 | IGD_MBOX3 | \
+ IGD_MBOX4 | IGD_MBOX5)
+#define MAILBOXES_DESKTOP (IGD_MBOX2 | IGD_MBOX4)
+
+#define SBIOS_VERSION_SIZE 32
+
+/* mailbox 1: public acpi methods */
+__packed struct opregion_mailbox1 {
+ u32 drdy;
+ u32 csts;
+ u32 cevt;
+ u8 reserved1[20];
+ u32 didl[8];
+ u32 cpdl[8];
+ u32 cadl[8];
+ u32 nadl[8];
+ u32 aslp;
+ u32 tidx;
+ u32 chpd;
+ u32 clid;
+ u32 cdck;
+ u32 sxsw;
+ u32 evts;
+ u32 cnot;
+ u32 nrdy;
+ u8 reserved2[60];
+};
+
+/* mailbox 2: software sci interface */
+__packed struct opregion_mailbox2 {
+ u32 scic;
+ u32 parm;
+ u32 dslp;
+ u8 reserved[244];
+};
+
+/* mailbox 3: power conservation */
+__packed struct opregion_mailbox3 {
+ u32 ardy;
+ u32 aslc;
+ u32 tche;
+ u32 alsi;
+ u32 bclp;
+ u32 pfit;
+ u32 cblv;
+ u16 bclm[20];
+ u32 cpfm;
+ u32 epfm;
+ u8 plut[74];
+ u32 pfmb;
+ u32 ccdv;
+ u32 pcft;
+ u8 reserved[94];
+};
+
+#define IGD_BACKLIGHT_BRIGHTNESS 0xff
+#define IGD_INITIAL_BRIGHTNESS 0x64
+
+#define IGD_FIELD_VALID (1 << 31)
+#define IGD_WORD_FIELD_VALID (1 << 15)
+#define IGD_PFIT_STRETCH 6
+
+/* mailbox 4: vbt */
+__packed struct {
+ u8 gvd1[7168];
+} opregion_vbt_t;
+
+/* IGD OpRegion */
+__packed struct igd_opregion {
+ opregion_header_t header;
+ opregion_mailbox1_t mailbox1;
+ opregion_mailbox2_t mailbox2;
+ opregion_mailbox3_t mailbox3;
+ opregion_vbt_t vbt;
+};
+
+/* Intel Video BIOS (Option ROM) */
+__packed struct optionrom_header {
+ u16 signature;
+ u8 size;
+ u8 reserved[21];
+ u16 pcir_offset;
+ u16 vbt_offset;
+};
+
+#define OPROM_SIGNATURE 0xaa55
+
+__packed struct optionrom_pcir {
+ u32 signature;
+ u16 vendor;
+ u16 device;
+ u16 reserved1;
+ u16 length;
+ u8 revision;
+ u8 classcode[3];
+ u16 imagelength;
+ u16 coderevision;
+ u8 codetype;
+ u8 indicator;
+ u16 reserved2;
+};
+
+__packed struct optionrom_vbt {
+ u8 hdr_signature[20];
+ u16 hdr_version;
+ u16 hdr_size;
+ u16 hdr_vbt_size;
+ u8 hdr_vbt_checksum;
+ u8 hdr_reserved;
+ u32 hdr_vbt_datablock;
+ u32 hdr_aim[4];
+ u8 datahdr_signature[16];
+ u16 datahdr_version;
+ u16 datahdr_size;
+ u16 datahdr_datablocksize;
+ u8 coreblock_id;
+ u16 coreblock_size;
+ u16 coreblock_biossize;
+ u8 coreblock_biostype;
+ u8 coreblock_releasestatus;
+ u8 coreblock_hwsupported;
+ u8 coreblock_integratedhw;
+ u8 coreblock_biosbuild[4];
+ u8 coreblock_biossignon[155];
+};
+
+#define VBT_SIGNATURE 0x54425624
diff --git a/arch/x86/cpu/ivybridge/lpc.c b/arch/x86/cpu/ivybridge/lpc.c
new file mode 100644
index 0000000000..43fdd31428
--- /dev/null
+++ b/arch/x86/cpu/ivybridge/lpc.c
@@ -0,0 +1,569 @@
+/*
+ * 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 <rtc.h>
+#include <pci.h>
+#include <asm/acpi.h>
+#include <asm/interrupt.h>
+#include <asm/io.h>
+#include <asm/ioapic.h>
+#include <asm/pci.h>
+#include <asm/arch/pch.h>
+
+#define NMI_OFF 0
+
+#define ENABLE_ACPI_MODE_IN_COREBOOT 0
+#define TEST_SMM_FLASH_LOCKDOWN 0
+
+static int pch_enable_apic(pci_dev_t dev)
+{
+ u32 reg32;
+ int i;
+
+ /* Enable ACPI I/O and power management. Set SCI IRQ to IRQ9 */
+ pci_write_config8(dev, ACPI_CNTL, 0x80);
+
+ writel(0, IO_APIC_INDEX);
+ writel(1 << 25, IO_APIC_DATA);
+
+ /* affirm full set of redirection table entries ("write once") */
+ writel(1, IO_APIC_INDEX);
+ reg32 = readl(IO_APIC_DATA);
+ writel(1, IO_APIC_INDEX);
+ writel(reg32, IO_APIC_DATA);
+
+ writel(0, IO_APIC_INDEX);
+ reg32 = readl(IO_APIC_DATA);
+ debug("PCH APIC ID = %x\n", (reg32 >> 24) & 0x0f);
+ if (reg32 != (1 << 25)) {
+ printf("APIC Error - cannot write to registers\n");
+ return -EPERM;
+ }
+
+ debug("Dumping IOAPIC registers\n");
+ for (i = 0; i < 3; i++) {
+ writel(i, IO_APIC_INDEX);
+ debug(" reg 0x%04x:", i);
+ reg32 = readl(IO_APIC_DATA);
+ debug(" 0x%08x\n", reg32);
+ }
+
+ /* Select Boot Configuration register. */
+ writel(3, IO_APIC_INDEX);
+
+ /* Use Processor System Bus to deliver interrupts. */
+ writel(1, IO_APIC_DATA);
+
+ return 0;
+}
+
+static void pch_enable_serial_irqs(pci_dev_t dev)
+{
+ u32 value;
+
+ /* Set packet length and toggle silent mode bit for one frame. */
+ value = (1 << 7) | (1 << 6) | ((21 - 17) << 2) | (0 << 0);
+#ifdef CONFIG_SERIRQ_CONTINUOUS_MODE
+ pci_write_config8(dev, SERIRQ_CNTL, value);
+#else
+ pci_write_config8(dev, SERIRQ_CNTL, value | (1 << 6));
+#endif
+}
+
+static int pch_pirq_init(const void *blob, int node, pci_dev_t dev)
+{
+ uint8_t route[8], *ptr;
+
+ if (fdtdec_get_byte_array(blob, node, "intel,pirq-routing", route,
+ sizeof(route)))
+ return -EINVAL;
+ ptr = route;
+ pci_write_config8(dev, PIRQA_ROUT, *ptr++);
+ pci_write_config8(dev, PIRQB_ROUT, *ptr++);
+ pci_write_config8(dev, PIRQC_ROUT, *ptr++);
+ pci_write_config8(dev, PIRQD_ROUT, *ptr++);
+
+ pci_write_config8(dev, PIRQE_ROUT, *ptr++);
+ pci_write_config8(dev, PIRQF_ROUT, *ptr++);
+ pci_write_config8(dev, PIRQG_ROUT, *ptr++);
+ pci_write_config8(dev, PIRQH_ROUT, *ptr++);
+
+ /*
+ * TODO(sjg@chromium.org): U-Boot does not set up the interrupts
+ * here. It's unclear if it is needed
+ */
+ return 0;
+}
+
+static int pch_gpi_routing(const void *blob, int node, pci_dev_t dev)
+{
+ u8 route[16];
+ u32 reg;
+ int gpi;
+
+ if (fdtdec_get_byte_array(blob, node, "intel,gpi-routing", route,
+ sizeof(route)))
+ return -EINVAL;
+
+ for (reg = 0, gpi = 0; gpi < ARRAY_SIZE(route); gpi++)
+ reg |= route[gpi] << (gpi * 2);
+
+ pci_write_config32(dev, 0xb8, reg);
+
+ return 0;
+}
+
+static int pch_power_options(const void *blob, int node, pci_dev_t dev)
+{
+ u8 reg8;
+ u16 reg16, pmbase;
+ u32 reg32;
+ const char *state;
+ int pwr_on;
+ int nmi_option;
+ int ret;
+
+ /*
+ * Which state do we want to goto after g3 (power restored)?
+ * 0 == S0 Full On
+ * 1 == S5 Soft Off
+ *
+ * If the option is not existent (Laptops), use Kconfig setting.
+ * TODO(sjg@chromium.org): Make this configurable
+ */
+ pwr_on = MAINBOARD_POWER_ON;
+
+ reg16 = pci_read_config16(dev, GEN_PMCON_3);
+ reg16 &= 0xfffe;
+ switch (pwr_on) {
+ case MAINBOARD_POWER_OFF:
+ reg16 |= 1;
+ state = "off";
+ break;
+ case MAINBOARD_POWER_ON:
+ reg16 &= ~1;
+ state = "on";
+ break;
+ case MAINBOARD_POWER_KEEP:
+ reg16 &= ~1;
+ state = "state keep";
+ break;
+ default:
+ state = "undefined";
+ }
+
+ reg16 &= ~(3 << 4); /* SLP_S4# Assertion Stretch 4s */
+ reg16 |= (1 << 3); /* SLP_S4# Assertion Stretch Enable */
+
+ reg16 &= ~(1 << 10);
+ reg16 |= (1 << 11); /* SLP_S3# Min Assertion Width 50ms */
+
+ reg16 |= (1 << 12); /* Disable SLP stretch after SUS well */
+
+ pci_write_config16(dev, GEN_PMCON_3, reg16);
+ debug("Set power %s after power failure.\n", state);
+
+ /* Set up NMI on errors. */
+ reg8 = inb(0x61);
+ reg8 &= 0x0f; /* Higher Nibble must be 0 */
+ reg8 &= ~(1 << 3); /* IOCHK# NMI Enable */
+ reg8 |= (1 << 2); /* PCI SERR# Disable for now */
+ outb(reg8, 0x61);
+
+ reg8 = inb(0x70);
+ /* TODO(sjg@chromium.org): Make this configurable */
+ nmi_option = NMI_OFF;
+ if (nmi_option) {
+ debug("NMI sources enabled.\n");
+ reg8 &= ~(1 << 7); /* Set NMI. */
+ } else {
+ debug("NMI sources disabled.\n");
+ /* Can't mask NMI from PCI-E and NMI_NOW */
+ reg8 |= (1 << 7);
+ }
+ outb(reg8, 0x70);
+
+ /* Enable CPU_SLP# and Intel Speedstep, set SMI# rate down */
+ reg16 = pci_read_config16(dev, GEN_PMCON_1);
+ reg16 &= ~(3 << 0); /* SMI# rate 1 minute */
+ reg16 &= ~(1 << 10); /* Disable BIOS_PCI_EXP_EN for native PME */
+#if DEBUG_PERIODIC_SMIS
+ /* Set DEBUG_PERIODIC_SMIS in pch.h to debug using periodic SMIs */
+ reg16 |= (3 << 0); /* Periodic SMI every 8s */
+#endif
+ pci_write_config16(dev, GEN_PMCON_1, reg16);
+
+ /* Set the board's GPI routing. */
+ ret = pch_gpi_routing(blob, node, dev);
+ if (ret)
+ return ret;
+
+ pmbase = pci_read_config16(dev, 0x40) & 0xfffe;
+
+ writel(pmbase + GPE0_EN, fdtdec_get_int(blob, node,
+ "intel,gpe0-enable", 0));
+ writew(pmbase + ALT_GP_SMI_EN, fdtdec_get_int(blob, node,
+ "intel,alt-gp-smi-enable", 0));
+
+ /* Set up power management block and determine sleep mode */
+ reg32 = inl(pmbase + 0x04); /* PM1_CNT */
+ reg32 &= ~(7 << 10); /* SLP_TYP */
+ reg32 |= (1 << 0); /* SCI_EN */
+ outl(reg32, pmbase + 0x04);
+
+ /* Clear magic status bits to prevent unexpected wake */
+ setbits_le32(RCB_REG(0x3310), (1 << 4) | (1 << 5) | (1 << 0));
+ clrbits_le32(RCB_REG(0x3f02), 0xf);
+
+ return 0;
+}
+
+static void pch_rtc_init(pci_dev_t dev)
+{
+ int rtc_failed;
+ u8 reg8;
+
+ reg8 = pci_read_config8(dev, GEN_PMCON_3);
+ rtc_failed = reg8 & RTC_BATTERY_DEAD;
+ if (rtc_failed) {
+ reg8 &= ~RTC_BATTERY_DEAD;
+ pci_write_config8(dev, GEN_PMCON_3, reg8);
+ }
+ debug("rtc_failed = 0x%x\n", rtc_failed);
+
+#if CONFIG_HAVE_ACPI_RESUME
+ /* Avoid clearing pending interrupts and resetting the RTC control
+ * register in the resume path because the Linux kernel relies on
+ * this to know if it should restart the RTC timerqueue if the wake
+ * was due to the RTC alarm.
+ */
+ if (acpi_get_slp_type() == 3)
+ return;
+#endif
+ /* TODO: Handle power failure */
+ if (rtc_failed)
+ printf("RTC power failed\n");
+ rtc_init();
+}
+
+/* CougarPoint PCH Power Management init */
+static void cpt_pm_init(pci_dev_t dev)
+{
+ debug("CougarPoint PM init\n");
+ pci_write_config8(dev, 0xa9, 0x47);
+ setbits_le32(RCB_REG(0x2238), (1 << 6) | (1 << 0));
+
+ setbits_le32(RCB_REG(0x228c), 1 << 0);
+ setbits_le32(RCB_REG(0x1100), (1 << 13) | (1 << 14));
+ setbits_le32(RCB_REG(0x0900), 1 << 14);
+ writel(0xc0388400, RCB_REG(0x2304));
+ setbits_le32(RCB_REG(0x2314), (1 << 5) | (1 << 18));
+ setbits_le32(RCB_REG(0x2320), (1 << 15) | (1 << 1));
+ clrsetbits_le32(RCB_REG(0x3314), ~0x1f, 0xf);
+ writel(0x050f0000, RCB_REG(0x3318));
+ writel(0x04000000, RCB_REG(0x3324));
+ setbits_le32(RCB_REG(0x3340), 0xfffff);
+ setbits_le32(RCB_REG(0x3344), 1 << 1);
+
+ writel(0x0001c000, RCB_REG(0x3360));
+ writel(0x00061100, RCB_REG(0x3368));
+ writel(0x7f8fdfff, RCB_REG(0x3378));
+ writel(0x000003fc, RCB_REG(0x337c));
+ writel(0x00001000, RCB_REG(0x3388));
+ writel(0x0001c000, RCB_REG(0x3390));
+ writel(0x00000800, RCB_REG(0x33a0));
+ writel(0x00001000, RCB_REG(0x33b0));
+ writel(0x00093900, RCB_REG(0x33c0));
+ writel(0x24653002, RCB_REG(0x33cc));
+ writel(0x062108fe, RCB_REG(0x33d0));
+ clrsetbits_le32(RCB_REG(0x33d4), 0x0fff0fff, 0x00670060);
+ writel(0x01010000, RCB_REG(0x3a28));
+ writel(0x01010404, RCB_REG(0x3a2c));
+ writel(0x01041041, RCB_REG(0x3a80));
+ clrsetbits_le32(RCB_REG(0x3a84), 0x0000ffff, 0x00001001);
+ setbits_le32(RCB_REG(0x3a84), 1 << 24); /* SATA 2/3 disabled */
+ setbits_le32(RCB_REG(0x3a88), 1 << 0); /* SATA 4/5 disabled */
+ writel(0x00000001, RCB_REG(0x3a6c));
+ clrsetbits_le32(RCB_REG(0x2344), ~0x00ffff00, 0xff00000c);
+ clrsetbits_le32(RCB_REG(0x80c), 0xff << 20, 0x11 << 20);
+ writel(0, RCB_REG(0x33c8));
+ setbits_le32(RCB_REG(0x21b0), 0xf);
+}
+
+/* PantherPoint PCH Power Management init */
+static void ppt_pm_init(pci_dev_t dev)
+{
+ debug("PantherPoint PM init\n");
+ pci_write_config8(dev, 0xa9, 0x47);
+ setbits_le32(RCB_REG(0x2238), 1 << 0);
+ setbits_le32(RCB_REG(0x228c), 1 << 0);
+ setbits_le16(RCB_REG(0x1100), (1 << 13) | (1 << 14));
+ setbits_le16(RCB_REG(0x0900), 1 << 14);
+ writel(0xc03b8400, RCB_REG(0x2304));
+ setbits_le32(RCB_REG(0x2314), (1 << 5) | (1 << 18));
+ setbits_le32(RCB_REG(0x2320), (1 << 15) | (1 << 1));
+ clrsetbits_le32(RCB_REG(0x3314), 0x1f, 0xf);
+ writel(0x054f0000, RCB_REG(0x3318));
+ writel(0x04000000, RCB_REG(0x3324));
+ setbits_le32(RCB_REG(0x3340), 0xfffff);
+ setbits_le32(RCB_REG(0x3344), (1 << 1) | (1 << 0));
+ writel(0x0001c000, RCB_REG(0x3360));
+ writel(0x00061100, RCB_REG(0x3368));
+ writel(0x7f8fdfff, RCB_REG(0x3378));
+ writel(0x000003fd, RCB_REG(0x337c));
+ writel(0x00001000, RCB_REG(0x3388));
+ writel(0x0001c000, RCB_REG(0x3390));
+ writel(0x00000800, RCB_REG(0x33a0));
+ writel(0x00001000, RCB_REG(0x33b0));
+ writel(0x00093900, RCB_REG(0x33c0));
+ writel(0x24653002, RCB_REG(0x33cc));
+ writel(0x067388fe, RCB_REG(0x33d0));
+ clrsetbits_le32(RCB_REG(0x33d4), 0x0fff0fff, 0x00670060);
+ writel(0x01010000, RCB_REG(0x3a28));
+ writel(0x01010404, RCB_REG(0x3a2c));
+ writel(0x01040000, RCB_REG(0x3a80));
+ clrsetbits_le32(RCB_REG(0x3a84), 0x0000ffff, 0x00001001);
+ /* SATA 2/3 disabled */
+ setbits_le32(RCB_REG(0x3a84), 1 << 24);
+ /* SATA 4/5 disabled */
+ setbits_le32(RCB_REG(0x3a88), 1 << 0);
+ writel(0x00000001, RCB_REG(0x3a6c));
+ clrsetbits_le32(RCB_REG(0x2344), 0xff0000ff, 0xff00000c);
+ clrsetbits_le32(RCB_REG(0x80c), 0xff << 20, 0x11 << 20);
+ setbits_le32(RCB_REG(0x33a4), (1 << 0));
+ writel(0, RCB_REG(0x33c8));
+ setbits_le32(RCB_REG(0x21b0), 0xf);
+}
+
+static void enable_hpet(void)
+{
+ /* Move HPET to default address 0xfed00000 and enable it */
+ clrsetbits_le32(RCB_REG(HPTC), 3 << 0, 1 << 7);
+}
+
+static void enable_clock_gating(pci_dev_t dev)
+{
+ u32 reg32;
+ u16 reg16;
+
+ setbits_le32(RCB_REG(0x2234), 0xf);
+
+ reg16 = pci_read_config16(dev, GEN_PMCON_1);
+ reg16 |= (1 << 2) | (1 << 11);
+ pci_write_config16(dev, GEN_PMCON_1, reg16);
+
+ pch_iobp_update(0xEB007F07, ~0UL, (1 << 31));
+ pch_iobp_update(0xEB004000, ~0UL, (1 << 7));
+ pch_iobp_update(0xEC007F07, ~0UL, (1 << 31));
+ pch_iobp_update(0xEC004000, ~0UL, (1 << 7));
+
+ reg32 = readl(RCB_REG(CG));
+ reg32 |= (1 << 31);
+ reg32 |= (1 << 29) | (1 << 28);
+ reg32 |= (1 << 27) | (1 << 26) | (1 << 25) | (1 << 24);
+ reg32 |= (1 << 16);
+ reg32 |= (1 << 17);
+ reg32 |= (1 << 18);
+ reg32 |= (1 << 22);
+ reg32 |= (1 << 23);
+ reg32 &= ~(1 << 20);
+ reg32 |= (1 << 19);
+ reg32 |= (1 << 0);
+ reg32 |= (0xf << 1);
+ writel(reg32, RCB_REG(CG));
+
+ setbits_le32(RCB_REG(0x38c0), 0x7);
+ setbits_le32(RCB_REG(0x36d4), 0x6680c004);
+ setbits_le32(RCB_REG(0x3564), 0x3);
+}
+
+#if CONFIG_HAVE_SMI_HANDLER
+static void pch_lock_smm(pci_dev_t dev)
+{
+#if TEST_SMM_FLASH_LOCKDOWN
+ u8 reg8;
+#endif
+
+ if (acpi_slp_type != 3) {
+#if ENABLE_ACPI_MODE_IN_COREBOOT
+ debug("Enabling ACPI via APMC:\n");
+ outb(0xe1, 0xb2); /* Enable ACPI mode */
+ debug("done.\n");
+#else
+ debug("Disabling ACPI via APMC:\n");
+ outb(0x1e, 0xb2); /* Disable ACPI mode */
+ debug("done.\n");
+#endif
+ }
+
+ /* Don't allow evil boot loaders, kernels, or
+ * userspace applications to deceive us:
+ */
+ smm_lock();
+
+#if TEST_SMM_FLASH_LOCKDOWN
+ /* Now try this: */
+ debug("Locking BIOS to RO... ");
+ reg8 = pci_read_config8(dev, 0xdc); /* BIOS_CNTL */
+ debug(" BLE: %s; BWE: %s\n", (reg8 & 2) ? "on" : "off",
+ (reg8 & 1) ? "rw" : "ro");
+ reg8 &= ~(1 << 0); /* clear BIOSWE */
+ pci_write_config8(dev, 0xdc, reg8);
+ reg8 |= (1 << 1); /* set BLE */
+ pci_write_config8(dev, 0xdc, reg8);
+ debug("ok.\n");
+ reg8 = pci_read_config8(dev, 0xdc); /* BIOS_CNTL */
+ debug(" BLE: %s; BWE: %s\n", (reg8 & 2) ? "on" : "off",
+ (reg8 & 1) ? "rw" : "ro");
+
+ debug("Writing:\n");
+ writeb(0, 0xfff00000);
+ debug("Testing:\n");
+ reg8 |= (1 << 0); /* set BIOSWE */
+ pci_write_config8(dev, 0xdc, reg8);
+
+ reg8 = pci_read_config8(dev, 0xdc); /* BIOS_CNTL */
+ debug(" BLE: %s; BWE: %s\n", (reg8 & 2) ? "on" : "off",
+ (reg8 & 1) ? "rw" : "ro");
+ debug("Done.\n");
+#endif
+}
+#endif
+
+static void pch_disable_smm_only_flashing(pci_dev_t dev)
+{
+ u8 reg8;
+
+ debug("Enabling BIOS updates outside of SMM... ");
+ reg8 = pci_read_config8(dev, 0xdc); /* BIOS_CNTL */
+ reg8 &= ~(1 << 5);
+ pci_write_config8(dev, 0xdc, reg8);
+}
+
+static void pch_fixups(pci_dev_t dev)
+{
+ u8 gen_pmcon_2;
+
+ /* Indicate DRAM init done for MRC S3 to know it can resume */
+ gen_pmcon_2 = pci_read_config8(dev, GEN_PMCON_2);
+ gen_pmcon_2 |= (1 << 7);
+ pci_write_config8(dev, GEN_PMCON_2, gen_pmcon_2);
+
+ /* Enable DMI ASPM in the PCH */
+ clrbits_le32(RCB_REG(0x2304), 1 << 10);
+ setbits_le32(RCB_REG(0x21a4), (1 << 11) | (1 << 10));
+ setbits_le32(RCB_REG(0x21a8), 0x3);
+}
+
+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, "intel,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;
+}
+
+int lpc_init(struct pci_controller *hose, pci_dev_t dev)
+{
+ const void *blob = gd->fdt_blob;
+ int node;
+
+ debug("pch: lpc_init\n");
+ pci_write_bar32(hose, dev, 0, 0);
+ pci_write_bar32(hose, dev, 1, 0xff800000);
+ pci_write_bar32(hose, dev, 2, 0xfec00000);
+ pci_write_bar32(hose, dev, 3, 0x800);
+ pci_write_bar32(hose, dev, 4, 0x900);
+
+ node = fdtdec_next_compatible(blob, 0, COMPAT_INTEL_LPC);
+ if (node < 0)
+ return -ENOENT;
+
+ /* Set the value for PCI command register. */
+ pci_write_config16(dev, PCI_COMMAND, 0x000f);
+
+ /* IO APIC initialization. */
+ pch_enable_apic(dev);
+
+ pch_enable_serial_irqs(dev);
+
+ /* Setup the PIRQ. */
+ pch_pirq_init(blob, node, dev);
+
+ /* Setup power options. */
+ pch_power_options(blob, node, dev);
+
+ /* Initialize power management */
+ switch (pch_silicon_type()) {
+ case PCH_TYPE_CPT: /* CougarPoint */
+ cpt_pm_init(dev);
+ break;
+ case PCH_TYPE_PPT: /* PantherPoint */
+ ppt_pm_init(dev);
+ break;
+ default:
+ printf("Unknown Chipset: %#02x.%dx\n", PCI_DEV(dev),
+ PCI_FUNC(dev));
+ return -ENOSYS;
+ }
+
+ /* Initialize the real time clock. */
+ pch_rtc_init(dev);
+
+ /* Initialize the High Precision Event Timers, if present. */
+ enable_hpet();
+
+ /* Initialize Clock Gating */
+ enable_clock_gating(dev);
+
+ pch_disable_smm_only_flashing(dev);
+
+#if CONFIG_HAVE_SMI_HANDLER
+ pch_lock_smm(dev);
+#endif
+
+ pch_fixups(dev);
+
+ return 0;
+}
+
+void lpc_enable(pci_dev_t dev)
+{
+ /* Enable PCH Display Port */
+ writew(0x0010, RCB_REG(DISPBDF));
+ setbits_le32(RCB_REG(FD2), PCH_ENABLE_DBDF);
+}
diff --git a/arch/x86/cpu/ivybridge/me_status.c b/arch/x86/cpu/ivybridge/me_status.c
new file mode 100644
index 0000000000..15cf69f40e
--- /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 0000000000..2440a97c48
--- /dev/null
+++ b/arch/x86/cpu/ivybridge/microcode_intel.c
@@ -0,0 +1,166 @@
+/*
+ * 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/msr-index.h>
+#include <asm/processor.h>
+#include <asm/arch/microcode.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->data += UCODE_HEADER_LEN;
+ update->size -= UCODE_HEADER_LEN;
+
+ 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,
+ "intel,loader-revision", 0);
+ update->processor_flags = fdtdec_get_int(blob, node,
+ "intel,processor-flags", 0);
+
+ return 0;
+}
+
+static inline 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 %2, %%ecx\n"
+ "wrmsr\n"
+ "movl $0x01, %%eax\n"
+ "cpuid\n"
+ "movl %2, %%ecx\n"
+ "rdmsr\n"
+ : /* outputs */
+ "=a" (low), "=d" (high)
+ : /* inputs */
+ "i" (MSR_IA32_UCODE_REV)
+ : /* 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(MSR_IA32_UCODE_REV, 0, 0);
+ result = cpuid(1);
+ rdmsr(MSR_IA32_UCODE_REV, 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 skipped;
+ int count;
+ int node;
+ int ret;
+ int rev;
+
+ microcode_read_cpu(&cpu);
+ node = 0;
+ count = 0;
+ skipped = 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 : skipped ? -EEXIST : -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: Skipping non-matching update, sig=%x, pf=%x\n",
+ __func__, update.processor_signature,
+ update.processor_flags);
+ skipped++;
+ continue;
+ }
+ wrmsr(MSR_IA32_UCODE_WRITE, (ulong)update.data, 0);
+ rev = microcode_read_rev();
+ debug("microcode: updated to revision 0x%x date=%04x-%02x-%02x\n",
+ rev, update.date_code & 0xffff,
+ (update.date_code >> 24) & 0xff,
+ (update.date_code >> 16) & 0xff);
+ if (update.update_revision != rev) {
+ printf("Microcode update failed\n");
+ return -EFAULT;
+ }
+ count++;
+ } while (1);
+}
diff --git a/arch/x86/cpu/ivybridge/model_206ax.c b/arch/x86/cpu/ivybridge/model_206ax.c
new file mode 100644
index 0000000000..11dc625da9
--- /dev/null
+++ b/arch/x86/cpu/ivybridge/model_206ax.c
@@ -0,0 +1,514 @@
+/*
+ * From Coreboot file of same name
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ * Copyright (C) 2011 The Chromium Authors
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <malloc.h>
+#include <asm/acpi.h>
+#include <asm/cpu.h>
+#include <asm/lapic.h>
+#include <asm/lapic_def.h>
+#include <asm/msr.h>
+#include <asm/mtrr.h>
+#include <asm/processor.h>
+#include <asm/speedstep.h>
+#include <asm/turbo.h>
+#include <asm/arch/model_206ax.h>
+
+static void enable_vmx(void)
+{
+ struct cpuid_result regs;
+#ifdef CONFIG_ENABLE_VMX
+ int enable = true;
+#else
+ int enable = false;
+#endif
+ msr_t msr;
+
+ regs = cpuid(1);
+ /* Check that the VMX is supported before reading or writing the MSR. */
+ if (!((regs.ecx & CPUID_VMX) || (regs.ecx & CPUID_SMX)))
+ return;
+
+ msr = msr_read(MSR_IA32_FEATURE_CONTROL);
+
+ if (msr.lo & (1 << 0)) {
+ debug("VMX is locked, so %s will do nothing\n", __func__);
+ /* VMX locked. If we set it again we get an illegal
+ * instruction
+ */
+ return;
+ }
+
+ /* The IA32_FEATURE_CONTROL MSR may initialize with random values.
+ * It must be cleared regardless of VMX config setting.
+ */
+ msr.hi = 0;
+ msr.lo = 0;
+
+ debug("%s VMX\n", enable ? "Enabling" : "Disabling");
+
+ /*
+ * Even though the Intel manual says you must set the lock bit in
+ * addition to the VMX bit in order for VMX to work, it is incorrect.
+ * Thus we leave it unlocked for the OS to manage things itself.
+ * This is good for a few reasons:
+ * - No need to reflash the bios just to toggle the lock bit.
+ * - The VMX bits really really should match each other across cores,
+ * so hard locking it on one while another has the opposite setting
+ * can easily lead to crashes as code using VMX migrates between
+ * them.
+ * - Vendors that want to "upsell" from a bios that disables+locks to
+ * one that doesn't is sleazy.
+ * By leaving this to the OS (e.g. Linux), people can do exactly what
+ * they want on the fly, and do it correctly (e.g. across multiple
+ * cores).
+ */
+ if (enable) {
+ msr.lo |= (1 << 2);
+ if (regs.ecx & CPUID_SMX)
+ msr.lo |= (1 << 1);
+ }
+
+ msr_write(MSR_IA32_FEATURE_CONTROL, msr);
+}
+
+/* Convert time in seconds to POWER_LIMIT_1_TIME MSR value */
+static const u8 power_limit_time_sec_to_msr[] = {
+ [0] = 0x00,
+ [1] = 0x0a,
+ [2] = 0x0b,
+ [3] = 0x4b,
+ [4] = 0x0c,
+ [5] = 0x2c,
+ [6] = 0x4c,
+ [7] = 0x6c,
+ [8] = 0x0d,
+ [10] = 0x2d,
+ [12] = 0x4d,
+ [14] = 0x6d,
+ [16] = 0x0e,
+ [20] = 0x2e,
+ [24] = 0x4e,
+ [28] = 0x6e,
+ [32] = 0x0f,
+ [40] = 0x2f,
+ [48] = 0x4f,
+ [56] = 0x6f,
+ [64] = 0x10,
+ [80] = 0x30,
+ [96] = 0x50,
+ [112] = 0x70,
+ [128] = 0x11,
+};
+
+/* Convert POWER_LIMIT_1_TIME MSR value to seconds */
+static const u8 power_limit_time_msr_to_sec[] = {
+ [0x00] = 0,
+ [0x0a] = 1,
+ [0x0b] = 2,
+ [0x4b] = 3,
+ [0x0c] = 4,
+ [0x2c] = 5,
+ [0x4c] = 6,
+ [0x6c] = 7,
+ [0x0d] = 8,
+ [0x2d] = 10,
+ [0x4d] = 12,
+ [0x6d] = 14,
+ [0x0e] = 16,
+ [0x2e] = 20,
+ [0x4e] = 24,
+ [0x6e] = 28,
+ [0x0f] = 32,
+ [0x2f] = 40,
+ [0x4f] = 48,
+ [0x6f] = 56,
+ [0x10] = 64,
+ [0x30] = 80,
+ [0x50] = 96,
+ [0x70] = 112,
+ [0x11] = 128,
+};
+
+int cpu_config_tdp_levels(void)
+{
+ struct cpuid_result result;
+ msr_t platform_info;
+
+ /* Minimum CPU revision */
+ result = cpuid(1);
+ if (result.eax < IVB_CONFIG_TDP_MIN_CPUID)
+ return 0;
+
+ /* Bits 34:33 indicate how many levels supported */
+ platform_info = msr_read(MSR_PLATFORM_INFO);
+ return (platform_info.hi >> 1) & 3;
+}
+
+/*
+ * Configure processor power limits if possible
+ * This must be done AFTER set of BIOS_RESET_CPL
+ */
+void set_power_limits(u8 power_limit_1_time)
+{
+ msr_t msr = msr_read(MSR_PLATFORM_INFO);
+ msr_t limit;
+ unsigned power_unit;
+ unsigned tdp, min_power, max_power, max_time;
+ u8 power_limit_1_val;
+
+ if (power_limit_1_time > ARRAY_SIZE(power_limit_time_sec_to_msr))
+ return;
+
+ if (!(msr.lo & PLATFORM_INFO_SET_TDP))
+ return;
+
+ /* Get units */
+ msr = msr_read(MSR_PKG_POWER_SKU_UNIT);
+ power_unit = 2 << ((msr.lo & 0xf) - 1);
+
+ /* Get power defaults for this SKU */
+ msr = msr_read(MSR_PKG_POWER_SKU);
+ tdp = msr.lo & 0x7fff;
+ min_power = (msr.lo >> 16) & 0x7fff;
+ max_power = msr.hi & 0x7fff;
+ max_time = (msr.hi >> 16) & 0x7f;
+
+ debug("CPU TDP: %u Watts\n", tdp / power_unit);
+
+ if (power_limit_time_msr_to_sec[max_time] > power_limit_1_time)
+ power_limit_1_time = power_limit_time_msr_to_sec[max_time];
+
+ if (min_power > 0 && tdp < min_power)
+ tdp = min_power;
+
+ if (max_power > 0 && tdp > max_power)
+ tdp = max_power;
+
+ power_limit_1_val = power_limit_time_sec_to_msr[power_limit_1_time];
+
+ /* Set long term power limit to TDP */
+ limit.lo = 0;
+ limit.lo |= tdp & PKG_POWER_LIMIT_MASK;
+ limit.lo |= PKG_POWER_LIMIT_EN;
+ limit.lo |= (power_limit_1_val & PKG_POWER_LIMIT_TIME_MASK) <<
+ PKG_POWER_LIMIT_TIME_SHIFT;
+
+ /* Set short term power limit to 1.25 * TDP */
+ limit.hi = 0;
+ limit.hi |= ((tdp * 125) / 100) & PKG_POWER_LIMIT_MASK;
+ limit.hi |= PKG_POWER_LIMIT_EN;
+ /* Power limit 2 time is only programmable on SNB EP/EX */
+
+ msr_write(MSR_PKG_POWER_LIMIT, limit);
+
+ /* Use nominal TDP values for CPUs with configurable TDP */
+ if (cpu_config_tdp_levels()) {
+ msr = msr_read(MSR_CONFIG_TDP_NOMINAL);
+ limit.hi = 0;
+ limit.lo = msr.lo & 0xff;
+ msr_write(MSR_TURBO_ACTIVATION_RATIO, limit);
+ }
+}
+
+static void configure_c_states(void)
+{
+ struct cpuid_result result;
+ msr_t msr;
+
+ msr = msr_read(MSR_PMG_CST_CONFIG_CTL);
+ msr.lo |= (1 << 28); /* C1 Auto Undemotion Enable */
+ msr.lo |= (1 << 27); /* C3 Auto Undemotion Enable */
+ msr.lo |= (1 << 26); /* C1 Auto Demotion Enable */
+ msr.lo |= (1 << 25); /* C3 Auto Demotion Enable */
+ msr.lo &= ~(1 << 10); /* Disable IO MWAIT redirection */
+ msr.lo |= 7; /* No package C-state limit */
+ msr_write(MSR_PMG_CST_CONFIG_CTL, msr);
+
+ msr = msr_read(MSR_PMG_IO_CAPTURE_ADR);
+ msr.lo &= ~0x7ffff;
+ msr.lo |= (PMB0_BASE + 4); /* LVL_2 base address */
+ msr.lo |= (2 << 16); /* CST Range: C7 is max C-state */
+ msr_write(MSR_PMG_IO_CAPTURE_ADR, msr);
+
+ msr = msr_read(MSR_MISC_PWR_MGMT);
+ msr.lo &= ~(1 << 0); /* Enable P-state HW_ALL coordination */
+ msr_write(MSR_MISC_PWR_MGMT, msr);
+
+ msr = msr_read(MSR_POWER_CTL);
+ msr.lo |= (1 << 18); /* Enable Energy Perf Bias MSR 0x1b0 */
+ msr.lo |= (1 << 1); /* C1E Enable */
+ msr.lo |= (1 << 0); /* Bi-directional PROCHOT# */
+ msr_write(MSR_POWER_CTL, msr);
+
+ /* C3 Interrupt Response Time Limit */
+ msr.hi = 0;
+ msr.lo = IRTL_VALID | IRTL_1024_NS | 0x50;
+ msr_write(MSR_PKGC3_IRTL, msr);
+
+ /* C6 Interrupt Response Time Limit */
+ msr.hi = 0;
+ msr.lo = IRTL_VALID | IRTL_1024_NS | 0x68;
+ msr_write(MSR_PKGC6_IRTL, msr);
+
+ /* C7 Interrupt Response Time Limit */
+ msr.hi = 0;
+ msr.lo = IRTL_VALID | IRTL_1024_NS | 0x6D;
+ msr_write(MSR_PKGC7_IRTL, msr);
+
+ /* Primary Plane Current Limit */
+ msr = msr_read(MSR_PP0_CURRENT_CONFIG);
+ msr.lo &= ~0x1fff;
+ msr.lo |= PP0_CURRENT_LIMIT;
+ msr_write(MSR_PP0_CURRENT_CONFIG, msr);
+
+ /* Secondary Plane Current Limit */
+ msr = msr_read(MSR_PP1_CURRENT_CONFIG);
+ msr.lo &= ~0x1fff;
+ result = cpuid(1);
+ if (result.eax >= 0x30600)
+ msr.lo |= PP1_CURRENT_LIMIT_IVB;
+ else
+ msr.lo |= PP1_CURRENT_LIMIT_SNB;
+ msr_write(MSR_PP1_CURRENT_CONFIG, msr);
+}
+
+static int configure_thermal_target(void)
+{
+ int tcc_offset;
+ msr_t msr;
+ int node;
+
+ /* Find pointer to CPU configuration */
+ node = fdtdec_next_compatible(gd->fdt_blob, 0,
+ COMPAT_INTEL_MODEL_206AX);
+ if (node < 0)
+ return -ENOENT;
+ tcc_offset = fdtdec_get_int(gd->fdt_blob, node, "tcc-offset", 0);
+
+ /* Set TCC activaiton offset if supported */
+ msr = msr_read(MSR_PLATFORM_INFO);
+ if ((msr.lo & (1 << 30)) && tcc_offset) {
+ msr = msr_read(MSR_TEMPERATURE_TARGET);
+ msr.lo &= ~(0xf << 24); /* Bits 27:24 */
+ msr.lo |= (tcc_offset & 0xf) << 24;
+ msr_write(MSR_TEMPERATURE_TARGET, msr);
+ }
+
+ return 0;
+}
+
+static void configure_misc(void)
+{
+ msr_t msr;
+
+ msr = msr_read(IA32_MISC_ENABLE);
+ msr.lo |= (1 << 0); /* Fast String enable */
+ msr.lo |= (1 << 3); /* TM1/TM2/EMTTM enable */
+ msr.lo |= (1 << 16); /* Enhanced SpeedStep Enable */
+ msr_write(IA32_MISC_ENABLE, msr);
+
+ /* Disable Thermal interrupts */
+ msr.lo = 0;
+ msr.hi = 0;
+ msr_write(IA32_THERM_INTERRUPT, msr);
+
+ /* Enable package critical interrupt only */
+ msr.lo = 1 << 4;
+ msr.hi = 0;
+ msr_write(IA32_PACKAGE_THERM_INTERRUPT, msr);
+}
+
+static void enable_lapic_tpr(void)
+{
+ msr_t msr;
+
+ msr = msr_read(MSR_PIC_MSG_CONTROL);
+ msr.lo &= ~(1 << 10); /* Enable APIC TPR updates */
+ msr_write(MSR_PIC_MSG_CONTROL, msr);
+}
+
+static void configure_dca_cap(void)
+{
+ struct cpuid_result cpuid_regs;
+ msr_t msr;
+
+ /* Check feature flag in CPUID.(EAX=1):ECX[18]==1 */
+ cpuid_regs = cpuid(1);
+ if (cpuid_regs.ecx & (1 << 18)) {
+ msr = msr_read(IA32_PLATFORM_DCA_CAP);
+ msr.lo |= 1;
+ msr_write(IA32_PLATFORM_DCA_CAP, msr);
+ }
+}
+
+static void set_max_ratio(void)
+{
+ msr_t msr, perf_ctl;
+
+ perf_ctl.hi = 0;
+
+ /* Check for configurable TDP option */
+ if (cpu_config_tdp_levels()) {
+ /* Set to nominal TDP ratio */
+ msr = msr_read(MSR_CONFIG_TDP_NOMINAL);
+ perf_ctl.lo = (msr.lo & 0xff) << 8;
+ } else {
+ /* Platform Info bits 15:8 give max ratio */
+ msr = msr_read(MSR_PLATFORM_INFO);
+ perf_ctl.lo = msr.lo & 0xff00;
+ }
+ msr_write(IA32_PERF_CTL, perf_ctl);
+
+ debug("model_x06ax: frequency set to %d\n",
+ ((perf_ctl.lo >> 8) & 0xff) * SANDYBRIDGE_BCLK);
+}
+
+static void set_energy_perf_bias(u8 policy)
+{
+ msr_t msr;
+
+ /* Energy Policy is bits 3:0 */
+ msr = msr_read(IA32_ENERGY_PERFORMANCE_BIAS);
+ msr.lo &= ~0xf;
+ msr.lo |= policy & 0xf;
+ msr_write(IA32_ENERGY_PERFORMANCE_BIAS, msr);
+
+ debug("model_x06ax: energy policy set to %u\n", policy);
+}
+
+static void configure_mca(void)
+{
+ msr_t msr;
+ int i;
+
+ msr.lo = 0;
+ msr.hi = 0;
+ /* This should only be done on a cold boot */
+ for (i = 0; i < 7; i++)
+ msr_write(IA32_MC0_STATUS + (i * 4), msr);
+}
+
+#if CONFIG_USBDEBUG
+static unsigned ehci_debug_addr;
+#endif
+
+/*
+ * Initialize any extra cores/threads in this package.
+ */
+static int intel_cores_init(struct x86_cpu_priv *cpu)
+{
+ struct cpuid_result result;
+ unsigned threads_per_package, threads_per_core, i;
+
+ /* Logical processors (threads) per core */
+ result = cpuid_ext(0xb, 0);
+ threads_per_core = result.ebx & 0xffff;
+
+ /* Logical processors (threads) per package */
+ result = cpuid_ext(0xb, 1);
+ threads_per_package = result.ebx & 0xffff;
+
+ debug("CPU: %u has %u cores, %u threads per core\n",
+ cpu->apic_id, threads_per_package / threads_per_core,
+ threads_per_core);
+
+ for (i = 1; i < threads_per_package; ++i) {
+ struct x86_cpu_priv *new_cpu;
+
+ new_cpu = calloc(1, sizeof(*new_cpu));
+ if (!new_cpu)
+ return -ENOMEM;
+
+ new_cpu->apic_id = cpu->apic_id + i;
+
+ /* Update APIC ID if no hyperthreading */
+ if (threads_per_core == 1)
+ new_cpu->apic_id <<= 1;
+
+ debug("CPU: %u has core %u\n", cpu->apic_id, new_cpu->apic_id);
+
+#if CONFIG_SMP && CONFIG_MAX_CPUS > 1
+ /* Start the new cpu */
+ if (!start_cpu(new_cpu)) {
+ /* Record the error in cpu? */
+ printk(BIOS_ERR, "CPU %u would not start!\n",
+ new_cpu->apic_id);
+ new_cpu->start_err = 1;
+ }
+#endif
+ }
+
+ return 0;
+}
+
+int model_206ax_init(struct x86_cpu_priv *cpu)
+{
+ int ret;
+
+ /* Clear out pending MCEs */
+ configure_mca();
+
+#if CONFIG_USBDEBUG
+ /* Is this caution really needed? */
+ if (!ehci_debug_addr)
+ ehci_debug_addr = get_ehci_debug();
+ set_ehci_debug(0);
+#endif
+
+ /* Setup MTRRs based on physical address size */
+#if 0 /* TODO: Implement this */
+ struct cpuid_result cpuid_regs;
+
+ cpuid_regs = cpuid(0x80000008);
+ x86_setup_fixed_mtrrs();
+ x86_setup_var_mtrrs(cpuid_regs.eax & 0xff, 2);
+ x86_mtrr_check();
+#endif
+
+#if CONFIG_USBDEBUG
+ set_ehci_debug(ehci_debug_addr);
+#endif
+
+ /* Enable the local cpu apics */
+ enable_lapic_tpr();
+ lapic_setup();
+
+ /* Enable virtualization if enabled in CMOS */
+ enable_vmx();
+
+ /* Configure C States */
+ configure_c_states();
+
+ /* Configure Enhanced SpeedStep and Thermal Sensors */
+ configure_misc();
+
+ /* Thermal throttle activation offset */
+ ret = configure_thermal_target();
+ if (ret)
+ return ret;
+
+ /* Enable Direct Cache Access */
+ configure_dca_cap();
+
+ /* Set energy policy */
+ set_energy_perf_bias(ENERGY_POLICY_NORMAL);
+
+ /* Set Max Ratio */
+ set_max_ratio();
+
+ /* Enable Turbo */
+ turbo_enable();
+
+ /* Start up extra cores */
+ intel_cores_init(cpu);
+
+ return 0;
+}
diff --git a/arch/x86/cpu/ivybridge/northbridge.c b/arch/x86/cpu/ivybridge/northbridge.c
new file mode 100644
index 0000000000..c50b5ded83
--- /dev/null
+++ b/arch/x86/cpu/ivybridge/northbridge.c
@@ -0,0 +1,188 @@
+/*
+ * From Coreboot northbridge/intel/sandybridge/northbridge.c
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ * Copyright (C) 2011 The Chromium Authors
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <common.h>
+#include <asm/msr.h>
+#include <asm/acpi.h>
+#include <asm/cpu.h>
+#include <asm/io.h>
+#include <asm/pci.h>
+#include <asm/processor.h>
+#include <asm/arch/pch.h>
+#include <asm/arch/model_206ax.h>
+#include <asm/arch/sandybridge.h>
+
+static int bridge_revision_id = -1;
+
+int bridge_silicon_revision(void)
+{
+ if (bridge_revision_id < 0) {
+ struct cpuid_result result;
+ uint8_t stepping, bridge_id;
+ pci_dev_t dev;
+
+ result = cpuid(1);
+ stepping = result.eax & 0xf;
+ dev = PCI_BDF(0, 0, 0);
+ bridge_id = pci_read_config16(dev, PCI_DEVICE_ID) & 0xf0;
+ bridge_revision_id = bridge_id | stepping;
+ }
+
+ return bridge_revision_id;
+}
+
+/*
+ * Reserve everything between A segment and 1MB:
+ *
+ * 0xa0000 - 0xbffff: legacy VGA
+ * 0xc0000 - 0xcffff: VGA OPROM (needed by kernel)
+ * 0xe0000 - 0xfffff: SeaBIOS, if used, otherwise DMI
+ */
+static const int legacy_hole_base_k = 0xa0000 / 1024;
+static const int legacy_hole_size_k = 384;
+
+static int get_pcie_bar(u32 *base, u32 *len)
+{
+ pci_dev_t dev = PCI_BDF(0, 0, 0);
+ u32 pciexbar_reg;
+
+ *base = 0;
+ *len = 0;
+
+ pciexbar_reg = pci_read_config32(dev, PCIEXBAR);
+
+ if (!(pciexbar_reg & (1 << 0)))
+ return 0;
+
+ switch ((pciexbar_reg >> 1) & 3) {
+ case 0: /* 256MB */
+ *base = pciexbar_reg & ((1 << 31) | (1 << 30) | (1 << 29) |
+ (1 << 28));
+ *len = 256 * 1024 * 1024;
+ return 1;
+ case 1: /* 128M */
+ *base = pciexbar_reg & ((1 << 31) | (1 << 30) | (1 << 29) |
+ (1 << 28) | (1 << 27));
+ *len = 128 * 1024 * 1024;
+ return 1;
+ case 2: /* 64M */
+ *base = pciexbar_reg & ((1 << 31) | (1 << 30) | (1 << 29) |
+ (1 << 28) | (1 << 27) | (1 << 26));
+ *len = 64 * 1024 * 1024;
+ return 1;
+ }
+
+ return 0;
+}
+
+static void add_fixed_resources(pci_dev_t dev, int index)
+{
+ u32 pcie_config_base, pcie_config_size;
+
+ if (get_pcie_bar(&pcie_config_base, &pcie_config_size)) {
+ debug("Adding PCIe config bar base=0x%08x size=0x%x\n",
+ pcie_config_base, pcie_config_size);
+ }
+}
+
+static void northbridge_dmi_init(pci_dev_t dev)
+{
+ /* Clear error status bits */
+ writel(0xffffffff, DMIBAR_REG(0x1c4));
+ writel(0xffffffff, DMIBAR_REG(0x1d0));
+
+ /* Steps prior to DMI ASPM */
+ if ((bridge_silicon_revision() & BASE_REV_MASK) == BASE_REV_SNB) {
+ clrsetbits_le32(DMIBAR_REG(0x250), (1 << 22) | (1 << 20),
+ 1 << 21);
+ }
+
+ setbits_le32(DMIBAR_REG(0x238), 1 << 29);
+
+ if (bridge_silicon_revision() >= SNB_STEP_D0) {
+ setbits_le32(DMIBAR_REG(0x1f8), 1 << 16);
+ } else if (bridge_silicon_revision() >= SNB_STEP_D1) {
+ clrsetbits_le32(DMIBAR_REG(0x1f8), 1 << 26, 1 << 16);
+ setbits_le32(DMIBAR_REG(0x1fc), (1 << 12) | (1 << 23));
+ }
+
+ /* Enable ASPM on SNB link, should happen before PCH link */
+ if ((bridge_silicon_revision() & BASE_REV_MASK) == BASE_REV_SNB)
+ setbits_le32(DMIBAR_REG(0xd04), 1 << 4);
+
+ setbits_le32(DMIBAR_REG(0x88), (1 << 1) | (1 << 0));
+}
+
+void northbridge_init(pci_dev_t dev)
+{
+ u32 bridge_type;
+
+ add_fixed_resources(dev, 6);
+ northbridge_dmi_init(dev);
+
+ bridge_type = readl(MCHBAR_REG(0x5f10));
+ bridge_type &= ~0xff;
+
+ if ((bridge_silicon_revision() & BASE_REV_MASK) == BASE_REV_IVB) {
+ /* Enable Power Aware Interrupt Routing - fixed priority */
+ clrsetbits_8(MCHBAR_REG(0x5418), 0xf, 0x4);
+
+ /* 30h for IvyBridge */
+ bridge_type |= 0x30;
+ } else {
+ /* 20h for Sandybridge */
+ bridge_type |= 0x20;
+ }
+ writel(bridge_type, MCHBAR_REG(0x5f10));
+
+ /*
+ * Set bit 0 of BIOS_RESET_CPL to indicate to the CPU
+ * that BIOS has initialized memory and power management
+ */
+ setbits_8(MCHBAR_REG(BIOS_RESET_CPL), 1);
+ debug("Set BIOS_RESET_CPL\n");
+
+ /* Configure turbo power limits 1ms after reset complete bit */
+ mdelay(1);
+ set_power_limits(28);
+
+ /*
+ * CPUs with configurable TDP also need power limits set
+ * in MCHBAR. Use same values from MSR_PKG_POWER_LIMIT.
+ */
+ if (cpu_config_tdp_levels()) {
+ msr_t msr = msr_read(MSR_PKG_POWER_LIMIT);
+
+ writel(msr.lo, MCHBAR_REG(0x59A0));
+ writel(msr.hi, MCHBAR_REG(0x59A4));
+ }
+
+ /* Set here before graphics PM init */
+ writel(0x00100001, MCHBAR_REG(0x5500));
+}
+
+void northbridge_enable(pci_dev_t dev)
+{
+#if CONFIG_HAVE_ACPI_RESUME
+ switch (pci_read_config32(dev, SKPAD)) {
+ case 0xcafebabe:
+ debug("Normal boot.\n");
+ apci_set_slp_type(0);
+ break;
+ case 0xcafed00d:
+ debug("S3 Resume.\n");
+ apci_set_slp_type(3);
+ break;
+ default:
+ debug("Unknown boot method, assuming normal.\n");
+ apci_set_slp_type(0);
+ break;
+ }
+#endif
+}
diff --git a/arch/x86/cpu/ivybridge/pch.c b/arch/x86/cpu/ivybridge/pch.c
new file mode 100644
index 0000000000..fa04d488f3
--- /dev/null
+++ b/arch/x86/cpu/ivybridge/pch.c
@@ -0,0 +1,123 @@
+/*
+ * From Coreboot
+ * Copyright (C) 2008-2009 coresystems GmbH
+ * Copyright (C) 2012 The Chromium OS Authors.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/pci.h>
+#include <asm/arch/pch.h>
+
+static int pch_revision_id = -1;
+static int pch_type = -1;
+
+int pch_silicon_revision(void)
+{
+ pci_dev_t dev;
+
+ dev = PCH_LPC_DEV;
+
+ if (pch_revision_id < 0)
+ pch_revision_id = pci_read_config8(dev, PCI_REVISION_ID);
+ return pch_revision_id;
+}
+
+int pch_silicon_type(void)
+{
+ pci_dev_t dev;
+
+ dev = PCH_LPC_DEV;
+
+ if (pch_type < 0)
+ pch_type = pci_read_config8(dev, PCI_DEVICE_ID + 1);
+ return pch_type;
+}
+
+int pch_silicon_supported(int type, int rev)
+{
+ int cur_type = pch_silicon_type();
+ int cur_rev = pch_silicon_revision();
+
+ switch (type) {
+ case PCH_TYPE_CPT:
+ /* CougarPoint minimum revision */
+ if (cur_type == PCH_TYPE_CPT && cur_rev >= rev)
+ return 1;
+ /* PantherPoint any revision */
+ if (cur_type == PCH_TYPE_PPT)
+ return 1;
+ break;
+
+ case PCH_TYPE_PPT:
+ /* PantherPoint minimum revision */
+ if (cur_type == PCH_TYPE_PPT && cur_rev >= rev)
+ return 1;
+ break;
+ }
+
+ return 0;
+}
+
+#define IOBP_RETRY 1000
+static inline int iobp_poll(void)
+{
+ unsigned try = IOBP_RETRY;
+ u32 data;
+
+ while (try--) {
+ data = readl(RCB_REG(IOBPS));
+ if ((data & 1) == 0)
+ return 1;
+ udelay(10);
+ }
+
+ printf("IOBP timeout\n");
+ return 0;
+}
+
+void pch_iobp_update(u32 address, u32 andvalue, u32 orvalue)
+{
+ u32 data;
+
+ /* Set the address */
+ writel(address, RCB_REG(IOBPIRI));
+
+ /* READ OPCODE */
+ if (pch_silicon_supported(PCH_TYPE_CPT, PCH_STEP_B0))
+ writel(IOBPS_RW_BX, RCB_REG(IOBPS));
+ else
+ writel(IOBPS_READ_AX, RCB_REG(IOBPS));
+ if (!iobp_poll())
+ return;
+
+ /* Read IOBP data */
+ data = readl(RCB_REG(IOBPD));
+ if (!iobp_poll())
+ return;
+
+ /* Check for successful transaction */
+ if ((readl(RCB_REG(IOBPS)) & 0x6) != 0) {
+ printf("IOBP read 0x%08x failed\n", address);
+ return;
+ }
+
+ /* Update the data */
+ data &= andvalue;
+ data |= orvalue;
+
+ /* WRITE OPCODE */
+ if (pch_silicon_supported(PCH_TYPE_CPT, PCH_STEP_B0))
+ writel(IOBPS_RW_BX, RCB_REG(IOBPS));
+ else
+ writel(IOBPS_WRITE_AX, RCB_REG(IOBPS));
+ if (!iobp_poll())
+ return;
+
+ /* Write IOBP data */
+ writel(data, RCB_REG(IOBPD));
+ if (!iobp_poll())
+ return;
+}
diff --git a/arch/x86/cpu/ivybridge/pci.c b/arch/x86/cpu/ivybridge/pci.c
new file mode 100644
index 0000000000..452d1c3a15
--- /dev/null
+++ b/arch/x86/cpu/ivybridge/pci.c
@@ -0,0 +1,100 @@
+/*
+ * 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>
+#include <asm/arch/bd82x6x.h>
+#include <asm/arch/pch.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;
+}
+
+int board_pci_pre_scan(struct pci_controller *hose)
+{
+ pci_dev_t dev;
+ u16 reg16;
+
+ bd82x6x_init();
+
+ reg16 = 0xff;
+ dev = PCH_DEV;
+ reg16 = pci_read_config16(dev, PCI_COMMAND);
+ reg16 |= PCI_COMMAND_SERR | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
+ pci_write_config16(dev, PCI_COMMAND, reg16);
+
+ /*
+ * Clear non-reserved bits in status register.
+ */
+ pci_hose_write_config_word(hose, dev, PCI_STATUS, 0xffff);
+ pci_hose_write_config_byte(hose, dev, PCI_LATENCY_TIMER, 0x80);
+ pci_hose_write_config_byte(hose, dev, PCI_CACHE_LINE_SIZE, 0x08);
+
+ pci_write_bar32(hose, dev, 0, 0xf0000000);
+
+ return 0;
+}
+
+int board_pci_post_scan(struct pci_controller *hose)
+{
+ int ret;
+
+ ret = bd82x6x_init_pci_devices();
+ if (ret) {
+ printf("bd82x6x_init_pci_devices() failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/arch/x86/cpu/ivybridge/report_platform.c b/arch/x86/cpu/ivybridge/report_platform.c
new file mode 100644
index 0000000000..69e31b3ca2
--- /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/sata.c b/arch/x86/cpu/ivybridge/sata.c
new file mode 100644
index 0000000000..bbcd47da60
--- /dev/null
+++ b/arch/x86/cpu/ivybridge/sata.c
@@ -0,0 +1,225 @@
+/*
+ * From Coreboot
+ * Copyright (C) 2008-2009 coresystems GmbH
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <asm/io.h>
+#include <asm/pci.h>
+#include <asm/arch/pch.h>
+#include <asm/arch/bd82x6x.h>
+
+static inline u32 sir_read(pci_dev_t dev, int idx)
+{
+ pci_write_config32(dev, SATA_SIRI, idx);
+ return pci_read_config32(dev, SATA_SIRD);
+}
+
+static inline void sir_write(pci_dev_t dev, int idx, u32 value)
+{
+ pci_write_config32(dev, SATA_SIRI, idx);
+ pci_write_config32(dev, SATA_SIRD, value);
+}
+
+static void common_sata_init(pci_dev_t dev, unsigned int port_map)
+{
+ u32 reg32;
+ u16 reg16;
+
+ /* Set IDE I/O Configuration */
+ reg32 = SIG_MODE_PRI_NORMAL | FAST_PCB1 | FAST_PCB0 | PCB1 | PCB0;
+ pci_write_config32(dev, IDE_CONFIG, reg32);
+
+ /* Port enable */
+ reg16 = pci_read_config16(dev, 0x92);
+ reg16 &= ~0x3f;
+ reg16 |= port_map;
+ pci_write_config16(dev, 0x92, reg16);
+
+ /* SATA Initialization register */
+ port_map &= 0xff;
+ pci_write_config32(dev, 0x94, ((port_map ^ 0x3f) << 24) | 0x183);
+}
+
+void bd82x6x_sata_init(pci_dev_t dev, const void *blob, int node)
+{
+ unsigned int port_map, speed_support, port_tx;
+ struct pci_controller *hose = pci_bus_to_hose(0);
+ const char *mode;
+ u32 reg32;
+ u16 reg16;
+
+ debug("SATA: Initializing...\n");
+
+ /* SATA configuration */
+ port_map = fdtdec_get_int(blob, node, "intel,sata-port-map", 0);
+ speed_support = fdtdec_get_int(blob, node,
+ "sata_interface_speed_support", 0);
+
+ /* Enable BARs */
+ pci_write_config16(dev, PCI_COMMAND, 0x0007);
+
+ mode = fdt_getprop(blob, node, "intel,sata-mode", NULL);
+ if (!mode || !strcmp(mode, "ahci")) {
+ u32 abar;
+
+ debug("SATA: Controller in AHCI mode\n");
+
+ /* Set Interrupt Line, Interrupt Pin is set by D31IP.PIP */
+ pci_write_config8(dev, INTR_LN, 0x0a);
+
+ /* Set timings */
+ pci_write_config16(dev, IDE_TIM_PRI, IDE_DECODE_ENABLE |
+ IDE_ISP_3_CLOCKS | IDE_RCT_1_CLOCKS |
+ IDE_PPE0 | IDE_IE0 | IDE_TIME0);
+ pci_write_config16(dev, IDE_TIM_SEC, IDE_DECODE_ENABLE |
+ IDE_ISP_5_CLOCKS | IDE_RCT_4_CLOCKS);
+
+ /* Sync DMA */
+ pci_write_config16(dev, IDE_SDMA_CNT, IDE_PSDE0);
+ pci_write_config16(dev, IDE_SDMA_TIM, 0x0001);
+
+ common_sata_init(dev, 0x8000 | port_map);
+
+ /* Initialize AHCI memory-mapped space */
+ abar = pci_read_bar32(hose, dev, 5);
+ debug("ABAR: %08X\n", abar);
+ /* CAP (HBA Capabilities) : enable power management */
+ reg32 = readl(abar + 0x00);
+ reg32 |= 0x0c006000; /* set PSC+SSC+SALP+SSS */
+ reg32 &= ~0x00020060; /* clear SXS+EMS+PMS */
+ /* Set ISS, if available */
+ if (speed_support) {
+ reg32 &= ~0x00f00000;
+ reg32 |= (speed_support & 0x03) << 20;
+ }
+ writel(reg32, abar + 0x00);
+ /* PI (Ports implemented) */
+ writel(port_map, abar + 0x0c);
+ (void) readl(abar + 0x0c); /* Read back 1 */
+ (void) readl(abar + 0x0c); /* Read back 2 */
+ /* CAP2 (HBA Capabilities Extended)*/
+ reg32 = readl(abar + 0x24);
+ reg32 &= ~0x00000002;
+ writel(reg32, abar + 0x24);
+ /* VSP (Vendor Specific Register */
+ reg32 = readl(abar + 0xa0);
+ reg32 &= ~0x00000005;
+ writel(reg32, abar + 0xa0);
+ } else if (!strcmp(mode, "combined")) {
+ debug("SATA: Controller in combined mode\n");
+
+ /* No AHCI: clear AHCI base */
+ pci_write_bar32(hose, dev, 5, 0x00000000);
+ /* And without AHCI BAR no memory decoding */
+ reg16 = pci_read_config16(dev, PCI_COMMAND);
+ reg16 &= ~PCI_COMMAND_MEMORY;
+ pci_write_config16(dev, PCI_COMMAND, reg16);
+
+ pci_write_config8(dev, 0x09, 0x80);
+
+ /* Set timings */
+ pci_write_config16(dev, IDE_TIM_PRI, IDE_DECODE_ENABLE |
+ IDE_ISP_5_CLOCKS | IDE_RCT_4_CLOCKS);
+ pci_write_config16(dev, IDE_TIM_SEC, IDE_DECODE_ENABLE |
+ IDE_ISP_3_CLOCKS | IDE_RCT_1_CLOCKS |
+ IDE_PPE0 | IDE_IE0 | IDE_TIME0);
+
+ /* Sync DMA */
+ pci_write_config16(dev, IDE_SDMA_CNT, IDE_SSDE0);
+ pci_write_config16(dev, IDE_SDMA_TIM, 0x0200);
+
+ common_sata_init(dev, port_map);
+ } else {
+ debug("SATA: Controller in plain-ide mode\n");
+
+ /* No AHCI: clear AHCI base */
+ pci_write_bar32(hose, dev, 5, 0x00000000);
+
+ /* And without AHCI BAR no memory decoding */
+ reg16 = pci_read_config16(dev, PCI_COMMAND);
+ reg16 &= ~PCI_COMMAND_MEMORY;
+ pci_write_config16(dev, PCI_COMMAND, reg16);
+
+ /*
+ * Native mode capable on both primary and secondary (0xa)
+ * OR'ed with enabled (0x50) = 0xf
+ */
+ pci_write_config8(dev, 0x09, 0x8f);
+
+ /* Set Interrupt Line */
+ /* Interrupt Pin is set by D31IP.PIP */
+ pci_write_config8(dev, INTR_LN, 0xff);
+
+ /* Set timings */
+ pci_write_config16(dev, IDE_TIM_PRI, IDE_DECODE_ENABLE |
+ IDE_ISP_3_CLOCKS | IDE_RCT_1_CLOCKS |
+ IDE_PPE0 | IDE_IE0 | IDE_TIME0);
+ pci_write_config16(dev, IDE_TIM_SEC, IDE_DECODE_ENABLE |
+ IDE_SITRE | IDE_ISP_3_CLOCKS |
+ IDE_RCT_1_CLOCKS | IDE_IE0 | IDE_TIME0);
+
+ /* Sync DMA */
+ pci_write_config16(dev, IDE_SDMA_CNT, IDE_SSDE0 | IDE_PSDE0);
+ pci_write_config16(dev, IDE_SDMA_TIM, 0x0201);
+
+ common_sata_init(dev, port_map);
+ }
+
+ /* Set Gen3 Transmitter settings if needed */
+ port_tx = fdtdec_get_int(blob, node, "intel,sata-port0-gen3-tx", 0);
+ if (port_tx)
+ pch_iobp_update(SATA_IOBP_SP0G3IR, 0, port_tx);
+
+ port_tx = fdtdec_get_int(blob, node, "intel,sata-port1-gen3-tx", 0);
+ if (port_tx)
+ pch_iobp_update(SATA_IOBP_SP1G3IR, 0, port_tx);
+
+ /* Additional Programming Requirements */
+ sir_write(dev, 0x04, 0x00001600);
+ sir_write(dev, 0x28, 0xa0000033);
+ reg32 = sir_read(dev, 0x54);
+ reg32 &= 0xff000000;
+ reg32 |= 0x5555aa;
+ sir_write(dev, 0x54, reg32);
+ sir_write(dev, 0x64, 0xcccc8484);
+ reg32 = sir_read(dev, 0x68);
+ reg32 &= 0xffff0000;
+ reg32 |= 0xcccc;
+ sir_write(dev, 0x68, reg32);
+ reg32 = sir_read(dev, 0x78);
+ reg32 &= 0x0000ffff;
+ reg32 |= 0x88880000;
+ sir_write(dev, 0x78, reg32);
+ sir_write(dev, 0x84, 0x001c7000);
+ sir_write(dev, 0x88, 0x88338822);
+ sir_write(dev, 0xa0, 0x001c7000);
+ sir_write(dev, 0xc4, 0x0c0c0c0c);
+ sir_write(dev, 0xc8, 0x0c0c0c0c);
+ sir_write(dev, 0xd4, 0x10000000);
+
+ pch_iobp_update(0xea004001, 0x3fffffff, 0xc0000000);
+ pch_iobp_update(0xea00408a, 0xfffffcff, 0x00000100);
+}
+
+void bd82x6x_sata_enable(pci_dev_t dev, const void *blob, int node)
+{
+ unsigned port_map;
+ const char *mode;
+ u16 map = 0;
+
+ /*
+ * Set SATA controller mode early so the resource allocator can
+ * properly assign IO/Memory resources for the controller.
+ */
+ mode = fdt_getprop(blob, node, "intel,sata-mode", NULL);
+ if (mode && !strcmp(mode, "ahci"))
+ map = 0x0060;
+ port_map = fdtdec_get_int(blob, node, "intel,sata-port-map", 0);
+
+ map |= (port_map ^ 0x3f) << 8;
+ pci_write_config16(dev, 0x90, map);
+}
diff --git a/arch/x86/cpu/ivybridge/sdram.c b/arch/x86/cpu/ivybridge/sdram.c
new file mode 100644
index 0000000000..95047359ff
--- /dev/null
+++ b/arch/x86/cpu/ivybridge/sdram.c
@@ -0,0 +1,581 @@
+/*
+ * 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/mtrr.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_ADDR;
+ 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);
+
+ /* Add MTRRs for memory */
+ mtrr_add_request(MTRR_TYPE_WRBACK, 0, 2ULL << 30);
+ mtrr_add_request(MTRR_TYPE_WRBACK, 2ULL << 30, 512 << 20);
+ mtrr_add_request(MTRR_TYPE_WRBACK, 0xaULL << 28, 256 << 20);
+ mtrr_add_request(MTRR_TYPE_UNCACHEABLE, tseg_base, 16 << 20);
+ mtrr_add_request(MTRR_TYPE_UNCACHEABLE, tseg_base + (16 << 20),
+ 32 << 20);
+
+ /*
+ * 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/ivybridge/usb_ehci.c b/arch/x86/cpu/ivybridge/usb_ehci.c
new file mode 100644
index 0000000000..291c971a2f
--- /dev/null
+++ b/arch/x86/cpu/ivybridge/usb_ehci.c
@@ -0,0 +1,29 @@
+/*
+ * From Coreboot
+ * Copyright (C) 2008-2009 coresystems GmbH
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/pci.h>
+#include <asm/arch/pch.h>
+
+void bd82x6x_usb_ehci_init(pci_dev_t dev)
+{
+ u32 reg32;
+
+ /* Disable Wake on Disconnect in RMH */
+ reg32 = readl(RCB_REG(0x35b0));
+ reg32 |= 0x22;
+ writel(reg32, RCB_REG(0x35b0));
+
+ debug("EHCI: Setting up controller.. ");
+ reg32 = pci_read_config32(dev, PCI_COMMAND);
+ reg32 |= PCI_COMMAND_MASTER;
+ /* reg32 |= PCI_COMMAND_SERR; */
+ pci_write_config32(dev, PCI_COMMAND, reg32);
+
+ debug("done.\n");
+}
diff --git a/arch/x86/cpu/ivybridge/usb_xhci.c b/arch/x86/cpu/ivybridge/usb_xhci.c
new file mode 100644
index 0000000000..4a32a7eb31
--- /dev/null
+++ b/arch/x86/cpu/ivybridge/usb_xhci.c
@@ -0,0 +1,32 @@
+/*
+ * From Coreboot
+ * Copyright (C) 2008-2009 coresystems GmbH
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <common.h>
+#include <asm/pci.h>
+#include <asm/arch/pch.h>
+
+void bd82x6x_usb_xhci_init(pci_dev_t dev)
+{
+ u32 reg32;
+
+ debug("XHCI: Setting up controller.. ");
+
+ /* lock overcurrent map */
+ reg32 = pci_read_config32(dev, 0x44);
+ reg32 |= 1;
+ pci_write_config32(dev, 0x44, reg32);
+
+ /* Enable clock gating */
+ reg32 = pci_read_config32(dev, 0x40);
+ reg32 &= ~((1 << 20) | (1 << 21));
+ reg32 |= (1 << 19) | (1 << 18) | (1 << 17);
+ reg32 |= (1 << 10) | (1 << 9) | (1 << 8);
+ reg32 |= (1 << 31); /* lock */
+ pci_write_config32(dev, 0x40, reg32);
+
+ debug("done.\n");
+}
diff --git a/arch/x86/cpu/lapic.c b/arch/x86/cpu/lapic.c
new file mode 100644
index 0000000000..4690603c75
--- /dev/null
+++ b/arch/x86/cpu/lapic.c
@@ -0,0 +1,57 @@
+/*
+ * From coreboot file of same name
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ * Copyright (C) 2014 Google, Inc
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <common.h>
+#include <asm/msr.h>
+#include <asm/io.h>
+#include <asm/lapic.h>
+#include <asm/post.h>
+
+void lapic_setup(void)
+{
+#if NEED_LAPIC == 1
+ /* Only Pentium Pro and later have those MSR stuff */
+ debug("Setting up local apic: ");
+
+ /* Enable the local apic */
+ enable_lapic();
+
+ /*
+ * Set Task Priority to 'accept all'.
+ */
+ lapic_write_around(LAPIC_TASKPRI,
+ lapic_read_around(LAPIC_TASKPRI) & ~LAPIC_TPRI_MASK);
+
+ /* Put the local apic in virtual wire mode */
+ lapic_write_around(LAPIC_SPIV, (lapic_read_around(LAPIC_SPIV) &
+ ~(LAPIC_VECTOR_MASK)) | LAPIC_SPIV_ENABLE);
+ lapic_write_around(LAPIC_LVT0, (lapic_read_around(LAPIC_LVT0) &
+ ~(LAPIC_LVT_MASKED | LAPIC_LVT_LEVEL_TRIGGER |
+ LAPIC_LVT_REMOTE_IRR | LAPIC_INPUT_POLARITY |
+ LAPIC_SEND_PENDING | LAPIC_LVT_RESERVED_1 |
+ LAPIC_DELIVERY_MODE_MASK)) |
+ (LAPIC_LVT_REMOTE_IRR | LAPIC_SEND_PENDING |
+ LAPIC_DELIVERY_MODE_EXTINT));
+ lapic_write_around(LAPIC_LVT1, (lapic_read_around(LAPIC_LVT1) &
+ ~(LAPIC_LVT_MASKED | LAPIC_LVT_LEVEL_TRIGGER |
+ LAPIC_LVT_REMOTE_IRR | LAPIC_INPUT_POLARITY |
+ LAPIC_SEND_PENDING | LAPIC_LVT_RESERVED_1 |
+ LAPIC_DELIVERY_MODE_MASK)) |
+ (LAPIC_LVT_REMOTE_IRR | LAPIC_SEND_PENDING |
+ LAPIC_DELIVERY_MODE_NMI));
+
+ debug("apic_id: 0x%02lx, ", lapicid());
+#else /* !NEED_LLAPIC */
+ /* Only Pentium Pro and later have those MSR stuff */
+ debug("Disabling local apic: ");
+ disable_lapic();
+#endif /* !NEED_LAPIC */
+ debug("done.\n");
+ post_code(POST_LAPIC);
+}
diff --git a/arch/x86/cpu/mtrr.c b/arch/x86/cpu/mtrr.c
new file mode 100644
index 0000000000..d5a825d181
--- /dev/null
+++ b/arch/x86/cpu/mtrr.c
@@ -0,0 +1,81 @@
+/*
+ * (C) Copyright 2014 Google, Inc
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * Memory Type Range Regsters - these are used to tell the CPU whether
+ * memory is cacheable and if so the cache write mode to use.
+ *
+ * These can speed up booting. See the mtrr command.
+ *
+ * Reference: Intel Architecture Software Developer's Manual, Volume 3:
+ * System Programming
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/msr.h>
+#include <asm/mtrr.h>
+
+/* Prepare to adjust MTRRs */
+void mtrr_open(struct mtrr_state *state)
+{
+ state->enable_cache = dcache_status();
+
+ if (state->enable_cache)
+ disable_caches();
+ state->deftype = native_read_msr(MTRR_DEF_TYPE_MSR);
+ wrmsrl(MTRR_DEF_TYPE_MSR, state->deftype & ~MTRR_DEF_TYPE_EN);
+}
+
+/* Clean up after adjusting MTRRs, and enable them */
+void mtrr_close(struct mtrr_state *state)
+{
+ wrmsrl(MTRR_DEF_TYPE_MSR, state->deftype | MTRR_DEF_TYPE_EN);
+ if (state->enable_cache)
+ enable_caches();
+}
+
+int mtrr_commit(bool do_caches)
+{
+ struct mtrr_request *req = gd->arch.mtrr_req;
+ struct mtrr_state state;
+ uint64_t mask;
+ int i;
+
+ mtrr_open(&state);
+ for (i = 0; i < gd->arch.mtrr_req_count; i++, req++) {
+ mask = ~(req->size - 1);
+ mask &= (1ULL << CONFIG_CPU_ADDR_BITS) - 1;
+ wrmsrl(MTRR_PHYS_BASE_MSR(i), req->start | req->type);
+ wrmsrl(MTRR_PHYS_MASK_MSR(i), mask | MTRR_PHYS_MASK_VALID);
+ }
+
+ /* Clear the ones that are unused */
+ for (; i < MTRR_COUNT; i++)
+ wrmsrl(MTRR_PHYS_MASK_MSR(i), 0);
+ mtrr_close(&state);
+
+ return 0;
+}
+
+int mtrr_add_request(int type, uint64_t start, uint64_t size)
+{
+ struct mtrr_request *req;
+ uint64_t mask;
+
+ if (gd->arch.mtrr_req_count == MAX_MTRR_REQUESTS)
+ return -ENOSPC;
+ req = &gd->arch.mtrr_req[gd->arch.mtrr_req_count++];
+ req->type = type;
+ req->start = start;
+ req->size = size;
+ debug("%d: type=%d, %08llx %08llx\n", gd->arch.mtrr_req_count - 1,
+ req->type, req->start, req->size);
+ mask = ~(req->size - 1);
+ mask &= (1ULL << CONFIG_CPU_ADDR_BITS) - 1;
+ mask |= MTRR_PHYS_MASK_VALID;
+ debug(" %016llx %016llx\n", req->start | req->type, mask);
+
+ return 0;
+}
diff --git a/arch/x86/cpu/pci.c b/arch/x86/cpu/pci.c
new file mode 100644
index 0000000000..ab1aaaa059
--- /dev/null
+++ b/arch/x86/cpu/pci.c
@@ -0,0 +1,113 @@
+/*
+ * 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>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+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);
+ hose->last_busno = pci_hose_scan(hose);
+ gd->hose = hose;
+ *hosep = hose;
+
+ return 0;
+}
+
+__weak int board_pci_pre_scan(struct pci_controller *hose)
+{
+ return 0;
+}
+
+__weak int board_pci_post_scan(struct pci_controller *hose)
+{
+ return 0;
+}
+
+void pci_init_board(void)
+{
+ struct pci_controller *hose = &x86_hose;
+
+ /* Stop using the early hose */
+ gd->hose = NULL;
+
+ board_pci_setup_hose(hose);
+ pci_setup_type1(hose);
+ pci_register_hose(hose);
+
+ board_pci_pre_scan(hose);
+ hose->last_busno = pci_hose_scan(hose);
+ board_pci_post_scan(hose);
+}
+
+static struct pci_controller *get_hose(void)
+{
+ if (gd->hose)
+ return gd->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/queensbay/Kconfig b/arch/x86/cpu/queensbay/Kconfig
new file mode 100644
index 0000000000..f6b52010c3
--- /dev/null
+++ b/arch/x86/cpu/queensbay/Kconfig
@@ -0,0 +1,79 @@
+#
+# Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+
+config INTEL_QUEENSBAY
+ bool
+ select HAVE_FSP
+ select HAVE_CMC
+
+if INTEL_QUEENSBAY
+
+config HAVE_FSP
+ bool "Add an Firmware Support Package binary"
+ help
+ Select this option to add an Firmware Support Package binary to
+ the resulting U-Boot image. It is a binary blob which U-Boot uses
+ to set up SDRAM and other chipset specific initialization.
+
+ Note: Without this binary U-Boot will not be able to set up its
+ SDRAM so will not boot.
+
+config FSP_FILE
+ string "Firmware Support Package binary filename"
+ depends on HAVE_FSP
+ default "fsp.bin"
+ help
+ The filename of the file to use as Firmware Support Package binary
+ in the board directory.
+
+config FSP_ADDR
+ hex "Firmware Support Package binary location"
+ depends on HAVE_FSP
+ default 0xfffc0000
+ help
+ FSP is not Position Independent Code (PIC) and the whole FSP has to
+ be rebased if it is placed at a location which is different from the
+ perferred base address specified during the FSP build. Use Intel's
+ Binary Configuration Tool (BCT) to do the rebase.
+
+ The default base address of 0xfffc0000 indicates that the binary must
+ be located at offset 0xc0000 from the beginning of a 1MB flash device.
+
+config FSP_TEMP_RAM_ADDR
+ hex
+ default 0x2000000
+ help
+ Stack top address which is used in FspInit after DRAM is ready and
+ CAR is disabled.
+
+config HAVE_CMC
+ bool "Add a Chipset Micro Code state machine binary"
+ help
+ Select this option to add a Chipset Micro Code state machine binary
+ to the resulting U-Boot image. It is a 64K data block of machine
+ specific code which must be put in the flash for the processor to
+ access when powered up before system BIOS is executed.
+
+config CMC_FILE
+ string "Chipset Micro Code state machine filename"
+ depends on HAVE_CMC
+ default "cmc.bin"
+ help
+ The filename of the file to use as Chipset Micro Code state machine
+ binary in the board directory.
+
+config CMC_ADDR
+ hex "Chipset Micro Code state machine binary location"
+ depends on HAVE_CMC
+ default 0xfffb0000
+ help
+ The location of the CMC binary is determined by a strap. It must be
+ put in flash at a location matching the strap-determined base address.
+
+ The default base address of 0xfffb0000 indicates that the binary must
+ be located at offset 0xb0000 from the beginning of a 1MB flash device.
+
+endif
diff --git a/arch/x86/cpu/queensbay/Makefile b/arch/x86/cpu/queensbay/Makefile
new file mode 100644
index 0000000000..2c2ec01ed2
--- /dev/null
+++ b/arch/x86/cpu/queensbay/Makefile
@@ -0,0 +1,9 @@
+#
+# Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+
+obj-y += tnc_car.o tnc_dram.o tnc.o topcliff.o
+obj-y += fsp_configs.o fsp_support.o
+obj-$(CONFIG_PCI) += tnc_pci.o
diff --git a/arch/x86/cpu/queensbay/fsp_configs.c b/arch/x86/cpu/queensbay/fsp_configs.c
new file mode 100644
index 0000000000..af28e457f6
--- /dev/null
+++ b/arch/x86/cpu/queensbay/fsp_configs.c
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2013, Intel Corporation
+ * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * SPDX-License-Identifier: Intel
+ */
+
+#include <common.h>
+#include <asm/arch/fsp/fsp_support.h>
+
+void update_fsp_upd(struct upd_region *fsp_upd)
+{
+ /* Override any UPD setting if required */
+
+ /* Uncomment the line below to enable DEBUG message */
+ /* fsp_upd->serial_dbgport_type = 1; */
+
+ /* Examples on how to initialize the pointers in UPD region */
+ /* fsp_upd->pcd_example = (EXAMPLE_DATA *)&example; */
+}
diff --git a/arch/x86/cpu/queensbay/fsp_support.c b/arch/x86/cpu/queensbay/fsp_support.c
new file mode 100644
index 0000000000..aed3e2b300
--- /dev/null
+++ b/arch/x86/cpu/queensbay/fsp_support.c
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2013, Intel Corporation
+ * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * SPDX-License-Identifier: Intel
+ */
+
+#include <common.h>
+#include <asm/arch/fsp/fsp_support.h>
+#include <asm/post.h>
+
+/**
+ * Compares two GUIDs
+ *
+ * If the GUIDs are identical then true is returned.
+ * If there are any bit differences in the two GUIDs, then false is returned.
+ *
+ * @guid1: A pointer to a 128 bit GUID.
+ * @guid2: A pointer to a 128 bit GUID.
+ *
+ * @retval true: guid1 and guid2 are identical.
+ * @retval false: guid1 and guid2 are not identical.
+ */
+static bool compare_guid(const struct efi_guid *guid1,
+ const struct efi_guid *guid2)
+{
+ if (memcmp(guid1, guid2, sizeof(struct efi_guid)) == 0)
+ return true;
+ else
+ return false;
+}
+
+u32 __attribute__((optimize("O0"))) find_fsp_header(void)
+{
+ /*
+ * This function may be called before the a stack is established,
+ * so special care must be taken. First, it cannot declare any local
+ * variable using stack. Only register variable can be used here.
+ * Secondly, some compiler version will add prolog or epilog code
+ * for the C function. If so the function call may not work before
+ * stack is ready.
+ *
+ * GCC 4.8.1 has been verified to be working for the following codes.
+ */
+ volatile register u8 *fsp asm("eax");
+
+ /* Initalize the FSP base */
+ fsp = (u8 *)CONFIG_FSP_ADDR;
+
+ /* Check the FV signature, _FVH */
+ if (((struct fv_header *)fsp)->sign == EFI_FVH_SIGNATURE) {
+ /* Go to the end of the FV header and align the address */
+ fsp += ((struct fv_header *)fsp)->ext_hdr_off;
+ fsp += ((struct fv_ext_header *)fsp)->ext_hdr_size;
+ fsp = (u8 *)(((u32)fsp + 7) & 0xFFFFFFF8);
+ } else {
+ fsp = 0;
+ }
+
+ /* Check the FFS GUID */
+ if (fsp &&
+ ((struct ffs_file_header *)fsp)->name.data1 == FSP_GUID_DATA1 &&
+ ((struct ffs_file_header *)fsp)->name.data2 == FSP_GUID_DATA2 &&
+ ((struct ffs_file_header *)fsp)->name.data3 == FSP_GUID_DATA3 &&
+ ((struct ffs_file_header *)fsp)->name.data4[0] == FSP_GUID_DATA4_0 &&
+ ((struct ffs_file_header *)fsp)->name.data4[1] == FSP_GUID_DATA4_1 &&
+ ((struct ffs_file_header *)fsp)->name.data4[2] == FSP_GUID_DATA4_2 &&
+ ((struct ffs_file_header *)fsp)->name.data4[3] == FSP_GUID_DATA4_3 &&
+ ((struct ffs_file_header *)fsp)->name.data4[4] == FSP_GUID_DATA4_4 &&
+ ((struct ffs_file_header *)fsp)->name.data4[5] == FSP_GUID_DATA4_5 &&
+ ((struct ffs_file_header *)fsp)->name.data4[6] == FSP_GUID_DATA4_6 &&
+ ((struct ffs_file_header *)fsp)->name.data4[7] == FSP_GUID_DATA4_7) {
+ /* Add the FFS header size to find the raw section header */
+ fsp += sizeof(struct ffs_file_header);
+ } else {
+ fsp = 0;
+ }
+
+ if (fsp &&
+ ((struct raw_section *)fsp)->type == EFI_SECTION_RAW) {
+ /* Add the raw section header size to find the FSP header */
+ fsp += sizeof(struct raw_section);
+ } else {
+ fsp = 0;
+ }
+
+ return (u32)fsp;
+}
+
+void fsp_continue(struct shared_data *shared_data, u32 status, void *hob_list)
+{
+ u32 stack_len;
+ u32 stack_base;
+ u32 stack_top;
+
+ post_code(POST_MRC);
+
+ assert(status == 0);
+
+ /* Get the migrated stack in normal memory */
+ stack_base = (u32)fsp_get_bootloader_tmp_mem(hob_list, &stack_len);
+ assert(stack_base != 0);
+ stack_top = stack_base + stack_len - sizeof(u32);
+
+ /*
+ * Old stack base is stored at the very end of the stack top,
+ * use it to calculate the migrated shared data base
+ */
+ shared_data = (struct shared_data *)(stack_base +
+ ((u32)shared_data - *(u32 *)stack_top));
+
+ /* The boot loader main function entry */
+ fsp_init_done(hob_list);
+}
+
+void fsp_init(u32 stack_top, u32 boot_mode, void *nvs_buf)
+{
+ struct shared_data shared_data;
+ fsp_init_f init;
+ struct fsp_init_params params;
+ struct fspinit_rtbuf rt_buf;
+ struct vpd_region *fsp_vpd;
+ struct fsp_header *fsp_hdr;
+ struct fsp_init_params *params_ptr;
+ struct upd_region *fsp_upd;
+
+ fsp_hdr = (struct fsp_header *)find_fsp_header();
+ if (fsp_hdr == NULL) {
+ /* No valid FSP info header was found */
+ panic("Invalid FSP header");
+ }
+
+ fsp_upd = (struct upd_region *)&shared_data.fsp_upd;
+ memset(&rt_buf, 0, sizeof(struct fspinit_rtbuf));
+
+ /* Reserve a gap in stack top */
+ rt_buf.common.stack_top = (u32 *)stack_top - 32;
+ rt_buf.common.boot_mode = boot_mode;
+ rt_buf.common.upd_data = (struct upd_region *)fsp_upd;
+
+ /* Get VPD region start */
+ fsp_vpd = (struct vpd_region *)(fsp_hdr->img_base +
+ fsp_hdr->cfg_region_off);
+
+ /* Verifify the VPD data region is valid */
+ assert((fsp_vpd->img_rev == VPD_IMAGE_REV) &&
+ (fsp_vpd->sign == VPD_IMAGE_ID));
+
+ /* Copy default data from Flash */
+ memcpy(fsp_upd, (void *)(fsp_hdr->img_base + fsp_vpd->upd_offset),
+ sizeof(struct upd_region));
+
+ /* Verifify the UPD data region is valid */
+ assert(fsp_upd->terminator == UPD_TERMINATOR);
+
+ /* Override any UPD setting if required */
+ update_fsp_upd(fsp_upd);
+
+ memset(&params, 0, sizeof(struct fsp_init_params));
+ params.nvs_buf = nvs_buf;
+ params.rt_buf = (struct fspinit_rtbuf *)&rt_buf;
+ params.continuation = (fsp_continuation_f)asm_continuation;
+
+ init = (fsp_init_f)(fsp_hdr->img_base + fsp_hdr->fsp_init);
+ params_ptr = &params;
+
+ shared_data.fsp_hdr = fsp_hdr;
+ shared_data.stack_top = (u32 *)stack_top;
+
+ post_code(POST_PRE_MRC);
+
+ /*
+ * Use ASM code to ensure the register value in EAX & ECX
+ * will be passed into BlContinuationFunc
+ */
+ asm volatile (
+ "pushl %0;"
+ "call *%%eax;"
+ ".global asm_continuation;"
+ "asm_continuation:;"
+ "movl %%ebx, %%eax;" /* shared_data */
+ "movl 4(%%esp), %%edx;" /* status */
+ "movl 8(%%esp), %%ecx;" /* hob_list */
+ "jmp fsp_continue;"
+ : : "m"(params_ptr), "a"(init), "b"(&shared_data)
+ );
+
+ /*
+ * Should never get here.
+ * Control will continue from fsp_continue.
+ * This line below is to prevent the compiler from optimizing
+ * structure intialization.
+ *
+ * DO NOT REMOVE!
+ */
+ init(&params);
+}
+
+u32 fsp_notify(struct fsp_header *fsp_hdr, u32 phase)
+{
+ fsp_notify_f notify;
+ struct fsp_notify_params params;
+ struct fsp_notify_params *params_ptr;
+ u32 status;
+
+ if (!fsp_hdr)
+ fsp_hdr = (struct fsp_header *)find_fsp_header();
+
+ if (fsp_hdr == NULL) {
+ /* No valid FSP info header */
+ panic("Invalid FSP header");
+ }
+
+ notify = (fsp_notify_f)(fsp_hdr->img_base + fsp_hdr->fsp_notify);
+ params.phase = phase;
+ params_ptr = &params;
+
+ /*
+ * Use ASM code to ensure correct parameter is on the stack for
+ * FspNotify as U-Boot is using different ABI from FSP
+ */
+ asm volatile (
+ "pushl %1;" /* push notify phase */
+ "call *%%eax;" /* call FspNotify */
+ "addl $4, %%esp;" /* clean up the stack */
+ : "=a"(status) : "m"(params_ptr), "a"(notify), "m"(*params_ptr)
+ );
+
+ return status;
+}
+
+u32 fsp_get_usable_lowmem_top(const void *hob_list)
+{
+ const struct hob_header *hdr;
+ struct hob_res_desc *res_desc;
+ phys_addr_t phys_start;
+ u32 top;
+
+ /* Get the HOB list for processing */
+ hdr = hob_list;
+
+ /* * Collect memory ranges */
+ top = FSP_LOWMEM_BASE;
+ while (!end_of_hob(hdr)) {
+ if (hdr->type == HOB_TYPE_RES_DESC) {
+ res_desc = (struct hob_res_desc *)hdr;
+ if (res_desc->type == RES_SYS_MEM) {
+ phys_start = res_desc->phys_start;
+ /* Need memory above 1MB to be collected here */
+ if (phys_start >= FSP_LOWMEM_BASE &&
+ phys_start < (phys_addr_t)FSP_HIGHMEM_BASE)
+ top += (u32)(res_desc->len);
+ }
+ }
+ hdr = get_next_hob(hdr);
+ }
+
+ return top;
+}
+
+u64 fsp_get_usable_highmem_top(const void *hob_list)
+{
+ const struct hob_header *hdr;
+ struct hob_res_desc *res_desc;
+ phys_addr_t phys_start;
+ u64 top;
+
+ /* Get the HOB list for processing */
+ hdr = hob_list;
+
+ /* Collect memory ranges */
+ top = FSP_HIGHMEM_BASE;
+ while (!end_of_hob(hdr)) {
+ if (hdr->type == HOB_TYPE_RES_DESC) {
+ res_desc = (struct hob_res_desc *)hdr;
+ if (res_desc->type == RES_SYS_MEM) {
+ phys_start = res_desc->phys_start;
+ /* Need memory above 1MB to be collected here */
+ if (phys_start >= (phys_addr_t)FSP_HIGHMEM_BASE)
+ top += (u32)(res_desc->len);
+ }
+ }
+ hdr = get_next_hob(hdr);
+ }
+
+ return top;
+}
+
+u64 fsp_get_reserved_mem_from_guid(const void *hob_list, u64 *len,
+ struct efi_guid *guid)
+{
+ const struct hob_header *hdr;
+ struct hob_res_desc *res_desc;
+
+ /* Get the HOB list for processing */
+ hdr = hob_list;
+
+ /* Collect memory ranges */
+ while (!end_of_hob(hdr)) {
+ if (hdr->type == HOB_TYPE_RES_DESC) {
+ res_desc = (struct hob_res_desc *)hdr;
+ if (res_desc->type == RES_MEM_RESERVED) {
+ if (compare_guid(&res_desc->owner, guid)) {
+ if (len)
+ *len = (u32)(res_desc->len);
+
+ return (u64)(res_desc->phys_start);
+ }
+ }
+ }
+ hdr = get_next_hob(hdr);
+ }
+
+ return 0;
+}
+
+u32 fsp_get_fsp_reserved_mem(const void *hob_list, u32 *len)
+{
+ const struct efi_guid guid = FSP_HOB_RESOURCE_OWNER_FSP_GUID;
+ u64 length;
+ u32 base;
+
+ base = (u32)fsp_get_reserved_mem_from_guid(hob_list,
+ &length, (struct efi_guid *)&guid);
+ if ((len != 0) && (base != 0))
+ *len = (u32)length;
+
+ return base;
+}
+
+u32 fsp_get_tseg_reserved_mem(const void *hob_list, u32 *len)
+{
+ const struct efi_guid guid = FSP_HOB_RESOURCE_OWNER_TSEG_GUID;
+ u64 length;
+ u32 base;
+
+ base = (u32)fsp_get_reserved_mem_from_guid(hob_list,
+ &length, (struct efi_guid *)&guid);
+ if ((len != 0) && (base != 0))
+ *len = (u32)length;
+
+ return base;
+}
+
+const struct hob_header *fsp_get_next_hob(uint type, const void *hob_list)
+{
+ const struct hob_header *hdr;
+
+ hdr = hob_list;
+
+ /* Parse the HOB list until end of list or matching type is found */
+ while (!end_of_hob(hdr)) {
+ if (hdr->type == type)
+ return hdr;
+
+ hdr = get_next_hob(hdr);
+ }
+
+ return NULL;
+}
+
+const struct hob_header *fsp_get_next_guid_hob(const struct efi_guid *guid,
+ const void *hob_list)
+{
+ const struct hob_header *hdr;
+ struct hob_guid *guid_hob;
+
+ hdr = hob_list;
+ while ((hdr = fsp_get_next_hob(HOB_TYPE_GUID_EXT,
+ hdr)) != NULL) {
+ guid_hob = (struct hob_guid *)hdr;
+ if (compare_guid(guid, &(guid_hob->name)))
+ break;
+ hdr = get_next_hob(hdr);
+ }
+
+ return hdr;
+}
+
+void *fsp_get_guid_hob_data(const void *hob_list, u32 *len,
+ struct efi_guid *guid)
+{
+ const struct hob_header *guid_hob;
+
+ guid_hob = fsp_get_next_guid_hob(guid, hob_list);
+ if (guid_hob == NULL) {
+ return NULL;
+ } else {
+ if (len)
+ *len = get_guid_hob_data_size(guid_hob);
+
+ return get_guid_hob_data(guid_hob);
+ }
+}
+
+void *fsp_get_nvs_data(const void *hob_list, u32 *len)
+{
+ const struct efi_guid guid = FSP_NON_VOLATILE_STORAGE_HOB_GUID;
+
+ return fsp_get_guid_hob_data(hob_list, len, (struct efi_guid *)&guid);
+}
+
+void *fsp_get_bootloader_tmp_mem(const void *hob_list, u32 *len)
+{
+ const struct efi_guid guid = FSP_BOOTLOADER_TEMP_MEM_HOB_GUID;
+
+ return fsp_get_guid_hob_data(hob_list, len, (struct efi_guid *)&guid);
+}
diff --git a/arch/x86/cpu/queensbay/tnc.c b/arch/x86/cpu/queensbay/tnc.c
new file mode 100644
index 0000000000..8637cdca2d
--- /dev/null
+++ b/arch/x86/cpu/queensbay/tnc.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/pci.h>
+#include <asm/post.h>
+#include <asm/arch/tnc.h>
+#include <asm/arch/fsp/fsp_support.h>
+#include <asm/processor.h>
+
+static void unprotect_spi_flash(void)
+{
+ u32 bc;
+
+ bc = pci_read_config32(PCH_LPC_DEV, 0xd8);
+ bc |= 0x1; /* unprotect the flash */
+ pci_write_config32(PCH_LPC_DEV, 0xd8, bc);
+}
+
+int arch_cpu_init(void)
+{
+ struct pci_controller *hose;
+ int ret;
+
+ post_code(POST_CPU_INIT);
+#ifdef CONFIG_SYS_X86_TSC_TIMER
+ timer_set_base(rdtsc());
+#endif
+
+ ret = x86_cpu_init_f();
+ if (ret)
+ return ret;
+
+ ret = pci_early_init_hose(&hose);
+ if (ret)
+ return ret;
+
+ unprotect_spi_flash();
+
+ return 0;
+}
+
+int print_cpuinfo(void)
+{
+ post_code(POST_CPU_INFO);
+ return default_print_cpuinfo();
+}
+
+void reset_cpu(ulong addr)
+{
+ /* cold reset */
+ outb(0x06, PORT_RESET);
+}
+
+void board_final_cleanup(void)
+{
+ u32 status;
+
+ /* call into FspNotify */
+ debug("Calling into FSP (notify phase INIT_PHASE_BOOT): ");
+ status = fsp_notify(NULL, INIT_PHASE_BOOT);
+ if (status != FSP_SUCCESS)
+ debug("fail, error code %x\n", status);
+ else
+ debug("OK\n");
+
+ return;
+}
diff --git a/arch/x86/cpu/queensbay/tnc_car.S b/arch/x86/cpu/queensbay/tnc_car.S
new file mode 100644
index 0000000000..5e09568b85
--- /dev/null
+++ b/arch/x86/cpu/queensbay/tnc_car.S
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <config.h>
+#include <asm/post.h>
+
+.globl car_init
+car_init:
+ /*
+ * Note: ebp holds the BIST value (built-in self test) so far, but ebp
+ * will be destroyed through the FSP call, thus we have to test the
+ * BIST value here before we call into FSP.
+ */
+ test %ebp, %ebp
+ jz car_init_start
+ post_code(POST_BIST_FAILURE)
+ jmp die
+
+car_init_start:
+ post_code(POST_CAR_START)
+ lea find_fsp_header_romstack, %esp
+ jmp find_fsp_header
+
+find_fsp_header_ret:
+ /* EAX points to FSP_INFO_HEADER */
+ mov %eax, %ebp
+
+ /* sanity test */
+ cmp $CONFIG_FSP_ADDR, %eax
+ jb die
+
+ /* calculate TempRamInitEntry address */
+ mov 0x30(%ebp), %eax
+ add 0x1c(%ebp), %eax
+
+ /* call FSP TempRamInitEntry to setup temporary stack */
+ lea temp_ram_init_romstack, %esp
+ jmp *%eax
+
+temp_ram_init_ret:
+ addl $4, %esp
+ cmp $0, %eax
+ jnz car_init_fail
+
+ post_code(POST_CAR_CPU_CACHE)
+
+ /*
+ * The FSP TempRamInit initializes the ecx and edx registers to
+ * point to a temporary but writable memory range (Cache-As-RAM).
+ * ecx: the start of this temporary memory range,
+ * edx: the end of this range.
+ */
+
+ /* stack grows down from top of CAR */
+ movl %edx, %esp
+
+ /*
+ * TODO:
+ *
+ * According to FSP architecture spec, the fsp_init() will not return
+ * to its caller, instead it requires the bootloader to provide a
+ * so-called continuation function to pass into the FSP as a parameter
+ * of fsp_init, and fsp_init() will call that continuation function
+ * directly.
+ *
+ * The call to fsp_init() may need to be moved out of the car_init()
+ * to cpu_init_f() with the help of some inline assembly codes.
+ * Note there is another issue that fsp_init() will setup another stack
+ * using the fsp_init parameter stack_top after DRAM is initialized,
+ * which means any data on the previous stack (on the CAR) gets lost
+ * (ie: U-Boot global_data). FSP is supposed to support such scenario,
+ * however it does not work. This should be revisited in the future.
+ */
+ movl $CONFIG_FSP_TEMP_RAM_ADDR, %eax
+ xorl %edx, %edx
+ xorl %ecx, %ecx
+ call fsp_init
+
+.global fsp_init_done
+fsp_init_done:
+ /*
+ * We come here from FspInit with eax pointing to the HOB list.
+ * Save eax to esi temporarily.
+ */
+ movl %eax, %esi
+ /*
+ * Re-initialize the ebp (BIST) to zero, as we already reach here
+ * which means we passed BIST testing before.
+ */
+ xorl %ebp, %ebp
+ jmp car_init_ret
+
+car_init_fail:
+ post_code(POST_CAR_FAILURE)
+
+die:
+ hlt
+ jmp die
+ hlt
+
+ /*
+ * The function call before CAR initialization is tricky. It cannot
+ * be called using the 'call' instruction but only the 'jmp' with
+ * the help of a handcrafted stack in the ROM. The stack needs to
+ * contain the function return address as well as the parameters.
+ */
+ .balign 4
+find_fsp_header_romstack:
+ .long find_fsp_header_ret
+
+ .balign 4
+temp_ram_init_romstack:
+ .long temp_ram_init_ret
+ .long temp_ram_init_params
+temp_ram_init_params:
+_dt_ucode_base_size:
+ /* These next two fields are filled in by ifdtool */
+ .long 0 /* microcode base */
+ .long 0 /* microcode size */
+ .long CONFIG_SYS_MONITOR_BASE /* code region base */
+ .long CONFIG_SYS_MONITOR_LEN /* code region size */
diff --git a/arch/x86/cpu/queensbay/tnc_dram.c b/arch/x86/cpu/queensbay/tnc_dram.c
new file mode 100644
index 0000000000..df79a39dd8
--- /dev/null
+++ b/arch/x86/cpu/queensbay/tnc_dram.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/arch/fsp/fsp_support.h>
+#include <asm/e820.h>
+#include <asm/post.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int dram_init(void)
+{
+ phys_size_t ram_size = 0;
+ const struct hob_header *hdr;
+ struct hob_res_desc *res_desc;
+
+ hdr = gd->arch.hob_list;
+ while (!end_of_hob(hdr)) {
+ if (hdr->type == HOB_TYPE_RES_DESC) {
+ res_desc = (struct hob_res_desc *)hdr;
+ if (res_desc->type == RES_SYS_MEM ||
+ res_desc->type == RES_MEM_RESERVED) {
+ ram_size += res_desc->len;
+ }
+ }
+ hdr = get_next_hob(hdr);
+ }
+
+ gd->ram_size = ram_size;
+ post_code(POST_DRAM);
+
+ return 0;
+}
+
+void dram_init_banksize(void)
+{
+ gd->bd->bi_dram[0].start = 0;
+ gd->bd->bi_dram[0].size = gd->ram_size;
+}
+
+/*
+ * 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)
+{
+ return fsp_get_usable_lowmem_top(gd->arch.hob_list);
+}
+
+unsigned install_e820_map(unsigned max_entries, struct e820entry *entries)
+{
+ unsigned num_entries = 0;
+ const struct hob_header *hdr;
+ struct hob_res_desc *res_desc;
+
+ hdr = gd->arch.hob_list;
+
+ while (!end_of_hob(hdr)) {
+ if (hdr->type == HOB_TYPE_RES_DESC) {
+ res_desc = (struct hob_res_desc *)hdr;
+ entries[num_entries].addr = res_desc->phys_start;
+ entries[num_entries].size = res_desc->len;
+
+ if (res_desc->type == RES_SYS_MEM)
+ entries[num_entries].type = E820_RAM;
+ else if (res_desc->type == RES_MEM_RESERVED)
+ entries[num_entries].type = E820_RESERVED;
+ }
+ hdr = get_next_hob(hdr);
+ num_entries++;
+ }
+
+ return num_entries;
+}
diff --git a/arch/x86/cpu/queensbay/tnc_pci.c b/arch/x86/cpu/queensbay/tnc_pci.c
new file mode 100644
index 0000000000..39bff49c8d
--- /dev/null
+++ b/arch/x86/cpu/queensbay/tnc_pci.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <pci.h>
+#include <asm/pci.h>
+#include <asm/arch/fsp/fsp_support.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+void board_pci_setup_hose(struct pci_controller *hose)
+{
+ 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);
+
+ pci_set_region(hose->regions + 3,
+ 0,
+ 0,
+ gd->ram_size,
+ PCI_REGION_MEM | PCI_REGION_SYS_MEMORY);
+
+ hose->region_count = 4;
+}
+
+int board_pci_post_scan(struct pci_controller *hose)
+{
+ u32 status;
+
+ /* call into FspNotify */
+ debug("Calling into FSP (notify phase INIT_PHASE_PCI): ");
+ status = fsp_notify(NULL, INIT_PHASE_PCI);
+ if (status != FSP_SUCCESS)
+ debug("fail, error code %x\n", status);
+ else
+ debug("OK\n");
+
+ return 0;
+}
diff --git a/arch/x86/cpu/queensbay/topcliff.c b/arch/x86/cpu/queensbay/topcliff.c
new file mode 100644
index 0000000000..b01422a965
--- /dev/null
+++ b/arch/x86/cpu/queensbay/topcliff.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <malloc.h>
+#include <pci.h>
+#include <pci_ids.h>
+#include <sdhci.h>
+
+static struct pci_device_id mmc_supported[] = {
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TCF_SDIO_0 },
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TCF_SDIO_1 },
+ { }
+};
+
+int cpu_mmc_init(bd_t *bis)
+{
+ struct sdhci_host *mmc_host;
+ pci_dev_t devbusfn;
+ u32 iobase;
+ int ret;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mmc_supported); i++) {
+ devbusfn = pci_find_devices(mmc_supported, i);
+ if (devbusfn == -1)
+ return -ENODEV;
+
+ mmc_host = (struct sdhci_host *)malloc(sizeof(struct sdhci_host));
+ if (!mmc_host)
+ return -ENOMEM;
+
+ mmc_host->name = "Topcliff SDHCI";
+ pci_read_config_dword(devbusfn, PCI_BASE_ADDRESS_0, &iobase);
+ mmc_host->ioaddr = (void *)iobase;
+ mmc_host->quirks = 0;
+ ret = add_sdhci(mmc_host, 0, 0);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/arch/x86/cpu/start.S b/arch/x86/cpu/start.S
index 338bab19e4..f51f1121d0 100644
--- a/arch/x86/cpu/start.S
+++ b/arch/x86/cpu/start.S
@@ -1,5 +1,5 @@
/*
- * U-boot - x86 Startup Code
+ * U-Boot - x86 Startup Code
*
* (C) Copyright 2008-2011
* Graeme Russ, <graeme.russ@gmail.com>
@@ -13,9 +13,11 @@
#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>
+#include <generated/asm-offsets.h>
.section .text
.code32
@@ -49,6 +51,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,25 +69,46 @@ _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
.globl car_init_ret
car_init_ret:
+#ifndef CONFIG_HAVE_FSP
/*
* 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
+#else
+ /*
+ * When we get here after car_init, esp points to a temporary stack
+ * and esi holds the HOB list address returned by the FSP.
+ */
+#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 +116,43 @@ car_init_ret:
movl %esp, %edi
rep stosb
- /* Setup first parameter to setup_gdt */
+#ifdef CONFIG_HAVE_FSP
+ /* Store HOB list */
+ movl %esp, %edx
+ addl $GD_HOB_LIST, %edx
+ movl %esi, (%edx)
+#endif
+
+ /* 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
@@ -168,6 +205,14 @@ board_init_f_r_trampoline:
/* Setup global descriptor table so gd->xyz works */
call setup_gdt
+ /* Set if we need to disable CAR */
+.weak car_uninit
+ movl $car_uninit, %eax
+ cmpl $0, %eax
+ jz 1f
+
+ call car_uninit
+1:
/* Re-enter U-Boot by calling board_init_f_r */
call board_init_f_r
diff --git a/arch/x86/cpu/start16.S b/arch/x86/cpu/start16.S
index 6968fda649..9550502e9a 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 */
diff --git a/arch/x86/cpu/turbo.c b/arch/x86/cpu/turbo.c
new file mode 100644
index 0000000000..254d0de0e4
--- /dev/null
+++ b/arch/x86/cpu/turbo.c
@@ -0,0 +1,98 @@
+/*
+ * From Coreboot file of the same name
+ *
+ * Copyright (C) 2011 The Chromium Authors.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <common.h>
+#include <asm/cpu.h>
+#include <asm/msr.h>
+#include <asm/processor.h>
+#include <asm/turbo.h>
+
+#if CONFIG_CPU_INTEL_TURBO_NOT_PACKAGE_SCOPED
+static inline int get_global_turbo_state(void)
+{
+ return TURBO_UNKNOWN;
+}
+
+static inline void set_global_turbo_state(int state)
+{
+}
+#else
+static int g_turbo_state = TURBO_UNKNOWN;
+
+static inline int get_global_turbo_state(void)
+{
+ return g_turbo_state;
+}
+
+static inline void set_global_turbo_state(int state)
+{
+ g_turbo_state = state;
+}
+#endif
+
+static const char *const turbo_state_desc[] = {
+ [TURBO_UNKNOWN] = "unknown",
+ [TURBO_UNAVAILABLE] = "unavailable",
+ [TURBO_DISABLED] = "available but hidden",
+ [TURBO_ENABLED] = "available and visible"
+};
+
+/*
+ * Determine the current state of Turbo and cache it for later.
+ * Turbo is a package level config so it does not need to be
+ * enabled on every core.
+ */
+int turbo_get_state(void)
+{
+ struct cpuid_result cpuid_regs;
+ int turbo_en, turbo_cap;
+ msr_t msr;
+ int turbo_state = get_global_turbo_state();
+
+ /* Return cached state if available */
+ if (turbo_state != TURBO_UNKNOWN)
+ return turbo_state;
+
+ cpuid_regs = cpuid(CPUID_LEAF_PM);
+ turbo_cap = !!(cpuid_regs.eax & PM_CAP_TURBO_MODE);
+
+ msr = msr_read(MSR_IA32_MISC_ENABLES);
+ turbo_en = !(msr.hi & H_MISC_DISABLE_TURBO);
+
+ if (!turbo_cap && turbo_en) {
+ /* Unavailable */
+ turbo_state = TURBO_UNAVAILABLE;
+ } else if (!turbo_cap && !turbo_en) {
+ /* Available but disabled */
+ turbo_state = TURBO_DISABLED;
+ } else if (turbo_cap && turbo_en) {
+ /* Available */
+ turbo_state = TURBO_ENABLED;
+ }
+
+ set_global_turbo_state(turbo_state);
+ debug("Turbo is %s\n", turbo_state_desc[turbo_state]);
+ return turbo_state;
+}
+
+void turbo_enable(void)
+{
+ msr_t msr;
+
+ /* Only possible if turbo is available but hidden */
+ if (turbo_get_state() == TURBO_DISABLED) {
+ /* Clear Turbo Disable bit in Misc Enables */
+ msr = msr_read(MSR_IA32_MISC_ENABLES);
+ msr.hi &= ~H_MISC_DISABLE_TURBO;
+ msr_write(MSR_IA32_MISC_ENABLES, msr);
+
+ /* Update cached turbo state */
+ set_global_turbo_state(TURBO_ENABLED);
+ debug("Turbo has been enabled\n");
+ }
+}
diff --git a/arch/x86/cpu/u-boot.lds b/arch/x86/cpu/u-boot.lds
index f48bff54e1..b0d8531a60 100644
--- a/arch/x86/cpu/u-boot.lds
+++ b/arch/x86/cpu/u-boot.lds
@@ -44,7 +44,9 @@ SECTIONS
. = ALIGN(4);
__rel_dyn_start = .;
- .rel.dyn : { *(.rel.dyn) }
+ .rel.dyn : {
+ *(.rel*)
+ }
__rel_dyn_end = .;
. = ALIGN(4);
_end = .;
@@ -64,15 +66,19 @@ SECTIONS
/DISCARD/ : { *(.gnu*) }
#ifdef CONFIG_X86_RESET_VECTOR
-
/*
* The following expressions place the 16-bit Real-Mode code and
* Reset Vector at the end of the Flash ROM
*/
- . = START_16;
- .start16 : AT (CONFIG_SYS_TEXT_BASE + (CONFIG_SYS_MONITOR_LEN - RESET_SEG_SIZE + START_16)) { KEEP(*(.start16)); }
+ . = START_16 - RESET_SEG_START;
+ .start16 : AT (START_16) {
+ KEEP(*(.start16));
+ }
- . = RESET_VEC_LOC;
- .resetvec : AT (CONFIG_SYS_TEXT_BASE + (CONFIG_SYS_MONITOR_LEN - RESET_SEG_SIZE + RESET_VEC_LOC)) { KEEP(*(.resetvec)); }
+ . = RESET_VEC_LOC - RESET_SEG_START;
+ .resetvec : AT (RESET_VEC_LOC) {
+ KEEP(*(.resetvec));
+ }
#endif
+
}