diff options
Diffstat (limited to 'arch/x86/cpu')
43 files changed, 3590 insertions, 654 deletions
diff --git a/arch/x86/cpu/Makefile b/arch/x86/cpu/Makefile index 2ff237701d..2667e0b478 100644 --- a/arch/x86/cpu/Makefile +++ b/arch/x86/cpu/Makefile @@ -18,7 +18,9 @@ AFLAGS_call32.o := -fpic -fshort-wchar extra-y += call32.o +obj-y += intel_common/ obj-$(CONFIG_INTEL_BAYTRAIL) += baytrail/ +obj-$(CONFIG_INTEL_BROADWELL) += broadwell/ obj-$(CONFIG_SYS_COREBOOT) += coreboot/ obj-$(CONFIG_EFI_APP) += efi/ obj-$(CONFIG_QEMU) += qemu/ diff --git a/arch/x86/cpu/broadwell/Kconfig b/arch/x86/cpu/broadwell/Kconfig new file mode 100644 index 0000000000..1ce3848be3 --- /dev/null +++ b/arch/x86/cpu/broadwell/Kconfig @@ -0,0 +1,30 @@ +# +# Copyright (C) 2016 Google Inc. +# +# SPDX-License-Identifier: GPL-2.0 + +config INTEL_BROADWELL + bool + select CACHE_MRC_BIN + +if INTEL_BROADWELL + +config DCACHE_RAM_BASE + default 0xff7c0000 + +config DCACHE_RAM_SIZE + default 0x40000 + +config DCACHE_RAM_MRC_VAR_SIZE + default 0x30000 + +config CPU_SPECIFIC_OPTIONS + def_bool y + select SMM_TSEG + select X86_RAMTEST + +config SMM_TSEG_SIZE + hex + default 0x800000 + +endif diff --git a/arch/x86/cpu/broadwell/Makefile b/arch/x86/cpu/broadwell/Makefile new file mode 100644 index 0000000000..7edb6f6f19 --- /dev/null +++ b/arch/x86/cpu/broadwell/Makefile @@ -0,0 +1,17 @@ +# +# Copyright (c) 2016 Google, Inc +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y += cpu.o +obj-y += iobp.o +obj-y += lpc.o +obj-y += me.o +obj-y += northbridge.o +obj-y += pch.o +obj-y += pinctrl_broadwell.o +obj-y += power_state.o +obj-y += refcode.o +obj-y += sata.o +obj-y += sdram.o diff --git a/arch/x86/cpu/broadwell/cpu.c b/arch/x86/cpu/broadwell/cpu.c new file mode 100644 index 0000000000..3ba21aacec --- /dev/null +++ b/arch/x86/cpu/broadwell/cpu.c @@ -0,0 +1,761 @@ +/* + * Copyright (c) 2016 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0 + * + * Based on code from coreboot src/soc/intel/broadwell/cpu.c + */ + +#include <common.h> +#include <dm.h> +#include <cpu.h> +#include <asm/cpu.h> +#include <asm/cpu_x86.h> +#include <asm/cpu_common.h> +#include <asm/intel_regs.h> +#include <asm/msr.h> +#include <asm/post.h> +#include <asm/turbo.h> +#include <asm/arch/cpu.h> +#include <asm/arch/pch.h> +#include <asm/arch/rcb.h> + +struct cpu_broadwell_priv { + bool ht_disabled; +}; + +/* 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 arch_cpu_init_dm(void) +{ + struct udevice *dev; + int ret; + + /* Start up the LPC so we have serial */ + ret = uclass_first_device(UCLASS_LPC, &dev); + if (ret) + return ret; + if (!dev) + return -ENODEV; + ret = cpu_set_flex_ratio_to_tdp_nominal(); + if (ret) + return ret; + + return 0; +} + +void set_max_freq(void) +{ + msr_t msr, perf_ctl, platform_info; + + /* Check for configurable TDP option */ + platform_info = msr_read(MSR_PLATFORM_INFO); + + if ((platform_info.hi >> 1) & 3) { + /* 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; + } + + perf_ctl.hi = 0; + msr_write(IA32_PERF_CTL, perf_ctl); + + debug("CPU: frequency set to %d MHz\n", + ((perf_ctl.lo >> 8) & 0xff) * CPU_BCLK); +} + +int arch_cpu_init(void) +{ + post_code(POST_CPU_INIT); + + return x86_cpu_init_f(); +} + +int print_cpuinfo(void) +{ + char processor_name[CPU_MAX_NAME_LEN]; + const char *name; + int ret; + + set_max_freq(); + + ret = cpu_common_init(); + if (ret) + return ret; + gd->arch.pei_boot_mode = PEI_BOOT_NONE; + + /* Print processor name */ + name = cpu_get_name(processor_name); + printf("CPU: %s\n", name); + + return 0; +} + +/* + * The core 100MHz BLCK is disabled in deeper c-states. One needs to calibrate + * the 100MHz BCLCK against the 24MHz BLCK to restore the clocks properly + * when a core is woken up + */ +static int pcode_ready(void) +{ + int wait_count; + const int delay_step = 10; + + wait_count = 0; + do { + if (!(readl(MCHBAR_REG(BIOS_MAILBOX_INTERFACE)) & + MAILBOX_RUN_BUSY)) + return 0; + wait_count += delay_step; + udelay(delay_step); + } while (wait_count < 1000); + + return -ETIMEDOUT; +} + +static u32 pcode_mailbox_read(u32 command) +{ + int ret; + + ret = pcode_ready(); + if (ret) { + debug("PCODE: mailbox timeout on wait ready\n"); + return ret; + } + + /* Send command and start transaction */ + writel(command | MAILBOX_RUN_BUSY, MCHBAR_REG(BIOS_MAILBOX_INTERFACE)); + + ret = pcode_ready(); + if (ret) { + debug("PCODE: mailbox timeout on completion\n"); + return ret; + } + + /* Read mailbox */ + return readl(MCHBAR_REG(BIOS_MAILBOX_DATA)); +} + +static int pcode_mailbox_write(u32 command, u32 data) +{ + int ret; + + ret = pcode_ready(); + if (ret) { + debug("PCODE: mailbox timeout on wait ready\n"); + return ret; + } + + writel(data, MCHBAR_REG(BIOS_MAILBOX_DATA)); + + /* Send command and start transaction */ + writel(command | MAILBOX_RUN_BUSY, MCHBAR_REG(BIOS_MAILBOX_INTERFACE)); + + ret = pcode_ready(); + if (ret) { + debug("PCODE: mailbox timeout on completion\n"); + return ret; + } + + return 0; +} + +/* @dev is the CPU device */ +static void initialize_vr_config(struct udevice *dev) +{ + int ramp, min_vid; + msr_t msr; + + debug("Initializing VR config\n"); + + /* Configure VR_CURRENT_CONFIG */ + msr = msr_read(MSR_VR_CURRENT_CONFIG); + /* + * Preserve bits 63 and 62. Bit 62 is PSI4 enable, but it is only valid + * on ULT systems + */ + msr.hi &= 0xc0000000; + msr.hi |= (0x01 << (52 - 32)); /* PSI3 threshold - 1A */ + msr.hi |= (0x05 << (42 - 32)); /* PSI2 threshold - 5A */ + msr.hi |= (0x14 << (32 - 32)); /* PSI1 threshold - 20A */ + msr.hi |= (1 << (62 - 32)); /* Enable PSI4 */ + /* Leave the max instantaneous current limit (12:0) to default */ + msr_write(MSR_VR_CURRENT_CONFIG, msr); + + /* Configure VR_MISC_CONFIG MSR */ + msr = msr_read(MSR_VR_MISC_CONFIG); + /* Set the IOUT_SLOPE scalar applied to dIout in U10.1.9 format */ + msr.hi &= ~(0x3ff << (40 - 32)); + msr.hi |= (0x200 << (40 - 32)); /* 1.0 */ + /* Set IOUT_OFFSET to 0 */ + msr.hi &= ~0xff; + /* Set entry ramp rate to slow */ + msr.hi &= ~(1 << (51 - 32)); + /* Enable decay mode on C-state entry */ + msr.hi |= (1 << (52 - 32)); + /* Set the slow ramp rate */ + msr.hi &= ~(0x3 << (53 - 32)); + /* Configure the C-state exit ramp rate */ + ramp = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "intel,slow-ramp", + -1); + if (ramp != -1) { + /* Configured slow ramp rate */ + msr.hi |= ((ramp & 0x3) << (53 - 32)); + /* Set exit ramp rate to slow */ + msr.hi &= ~(1 << (50 - 32)); + } else { + /* Fast ramp rate / 4 */ + msr.hi |= (0x01 << (53 - 32)); + /* Set exit ramp rate to fast */ + msr.hi |= (1 << (50 - 32)); + } + /* Set MIN_VID (31:24) to allow CPU to have full control */ + msr.lo &= ~0xff000000; + min_vid = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "intel,min-vid", + 0); + msr.lo |= (min_vid & 0xff) << 24; + msr_write(MSR_VR_MISC_CONFIG, msr); + + /* Configure VR_MISC_CONFIG2 MSR */ + msr = msr_read(MSR_VR_MISC_CONFIG2); + msr.lo &= ~0xffff; + /* + * Allow CPU to control minimum voltage completely (15:8) and + * set the fast ramp voltage in 10mV steps + */ + if (cpu_get_family_model() == BROADWELL_FAMILY_ULT) + msr.lo |= 0x006a; /* 1.56V */ + else + msr.lo |= 0x006f; /* 1.60V */ + msr_write(MSR_VR_MISC_CONFIG2, msr); + + /* Set C9/C10 VCC Min */ + pcode_mailbox_write(MAILBOX_BIOS_CMD_WRITE_C9C10_VOLTAGE, 0x1f1f); +} + +static int calibrate_24mhz_bclk(void) +{ + int err_code; + int ret; + + ret = pcode_ready(); + if (ret) + return ret; + + /* A non-zero value initiates the PCODE calibration */ + writel(~0, MCHBAR_REG(BIOS_MAILBOX_DATA)); + writel(MAILBOX_RUN_BUSY | MAILBOX_BIOS_CMD_FSM_MEASURE_INTVL, + MCHBAR_REG(BIOS_MAILBOX_INTERFACE)); + + ret = pcode_ready(); + if (ret) + return ret; + + err_code = readl(MCHBAR_REG(BIOS_MAILBOX_INTERFACE)) & 0xff; + + debug("PCODE: 24MHz BLCK calibration response: %d\n", err_code); + + /* Read the calibrated value */ + writel(MAILBOX_RUN_BUSY | MAILBOX_BIOS_CMD_READ_CALIBRATION, + MCHBAR_REG(BIOS_MAILBOX_INTERFACE)); + + ret = pcode_ready(); + if (ret) + return ret; + + debug("PCODE: 24MHz BLCK calibration value: 0x%08x\n", + readl(MCHBAR_REG(BIOS_MAILBOX_DATA))); + + return 0; +} + +static void configure_pch_power_sharing(void) +{ + u32 pch_power, pch_power_ext, pmsync, pmsync2; + int i; + + /* Read PCH Power levels from PCODE */ + pch_power = pcode_mailbox_read(MAILBOX_BIOS_CMD_READ_PCH_POWER); + pch_power_ext = pcode_mailbox_read(MAILBOX_BIOS_CMD_READ_PCH_POWER_EXT); + + debug("PCH Power: PCODE Levels 0x%08x 0x%08x\n", pch_power, + pch_power_ext); + + pmsync = readl(RCB_REG(PMSYNC_CONFIG)); + pmsync2 = readl(RCB_REG(PMSYNC_CONFIG2)); + + /* + * Program PMSYNC_TPR_CONFIG PCH power limit values + * pmsync[0:4] = mailbox[0:5] + * pmsync[8:12] = mailbox[6:11] + * pmsync[16:20] = mailbox[12:17] + */ + for (i = 0; i < 3; i++) { + u32 level = pch_power & 0x3f; + pch_power >>= 6; + pmsync &= ~(0x1f << (i * 8)); + pmsync |= (level & 0x1f) << (i * 8); + } + writel(pmsync, RCB_REG(PMSYNC_CONFIG)); + + /* + * Program PMSYNC_TPR_CONFIG2 Extended PCH power limit values + * pmsync2[0:4] = mailbox[23:18] + * pmsync2[8:12] = mailbox_ext[6:11] + * pmsync2[16:20] = mailbox_ext[12:17] + * pmsync2[24:28] = mailbox_ext[18:22] + */ + pmsync2 &= ~0x1f; + pmsync2 |= pch_power & 0x1f; + + for (i = 1; i < 4; i++) { + u32 level = pch_power_ext & 0x3f; + pch_power_ext >>= 6; + pmsync2 &= ~(0x1f << (i * 8)); + pmsync2 |= (level & 0x1f) << (i * 8); + } + writel(pmsync2, RCB_REG(PMSYNC_CONFIG2)); +} + +static int bsp_init_before_ap_bringup(struct udevice *dev) +{ + int ret; + + initialize_vr_config(dev); + ret = calibrate_24mhz_bclk(); + if (ret) + return ret; + configure_pch_power_sharing(); + + return 0; +} + +int cpu_config_tdp_levels(void) +{ + msr_t platform_info; + + /* Bits 34:33 indicate how many levels supported */ + platform_info = msr_read(MSR_PLATFORM_INFO); + return (platform_info.hi >> 1) & 3; +} + +static void set_max_ratio(void) +{ + msr_t msr, perf_ctl; + + perf_ctl.hi = 0; + + /* Check for configurable TDP option */ + if (turbo_get_state() == TURBO_ENABLED) { + msr = msr_read(MSR_NHM_TURBO_RATIO_LIMIT); + perf_ctl.lo = (msr.lo & 0xff) << 8; + } else 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("cpu: frequency set to %d\n", + ((perf_ctl.lo >> 8) & 0xff) * CPU_BCLK); +} + +int broadwell_init(struct udevice *dev) +{ + struct cpu_broadwell_priv *priv = dev_get_priv(dev); + int num_threads; + int num_cores; + msr_t msr; + int ret; + + msr = msr_read(CORE_THREAD_COUNT_MSR); + num_threads = (msr.lo >> 0) & 0xffff; + num_cores = (msr.lo >> 16) & 0xffff; + debug("CPU has %u cores, %u threads enabled\n", num_cores, + num_threads); + + priv->ht_disabled = num_threads == num_cores; + + ret = bsp_init_before_ap_bringup(dev); + if (ret) + return ret; + + set_max_ratio(); + + return ret; +} + +static void configure_mca(void) +{ + msr_t msr; + const unsigned int mcg_cap_msr = 0x179; + int i; + int num_banks; + + msr = msr_read(mcg_cap_msr); + num_banks = msr.lo & 0xff; + msr.lo = 0; + msr.hi = 0; + /* + * TODO(adurbin): This should only be done on a cold boot. Also, some + * of these banks are core vs package scope. For now every CPU clears + * every bank + */ + for (i = 0; i < num_banks; i++) + msr_write(MSR_IA32_MC0_STATUS + (i * 4), 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_c_states(void) +{ + msr_t msr; + + msr = msr_read(MSR_PMG_CST_CONFIG_CONTROL); + msr.lo |= (1 << 31); /* Timed MWAIT Enable */ + msr.lo |= (1 << 30); /* Package c-state Undemotion Enable */ + msr.lo |= (1 << 29); /* Package c-state Demotion Enable */ + 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 */ + /* The deepest package c-state defaults to factory-configured value */ + msr_write(MSR_PMG_CST_CONFIG_CONTROL, 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); + + /* C-state Interrupt Response Latency Control 0 - package C3 latency */ + msr.hi = 0; + msr.lo = IRTL_VALID | IRTL_1024_NS | C_STATE_LATENCY_CONTROL_0_LIMIT; + msr_write(MSR_C_STATE_LATENCY_CONTROL_0, msr); + + /* C-state Interrupt Response Latency Control 1 */ + msr.hi = 0; + msr.lo = IRTL_VALID | IRTL_1024_NS | C_STATE_LATENCY_CONTROL_1_LIMIT; + msr_write(MSR_C_STATE_LATENCY_CONTROL_1, msr); + + /* C-state Interrupt Response Latency Control 2 - package C6/C7 short */ + msr.hi = 0; + msr.lo = IRTL_VALID | IRTL_1024_NS | C_STATE_LATENCY_CONTROL_2_LIMIT; + msr_write(MSR_C_STATE_LATENCY_CONTROL_2, msr); + + /* C-state Interrupt Response Latency Control 3 - package C8 */ + msr.hi = 0; + msr.lo = IRTL_VALID | IRTL_1024_NS | C_STATE_LATENCY_CONTROL_3_LIMIT; + msr_write(MSR_C_STATE_LATENCY_CONTROL_3, msr); + + /* C-state Interrupt Response Latency Control 4 - package C9 */ + msr.hi = 0; + msr.lo = IRTL_VALID | IRTL_1024_NS | C_STATE_LATENCY_CONTROL_4_LIMIT; + msr_write(MSR_C_STATE_LATENCY_CONTROL_4, msr); + + /* C-state Interrupt Response Latency Control 5 - package C10 */ + msr.hi = 0; + msr.lo = IRTL_VALID | IRTL_1024_NS | C_STATE_LATENCY_CONTROL_5_LIMIT; + msr_write(MSR_C_STATE_LATENCY_CONTROL_5, msr); +} + +static void configure_misc(void) +{ + msr_t msr; + + msr = msr_read(MSR_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(MSR_IA32_MISC_ENABLE, msr); + + /* Disable thermal interrupts */ + msr.lo = 0; + msr.hi = 0; + msr_write(MSR_IA32_THERM_INTERRUPT, msr); + + /* Enable package critical interrupt only */ + msr.lo = 1 << 4; + msr.hi = 0; + msr_write(MSR_IA32_PACKAGE_THERM_INTERRUPT, msr); +} + +static void configure_thermal_target(struct udevice *dev) +{ + int tcc_offset; + msr_t msr; + + tcc_offset = fdtdec_get_int(gd->fdt_blob, dev->of_offset, + "intel,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); + } +} + +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(MSR_IA32_PLATFORM_DCA_CAP); + msr.lo |= 1; + msr_write(MSR_IA32_PLATFORM_DCA_CAP, msr); + } +} + +static void set_energy_perf_bias(u8 policy) +{ + msr_t msr; + int ecx; + + /* Determine if energy efficient policy is supported */ + ecx = cpuid_ecx(0x6); + if (!(ecx & (1 << 3))) + return; + + /* Energy Policy is bits 3:0 */ + msr = msr_read(MSR_IA32_ENERGY_PERFORMANCE_BIAS); + msr.lo &= ~0xf; + msr.lo |= policy & 0xf; + msr_write(MSR_IA32_ENERGY_PERFORMANCE_BIAS, msr); + + debug("cpu: energy policy set to %u\n", policy); +} + +/* All CPUs including BSP will run the following function */ +static void cpu_core_init(struct udevice *dev) +{ + /* Clear out pending MCEs */ + configure_mca(); + + /* Enable the local cpu apics */ + enable_lapic_tpr(); + + /* Configure C States */ + configure_c_states(); + + /* Configure Enhanced SpeedStep and Thermal Sensors */ + configure_misc(); + + /* Thermal throttle activation offset */ + configure_thermal_target(dev); + + /* Enable Direct Cache Access */ + configure_dca_cap(); + + /* Set energy policy */ + set_energy_perf_bias(ENERGY_POLICY_NORMAL); + + /* Enable Turbo */ + turbo_enable(); +} + +/* + * Configure processor power limits if possible + * This must be done AFTER set of BIOS_RESET_CPL + */ +void cpu_set_power_limits(int power_limit_1_time) +{ + msr_t msr; + msr_t limit; + unsigned power_unit; + unsigned tdp, min_power, max_power, max_time; + u8 power_limit_1_val; + + msr = msr_read(MSR_PLATFORM_INFO); + if (power_limit_1_time > ARRAY_SIZE(power_limit_time_sec_to_msr)) + power_limit_1_time = 28; + + 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 server SKU */ + + msr_write(MSR_PKG_POWER_LIMIT, limit); + + /* Set power limit values in MCHBAR as well */ + writel(limit.lo, MCHBAR_REG(MCH_PKG_POWER_LIMIT_LO)); + writel(limit.hi, MCHBAR_REG(MCH_PKG_POWER_LIMIT_HI)); + + /* Set DDR RAPL power limit by copying from MMIO to MSR */ + msr.lo = readl(MCHBAR_REG(MCH_DDR_POWER_LIMIT_LO)); + msr.hi = readl(MCHBAR_REG(MCH_DDR_POWER_LIMIT_HI)); + msr_write(MSR_DDR_RAPL_LIMIT, msr); + + /* 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 int broadwell_get_info(struct udevice *dev, struct cpu_info *info) +{ + msr_t msr; + + msr = msr_read(IA32_PERF_CTL); + info->cpu_freq = ((msr.lo >> 8) & 0xff) * BROADWELL_BCLK * 1000000; + info->features = 1 << CPU_FEAT_L1_CACHE | 1 << CPU_FEAT_MMU | + 1 << CPU_FEAT_UCODE | 1 << CPU_FEAT_DEVICE_ID; + + return 0; +} + +static int broadwell_get_count(struct udevice *dev) +{ + return 4; +} + +static int cpu_x86_broadwell_probe(struct udevice *dev) +{ + if (dev->seq == 0) { + cpu_core_init(dev); + return broadwell_init(dev); + } + + return 0; +} + +static const struct cpu_ops cpu_x86_broadwell_ops = { + .get_desc = cpu_x86_get_desc, + .get_info = broadwell_get_info, + .get_count = broadwell_get_count, +}; + +static const struct udevice_id cpu_x86_broadwell_ids[] = { + { .compatible = "intel,core-i3-gen5" }, + { } +}; + +U_BOOT_DRIVER(cpu_x86_broadwell_drv) = { + .name = "cpu_x86_broadwell", + .id = UCLASS_CPU, + .of_match = cpu_x86_broadwell_ids, + .bind = cpu_x86_bind, + .probe = cpu_x86_broadwell_probe, + .ops = &cpu_x86_broadwell_ops, + .priv_auto_alloc_size = sizeof(struct cpu_broadwell_priv), +}; diff --git a/arch/x86/cpu/broadwell/iobp.c b/arch/x86/cpu/broadwell/iobp.c new file mode 100644 index 0000000000..5eed849812 --- /dev/null +++ b/arch/x86/cpu/broadwell/iobp.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2016 Google, Inc + * + * Modified from coreboot + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <errno.h> +#include <asm/intel_regs.h> +#include <asm/io.h> +#include <asm/arch/pch.h> + +#define IOBP_RETRY 1000 + +/* IO Buffer Programming */ +#define IOBPIRI 0x2330 +#define IOBPD 0x2334 +#define IOBPS 0x2338 +#define IOBPS_READY 0x0001 +#define IOBPS_TX_MASK 0x0006 +#define IOBPS_MASK 0xff00 +#define IOBPS_READ 0x0600 +#define IOBPS_WRITE 0x0700 +#define IOBPU 0x233a +#define IOBPU_MAGIC 0xf000 +#define IOBP_PCICFG_READ 0x0400 +#define IOBP_PCICFG_WRITE 0x0500 + +static inline int iobp_poll(void) +{ + unsigned try; + + for (try = IOBP_RETRY; try > 0; try--) { + u16 status = readw(RCB_REG(IOBPS)); + if ((status & IOBPS_READY) == 0) + return 1; + udelay(10); + } + + printf("IOBP: timeout waiting for transaction to complete\n"); + return 0; +} + +int pch_iobp_trans_start(u32 address, int op) +{ + if (!iobp_poll()) + return 0; + + /* Set the address */ + writel(address, RCB_REG(IOBPIRI)); + + /* READ OPCODE */ + clrsetbits_le16(RCB_REG(IOBPS), IOBPS_MASK, op); + + return 1; +} + +int pch_iobp_trans_finish(void) +{ + u16 status; + + /* Undocumented magic */ + writew(IOBPU_MAGIC, RCB_REG(IOBPU)); + + /* Set ready bit */ + setbits_le16(RCB_REG(IOBPS), IOBPS_READY); + + if (!iobp_poll()) + return 1; + + /* Check for successful transaction */ + status = readw(RCB_REG(IOBPS)); + if (status & IOBPS_TX_MASK) + return 1; + + return 0; +} + +u32 pch_iobp_read(u32 address) +{ + if (!pch_iobp_trans_start(address, IOBPS_READ)) + return 0; + if (pch_iobp_trans_finish()) { + printf("IOBP: read 0x%08x failed\n", address); + return 0; + } + + /* Read IOBP data */ + return readl(RCB_REG(IOBPD)); +} + +int pch_iobp_write(u32 address, u32 data) +{ + if (!pch_iobp_trans_start(address, IOBPS_WRITE)) + return -EIO; + + writel(data, RCB_REG(IOBPD)); + + if (pch_iobp_trans_finish()) { + printf("IOBP: write 0x%08x failed\n", address); + return -EIO; + } + + return 0; +} + +int pch_iobp_update(u32 address, u32 andvalue, u32 orvalue) +{ + u32 data = pch_iobp_read(address); + + /* Update the data */ + data &= andvalue; + data |= orvalue; + + return pch_iobp_write(address, data); +} + +int pch_iobp_exec(u32 addr, u16 op_code, u8 route_id, u32 *data, u8 *resp) +{ + if (!data || !resp) + return 0; + + *resp = -1; + if (!iobp_poll()) + return -EIO; + + writel(addr, RCB_REG(IOBPIRI)); + clrsetbits_le16(RCB_REG(IOBPS), 0xff00, op_code); + writew(IOBPU_MAGIC | route_id, RCB_REG(IOBPU)); + + writel(*data, RCB_REG(IOBPD)); + /* Set IOBPS[0] to trigger IOBP transaction*/ + setbits_le16(RCB_REG(IOBPS), 1); + + if (!iobp_poll()) + return -EIO; + + *resp = (readw(RCB_REG(IOBPS)) & IOBPS_TX_MASK) >> 1; + *data = readl(RCB_REG(IOBPD)); + + return 0; +} diff --git a/arch/x86/cpu/broadwell/lpc.c b/arch/x86/cpu/broadwell/lpc.c new file mode 100644 index 0000000000..ee3ac1820d --- /dev/null +++ b/arch/x86/cpu/broadwell/lpc.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2016 Google, Inc + * + * From coreboot broadwell support + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <dm.h> +#include <pch.h> +#include <asm/intel_regs.h> +#include <asm/io.h> +#include <asm/lpc_common.h> +#include <asm/arch/pch.h> +#include <asm/arch/spi.h> + +static void set_spi_speed(void) +{ + u32 fdod; + u8 ssfc; + + /* Observe SPI Descriptor Component Section 0 */ + writel(0x1000, SPI_REG(SPIBAR_FDOC)); + + /* Extract the Write/Erase SPI Frequency from descriptor */ + fdod = readl(SPI_REG(SPIBAR_FDOD)); + fdod >>= 24; + fdod &= 7; + + /* Set Software Sequence frequency to match */ + ssfc = readb(SPI_REG(SPIBAR_SSFC + 2)); + ssfc &= ~7; + ssfc |= fdod; + writeb(ssfc, SPI_REG(SPIBAR_SSFC + 2)); +} + +static int broadwell_lpc_early_init(struct udevice *dev) +{ + set_spi_speed(); + + return 0; +} + +static int lpc_init_extra(struct udevice *dev) +{ + return 0; +} + +static int broadwell_lpc_probe(struct udevice *dev) +{ + int ret; + + if (!(gd->flags & GD_FLG_RELOC)) { + ret = lpc_common_early_init(dev); + if (ret) { + debug("%s: lpc_early_init() failed\n", __func__); + return ret; + } + + return broadwell_lpc_early_init(dev); + } + + return lpc_init_extra(dev); +} + +static const struct udevice_id broadwell_lpc_ids[] = { + { .compatible = "intel,broadwell-lpc" }, + { } +}; + +U_BOOT_DRIVER(broadwell_lpc_drv) = { + .name = "lpc", + .id = UCLASS_LPC, + .of_match = broadwell_lpc_ids, + .probe = broadwell_lpc_probe, +}; diff --git a/arch/x86/cpu/broadwell/me.c b/arch/x86/cpu/broadwell/me.c new file mode 100644 index 0000000000..e03b87c40f --- /dev/null +++ b/arch/x86/cpu/broadwell/me.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0 + * + * Based on code from coreboot src/soc/intel/broadwell/me_status.c + */ + +#include <common.h> +#include <errno.h> +#include <asm/arch/me.h> + +static inline void me_read_dword_ptr(struct udevice *dev, void *ptr, int offset) +{ + u32 dword; + + dm_pci_read_config32(dev, offset, &dword); + memcpy(ptr, &dword, sizeof(dword)); +} + +int intel_me_hsio_version(struct udevice *dev, uint16_t *versionp, + uint16_t *checksump) +{ + int count; + u32 hsiover; + struct me_hfs hfs; + + /* Query for HSIO version, overloads H_GS and HFS */ + dm_pci_write_config32(dev, PCI_ME_H_GS, + ME_HSIO_MESSAGE | ME_HSIO_CMD_GETHSIOVER); + + /* Must wait for ME acknowledgement */ + for (count = ME_RETRY; count > 0; --count) { + me_read_dword_ptr(dev, &hfs, PCI_ME_HFS); + if (hfs.bios_msg_ack) + break; + udelay(ME_DELAY); + } + if (!count) { + debug("ERROR: ME failed to respond\n"); + return -ETIMEDOUT; + } + + /* HSIO version should be in HFS_5 */ + dm_pci_read_config32(dev, PCI_ME_HFS5, &hsiover); + *versionp = hsiover >> 16; + *checksump = hsiover & 0xffff; + + debug("ME: HSIO Version : %d (CRC 0x%04x)\n", + *versionp, *checksump); + + /* Reset registers to normal behavior */ + dm_pci_write_config32(dev, PCI_ME_H_GS, + ME_HSIO_MESSAGE | ME_HSIO_CMD_GETHSIOVER); + + return 0; +} diff --git a/arch/x86/cpu/broadwell/northbridge.c b/arch/x86/cpu/broadwell/northbridge.c new file mode 100644 index 0000000000..aa64808e45 --- /dev/null +++ b/arch/x86/cpu/broadwell/northbridge.c @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2011 The Chromium Authors + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <dm.h> +#include <asm/io.h> +#include <asm/arch/iomap.h> +#include <asm/arch/pch.h> + +static int broadwell_northbridge_early_init(struct udevice *dev) +{ + /* Move earlier? */ + dm_pci_write_config32(dev, PCIEXBAR + 4, 0); + /* 64MiB - 0-63 buses */ + dm_pci_write_config32(dev, PCIEXBAR, MCFG_BASE_ADDRESS | 4 | 1); + + dm_pci_write_config32(dev, MCHBAR, MCH_BASE_ADDRESS | 1); + dm_pci_write_config32(dev, DMIBAR, DMI_BASE_ADDRESS | 1); + dm_pci_write_config32(dev, EPBAR, EP_BASE_ADDRESS | 1); + writel(EDRAM_BASE_ADDRESS | 1, MCH_BASE_ADDRESS + EDRAMBAR); + writel(GDXC_BASE_ADDRESS | 1, MCH_BASE_ADDRESS + GDXCBAR); + + /* Set C0000-FFFFF to access RAM on both reads and writes */ + dm_pci_write_config8(dev, PAM0, 0x30); + dm_pci_write_config8(dev, PAM1, 0x33); + dm_pci_write_config8(dev, PAM2, 0x33); + dm_pci_write_config8(dev, PAM3, 0x33); + dm_pci_write_config8(dev, PAM4, 0x33); + dm_pci_write_config8(dev, PAM5, 0x33); + dm_pci_write_config8(dev, PAM6, 0x33); + + /* Device enable: IGD and Mini-HD */ + dm_pci_write_config32(dev, DEVEN, DEVEN_D0EN | DEVEN_D2EN | DEVEN_D3EN); + + return 0; +} + +static int broadwell_northbridge_probe(struct udevice *dev) +{ + if (!(gd->flags & GD_FLG_RELOC)) + return broadwell_northbridge_early_init(dev); + + return 0; +} + +static const struct udevice_id broadwell_northbridge_ids[] = { + { .compatible = "intel,broadwell-northbridge" }, + { } +}; + +U_BOOT_DRIVER(broadwell_northbridge_drv) = { + .name = "broadwell_northbridge", + .id = UCLASS_NORTHBRIDGE, + .of_match = broadwell_northbridge_ids, + .probe = broadwell_northbridge_probe, +}; diff --git a/arch/x86/cpu/broadwell/pch.c b/arch/x86/cpu/broadwell/pch.c new file mode 100644 index 0000000000..f0798a7f9e --- /dev/null +++ b/arch/x86/cpu/broadwell/pch.c @@ -0,0 +1,540 @@ +/* + * Copyright (c) 2016 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <dm.h> +#include <pch.h> +#include <asm/cpu.h> +#include <asm/gpio.h> +#include <asm/i8259.h> +#include <asm/intel_regs.h> +#include <asm/io.h> +#include <asm/ioapic.h> +#include <asm/lpc_common.h> +#include <asm/pch_common.h> +#include <asm/arch/cpu.h> +#include <asm/arch/gpio.h> +#include <asm/arch/iomap.h> +#include <asm/arch/pch.h> +#include <asm/arch/pm.h> +#include <asm/arch/rcb.h> +#include <asm/arch/spi.h> + +#define BIOS_CTRL 0xdc + +bool cpu_is_ult(void) +{ + u32 fm = cpu_get_family_model(); + + return fm == BROADWELL_FAMILY_ULT || fm == HASWELL_FAMILY_ULT; +} + +static int broadwell_pch_early_init(struct udevice *dev) +{ + struct gpio_desc desc; + struct udevice *bus; + pci_dev_t bdf; + int ret; + + dm_pci_write_config32(dev, PCH_RCBA, RCB_BASE_ADDRESS | 1); + + dm_pci_write_config32(dev, PMBASE, ACPI_BASE_ADDRESS | 1); + dm_pci_write_config8(dev, ACPI_CNTL, ACPI_EN); + dm_pci_write_config32(dev, GPIO_BASE, GPIO_BASE_ADDRESS | 1); + dm_pci_write_config8(dev, GPIO_CNTL, GPIO_EN); + + /* Enable IOAPIC */ + writew(0x1000, RCB_REG(OIC)); + /* Read back for posted write */ + readw(RCB_REG(OIC)); + + /* Set HPET address and enable it */ + clrsetbits_le32(RCB_REG(HPTC), 3, 1 << 7); + /* Read back for posted write */ + readl(RCB_REG(HPTC)); + /* Enable HPET to start counter */ + setbits_le32(HPET_BASE_ADDRESS + 0x10, 1 << 0); + + setbits_le32(RCB_REG(GCS), 1 << 5); + + /* + * Enable PP3300_AUTOBAHN_EN after initial GPIO setup + * to prevent possible brownout. This will cause the GPIOs to be set + * up if it has not been done already. + */ + ret = gpio_request_by_name(dev, "power-enable-gpio", 0, &desc, + GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); + if (ret) + return ret; + + /* 8.14 Additional PCI Express Programming Steps, step #1 */ + bdf = PCI_BDF(0, 0x1c, 0); + bus = pci_get_controller(dev); + pci_bus_clrset_config32(bus, bdf, 0xf4, 0x60, 0); + pci_bus_clrset_config32(bus, bdf, 0xf4, 0x80, 0x80); + pci_bus_clrset_config32(bus, bdf, 0xe2, 0x30, 0x30); + + return 0; +} + +static void pch_misc_init(struct udevice *dev) +{ + /* Setup SLP signal assertion, SLP_S4=4s, SLP_S3=50ms */ + dm_pci_clrset_config8(dev, GEN_PMCON_3, 3 << 4 | 1 << 10, + 1 << 3 | 1 << 11 | 1 << 12); + /* Prepare sleep mode */ + clrsetio_32(ACPI_BASE_ADDRESS + PM1_CNT, SLP_TYP, SCI_EN); + + /* Setup NMI on errors, disable SERR */ + clrsetio_8(0x61, 0xf0, 1 << 2); + /* Disable NMI sources */ + setio_8(0x70, 1 << 7); + /* Indicate DRAM init done for MRC */ + dm_pci_clrset_config8(dev, GEN_PMCON_2, 0, 1 << 7); + + /* Clear status bits to prevent unexpected wake */ + setbits_le32(RCB_REG(0x3310), 0x0000002f); + clrsetbits_le32(RCB_REG(0x3f02), 0x0000000f, 0); + /* Enable PCIe Relaxed Order */ + setbits_le32(RCB_REG(0x2314), 1 << 31 | 1 << 7); + setbits_le32(RCB_REG(0x1114), 1 << 15 | 1 << 14); + /* Setup SERIRQ, enable continuous mode */ + dm_pci_clrset_config8(dev, SERIRQ_CNTL, 0, 1 << 7 | 1 << 6); +}; + +static void pch_enable_ioapic(void) +{ + u32 reg32; + + io_apic_set_id(0x02); + + /* affirm full set of redirection table entries ("write once") */ + reg32 = io_apic_read(0x01); + + /* PCH-LP has 39 redirection entries */ + reg32 &= ~0x00ff0000; + reg32 |= 0x00270000; + + io_apic_write(0x01, reg32); + + /* + * Select Boot Configuration register (0x03) and + * use Processor System Bus (0x01) to deliver interrupts. + */ + io_apic_write(0x03, 0x01); +} + +/* Enable all requested GPE */ +void enable_all_gpe(u32 set1, u32 set2, u32 set3, u32 set4) +{ + outl(set1, ACPI_BASE_ADDRESS + GPE0_EN(GPE_31_0)); + outl(set2, ACPI_BASE_ADDRESS + GPE0_EN(GPE_63_32)); + outl(set3, ACPI_BASE_ADDRESS + GPE0_EN(GPE_94_64)); + outl(set4, ACPI_BASE_ADDRESS + GPE0_EN(GPE_STD)); +} + +/* + * Enable GPIO SMI events - it would be good to put this in the GPIO driver + * but it would need a new driver operation. + */ +int enable_alt_smi(struct udevice *pch, u32 mask) +{ + struct pch_lp_gpio_regs *regs; + u32 gpiobase; + int ret; + + ret = pch_get_gpio_base(pch, &gpiobase); + if (ret) { + debug("%s: invalid GPIOBASE address (%08x)\n", __func__, + gpiobase); + return -EINVAL; + } + + regs = (struct pch_lp_gpio_regs *)gpiobase; + setio_32(regs->alt_gpi_smi_en, mask); + + return 0; +} + +static int pch_power_options(struct udevice *dev) +{ + int pwr_on_after_power_fail = MAINBOARD_POWER_OFF; + const char *state; + u32 enable[4]; + u16 reg16; + int ret; + + dm_pci_read_config16(dev, GEN_PMCON_3, ®16); + reg16 &= 0xfffe; + switch (pwr_on_after_power_fail) { + 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"; + } + dm_pci_write_config16(dev, GEN_PMCON_3, reg16); + debug("Set power %s after power failure.\n", state); + + /* GPE setup based on device tree configuration */ + ret = fdtdec_get_int_array(gd->fdt_blob, dev->of_offset, + "intel,gpe0-en", enable, ARRAY_SIZE(enable)); + if (ret) + return -EINVAL; + enable_all_gpe(enable[0], enable[1], enable[2], enable[3]); + + /* SMI setup based on device tree configuration */ + enable_alt_smi(dev, fdtdec_get_int(gd->fdt_blob, dev->of_offset, + "intel,alt-gp-smi-enable", 0)); + + return 0; +} + +/* Magic register settings for power management */ +static void pch_pm_init_magic(struct udevice *dev) +{ + dm_pci_write_config8(dev, 0xa9, 0x46); + clrbits_le32(RCB_REG(0x232c), 1), + setbits_le32(RCB_REG(0x1100), 0x0000c13f); + clrsetbits_le32(RCB_REG(0x2320), 0x60, 0x10); + writel(0x00012fff, RCB_REG(0x3314)); + clrsetbits_le32(RCB_REG(0x3318), 0x000f0330, 0x0dcf0400); + writel(0x04000000, RCB_REG(0x3324)); + writel(0x00041400, RCB_REG(0x3368)); + writel(0x3f8ddbff, RCB_REG(0x3388)); + writel(0x00007001, RCB_REG(0x33ac)); + writel(0x00181900, RCB_REG(0x33b0)); + writel(0x00060A00, RCB_REG(0x33c0)); + writel(0x06200840, RCB_REG(0x33d0)); + writel(0x01010101, RCB_REG(0x3a28)); + writel(0x040c0404, RCB_REG(0x3a2c)); + writel(0x9000000a, RCB_REG(0x3a9c)); + writel(0x03808033, RCB_REG(0x2b1c)); + writel(0x80000009, RCB_REG(0x2b34)); + writel(0x022ddfff, RCB_REG(0x3348)); + writel(0x00000001, RCB_REG(0x334c)); + writel(0x0001c000, RCB_REG(0x3358)); + writel(0x3f8ddbff, RCB_REG(0x3380)); + writel(0x0001c7e1, RCB_REG(0x3384)); + writel(0x0001c7e1, RCB_REG(0x338c)); + writel(0x0001c000, RCB_REG(0x3398)); + writel(0x00181900, RCB_REG(0x33a8)); + writel(0x00080000, RCB_REG(0x33dc)); + writel(0x00000001, RCB_REG(0x33e0)); + writel(0x0000040c, RCB_REG(0x3a20)); + writel(0x01010101, RCB_REG(0x3a24)); + writel(0x01010101, RCB_REG(0x3a30)); + dm_pci_clrset_config32(dev, 0xac, 0x00200000, 0); + setbits_le32(RCB_REG(0x0410), 0x00000003); + setbits_le32(RCB_REG(0x2618), 0x08000000); + setbits_le32(RCB_REG(0x2300), 0x00000002); + setbits_le32(RCB_REG(0x2600), 0x00000008); + writel(0x00007001, RCB_REG(0x33b4)); + writel(0x022ddfff, RCB_REG(0x3350)); + writel(0x00000001, RCB_REG(0x3354)); + /* Power Optimizer */ + setbits_le32(RCB_REG(0x33d4), 0x08000000); + /* + * This stops the LCD from turning on: + * setbits_le32(RCB_REG(0x33c8), 0x08000080); + */ + writel(0x0000883c, RCB_REG(0x2b10)); + writel(0x1e0a4616, RCB_REG(0x2b14)); + writel(0x40000005, RCB_REG(0x2b24)); + writel(0x0005db01, RCB_REG(0x2b20)); + writel(0x05145005, RCB_REG(0x3a80)); + writel(0x00001005, RCB_REG(0x3a84)); + setbits_le32(RCB_REG(0x33d4), 0x2fff2fb1); + setbits_le32(RCB_REG(0x33c8), 0x00008000); +}; + +static int pch_type(struct udevice *dev) +{ + u16 type; + + dm_pci_read_config16(dev, PCI_DEVICE_ID, &type); + + return type; +} + +/* Return 1 if PCH type is WildcatPoint */ +static int pch_is_wpt(struct udevice *dev) +{ + return ((pch_type(dev) & 0xfff0) == 0x9cc0) ? 1 : 0; +} + +/* Return 1 if PCH type is WildcatPoint ULX */ +static int pch_is_wpt_ulx(struct udevice *dev) +{ + u16 lpcid = pch_type(dev); + + switch (lpcid) { + case PCH_WPT_BDW_Y_SAMPLE: + case PCH_WPT_BDW_Y_PREMIUM: + case PCH_WPT_BDW_Y_BASE: + return 1; + } + + return 0; +} + +static u32 pch_read_soft_strap(int id) +{ + clrbits_le32(SPI_REG(SPIBAR_FDOC), 0x00007ffc); + setbits_le32(SPI_REG(SPIBAR_FDOC), 0x00004000 | id * 4); + + return readl(SPI_REG(SPIBAR_FDOD)); +} + +static void pch_enable_mphy(struct udevice *dev) +{ + u32 data_and = 0xffffffff; + u32 data_or = (1 << 14) | (1 << 13) | (1 << 12); + + data_or |= (1 << 0); + if (pch_is_wpt(dev)) { + data_and &= ~((1 << 7) | (1 << 6) | (1 << 3)); + data_or |= (1 << 5) | (1 << 4); + + if (pch_is_wpt_ulx(dev)) { + /* Check if SATA and USB3 MPHY are enabled */ + u32 strap19 = pch_read_soft_strap(19); + strap19 &= ((1 << 31) | (1 << 30)); + strap19 >>= 30; + if (strap19 == 3) { + data_or |= (1 << 3); + debug("Enable ULX MPHY PG control in single domain\n"); + } else if (strap19 == 0) { + debug("Enable ULX MPHY PG control in split domains\n"); + } else { + debug("Invalid PCH Soft Strap 19 configuration\n"); + } + } else { + data_or |= (1 << 3); + } + } + + pch_iobp_update(0xCF000000, data_and, data_or); +} + +static void pch_init_deep_sx(bool deep_sx_enable_ac, bool deep_sx_enable_dc) +{ + if (deep_sx_enable_ac) { + setbits_le32(RCB_REG(DEEP_S3_POL), DEEP_S3_EN_AC); + setbits_le32(RCB_REG(DEEP_S5_POL), DEEP_S5_EN_AC); + } + + if (deep_sx_enable_dc) { + setbits_le32(RCB_REG(DEEP_S3_POL), DEEP_S3_EN_DC); + setbits_le32(RCB_REG(DEEP_S5_POL), DEEP_S5_EN_DC); + } + + if (deep_sx_enable_ac || deep_sx_enable_dc) { + setbits_le32(RCB_REG(DEEP_SX_CONFIG), + DEEP_SX_WAKE_PIN_EN | DEEP_SX_GP27_PIN_EN); + } +} + +/* Power Management init */ +static void pch_pm_init(struct udevice *dev) +{ + debug("PCH PM init\n"); + + pch_init_deep_sx(false, false); + pch_enable_mphy(dev); + pch_pm_init_magic(dev); + + if (pch_is_wpt(dev)) { + setbits_le32(RCB_REG(0x33e0), 1 << 4 | 1 << 1); + setbits_le32(RCB_REG(0x2b1c), 1 << 22 | 1 << 14 | 1 << 13); + writel(0x16bf0002, RCB_REG(0x33e4)); + setbits_le32(RCB_REG(0x33e4), 0x1); + } + + pch_iobp_update(0xCA000000, ~0UL, 0x00000009); + + /* Set RCBA 0x2b1c[29]=1 if DSP disabled */ + if (readl(RCB_REG(FD)) & PCH_DISABLE_ADSPD) + setbits_le32(RCB_REG(0x2b1c), 1 << 29); +} + +static void pch_cg_init(struct udevice *dev) +{ + struct udevice *bus = pci_get_controller(dev); + u32 reg32; + u16 reg16; + ulong val; + + /* DMI */ + setbits_le32(RCB_REG(0x2234), 0xf); + + dm_pci_read_config16(dev, GEN_PMCON_1, ®16); + reg16 &= ~(1 << 10); /* Disable BIOS_PCI_EXP_EN for native PME */ + if (pch_is_wpt(dev)) + reg16 &= ~(1 << 11); + else + reg16 |= 1 << 11; + reg16 |= 1 << 5 | 1 << 6 | 1 << 7 | 1 << 12; + reg16 |= 1 << 2; /* PCI CLKRUN# Enable */ + dm_pci_write_config16(dev, GEN_PMCON_1, reg16); + + /* + * RCBA + 0x2614[27:25,14:13,10,8] = 101,11,1,1 + * RCBA + 0x2614[23:16] = 0x20 + * RCBA + 0x2614[30:28] = 0x0 + * RCBA + 0x2614[26] = 1 (IF 0:2.0@0x08 >= 0x0b) + */ + clrsetbits_le32(RCB_REG(0x2614), 0x64ff0000, 0x0a206500); + + /* Check for 0:2.0@0x08 >= 0x0b */ + pci_bus_read_config(bus, PCI_BDF(0, 0x2, 0), 0x8, &val, PCI_SIZE_8); + if (pch_is_wpt(dev) || val >= 0x0b) + setbits_le32(RCB_REG(0x2614), 1 << 26); + + setbits_le32(RCB_REG(0x900), 0x0000031f); + + reg32 = readl(RCB_REG(CG)); + if (readl(RCB_REG(0x3454)) & (1 << 4)) + reg32 &= ~(1 << 29); /* LPC Dynamic */ + else + reg32 |= (1 << 29); /* LPC Dynamic */ + reg32 |= 1 << 31; /* LP LPC */ + reg32 |= 1 << 30; /* LP BLA */ + if (readl(RCB_REG(0x3454)) & (1 << 4)) + reg32 &= ~(1 << 29); + else + reg32 |= 1 << 29; + reg32 |= 1 << 28; /* GPIO Dynamic */ + reg32 |= 1 << 27; /* HPET Dynamic */ + reg32 |= 1 << 26; /* Generic Platform Event Clock */ + if (readl(RCB_REG(BUC)) & PCH_DISABLE_GBE) + reg32 |= 1 << 23; /* GbE Static */ + if (readl(RCB_REG(FD)) & PCH_DISABLE_HD_AUDIO) + reg32 |= 1 << 21; /* HDA Static */ + reg32 |= 1 << 22; /* HDA Dynamic */ + writel(reg32, RCB_REG(CG)); + + /* PCH-LP LPC */ + if (pch_is_wpt(dev)) + clrsetbits_le32(RCB_REG(0x3434), 0x1f, 0x17); + else + setbits_le32(RCB_REG(0x3434), 0x7); + + /* SPI */ + setbits_le32(RCB_REG(0x38c0), 0x3c07); + + pch_iobp_update(0xCE00C000, ~1UL, 0x00000000); +} + +static void systemagent_init(void) +{ + /* Enable Power Aware Interrupt Routing */ + clrsetbits_8(MCHBAR_REG(MCH_PAIR), 0x7, 0x4); /* Fixed Priority */ + + /* + * Set bits 0+1 of BIOS_RESET_CPL to indicate to the CPU + * that BIOS has initialized memory and power management + */ + setbits_8(MCHBAR_REG(BIOS_RESET_CPL), 3); + debug("Set BIOS_RESET_CPL\n"); + + /* Configure turbo power limits 1ms after reset complete bit */ + mdelay(1); + + cpu_set_power_limits(28); +} + +static int broadwell_pch_init(struct udevice *dev) +{ + int ret; + + /* Enable upper 128 bytes of CMOS */ + setbits_le32(RCB_REG(RC), 1 << 2); + + /* + * TODO: TCO timer halt - this hangs + * setio_16(ACPI_BASE_ADDRESS + TCO1_CNT, TCO_TMR_HLT); + */ + + /* Disable unused device (always) */ + setbits_le32(RCB_REG(FD), PCH_DISABLE_ALWAYS); + + pch_misc_init(dev); + + /* Interrupt configuration */ + pch_enable_ioapic(); + + /* Initialize power management */ + ret = pch_power_options(dev); + if (ret) + return ret; + pch_pm_init(dev); + pch_cg_init(dev); + systemagent_init(); + + return 0; +} + +static int broadwell_pch_probe(struct udevice *dev) +{ + if (!(gd->flags & GD_FLG_RELOC)) + return broadwell_pch_early_init(dev); + else + return broadwell_pch_init(dev); +} + +static int broadwell_pch_get_spi_base(struct udevice *dev, ulong *sbasep) +{ + u32 rcba; + + dm_pci_read_config32(dev, PCH_RCBA, &rcba); + /* Bits 31-14 are the base address, 13-1 are reserved, 0 is enable */ + rcba = rcba & 0xffffc000; + *sbasep = rcba + 0x3800; + + return 0; +} + +static int broadwell_set_spi_protect(struct udevice *dev, bool protect) +{ + return lpc_set_spi_protect(dev, BIOS_CTRL, protect); +} + +static int broadwell_get_gpio_base(struct udevice *dev, u32 *gbasep) +{ + dm_pci_read_config32(dev, GPIO_BASE, gbasep); + *gbasep &= PCI_BASE_ADDRESS_IO_MASK; + + return 0; +} + +static const struct pch_ops broadwell_pch_ops = { + .get_spi_base = broadwell_pch_get_spi_base, + .set_spi_protect = broadwell_set_spi_protect, + .get_gpio_base = broadwell_get_gpio_base, +}; + +static const struct udevice_id broadwell_pch_ids[] = { + { .compatible = "intel,broadwell-pch" }, + { } +}; + +U_BOOT_DRIVER(broadwell_pch) = { + .name = "broadwell_pch", + .id = UCLASS_PCH, + .of_match = broadwell_pch_ids, + .probe = broadwell_pch_probe, + .ops = &broadwell_pch_ops, +}; diff --git a/arch/x86/cpu/broadwell/pinctrl_broadwell.c b/arch/x86/cpu/broadwell/pinctrl_broadwell.c new file mode 100644 index 0000000000..2a3fcedd0d --- /dev/null +++ b/arch/x86/cpu/broadwell/pinctrl_broadwell.c @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2016 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <pch.h> +#include <pci.h> +#include <asm/cpu.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <asm/pci.h> +#include <asm/arch/gpio.h> +#include <dt-bindings/gpio/x86-gpio.h> +#include <dm/pinctrl.h> + +DECLARE_GLOBAL_DATA_PTR; + +enum { + MAX_GPIOS = 95, +}; + +#define PIRQ_SHIFT 16 +#define CONF_MASK 0xffff + +struct pin_info { + int node; + int phandle; + bool mode_gpio; + bool dir_input; + bool invert; + bool trigger_level; + bool output_high; + bool sense_disable; + bool owner_gpio; + bool route_smi; + bool irq_enable; + bool reset_rsmrst; + bool pirq_apic_route; +}; + +static int broadwell_pinctrl_read_configs(struct udevice *dev, + struct pin_info *conf, int max_pins) +{ + const void *blob = gd->fdt_blob; + int count = 0; + int node; + + debug("%s: starting\n", __func__); + for (node = fdt_first_subnode(blob, dev->of_offset); + node > 0; + node = fdt_next_subnode(blob, node)) { + int phandle = fdt_get_phandle(blob, node); + + if (!phandle) + continue; + if (count == max_pins) + return -ENOSPC; + + /* We've found a new configuration */ + memset(conf, '\0', sizeof(*conf)); + conf->node = node; + conf->phandle = phandle; + conf->mode_gpio = fdtdec_get_bool(blob, node, "mode-gpio"); + if (fdtdec_get_int(blob, node, "direction", -1) == PIN_INPUT) + conf->dir_input = true; + conf->invert = fdtdec_get_bool(blob, node, "invert"); + if (fdtdec_get_int(blob, node, "trigger", -1) == TRIGGER_LEVEL) + conf->trigger_level = true; + if (fdtdec_get_int(blob, node, "output-value", -1) == 1) + conf->output_high = true; + conf->sense_disable = fdtdec_get_bool(blob, node, + "sense-disable"); + if (fdtdec_get_int(blob, node, "owner", -1) == OWNER_GPIO) + conf->owner_gpio = true; + if (fdtdec_get_int(blob, node, "route", -1) == ROUTE_SMI) + conf->route_smi = true; + conf->irq_enable = fdtdec_get_bool(blob, node, "irq-enable"); + conf->reset_rsmrst = fdtdec_get_bool(blob, node, + "reset-rsmrst"); + if (fdtdec_get_int(blob, node, "pirq-apic", -1) == + PIRQ_APIC_ROUTE) + conf->pirq_apic_route = true; + debug("config: phandle=%d\n", phandle); + count++; + conf++; + } + debug("%s: Found %d configurations\n", __func__, count); + + return count; +} + +static int broadwell_pinctrl_lookup_phandle(struct pin_info *conf, + int conf_count, int phandle) +{ + int i; + + for (i = 0; i < conf_count; i++) { + if (conf[i].phandle == phandle) + return i; + } + + return -ENOENT; +} + +static int broadwell_pinctrl_read_pins(struct udevice *dev, + struct pin_info *conf, int conf_count, int gpio_conf[], + int num_gpios) +{ + const void *blob = gd->fdt_blob; + int count = 0; + int node; + + for (node = fdt_first_subnode(blob, dev->of_offset); + node > 0; + node = fdt_next_subnode(blob, node)) { + int len, i; + const u32 *prop = fdt_getprop(blob, node, "config", &len); + + if (!prop) + continue; + + /* There are three cells per pin */ + count = len / (sizeof(u32) * 3); + debug("Found %d GPIOs to configure\n", count); + for (i = 0; i < count; i++) { + uint gpio = fdt32_to_cpu(prop[i * 3]); + uint phandle = fdt32_to_cpu(prop[i * 3 + 1]); + int val; + + if (gpio >= num_gpios) { + debug("%s: GPIO %d out of range\n", __func__, + gpio); + return -EDOM; + } + val = broadwell_pinctrl_lookup_phandle(conf, conf_count, + phandle); + if (val < 0) { + debug("%s: Cannot find phandle %d\n", __func__, + phandle); + return -EINVAL; + } + gpio_conf[gpio] = val | + fdt32_to_cpu(prop[i * 3 + 2]) << PIRQ_SHIFT; + } + } + + return 0; +} + +static void broadwell_pinctrl_commit(struct pch_lp_gpio_regs *regs, + struct pin_info *pin_info, + int gpio_conf[], int count) +{ + u32 owner_gpio[GPIO_BANKS] = {0}; + u32 route_smi[GPIO_BANKS] = {0}; + u32 irq_enable[GPIO_BANKS] = {0}; + u32 reset_rsmrst[GPIO_BANKS] = {0}; + u32 pirq2apic = 0; + int set, bit, gpio = 0; + + for (gpio = 0; gpio < MAX_GPIOS; gpio++) { + int confnum = gpio_conf[gpio] & CONF_MASK; + struct pin_info *pin = &pin_info[confnum]; + u32 val; + + val = pin->mode_gpio << CONFA_MODE_SHIFT | + pin->dir_input << CONFA_DIR_SHIFT | + pin->invert << CONFA_INVERT_SHIFT | + pin->trigger_level << CONFA_TRIGGER_SHIFT | + pin->output_high << CONFA_OUTPUT_SHIFT; + outl(val, ®s->config[gpio].conf_a); + outl(pin->sense_disable << CONFB_SENSE_SHIFT, + ®s->config[gpio].conf_b); + + /* Determine set and bit based on GPIO number */ + set = gpio / GPIO_PER_BANK; + bit = gpio % GPIO_PER_BANK; + + /* Apply settings to set specific bits */ + owner_gpio[set] |= pin->owner_gpio << bit; + route_smi[set] |= pin->route_smi << bit; + irq_enable[set] |= pin->irq_enable << bit; + reset_rsmrst[set] |= pin->reset_rsmrst << bit; + + /* PIRQ to IO-APIC map */ + if (pin->pirq_apic_route) + pirq2apic |= gpio_conf[gpio] >> PIRQ_SHIFT; + debug("gpio %d: conf %d, mode_gpio %d, dir_input %d, output_high %d\n", + gpio, confnum, pin->mode_gpio, pin->dir_input, + pin->output_high); + } + + for (set = 0; set < GPIO_BANKS; set++) { + outl(owner_gpio[set], ®s->own[set]); + outl(route_smi[set], ®s->gpi_route[set]); + outl(irq_enable[set], ®s->gpi_ie[set]); + outl(reset_rsmrst[set], ®s->rst_sel[set]); + } + + outl(pirq2apic, ®s->pirq_to_ioxapic); +} + +static int broadwell_pinctrl_probe(struct udevice *dev) +{ + struct pch_lp_gpio_regs *regs; + struct pin_info conf[12]; + int gpio_conf[MAX_GPIOS]; + struct udevice *pch; + int conf_count; + u32 gpiobase; + int ret; + + ret = uclass_first_device(UCLASS_PCH, &pch); + if (ret) + return ret; + if (!pch) + return -ENODEV; + debug("%s: start\n", __func__); + + /* Only init once, before relocation */ + if (gd->flags & GD_FLG_RELOC) + return 0; + + /* + * Get the memory/io base address to configure every pins. + * IOBASE is used to configure the mode/pads + * GPIOBASE is used to configure the direction and default value + */ + ret = pch_get_gpio_base(pch, &gpiobase); + if (ret) { + debug("%s: invalid GPIOBASE address (%08x)\n", __func__, + gpiobase); + return -EINVAL; + } + + conf_count = broadwell_pinctrl_read_configs(dev, conf, + ARRAY_SIZE(conf)); + if (conf_count < 0) { + debug("%s: Cannot read configs: err=%d\n", __func__, ret); + return conf_count; + } + + /* + * Assume that pin settings are provided for every pin. Pins not + * mentioned will get the first config mentioned in the list. + */ + ret = broadwell_pinctrl_read_pins(dev, conf, conf_count, gpio_conf, + MAX_GPIOS); + if (ret) { + debug("%s: Cannot read pin settings: err=%d\n", __func__, ret); + return ret; + } + + regs = (struct pch_lp_gpio_regs *)gpiobase; + broadwell_pinctrl_commit(regs, conf, gpio_conf, ARRAY_SIZE(conf)); + + debug("%s: done\n", __func__); + + return 0; +} + +static const struct udevice_id broadwell_pinctrl_match[] = { + { .compatible = "intel,x86-broadwell-pinctrl", + .data = X86_SYSCON_PINCONF }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(broadwell_pinctrl) = { + .name = "broadwell_pinctrl", + .id = UCLASS_SYSCON, + .of_match = broadwell_pinctrl_match, + .probe = broadwell_pinctrl_probe, +}; diff --git a/arch/x86/cpu/broadwell/power_state.c b/arch/x86/cpu/broadwell/power_state.c new file mode 100644 index 0000000000..2b9a6bf3a1 --- /dev/null +++ b/arch/x86/cpu/broadwell/power_state.c @@ -0,0 +1,90 @@ +/* + * From coreboot src/soc/intel/broadwell/romstage/power_state.c + * + * Copyright (C) 2016 Google, Inc. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <pci.h> +#include <asm/io.h> +#include <asm/intel_regs.h> +#include <asm/arch/iomap.h> +#include <asm/arch/lpc.h> +#include <asm/arch/pch.h> +#include <asm/arch/pm.h> + +/* Return 0, 3, or 5 to indicate the previous sleep state. */ +static int prev_sleep_state(struct chipset_power_state *ps) +{ + /* Default to S0. */ + int prev_sleep_state = SLEEP_STATE_S0; + + if (ps->pm1_sts & WAK_STS) { + switch ((ps->pm1_cnt & SLP_TYP) >> SLP_TYP_SHIFT) { +#if CONFIG_HAVE_ACPI_RESUME + case SLP_TYP_S3: + prev_sleep_state = SLEEP_STATE_S3; + break; +#endif + case SLP_TYP_S5: + prev_sleep_state = SLEEP_STATE_S5; + break; + } + /* Clear SLP_TYP. */ + outl(ps->pm1_cnt & ~(SLP_TYP), ACPI_BASE_ADDRESS + PM1_CNT); + } + + if (ps->gen_pmcon3 & (PWR_FLR | SUS_PWR_FLR)) + prev_sleep_state = SLEEP_STATE_S5; + + return prev_sleep_state; +} + +static void dump_power_state(struct chipset_power_state *ps) +{ + debug("PM1_STS: %04x\n", ps->pm1_sts); + debug("PM1_EN: %04x\n", ps->pm1_en); + debug("PM1_CNT: %08x\n", ps->pm1_cnt); + debug("TCO_STS: %04x %04x\n", ps->tco1_sts, ps->tco2_sts); + + debug("GPE0_STS: %08x %08x %08x %08x\n", + ps->gpe0_sts[0], ps->gpe0_sts[1], + ps->gpe0_sts[2], ps->gpe0_sts[3]); + debug("GPE0_EN: %08x %08x %08x %08x\n", + ps->gpe0_en[0], ps->gpe0_en[1], + ps->gpe0_en[2], ps->gpe0_en[3]); + + debug("GEN_PMCON: %04x %04x %04x\n", + ps->gen_pmcon1, ps->gen_pmcon2, ps->gen_pmcon3); + + debug("Previous Sleep State: S%d\n", + ps->prev_sleep_state); +} + +/* Fill power state structure from ACPI PM registers */ +void power_state_get(struct udevice *pch_dev, struct chipset_power_state *ps) +{ + ps->pm1_sts = inw(ACPI_BASE_ADDRESS + PM1_STS); + ps->pm1_en = inw(ACPI_BASE_ADDRESS + PM1_EN); + ps->pm1_cnt = inl(ACPI_BASE_ADDRESS + PM1_CNT); + ps->tco1_sts = inw(ACPI_BASE_ADDRESS + TCO1_STS); + ps->tco2_sts = inw(ACPI_BASE_ADDRESS + TCO2_STS); + ps->gpe0_sts[0] = inl(ACPI_BASE_ADDRESS + GPE0_STS(0)); + ps->gpe0_sts[1] = inl(ACPI_BASE_ADDRESS + GPE0_STS(1)); + ps->gpe0_sts[2] = inl(ACPI_BASE_ADDRESS + GPE0_STS(2)); + ps->gpe0_sts[3] = inl(ACPI_BASE_ADDRESS + GPE0_STS(3)); + ps->gpe0_en[0] = inl(ACPI_BASE_ADDRESS + GPE0_EN(0)); + ps->gpe0_en[1] = inl(ACPI_BASE_ADDRESS + GPE0_EN(1)); + ps->gpe0_en[2] = inl(ACPI_BASE_ADDRESS + GPE0_EN(2)); + ps->gpe0_en[3] = inl(ACPI_BASE_ADDRESS + GPE0_EN(3)); + + dm_pci_read_config16(pch_dev, GEN_PMCON_1, &ps->gen_pmcon1); + dm_pci_read_config16(pch_dev, GEN_PMCON_2, &ps->gen_pmcon2); + dm_pci_read_config16(pch_dev, GEN_PMCON_3, &ps->gen_pmcon3); + + ps->prev_sleep_state = prev_sleep_state(ps); + + dump_power_state(ps); +} diff --git a/arch/x86/cpu/broadwell/refcode.c b/arch/x86/cpu/broadwell/refcode.c new file mode 100644 index 0000000000..436c6c49c3 --- /dev/null +++ b/arch/x86/cpu/broadwell/refcode.c @@ -0,0 +1,113 @@ +/* + * Read a coreboot rmodule and execute it. + * The rmodule_header struct is from coreboot. + * + * Copyright (c) 2016 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <errno.h> +#include <asm/arch/pei_data.h> + +#define RMODULE_MAGIC 0xf8fe +#define RMODULE_VERSION_1 1 + +/* + * All fields with '_offset' in the name are byte offsets into the flat blob. + * The linker and the linker script takes are of assigning the values. + */ +struct rmodule_header { + uint16_t magic; + uint8_t version; + uint8_t type; + /* The payload represents the program's loadable code and data */ + uint32_t payload_begin_offset; + uint32_t payload_end_offset; + /* Begin and of relocation information about the program module */ + uint32_t relocations_begin_offset; + uint32_t relocations_end_offset; + /* + * The starting address of the linked program. This address is vital + * for determining relocation offsets as the relocation info and other + * symbols (bss, entry point) need this value as a basis to calculate + * the offsets. + */ + uint32_t module_link_start_address; + /* + * The module_program_size is the size of memory used while running + * the program. The program is assumed to consume a contiguous amount + * of memory + */ + uint32_t module_program_size; + /* This is program's execution entry point */ + uint32_t module_entry_point; + /* + * Optional parameter structure that can be used to pass data into + * the module + */ + uint32_t parameters_begin; + uint32_t parameters_end; + /* BSS section information so the loader can clear the bss */ + uint32_t bss_begin; + uint32_t bss_end; + /* Add some room for growth */ + uint32_t padding[4]; +} __packed; + +int cpu_run_reference_code(void) +{ + struct pei_data _pei_data __aligned(8); + struct pei_data *pei_data = &_pei_data; + asmlinkage int (*func)(void *); + struct rmodule_header *hdr; + char *src, *dest; + int ret, dummy; + int size; + + hdr = (struct rmodule_header *)CONFIG_X86_REFCODE_ADDR; + debug("Extracting code from rmodule at %p\n", hdr); + if (hdr->magic != RMODULE_MAGIC) { + debug("Invalid rmodule magic\n"); + return -EINVAL; + } + if (hdr->module_link_start_address != 0) { + debug("Link start address must be 0\n"); + return -EPERM; + } + if (hdr->module_entry_point != 0) { + debug("Entry point must be 0\n"); + return -EPERM; + } + + memset(pei_data, '\0', sizeof(struct pei_data)); + broadwell_fill_pei_data(pei_data); + mainboard_fill_pei_data(pei_data); + pei_data->saved_data = (void *)&dummy; + + src = (char *)hdr + hdr->payload_begin_offset; + dest = (char *)CONFIG_X86_REFCODE_RUN_ADDR; + + size = hdr->payload_end_offset - hdr->payload_begin_offset; + debug("Copying refcode from %p to %p, size %x\n", src, dest, size); + memcpy(dest, src, size); + + size = hdr->bss_end - hdr->bss_begin; + debug("Zeroing BSS at %p, size %x\n", dest + hdr->bss_begin, size); + memset(dest + hdr->bss_begin, '\0', size); + + func = (asmlinkage int (*)(void *))dest; + debug("Running reference code at %p\n", func); +#ifdef DEBUG + print_buffer(CONFIG_X86_REFCODE_RUN_ADDR, (void *)func, 1, 0x40, 0); +#endif + ret = func(pei_data); + if (ret != 0) { + debug("Reference code returned %d\n", ret); + return -EL2HLT; + } + debug("Refereence code completed\n"); + + return 0; +} diff --git a/arch/x86/cpu/broadwell/sata.c b/arch/x86/cpu/broadwell/sata.c new file mode 100644 index 0000000000..dfb8486d06 --- /dev/null +++ b/arch/x86/cpu/broadwell/sata.c @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2016 Google, Inc + * + * From coreboot src/soc/intel/broadwell/sata.c + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <dm.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <asm/intel_regs.h> +#include <asm/lpc_common.h> +#include <asm/pch_common.h> +#include <asm/pch_common.h> +#include <asm/arch/pch.h> + +struct sata_platdata { + int port_map; + uint port0_gen3_tx; + uint port1_gen3_tx; + uint port0_gen3_dtle; + uint port1_gen3_dtle; + + /* + * SATA DEVSLP Mux + * 0 = port 0 DEVSLP on DEVSLP0/GPIO33 + * 1 = port 3 DEVSLP on DEVSLP0/GPIO33 + */ + int devslp_mux; + + /* + * DEVSLP Disable + * 0: DEVSLP is enabled + * 1: DEVSLP is disabled + */ + int devslp_disable; +}; + +static void broadwell_sata_init(struct udevice *dev) +{ + struct sata_platdata *plat = dev_get_platdata(dev); + u32 reg32; + u8 *abar; + u16 reg16; + int port; + + debug("SATA: Initializing controller in AHCI mode.\n"); + + /* Set timings */ + dm_pci_write_config16(dev, IDE_TIM_PRI, IDE_DECODE_ENABLE); + dm_pci_write_config16(dev, IDE_TIM_SEC, IDE_DECODE_ENABLE); + + /* for AHCI, Port Enable is managed in memory mapped space */ + dm_pci_read_config16(dev, 0x92, ®16); + reg16 &= ~0xf; + reg16 |= 0x8000 | plat->port_map; + dm_pci_write_config16(dev, 0x92, reg16); + udelay(2); + + /* Setup register 98h */ + dm_pci_read_config32(dev, 0x98, ®32); + reg32 &= ~((1 << 31) | (1 << 30)); + reg32 |= 1 << 23; + reg32 |= 1 << 24; /* Enable MPHY Dynamic Power Gating */ + dm_pci_write_config32(dev, 0x98, reg32); + + /* Setup register 9Ch */ + reg16 = 0; /* Disable alternate ID */ + reg16 = 1 << 5; /* BWG step 12 */ + dm_pci_write_config16(dev, 0x9c, reg16); + + /* SATA Initialization register */ + reg32 = 0x183; + reg32 |= (plat->port_map ^ 0xf) << 24; + reg32 |= (plat->devslp_mux & 1) << 15; + dm_pci_write_config32(dev, 0x94, reg32); + + /* Initialize AHCI memory-mapped space */ + dm_pci_read_config32(dev, PCI_BASE_ADDRESS_5, ®32); + abar = (u8 *)reg32; + debug("ABAR: %p\n", abar); + + /* CAP (HBA Capabilities) : enable power management */ + clrsetbits_le32(abar + 0x00, 0x00020060 /* SXS+EMS+PMS */, + 0x0c006000 /* PSC+SSC+SALP+SSS */ | + 1 << 18); /* SAM: SATA AHCI MODE ONLY */ + + /* PI (Ports implemented) */ + writel(plat->port_map, abar + 0x0c); + (void) readl(abar + 0x0c); /* Read back 1 */ + (void) readl(abar + 0x0c); /* Read back 2 */ + + /* CAP2 (HBA Capabilities Extended)*/ + if (plat->devslp_disable) { + clrbits_le32(abar + 0x24, 1 << 3); + } else { + /* Enable DEVSLP */ + setbits_le32(abar + 0x24, 1 << 5 | 1 << 4 | 1 << 3 | 1 << 2); + + for (port = 0; port < 4; port++) { + if (!(plat->port_map & (1 << port))) + continue; + /* DEVSLP DSP */ + setbits_le32(abar + 0x144 + (0x80 * port), 1 << 1); + } + } + + /* Static Power Gating for unused ports */ + reg32 = readl(RCB_REG(0x3a84)); + /* Port 3 and 2 disabled */ + if ((plat->port_map & ((1 << 3)|(1 << 2))) == 0) + reg32 |= (1 << 24) | (1 << 26); + /* Port 1 and 0 disabled */ + if ((plat->port_map & ((1 << 1)|(1 << 0))) == 0) + reg32 |= (1 << 20) | (1 << 18); + writel(reg32, RCB_REG(0x3a84)); + + /* Set Gen3 Transmitter settings if needed */ + if (plat->port0_gen3_tx) + pch_iobp_update(SATA_IOBP_SP0_SECRT88, + ~(SATA_SECRT88_VADJ_MASK << + SATA_SECRT88_VADJ_SHIFT), + (plat->port0_gen3_tx & + SATA_SECRT88_VADJ_MASK) + << SATA_SECRT88_VADJ_SHIFT); + + if (plat->port1_gen3_tx) + pch_iobp_update(SATA_IOBP_SP1_SECRT88, + ~(SATA_SECRT88_VADJ_MASK << + SATA_SECRT88_VADJ_SHIFT), + (plat->port1_gen3_tx & + SATA_SECRT88_VADJ_MASK) + << SATA_SECRT88_VADJ_SHIFT); + + /* Set Gen3 DTLE DATA / EDGE registers if needed */ + if (plat->port0_gen3_dtle) { + pch_iobp_update(SATA_IOBP_SP0DTLE_DATA, + ~(SATA_DTLE_MASK << SATA_DTLE_DATA_SHIFT), + (plat->port0_gen3_dtle & SATA_DTLE_MASK) + << SATA_DTLE_DATA_SHIFT); + + pch_iobp_update(SATA_IOBP_SP0DTLE_EDGE, + ~(SATA_DTLE_MASK << SATA_DTLE_EDGE_SHIFT), + (plat->port0_gen3_dtle & SATA_DTLE_MASK) + << SATA_DTLE_EDGE_SHIFT); + } + + if (plat->port1_gen3_dtle) { + pch_iobp_update(SATA_IOBP_SP1DTLE_DATA, + ~(SATA_DTLE_MASK << SATA_DTLE_DATA_SHIFT), + (plat->port1_gen3_dtle & SATA_DTLE_MASK) + << SATA_DTLE_DATA_SHIFT); + + pch_iobp_update(SATA_IOBP_SP1DTLE_EDGE, + ~(SATA_DTLE_MASK << SATA_DTLE_EDGE_SHIFT), + (plat->port1_gen3_dtle & SATA_DTLE_MASK) + << SATA_DTLE_EDGE_SHIFT); + } + + /* + * Additional Programming Requirements for Power Optimizer + */ + + /* Step 1 */ + pch_common_sir_write(dev, 0x64, 0x883c9003); + + /* Step 2: SIR 68h[15:0] = 880Ah */ + reg32 = pch_common_sir_read(dev, 0x68); + reg32 &= 0xffff0000; + reg32 |= 0x880a; + pch_common_sir_write(dev, 0x68, reg32); + + /* Step 3: SIR 60h[3] = 1 */ + reg32 = pch_common_sir_read(dev, 0x60); + reg32 |= (1 << 3); + pch_common_sir_write(dev, 0x60, reg32); + + /* Step 4: SIR 60h[0] = 1 */ + reg32 = pch_common_sir_read(dev, 0x60); + reg32 |= (1 << 0); + pch_common_sir_write(dev, 0x60, reg32); + + /* Step 5: SIR 60h[1] = 1 */ + reg32 = pch_common_sir_read(dev, 0x60); + reg32 |= (1 << 1); + pch_common_sir_write(dev, 0x60, reg32); + + /* Clock Gating */ + pch_common_sir_write(dev, 0x70, 0x3f00bf1f); + pch_common_sir_write(dev, 0x54, 0xcf000f0f); + pch_common_sir_write(dev, 0x58, 0x00190000); + clrsetbits_le32(RCB_REG(0x333c), 0x00300000, 0x00c00000); + + dm_pci_read_config32(dev, 0x300, ®32); + reg32 |= 1 << 17 | 1 << 16 | 1 << 19; + reg32 |= 1 << 31 | 1 << 30 | 1 << 29; + dm_pci_write_config32(dev, 0x300, reg32); + + dm_pci_read_config32(dev, 0x98, ®32); + reg32 |= 1 << 29; + dm_pci_write_config32(dev, 0x98, reg32); + + /* Register Lock */ + dm_pci_read_config32(dev, 0x9c, ®32); + reg32 |= 1 << 31; + dm_pci_write_config32(dev, 0x9c, reg32); +} + +static int broadwell_sata_enable(struct udevice *dev) +{ + struct sata_platdata *plat = dev_get_platdata(dev); + struct gpio_desc desc; + u16 map; + int ret; + + /* + * Set SATA controller mode early so the resource allocator can + * properly assign IO/Memory resources for the controller. + */ + map = 0x0060; + + map |= (plat->port_map ^ 0x3f) << 8; + dm_pci_write_config16(dev, 0x90, map); + + ret = gpio_request_by_name(dev, "reset-gpio", 0, &desc, GPIOD_IS_OUT); + if (ret) + return ret; + + return 0; +} + +static int broadwell_sata_ofdata_to_platdata(struct udevice *dev) +{ + struct sata_platdata *plat = dev_get_platdata(dev); + const void *blob = gd->fdt_blob; + int node = dev->of_offset; + + plat->port_map = fdtdec_get_int(blob, node, "intel,sata-port-map", 0); + plat->port0_gen3_tx = fdtdec_get_int(blob, node, + "intel,sata-port0-gen3-tx", 0); + + return 0; +} + +static int broadwell_sata_probe(struct udevice *dev) +{ + if (!(gd->flags & GD_FLG_RELOC)) + return broadwell_sata_enable(dev); + else + broadwell_sata_init(dev); + + return 0; +} + +static const struct udevice_id broadwell_ahci_ids[] = { + { .compatible = "intel,wildcatpoint-ahci" }, + { } +}; + +U_BOOT_DRIVER(ahci_broadwell_drv) = { + .name = "ahci_broadwell", + .id = UCLASS_DISK, + .of_match = broadwell_ahci_ids, + .ofdata_to_platdata = broadwell_sata_ofdata_to_platdata, + .probe = broadwell_sata_probe, + .platdata_auto_alloc_size = sizeof(struct sata_platdata), +}; diff --git a/arch/x86/cpu/broadwell/sdram.c b/arch/x86/cpu/broadwell/sdram.c new file mode 100644 index 0000000000..4bf5d15b26 --- /dev/null +++ b/arch/x86/cpu/broadwell/sdram.c @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2016 Google, Inc + * + * From coreboot src/soc/intel/broadwell/romstage/raminit.c + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <dm.h> +#include <pci.h> +#include <syscon.h> +#include <asm/cpu.h> +#include <asm/io.h> +#include <asm/lpc_common.h> +#include <asm/mrccache.h> +#include <asm/mrc_common.h> +#include <asm/mtrr.h> +#include <asm/pci.h> +#include <asm/arch/iomap.h> +#include <asm/arch/me.h> +#include <asm/arch/pch.h> +#include <asm/arch/pei_data.h> +#include <asm/arch/pm.h> + +ulong board_get_usable_ram_top(ulong total_size) +{ + return mrc_common_board_get_usable_ram_top(total_size); +} + +void dram_init_banksize(void) +{ + mrc_common_dram_init_banksize(); +} + +void broadwell_fill_pei_data(struct pei_data *pei_data) +{ + pei_data->pei_version = PEI_VERSION; + pei_data->board_type = BOARD_TYPE_ULT; + pei_data->pciexbar = MCFG_BASE_ADDRESS; + pei_data->smbusbar = SMBUS_BASE_ADDRESS; + pei_data->ehcibar = EARLY_EHCI_BAR; + pei_data->xhcibar = EARLY_XHCI_BAR; + pei_data->gttbar = EARLY_GTT_BAR; + pei_data->pmbase = ACPI_BASE_ADDRESS; + pei_data->gpiobase = GPIO_BASE_ADDRESS; + pei_data->tseg_size = CONFIG_SMM_TSEG_SIZE; + pei_data->temp_mmio_base = EARLY_TEMP_MMIO; + pei_data->tx_byte = sdram_console_tx_byte; + pei_data->ddr_refresh_2x = 1; +} + +static inline void pei_data_usb2_port(struct pei_data *pei_data, int port, + uint16_t length, uint8_t enable, + uint8_t oc_pin, uint8_t location) +{ + pei_data->usb2_ports[port].length = length; + pei_data->usb2_ports[port].enable = enable; + pei_data->usb2_ports[port].oc_pin = oc_pin; + pei_data->usb2_ports[port].location = location; +} + +static inline void pei_data_usb3_port(struct pei_data *pei_data, int port, + uint8_t enable, uint8_t oc_pin, + uint8_t fixed_eq) +{ + pei_data->usb3_ports[port].enable = enable; + pei_data->usb3_ports[port].oc_pin = oc_pin; + pei_data->usb3_ports[port].fixed_eq = fixed_eq; +} + +void mainboard_fill_pei_data(struct pei_data *pei_data) +{ + /* DQ byte map for Samus board */ + const u8 dq_map[2][6][2] = { + { { 0x0F, 0xF0 }, { 0x00, 0xF0 }, { 0x0F, 0xF0 }, + { 0x0F, 0x00 }, { 0xFF, 0x00 }, { 0xFF, 0x00 } }, + { { 0x0F, 0xF0 }, { 0x00, 0xF0 }, { 0x0F, 0xF0 }, + { 0x0F, 0x00 }, { 0xFF, 0x00 }, { 0xFF, 0x00 } } }; + /* DQS CPU<>DRAM map for Samus board */ + const u8 dqs_map[2][8] = { + { 2, 0, 1, 3, 6, 4, 7, 5 }, + { 2, 1, 0, 3, 6, 5, 4, 7 } }; + + pei_data->ec_present = 1; + + /* One installed DIMM per channel */ + pei_data->dimm_channel0_disabled = 2; + pei_data->dimm_channel1_disabled = 2; + + memcpy(pei_data->dq_map, dq_map, sizeof(dq_map)); + memcpy(pei_data->dqs_map, dqs_map, sizeof(dqs_map)); + + /* P0: HOST PORT */ + pei_data_usb2_port(pei_data, 0, 0x0080, 1, 0, + USB_PORT_BACK_PANEL); + /* P1: HOST PORT */ + pei_data_usb2_port(pei_data, 1, 0x0080, 1, 1, + USB_PORT_BACK_PANEL); + /* P2: RAIDEN */ + pei_data_usb2_port(pei_data, 2, 0x0080, 1, USB_OC_PIN_SKIP, + USB_PORT_BACK_PANEL); + /* P3: SD CARD */ + pei_data_usb2_port(pei_data, 3, 0x0040, 1, USB_OC_PIN_SKIP, + USB_PORT_INTERNAL); + /* P4: RAIDEN */ + pei_data_usb2_port(pei_data, 4, 0x0080, 1, USB_OC_PIN_SKIP, + USB_PORT_BACK_PANEL); + /* P5: WWAN (Disabled) */ + pei_data_usb2_port(pei_data, 5, 0x0000, 0, USB_OC_PIN_SKIP, + USB_PORT_SKIP); + /* P6: CAMERA */ + pei_data_usb2_port(pei_data, 6, 0x0040, 1, USB_OC_PIN_SKIP, + USB_PORT_INTERNAL); + /* P7: BT */ + pei_data_usb2_port(pei_data, 7, 0x0040, 1, USB_OC_PIN_SKIP, + USB_PORT_INTERNAL); + + /* P1: HOST PORT */ + pei_data_usb3_port(pei_data, 0, 1, 0, 0); + /* P2: HOST PORT */ + pei_data_usb3_port(pei_data, 1, 1, 1, 0); + /* P3: RAIDEN */ + pei_data_usb3_port(pei_data, 2, 1, USB_OC_PIN_SKIP, 0); + /* P4: RAIDEN */ + pei_data_usb3_port(pei_data, 3, 1, USB_OC_PIN_SKIP, 0); +} + +static unsigned long get_top_of_ram(struct udevice *dev) +{ + /* + * Base of DPR is top of usable DRAM below 4GiB. The register has + * 1 MiB alignment and reports the TOP of the range, the base + * must be calculated from the size in MiB in bits 11:4. + */ + u32 dpr, tom; + + dm_pci_read_config32(dev, DPR, &dpr); + tom = dpr & ~((1 << 20) - 1); + + debug("dpt %08x tom %08x\n", dpr, tom); + /* Subtract DMA Protected Range size if enabled */ + if (dpr & DPR_EPM) + tom -= (dpr & DPR_SIZE_MASK) << 16; + + return (unsigned long)tom; +} + +/** + * 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 + * + * @dev: Northbridge device + */ +static int sdram_find(struct udevice *dev) +{ + struct memory_info *info = &gd->arch.meminfo; + ulong top_of_ram; + + top_of_ram = get_top_of_ram(dev); + mrc_add_memory_area(info, 0, top_of_ram); + + /* Add MTRRs for memory */ + mtrr_add_request(MTRR_TYPE_WRBACK, 0, 2ULL << 30); + + return 0; +} + +static int prepare_mrc_cache(struct pei_data *pei_data) +{ + struct mrc_data_container *mrc_cache; + struct mrc_region entry; + int ret; + + ret = mrccache_get_region(NULL, &entry); + if (ret) + return ret; + mrc_cache = mrccache_find_current(&entry); + if (!mrc_cache) + return -ENOENT; + + pei_data->saved_data = mrc_cache->data; + pei_data->saved_data_size = mrc_cache->data_size; + debug("%s: at %p, size %x checksum %04x\n", __func__, + pei_data->saved_data, pei_data->saved_data_size, + mrc_cache->checksum); + + return 0; +} + +int reserve_arch(void) +{ + return mrccache_reserve(); +} + +int dram_init(void) +{ + struct pei_data _pei_data __aligned(8); + struct pei_data *pei_data = &_pei_data; + struct udevice *dev, *me_dev, *pch_dev; + struct chipset_power_state ps; + const void *spd_data; + int ret, size; + + memset(pei_data, '\0', sizeof(struct pei_data)); + + /* Print ME state before MRC */ + ret = syscon_get_by_driver_data(X86_SYSCON_ME, &me_dev); + if (ret) + return ret; + intel_me_status(me_dev); + + /* Save ME HSIO version */ + ret = uclass_first_device(UCLASS_PCH, &pch_dev); + if (ret) + return ret; + if (!pch_dev) + return -ENODEV; + power_state_get(pch_dev, &ps); + + intel_me_hsio_version(me_dev, &ps.hsio_version, &ps.hsio_checksum); + + broadwell_fill_pei_data(pei_data); + mainboard_fill_pei_data(pei_data); + + ret = uclass_first_device(UCLASS_NORTHBRIDGE, &dev); + if (ret) + return ret; + if (!dev) + return -ENODEV; + size = 256; + ret = mrc_locate_spd(dev, size, &spd_data); + if (ret) + return ret; + memcpy(pei_data->spd_data[0][0], spd_data, size); + memcpy(pei_data->spd_data[1][0], spd_data, size); + + ret = prepare_mrc_cache(pei_data); + if (ret) + debug("prepare_mrc_cache failed: %d\n", ret); + + debug("PEI version %#x\n", pei_data->pei_version); + ret = mrc_common_init(dev, pei_data, true); + if (ret) + return ret; + debug("Memory init done\n"); + + ret = sdram_find(dev); + if (ret) + return ret; + gd->ram_size = gd->arch.meminfo.total_32bit_memory; + debug("RAM size %llx\n", (unsigned long long)gd->ram_size); + + debug("MRC output data length %#x at %p\n", pei_data->data_to_save_size, + pei_data->data_to_save); + /* S3 resume: don't save scrambler seed or MRC data */ + if (pei_data->boot_mode != SLEEP_STATE_S3) { + /* + * This will be copied to SDRAM in reserve_arch(), then written + * to SPI flash in mrccache_save() + */ + gd->arch.mrc_output = (char *)pei_data->data_to_save; + gd->arch.mrc_output_len = pei_data->data_to_save_size; + } + gd->arch.pei_meminfo = pei_data->meminfo; + + return 0; +} + +/* Use this hook to save our SDRAM parameters */ +int misc_init_r(void) +{ + int ret; + + ret = mrccache_save(); + if (ret) + printf("Unable to save MRC data: %d\n", ret); + else + debug("Saved MRC cache data\n"); + + return 0; +} + +void board_debug_uart_init(void) +{ + struct udevice *bus = NULL; + + /* com1 / com2 decode range */ + pci_x86_write_config(bus, PCH_DEV_LPC, LPC_IO_DEC, 1 << 4, PCI_SIZE_16); + + pci_x86_write_config(bus, PCH_DEV_LPC, LPC_EN, COMA_LPC_EN, + PCI_SIZE_16); +} + +static const struct udevice_id broadwell_syscon_ids[] = { + { .compatible = "intel,me", .data = X86_SYSCON_ME }, + { .compatible = "intel,gma", .data = X86_SYSCON_GMA }, + { } +}; + +U_BOOT_DRIVER(syscon_intel_me) = { + .name = "intel_me_syscon", + .id = UCLASS_SYSCON, + .of_match = broadwell_syscon_ids, +}; diff --git a/arch/x86/cpu/coreboot/sdram.c b/arch/x86/cpu/coreboot/sdram.c index 32f595d537..7115e7a151 100644 --- a/arch/x86/cpu/coreboot/sdram.c +++ b/arch/x86/cpu/coreboot/sdram.c @@ -9,7 +9,6 @@ #include <common.h> #include <asm/e820.h> #include <asm/arch/sysinfo.h> -#include <asm/arch/tables.h> DECLARE_GLOBAL_DATA_PTR; diff --git a/arch/x86/cpu/coreboot/tables.c b/arch/x86/cpu/coreboot/tables.c index 2b12b19ba2..543e51d96a 100644 --- a/arch/x86/cpu/coreboot/tables.c +++ b/arch/x86/cpu/coreboot/tables.c @@ -10,7 +10,6 @@ #include <common.h> #include <net.h> #include <asm/arch/sysinfo.h> -#include <asm/arch/tables.h> /* * This needs to be in the .data section so that it's copied over during diff --git a/arch/x86/cpu/cpu.c b/arch/x86/cpu/cpu.c index 6c3a748f75..233a6c8695 100644 --- a/arch/x86/cpu/cpu.c +++ b/arch/x86/cpu/cpu.c @@ -27,6 +27,7 @@ #include <asm/control_regs.h> #include <asm/cpu.h> #include <asm/lapic.h> +#include <asm/microcode.h> #include <asm/mp.h> #include <asm/msr.h> #include <asm/mtrr.h> @@ -71,7 +72,7 @@ struct cpuinfo_x86 { * List of cpu vendor strings along with their normalized * id values. */ -static struct { +static const struct { int vendor; const char *name; } x86_vendors[] = { @@ -333,6 +334,16 @@ static inline void get_fms(struct cpuinfo_x86 *c, uint32_t tfms) c->x86_model += ((tfms >> 16) & 0xF) << 4; } +u32 cpu_get_family_model(void) +{ + return gd->arch.x86_device & 0x0fff0ff0; +} + +u32 cpu_get_stepping(void) +{ + return gd->arch.x86_mask; +} + int x86_cpu_init_f(void) { const u32 em_rst = ~X86_CR0_EM; @@ -459,14 +470,14 @@ void flush_cache(unsigned long dummy1, unsigned long dummy2) __weak void reset_cpu(ulong addr) { /* Do a hard reset through the chipset's reset control register */ - outb(SYS_RST | RST_CPU, PORT_RESET); + outb(SYS_RST | RST_CPU, IO_PORT_RESET); for (;;) cpu_hlt(); } void x86_full_reset(void) { - outb(FULL_RST | SYS_RST | RST_CPU, PORT_RESET); + outb(FULL_RST | SYS_RST | RST_CPU, IO_PORT_RESET); } int dcache_status(void) diff --git a/arch/x86/cpu/intel_common/Makefile b/arch/x86/cpu/intel_common/Makefile new file mode 100644 index 0000000000..804c539ca4 --- /dev/null +++ b/arch/x86/cpu/intel_common/Makefile @@ -0,0 +1,16 @@ +# +# Copyright (c) 2016 Google, Inc +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_HAVE_MRC) += car.o +obj-y += cpu.o +obj-y += lpc.o +obj-$(CONFIG_HAVE_MRC) += me_status.o +ifndef CONFIG_TARGET_EFI +obj-y += microcode.o +endif +obj-y += pch.o +obj-$(CONFIG_HAVE_MRC) += report_platform.o +obj-$(CONFIG_HAVE_MRC) += mrc.o diff --git a/arch/x86/cpu/ivybridge/car.S b/arch/x86/cpu/intel_common/car.S index 1defabf91f..6e0db96c01 100644 --- a/arch/x86/cpu/ivybridge/car.S +++ b/arch/x86/cpu/intel_common/car.S @@ -12,12 +12,12 @@ */ #include <common.h> +#include <asm/microcode.h> #include <asm/msr-index.h> #include <asm/mtrr.h> #include <asm/post.h> #include <asm/processor.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) @@ -237,5 +237,7 @@ mtrr_table_end: .align 4 _dt_ucode_base_size: /* These next two fields are filled in by ifdtool */ +.globl ucode_base +ucode_base: /* Declared in microcode.h */ .long 0 /* microcode base */ .long 0 /* microcode size */ diff --git a/arch/x86/cpu/intel_common/cpu.c b/arch/x86/cpu/intel_common/cpu.c new file mode 100644 index 0000000000..93e4505e4d --- /dev/null +++ b/arch/x86/cpu/intel_common/cpu.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2016 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <asm/cpu_common.h> +#include <asm/intel_regs.h> +#include <asm/lapic.h> +#include <asm/lpc_common.h> +#include <asm/msr.h> +#include <asm/mtrr.h> +#include <asm/post.h> +#include <asm/microcode.h> + +DECLARE_GLOBAL_DATA_PTR; + +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 cpu_common_init(void) +{ + struct udevice *dev, *lpc; + int ret; + + /* Halt if there was a built in self test failure */ + ret = report_bist_failure(); + if (ret) + return ret; + + enable_lapic(); + + ret = microcode_update_intel(); + if (ret && ret != -EEXIST) + return ret; + + /* Enable upper 128bytes of CMOS */ + writel(1 << 2, RCB_REG(RC)); + + /* Early chipset init required before RAM init can work */ + uclass_first_device(UCLASS_NORTHBRIDGE, &dev); + + ret = uclass_first_device(UCLASS_LPC, &lpc); + if (ret) + return ret; + if (!lpc) + return -ENODEV; + + /* Cause the SATA device to do its early init */ + uclass_first_device(UCLASS_DISK, &dev); + + return 0; +} + +int cpu_set_flex_ratio_to_tdp_nominal(void) +{ + msr_t flex_ratio, msr; + u8 nominal_ratio; + + /* 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); + + debug("CPU: Soft reset to set up flex ratio\n"); + + /* 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, IO_PORT_RESET); + outb(SYS_RST | RST_CPU, IO_PORT_RESET); + cpu_hlt(); + + /* Not reached */ + return -EINVAL; +} diff --git a/arch/x86/cpu/intel_common/lpc.c b/arch/x86/cpu/intel_common/lpc.c new file mode 100644 index 0000000000..03cb45b636 --- /dev/null +++ b/arch/x86/cpu/intel_common/lpc.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2016 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <pch.h> +#include <pci.h> +#include <asm/intel_regs.h> +#include <asm/io.h> +#include <asm/lpc_common.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Enable Prefetching and Caching */ +static void enable_spi_prefetch(struct udevice *pch) +{ + u8 reg8; + + dm_pci_read_config8(pch, 0xdc, ®8); + reg8 &= ~(3 << 2); + reg8 |= (2 << 2); /* Prefetching and Caching Enabled */ + dm_pci_write_config8(pch, 0xdc, reg8); +} + +static void enable_port80_on_lpc(struct udevice *pch) +{ + /* Enable port 80 POST on LPC */ + dm_pci_write_config32(pch, PCH_RCBA_BASE, RCB_BASE_ADDRESS | 1); + clrbits_le32(RCB_REG(GCS), 4); +} + +/** + * lpc_early_init() - set up LPC serial ports and other early things + * + * @dev: LPC device + * @return 0 if OK, -ve on error + */ +int lpc_common_early_init(struct udevice *dev) +{ + struct udevice *pch = dev->parent; + struct reg_info { + u32 base; + u32 size; + } values[4], *ptr; + int count; + int i; + + count = fdtdec_get_int_array_count(gd->fdt_blob, dev->of_offset, + "intel,gen-dec", (u32 *)values, + sizeof(values) / sizeof(u32)); + if (count < 0) + return -EINVAL; + + /* Set COM1/COM2 decode range */ + dm_pci_write_config16(pch, LPC_IO_DEC, 0x0010); + + /* Enable PS/2 Keyboard/Mouse, EC areas and COM1 */ + dm_pci_write_config16(pch, 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); + dm_pci_write_config32(pch, LPC_GENX_DEC(i), reg); + } + + enable_spi_prefetch(pch); + + /* This is already done in start.S, but let's do it in C */ + enable_port80_on_lpc(pch); + + return 0; +} + +int lpc_set_spi_protect(struct udevice *dev, int bios_ctrl, bool protect) +{ + uint8_t bios_cntl; + + /* Adjust the BIOS write protect and SMM BIOS Write Protect Disable */ + dm_pci_read_config8(dev, bios_ctrl, &bios_cntl); + if (protect) { + bios_cntl &= ~BIOS_CTRL_BIOSWE; + bios_cntl |= BIT(5); + } else { + bios_cntl |= BIOS_CTRL_BIOSWE; + bios_cntl &= ~BIT(5); + } + dm_pci_write_config8(dev, bios_ctrl, bios_cntl); + + return 0; +} diff --git a/arch/x86/cpu/ivybridge/me_status.c b/arch/x86/cpu/intel_common/me_status.c index 15cf69f40e..130d219bc9 100644 --- a/arch/x86/cpu/ivybridge/me_status.c +++ b/arch/x86/cpu/intel_common/me_status.c @@ -128,7 +128,14 @@ static const char *const me_progress_policy_values[] = { [0x10] = "Required VSCC values for flash parts do not match", }; -void intel_me_status(struct me_hfs *hfs, struct me_gmes *gmes) + +/** + * _intel_me_status() - Check Intel Management Engine status + * + * struct hfs: Firmware status + * struct gmes: Management engine status + */ +static void _intel_me_status(struct me_hfs *hfs, struct me_gmes *gmes) { /* Check Current States */ debug("ME: FW Partition Table : %s\n", @@ -193,3 +200,14 @@ void intel_me_status(struct me_hfs *hfs, struct me_gmes *gmes) } debug("\n"); } + +void intel_me_status(struct udevice *me_dev) +{ + struct me_hfs hfs; + struct me_gmes gmes; + + pci_read_dword_ptr(me_dev, &hfs, PCI_ME_HFS); + pci_read_dword_ptr(me_dev, &gmes, PCI_ME_GMES); + + _intel_me_status(&hfs, &gmes); +} diff --git a/arch/x86/cpu/ivybridge/microcode_intel.c b/arch/x86/cpu/intel_common/microcode.c index 2440a97c48..daf0d69494 100644 --- a/arch/x86/cpu/ivybridge/microcode_intel.c +++ b/arch/x86/cpu/intel_common/microcode.c @@ -12,10 +12,12 @@ #include <fdtdec.h> #include <libfdt.h> #include <asm/cpu.h> +#include <asm/microcode.h> #include <asm/msr.h> #include <asm/msr-index.h> #include <asm/processor.h> -#include <asm/arch/microcode.h> + +DECLARE_GLOBAL_DATA_PTR; /** * struct microcode_update - standard microcode header from Intel @@ -62,8 +64,12 @@ static int microcode_decode_node(const void *blob, int node, return 0; } -static inline uint32_t microcode_read_rev(void) +int microcode_read_rev(void) { + /* Quark does not have microcode MSRs */ +#ifdef CONFIG_INTEL_QUARK + return 0; +#else /* * Some Intel CPUs can be very finicky about the CPUID sequence used. * So this is implemented in assembly so that it works reliably. @@ -88,6 +94,7 @@ static inline uint32_t microcode_read_rev(void) ); return high; +#endif } static void microcode_read_cpu(struct microcode_update *cpu) diff --git a/arch/x86/cpu/intel_common/mrc.c b/arch/x86/cpu/intel_common/mrc.c new file mode 100644 index 0000000000..01b6e866b5 --- /dev/null +++ b/arch/x86/cpu/intel_common/mrc.c @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2016 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <dm.h> +#include <syscon.h> +#include <asm/cpu.h> +#include <asm/gpio.h> +#include <asm/intel_regs.h> +#include <asm/mrc_common.h> +#include <asm/pch_common.h> +#include <asm/post.h> +#include <asm/arch/me.h> +#include <asm/report_platform.h> + +static const char *const ecc_decoder[] = { + "inactive", + "active on IO", + "disabled on IO", + "active" +}; + +ulong mrc_common_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 mrc_common_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++; + } +} + +int mrc_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; +} + +/* + * Dump in the log memory controller configuration as read from the memory + * controller registers. + */ +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" : ""); + } +} + +int mrc_locate_spd(struct udevice *dev, int size, const void **spd_datap) +{ + const void *blob = gd->fdt_blob; + int spd_index; + struct gpio_desc desc[4]; + int spd_node; + int node; + int ret; + + ret = gpio_request_list_by_name(dev, "board-id-gpios", desc, + ARRAY_SIZE(desc), GPIOD_IS_IN); + if (ret < 0) { + debug("%s: gpio ret=%d\n", __func__, ret); + return ret; + } + spd_index = dm_gpio_get_values_as_int(desc, ret); + debug("spd index %d\n", spd_index); + + node = fdt_first_subnode(blob, dev->of_offset); + if (node < 0) + return -EINVAL; + for (spd_node = fdt_first_subnode(blob, node); + spd_node > 0; + spd_node = fdt_next_subnode(blob, spd_node)) { + int len; + + if (fdtdec_get_int(blob, spd_node, "reg", -1) != spd_index) + continue; + *spd_datap = fdt_getprop(blob, spd_node, "data", &len); + if (len < size) { + printf("Missing SPD data\n"); + return -EINVAL; + } + + debug("Using SDRAM SPD data for '%s'\n", + fdt_get_name(blob, spd_node, NULL)); + return 0; + } + + printf("No SPD data found for index %d\n", spd_index); + return -ENOENT; +} + +asmlinkage void sdram_console_tx_byte(unsigned char byte) +{ +#ifdef DEBUG + putc(byte); +#endif +} + +/** + * Find the PEI executable in the ROM and execute it. + * + * @me_dev: Management Engine device + * @pei_data: configuration data for UEFI PEI reference code + */ +static int sdram_initialise(struct udevice *dev, struct udevice *me_dev, + void *pei_data, bool use_asm_linkage) +{ + unsigned version; + const char *data; + + report_platform_info(dev); + debug("Starting UEFI PEI System Agent\n"); + + debug("PEI data at %p:\n", pei_data); + + data = (char *)CONFIG_X86_MRC_ADDR; + if (data) { + int rv; + ulong start; + + debug("Calling MRC at %p\n", data); + post_code(POST_PRE_MRC); + start = get_timer(0); + if (use_asm_linkage) { + asmlinkage int (*func)(void *); + + func = (asmlinkage int (*)(void *))data; + rv = func(pei_data); + } else { + int (*func)(void *); + + func = (int (*)(void *))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; + } + debug("MRC execution time %lu ms\n", get_timer(start)); + } else { + printf("UEFI PEI System Agent not found.\n"); + return -ENOSYS; + } + + version = readl(MCHBAR_REG(MCHBAR_PEI_VERSION)); + debug("System Agent Version %d.%d.%d Build %d\n", + version >> 24 , (version >> 16) & 0xff, + (version >> 8) & 0xff, version & 0xff); + +#if CONFIG_USBDEBUG + /* mrc.bin reconfigures USB, so reinit it to have debug */ + early_usbdebug_init(); +#endif + + return 0; +} + +int mrc_common_init(struct udevice *dev, void *pei_data, bool use_asm_linkage) +{ + struct udevice *me_dev; + int ret; + + ret = syscon_get_by_driver_data(X86_SYSCON_ME, &me_dev); + if (ret) + return ret; + + ret = sdram_initialise(dev, me_dev, pei_data, use_asm_linkage); + if (ret) + return ret; + quick_ram_check(); + post_code(POST_DRAM); + report_memory_config(); + + return 0; +} diff --git a/arch/x86/cpu/intel_common/pch.c b/arch/x86/cpu/intel_common/pch.c new file mode 100644 index 0000000000..1f05b44586 --- /dev/null +++ b/arch/x86/cpu/intel_common/pch.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2016 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <dm.h> +#include <asm/pch_common.h> + +u32 pch_common_sir_read(struct udevice *dev, int idx) +{ + u32 data; + + dm_pci_write_config32(dev, SATA_SIRI, idx); + dm_pci_read_config32(dev, SATA_SIRD, &data); + + return data; +} + +void pch_common_sir_write(struct udevice *dev, int idx, u32 value) +{ + dm_pci_write_config32(dev, SATA_SIRI, idx); + dm_pci_write_config32(dev, SATA_SIRD, value); +} diff --git a/arch/x86/cpu/ivybridge/report_platform.c b/arch/x86/cpu/intel_common/report_platform.c index c78322aef9..05e1cf9cdb 100644 --- a/arch/x86/cpu/ivybridge/report_platform.c +++ b/arch/x86/cpu/intel_common/report_platform.c @@ -9,8 +9,8 @@ #include <common.h> #include <asm/cpu.h> #include <asm/pci.h> +#include <asm/report_platform.h> #include <asm/arch/pch.h> -#include <asm/arch/sandybridge.h> static void report_cpu_info(void) { diff --git a/arch/x86/cpu/interrupts.c b/arch/x86/cpu/interrupts.c index c40200bf85..10dc4d47f0 100644 --- a/arch/x86/cpu/interrupts.c +++ b/arch/x86/cpu/interrupts.c @@ -249,7 +249,7 @@ int interrupt_init(void) int ret; /* Try to set up the interrupt router, but don't require one */ - ret = uclass_first_device(UCLASS_IRQ, &dev); + ret = uclass_first_device_err(UCLASS_IRQ, &dev); if (ret && ret != -ENODEV) return ret; diff --git a/arch/x86/cpu/ioapic.c b/arch/x86/cpu/ioapic.c index 112a9c63b4..d15e86c1c5 100644 --- a/arch/x86/cpu/ioapic.c +++ b/arch/x86/cpu/ioapic.c @@ -7,6 +7,7 @@ #include <common.h> #include <asm/io.h> #include <asm/ioapic.h> +#include <asm/lapic.h> u32 io_apic_read(u32 reg) { @@ -19,3 +20,18 @@ void io_apic_write(u32 reg, u32 val) writel(reg, IO_APIC_INDEX); writel(val, IO_APIC_DATA); } + +void io_apic_set_id(int ioapic_id) +{ + int bsp_lapicid = lapicid(); + + debug("IOAPIC: Initialising IOAPIC at %08x\n", IO_APIC_ADDR); + debug("IOAPIC: Bootstrap Processor Local APIC = %#02x\n", bsp_lapicid); + + if (ioapic_id) { + debug("IOAPIC: ID = 0x%02x\n", ioapic_id); + /* Set IOAPIC ID if it has been specified */ + io_apic_write(0x00, (io_apic_read(0x00) & 0xf0ffffff) | + (ioapic_id << 24)); + } +} diff --git a/arch/x86/cpu/ivybridge/Kconfig b/arch/x86/cpu/ivybridge/Kconfig index 0819347b59..e23d01a08f 100644 --- a/arch/x86/cpu/ivybridge/Kconfig +++ b/arch/x86/cpu/ivybridge/Kconfig @@ -7,43 +7,18 @@ config NORTHBRIDGE_INTEL_IVYBRIDGE bool - select CACHE_MRC_BIN + select CACHE_MRC_BIN if HAVE_MRC if NORTHBRIDGE_INTEL_IVYBRIDGE -config CACHE_MRC_BIN - bool - default n - -config CACHE_MRC_SIZE_KB - int - default 512 - config DCACHE_RAM_BASE - hex default 0xff7e0000 config DCACHE_RAM_SIZE - hex default 0x20000 -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 CPU_SPECIFIC_OPTIONS def_bool y diff --git a/arch/x86/cpu/ivybridge/Makefile b/arch/x86/cpu/ivybridge/Makefile index 9203219f31..9cdb07bdf2 100644 --- a/arch/x86/cpu/ivybridge/Makefile +++ b/arch/x86/cpu/ivybridge/Makefile @@ -7,16 +7,12 @@ ifdef CONFIG_HAVE_FSP obj-y += fsp_configs.o ivybridge.o else -obj-y += car.o obj-y += cpu.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 += report_platform.o obj-y += sata.o obj-y += sdram.o endif diff --git a/arch/x86/cpu/ivybridge/bd82x6x.c b/arch/x86/cpu/ivybridge/bd82x6x.c index 9972b0ae7f..4c039ac9c6 100644 --- a/arch/x86/cpu/ivybridge/bd82x6x.c +++ b/arch/x86/cpu/ivybridge/bd82x6x.c @@ -11,8 +11,10 @@ #include <pch.h> #include <syscon.h> #include <asm/cpu.h> +#include <asm/intel_regs.h> #include <asm/io.h> #include <asm/lapic.h> +#include <asm/lpc_common.h> #include <asm/pci.h> #include <asm/arch/bd82x6x.h> #include <asm/arch/model_206ax.h> @@ -187,20 +189,7 @@ static int bd82x6x_pch_get_spi_base(struct udevice *dev, ulong *sbasep) static int bd82x6x_set_spi_protect(struct udevice *dev, bool protect) { - uint8_t bios_cntl; - - /* Adjust the BIOS write protect and SMM BIOS Write Protect Disable */ - dm_pci_read_config8(dev, BIOS_CTRL, &bios_cntl); - if (protect) { - bios_cntl &= ~BIOS_CTRL_BIOSWE; - bios_cntl |= BIT(5); - } else { - bios_cntl |= BIOS_CTRL_BIOSWE; - bios_cntl &= ~BIT(5); - } - dm_pci_write_config8(dev, BIOS_CTRL, bios_cntl); - - return 0; + return lpc_set_spi_protect(dev, BIOS_CTRL, protect); } static int bd82x6x_get_gpio_base(struct udevice *dev, u32 *gbasep) diff --git a/arch/x86/cpu/ivybridge/cpu.c b/arch/x86/cpu/ivybridge/cpu.c index 948833c028..0f9390517f 100644 --- a/arch/x86/cpu/ivybridge/cpu.c +++ b/arch/x86/cpu/ivybridge/cpu.c @@ -17,15 +17,18 @@ #include <fdtdec.h> #include <pch.h> #include <asm/cpu.h> +#include <asm/cpu_common.h> +#include <asm/intel_regs.h> #include <asm/io.h> #include <asm/lapic.h> +#include <asm/lpc_common.h> +#include <asm/microcode.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> @@ -33,51 +36,11 @@ DECLARE_GLOBAL_DATA_PTR; 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(SYS_RST | RST_CPU, PORT_RESET); - cpu_hlt(); - - /* Not reached */ - return -EINVAL; + return cpu_set_flex_ratio_to_tdp_nominal(); } int arch_cpu_init(void) @@ -104,9 +67,9 @@ int arch_cpu_init_dm(void) /* TODO(sjg@chromium.org): Get rid of gd->hose */ gd->hose = hose; - ret = uclass_first_device(UCLASS_LPC, &dev); - if (!dev) - return -ENODEV; + ret = uclass_first_device_err(UCLASS_LPC, &dev); + if (ret) + return ret; /* * We should do as little as possible before the serial console is @@ -162,17 +125,6 @@ static void enable_usb_bar(struct udevice *bus) pci_bus_write_config(bus, usb3, PCI_COMMAND, cmd, PCI_SIZE_32); } -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; @@ -183,20 +135,6 @@ int print_cpuinfo(void) 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"); @@ -207,17 +145,9 @@ int print_cpuinfo(void) reset_cpu(0); } - /* Early chipset init required before RAM init can work */ - uclass_first_device(UCLASS_NORTHBRIDGE, &dev); - - ret = uclass_first_device(UCLASS_LPC, &lpc); + ret = cpu_common_init(); if (ret) return ret; - if (!dev) - return -ENODEV; - - /* Cause the SATA device to do its early init */ - uclass_first_device(UCLASS_DISK, &dev); /* Check PM1_STS[15] to see if we are waking from Sx */ pm1_sts = inw(DEFAULT_PMBASE + PM1_STS); @@ -236,15 +166,15 @@ int print_cpuinfo(void) post_code(POST_EARLY_INIT); /* Enable SPD ROMs and DDR-III DRAM */ - ret = uclass_first_device(UCLASS_I2C, &dev); + ret = uclass_first_device_err(UCLASS_I2C, &dev); if (ret) return ret; - if (!dev) - return -ENODEV; /* Prepare USB controller early in S3 resume */ - if (boot_mode == PEI_BOOT_RESUME) + if (boot_mode == PEI_BOOT_RESUME) { + uclass_first_device(UCLASS_LPC, &lpc); enable_usb_bar(pci_get_controller(lpc->parent)); + } gd->arch.pei_boot_mode = boot_mode; diff --git a/arch/x86/cpu/ivybridge/early_me.c b/arch/x86/cpu/ivybridge/early_me.c index b1df77d571..cda96ab398 100644 --- a/arch/x86/cpu/ivybridge/early_me.c +++ b/arch/x86/cpu/ivybridge/early_me.c @@ -27,35 +27,6 @@ static const char *const me_ack_values[] = { [ME_HFS_ACK_CONTINUE] = "Continue to boot" }; -static inline void pci_read_dword_ptr(struct udevice *me_dev, void *ptr, - int offset) -{ - u32 dword; - - dm_pci_read_config32(me_dev, offset, &dword); - memcpy(ptr, &dword, sizeof(dword)); -} - -static inline void pci_write_dword_ptr(struct udevice *me_dev, void *ptr, - int offset) -{ - u32 dword = 0; - - memcpy(&dword, ptr, sizeof(dword)); - dm_pci_write_config32(me_dev, offset, dword); -} - -void intel_early_me_status(struct udevice *me_dev) -{ - struct me_hfs hfs; - struct me_gmes gmes; - - pci_read_dword_ptr(me_dev, &hfs, PCI_ME_HFS); - pci_read_dword_ptr(me_dev, &gmes, PCI_ME_GMES); - - intel_me_status(&hfs, &gmes); -} - int intel_early_me_init(struct udevice *me_dev) { int count; @@ -159,7 +130,7 @@ int intel_early_me_init_done(struct udevice *dev, struct udevice *me_dev, debug("ME: Requested BIOS Action: %s\n", me_ack_values[hfs.ack_data]); /* Check status after acknowledgement */ - intel_early_me_status(me_dev); + intel_me_status(me_dev); switch (hfs.ack_data) { case ME_HFS_ACK_CONTINUE: diff --git a/arch/x86/cpu/ivybridge/gma.c b/arch/x86/cpu/ivybridge/gma.c index 3b6291e905..37e2e6ead8 100644 --- a/arch/x86/cpu/ivybridge/gma.c +++ b/arch/x86/cpu/ivybridge/gma.c @@ -12,6 +12,7 @@ #include <errno.h> #include <fdtdec.h> #include <pci_rom.h> +#include <asm/intel_regs.h> #include <asm/io.h> #include <asm/mtrr.h> #include <asm/pci.h> @@ -812,9 +813,9 @@ int gma_func0_init(struct udevice *dev) writew(0x0010, RCB_REG(DISPBDF)); setbits_le32(RCB_REG(FD2), PCH_ENABLE_DBDF); - ret = uclass_first_device(UCLASS_NORTHBRIDGE, &nbridge); - if (!nbridge) - return -ENODEV; + ret = uclass_first_device_err(UCLASS_NORTHBRIDGE, &nbridge); + if (ret) + return ret; rev = bridge_silicon_revision(nbridge); sandybridge_setup_graphics(nbridge, dev); diff --git a/arch/x86/cpu/ivybridge/lpc.c b/arch/x86/cpu/ivybridge/lpc.c index 9ab5ed3ff9..88ab7973fd 100644 --- a/arch/x86/cpu/ivybridge/lpc.c +++ b/arch/x86/cpu/ivybridge/lpc.c @@ -13,9 +13,11 @@ #include <rtc.h> #include <pci.h> #include <asm/acpi.h> +#include <asm/intel_regs.h> #include <asm/interrupt.h> #include <asm/io.h> #include <asm/ioapic.h> +#include <asm/lpc_common.h> #include <asm/pci.h> #include <asm/arch/pch.h> @@ -404,26 +406,6 @@ static void pch_fixups(struct udevice *pch) setbits_le32(RCB_REG(0x21a8), 0x3); } -/* - * Enable Prefetching and Caching. - */ -static void enable_spi_prefetch(struct udevice *pch) -{ - u8 reg8; - - dm_pci_read_config8(pch, 0xdc, ®8); - reg8 &= ~(3 << 2); - reg8 |= (2 << 2); /* Prefetching and Caching Enabled */ - dm_pci_write_config8(pch, 0xdc, reg8); -} - -static void enable_port80_on_lpc(struct udevice *pch) -{ - /* Enable port 80 POST on LPC */ - dm_pci_write_config32(pch, PCH_RCBA_BASE, DEFAULT_RCBA | 1); - clrbits_le32(RCB_REG(GCS), 4); -} - static void set_spi_speed(void) { u32 fdod; @@ -440,54 +422,6 @@ static void set_spi_speed(void) clrsetbits_8(RCB_REG(SPI_FREQ_SWSEQ), 7, fdod); } -/** - * lpc_early_init() - set up LPC serial ports and other early things - * - * @dev: LPC device - * @return 0 if OK, -ve on error - */ -static int lpc_early_init(struct udevice *dev) -{ - struct reg_info { - u32 base; - u32 size; - } values[4], *ptr; - int count; - int i; - - count = fdtdec_get_int_array_count(gd->fdt_blob, dev->of_offset, - "intel,gen-dec", (u32 *)values, - sizeof(values) / sizeof(u32)); - if (count < 0) - return -EINVAL; - - /* Set COM1/COM2 decode range */ - dm_pci_write_config16(dev->parent, LPC_IO_DEC, 0x0010); - - /* Enable PS/2 Keyboard/Mouse, EC areas and COM1 */ - dm_pci_write_config16(dev->parent, 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); - dm_pci_write_config32(dev->parent, LPC_GENX_DEC(i), reg); - } - - enable_spi_prefetch(dev->parent); - - /* This is already done in start.S, but let's do it in C */ - enable_port80_on_lpc(dev->parent); - - set_spi_speed(); - - return 0; -} - static int lpc_init_extra(struct udevice *dev) { struct udevice *pch = dev->parent; @@ -550,9 +484,12 @@ static int lpc_init_extra(struct udevice *dev) static int bd82x6x_lpc_early_init(struct udevice *dev) { + set_spi_speed(); + /* Setting up Southbridge. In the northbridge code. */ debug("Setting up static southbridge registers\n"); - dm_pci_write_config32(dev->parent, PCH_RCBA_BASE, DEFAULT_RCBA | 1); + dm_pci_write_config32(dev->parent, PCH_RCBA_BASE, + RCB_BASE_ADDRESS | 1); dm_pci_write_config32(dev->parent, PMBASE, DEFAULT_PMBASE | 1); /* Enable ACPI BAR */ @@ -573,7 +510,7 @@ static int bd82x6x_lpc_probe(struct udevice *dev) int ret; if (!(gd->flags & GD_FLG_RELOC)) { - ret = lpc_early_init(dev); + ret = lpc_common_early_init(dev); if (ret) { debug("%s: lpc_early_init() failed\n", __func__); return ret; diff --git a/arch/x86/cpu/ivybridge/model_206ax.c b/arch/x86/cpu/ivybridge/model_206ax.c index 9654600cf1..cef425669c 100644 --- a/arch/x86/cpu/ivybridge/model_206ax.c +++ b/arch/x86/cpu/ivybridge/model_206ax.c @@ -17,6 +17,7 @@ #include <asm/cpu_x86.h> #include <asm/lapic.h> #include <asm/msr.h> +#include <asm/msr-index.h> #include <asm/mtrr.h> #include <asm/processor.h> #include <asm/speedstep.h> @@ -363,7 +364,7 @@ static void set_max_ratio(void) msr = msr_read(MSR_PLATFORM_INFO); perf_ctl.lo = msr.lo & 0xff00; } - msr_write(IA32_PERF_CTL, perf_ctl); + msr_write(MSR_IA32_PERF_CTL, perf_ctl); debug("model_x06ax: frequency set to %d\n", ((perf_ctl.lo >> 8) & 0xff) * SANDYBRIDGE_BCLK); @@ -455,9 +456,10 @@ static int model_206ax_get_info(struct udevice *dev, struct cpu_info *info) { msr_t msr; - msr = msr_read(IA32_PERF_CTL); + msr = msr_read(MSR_IA32_PERF_CTL); info->cpu_freq = ((msr.lo >> 8) & 0xff) * SANDYBRIDGE_BCLK * 1000000; - info->features = 1 << CPU_FEAT_L1_CACHE | 1 << CPU_FEAT_MMU; + info->features = 1 << CPU_FEAT_L1_CACHE | 1 << CPU_FEAT_MMU | + 1 << CPU_FEAT_UCODE; return 0; } diff --git a/arch/x86/cpu/ivybridge/northbridge.c b/arch/x86/cpu/ivybridge/northbridge.c index a066607a18..f7e0bc3f18 100644 --- a/arch/x86/cpu/ivybridge/northbridge.c +++ b/arch/x86/cpu/ivybridge/northbridge.c @@ -12,6 +12,7 @@ #include <asm/msr.h> #include <asm/acpi.h> #include <asm/cpu.h> +#include <asm/intel_regs.h> #include <asm/io.h> #include <asm/pci.h> #include <asm/processor.h> @@ -167,8 +168,8 @@ static void sandybridge_setup_northbridge_bars(struct udevice *dev) debug("Setting up static registers\n"); dm_pci_write_config32(dev, EPBAR, DEFAULT_EPBAR | 1); dm_pci_write_config32(dev, EPBAR + 4, (0LL + DEFAULT_EPBAR) >> 32); - dm_pci_write_config32(dev, MCHBAR, DEFAULT_MCHBAR | 1); - dm_pci_write_config32(dev, MCHBAR + 4, (0LL + DEFAULT_MCHBAR) >> 32); + dm_pci_write_config32(dev, MCHBAR, MCH_BASE_ADDRESS | 1); + dm_pci_write_config32(dev, MCHBAR + 4, (0LL + MCH_BASE_ADDRESS) >> 32); /* 64MB - busses 0-63 */ dm_pci_write_config32(dev, PCIEXBAR, DEFAULT_PCIEXBAR | 5); dm_pci_write_config32(dev, PCIEXBAR + 4, diff --git a/arch/x86/cpu/ivybridge/sata.c b/arch/x86/cpu/ivybridge/sata.c index a59d9edce5..c3d1057c29 100644 --- a/arch/x86/cpu/ivybridge/sata.c +++ b/arch/x86/cpu/ivybridge/sata.c @@ -9,28 +9,13 @@ #include <dm.h> #include <fdtdec.h> #include <asm/io.h> +#include <asm/pch_common.h> #include <asm/pci.h> #include <asm/arch/pch.h> #include <asm/arch/bd82x6x.h> DECLARE_GLOBAL_DATA_PTR; -static inline u32 sir_read(struct udevice *dev, int idx) -{ - u32 data; - - dm_pci_write_config32(dev, SATA_SIRI, idx); - dm_pci_read_config32(dev, SATA_SIRD, &data); - - return data; -} - -static inline void sir_write(struct udevice *dev, int idx, u32 value) -{ - dm_pci_write_config32(dev, SATA_SIRI, idx); - dm_pci_write_config32(dev, SATA_SIRD, value); -} - static void common_sata_init(struct udevice *dev, unsigned int port_map) { u32 reg32; @@ -177,27 +162,27 @@ static void bd82x6x_sata_init(struct udevice *dev, struct udevice *pch) pch_iobp_update(pch, SATA_IOBP_SP1G3IR, 0, port_tx); /* Additional Programming Requirements */ - sir_write(dev, 0x04, 0x00001600); - sir_write(dev, 0x28, 0xa0000033); - reg32 = sir_read(dev, 0x54); + pch_common_sir_write(dev, 0x04, 0x00001600); + pch_common_sir_write(dev, 0x28, 0xa0000033); + reg32 = pch_common_sir_read(dev, 0x54); reg32 &= 0xff000000; reg32 |= 0x5555aa; - sir_write(dev, 0x54, reg32); - sir_write(dev, 0x64, 0xcccc8484); - reg32 = sir_read(dev, 0x68); + pch_common_sir_write(dev, 0x54, reg32); + pch_common_sir_write(dev, 0x64, 0xcccc8484); + reg32 = pch_common_sir_read(dev, 0x68); reg32 &= 0xffff0000; reg32 |= 0xcccc; - sir_write(dev, 0x68, reg32); - reg32 = sir_read(dev, 0x78); + pch_common_sir_write(dev, 0x68, reg32); + reg32 = pch_common_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_common_sir_write(dev, 0x78, reg32); + pch_common_sir_write(dev, 0x84, 0x001c7000); + pch_common_sir_write(dev, 0x88, 0x88338822); + pch_common_sir_write(dev, 0xa0, 0x001c7000); + pch_common_sir_write(dev, 0xc4, 0x0c0c0c0c); + pch_common_sir_write(dev, 0xc8, 0x0c0c0c0c); + pch_common_sir_write(dev, 0xd4, 0x10000000); pch_iobp_update(pch, 0xea004001, 0x3fffffff, 0xc0000000); pch_iobp_update(pch, 0xea00408a, 0xfffffcff, 0x00000100); @@ -229,11 +214,9 @@ static int bd82x6x_sata_probe(struct udevice *dev) struct udevice *pch; int ret; - ret = uclass_first_device(UCLASS_PCH, &pch); + ret = uclass_first_device_err(UCLASS_PCH, &pch); if (ret) return ret; - if (!pch) - return -ENODEV; if (!(gd->flags & GD_FLG_RELOC)) bd82x6x_sata_enable(dev); diff --git a/arch/x86/cpu/ivybridge/sdram.c b/arch/x86/cpu/ivybridge/sdram.c index e23c422cd0..e35e543c3e 100644 --- a/arch/x86/cpu/ivybridge/sdram.c +++ b/arch/x86/cpu/ivybridge/sdram.c @@ -23,9 +23,12 @@ #include <asm/processor.h> #include <asm/gpio.h> #include <asm/global_data.h> +#include <asm/intel_regs.h> #include <asm/mrccache.h> +#include <asm/mrc_common.h> #include <asm/mtrr.h> #include <asm/pci.h> +#include <asm/report_platform.h> #include <asm/arch/me.h> #include <asm/arch/pei_data.h> #include <asm/arch/pch.h> @@ -38,57 +41,14 @@ DECLARE_GLOBAL_DATA_PTR; #define CMOS_OFFSET_MRC_SEED_S3 156 #define CMOS_OFFSET_MRC_SEED_CHK 160 -/* - * 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; + return mrc_common_board_get_usable_ram_top(total_size); } 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++; - } + mrc_common_dram_init_banksize(); } static int read_seed_from_cmos(struct pei_data *pei_data) @@ -215,164 +175,10 @@ int misc_init_r(void) return 0; } -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) +static void post_system_agent_init(struct udevice *dev, struct udevice *me_dev, + struct pei_data *pei_data) { - 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 -} - -static int recovery_mode_enabled(void) -{ - return false; -} - -/** - * Find the PEI executable in the ROM and execute it. - * - * @dev: Northbridge device - * @pei_data: configuration data for UEFI PEI reference code - */ -int sdram_initialise(struct udevice *dev, struct udevice *me_dev, - struct pei_data *pei_data) -{ - unsigned version; - const char *data; uint16_t done; - int ret; - - report_platform_info(dev); - - /* Wait for ME to be ready */ - ret = intel_early_me_init(me_dev); - if (ret) - return ret; - ret = intel_early_me_uma_size(me_dev); - if (ret < 0) - return ret; - - debug("Starting UEFI PEI System Agent\n"); - - /* - * Do not pass MRC data in for recovery mode boot, - * Always pass it in for S3 resume. - */ - if (!recovery_mode_enabled() || - pei_data->boot_mode == PEI_BOOT_RESUME) { - ret = prepare_mrc_cache(pei_data); - if (ret) - debug("prepare_mrc_cache failed: %d\n", ret); - } - - /* 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"); - reset_cpu(0); - } - - /* 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 *); - ulong start; - - debug("Calling MRC at %p\n", data); - post_code(POST_PRE_MRC); - start = get_timer(0); - 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; - } - debug("MRC execution time %lu ms\n", get_timer(start)); - } 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); - debug("MRC output data length %#x at %p\n", pei_data->mrc_output_len, - pei_data->mrc_output); /* * Send ME init done for SandyBridge here. This is done inside the @@ -383,25 +189,16 @@ int sdram_initialise(struct udevice *dev, struct udevice *me_dev, if (BASE_REV_SNB == done) intel_early_me_init_done(dev, me_dev, ME_INIT_STATUS_SUCCESS); else - intel_early_me_status(me_dev); - - post_system_agent_init(pei_data); - report_memory_config(); + intel_me_status(me_dev); - /* S3 resume: don't save scrambler seed or MRC data */ - if (pei_data->boot_mode != PEI_BOOT_RESUME) { - /* - * This will be copied to SDRAM in reserve_arch(), then written - * to SPI flash in mrccache_save() - */ - gd->arch.mrc_output = (char *)pei_data->mrc_output; - gd->arch.mrc_output_len = pei_data->mrc_output_len; - ret = write_seeds_to_cmos(pei_data); - if (ret) - debug("Failed to write seeds to CMOS: %d\n", ret); - } + /* If PCIe init is skipped, set the PEG clock gating */ + if (!pei_data->pcie_init) + setbits_le32(MCHBAR_REG(0x7010), 1); +} - return 0; +static int recovery_mode_enabled(void) +{ + return false; } int reserve_arch(void) @@ -409,87 +206,16 @@ int reserve_arch(void) return mrccache_reserve(); } -static int copy_spd(struct pei_data *peid) +static int copy_spd(struct udevice *dev, 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; - } + const void *data; + int ret; - return 0; -} + ret = mrc_locate_spd(dev, sizeof(peid->spd_data[0]), &data); + if (ret) + return ret; -/** - * 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++; + memcpy(peid->spd_data[0], data, sizeof(peid->spd_data[0])); return 0; } @@ -608,10 +334,10 @@ static int sdram_find(struct udevice *dev) 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); + mrc_add_memory_area(info, 1 << 20, 2 << 28); + mrc_add_memory_area(info, (2 << 28) + (2 << 20), 4 << 28); + mrc_add_memory_area(info, (4 << 28) + (2 << 20), tseg_base); + mrc_add_memory_area(info, 1ULL << 32, touud); /* Add MTRRs for memory */ mtrr_add_request(MTRR_TYPE_WRBACK, 0, 2ULL << 30); @@ -680,9 +406,9 @@ static void rcba_config(void) int dram_init(void) { - struct pei_data pei_data __aligned(8) = { + struct pei_data _pei_data __aligned(8) = { .pei_version = PEI_VERSION, - .mchbar = DEFAULT_MCHBAR, + .mchbar = MCH_BASE_ADDRESS, .dmibar = DEFAULT_DMIBAR, .epbar = DEFAULT_EPBAR, .pciexbar = CONFIG_PCIE_ECAM_BASE, @@ -733,38 +459,84 @@ int dram_init(void) { 0, 4, 0x0000 }, /* P13= Empty */ }, }; + struct pei_data *pei_data = &_pei_data; struct udevice *dev, *me_dev; int ret; - ret = uclass_first_device(UCLASS_NORTHBRIDGE, &dev); + ret = uclass_first_device_err(UCLASS_NORTHBRIDGE, &dev); if (ret) return ret; - if (!dev) - return -ENODEV; ret = syscon_get_by_driver_data(X86_SYSCON_ME, &me_dev); if (ret) return ret; - debug("Boot mode %d\n", gd->arch.pei_boot_mode); - debug("mrc_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(dev, me_dev, &pei_data); + ret = copy_spd(dev, pei_data); if (ret) return ret; + pei_data->boot_mode = gd->arch.pei_boot_mode; + debug("Boot mode %d\n", gd->arch.pei_boot_mode); + debug("mrc_input %p\n", pei_data->mrc_input); - rcba_config(); - quick_ram_check(); + /* + * Do not pass MRC data in for recovery mode boot, + * Always pass it in for S3 resume. + */ + if (!recovery_mode_enabled() || + pei_data->boot_mode == PEI_BOOT_RESUME) { + ret = prepare_mrc_cache(pei_data); + if (ret) + debug("prepare_mrc_cache failed: %d\n", ret); + } - writew(0xCAFE, MCHBAR_REG(SSKPD)); + /* 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"); + reset_cpu(0); + } - post_code(POST_DRAM); + /* Pass console handler in pei_data */ + pei_data->tx_byte = sdram_console_tx_byte; - ret = sdram_find(dev); + /* Wait for ME to be ready */ + ret = intel_early_me_init(me_dev); if (ret) return ret; + ret = intel_early_me_uma_size(me_dev); + if (ret < 0) + return ret; + ret = mrc_common_init(dev, pei_data, false); + if (ret) + return ret; + + ret = sdram_find(dev); + if (ret) + return ret; gd->ram_size = gd->arch.meminfo.total_32bit_memory; + debug("MRC output data length %#x at %p\n", pei_data->mrc_output_len, + pei_data->mrc_output); + + post_system_agent_init(dev, me_dev, pei_data); + report_memory_config(); + + /* S3 resume: don't save scrambler seed or MRC data */ + if (pei_data->boot_mode != PEI_BOOT_RESUME) { + /* + * This will be copied to SDRAM in reserve_arch(), then written + * to SPI flash in mrccache_save() + */ + gd->arch.mrc_output = (char *)pei_data->mrc_output; + gd->arch.mrc_output_len = pei_data->mrc_output_len; + ret = write_seeds_to_cmos(pei_data); + if (ret) + debug("Failed to write seeds to CMOS: %d\n", ret); + } + + writew(0xCAFE, MCHBAR_REG(SSKPD)); + if (ret) + return ret; + + rcba_config(); + return 0; } diff --git a/arch/x86/cpu/mp_init.c b/arch/x86/cpu/mp_init.c index fc2fb5bf44..2604a687ab 100644 --- a/arch/x86/cpu/mp_init.c +++ b/arch/x86/cpu/mp_init.c @@ -15,6 +15,7 @@ #include <asm/cpu.h> #include <asm/interrupt.h> #include <asm/lapic.h> +#include <asm/microcode.h> #include <asm/mp.h> #include <asm/msr.h> #include <asm/mtrr.h> @@ -195,7 +196,7 @@ static int save_bsp_msrs(char *start, int size) msr_count = 2 * num_var_mtrrs + NUM_FIXED_MTRRS + 1; if ((msr_count * sizeof(struct saved_msr)) > size) { - printf("Cannot mirror all %d msrs.\n", msr_count); + printf("Cannot mirror all %d msrs\n", msr_count); return -ENOSPC; } @@ -247,8 +248,10 @@ static int load_sipi_vector(atomic_t **ap_countp, int num_cpus) if (!stack) return -ENOMEM; params->stack_top = (u32)(stack + size); - - params->microcode_ptr = 0; +#if !defined(CONFIG_QEMU) && !defined(CONFIG_HAVE_FSP) + params->microcode_ptr = ucode_base; + debug("Microcode at %x\n", params->microcode_ptr); +#endif params->msr_table_ptr = (u32)msr_save; ret = save_bsp_msrs(msr_save, sizeof(msr_save)); if (ret < 0) @@ -283,21 +286,25 @@ static int check_cpu_devices(int expected_cpus) } /* Returns 1 for timeout. 0 on success */ -static int apic_wait_timeout(int total_delay, int delay_step) +static int apic_wait_timeout(int total_delay, const char *msg) { int total = 0; - int timeout = 0; + if (!(lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY)) + return 0; + + debug("Waiting for %s...", msg); while (lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY) { - udelay(delay_step); - total += delay_step; + udelay(50); + total += 50; if (total >= total_delay) { - timeout = 1; - break; + debug("timed out: aborting\n"); + return -ETIMEDOUT; } } + debug("done\n"); - return timeout; + return 0; } static int start_aps(int ap_count, atomic_t *num_aps) @@ -320,73 +327,42 @@ static int start_aps(int ap_count, atomic_t *num_aps) debug("Attempting to start %d APs\n", ap_count); - if ((lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY)) { - debug("Waiting for ICR not to be busy..."); - if (apic_wait_timeout(1000, 50)) { - debug("timed out. Aborting.\n"); - return -1; - } else { - debug("done.\n"); - } - } + if (apic_wait_timeout(1000, "ICR not to be busy")) + return -ETIMEDOUT; /* Send INIT IPI to all but self */ lapic_write(LAPIC_ICR2, SET_LAPIC_DEST_FIELD(0)); lapic_write(LAPIC_ICR, LAPIC_DEST_ALLBUT | LAPIC_INT_ASSERT | LAPIC_DM_INIT); - debug("Waiting for 10ms after sending INIT.\n"); + debug("Waiting for 10ms after sending INIT\n"); mdelay(10); /* Send 1st SIPI */ - if ((lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY)) { - debug("Waiting for ICR not to be busy..."); - if (apic_wait_timeout(1000, 50)) { - debug("timed out. Aborting.\n"); - return -1; - } else { - debug("done.\n"); - } - } + if (apic_wait_timeout(1000, "ICR not to be busy")) + return -ETIMEDOUT; lapic_write(LAPIC_ICR2, SET_LAPIC_DEST_FIELD(0)); lapic_write(LAPIC_ICR, LAPIC_DEST_ALLBUT | LAPIC_INT_ASSERT | LAPIC_DM_STARTUP | sipi_vector); - debug("Waiting for 1st SIPI to complete..."); - if (apic_wait_timeout(10000, 50)) { - debug("timed out.\n"); - return -1; - } else { - debug("done.\n"); - } + if (apic_wait_timeout(10000, "first SIPI to complete")) + return -ETIMEDOUT; /* Wait for CPUs to check in up to 200 us */ wait_for_aps(num_aps, ap_count, 200, 15); /* Send 2nd SIPI */ - if ((lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY)) { - debug("Waiting for ICR not to be busy..."); - if (apic_wait_timeout(1000, 50)) { - debug("timed out. Aborting.\n"); - return -1; - } else { - debug("done.\n"); - } - } + if (apic_wait_timeout(1000, "ICR not to be busy")) + return -ETIMEDOUT; lapic_write(LAPIC_ICR2, SET_LAPIC_DEST_FIELD(0)); lapic_write(LAPIC_ICR, LAPIC_DEST_ALLBUT | LAPIC_INT_ASSERT | LAPIC_DM_STARTUP | sipi_vector); - debug("Waiting for 2nd SIPI to complete..."); - if (apic_wait_timeout(10000, 50)) { - debug("timed out.\n"); - return -1; - } else { - debug("done.\n"); - } + if (apic_wait_timeout(10000, "second SIPI to complete")) + return -ETIMEDOUT; /* Wait for CPUs to check in */ if (wait_for_aps(num_aps, ap_count, 10000, 50)) { - debug("Not all APs checked in: %d/%d.\n", + debug("Not all APs checked in: %d/%d\n", atomic_read(num_aps), ap_count); return -1; } @@ -410,7 +386,7 @@ static int bsp_do_flight_plan(struct udevice *cpu, struct mp_params *mp_params) /* Wait for the APs to check in */ if (wait_for_aps(&rec->cpus_entered, num_aps, timeout_us, step_us)) { - debug("MP record %d timeout.\n", i); + debug("MP record %d timeout\n", i); ret = -1; } } @@ -430,7 +406,7 @@ static int init_bsp(struct udevice **devp) int ret; cpu_get_name(processor_name); - debug("CPU: %s.\n", processor_name); + debug("CPU: %s\n", processor_name); lapic_setup(); @@ -587,12 +563,16 @@ int mp_init(struct mp_params *p) int mp_init_cpu(struct udevice *cpu, void *unused) { + struct cpu_platdata *plat = dev_get_parent_platdata(cpu); + /* * Multiple APs are brought up simultaneously and they may get the same * seq num in the uclass_resolve_seq() during device_probe(). To avoid * this, set req_seq to the reg number in the device tree in advance. */ cpu->req_seq = fdtdec_get_int(gd->fdt_blob, cpu->of_offset, "reg", -1); + plat->ucode_version = microcode_read_rev(); + plat->device_id = gd->arch.x86_device; return device_probe(cpu); } diff --git a/arch/x86/cpu/qemu/fw_cfg.c b/arch/x86/cpu/qemu/fw_cfg.c index 5ea7a6e2e5..a0a3d08079 100644 --- a/arch/x86/cpu/qemu/fw_cfg.c +++ b/arch/x86/cpu/qemu/fw_cfg.c @@ -232,8 +232,7 @@ static struct fw_file *qemu_fwcfg_find_file(const char *name) * be ignored. * @return: 0 on success, or negative value on failure */ -static int bios_linker_allocate(struct bios_linker_entry *entry, - unsigned long *addr) +static int bios_linker_allocate(struct bios_linker_entry *entry, u32 *addr) { uint32_t size, align; struct fw_file *file; @@ -383,7 +382,7 @@ unsigned install_e820_map(unsigned max_entries, struct e820entry *entries) } /* This function loads and patches ACPI tables provided by QEMU */ -unsigned long write_acpi_tables(unsigned long addr) +u32 write_acpi_tables(u32 addr) { int i, ret = 0; struct fw_file *file; diff --git a/arch/x86/cpu/sipi_vector.S b/arch/x86/cpu/sipi_vector.S index 0c4a157f38..94c0f5a702 100644 --- a/arch/x86/cpu/sipi_vector.S +++ b/arch/x86/cpu/sipi_vector.S @@ -193,6 +193,7 @@ load_msr: mov c_handler, %esi call *%esi + /* This matches struct sipi_param */ .align 4 .globl sipi_params sipi_params: diff --git a/arch/x86/cpu/start.S b/arch/x86/cpu/start.S index 485868ff57..a5cba1cf2a 100644 --- a/arch/x86/cpu/start.S +++ b/arch/x86/cpu/start.S @@ -18,6 +18,14 @@ #include <generated/generic-asm-offsets.h> #include <generated/asm-offsets.h> +/* + * Define this to boot U-Boot from a 32-bit program which sets the GDT + * differently. This can be used to boot directly from any stage of coreboot, + * for example, bypassing the normal payload-loading feature. + * This is only useful for development. + */ +#undef LOAD_FROM_32_BIT + .section .text .code32 .globl _start @@ -68,6 +76,10 @@ _start: /* Save table pointer */ movl %ecx, %esi +#ifdef LOAD_FROM_32_BIT + lgdt gdt_ptr2 +#endif + /* Load the segement registers to match the GDT loaded in start16.S */ movl $(X86_GDT_ENTRY_32BIT_DS * X86_GDT_ENTRY_SIZE), %eax movw %ax, %fs @@ -220,3 +232,71 @@ multiboot_header: .long 0 /* entry addr */ .long CONFIG_SYS_TEXT_BASE + +#ifdef LOAD_FROM_32_BIT + /* + * The following Global Descriptor Table is just enough to get us into + * 'Flat Protected Mode' - It will be discarded as soon as the final + * GDT is setup in a safe location in RAM + */ +gdt_ptr2: + .word 0x1f /* limit (31 bytes = 4 GDT entries - 1) */ + .long gdt_rom2 /* base */ + + /* Some CPUs are picky about GDT alignment... */ + .align 16 +.globl gdt_rom2 +gdt_rom2: + /* + * The GDT table ... + * + * Selector Type + * 0x00 NULL + * 0x08 Unused + * 0x10 32bit code + * 0x18 32bit data/stack + */ + /* The NULL Desciptor - Mandatory */ + .word 0x0000 /* limit_low */ + .word 0x0000 /* base_low */ + .byte 0x00 /* base_middle */ + .byte 0x00 /* access */ + .byte 0x00 /* flags + limit_high */ + .byte 0x00 /* base_high */ + + /* Unused Desciptor - (matches Linux) */ + .word 0x0000 /* limit_low */ + .word 0x0000 /* base_low */ + .byte 0x00 /* base_middle */ + .byte 0x00 /* access */ + .byte 0x00 /* flags + limit_high */ + .byte 0x00 /* base_high */ + + /* + * The Code Segment Descriptor: + * - Base = 0x00000000 + * - Size = 4GB + * - Access = Present, Ring 0, Exec (Code), Readable + * - Flags = 4kB Granularity, 32-bit + */ + .word 0xffff /* limit_low */ + .word 0x0000 /* base_low */ + .byte 0x00 /* base_middle */ + .byte 0x9b /* access */ + .byte 0xcf /* flags + limit_high */ + .byte 0x00 /* base_high */ + + /* + * The Data Segment Descriptor: + * - Base = 0x00000000 + * - Size = 4GB + * - Access = Present, Ring 0, Non-Exec (Data), Writable + * - Flags = 4kB Granularity, 32-bit + */ + .word 0xffff /* limit_low */ + .word 0x0000 /* base_low */ + .byte 0x00 /* base_middle */ + .byte 0x93 /* access */ + .byte 0xcf /* flags + limit_high */ + .byte 0x00 /* base_high */ +#endif |