/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "model_2065x.h" #include "chip.h" /* * List of supported C-states in this processor * * Latencies are typical worst-case package exit time in uS * taken from the SandyBridge BIOS specification. */ static acpi_cstate_t cstate_map[] = { { /* 0: C0 */ },{ /* 1: C1 */ .latency = 1, .power = 1000, .resource = { .addrl = 0x00, /* MWAIT State 0 */ .space_id = ACPI_ADDRESS_SPACE_FIXED, .bit_width = ACPI_FFIXEDHW_VENDOR_INTEL, .bit_offset = ACPI_FFIXEDHW_CLASS_MWAIT, .resv = ACPI_FFIXEDHW_FLAG_HW_COORD, } }, { /* 2: C1E */ .latency = 1, .power = 1000, .resource = { .addrl = 0x01, /* MWAIT State 0 Sub-state 1 */ .space_id = ACPI_ADDRESS_SPACE_FIXED, .bit_width = ACPI_FFIXEDHW_VENDOR_INTEL, .bit_offset = ACPI_FFIXEDHW_CLASS_MWAIT, .resv = ACPI_FFIXEDHW_FLAG_HW_COORD, } }, { /* 3: C3 */ .latency = 63, .power = 500, .resource = { .addrl = 0x10, /* MWAIT State 1 */ .space_id = ACPI_ADDRESS_SPACE_FIXED, .bit_width = ACPI_FFIXEDHW_VENDOR_INTEL, .bit_offset = ACPI_FFIXEDHW_CLASS_MWAIT, .resv = ACPI_FFIXEDHW_FLAG_HW_COORD, } }, { /* 4: C6 */ .latency = 87, .power = 350, .resource = { .addrl = 0x20, /* MWAIT State 2 */ .space_id = ACPI_ADDRESS_SPACE_FIXED, .bit_width = ACPI_FFIXEDHW_VENDOR_INTEL, .bit_offset = ACPI_FFIXEDHW_CLASS_MWAIT, .resv = ACPI_FFIXEDHW_FLAG_HW_COORD, } }, { /* 5: C7 */ .latency = 90, .power = 200, .resource = { .addrl = 0x30, /* MWAIT State 3 */ .space_id = ACPI_ADDRESS_SPACE_FIXED, .bit_width = ACPI_FFIXEDHW_VENDOR_INTEL, .bit_offset = ACPI_FFIXEDHW_CLASS_MWAIT, .resv = ACPI_FFIXEDHW_FLAG_HW_COORD, } }, { /* 6: C7S */ .latency = 90, .power = 200, .resource = { .addrl = 0x31, /* MWAIT State 3 Sub-state 1 */ .space_id = ACPI_ADDRESS_SPACE_FIXED, .bit_width = ACPI_FFIXEDHW_VENDOR_INTEL, .bit_offset = ACPI_FFIXEDHW_CLASS_MWAIT, .resv = ACPI_FFIXEDHW_FLAG_HW_COORD, } }, { 0 } }; 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"); /* 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); } wrmsr(IA32_FEATURE_CONTROL, msr); } 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; } static void configure_thermal_target(void) { struct cpu_intel_model_2065x_config *conf; struct device *lapic; msr_t msr; /* Find pointer to CPU configuration */ lapic = dev_find_lapic(SPEEDSTEP_APIC_MAGIC); if (!lapic || !lapic->chip_info) return; conf = lapic->chip_info; /* Set TCC activation offset if supported */ msr = rdmsr(MSR_PLATFORM_INFO); if ((msr.lo & (1 << 30)) && conf->tcc_offset) { msr = rdmsr(MSR_TEMPERATURE_TARGET); msr.lo &= ~(0xf << 24); /* Bits 27:24 */ msr.lo |= (conf->tcc_offset & 0xf) << 24; wrmsr(MSR_TEMPERATURE_TARGET, msr); } } 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); #ifdef DISABLED /* Enable package critical interrupt only */ msr.lo = 1 << 4; msr.hi = 0; wrmsr(IA32_PACKAGE_THERM_INTERRUPT, msr); #endif } 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 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) * NEHALEM_BCLK); } static void set_energy_perf_bias(u8 policy) { #ifdef DISABLED msr_t msr; /* Energy Policy is bits 3:0 */ msr = rdmsr(IA32_ENERGY_PERFORMANCE_BIAS); msr.lo &= ~0xf; msr.lo |= policy & 0xf; wrmsr(IA32_ENERGY_PERFORMANCE_BIAS, msr); printk(BIOS_DEBUG, "model_x06ax: energy policy set to %u\n", policy); #endif } 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); } /* * Initialize any extra cores/threads in this package. */ static void intel_cores_init(struct device *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; struct device *new; /* Build the cpu device path */ cpu_path.type = DEVICE_PATH_APIC; cpu_path.apic.apic_id = cpu->path.apic.apic_id + (i % threads_per_core) + ((i / threads_per_core) << 2); /* 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_2065x_init(struct device *cpu) { char processor_name[49]; struct cpuid_result cpuid_regs; /* Turn on caching if we haven't already */ x86_enable_cache(); intel_update_microcode_from_cbfs(); /* Clear out pending MCEs */ configure_mca(); /* Print processor name */ fill_processor_name(processor_name); printk(BIOS_INFO, "CPU: %s.\n", processor_name); printk(BIOS_INFO, "CPU:lapic=%ld, boot_cpu=%d\n", lapicid (), boot_cpu ()); /* 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 /* 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(); /* Thermal throttle activation offset */ configure_thermal_target(); /* Set energy policy */ set_energy_perf_bias(ENERGY_POLICY_NORMAL); /* 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_2065x_init, }; static struct cpu_device_id cpu_table[] = { { X86_VENDOR_INTEL, 0x20652 }, /* Intel Nehalem */ { X86_VENDOR_INTEL, 0x20655 }, /* Intel Nehalem */ { 0, 0 }, }; static const struct cpu_driver driver __cpu_driver = { .ops = &cpu_dev_ops, .id_table = cpu_table, .cstates = cstate_map, };