summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc Jones <marc.jones@se-eng.com>2013-10-29 17:46:54 -0600
committerMarc Jones <marc.jones@se-eng.com>2013-12-04 18:45:42 +0100
commitbdafcfa55509d0cf2cbbb686411f569d56d3916c (patch)
tree8a46755738c18acbbfb2678cc6ff76439543bb30
parent54b8e7a0bba7787eca737506cb5d85bf408344d2 (diff)
downloadcoreboot-bdafcfa55509d0cf2cbbb686411f569d56d3916c.tar.xz
Add the Intel FSP 206ax CPU core support
Add support for 206ax using the Intel FSP. The FSP is different enough to warrant its own source files for now. It has different CAR code, micorcode, and FSP inclusion. It may be possible to combine this code with the mrc based solution used by the chromebooks in the future. Change-Id: I5105631af34e9c3a804ace908c4205f073abb9b4 Signed-off-by: Marc Jones <marc.jones@se-eng.com> Reviewed-on: http://review.coreboot.org/4016 Tested-by: build bot (Jenkins) Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
-rw-r--r--src/cpu/Kconfig39
-rw-r--r--src/cpu/Makefile.inc8
-rw-r--r--src/cpu/intel/Kconfig1
-rw-r--r--src/cpu/intel/Makefile.inc2
-rw-r--r--src/cpu/intel/fsp_model_206ax/Kconfig93
-rw-r--r--src/cpu/intel/fsp_model_206ax/Makefile.inc12
-rw-r--r--src/cpu/intel/fsp_model_206ax/acpi.c364
-rw-r--r--src/cpu/intel/fsp_model_206ax/acpi/cpu.asl102
-rw-r--r--src/cpu/intel/fsp_model_206ax/bootblock.c31
-rw-r--r--src/cpu/intel/fsp_model_206ax/cache_as_ram.inc292
-rw-r--r--src/cpu/intel/fsp_model_206ax/chip.h37
-rw-r--r--src/cpu/intel/fsp_model_206ax/finalize.c72
-rw-r--r--src/cpu/intel/fsp_model_206ax/microcode_blob.c24
-rw-r--r--src/cpu/intel/fsp_model_206ax/microcode_blob.h33
-rw-r--r--src/cpu/intel/fsp_model_206ax/model_206ax.h113
-rw-r--r--src/cpu/intel/fsp_model_206ax/model_206ax_init.c433
16 files changed, 1653 insertions, 3 deletions
diff --git a/src/cpu/Kconfig b/src/cpu/Kconfig
index e48fe877e0..46b6f1c765 100644
--- a/src/cpu/Kconfig
+++ b/src/cpu/Kconfig
@@ -77,10 +77,32 @@ config CPU_MICROCODE_IN_CBFS
bool
default n
+# The following options (BOARD_MICROCODE_CBFS_*) make it possible for the
+# microcode configuration to be selected in the board-level Kconfig. If
+# you want your platform's microcode to be generated during the build,
+# add "select BOARD_MICROCODE_CBFS_GENERATE" to your mainboard's Kconfig.
+# If you want to use an external microcode file, add "select
+# BOARD_MICROCODE_CBFS_EXTERNAL" instead. If neither of these statements
+# exists in your mainboard's configuration file (or if you include a
+# "select BOARD_MICROCODE_CBFS_NONE" statement), then the ROM image will
+# be built without any microcode.
+config BOARD_MICROCODE_CBFS_NONE
+ bool
+ default y
+
+config BOARD_MICROCODE_CBFS_GENERATE
+ bool
+ default n
+
+config BOARD_MICROCODE_CBFS_EXTERNAL
+ bool
+ default n
+
choice
prompt "Include CPU microcode in CBFS" if ARCH_X86
- default CPU_MICROCODE_CBFS_GENERATE if CPU_MICROCODE_IN_CBFS
- default CPU_MICROCODE_CBFS_NONE if !CPU_MICROCODE_IN_CBFS
+ default CPU_MICROCODE_CBFS_GENERATE if CPU_MICROCODE_IN_CBFS && BOARD_MICROCODE_CBFS_GENERATE
+ default CPU_MICROCODE_CBFS_EXTERNAL if CPU_MICROCODE_IN_CBFS && BOARD_MICROCODE_CBFS_EXTERNAL
+ default CPU_MICROCODE_CBFS_NONE
config CPU_MICROCODE_CBFS_GENERATE
bool "Generate from tree"
@@ -158,3 +180,16 @@ config CPU_MICROCODE_FILE
default "cpu_microcode.bin"
help
The path and filename of the file containing the CPU microcode.
+
+config CPU_MICROCODE_CBFS_LOC
+ hex "Microcode address in CBFS"
+ depends on CPU_MICROCODE_IN_CBFS
+ default 0
+
+config CPU_MICROCODE_CBFS_LEN
+ hex "Microcode length in CBFS"
+ depends on CPU_MICROCODE_IN_CBFS
+ default 0xC000
+ help
+ The microcode needs a specific length to get correctly
+ detected and loaded by all CPUs.
diff --git a/src/cpu/Makefile.inc b/src/cpu/Makefile.inc
index a860f67752..34ae53ecef 100644
--- a/src/cpu/Makefile.inc
+++ b/src/cpu/Makefile.inc
@@ -28,6 +28,12 @@ cpu_ucode_cbfs_file = $(obj)/cpu_microcode_blob.bin
cbfs_include_ucode = y
endif
+ifneq ($(CONFIG_CPU_MICROCODE_CBFS_LOC), 0)
+cpu_ucode_cbfs_offset = "-b $(CONFIG_CPU_MICROCODE_CBFS_LOC)"
+else
+cpu_ucode_cbfs_offset = "-b"
+endif
+
# In case we have more than one "source" (cough) files containing microcode, we
# link them together in one large blob, so that we get all the microcode updates
# in one file. This makes it easier for objcopy in the final step.
@@ -46,7 +52,7 @@ $(obj)/cpu_microcode_blob.bin: $(obj)/cpu_microcode_blob.o
ifeq ($(cbfs_include_ucode),y)
# Add CPU microcode to specified rom image $(1)
add-cpu-microcode-to-cbfs = \
- $(CBFSTOOL) $(1) locate -f $(cpu_ucode_cbfs_file) -n $(cpu_ucode_cbfs_name) -a 16 | xargs $(CBFSTOOL) $(1) add -n $(cpu_ucode_cbfs_name) -f $(cpu_ucode_cbfs_file) -t 0x53 -b
+ $(CBFSTOOL) $(1) locate -f $(cpu_ucode_cbfs_file) -n $(cpu_ucode_cbfs_name) -a 16 | xargs $(CBFSTOOL) $(1) add -n $(cpu_ucode_cbfs_name) -f $(cpu_ucode_cbfs_file) -t 0x53 $(cpu_ucode_cbfs_offset)
else
add-cpu-microcode-to-cbfs = true
endif
diff --git a/src/cpu/intel/Kconfig b/src/cpu/intel/Kconfig
index 2bafa498c3..6f4f561bf2 100644
--- a/src/cpu/intel/Kconfig
+++ b/src/cpu/intel/Kconfig
@@ -10,6 +10,7 @@ source src/cpu/intel/model_6fx/Kconfig
source src/cpu/intel/model_1067x/Kconfig
source src/cpu/intel/model_106cx/Kconfig
source src/cpu/intel/model_206ax/Kconfig
+source src/cpu/intel/fsp_model_206ax/Kconfig
source src/cpu/intel/model_2065x/Kconfig
source src/cpu/intel/model_f0x/Kconfig
source src/cpu/intel/model_f1x/Kconfig
diff --git a/src/cpu/intel/Makefile.inc b/src/cpu/intel/Makefile.inc
index 54624fb8c7..964369ddd3 100644
--- a/src/cpu/intel/Makefile.inc
+++ b/src/cpu/intel/Makefile.inc
@@ -19,6 +19,8 @@ subdirs-$(CONFIG_NORTHBRIDGE_INTEL_NEHALEM) += model_2065x
subdirs-$(CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE) += model_206ax
subdirs-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += model_206ax
subdirs-$(CONFIG_NORTHBRIDGE_INTEL_HASWELL) += haswell
+subdirs-$(CONFIG_NORTHBRIDGE_INTEL_FSP_SANDYBRIDGE) += fsp_model_206ax
+subdirs-$(CONFIG_NORTHBRIDGE_INTEL_FSP_IVYBRIDGE) += fsp_model_206ax
subdirs-$(CONFIG_CPU_INTEL_SLOT_2) += slot_2
subdirs-$(CONFIG_CPU_INTEL_SLOT_1) += slot_1
subdirs-$(CONFIG_CPU_INTEL_SOCKET_LGA771) += socket_LGA771
diff --git a/src/cpu/intel/fsp_model_206ax/Kconfig b/src/cpu/intel/fsp_model_206ax/Kconfig
new file mode 100644
index 0000000000..406f8a5c95
--- /dev/null
+++ b/src/cpu/intel/fsp_model_206ax/Kconfig
@@ -0,0 +1,93 @@
+##
+## This file is part of the coreboot project.
+##
+## Copyright (C) 2013 Sage Electronic Engineering, LLC.
+##
+## 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
+##
+
+
+config CPU_INTEL_FSP_MODEL_206AX
+ bool
+
+config CPU_INTEL_FSP_MODEL_306AX
+ bool
+
+if CPU_INTEL_FSP_MODEL_206AX || CPU_INTEL_FSP_MODEL_306AX
+
+config CPU_SPECIFIC_OPTIONS
+ def_bool y
+ select SMP
+ select SSE2
+ select UDELAY_LAPIC
+ select SMM_TSEG
+ select CPU_MICROCODE_IN_CBFS if HAVE_FSP_BIN
+ select BOARD_MICROCODE_CBFS_GENERATE
+ select TSC_SYNC_MFENCE
+
+config BOOTBLOCK_CPU_INIT
+ string
+ default "cpu/intel/fsp_model_206ax/bootblock.c"
+
+config SERIAL_CPU_INIT
+ bool
+ default n
+
+config SMM_TSEG_SIZE
+ hex
+ default 0x800000
+
+config ENABLE_VMX
+ bool "Enable VMX for virtualization"
+ default n
+
+config CPU_MICROCODE_CBFS_LOC
+ hex
+ depends on CPU_MICROCODE_IN_CBFS
+ default 0xfff70000
+
+config CPU_MICROCODE_CBFS_LEN
+ hex
+ depends on CPU_MICROCODE_IN_CBFS
+ default 0xC000 if CPU_INTEL_FSP_MODEL_306AX
+ default 0x2800 if CPU_INTEL_FSP_MODEL_206AX
+
+config MICROCODE_INCLUDE_PATH
+ string "Location of the intel microcode patches"
+ default "../intel/cpu/ivybridge/microcode" if CPU_INTEL_FSP_MODEL_306AX
+ default "../intel/cpu/sandybridge/microcode" if CPU_INTEL_FSP_MODEL_206AX
+
+config FSP_IMAGE_ID_DWORD0
+ hex
+ default 0x2D325453 if CPU_INTEL_FSP_MODEL_306AX && SOUTHBRIDGE_INTEL_FSP_I89XX
+ default 0x2D324343 if CPU_INTEL_FSP_MODEL_306AX && SOUTHBRIDGE_INTEL_FSP_BD82X6X
+ help
+ The FSP Image ID is different for each platform's FSP and can be used to
+ verify that the right FSP binary is loaded.
+ For the ivybridge/89xx FSP, the Image Id will be "ST2-FSP\0",
+ for ivybridge/bd82x6x FSPs, the Image Id will be "CC2-FSP\0",
+ This dword holds the first 4 bytes of the string, as
+ a hex value.
+
+config FSP_IMAGE_ID_DWORD1
+ hex
+ default 0x00505346
+ help
+ For the ivybridge/I89xx FSP, the Image Id will be "ST2-FSP\0",
+ for ivybridge/bd82x6x FSPs, the Image Id will be "CC2-FSP\0",
+ This dword holds the second 4 bytes of the string, as
+ a hex value. Since the strings use the same second dword,
+ no additional logic is needed.
+
+endif
diff --git a/src/cpu/intel/fsp_model_206ax/Makefile.inc b/src/cpu/intel/fsp_model_206ax/Makefile.inc
new file mode 100644
index 0000000000..1ea9c2aa49
--- /dev/null
+++ b/src/cpu/intel/fsp_model_206ax/Makefile.inc
@@ -0,0 +1,12 @@
+ramstage-y += model_206ax_init.c
+subdirs-y += ../../x86/name
+
+ramstage-$(CONFIG_GENERATE_ACPI_TABLES) += acpi.c
+
+smm-$(CONFIG_HAVE_SMI_HANDLER) += finalize.c
+
+cpu_microcode-$(CONFIG_CPU_MICROCODE_CBFS_GENERATE) += microcode_blob.c
+
+cpu_incs += $(src)/cpu/intel/fsp_model_206ax/cache_as_ram.inc
+
+CC := $(CC) -I$(CONFIG_MICROCODE_INCLUDE_PATH)
diff --git a/src/cpu/intel/fsp_model_206ax/acpi.c b/src/cpu/intel/fsp_model_206ax/acpi.c
new file mode 100644
index 0000000000..47e651f2ee
--- /dev/null
+++ b/src/cpu/intel/fsp_model_206ax/acpi.c
@@ -0,0 +1,364 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2009 coresystems GmbH
+ * Copyright (C) 2011 The Chromium OS 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
+ */
+
+#include <types.h>
+#include <console/console.h>
+#include <arch/acpi.h>
+#include <arch/acpigen.h>
+#include <arch/cpu.h>
+#include <cpu/x86/msr.h>
+#include <cpu/intel/speedstep.h>
+#include <cpu/intel/turbo.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include "model_206ax.h"
+#include "chip.h"
+
+static int get_cores_per_package(void)
+{
+ struct cpuinfo_x86 c;
+ struct cpuid_result result;
+ int cores = 1;
+
+ get_fms(&c, cpuid_eax(1));
+ if (c.x86 != 6)
+ return 1;
+
+ result = cpuid_ext(0xb, 1);
+ cores = result.ebx & 0xff;
+
+ return cores;
+}
+
+static int generate_cstate_entries(acpi_cstate_t *cstates,
+ int c1, int c2, int c3)
+{
+ int length, cstate_count = 0;
+
+ /* Count number of active C-states */
+ if (c1 > 0)
+ ++cstate_count;
+ if (c2 > 0)
+ ++cstate_count;
+ if (c3 > 0)
+ ++cstate_count;
+ if (!cstate_count)
+ return 0;
+
+ length = acpigen_write_package(cstate_count + 1);
+ length += acpigen_write_byte(cstate_count);
+
+ /* Add an entry if the level is enabled */
+ if (c1 > 0) {
+ cstates[c1].ctype = 1;
+ length += acpigen_write_CST_package_entry(&cstates[c1]);
+ }
+ if (c2 > 0) {
+ cstates[c2].ctype = 2;
+ length += acpigen_write_CST_package_entry(&cstates[c2]);
+ }
+ if (c3 > 0) {
+ cstates[c3].ctype = 3;
+ length += acpigen_write_CST_package_entry(&cstates[c3]);
+ }
+
+ acpigen_patch_len(length - 1);
+ return length;
+}
+
+static int generate_C_state_entries(void)
+{
+ struct cpu_info *info;
+ struct cpu_driver *cpu;
+ int len, lenif;
+ device_t lapic;
+ struct cpu_intel_fsp_model_206ax_config *conf = NULL;
+
+ /* Find the SpeedStep CPU in the device tree using magic APIC ID */
+ lapic = dev_find_lapic(SPEEDSTEP_APIC_MAGIC);
+ if (!lapic)
+ return 0;
+ conf = lapic->chip_info;
+ if (!conf)
+ return 0;
+
+ /* Find CPU map of supported C-states */
+ info = cpu_info();
+ if (!info)
+ return 0;
+ cpu = find_cpu_driver(info->cpu);
+ if (!cpu || !cpu->cstates)
+ return 0;
+
+ len = acpigen_emit_byte(0x14); /* MethodOp */
+ len += acpigen_write_len_f(); /* PkgLength */
+ len += acpigen_emit_namestring("_CST");
+ len += acpigen_emit_byte(0x00); /* No Arguments */
+
+ /* If running on AC power */
+ len += acpigen_emit_byte(0xa0); /* IfOp */
+ lenif = acpigen_write_len_f(); /* PkgLength */
+ lenif += acpigen_emit_namestring("PWRS");
+ lenif += acpigen_emit_byte(0xa4); /* ReturnOp */
+ lenif += generate_cstate_entries(cpu->cstates, conf->c1_acpower,
+ conf->c2_acpower, conf->c3_acpower);
+ acpigen_patch_len(lenif - 1);
+ len += lenif;
+
+ /* Else on battery power */
+ len += acpigen_emit_byte(0xa4); /* ReturnOp */
+ len += generate_cstate_entries(cpu->cstates, conf->c1_battery,
+ conf->c2_battery, conf->c3_battery);
+ acpigen_patch_len(len - 1);
+ return len;
+}
+
+static acpi_tstate_t tss_table_fine[] = {
+ { 100, 1000, 0, 0x00, 0 },
+ { 94, 940, 0, 0x1f, 0 },
+ { 88, 880, 0, 0x1e, 0 },
+ { 82, 820, 0, 0x1d, 0 },
+ { 75, 760, 0, 0x1c, 0 },
+ { 69, 700, 0, 0x1b, 0 },
+ { 63, 640, 0, 0x1a, 0 },
+ { 57, 580, 0, 0x19, 0 },
+ { 50, 520, 0, 0x18, 0 },
+ { 44, 460, 0, 0x17, 0 },
+ { 38, 400, 0, 0x16, 0 },
+ { 32, 340, 0, 0x15, 0 },
+ { 25, 280, 0, 0x14, 0 },
+ { 19, 220, 0, 0x13, 0 },
+ { 13, 160, 0, 0x12, 0 },
+};
+
+static acpi_tstate_t tss_table_coarse[] = {
+ { 100, 1000, 0, 0x00, 0 },
+ { 88, 875, 0, 0x1f, 0 },
+ { 75, 750, 0, 0x1e, 0 },
+ { 63, 625, 0, 0x1d, 0 },
+ { 50, 500, 0, 0x1c, 0 },
+ { 38, 375, 0, 0x1b, 0 },
+ { 25, 250, 0, 0x1a, 0 },
+ { 13, 125, 0, 0x19, 0 },
+};
+
+static int generate_T_state_entries(int core, int cores_per_package)
+{
+ int len;
+
+ /* Indicate SW_ALL coordination for T-states */
+ len = acpigen_write_TSD_package(core, cores_per_package, SW_ALL);
+
+ /* Indicate FFixedHW so OS will use MSR */
+ len += acpigen_write_empty_PTC();
+
+ /* Set a T-state limit that can be modified in NVS */
+ len += acpigen_write_TPC("\\TLVL");
+
+ /*
+ * CPUID.(EAX=6):EAX[5] indicates support
+ * for extended throttle levels.
+ */
+ if (cpuid_eax(6) & (1 << 5))
+ len += acpigen_write_TSS_package(
+ ARRAY_SIZE(tss_table_fine), tss_table_fine);
+ else
+ len += acpigen_write_TSS_package(
+ ARRAY_SIZE(tss_table_coarse), tss_table_coarse);
+
+ return len;
+}
+
+static int calculate_power(int tdp, int p1_ratio, int ratio)
+{
+ u32 m;
+ u32 power;
+
+ /*
+ * M = ((1.1 - ((p1_ratio - ratio) * 0.00625)) / 1.1) ^ 2
+ *
+ * Power = (ratio / p1_ratio) * m * tdp
+ */
+
+ m = (110000 - ((p1_ratio - ratio) * 625)) / 11;
+ m = (m * m) / 1000;
+
+ power = ((ratio * 100000 / p1_ratio) / 100);
+ power *= (m / 100) * (tdp / 1000);
+ power /= 1000;
+
+ return (int)power;
+}
+
+static int generate_P_state_entries(int core, int cores_per_package)
+{
+ int len, len_pss;
+ int ratio_min, ratio_max, ratio_turbo, ratio_step;
+ int coord_type, power_max, power_unit, num_entries;
+ int ratio, power, clock, clock_max;
+ msr_t msr;
+
+ /* Determine P-state coordination type from MISC_PWR_MGMT[0] */
+ msr = rdmsr(MSR_MISC_PWR_MGMT);
+ if (msr.lo & MISC_PWR_MGMT_EIST_HW_DIS)
+ coord_type = SW_ANY;
+ else
+ coord_type = HW_ALL;
+
+ /* Get bus ratio limits and calculate clock speeds */
+ msr = rdmsr(MSR_PLATFORM_INFO);
+ ratio_min = (msr.hi >> (40-32)) & 0xff; /* Max Efficiency Ratio */
+
+ /* Determine if this CPU has configurable TDP */
+ if (cpu_config_tdp_levels()) {
+ /* Set max ratio to nominal TDP ratio */
+ msr = rdmsr(MSR_CONFIG_TDP_NOMINAL);
+ ratio_max = msr.lo & 0xff;
+ } else {
+ /* Max Non-Turbo Ratio */
+ ratio_max = (msr.lo >> 8) & 0xff;
+ }
+ clock_max = ratio_max * SANDYBRIDGE_BCLK;
+
+ /* Calculate CPU TDP in mW */
+ msr = rdmsr(MSR_PKG_POWER_SKU_UNIT);
+ power_unit = 2 << ((msr.lo & 0xf) - 1);
+ msr = rdmsr(MSR_PKG_POWER_SKU);
+ power_max = ((msr.lo & 0x7fff) / power_unit) * 1000;
+
+ /* Write _PCT indicating use of FFixedHW */
+ len = acpigen_write_empty_PCT();
+
+ /* Write _PPC with no limit on supported P-state */
+ len += acpigen_write_PPC_NVS();
+
+ /* Write PSD indicating configured coordination type */
+ len += acpigen_write_PSD_package(core, cores_per_package, coord_type);
+
+ /* Add P-state entries in _PSS table */
+ len += acpigen_write_name("_PSS");
+
+ /* Determine ratio points */
+ ratio_step = PSS_RATIO_STEP;
+ num_entries = (ratio_max - ratio_min) / ratio_step;
+ while (num_entries > PSS_MAX_ENTRIES-1) {
+ ratio_step <<= 1;
+ num_entries >>= 1;
+ }
+
+ /* P[T] is Turbo state if enabled */
+ if (get_turbo_state() == TURBO_ENABLED) {
+ /* _PSS package count including Turbo */
+ len_pss = acpigen_write_package(num_entries + 2);
+
+ msr = rdmsr(MSR_TURBO_RATIO_LIMIT);
+ ratio_turbo = msr.lo & 0xff;
+
+ /* Add entry for Turbo ratio */
+ len_pss += acpigen_write_PSS_package(
+ clock_max + 1, /*MHz*/
+ power_max, /*mW*/
+ PSS_LATENCY_TRANSITION, /*lat1*/
+ PSS_LATENCY_BUSMASTER, /*lat2*/
+ ratio_turbo << 8, /*control*/
+ ratio_turbo << 8); /*status*/
+ } else {
+ /* _PSS package count without Turbo */
+ len_pss = acpigen_write_package(num_entries + 1);
+ }
+
+ /* First regular entry is max non-turbo ratio */
+ len_pss += acpigen_write_PSS_package(
+ clock_max, /*MHz*/
+ power_max, /*mW*/
+ PSS_LATENCY_TRANSITION, /*lat1*/
+ PSS_LATENCY_BUSMASTER, /*lat2*/
+ ratio_max << 8, /*control*/
+ ratio_max << 8); /*status*/
+
+ /* Generate the remaining entries */
+ for (ratio = ratio_min + ((num_entries - 1) * ratio_step);
+ ratio >= ratio_min; ratio -= ratio_step) {
+
+ /* Calculate power at this ratio */
+ power = calculate_power(power_max, ratio_max, ratio);
+ clock = ratio * SANDYBRIDGE_BCLK;
+
+ len_pss += acpigen_write_PSS_package(
+ clock, /*MHz*/
+ power, /*mW*/
+ PSS_LATENCY_TRANSITION, /*lat1*/
+ PSS_LATENCY_BUSMASTER, /*lat2*/
+ ratio << 8, /*control*/
+ ratio << 8); /*status*/
+ }
+
+ /* Fix package length */
+ len_pss--;
+ acpigen_patch_len(len_pss);
+
+ return len + len_pss;
+}
+
+void generate_cpu_entries(void)
+{
+ int len_pr;
+ int coreID, cpuID, pcontrol_blk = PMB0_BASE, plen = 6;
+ int totalcores = dev_count_cpu();
+ int cores_per_package = get_cores_per_package();
+ int numcpus = totalcores/cores_per_package;
+
+ printk(BIOS_DEBUG, "Found %d CPU(s) with %d core(s) each.\n",
+ numcpus, cores_per_package);
+
+ for (cpuID=1; cpuID <=numcpus; cpuID++) {
+ for (coreID=1; coreID<=cores_per_package; coreID++) {
+ if (coreID>1) {
+ pcontrol_blk = 0;
+ plen = 0;
+ }
+
+ /* Generate processor \_PR.CPUx */
+ len_pr = acpigen_write_processor(
+ (cpuID-1)*cores_per_package+coreID-1,
+ pcontrol_blk, plen);
+
+ /* Generate P-state tables */
+ len_pr += generate_P_state_entries(
+ cpuID-1, cores_per_package);
+
+ /* Generate C-state tables */
+ len_pr += generate_C_state_entries();
+
+ /* Generate T-state tables */
+ len_pr += generate_T_state_entries(
+ cpuID-1, cores_per_package);
+
+ len_pr--;
+ acpigen_patch_len(len_pr);
+ }
+ }
+}
+
+struct chip_operations cpu_intel_model_206ax_ops = {
+ CHIP_NAME("Intel SandyBridge/IvyBridge CPU")
+};
diff --git a/src/cpu/intel/fsp_model_206ax/acpi/cpu.asl b/src/cpu/intel/fsp_model_206ax/acpi/cpu.asl
new file mode 100644
index 0000000000..558a9d3663
--- /dev/null
+++ b/src/cpu/intel/fsp_model_206ax/acpi/cpu.asl
@@ -0,0 +1,102 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * 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
+ */
+
+/* These devices are created at runtime */
+External (\_PR.CPU0, DeviceObj)
+External (\_PR.CPU1, DeviceObj)
+External (\_PR.CPU2, DeviceObj)
+External (\_PR.CPU3, DeviceObj)
+External (\_PR.CPU4, DeviceObj)
+External (\_PR.CPU5, DeviceObj)
+External (\_PR.CPU6, DeviceObj)
+External (\_PR.CPU7, DeviceObj)
+
+/* Notify OS to re-read CPU tables, assuming ^2 CPU count */
+Method (PNOT)
+{
+ If (LGreaterEqual (\PCNT, 2)) {
+ Notify (\_PR.CPU0, 0x81) // _CST
+ Notify (\_PR.CPU1, 0x81) // _CST
+ }
+ If (LGreaterEqual (\PCNT, 4)) {
+ Notify (\_PR.CPU2, 0x81) // _CST
+ Notify (\_PR.CPU3, 0x81) // _CST
+ }
+ If (LGreaterEqual (\PCNT, 8)) {
+ Notify (\_PR.CPU4, 0x81) // _CST
+ Notify (\_PR.CPU5, 0x81) // _CST
+ Notify (\_PR.CPU6, 0x81) // _CST
+ Notify (\_PR.CPU7, 0x81) // _CST
+ }
+}
+
+/* Notify OS to re-read CPU _PPC limit, assuming ^2 CPU count */
+Method (PPCN)
+{
+ If (LGreaterEqual (\PCNT, 2)) {
+ Notify (\_PR.CPU0, 0x80) // _PPC
+ Notify (\_PR.CPU1, 0x80) // _PPC
+ }
+ If (LGreaterEqual (\PCNT, 4)) {
+ Notify (\_PR.CPU2, 0x80) // _PPC
+ Notify (\_PR.CPU3, 0x80) // _PPC
+ }
+ If (LGreaterEqual (\PCNT, 8)) {
+ Notify (\_PR.CPU4, 0x80) // _PPC
+ Notify (\_PR.CPU5, 0x80) // _PPC
+ Notify (\_PR.CPU6, 0x80) // _PPC
+ Notify (\_PR.CPU7, 0x80) // _PPC
+ }
+}
+
+/* Notify OS to re-read Throttle Limit tables, assuming ^2 CPU count */
+Method (TNOT)
+{
+ If (LGreaterEqual (\PCNT, 2)) {
+ Notify (\_PR.CPU0, 0x82) // _TPC
+ Notify (\_PR.CPU1, 0x82) // _TPC
+ }
+ If (LGreaterEqual (\PCNT, 4)) {
+ Notify (\_PR.CPU2, 0x82) // _TPC
+ Notify (\_PR.CPU3, 0x82) // _TPC
+ }
+ If (LGreaterEqual (\PCNT, 8)) {
+ Notify (\_PR.CPU4, 0x82) // _TPC
+ Notify (\_PR.CPU5, 0x82) // _TPC
+ Notify (\_PR.CPU6, 0x82) // _TPC
+ Notify (\_PR.CPU7, 0x82) // _TPC
+ }
+}
+
+/* Return a package containing enabled processor entries */
+Method (PPKG)
+{
+ If (LGreaterEqual (\PCNT, 8)) {
+ Return (Package() {\_PR.CPU0, \_PR.CPU1, \_PR.CPU2, \_PR.CPU3,
+ \_PR.CPU4, \_PR.CPU5, \_PR.CPU6, \_PR.CPU7})
+ } ElseIf (LGreaterEqual (\PCNT, 4)) {
+ Return (Package() {\_PR.CPU0, \_PR.CPU1, \_PR.CPU2, \_PR.CPU3})
+ } ElseIf (LGreaterEqual (\PCNT, 2)) {
+ Return (Package() {\_PR.CPU0, \_PR.CPU1})
+ } Else {
+ Return (Package() {\_PR.CPU0})
+ }
+}
diff --git a/src/cpu/intel/fsp_model_206ax/bootblock.c b/src/cpu/intel/fsp_model_206ax/bootblock.c
new file mode 100644
index 0000000000..e57217132d
--- /dev/null
+++ b/src/cpu/intel/fsp_model_206ax/bootblock.c
@@ -0,0 +1,31 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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
+ */
+
+#include <stdint.h>
+#include <arch/cpu.h>
+#include <cpu/x86/cache.h>
+#include <cpu/x86/msr.h>
+#include <cpu/x86/mtrr.h>
+#include <arch/io.h>
+
+#include "model_206ax.h"
+
+static void bootblock_cpu_init(void)
+{
+}
diff --git a/src/cpu/intel/fsp_model_206ax/cache_as_ram.inc b/src/cpu/intel/fsp_model_206ax/cache_as_ram.inc
new file mode 100644
index 0000000000..a8d7cc54cb
--- /dev/null
+++ b/src/cpu/intel/fsp_model_206ax/cache_as_ram.inc
@@ -0,0 +1,292 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2000,2007 Ronald G. Minnich <rminnich@gmail.com>
+ * Copyright (C) 2007-2008 coresystems GmbH
+ * Copyright (C) 2013 Sage Electronic Engineering, LLC.
+ *
+ * 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
+ */
+
+#include <cpu/x86/stack.h>
+#include <cpu/x86/mtrr.h>
+#include <cpu/x86/cache.h>
+#include <cpu/x86/post_code.h>
+#include <cbmem.h>
+
+#define CACHE_AS_RAM_SIZE CONFIG_DCACHE_RAM_SIZE
+#define CACHE_AS_RAM_BASE CONFIG_DCACHE_RAM_BASE
+
+#define CPU_PHYSMASK_HI (1 << (CONFIG_CPU_ADDR_BITS - 32) - 1)
+#define LHLT_DELAY 0x50000 /* delay between post codes on FSP failure */
+#define NoEvictMod_MSR 0x2e0
+
+cache_as_ram:
+ post_code(0x20)
+
+ /* Send INIT IPI to all excluding ourself. */
+ movl $0x000C4500, %eax
+ movl $0xFEE00300, %esi
+ movl %eax, (%esi)
+
+ /* All CPUs need to be in Wait for SIPI state */
+wait_for_sipi:
+ movl (%esi), %eax
+ bt $12, %eax
+ jc wait_for_sipi
+
+ post_code(0x21)
+ /* 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
+
+ /* Init floating point */
+ emms
+ fninit
+
+ /*
+ * Find the FSP binary in cbfs.
+ * Make a fake stack that has the return value back to this code.
+ */
+ lea fake_fsp_stack, %esp
+ jmp find_fsp
+find_fsp_ret:
+ /* Save the FSP location */
+ mov %eax, %ebp
+ cmp $CONFIG_FSP_LOC, %eax
+ jb halt1
+
+ post_code(0x22)
+
+ /* Calculate entry into FSP */
+ mov 0x30(%ebp), %eax /* Load TempRamInitEntry */
+ add 0x1c(%ebp), %eax /* add in the offset for the FSP base address */
+
+ /*
+ * Pass early init variables on a fake stack (no memory yet)
+ * as well as the return location
+ */
+ lea CAR_init_stack, %esp
+
+ /* call FSP binary to setup temporary stack */
+ jmp *%eax
+
+CAR_init_done:
+ addl $4, %esp
+ cmp $0, %eax
+ jne halt2
+
+ /* Save FSP_INFO_HEADER location in ebx */
+ mov %ebp, %ebx
+
+ /*
+ * setup bootloader stack
+ * ecx: stack base
+ * edx: stack top
+ */
+ lea -4(%edx), %esp
+ movl %esp, %ebp
+
+before_romstage:
+ post_code(0x23)
+
+ /* Call romstage.c main function. */
+ pushl %ebx
+ call main
+
+romstage_main_return:
+ post_code(0x2f)
+
+/* Disable cache. */
+ movl %cr0, %eax
+ orl $CR0_CacheDisable, %eax
+ movl %eax, %cr0
+
+ post_code(0x31)
+
+ /* Disable MTRR. */
+ movl $MTRRdefType_MSR, %ecx
+ rdmsr
+ andl $(~MTRRdefTypeEn), %eax
+ wrmsr
+
+ post_code(0x32)
+
+ /* 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(0x33)
+
+ /* Enable cache. */
+ movl %cr0, %eax
+ andl $~(CR0_CacheDisable | CR0_NoWriteThrough), %eax
+ movl %eax, %cr0
+
+ post_code(0x36)
+
+ /* Disable cache. */
+ movl %cr0, %eax
+ orl $CR0_CacheDisable, %eax
+ movl %eax, %cr0
+
+ post_code(0x38)
+
+ /* Enable Write Back and Speculative Reads for the first MB
+ * and coreboot_ram.
+ */
+ movl $MTRRphysBase_MSR(0), %ecx
+ movl $(0x00000000 | MTRR_TYPE_WRBACK), %eax
+ xorl %edx, %edx
+ wrmsr
+ movl $MTRRphysMask_MSR(0), %ecx
+ movl $(~(CONFIG_RAMTOP - 1) | MTRRphysMaskValid), %eax
+ movl $CPU_PHYSMASK_HI, %edx // 36bit address space
+ wrmsr
+
+#if CONFIG_CACHE_ROM_SIZE
+ /* Enable Caching and speculative Reads for the
+ * complete ROM now that we actually have RAM.
+ */
+ movl $MTRRphysBase_MSR(1), %ecx
+ movl $(CACHE_ROM_BASE | MTRR_TYPE_WRPROT), %eax
+ xorl %edx, %edx
+ wrmsr
+ movl $MTRRphysMask_MSR(1), %ecx
+ movl $(~(CONFIG_CACHE_ROM_SIZE - 1) | MTRRphysMaskValid), %eax
+ movl $CPU_PHYSMASK_HI, %edx
+ wrmsr
+#endif
+
+ post_code(0x39)
+
+ /* And enable cache again after setting MTRRs. */
+ movl %cr0, %eax
+ andl $~(CR0_CacheDisable | CR0_NoWriteThrough), %eax
+ movl %eax, %cr0
+
+ post_code(0x3a)
+
+ /* Enable MTRR. */
+ movl $MTRRdefType_MSR, %ecx
+ rdmsr
+ orl $MTRRdefTypeEn, %eax
+ wrmsr
+
+ post_code(0x3d)
+
+ /* Clear boot_complete flag. */
+ xorl %ebp, %ebp
+__main:
+ post_code(POST_PREPARE_RAMSTAGE)
+ cld /* Clear direction flag. */
+
+ movl %ebp, %esi
+
+ movl $ROMSTAGE_STACK, %esp
+ movl %esp, %ebp
+ pushl %esi
+ call copy_and_run
+
+halt1:
+ /*
+ * Failures for postcode 0xBA - failed in find_fsp()
+ *
+ * Values are:
+ * 0x01 - FV signature, "_FVH" not present
+ * 0x02 - FFS GUID not present
+ * 0x03 - FSP INFO Header not found
+ * 0x04 - ImageBase does not equal CONFIG_FSP_LOC - Is the FSP rebased to
+ * a different location, or does it need to be?
+ * 0x05 - FSP INFO Header signature "FSPH" not found
+ * 0x06 - FSP Image ID is not the expected ID.
+ * For ivybridge_bd82x6x, the ID is expected to be 'CC2-FSP\0'
+ * For ivybridge_i89xx, the ID is expected to be 'ST2-FSP\0'
+ *
+ */
+ movb $0xBA, %ah
+ jmp .Lhlt
+
+halt2:
+ /*
+ * Failures for postcode 0xBB - failed in the FSP:
+ *
+ * 0x00 - FSP_SUCCESS: Temp RAM was initialized successfully.
+ * 0x02 - FSP_INVALID_PARAMETER: Input parameters are invalid.
+ * 0x0E - FSP_NOT_FOUND: No valid microcode was found in the microcode region.
+ * 0x03 - FSP_UNSUPPORTED: The FSP calling conditions were not met.
+ * 0x07 - FSP_DEVICE_ERROR: Temp RAM initialization failed
+ * 0x14 - FSP_ALREADY_STARTED: Temp RAM initialization has been invoked
+ */
+ movb $0xBB, %ah
+
+.Lhlt:
+ xchg %al, %ah
+#if CONFIG_IO_POST
+ outb %al, $CONFIG_IO_POST_PORT
+#else
+ post_code(POST_DEAD_CODE)
+#endif
+ movl $LHLT_DELAY, %ecx
+.Lhlt_Delay:
+ outb %al, $0xED
+ loop .Lhlt_Delay
+ jmp .Lhlt
+
+ .align 4
+fake_fsp_stack:
+ .long find_fsp_ret
+
+CAR_init_params:
+ .long CONFIG_CPU_MICROCODE_CBFS_LOC
+ .long CONFIG_CPU_MICROCODE_CBFS_LEN /* Microcode Length */
+ .long 0xFFFFFFFF - CONFIG_ROM_SIZE + 1 /* Firmware Location */
+ .long CONFIG_ROM_SIZE /* Total Firmware Length */
+
+CAR_init_stack:
+ .long CAR_init_done
+ .long CAR_init_params
+
+mtrr_table:
+ /* Fixed MTRRs */
+ .word 0x250, 0x258, 0x259
+ .word 0x268, 0x269, 0x26A
+ .word 0x26B, 0x26C, 0x26D
+ .word 0x26E, 0x26F
+ /* Variable MTRRs */
+ .word 0x200, 0x201, 0x202, 0x203
+ .word 0x204, 0x205, 0x206, 0x207
+ .word 0x208, 0x209, 0x20A, 0x20B
+ .word 0x20C, 0x20D, 0x20E, 0x20F
+/* .word 0x210, 0x211, 0x212, 0x213 */
+mtrr_table_end:
+
diff --git a/src/cpu/intel/fsp_model_206ax/chip.h b/src/cpu/intel/fsp_model_206ax/chip.h
new file mode 100644
index 0000000000..afa5774fa4
--- /dev/null
+++ b/src/cpu/intel/fsp_model_206ax/chip.h
@@ -0,0 +1,37 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011 The Chromium OS 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
+ */
+
+/* Magic value used to locate this chip in the device tree */
+#define SPEEDSTEP_APIC_MAGIC 0xACAC
+
+struct cpu_intel_fsp_model_206ax_config {
+ u8 disable_acpi; /* Do not generate CPU ACPI tables */
+
+ u8 pstate_coord_type; /* Processor Coordination Type */
+
+ int c1_battery; /* ACPI C1 on Battery Power */
+ int c2_battery; /* ACPI C2 on Battery Power */
+ int c3_battery; /* ACPI C3 on Battery Power */
+
+ int c1_acpower; /* ACPI C1 on AC Power */
+ int c2_acpower; /* ACPI C2 on AC Power */
+ int c3_acpower; /* ACPI C3 on AC Power */
+
+ int tcc_offset; /* TCC Activation Offset */
+};
diff --git a/src/cpu/intel/fsp_model_206ax/finalize.c b/src/cpu/intel/fsp_model_206ax/finalize.c
new file mode 100644
index 0000000000..4ed5d1e5f8
--- /dev/null
+++ b/src/cpu/intel/fsp_model_206ax/finalize.c
@@ -0,0 +1,72 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2012 The Chromium OS 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
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <cpu/cpu.h>
+#include <cpu/x86/msr.h>
+#include "model_206ax.h"
+
+static void msr_set_bit(unsigned reg, unsigned bit)
+{
+ msr_t msr = rdmsr(reg);
+
+ if (bit < 32) {
+ if (msr.lo & (1 << bit))
+ return;
+ msr.lo |= 1 << bit;
+ } else {
+ if (msr.hi & (1 << (bit - 32)))
+ return;
+ msr.hi |= 1 << (bit - 32);
+ }
+
+ wrmsr(reg, msr);
+}
+
+void intel_model_206ax_finalize_smm(void)
+{
+ msr_set_bit(MSR_PMG_CST_CONFIG_CONTROL, 15);
+
+ /* Lock AES-NI only if supported */
+ if (cpuid_ecx(1) & (1 << 25))
+ msr_set_bit(MSR_FEATURE_CONFIG, 0);
+
+#ifdef LOCK_POWER_CONTROL_REGISTERS
+ /*
+ * Lock the power control registers.
+ *
+ * These registers can be left unlocked if modifying power
+ * limits from the OS is desirable. Modifying power limits
+ * from the OS can be especially useful for experimentation
+ * during early phases of system bringup while the thermal
+ * power envelope is being proven.
+ */
+
+ msr_set_bit(MSR_PP0_CURRENT_CONFIG, 31);
+ msr_set_bit(MSR_PP1_CURRENT_CONFIG, 31);
+ msr_set_bit(MSR_PKG_POWER_LIMIT, 63);
+ msr_set_bit(MSR_PP0_POWER_LIMIT, 31);
+ msr_set_bit(MSR_PP1_POWER_LIMIT, 31);
+#endif
+
+ msr_set_bit(MSR_MISC_PWR_MGMT, 22);
+ msr_set_bit(MSR_LT_LOCK_MEMORY, 0);
+}
diff --git a/src/cpu/intel/fsp_model_206ax/microcode_blob.c b/src/cpu/intel/fsp_model_206ax/microcode_blob.c
new file mode 100644
index 0000000000..8bd790ade4
--- /dev/null
+++ b/src/cpu/intel/fsp_model_206ax/microcode_blob.c
@@ -0,0 +1,24 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2012 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
+ */
+
+unsigned microcode[] = {
+#if IS_ENABLED(CONFIG_CPU_MICROCODE_IN_CBFS)
+#include "microcode_blob.h"
+#endif
+};
diff --git a/src/cpu/intel/fsp_model_206ax/microcode_blob.h b/src/cpu/intel/fsp_model_206ax/microcode_blob.h
new file mode 100644
index 0000000000..e56bef17a6
--- /dev/null
+++ b/src/cpu/intel/fsp_model_206ax/microcode_blob.h
@@ -0,0 +1,33 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011 Google Inc.
+ * Copyright (C) 2013 Sage Electronic Engineering, LLC.
+ *
+ * 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
+ */
+
+#if IS_ENABLED(CONFIG_CPU_INTEL_FSP_MODEL_206AX)
+ /* Size is 0x2800 - Update in Kconfigs when any included file changes*/
+ #include <microcode-m12206a7_00000029.h>
+#endif
+
+#if IS_ENABLED(CONFIG_CPU_INTEL_FSP_MODEL_306AX)
+ /* Size is 0xC000 - Update in Kconfigs when any included file changes*/
+ #include <microcode-m12306a2_00000008.h>
+ #include <microcode-m12306a4_00000007.h>
+ #include <microcode-m12306a5_00000007.h>
+ #include <microcode-m12306a8_00000010.h>
+ #include <microcode-m12306a9_00000019.h>
+#endif
diff --git a/src/cpu/intel/fsp_model_206ax/model_206ax.h b/src/cpu/intel/fsp_model_206ax/model_206ax.h
new file mode 100644
index 0000000000..a7ca04a690
--- /dev/null
+++ b/src/cpu/intel/fsp_model_206ax/model_206ax.h
@@ -0,0 +1,113 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * 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
+ */
+
+#ifndef _CPU_INTEL_MODEL_206AX_H
+#define _CPU_INTEL_MODEL_206AX_H
+
+/* SandyBridge/IvyBridge bus clock is fixed at 100MHz */
+#define SANDYBRIDGE_BCLK 100
+
+#define IA32_FEATURE_CONTROL 0x3a
+#define CPUID_VMX (1 << 5)
+#define CPUID_SMX (1 << 6)
+#define MSR_FEATURE_CONFIG 0x13c
+#define MSR_FLEX_RATIO 0x194
+#define FLEX_RATIO_LOCK (1 << 20)
+#define FLEX_RATIO_EN (1 << 16)
+#define IA32_PLATFORM_DCA_CAP 0x1f8
+#define IA32_MISC_ENABLE 0x1a0
+#define MSR_TEMPERATURE_TARGET 0x1a2
+#define IA32_PERF_CTL 0x199
+#define IA32_THERM_INTERRUPT 0x19b
+#define IA32_ENERGY_PERFORMANCE_BIAS 0x1b0
+#define ENERGY_POLICY_PERFORMANCE 0
+#define ENERGY_POLICY_NORMAL 6
+#define ENERGY_POLICY_POWERSAVE 15
+#define IA32_PACKAGE_THERM_INTERRUPT 0x1b2
+#define MSR_LT_LOCK_MEMORY 0x2e7
+#define IA32_MC0_STATUS 0x401
+
+#define MSR_PIC_MSG_CONTROL 0x2e
+#define MSR_PLATFORM_INFO 0xce
+#define PLATFORM_INFO_SET_TDP (1 << 29)
+#define MSR_PMG_CST_CONFIG_CONTROL 0xe2
+#define MSR_PMG_IO_CAPTURE_BASE 0xe4
+
+#define MSR_MISC_PWR_MGMT 0x1aa
+#define MISC_PWR_MGMT_EIST_HW_DIS (1 << 0)
+#define MSR_TURBO_RATIO_LIMIT 0x1ad
+#define MSR_POWER_CTL 0x1fc
+
+#define MSR_PKGC3_IRTL 0x60a
+#define MSR_PKGC6_IRTL 0x60b
+#define MSR_PKGC7_IRTL 0x60c
+#define IRTL_VALID (1 << 15)
+#define IRTL_1_NS (0 << 10)
+#define IRTL_32_NS (1 << 10)
+#define IRTL_1024_NS (2 << 10)
+#define IRTL_32768_NS (3 << 10)
+#define IRTL_1048576_NS (4 << 10)
+#define IRTL_33554432_NS (5 << 10)
+#define IRTL_RESPONSE_MASK (0x3ff)
+
+/* long duration in low dword, short duration in high dword */
+#define MSR_PKG_POWER_LIMIT 0x610
+#define PKG_POWER_LIMIT_MASK 0x7fff
+#define PKG_POWER_LIMIT_EN (1 << 15)
+#define PKG_POWER_LIMIT_CLAMP (1 << 16)
+#define PKG_POWER_LIMIT_TIME_SHIFT 17
+#define PKG_POWER_LIMIT_TIME_MASK 0x7f
+
+#define MSR_PP0_CURRENT_CONFIG 0x601
+#define PP0_CURRENT_LIMIT (112 << 3) /* 112 A */
+#define MSR_PP1_CURRENT_CONFIG 0x602
+#define PP1_CURRENT_LIMIT_SNB (35 << 3) /* 35 A */
+#define PP1_CURRENT_LIMIT_IVB (50 << 3) /* 50 A */
+#define MSR_PKG_POWER_SKU_UNIT 0x606
+#define MSR_PKG_POWER_SKU 0x614
+#define MSR_PP0_POWER_LIMIT 0x638
+#define MSR_PP1_POWER_LIMIT 0x640
+
+#define IVB_CONFIG_TDP_MIN_CPUID 0x306a2
+#define MSR_CONFIG_TDP_NOMINAL 0x648
+#define MSR_CONFIG_TDP_LEVEL1 0x649
+#define MSR_CONFIG_TDP_LEVEL2 0x64a
+#define MSR_CONFIG_TDP_CONTROL 0x64b
+#define MSR_TURBO_ACTIVATION_RATIO 0x64c
+
+/* P-state configuration */
+#define PSS_MAX_ENTRIES 8
+#define PSS_RATIO_STEP 2
+#define PSS_LATENCY_TRANSITION 10
+#define PSS_LATENCY_BUSMASTER 10
+
+#ifndef __ROMCC__
+#ifdef __SMM__
+/* Lock MSRs */
+void intel_model_206ax_finalize_smm(void);
+#else
+/* Configure power limits for turbo mode */
+void set_power_limits(u8 power_limit_1_time);
+int cpu_config_tdp_levels(void);
+#endif
+#endif
+
+#endif
diff --git a/src/cpu/intel/fsp_model_206ax/model_206ax_init.c b/src/cpu/intel/fsp_model_206ax/model_206ax_init.c
new file mode 100644
index 0000000000..6eb4787353
--- /dev/null
+++ b/src/cpu/intel/fsp_model_206ax/model_206ax_init.c
@@ -0,0 +1,433 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ * 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
+ */
+
+#include <console/console.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include <string.h>
+#include <arch/acpi.h>
+#include <cpu/cpu.h>
+#include <cpu/x86/mtrr.h>
+#include <cpu/x86/msr.h>
+#include <cpu/x86/lapic.h>
+#include <cpu/intel/speedstep.h>
+#include <cpu/intel/turbo.h>
+#include <cpu/x86/cache.h>
+#include <cpu/x86/name.h>
+#include <pc80/mc146818rtc.h>
+#include <usbdebug.h>
+#include "model_206ax.h"
+#include "chip.h"
+
+static void enable_vmx(void)
+{
+ struct cpuid_result regs;
+ msr_t msr;
+ int enable = CONFIG_ENABLE_VMX;
+
+ 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 = rdmsr(IA32_FEATURE_CONTROL);
+
+ if (msr.lo & (1 << 0)) {
+ printk(BIOS_ERR, "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 = msr.lo = 0;
+
+ printk(BIOS_DEBUG, "%s VMX\n", enable ? "Enabling" : "Disabling");
+
+ if (enable) {
+ msr.lo |= (1 << 2);
+ if (regs.ecx & CPUID_SMX)
+ msr.lo |= (1 << 1);
+ }
+
+ wrmsr(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)
+{
+ msr_t platform_info;
+
+ /* Minimum CPU revision */
+ if (cpuid_eax(1) < IVB_CONFIG_TDP_MIN_CPUID)
+ return 0;
+
+ /* Bits 34:33 indicate how many levels supported */
+ platform_info = rdmsr(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 = rdmsr(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 = rdmsr(MSR_PKG_POWER_SKU_UNIT);
+ power_unit = 2 << ((msr.lo & 0xf) - 1);
+
+ /* Get power defaults for this SKU */
+ msr = rdmsr(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;
+
+ printk(BIOS_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 */
+
+ wrmsr(MSR_PKG_POWER_LIMIT, limit);
+
+ /* Use nominal TDP values for CPUs with configurable TDP */
+ if (cpu_config_tdp_levels()) {
+ msr = rdmsr(MSR_CONFIG_TDP_NOMINAL);
+ limit.hi = 0;
+ limit.lo = msr.lo & 0xff;
+ wrmsr(MSR_TURBO_ACTIVATION_RATIO, limit);
+ }
+}
+
+static void configure_misc(void)
+{
+ msr_t msr;
+
+ msr = rdmsr(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 */
+ wrmsr(IA32_MISC_ENABLE, msr);
+
+ /* Disable Thermal interrupts */
+ msr.lo = 0;
+ msr.hi = 0;
+ wrmsr(IA32_THERM_INTERRUPT, msr);
+
+ /* Enable package critical interrupt only */
+ msr.lo = 1 << 4;
+ msr.hi = 0;
+ wrmsr(IA32_PACKAGE_THERM_INTERRUPT, msr);
+}
+
+static void enable_lapic_tpr(void)
+{
+ msr_t msr;
+
+ msr = rdmsr(MSR_PIC_MSG_CONTROL);
+ msr.lo &= ~(1 << 10); /* Enable APIC TPR updates */
+ wrmsr(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 = rdmsr(IA32_PLATFORM_DCA_CAP);
+ msr.lo |= 1;
+ wrmsr(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 = rdmsr(MSR_CONFIG_TDP_NOMINAL);
+ perf_ctl.lo = (msr.lo & 0xff) << 8;
+ } else {
+ /* Platform Info bits 15:8 give max ratio */
+ msr = rdmsr(MSR_PLATFORM_INFO);
+ perf_ctl.lo = msr.lo & 0xff00;
+ }
+ wrmsr(IA32_PERF_CTL, perf_ctl);
+
+ printk(BIOS_DEBUG, "model_x06ax: frequency set to %d\n",
+ ((perf_ctl.lo >> 8) & 0xff) * SANDYBRIDGE_BCLK);
+}
+
+static void configure_mca(void)
+{
+ msr_t msr;
+ int i;
+
+ msr.lo = msr.hi = 0;
+ /* This should only be done on a cold boot */
+ for (i = 0; i < 7; i++)
+ wrmsr(IA32_MC0_STATUS + (i * 4), msr);
+}
+
+#if CONFIG_USBDEBUG
+static unsigned ehci_debug_addr;
+#endif
+
+/*
+ * Initialize any extra cores/threads in this package.
+ */
+static void intel_cores_init(device_t 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;
+
+ /* Only initialize extra cores from BSP */
+ if (cpu->path.apic.apic_id)
+ return;
+
+ printk(BIOS_DEBUG, "CPU: %u has %u cores, %u threads per core\n",
+ cpu->path.apic.apic_id, threads_per_package/threads_per_core,
+ threads_per_core);
+
+ for (i = 1; i < threads_per_package; ++i) {
+ struct device_path cpu_path;
+ device_t new;
+
+ /* Build the cpu device path */
+ cpu_path.type = DEVICE_PATH_APIC;
+ cpu_path.apic.apic_id =
+ cpu->path.apic.apic_id + i;
+
+ /* Update APIC ID if no hyperthreading */
+ if (threads_per_core == 1)
+ cpu_path.apic.apic_id <<= 1;
+
+ /* Allocate the new cpu device structure */
+ new = alloc_dev(cpu->bus, &cpu_path);
+ if (!new)
+ continue;
+
+ printk(BIOS_DEBUG, "CPU: %u has core %u\n",
+ cpu->path.apic.apic_id,
+ new->path.apic.apic_id);
+
+#if CONFIG_SMP && CONFIG_MAX_CPUS > 1
+ /* Start the new cpu */
+ if (!start_cpu(new)) {
+ /* Record the error in cpu? */
+ printk(BIOS_ERR, "CPU %u would not start!\n",
+ new->path.apic.apic_id);
+ }
+#endif
+ }
+}
+
+static void model_206ax_init(device_t cpu)
+{
+ char processor_name[49];
+ struct cpuid_result cpuid_regs;
+
+ /* Turn on caching if we haven't already */
+ x86_enable_cache();
+
+ /* Clear out pending MCEs */
+ configure_mca();
+
+ /* Print processor name */
+ fill_processor_name(processor_name);
+ printk(BIOS_INFO, "CPU: %s.\n", processor_name);
+
+#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 */
+ cpuid_regs = cpuid(0x80000008);
+ x86_setup_fixed_mtrrs();
+ x86_setup_var_mtrrs(cpuid_regs.eax & 0xff, 2);
+ x86_mtrr_check();
+
+ /* Setup Page Attribute Tables (PAT) */
+ // TODO set up PAT
+
+#if CONFIG_USBDEBUG
+ set_ehci_debug(ehci_debug_addr);
+#endif
+
+ /* Enable the local cpu apics */
+ enable_lapic_tpr();
+ setup_lapic();
+
+ /* Enable virtualization if enabled in CMOS */
+ enable_vmx();
+
+ /* Configure Enhanced SpeedStep and Thermal Sensors */
+ configure_misc();
+
+ /* Enable Direct Cache Access */
+ configure_dca_cap();
+
+ /* Set Max Ratio */
+ set_max_ratio();
+
+ /* Enable Turbo */
+ enable_turbo();
+
+ /* Start up extra cores */
+ intel_cores_init(cpu);
+}
+
+static struct device_operations cpu_dev_ops = {
+ .init = model_206ax_init,
+};
+
+static struct cpu_device_id cpu_table[] = {
+ { X86_VENDOR_INTEL, 0x206a0 }, /* Intel Sandybridge */
+ { X86_VENDOR_INTEL, 0x206a6 }, /* Intel Sandybridge D1 */
+ { X86_VENDOR_INTEL, 0x206a7 }, /* Intel Sandybridge D2/J1 */
+ { X86_VENDOR_INTEL, 0x306a0 }, /* Intel IvyBridge */
+ { X86_VENDOR_INTEL, 0x306a2 }, /* Intel IvyBridge */
+ { X86_VENDOR_INTEL, 0x306a4 }, /* Intel IvyBridge */
+ { X86_VENDOR_INTEL, 0x306a5 }, /* Intel IvyBridge */
+ { X86_VENDOR_INTEL, 0x306a6 }, /* Intel IvyBridge */
+ { X86_VENDOR_INTEL, 0x306a8 }, /* Intel IvyBridge */
+ { X86_VENDOR_INTEL, 0x306a9 }, /* Intel IvyBridge */
+ { 0, 0 },
+};
+
+static const struct cpu_driver driver __cpu_driver = {
+ .ops = &cpu_dev_ops,
+ .id_table = cpu_table,
+};
+