/* * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "model_2065x.h" #include "chip.h" #include #include /* * 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 } }; int cpu_get_apic_id_map(int *apic_id_map) { int i; struct cpuid_result result; unsigned int threads_per_package, threads_per_core; /* 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; for (i = 0; i < threads_per_package && i < CONFIG_MAX_CPUS; ++i) { apic_id_map[i] = (i % threads_per_core) + ((i / threads_per_core) << 2); } return threads_per_package; } 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_PERF_BIAS); msr.lo &= ~0xf; msr.lo |= policy & 0xf; wrmsr(IA32_ENERGY_PERF_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 int 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); /* Start the new CPU */ if (is_smp_boot() && !start_cpu(new)) { /* Record the error in cpu? */ printk(BIOS_ERR, "CPU %u would not start!\n", new->path.apic.apic_id); } } } static void model_2065x_init(struct device *cpu) { char processor_name[49]; /* 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 */ x86_setup_mtrrs_with_detect(); x86_mtrr_check(); /* Setup Page Attribute Tables (PAT) */ // TODO set up PAT /* Enable the local CPU APICs */ enable_lapic_tpr(); setup_lapic(); /* Set virtualization based on Kconfig option */ set_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 const 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, };