diff --git a/src/soc/intel/broadwell/acpi.c b/src/soc/intel/broadwell/acpi.c new file mode 100644 index 0000000000..9e10c7527a --- /dev/null +++ b/src/soc/intel/broadwell/acpi.c @@ -0,0 +1,366 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2009 coresystems GmbH + * Copyright (C) 2011 The Chromium OS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "haswell.h" +#include "chip.h" + +#include + +static int get_cores_per_package(void) +{ + struct cpuinfo_x86 c; + struct cpuid_result result; + int cores = 1; + + get_fms(&c, cpuid_eax(1)); + if (c.x86 != 6) + return 1; + + result = cpuid_ext(0xb, 1); + cores = result.ebx & 0xff; + + return cores; +} + +static int generate_cstate_entries(acpi_cstate_t *cstates, + int c1, int c2, int c3) +{ + int length, cstate_count = 0; + + /* Count number of active C-states */ + if (c1 > 0) + ++cstate_count; + if (c2 > 0) + ++cstate_count; + if (c3 > 0) + ++cstate_count; + if (!cstate_count) + return 0; + + length = acpigen_write_package(cstate_count + 1); + length += acpigen_write_byte(cstate_count); + + /* Add an entry if the level is enabled */ + if (c1 > 0) { + cstates[c1].ctype = 1; + length += acpigen_write_CST_package_entry(&cstates[c1]); + } + if (c2 > 0) { + cstates[c2].ctype = 2; + length += acpigen_write_CST_package_entry(&cstates[c2]); + } + if (c3 > 0) { + cstates[c3].ctype = 3; + length += acpigen_write_CST_package_entry(&cstates[c3]); + } + + acpigen_patch_len(length - 1); + return length; +} + +static int generate_C_state_entries(void) +{ + struct cpu_info *info; + struct cpu_driver *cpu; + int len, lenif; + device_t lapic; + struct cpu_intel_haswell_config *conf = NULL; + + /* Find the SpeedStep CPU in the device tree using magic APIC ID */ + lapic = dev_find_lapic(SPEEDSTEP_APIC_MAGIC); + if (!lapic) + return 0; + conf = lapic->chip_info; + if (!conf) + return 0; + + /* Find CPU map of supported C-states */ + info = cpu_info(); + if (!info) + return 0; + cpu = find_cpu_driver(info->cpu); + if (!cpu || !cpu->cstates) + return 0; + + len = acpigen_emit_byte(0x14); /* MethodOp */ + len += acpigen_write_len_f(); /* PkgLength */ + len += acpigen_emit_namestring("_CST"); + len += acpigen_emit_byte(0x00); /* No Arguments */ + + /* If running on AC power */ + len += acpigen_emit_byte(0xa0); /* IfOp */ + lenif = acpigen_write_len_f(); /* PkgLength */ + lenif += acpigen_emit_namestring("PWRS"); + lenif += acpigen_emit_byte(0xa4); /* ReturnOp */ + lenif += generate_cstate_entries(cpu->cstates, conf->c1_acpower, + conf->c2_acpower, conf->c3_acpower); + acpigen_patch_len(lenif - 1); + len += lenif; + + /* Else on battery power */ + len += acpigen_emit_byte(0xa4); /* ReturnOp */ + len += generate_cstate_entries(cpu->cstates, conf->c1_battery, + conf->c2_battery, conf->c3_battery); + acpigen_patch_len(len - 1); + return len; +} + +static acpi_tstate_t tss_table_fine[] = { + { 100, 1000, 0, 0x00, 0 }, + { 94, 940, 0, 0x1f, 0 }, + { 88, 880, 0, 0x1e, 0 }, + { 82, 820, 0, 0x1d, 0 }, + { 75, 760, 0, 0x1c, 0 }, + { 69, 700, 0, 0x1b, 0 }, + { 63, 640, 0, 0x1a, 0 }, + { 57, 580, 0, 0x19, 0 }, + { 50, 520, 0, 0x18, 0 }, + { 44, 460, 0, 0x17, 0 }, + { 38, 400, 0, 0x16, 0 }, + { 32, 340, 0, 0x15, 0 }, + { 25, 280, 0, 0x14, 0 }, + { 19, 220, 0, 0x13, 0 }, + { 13, 160, 0, 0x12, 0 }, +}; + +static acpi_tstate_t tss_table_coarse[] = { + { 100, 1000, 0, 0x00, 0 }, + { 88, 875, 0, 0x1f, 0 }, + { 75, 750, 0, 0x1e, 0 }, + { 63, 625, 0, 0x1d, 0 }, + { 50, 500, 0, 0x1c, 0 }, + { 38, 375, 0, 0x1b, 0 }, + { 25, 250, 0, 0x1a, 0 }, + { 13, 125, 0, 0x19, 0 }, +}; + +static int generate_T_state_entries(int core, int cores_per_package) +{ + int len; + + /* Indicate SW_ALL coordination for T-states */ + len = acpigen_write_TSD_package(core, cores_per_package, SW_ALL); + + /* Indicate FFixedHW so OS will use MSR */ + len += acpigen_write_empty_PTC(); + + /* Set a T-state limit that can be modified in NVS */ + len += acpigen_write_TPC("\\TLVL"); + + /* + * CPUID.(EAX=6):EAX[5] indicates support + * for extended throttle levels. + */ + if (cpuid_eax(6) & (1 << 5)) + len += acpigen_write_TSS_package( + ARRAY_SIZE(tss_table_fine), tss_table_fine); + else + len += acpigen_write_TSS_package( + ARRAY_SIZE(tss_table_coarse), tss_table_coarse); + + return len; +} + +static int calculate_power(int tdp, int p1_ratio, int ratio) +{ + u32 m; + u32 power; + + /* + * M = ((1.1 - ((p1_ratio - ratio) * 0.00625)) / 1.1) ^ 2 + * + * Power = (ratio / p1_ratio) * m * tdp + */ + + m = (110000 - ((p1_ratio - ratio) * 625)) / 11; + m = (m * m) / 1000; + + power = ((ratio * 100000 / p1_ratio) / 100); + power *= (m / 100) * (tdp / 1000); + power /= 1000; + + return (int)power; +} + +static int generate_P_state_entries(int core, int cores_per_package) +{ + int len, len_pss; + int ratio_min, ratio_max, ratio_turbo, ratio_step; + int coord_type, power_max, power_unit, num_entries; + int ratio, power, clock, clock_max; + msr_t msr; + + /* Determine P-state coordination type from MISC_PWR_MGMT[0] */ + msr = rdmsr(MSR_MISC_PWR_MGMT); + if (msr.lo & MISC_PWR_MGMT_EIST_HW_DIS) + coord_type = SW_ANY; + else + coord_type = HW_ALL; + + /* Get bus ratio limits and calculate clock speeds */ + msr = rdmsr(MSR_PLATFORM_INFO); + ratio_min = (msr.hi >> (40-32)) & 0xff; /* Max Efficiency Ratio */ + + /* Determine if this CPU has configurable TDP */ + if (cpu_config_tdp_levels()) { + /* Set max ratio to nominal TDP ratio */ + msr = rdmsr(MSR_CONFIG_TDP_NOMINAL); + ratio_max = msr.lo & 0xff; + } else { + /* Max Non-Turbo Ratio */ + ratio_max = (msr.lo >> 8) & 0xff; + } + clock_max = ratio_max * HASWELL_BCLK; + + /* Calculate CPU TDP in mW */ + msr = rdmsr(MSR_PKG_POWER_SKU_UNIT); + power_unit = 2 << ((msr.lo & 0xf) - 1); + msr = rdmsr(MSR_PKG_POWER_SKU); + power_max = ((msr.lo & 0x7fff) / power_unit) * 1000; + + /* Write _PCT indicating use of FFixedHW */ + len = acpigen_write_empty_PCT(); + + /* Write _PPC with no limit on supported P-state */ + len += acpigen_write_PPC_NVS(); + + /* Write PSD indicating configured coordination type */ + len += acpigen_write_PSD_package(core, 1, coord_type); + + /* Add P-state entries in _PSS table */ + len += acpigen_write_name("_PSS"); + + /* Determine ratio points */ + ratio_step = PSS_RATIO_STEP; + num_entries = (ratio_max - ratio_min) / ratio_step; + while (num_entries > PSS_MAX_ENTRIES-1) { + ratio_step <<= 1; + num_entries >>= 1; + } + + /* P[T] is Turbo state if enabled */ + if (get_turbo_state() == TURBO_ENABLED) { + /* _PSS package count including Turbo */ + len_pss = acpigen_write_package(num_entries + 2); + + msr = rdmsr(MSR_TURBO_RATIO_LIMIT); + ratio_turbo = msr.lo & 0xff; + + /* Add entry for Turbo ratio */ + len_pss += acpigen_write_PSS_package( + clock_max + 1, /*MHz*/ + power_max, /*mW*/ + PSS_LATENCY_TRANSITION, /*lat1*/ + PSS_LATENCY_BUSMASTER, /*lat2*/ + ratio_turbo << 8, /*control*/ + ratio_turbo << 8); /*status*/ + } else { + /* _PSS package count without Turbo */ + len_pss = acpigen_write_package(num_entries + 1); + } + + /* First regular entry is max non-turbo ratio */ + len_pss += acpigen_write_PSS_package( + clock_max, /*MHz*/ + power_max, /*mW*/ + PSS_LATENCY_TRANSITION, /*lat1*/ + PSS_LATENCY_BUSMASTER, /*lat2*/ + ratio_max << 8, /*control*/ + ratio_max << 8); /*status*/ + + /* Generate the remaining entries */ + for (ratio = ratio_min + ((num_entries - 1) * ratio_step); + ratio >= ratio_min; ratio -= ratio_step) { + + /* Calculate power at this ratio */ + power = calculate_power(power_max, ratio_max, ratio); + clock = ratio * HASWELL_BCLK; + + len_pss += acpigen_write_PSS_package( + clock, /*MHz*/ + power, /*mW*/ + PSS_LATENCY_TRANSITION, /*lat1*/ + PSS_LATENCY_BUSMASTER, /*lat2*/ + ratio << 8, /*control*/ + ratio << 8); /*status*/ + } + + /* Fix package length */ + len_pss--; + acpigen_patch_len(len_pss); + + return len + len_pss; +} + +void generate_cpu_entries(void) +{ + int len_pr; + int coreID, cpuID, pcontrol_blk = get_pmbase(), plen = 6; + int totalcores = dev_count_cpu(); + int cores_per_package = get_cores_per_package(); + int numcpus = totalcores/cores_per_package; + + printk(BIOS_DEBUG, "Found %d CPU(s) with %d core(s) each.\n", + numcpus, cores_per_package); + + for (cpuID=1; cpuID <=numcpus; cpuID++) { + for (coreID=1; coreID<=cores_per_package; coreID++) { + if (coreID>1) { + pcontrol_blk = 0; + plen = 0; + } + + /* Generate processor \_PR.CPUx */ + len_pr = acpigen_write_processor( + (cpuID-1)*cores_per_package+coreID-1, + pcontrol_blk, plen); + + /* Generate P-state tables */ + len_pr += generate_P_state_entries( + coreID-1, cores_per_package); + + /* Generate C-state tables */ + len_pr += generate_C_state_entries(); + + /* Generate T-state tables */ + len_pr += generate_T_state_entries( + cpuID-1, cores_per_package); + + len_pr--; + acpigen_patch_len(len_pr); + } + } +} + +struct chip_operations cpu_intel_haswell_ops = { + CHIP_NAME("Intel Haswell CPU") +}; diff --git a/src/soc/intel/broadwell/acpi/cpu.asl b/src/soc/intel/broadwell/acpi/cpu.asl new file mode 100644 index 0000000000..558a9d3663 --- /dev/null +++ b/src/soc/intel/broadwell/acpi/cpu.asl @@ -0,0 +1,102 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 The ChromiumOS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +/* These devices are created at runtime */ +External (\_PR.CPU0, DeviceObj) +External (\_PR.CPU1, DeviceObj) +External (\_PR.CPU2, DeviceObj) +External (\_PR.CPU3, DeviceObj) +External (\_PR.CPU4, DeviceObj) +External (\_PR.CPU5, DeviceObj) +External (\_PR.CPU6, DeviceObj) +External (\_PR.CPU7, DeviceObj) + +/* Notify OS to re-read CPU tables, assuming ^2 CPU count */ +Method (PNOT) +{ + If (LGreaterEqual (\PCNT, 2)) { + Notify (\_PR.CPU0, 0x81) // _CST + Notify (\_PR.CPU1, 0x81) // _CST + } + If (LGreaterEqual (\PCNT, 4)) { + Notify (\_PR.CPU2, 0x81) // _CST + Notify (\_PR.CPU3, 0x81) // _CST + } + If (LGreaterEqual (\PCNT, 8)) { + Notify (\_PR.CPU4, 0x81) // _CST + Notify (\_PR.CPU5, 0x81) // _CST + Notify (\_PR.CPU6, 0x81) // _CST + Notify (\_PR.CPU7, 0x81) // _CST + } +} + +/* Notify OS to re-read CPU _PPC limit, assuming ^2 CPU count */ +Method (PPCN) +{ + If (LGreaterEqual (\PCNT, 2)) { + Notify (\_PR.CPU0, 0x80) // _PPC + Notify (\_PR.CPU1, 0x80) // _PPC + } + If (LGreaterEqual (\PCNT, 4)) { + Notify (\_PR.CPU2, 0x80) // _PPC + Notify (\_PR.CPU3, 0x80) // _PPC + } + If (LGreaterEqual (\PCNT, 8)) { + Notify (\_PR.CPU4, 0x80) // _PPC + Notify (\_PR.CPU5, 0x80) // _PPC + Notify (\_PR.CPU6, 0x80) // _PPC + Notify (\_PR.CPU7, 0x80) // _PPC + } +} + +/* Notify OS to re-read Throttle Limit tables, assuming ^2 CPU count */ +Method (TNOT) +{ + If (LGreaterEqual (\PCNT, 2)) { + Notify (\_PR.CPU0, 0x82) // _TPC + Notify (\_PR.CPU1, 0x82) // _TPC + } + If (LGreaterEqual (\PCNT, 4)) { + Notify (\_PR.CPU2, 0x82) // _TPC + Notify (\_PR.CPU3, 0x82) // _TPC + } + If (LGreaterEqual (\PCNT, 8)) { + Notify (\_PR.CPU4, 0x82) // _TPC + Notify (\_PR.CPU5, 0x82) // _TPC + Notify (\_PR.CPU6, 0x82) // _TPC + Notify (\_PR.CPU7, 0x82) // _TPC + } +} + +/* Return a package containing enabled processor entries */ +Method (PPKG) +{ + If (LGreaterEqual (\PCNT, 8)) { + Return (Package() {\_PR.CPU0, \_PR.CPU1, \_PR.CPU2, \_PR.CPU3, + \_PR.CPU4, \_PR.CPU5, \_PR.CPU6, \_PR.CPU7}) + } ElseIf (LGreaterEqual (\PCNT, 4)) { + Return (Package() {\_PR.CPU0, \_PR.CPU1, \_PR.CPU2, \_PR.CPU3}) + } ElseIf (LGreaterEqual (\PCNT, 2)) { + Return (Package() {\_PR.CPU0, \_PR.CPU1}) + } Else { + Return (Package() {\_PR.CPU0}) + } +} diff --git a/src/soc/intel/broadwell/acpi/globalnvs.asl b/src/soc/intel/broadwell/acpi/globalnvs.asl new file mode 100644 index 0000000000..ef05dca3c2 --- /dev/null +++ b/src/soc/intel/broadwell/acpi/globalnvs.asl @@ -0,0 +1,285 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2007-2009 coresystems GmbH + * Copyright (C) 2012 The Chromium OS Authors + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +/* Global Variables */ + +Name(\PICM, 0) // IOAPIC/8259 +Name(\DSEN, 1) // Display Output Switching Enable + +/* Global ACPI memory region. This region is used for passing information + * between coreboot (aka "the system bios"), ACPI, and the SMI handler. + * Since we don't know where this will end up in memory at ACPI compile time, + * we have to fix it up in coreboot's ACPI creation phase. + */ + + +OperationRegion (GNVS, SystemMemory, 0xC0DEBABE, 0xf00) +Field (GNVS, ByteAcc, NoLock, Preserve) +{ + /* Miscellaneous */ + Offset (0x00), + OSYS, 16, // 0x00 - Operating System + SMIF, 8, // 0x02 - SMI function + PRM0, 8, // 0x03 - SMI function parameter + PRM1, 8, // 0x04 - SMI function parameter + SCIF, 8, // 0x05 - SCI function + PRM2, 8, // 0x06 - SCI function parameter + PRM3, 8, // 0x07 - SCI function parameter + LCKF, 8, // 0x08 - Global Lock function for EC + PRM4, 8, // 0x09 - Lock function parameter + PRM5, 8, // 0x0a - Lock function parameter + P80D, 32, // 0x0b - Debug port (IO 0x80) value + LIDS, 8, // 0x0f - LID state (open = 1) + PWRS, 8, // 0x10 - Power State (AC = 1) + /* Thermal policy */ + Offset (0x11), + TLVL, 8, // 0x11 - Throttle Level Limit + FLVL, 8, // 0x12 - Current FAN Level + TCRT, 8, // 0x13 - Critical Threshold + TPSV, 8, // 0x14 - Passive Threshold + TMAX, 8, // 0x15 - CPU Tj_max + F0OF, 8, // 0x16 - FAN 0 OFF Threshold + F0ON, 8, // 0x17 - FAN 0 ON Threshold + F0PW, 8, // 0x18 - FAN 0 PWM value + F1OF, 8, // 0x19 - FAN 1 OFF Threshold + F1ON, 8, // 0x1a - FAN 1 ON Threshold + F1PW, 8, // 0x1b - FAN 1 PWM value + F2OF, 8, // 0x1c - FAN 2 OFF Threshold + F2ON, 8, // 0x1d - FAN 2 ON Threshold + F2PW, 8, // 0x1e - FAN 2 PWM value + F3OF, 8, // 0x1f - FAN 3 OFF Threshold + F3ON, 8, // 0x20 - FAN 3 ON Threshold + F3PW, 8, // 0x21 - FAN 3 PWM value + F4OF, 8, // 0x22 - FAN 4 OFF Threshold + F4ON, 8, // 0x23 - FAN 4 ON Threshold + F4PW, 8, // 0x24 - FAN 4 PWM value + TMPS, 8, // 0x25 - Temperature Sensor ID + /* Processor Identification */ + Offset (0x28), + APIC, 8, // 0x28 - APIC Enabled by coreboot + MPEN, 8, // 0x29 - Multi Processor Enable + PCP0, 8, // 0x2a - PDC CPU/CORE 0 + PCP1, 8, // 0x2b - PDC CPU/CORE 1 + PPCM, 8, // 0x2c - Max. PPC state + PCNT, 8, // 0x2d - Processor count + /* Super I/O & CMOS config */ + Offset (0x32), + NATP, 8, // 0x32 - + S5U0, 8, // 0x33 - Enable USB0 in S5 + S5U1, 8, // 0x34 - Enable USB1 in S5 + S3U0, 8, // 0x35 - Enable USB0 in S3 + S3U1, 8, // 0x36 - Enable USB1 in S3 + S33G, 8, // 0x37 - Enable 3G in S3 + CMEM, 32, // 0x38 - CBMEM TOC + /* Integrated Graphics Device */ + Offset (0x3c), + IGDS, 8, // 0x3c - IGD state (primary = 1) + TLST, 8, // 0x3d - Display Toggle List pointer + CADL, 8, // 0x3e - Currently Attached Devices List + PADL, 8, // 0x3f - Previously Attached Devices List + CSTE, 16, // 0x40 - Current display state + NSTE, 16, // 0x42 - Next display state + SSTE, 16, // 0x44 - Set display state + Offset (0x46), + NDID, 8, // 0x46 - Number of Device IDs + DID1, 32, // 0x47 - Device ID 1 + DID2, 32, // 0x4b - Device ID 2 + DID3, 32, // 0x4f - Device ID 3 + DID4, 32, // 0x53 - Device ID 4 + DID5, 32, // 0x57 - Device ID 5 + + /* TPM support */ + Offset (0x5b), + TPMP, 8, // 0x5b - TPM Present + TPME, 8, // 0x5c - TPM Enable + + /* LynxPoint Serial IO device BARs */ + Offset (0x60), + S0B0, 32, // 0x60 - D21:F0 Serial IO SDMA BAR0 + S1B0, 32, // 0x64 - D21:F1 Serial IO I2C0 BAR0 + S2B0, 32, // 0x68 - D21:F2 Serial IO I2C1 BAR0 + S3B0, 32, // 0x6c - D21:F3 Serial IO SPI0 BAR0 + S4B0, 32, // 0x70 - D21:F4 Serial IO SPI1 BAR0 + S5B0, 32, // 0x74 - D21:F5 Serial IO UAR0 BAR0 + S6B0, 32, // 0x78 - D21:F6 Serial IO UAR1 BAR0 + S7B0, 32, // 0x7c - D23:F0 Serial IO SDIO BAR0 + S0B1, 32, // 0x80 - D21:F0 Serial IO SDMA BAR1 + S1B1, 32, // 0x84 - D21:F1 Serial IO I2C0 BAR1 + S2B1, 32, // 0x88 - D21:F2 Serial IO I2C1 BAR1 + S3B1, 32, // 0x8c - D21:F3 Serial IO SPI0 BAR1 + S4B1, 32, // 0x90 - D21:F4 Serial IO SPI1 BAR1 + S5B1, 32, // 0x94 - D21:F5 Serial IO UAR0 BAR1 + S6B1, 32, // 0x98 - D21:F6 Serial IO UAR1 BAR1 + S7B1, 32, // 0x9c - D23:F0 Serial IO SDIO BAR1 + + Offset (0xa0), + CBMC, 32, // 0xa0 - coreboot mem console pointer + + /* IGD OpRegion */ + Offset (0xb4), + ASLB, 32, // 0xb4 - IGD OpRegion Base Address + IBTT, 8, // 0xb8 - IGD boot panel device + IPAT, 8, // 0xb9 - IGD panel type cmos option + ITVF, 8, // 0xba - IGD TV format cmos option + ITVM, 8, // 0xbb - IGD TV minor format option + IPSC, 8, // 0xbc - IGD panel scaling + IBLC, 8, // 0xbd - IGD BLC config + IBIA, 8, // 0xbe - IGD BIA config + ISSC, 8, // 0xbf - IGD SSC config + I409, 8, // 0xc0 - IGD 0409 modified settings + I509, 8, // 0xc1 - IGD 0509 modified settings + I609, 8, // 0xc2 - IGD 0609 modified settings + I709, 8, // 0xc3 - IGD 0709 modified settings + IDMM, 8, // 0xc4 - IGD Power conservation feature + IDMS, 8, // 0xc5 - IGD DVMT memory size + IF1E, 8, // 0xc6 - IGD function 1 enable + HVCO, 8, // 0xc7 - IGD HPLL VCO + NXD1, 32, // 0xc8 - IGD _DGS next DID1 + NXD2, 32, // 0xcc - IGD _DGS next DID2 + NXD3, 32, // 0xd0 - IGD _DGS next DID3 + NXD4, 32, // 0xd4 - IGD _DGS next DID4 + NXD5, 32, // 0xd8 - IGD _DGS next DID5 + NXD6, 32, // 0xdc - IGD _DGS next DID6 + NXD7, 32, // 0xe0 - IGD _DGS next DID7 + NXD8, 32, // 0xe4 - IGD _DGS next DID8 + + ISCI, 8, // 0xe8 - IGD SMI/SCI mode (0: SCI) + PAVP, 8, // 0xe9 - IGD PAVP data + Offset (0xeb), + OSCC, 8, // 0xeb - PCIe OSC control + NPCE, 8, // 0xec - native pcie support + PLFL, 8, // 0xed - platform flavor + BREV, 8, // 0xee - board revision + DPBM, 8, // 0xef - digital port b mode + DPCM, 8, // 0xf0 - digital port c mode + DPDM, 8, // 0xf1 - digital port d mode + ALFP, 8, // 0xf2 - active lfp + IMON, 8, // 0xf3 - current graphics turbo imon value + MMIO, 8, // 0xf4 - 64bit mmio support + + /* ChromeOS specific */ + Offset (0x100), + #include +} + +/* Set flag to enable USB charging in S3 */ +Method (S3UE) +{ + Store (One, \S3U0) + Store (One, \S3U1) +} + +/* Set flag to disable USB charging in S3 */ +Method (S3UD) +{ + Store (Zero, \S3U0) + Store (Zero, \S3U1) +} + +/* Set flag to enable USB charging in S5 */ +Method (S5UE) +{ + Store (One, \S5U0) + Store (One, \S5U1) +} + +/* Set flag to disable USB charging in S5 */ +Method (S5UD) +{ + Store (Zero, \S5U0) + Store (Zero, \S5U1) +} + +/* Set flag to enable 3G module in S3 */ +Method (S3GE) +{ + Store (One, \S33G) +} + +/* Set flag to disable 3G module in S3 */ +Method (S3GD) +{ + Store (Zero, \S33G) +} + +External (\_TZ.THRM) +External (\_TZ.SKIN) + +Method (TZUP) +{ + /* Update Primary Thermal Zone */ + If (CondRefOf (\_TZ.THRM, Local0)) { + Notify (\_TZ.THRM, 0x81) + } + + /* Update Secondary Thermal Zone */ + If (CondRefOf (\_TZ.SKIN, Local0)) { + Notify (\_TZ.SKIN, 0x81) + } +} + +/* Update Fan 0 thresholds */ +Method (F0UT, 2) +{ + Store (Arg0, \F0OF) + Store (Arg1, \F0ON) + TZUP () +} + +/* Update Fan 1 thresholds */ +Method (F1UT, 2) +{ + Store (Arg0, \F1OF) + Store (Arg1, \F1ON) + TZUP () +} + +/* Update Fan 2 thresholds */ +Method (F2UT, 2) +{ + Store (Arg0, \F2OF) + Store (Arg1, \F2ON) + TZUP () +} + +/* Update Fan 3 thresholds */ +Method (F3UT, 2) +{ + Store (Arg0, \F3OF) + Store (Arg1, \F3ON) + TZUP () +} + +/* Update Fan 4 thresholds */ +Method (F4UT, 2) +{ + Store (Arg0, \F4OF) + Store (Arg1, \F4ON) + TZUP () +} + +/* Update Temperature Sensor ID */ +Method (TMPU, 1) +{ + Store (Arg0, \TMPS) + TZUP () +} diff --git a/src/soc/intel/broadwell/acpi/gpio.asl b/src/soc/intel/broadwell/acpi/gpio.asl new file mode 100644 index 0000000000..8616f9724d --- /dev/null +++ b/src/soc/intel/broadwell/acpi/gpio.asl @@ -0,0 +1,133 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +Device (GPIO) +{ + // GPIO Controller + Name (_HID, "INT33C7") + Name (_CID, "INT33C7") + Name (_UID, 1) + + Name (RBUF, ResourceTemplate() + { + DWordIo (ResourceProducer, + MinFixed, // IsMinFixed + MaxFixed, // IsMaxFixed + PosDecode, // Decode + EntireRange, // ISARanges + 0x00000000, // AddressGranularity + 0x00000000, // AddressMinimum + 0x00000000, // AddressMaximum + 0x00000000, // AddressTranslation + 0x00000000, // RangeLength + , // ResourceSourceIndex + , // ResourceSource + BAR0) + Interrupt (ResourceConsumer, + Level, ActiveHigh, Shared, , , ) {14} + }) + + Method (_CRS, 0, NotSerialized) + { + CreateDwordField (^RBUF, ^BAR0._MIN, BMIN) + CreateDwordField (^RBUF, ^BAR0._MAX, BMAX) + CreateDwordField (^RBUF, ^BAR0._LEN, BLEN) + + Store (GPIO_BASE_SIZE, BLEN) + Store (GPIO_BASE_ADDRESS, BMIN) + Store (Subtract (Add (GPIO_BASE_ADDRESS, + GPIO_BASE_SIZE), 1), BMAX) + + Return (RBUF) + } + + Method (_STA, 0, NotSerialized) + { + Return (0xF) + } + + // GWAK: Setup GPIO as ACPI GPE for Wake + // Arg0: GPIO Number + Method (GWAK, 1, NotSerialized) + { + // Local0 = GPIO Base Address + Store (And (GPBS, Not(0x1)), Local0) + + // Local1 = BANK, Local2 = OFFSET + Divide (Arg0, 32, Local2, Local1) + + // + // Set OWNER to ACPI + // + + // Local3 = GPIOBASE + GPIO_OWN(BANK) + Store (Add (Local0, Multiply (Local1, 0x4)), Local3) + + // GPIO_OWN(BANK) + OperationRegion (IOWN, SystemIO, Local3, 4) + Field (IOWN, AnyAcc, NoLock, Preserve) { + GOWN, 32, + } + + // GPIO_OWN[GPIO] = 0 (ACPI) + Store (And (GOWN, Not (ShiftLeft (0x1, Local2))), GOWN) + + // + // Set ROUTE to SCI + // + + // Local3 = GPIOBASE + GPIO_ROUTE(BANK) + Store (Add (Add (Local0, 0x30), Multiply (Local1, 0x4)), Local3) + + // GPIO_ROUTE(BANK) + OperationRegion (IROU, SystemIO, Local3, 4) + Field (IROU, AnyAcc, NoLock, Preserve) { + GROU, 32, + } + + // GPIO_ROUTE[GPIO] = 0 (SCI) + Store (And (GROU, Not (ShiftLeft (0x1, Local2))), GROU) + + // + // Set GPnCONFIG to GPIO|INPUT|INVERT + // + + // Local3 = GPIOBASE + GPnCONFIG0(GPIO) + Store (Add (Add (Local0, 0x100), Multiply (Arg0, 0x8)), Local3) + + // GPnCONFIG(GPIO) + OperationRegion (GPNC, SystemIO, Local3, 8) + Field (GPNC, AnyAcc, NoLock, Preserve) { + GMOD, 1, // MODE: 0=NATIVE 1=GPIO + , 1, + GIOS, 1, // IO_SEL: 0=OUTPUT 1=INPUT + GINV, 1, // INVERT: 0=NORMAL 1=INVERT + GLES, 1, // LxEB: 0=EDGE 1=LEVEL + , 24, + ILVL, 1, // INPUT: 0=LOW 1=HIGH + OLVL, 1, // OUTPUT: 0=LOW 1=HIGH + GPWP, 2, // PULLUP: 00=NONE 01=DOWN 10=UP 11=INVALID + ISEN, 1, // SENSE: 0=ENABLE 1=DISABLE + } + + Store (0x1, GMOD) // GPIO + Store (0x1, GIOS) // INPUT + Store (0x1, GINV) // INVERT + } +} diff --git a/src/soc/intel/broadwell/acpi/hda.asl b/src/soc/intel/broadwell/acpi/hda.asl new file mode 100644 index 0000000000..2bc11686fd --- /dev/null +++ b/src/soc/intel/broadwell/acpi/hda.asl @@ -0,0 +1,41 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2007-2009 coresystems GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +/* Intel PCH HDA */ + +// Intel High Definition Audio (Azalia) 0:1b.0 + +Device (HDEF) +{ + Name (_ADR, 0x001b0000) + + Name (PRWH, Package(){ 0x0d, 3 }) // LPT-H + Name (PRWL, Package(){ 0x6d, 3 }) // LPT-LP + + Method (_PRW, 0) { // Power Resources for Wake + If (\ISLP ()) { + Return (PRWL) + } Else { + Return (PRWH) + } + } +} + diff --git a/src/soc/intel/broadwell/acpi/irqlinks.asl b/src/soc/intel/broadwell/acpi/irqlinks.asl new file mode 100644 index 0000000000..5fcee45f29 --- /dev/null +++ b/src/soc/intel/broadwell/acpi/irqlinks.asl @@ -0,0 +1,493 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2007-2009 coresystems GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +Device (LNKA) +{ + Name (_HID, EISAID("PNP0C0F")) + Name (_UID, 1) + + // Disable method + Method (_DIS, 0, Serialized) + { + Store (0x80, PRTA) + } + + // Possible Resource Settings for this Link + Name (_PRS, ResourceTemplate() + { + IRQ(Level, ActiveLow, Shared) + { 1, 3, 4, 5, 6, 7, 10, 12, 14, 15 } + }) + + // Current Resource Settings for this link + Method (_CRS, 0, Serialized) + { + Name (RTLA, ResourceTemplate() + { + IRQ(Level, ActiveLow, Shared) {} + }) + CreateWordField(RTLA, 1, IRQ0) + + // Clear the WordField + Store (Zero, IRQ0) + + // Set the bit from PRTA + ShiftLeft(1, And(PRTA, 0x0f), IRQ0) + + Return (RTLA) + } + + // Set Resource Setting for this IRQ link + Method (_SRS, 1, Serialized) + { + CreateWordField(Arg0, 1, IRQ0) + + // Which bit is set? + FindSetRightBit(IRQ0, Local0) + + Decrement(Local0) + Store(Local0, PRTA) + } + + // Status + Method (_STA, 0, Serialized) + { + If(And(PRTA, 0x80)) { + Return (0x9) + } Else { + Return (0xb) + } + } +} + +Device (LNKB) +{ + Name (_HID, EISAID("PNP0C0F")) + Name (_UID, 2) + + // Disable method + Method (_DIS, 0, Serialized) + { + Store (0x80, PRTB) + } + + // Possible Resource Settings for this Link + Name (_PRS, ResourceTemplate() + { + IRQ(Level, ActiveLow, Shared) + { 1, 3, 4, 5, 6, 7, 11, 12, 14, 15 } + }) + + // Current Resource Settings for this link + Method (_CRS, 0, Serialized) + { + Name (RTLB, ResourceTemplate() + { + IRQ(Level, ActiveLow, Shared) {} + }) + CreateWordField(RTLB, 1, IRQ0) + + // Clear the WordField + Store (Zero, IRQ0) + + // Set the bit from PRTB + ShiftLeft(1, And(PRTB, 0x0f), IRQ0) + + Return (RTLB) + } + + // Set Resource Setting for this IRQ link + Method (_SRS, 1, Serialized) + { + CreateWordField(Arg0, 1, IRQ0) + + // Which bit is set? + FindSetRightBit(IRQ0, Local0) + + Decrement(Local0) + Store(Local0, PRTB) + } + + // Status + Method (_STA, 0, Serialized) + { + If(And(PRTB, 0x80)) { + Return (0x9) + } Else { + Return (0xb) + } + } +} + +Device (LNKC) +{ + Name (_HID, EISAID("PNP0C0F")) + Name (_UID, 3) + + // Disable method + Method (_DIS, 0, Serialized) + { + Store (0x80, PRTC) + } + + // Possible Resource Settings for this Link + Name (_PRS, ResourceTemplate() + { + IRQ(Level, ActiveLow, Shared) + { 1, 3, 4, 5, 6, 7, 10, 12, 14, 15 } + }) + + // Current Resource Settings for this link + Method (_CRS, 0, Serialized) + { + Name (RTLC, ResourceTemplate() + { + IRQ(Level, ActiveLow, Shared) {} + }) + CreateWordField(RTLC, 1, IRQ0) + + // Clear the WordField + Store (Zero, IRQ0) + + // Set the bit from PRTC + ShiftLeft(1, And(PRTC, 0x0f), IRQ0) + + Return (RTLC) + } + + // Set Resource Setting for this IRQ link + Method (_SRS, 1, Serialized) + { + CreateWordField(Arg0, 1, IRQ0) + + // Which bit is set? + FindSetRightBit(IRQ0, Local0) + + Decrement(Local0) + Store(Local0, PRTC) + } + + // Status + Method (_STA, 0, Serialized) + { + If(And(PRTC, 0x80)) { + Return (0x9) + } Else { + Return (0xb) + } + } +} + +Device (LNKD) +{ + Name (_HID, EISAID("PNP0C0F")) + Name (_UID, 4) + + // Disable method + Method (_DIS, 0, Serialized) + { + Store (0x80, PRTD) + } + + // Possible Resource Settings for this Link + Name (_PRS, ResourceTemplate() + { + IRQ(Level, ActiveLow, Shared) + { 1, 3, 4, 5, 6, 7, 11, 12, 14, 15 } + }) + + // Current Resource Settings for this link + Method (_CRS, 0, Serialized) + { + Name (RTLD, ResourceTemplate() + { + IRQ(Level, ActiveLow, Shared) {} + }) + CreateWordField(RTLD, 1, IRQ0) + + // Clear the WordField + Store (Zero, IRQ0) + + // Set the bit from PRTD + ShiftLeft(1, And(PRTD, 0x0f), IRQ0) + + Return (RTLD) + } + + // Set Resource Setting for this IRQ link + Method (_SRS, 1, Serialized) + { + CreateWordField(Arg0, 1, IRQ0) + + // Which bit is set? + FindSetRightBit(IRQ0, Local0) + + Decrement(Local0) + Store(Local0, PRTD) + } + + // Status + Method (_STA, 0, Serialized) + { + If(And(PRTD, 0x80)) { + Return (0x9) + } Else { + Return (0xb) + } + } +} + +Device (LNKE) +{ + Name (_HID, EISAID("PNP0C0F")) + Name (_UID, 5) + + // Disable method + Method (_DIS, 0, Serialized) + { + Store (0x80, PRTE) + } + + // Possible Resource Settings for this Link + Name (_PRS, ResourceTemplate() + { + IRQ(Level, ActiveLow, Shared) + { 1, 3, 4, 5, 6, 7, 10, 12, 14, 15 } + }) + + // Current Resource Settings for this link + Method (_CRS, 0, Serialized) + { + Name (RTLE, ResourceTemplate() + { + IRQ(Level, ActiveLow, Shared) {} + }) + CreateWordField(RTLE, 1, IRQ0) + + // Clear the WordField + Store (Zero, IRQ0) + + // Set the bit from PRTE + ShiftLeft(1, And(PRTE, 0x0f), IRQ0) + + Return (RTLE) + } + + // Set Resource Setting for this IRQ link + Method (_SRS, 1, Serialized) + { + CreateWordField(Arg0, 1, IRQ0) + + // Which bit is set? + FindSetRightBit(IRQ0, Local0) + + Decrement(Local0) + Store(Local0, PRTE) + } + + // Status + Method (_STA, 0, Serialized) + { + If(And(PRTE, 0x80)) { + Return (0x9) + } Else { + Return (0xb) + } + } +} + +Device (LNKF) +{ + Name (_HID, EISAID("PNP0C0F")) + Name (_UID, 6) + + // Disable method + Method (_DIS, 0, Serialized) + { + Store (0x80, PRTF) + } + + // Possible Resource Settings for this Link + Name (_PRS, ResourceTemplate() + { + IRQ(Level, ActiveLow, Shared) + { 1, 3, 4, 5, 6, 7, 11, 12, 14, 15 } + }) + + // Current Resource Settings for this link + Method (_CRS, 0, Serialized) + { + Name (RTLF, ResourceTemplate() + { + IRQ(Level, ActiveLow, Shared) {} + }) + CreateWordField(RTLF, 1, IRQ0) + + // Clear the WordField + Store (Zero, IRQ0) + + // Set the bit from PRTF + ShiftLeft(1, And(PRTF, 0x0f), IRQ0) + + Return (RTLF) + } + + // Set Resource Setting for this IRQ link + Method (_SRS, 1, Serialized) + { + CreateWordField(Arg0, 1, IRQ0) + + // Which bit is set? + FindSetRightBit(IRQ0, Local0) + + Decrement(Local0) + Store(Local0, PRTF) + } + + // Status + Method (_STA, 0, Serialized) + { + If(And(PRTF, 0x80)) { + Return (0x9) + } Else { + Return (0xb) + } + } +} + +Device (LNKG) +{ + Name (_HID, EISAID("PNP0C0F")) + Name (_UID, 7) + + // Disable method + Method (_DIS, 0, Serialized) + { + Store (0x80, PRTG) + } + + // Possible Resource Settings for this Link + Name (_PRS, ResourceTemplate() + { + IRQ(Level, ActiveLow, Shared) + { 1, 3, 4, 5, 6, 7, 10, 12, 14, 15 } + }) + + // Current Resource Settings for this link + Method (_CRS, 0, Serialized) + { + Name (RTLG, ResourceTemplate() + { + IRQ(Level, ActiveLow, Shared) {} + }) + CreateWordField(RTLG, 1, IRQ0) + + // Clear the WordField + Store (Zero, IRQ0) + + // Set the bit from PRTG + ShiftLeft(1, And(PRTG, 0x0f), IRQ0) + + Return (RTLG) + } + + // Set Resource Setting for this IRQ link + Method (_SRS, 1, Serialized) + { + CreateWordField(Arg0, 1, IRQ0) + + // Which bit is set? + FindSetRightBit(IRQ0, Local0) + + Decrement(Local0) + Store(Local0, PRTG) + } + + // Status + Method (_STA, 0, Serialized) + { + If(And(PRTG, 0x80)) { + Return (0x9) + } Else { + Return (0xb) + } + } +} + +Device (LNKH) +{ + Name (_HID, EISAID("PNP0C0F")) + Name (_UID, 8) + + // Disable method + Method (_DIS, 0, Serialized) + { + Store (0x80, PRTH) + } + + // Possible Resource Settings for this Link + Name (_PRS, ResourceTemplate() + { + IRQ(Level, ActiveLow, Shared) + { 1, 3, 4, 5, 6, 7, 11, 12, 14, 15 } + }) + + // Current Resource Settings for this link + Method (_CRS, 0, Serialized) + { + Name (RTLH, ResourceTemplate() + { + IRQ(Level, ActiveLow, Shared) {} + }) + CreateWordField(RTLH, 1, IRQ0) + + // Clear the WordField + Store (Zero, IRQ0) + + // Set the bit from PRTH + ShiftLeft(1, And(PRTH, 0x0f), IRQ0) + + Return (RTLH) + } + + // Set Resource Setting for this IRQ link + Method (_SRS, 1, Serialized) + { + CreateWordField(Arg0, 1, IRQ0) + + // Which bit is set? + FindSetRightBit(IRQ0, Local0) + + Decrement(Local0) + Store(Local0, PRTH) + } + + // Status + Method (_STA, 0, Serialized) + { + If(And(PRTH, 0x80)) { + Return (0x9) + } Else { + Return (0xb) + } + } +} + diff --git a/src/soc/intel/broadwell/acpi/lpc.asl b/src/soc/intel/broadwell/acpi/lpc.asl new file mode 100644 index 0000000000..07b3fcf745 --- /dev/null +++ b/src/soc/intel/broadwell/acpi/lpc.asl @@ -0,0 +1,255 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2007-2009 coresystems GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +// Intel LPC Bus Device - 0:1f.0 + +Device (LPCB) +{ + Name(_ADR, 0x001f0000) + + OperationRegion(LPC0, PCI_Config, 0x00, 0x100) + Field (LPC0, AnyAcc, NoLock, Preserve) + { + Offset (0x3), + DIDH, 8, // Device ID High Byte + Offset (0x40), + PMBS, 16, // PMBASE + Offset (0x48), + GPBS, 16, // GPIOBASE + Offset (0x60), // Interrupt Routing Registers + PRTA, 8, + PRTB, 8, + PRTC, 8, + PRTD, 8, + Offset (0x68), + PRTE, 8, + PRTF, 8, + PRTG, 8, + PRTH, 8, + + Offset (0x80), // IO Decode Ranges + IOD0, 8, + IOD1, 8, + + Offset (0xf0), // RCBA + RCEN, 1, + , 13, + RCBA, 18, + } + + #include "irqlinks.asl" + + #include "acpi/ec.asl" + + Device (DMAC) // DMA Controller + { + Name(_HID, EISAID("PNP0200")) + Name(_CRS, ResourceTemplate() + { + IO (Decode16, 0x00, 0x00, 0x01, 0x20) + IO (Decode16, 0x81, 0x81, 0x01, 0x11) + IO (Decode16, 0x93, 0x93, 0x01, 0x0d) + IO (Decode16, 0xc0, 0xc0, 0x01, 0x20) + DMA (Compatibility, NotBusMaster, Transfer8_16) { 4 } + }) + } + + Device (FWH) // Firmware Hub + { + Name (_HID, EISAID("INT0800")) + Name (_CRS, ResourceTemplate() + { + Memory32Fixed(ReadOnly, 0xff000000, 0x01000000) + }) + } + + Device (HPET) + { + Name (_HID, EISAID("PNP0103")) + Name (_CID, 0x010CD041) + + Name(BUF0, ResourceTemplate() + { + Memory32Fixed(ReadOnly, 0xfed00000, 0x400, FED0) + }) + + Method (_STA, 0) // Device Status + { + If (HPTE) { + // Note: Ancient versions of Windows don't want + // to see the HPET in order to work right + If (LGreaterEqual(OSYS, 2001)) { + Return (0xf) // Enable and show device + } Else { + Return (0xb) // Enable and don't show device + } + } + + Return (0x0) // Not enabled, don't show. + } + + Method (_CRS, 0, Serialized) // Current resources + { + If (HPTE) { + CreateDWordField(BUF0, \_SB.PCI0.LPCB.HPET.FED0._BAS, HPT0) + If (Lequal(HPAS, 1)) { + Store(0xfed01000, HPT0) + } + + If (Lequal(HPAS, 2)) { + Store(0xfed02000, HPT0) + } + + If (Lequal(HPAS, 3)) { + Store(0xfed03000, HPT0) + } + } + + Return (BUF0) + } + } + + Device(PIC) // 8259 Interrupt Controller + { + Name(_HID,EISAID("PNP0000")) + Name(_CRS, ResourceTemplate() + { + IO (Decode16, 0x20, 0x20, 0x01, 0x02) + IO (Decode16, 0x24, 0x24, 0x01, 0x02) + IO (Decode16, 0x28, 0x28, 0x01, 0x02) + IO (Decode16, 0x2c, 0x2c, 0x01, 0x02) + IO (Decode16, 0x30, 0x30, 0x01, 0x02) + IO (Decode16, 0x34, 0x34, 0x01, 0x02) + IO (Decode16, 0x38, 0x38, 0x01, 0x02) + IO (Decode16, 0x3c, 0x3c, 0x01, 0x02) + IO (Decode16, 0xa0, 0xa0, 0x01, 0x02) + IO (Decode16, 0xa4, 0xa4, 0x01, 0x02) + IO (Decode16, 0xa8, 0xa8, 0x01, 0x02) + IO (Decode16, 0xac, 0xac, 0x01, 0x02) + IO (Decode16, 0xb0, 0xb0, 0x01, 0x02) + IO (Decode16, 0xb4, 0xb4, 0x01, 0x02) + IO (Decode16, 0xb8, 0xb8, 0x01, 0x02) + IO (Decode16, 0xbc, 0xbc, 0x01, 0x02) + IO (Decode16, 0x4d0, 0x4d0, 0x01, 0x02) + IRQNoFlags () { 2 } + }) + } + + Device(MATH) // FPU + { + Name (_HID, EISAID("PNP0C04")) + Name (_CRS, ResourceTemplate() + { + IO (Decode16, 0xf0, 0xf0, 0x01, 0x01) + IRQNoFlags() { 13 } + }) + } + + Device(LDRC) // LPC device: Resource consumption + { + Name (_HID, EISAID("PNP0C02")) + Name (_UID, 2) + + Name (RBUF, ResourceTemplate() + { + IO (Decode16, 0x2e, 0x2e, 0x1, 0x02) // First SuperIO + IO (Decode16, 0x4e, 0x4e, 0x1, 0x02) // Second SuperIO + IO (Decode16, 0x61, 0x61, 0x1, 0x01) // NMI Status + IO (Decode16, 0x63, 0x63, 0x1, 0x01) // CPU Reserved + IO (Decode16, 0x65, 0x65, 0x1, 0x01) // CPU Reserved + IO (Decode16, 0x67, 0x67, 0x1, 0x01) // CPU Reserved + IO (Decode16, 0x80, 0x80, 0x1, 0x01) // Port 80 Post + IO (Decode16, 0x92, 0x92, 0x1, 0x01) // CPU Reserved + IO (Decode16, 0xb2, 0xb2, 0x1, 0x02) // SWSMI + IO (Decode16, DEFAULT_PMBASE, DEFAULT_PMBASE, + 0x1, 0xff) + + // GPIO region may be 128 bytes or 4096 bytes + IO (Decode16, 0x0000, 0x0000, 0x1, 0x00, GPR1) + }) + + Method (_CRS, 0, NotSerialized) + { + // LynxPoint-LP GPIO resources are defined in the + // SerialIO GPIO device and LynxPoint-H GPIO resources + // are defined here. + If (LNot (\ISLP ())) { + CreateByteField (^RBUF, ^GPR1._LEN, R1LN) + CreateWordField (^RBUF, ^GPR1._MIN, R1MN) + CreateWordField (^RBUF, ^GPR1._MAX, R1MX) + + // Update GPIO region length + Store (DEFAULT_GPIOBASE, R1MN) + Store (DEFAULT_GPIOBASE, R1MX) + Store (DEFAULT_GPIOSIZE, R1LN) + } + Return (RBUF) + } + } + + Device (RTC) // Real Time Clock + { + Name (_HID, EISAID("PNP0B00")) + Name (_CRS, ResourceTemplate() + { + IO (Decode16, 0x70, 0x70, 1, 8) +// Disable as Windows doesn't like it, and systems don't seem to use it. +// IRQNoFlags() { 8 } + }) + } + + Device (TIMR) // Intel 8254 timer + { + Name(_HID, EISAID("PNP0100")) + Name(_CRS, ResourceTemplate() + { + IO (Decode16, 0x40, 0x40, 0x01, 0x04) + IO (Decode16, 0x50, 0x50, 0x10, 0x04) + IRQNoFlags() {0} + }) + } + + #include "acpi/superio.asl" + +#ifdef ENABLE_TPM + Device (TPM) // Trusted Platform Module + { + Name(_HID, EISAID("IFX0102")) + Name(_CID, 0x310cd041) + Name(_UID, 1) + + Method(_STA, 0) + { + If (TPMP) { + Return (0xf) + } + Return (0x0) + } + + Name(_CRS, ResourceTemplate() { + IO (Decode16, 0x2e, 0x2e, 0x01, 0x02) + IO (Decode16, 0x6f0, 0x6f0, 0x01, 0x10) + Memory32Fixed (ReadWrite, 0xfed40000, 0x5000) + IRQ (Edge, Activehigh, Exclusive) { 6 } + }) + } +#endif +} diff --git a/src/soc/intel/broadwell/acpi/pch.asl b/src/soc/intel/broadwell/acpi/pch.asl new file mode 100644 index 0000000000..8ac55eb851 --- /dev/null +++ b/src/soc/intel/broadwell/acpi/pch.asl @@ -0,0 +1,124 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2007-2009 coresystems GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +/* Intel Cougar Point PCH support */ + +Scope(\) +{ + // Return TRUE if chipset is LynxPoint-LP + Method (ISLP, 0, NotSerialized) + { + If (LEqual (\_SB.PCI0.LPCB.DIDH, 0x9c)) { + Return (1) + } else { + Return (0) + } + } + + // IO-Trap at 0x800. This is the ACPI->SMI communication interface. + + OperationRegion(IO_T, SystemIO, 0x800, 0x10) + Field(IO_T, ByteAcc, NoLock, Preserve) + { + Offset(0x8), + TRP0, 8 // IO-Trap at 0x808 + } + + // ICH7 Root Complex Register Block. Memory Mapped through RCBA) + OperationRegion(RCRB, SystemMemory, DEFAULT_RCBA, 0x4000) + Field(RCRB, DWordAcc, Lock, Preserve) + { + Offset(0x0000), // Backbone + Offset(0x1000), // Chipset + Offset(0x3000), // Legacy Configuration Registers + Offset(0x3404), // High Performance Timer Configuration + HPAS, 2, // Address Select + , 5, + HPTE, 1, // Address Enable + Offset(0x3418), // FD (Function Disable) + , 1, // Reserved + PCID, 1, // PCI bridge disable + SA1D, 1, // SATA1 disable + SMBD, 1, // SMBUS disable + HDAD, 1, // Azalia disable + , 8, // Reserved + EH2D, 1, // EHCI #2 disable + LPBD, 1, // LPC bridge disable + EH1D, 1, // EHCI #1 disable + RP1D, 1, // Root Port 1 disable + RP2D, 1, // Root Port 2 disable + RP3D, 1, // Root Port 3 disable + RP4D, 1, // Root Port 4 disable + RP5D, 1, // Root Port 5 disable + RP6D, 1, // Root Port 6 disable + RP7D, 1, // Root Port 7 disable + RP8D, 1, // Root Port 8 disable + TTRD, 1, // Thermal sensor registers disable + SA2D, 1, // SATA2 disable + Offset(0x3428), // FD2 (Function Disable 2) + BDFD, 1, // Display BDF + ME1D, 1, // ME Interface 1 disable + ME2D, 1, // ME Interface 2 disable + IDRD, 1, // IDE redirect disable + KTCT, 1, // Keyboard Text redirect disable + } +} + +// High Definition Audio (Azalia) 0:1b.0 +#include "audio.asl" + +// PCI Express Ports 0:1c.x +#include "pcie.asl" + +// USB 0:1d.0 and 0:1a.0 +#include "usb.asl" + +// LPC Bridge 0:1f.0 +#include "lpc.asl" + +// SATA 0:1f.2, 0:1f.5 +#include "sata.asl" + +// SMBus 0:1f.3 +#include "smbus.asl" + +// Serial IO +#if CONFIG_INTEL_LYNXPOINT_LP +#include "serialio.asl" +#include "lpt_lp.asl" +#endif + +Method (_OSC, 4) +{ + /* Check for proper GUID */ + If (LEqual (Arg0, ToUUID("33DB4D5B-1FF7-401C-9657-7441C03DD766"))) + { + /* Let OS control everything */ + Return (Arg3) + } + Else + { + /* Unrecognized UUID */ + CreateDWordField (Arg3, 0, CDW1) + Or (CDW1, 4, CDW1) + Return (Arg3) + } +} diff --git a/src/soc/intel/broadwell/acpi/pci_irqs.asl b/src/soc/intel/broadwell/acpi/pci_irqs.asl new file mode 100644 index 0000000000..0c23e10a51 --- /dev/null +++ b/src/soc/intel/broadwell/acpi/pci_irqs.asl @@ -0,0 +1,89 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2007-2009 coresystems GmbH + * Copyright (C) 2014 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +Method(_PRT) +{ + If (PICM) { + Return (Package() { + // Onboard graphics (IGD) 0:2.0 + Package() { 0x0002ffff, 0, 0, 16 }, + // Mini-HD Audio 0:3.0 + Package() { 0x0003ffff, 0, 0, 16 }, + // High Definition Audio 0:1b.0 + Package() { 0x001bffff, 0, 0, 22 }, + // PCIe Root Ports 0:1c.x + Package() { 0x001cffff, 0, 0, 16 }, + Package() { 0x001cffff, 1, 0, 17 }, + Package() { 0x001cffff, 2, 0, 18 }, + Package() { 0x001cffff, 3, 0, 19 }, + // EHCI 0:1d.0 + Package() { 0x001dffff, 0, 0, 19 }, + // Audio DSP (Smart Sound) 0:13.0 + Package() { 0x0013ffff, 0, 0, 23 }, + // XHCI 0:14.0 + Package() { 0x0014ffff, 0, 0, 18 }, + // LPC devices 0:1f.0 + Package() { 0x001fffff, 0, 0, 22 }, + Package() { 0x001fffff, 1, 0, 18 }, + Package() { 0x001fffff, 2, 0, 17 }, + Package() { 0x001fffff, 3, 0, 16 }, + // Serial IO 0:15.0 + Package() { 0x0015ffff, 0, 0, 20 }, + Package() { 0x0015ffff, 1, 0, 21 }, + Package() { 0x0015ffff, 2, 0, 21 }, + Package() { 0x0015ffff, 3, 0, 21 }, + // SDIO 0:17.0 + Package() { 0x0017ffff, 0, 0, 23 }, + }) + } Else { + Return (Package() { + // Onboard graphics (IGD) 0:2.0 + Package() { 0x0002ffff, 0, \_SB.PCI0.LPCB.LNKA, 0 }, + // Mini-HD Audio 0:3.0 + Package() { 0x0003ffff, 0, \_SB.PCI0.LPCB.LNKA, 0 }, + // High Definition Audio 0:1b.0 + Package() { 0x001bffff, 0, \_SB.PCI0.LPCB.LNKG, 0 }, + // PCIe Root Ports 0:1c.x + Package() { 0x001cffff, 0, \_SB.PCI0.LPCB.LNKA, 0 }, + Package() { 0x001cffff, 1, \_SB.PCI0.LPCB.LNKB, 0 }, + Package() { 0x001cffff, 2, \_SB.PCI0.LPCB.LNKC, 0 }, + Package() { 0x001cffff, 3, \_SB.PCI0.LPCB.LNKD, 0 }, + // EHCI 0:1d.0 + Package() { 0x001dffff, 0, \_SB.PCI0.LPCB.LNKD, 0 }, + // Audio DSP (Smart Sound) 0:13.0 + Package() { 0x0013ffff, 0, \_SB.PCI0.LPCB.LNKH, 0 }, + // XHCI 0:14.0 + Package() { 0x0014ffff, 0, \_SB.PCI0.LPCB.LNKC, 0 }, + // LPC device 0:1f.0 + Package() { 0x001fffff, 0, \_SB.PCI0.LPCB.LNKG, 0 }, + Package() { 0x001fffff, 1, \_SB.PCI0.LPCB.LNKC, 0 }, + Package() { 0x001fffff, 2, \_SB.PCI0.LPCB.LNKB, 0 }, + Package() { 0x001fffff, 3, \_SB.PCI0.LPCB.LNKA, 0 }, + // Serial IO 0:15.0 + Package() { 0x0015ffff, 0, \_SB.PCI0.LPCB.LNKE, 0 }, + Package() { 0x0015ffff, 1, \_SB.PCI0.LPCB.LNKF, 0 }, + Package() { 0x0015ffff, 2, \_SB.PCI0.LPCB.LNKF, 0 }, + Package() { 0x0015ffff, 3, \_SB.PCI0.LPCB.LNKF, 0 }, + // SDIO 0:17.0 + Package() { 0x0017ffff, 0, \_SB.PCI0.LPCB.LNKH, 0 }, + }) + } +} + diff --git a/src/soc/intel/broadwell/acpi/pcie.asl b/src/soc/intel/broadwell/acpi/pcie.asl new file mode 100644 index 0000000000..934cf782e9 --- /dev/null +++ b/src/soc/intel/broadwell/acpi/pcie.asl @@ -0,0 +1,218 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2007-2009 coresystems GmbH + * Copyright (C) 2012 The Chromium OS Authors. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +/* Intel 6/7 Series PCH PCIe support */ + +// PCI Express Ports + +Method (IRQM, 1, Serialized) { + + /* Interrupt Map INTA->INTA, INTB->INTB, INTC->INTC, INTD->INTD */ + Name (IQAA, Package() { + Package() { 0x0000ffff, 0, 0, 16 }, + Package() { 0x0000ffff, 1, 0, 17 }, + Package() { 0x0000ffff, 2, 0, 18 }, + Package() { 0x0000ffff, 3, 0, 19 } }) + Name (IQAP, Package() { + Package() { 0x0000ffff, 0, \_SB.PCI0.LPCB.LNKA, 0 }, + Package() { 0x0000ffff, 1, \_SB.PCI0.LPCB.LNKB, 0 }, + Package() { 0x0000ffff, 2, \_SB.PCI0.LPCB.LNKC, 0 }, + Package() { 0x0000ffff, 3, \_SB.PCI0.LPCB.LNKD, 0 } }) + + /* Interrupt Map INTA->INTB, INTB->INTC, INTC->INTD, INTD->INTA */ + Name (IQBA, Package() { + Package() { 0x0000ffff, 0, 0, 17 }, + Package() { 0x0000ffff, 1, 0, 18 }, + Package() { 0x0000ffff, 2, 0, 19 }, + Package() { 0x0000ffff, 3, 0, 16 } }) + Name (IQBP, Package() { + Package() { 0x0000ffff, 0, \_SB.PCI0.LPCB.LNKB, 0 }, + Package() { 0x0000ffff, 1, \_SB.PCI0.LPCB.LNKC, 0 }, + Package() { 0x0000ffff, 2, \_SB.PCI0.LPCB.LNKD, 0 }, + Package() { 0x0000ffff, 3, \_SB.PCI0.LPCB.LNKA, 0 } }) + + /* Interrupt Map INTA->INTC, INTB->INTD, INTC->INTA, INTD->INTB */ + Name (IQCA, Package() { + Package() { 0x0000ffff, 0, 0, 18 }, + Package() { 0x0000ffff, 1, 0, 19 }, + Package() { 0x0000ffff, 2, 0, 16 }, + Package() { 0x0000ffff, 3, 0, 17 } }) + Name (IQCP, Package() { + Package() { 0x0000ffff, 0, \_SB.PCI0.LPCB.LNKC, 0 }, + Package() { 0x0000ffff, 1, \_SB.PCI0.LPCB.LNKD, 0 }, + Package() { 0x0000ffff, 2, \_SB.PCI0.LPCB.LNKA, 0 }, + Package() { 0x0000ffff, 3, \_SB.PCI0.LPCB.LNKB, 0 } }) + + /* Interrupt Map INTA->INTD, INTB->INTA, INTC->INTB, INTD->INTC */ + Name (IQDA, Package() { + Package() { 0x0000ffff, 0, 0, 19 }, + Package() { 0x0000ffff, 1, 0, 16 }, + Package() { 0x0000ffff, 2, 0, 17 }, + Package() { 0x0000ffff, 3, 0, 18 } }) + Name (IQDP, Package() { + Package() { 0x0000ffff, 0, \_SB.PCI0.LPCB.LNKD, 0 }, + Package() { 0x0000ffff, 1, \_SB.PCI0.LPCB.LNKA, 0 }, + Package() { 0x0000ffff, 2, \_SB.PCI0.LPCB.LNKB, 0 }, + Package() { 0x0000ffff, 3, \_SB.PCI0.LPCB.LNKC, 0 } }) + + Switch (ToInteger (Arg0)) { + /* PCIe Root Port 1 and 5 */ + Case (Package() { 1, 5 }) { + If (PICM) { + Return (IQAA) + } Else { + Return (IQAP) + } + } + + /* PCIe Root Port 2 and 6 */ + Case (Package() { 2, 6 }) { + If (PICM) { + Return (IQBA) + } Else { + Return (IQBP) + } + } + + /* PCIe Root Port 3 and 7 */ + Case (Package() { 3, 7 }) { + If (PICM) { + Return (IQCA) + } Else { + Return (IQCP) + } + } + + /* PCIe Root Port 4 and 8 */ + Case (Package() { 4, 8 }) { + If (PICM) { + Return (IQDA) + } Else { + Return (IQDP) + } + } + + Default { + If (PICM) { + Return (IQDA) + } Else { + Return (IQDP) + } + } + } +} + +Device (RP01) +{ + Name (_ADR, 0x001c0000) + + #include "pcie_port.asl" + + Method (_PRT) + { + Return (IRQM (RPPN)) + } +} + +Device (RP02) +{ + Name (_ADR, 0x001c0001) + + #include "pcie_port.asl" + + Method (_PRT) + { + Return (IRQM (RPPN)) + } +} + +Device (RP03) +{ + Name (_ADR, 0x001c0002) + + #include "pcie_port.asl" + + Method (_PRT) + { + Return (IRQM (RPPN)) + } +} + +Device (RP04) +{ + Name (_ADR, 0x001c0003) + + #include "pcie_port.asl" + + Method (_PRT) + { + Return (IRQM (RPPN)) + } +} + +Device (RP05) +{ + Name (_ADR, 0x001c0004) + + #include "pcie_port.asl" + + Method (_PRT) + { + Return (IRQM (RPPN)) + } +} + +Device (RP06) +{ + Name (_ADR, 0x001c0005) + + #include "pcie_port.asl" + + Method (_PRT) + { + Return (IRQM (RPPN)) + } +} + +Device (RP07) +{ + Name (_ADR, 0x001c0006) + + #include "pcie_port.asl" + + Method (_PRT) + { + Return (IRQM (RPPN)) + } +} + +Device (RP08) +{ + Name (_ADR, 0x001c0007) + + #include "pcie_port.asl" + + Method (_PRT) + { + Return (IRQM (RPPN)) + } +} diff --git a/src/soc/intel/broadwell/acpi/pcie_port.asl b/src/soc/intel/broadwell/acpi/pcie_port.asl new file mode 100644 index 0000000000..fedd9c97c6 --- /dev/null +++ b/src/soc/intel/broadwell/acpi/pcie_port.asl @@ -0,0 +1,30 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 The Chromium OS Authors. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +/* Included in each PCIe Root Port device */ + +OperationRegion (RPCS, PCI_Config, 0x00, 0xFF) +Field (RPCS, AnyAcc, NoLock, Preserve) +{ + Offset (0x4c), // Link Capabilities + , 24, + RPPN, 8, // Root Port Number +} diff --git a/src/soc/intel/broadwell/acpi/sata.asl b/src/soc/intel/broadwell/acpi/sata.asl new file mode 100644 index 0000000000..e0c336ac5d --- /dev/null +++ b/src/soc/intel/broadwell/acpi/sata.asl @@ -0,0 +1,83 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2007-2009 coresystems GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +// Intel SATA Controller 0:1f.2 + +// Note: Some BIOSes put the S-ATA code into an SSDT to make it easily +// pluggable + +Device (SATA) +{ + Name (_ADR, 0x001f0002) + + Device (PRID) + { + Name (_ADR, 0) + + // Get Timing Mode + Method (_GTM) + { + Name(PBUF, Buffer(20) { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00 }) + + CreateDwordField (PBUF, 0, PIO0) + CreateDwordField (PBUF, 4, DMA0) + CreateDwordField (PBUF, 8, PIO1) + CreateDwordField (PBUF, 12, DMA1) + CreateDwordField (PBUF, 16, FLAG) + + // TODO fill return structure + + Return (PBUF) + } + + // Set Timing Mode + Method (_STM, 3) + { + CreateDwordField (Arg0, 0, PIO0) + CreateDwordField (Arg0, 4, DMA0) + CreateDwordField (Arg0, 8, PIO1) + CreateDwordField (Arg0, 12, DMA1) + CreateDwordField (Arg0, 16, FLAG) + + // TODO: Do the deed + } + + Device (DSK0) + { + Name (_ADR, 0) + // TODO: _RMV ? + // TODO: _GTF ? + } + + Device (DSK1) + { + Name (_ADR, 1) + + // TODO: _RMV ? + // TODO: _GTF ? + } + + } +} + diff --git a/src/soc/intel/broadwell/acpi/serialio.asl b/src/soc/intel/broadwell/acpi/serialio.asl new file mode 100644 index 0000000000..3e68c133a8 --- /dev/null +++ b/src/soc/intel/broadwell/acpi/serialio.asl @@ -0,0 +1,533 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +// Intel LynxPoint Serial IO Devices in ACPI Mode + +// Serial IO Device BAR0 and BAR1 is 4KB +#define SIO_BAR_LEN 0x1000 + +// This is defined in SSDT2 which is generated at boot based +// on whether or not the device is enabled in ACPI mode. +External(\S0EN) +External(\S1EN) +External(\S2EN) +External(\S3EN) +External(\S4EN) +External(\S5EN) +External(\S6EN) +External(\S7EN) + +// Serial IO Resource Consumption for BAR1 +Device (SIOR) +{ + Name (_HID, EISAID("PNP0C02")) + Name (_UID, 4) + + Name (RBUF, ResourceTemplate() + { + // Serial IO BAR1 (PCI config space) resources + Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, B1D0) // SDMA + Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, B1D1) // I2C0 + Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, B1D2) // I2C1 + Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, B1D3) // SPI0 + Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, B1D4) // SPI1 + Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, B1D5) // UART0 + Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, B1D6) // UART1 + Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, B1D7) // SDIO + }) + + // Update BAR1 address and length if set in NVS + Method (_CRS, 0, NotSerialized) + { + // SDMA + If (LNotEqual (\S0B1, Zero)) { + CreateDwordField (^RBUF, ^B1D0._BAS, B0AD) + CreateDwordField (^RBUF, ^B1D0._LEN, B0LN) + Store (\S0B1, B0AD) + Store (SIO_BAR_LEN, B0LN) + } + + // I2C0 + If (LNotEqual (\S1B1, Zero)) { + CreateDwordField (^RBUF, ^B1D1._BAS, B1AD) + CreateDwordField (^RBUF, ^B1D1._LEN, B1LN) + Store (\S1B1, B1AD) + Store (SIO_BAR_LEN, B1LN) + } + + // I2C1 + If (LNotEqual (\S2B1, Zero)) { + CreateDwordField (^RBUF, ^B1D2._BAS, B2AD) + CreateDwordField (^RBUF, ^B1D2._LEN, B2LN) + Store (\S2B1, B2AD) + Store (SIO_BAR_LEN, B2LN) + } + + // SPI0 + If (LNotEqual (\S3B1, Zero)) { + CreateDwordField (^RBUF, ^B1D3._BAS, B3AD) + CreateDwordField (^RBUF, ^B1D3._LEN, B3LN) + Store (\S3B1, B3AD) + Store (SIO_BAR_LEN, B3LN) + } + + // SPI1 + If (LNotEqual (\S4B1, Zero)) { + CreateDwordField (^RBUF, ^B1D4._BAS, B4AD) + CreateDwordField (^RBUF, ^B1D4._LEN, B4LN) + Store (\S4B1, B4AD) + Store (SIO_BAR_LEN, B4LN) + } + + // UART0 + If (LNotEqual (\S5B1, Zero)) { + CreateDwordField (^RBUF, ^B1D5._BAS, B5AD) + CreateDwordField (^RBUF, ^B1D5._LEN, B5LN) + Store (\S5B1, B5AD) + Store (SIO_BAR_LEN, B5LN) + } + + // UART1 + If (LNotEqual (\S6B1, Zero)) { + CreateDwordField (^RBUF, ^B1D6._BAS, B6AD) + CreateDwordField (^RBUF, ^B1D6._LEN, B6LN) + Store (\S6B1, B6AD) + Store (SIO_BAR_LEN, B6LN) + } + + // SDIO + If (LNotEqual (\S7B1, Zero)) { + CreateDwordField (^RBUF, ^B1D7._BAS, B7AD) + CreateDwordField (^RBUF, ^B1D7._LEN, B7LN) + Store (\S7B1, B7AD) + Store (SIO_BAR_LEN, B7LN) + } + + Return (RBUF) + } +} + +Device (SDMA) +{ + // Serial IO DMA Controller + Name (_HID, "INTL9C60") + Name (_UID, 1) + Name (_ADR, 0x00150000) + + // BAR0 is assigned during PCI enumeration and saved into NVS + Name (RBUF, ResourceTemplate () + { + Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, BAR0) + Interrupt (ResourceConsumer, Level, ActiveLow, Shared, , , ) {7} + }) + + Method (_CRS, 0, NotSerialized) + { + // Update BAR0 address and length if set in NVS + If (LNotEqual (\S0B0, Zero)) { + CreateDwordField (^RBUF, ^BAR0._BAS, B0AD) + CreateDwordField (^RBUF, ^BAR0._LEN, B0LN) + Store (\S0B0, B0AD) + Store (SIO_BAR_LEN, B0LN) + } + + Return (RBUF) + } + + Method (_STA, 0, NotSerialized) + { + If (LEqual (\S0EN, 0)) { + Return (0x0) + } Else { + Return (0xF) + } + } +} + +Device (I2C0) +{ + // Serial IO I2C0 Controller + Name (_HID, "INT33C2") + Name (_CID, "INT33C2") + Name (_UID, 1) + Name (_ADR, 0x00150001) + + // BAR0 is assigned during PCI enumeration and saved into NVS + Name (RBUF, ResourceTemplate () + { + Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, BAR0) + Interrupt (ResourceConsumer, Level, ActiveLow, Shared, , , ) {7} + }) + + // DMA channels are only used if Serial IO DMA controller is enabled + Name (DBUF, ResourceTemplate () + { + // TODO: Need to update IASL to support FixedDMA + //FixedDMA (0x18, 4, Width32Bit, DMA1) // Tx + //FixedDMA (0x19, 5, Width32Bit, DMA2) // Rx + }) + + Method (_CRS, 0, NotSerialized) + { + // Update BAR0 address and length if set in NVS + If (LNotEqual (\S1B0, Zero)) { + CreateDwordField (^RBUF, ^BAR0._BAS, B0AD) + CreateDwordField (^RBUF, ^BAR0._LEN, B0LN) + Store (\S1B0, B0AD) + Store (SIO_BAR_LEN, B0LN) + } + + // Check if Serial IO DMA Controller is enabled + If (LNotEqual (\_SB.PCI0.SDMA._STA, Zero)) { + Return (ConcatenateResTemplate (RBUF, DBUF)) + } Else { + Return (RBUF) + } + } + + Method (_STA, 0, NotSerialized) + { + If (LEqual (\S1EN, 0)) { + Return (0x0) + } Else { + Return (0xF) + } + } +} + +Device (I2C1) +{ + // Serial IO I2C1 Controller + Name (_HID, "INT33C3") + Name (_CID, "INT33C3") + Name (_UID, 1) + Name (_ADR, 0x00150002) + + // BAR0 is assigned during PCI enumeration and saved into NVS + Name (RBUF, ResourceTemplate () + { + Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, BAR0) + Interrupt (ResourceConsumer, Level, ActiveLow, Shared, , , ) {7} + }) + + // DMA channels are only used if Serial IO DMA controller is enabled + Name (DBUF, ResourceTemplate () + { + // TODO: Need to update IASL to support FixedDMA + //FixedDMA (0x1A, 6, Width32Bit, DMA1) // Tx + //FixedDMA (0x1B, 7, Width32Bit, DMA2) // Rx + }) + + Method (_CRS, 0, NotSerialized) + { + // Update BAR0 address and length if set in NVS + If (LNotEqual (\S2B0, Zero)) { + CreateDwordField (^RBUF, ^BAR0._BAS, B0AD) + CreateDwordField (^RBUF, ^BAR0._LEN, B0LN) + Store (\S2B0, B0AD) + Store (SIO_BAR_LEN, B0LN) + } + + // Check if Serial IO DMA Controller is enabled + If (LNotEqual (\_SB.PCI0.SDMA._STA, Zero)) { + Return (ConcatenateResTemplate (RBUF, DBUF)) + } Else { + Return (RBUF) + } + } + + Method (_STA, 0, NotSerialized) + { + If (LEqual (\S2EN, 0)) { + Return (0x0) + } Else { + Return (0xF) + } + } +} + +Device (SPI0) +{ + // Serial IO SPI0 Controller + Name (_HID, "INT33C0") + Name (_CID, "INT33C0") + Name (_UID, 1) + Name (_ADR, 0x00150003) + + // BAR0 is assigned during PCI enumeration and saved into NVS + Name (RBUF, ResourceTemplate () + { + Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, BAR0) + Interrupt (ResourceConsumer, Level, ActiveLow, Shared, , , ) {7} + }) + + Method (_CRS, 0, NotSerialized) + { + // Update BAR0 address and length if set in NVS + If (LNotEqual (\S3B0, Zero)) { + CreateDwordField (^RBUF, ^BAR0._BAS, B0AD) + CreateDwordField (^RBUF, ^BAR0._LEN, B0LN) + Store (\S3B0, B0AD) + Store (SIO_BAR_LEN, B0LN) + } + + Return (RBUF) + } + + Method (_STA, 0, NotSerialized) + { + If (LEqual (\S3EN, 0)) { + Return (0x0) + } Else { + Return (0xF) + } + } +} + +Device (SPI1) +{ + // Serial IO SPI1 Controller + Name (_HID, "INT33C1") + Name (_CID, "INT33C1") + Name (_UID, 1) + Name (_ADR, 0x00150004) + + // BAR0 is assigned during PCI enumeration and saved into NVS + Name (RBUF, ResourceTemplate () + { + Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, BAR0) + Interrupt (ResourceConsumer, Level, ActiveLow, Shared, , , ) {7} + }) + + // DMA channels are only used if Serial IO DMA controller is enabled + Name (DBUF, ResourceTemplate () + { + // TODO: Need to update IASL to support FixedDMA + //FixedDMA (0x10, 0, Width32Bit, DMA1) // Tx + //FixedDMA (0x11, 1, Width32Bit, DMA2) // Rx + }) + + Method (_CRS, 0, NotSerialized) + { + // Update BAR0 address and length if set in NVS + If (LNotEqual (\S4B0, Zero)) { + CreateDwordField (^RBUF, ^BAR0._BAS, B0AD) + CreateDwordField (^RBUF, ^BAR0._LEN, B0LN) + Store (\S4B0, B0AD) + Store (SIO_BAR_LEN, B0LN) + } + + // Check if Serial IO DMA Controller is enabled + If (LNotEqual (\_SB.PCI0.SDMA._STA, Zero)) { + Return (ConcatenateResTemplate (RBUF, DBUF)) + } Else { + Return (RBUF) + } + } + + Method (_STA, 0, NotSerialized) + { + If (LEqual (\S4EN, 0)) { + Return (0x0) + } Else { + Return (0xF) + } + } +} + +Device (UAR0) +{ + // Serial IO UART0 Controller + Name (_HID, "INT33C4") + Name (_CID, "INT33C4") + Name (_UID, 1) + Name (_ADR, 0x00150005) + + // BAR0 is assigned during PCI enumeration and saved into NVS + Name (RBUF, ResourceTemplate () + { + Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, BAR0) + Interrupt (ResourceConsumer, Level, ActiveLow, Shared, , , ) {13} + }) + + // DMA channels are only used if Serial IO DMA controller is enabled + Name (DBUF, ResourceTemplate () + { + // TODO: Need to update IASL to support FixedDMA + //FixedDMA (0x16, 2, Width32Bit, DMA1) // Tx + //FixedDMA (0x17, 3, Width32Bit, DMA2) // Rx + }) + + Method (_CRS, 0, NotSerialized) + { + // Update BAR0 address and length if set in NVS + If (LNotEqual (\S5B0, Zero)) { + CreateDwordField (^RBUF, ^BAR0._BAS, B0AD) + CreateDwordField (^RBUF, ^BAR0._LEN, B0LN) + Store (\S5B0, B0AD) + Store (SIO_BAR_LEN, B0LN) + } + + // Check if Serial IO DMA Controller is enabled + If (LNotEqual (\_SB.PCI0.SDMA._STA, Zero)) { + Return (ConcatenateResTemplate (RBUF, DBUF)) + } Else { + Return (RBUF) + } + } + + Method (_STA, 0, NotSerialized) + { + If (LEqual (\S5EN, 0)) { + Return (0x0) + } Else { + Return (0xF) + } + } +} + +Device (UAR1) +{ + // Serial IO UART1 Controller + Name (_HID, "INT33C5") + Name (_CID, "INT33C5") + Name (_UID, 1) + Name (_ADR, 0x00150006) + + // BAR0 is assigned during PCI enumeration and saved into NVS + Name (RBUF, ResourceTemplate () + { + Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, BAR0) + Interrupt (ResourceConsumer, Level, ActiveLow, Shared, , , ) {13} + }) + + Method (_CRS, 0, NotSerialized) + { + // Update BAR0 address and length if set in NVS + If (LNotEqual (\S6B0, Zero)) { + CreateDwordField (^RBUF, ^BAR0._BAS, B0AD) + CreateDwordField (^RBUF, ^BAR0._LEN, B0LN) + Store (\S6B0, B0AD) + Store (SIO_BAR_LEN, B0LN) + } + + Return (RBUF) + } + + Method (_STA, 0, NotSerialized) + { + If (LEqual (\S6EN, 0)) { + Return (0x0) + } Else { + Return (0xF) + } + } +} + +Device (SDIO) +{ + // Serial IO SDIO Controller + Name (_HID, "INT33C6") + Name (_CID, "PNP0D40") + Name (_UID, 1) + Name (_ADR, 0x00170000) + + // BAR0 is assigned during PCI enumeration and saved into NVS + Name (RBUF, ResourceTemplate () + { + Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, BAR0) + Interrupt (ResourceConsumer, Level, ActiveLow, Shared, , , ) {5} + }) + + Method (_CRS, 0, NotSerialized) + { + // Update BAR0 address and length if set in NVS + If (LNotEqual (\S7B0, Zero)) { + CreateDwordField (^RBUF, ^BAR0._BAS, B0AD) + CreateDwordField (^RBUF, ^BAR0._LEN, B0LN) + Store (\S7B0, B0AD) + Store (SIO_BAR_LEN, B0LN) + } + + Return (RBUF) + } + + Method (_STA, 0, NotSerialized) + { + If (LEqual (\S7EN, 0)) { + Return (0x0) + } Else { + Return (0xF) + } + } +} + +Device (GPIO) +{ + // GPIO Controller + Name (_HID, "INT33C7") + Name (_CID, "INT33C7") + Name (_UID, 1) + + Name (RBUF, ResourceTemplate() + { + DWordIo (ResourceProducer, + MinFixed, // IsMinFixed + MaxFixed, // IsMaxFixed + PosDecode, // Decode + EntireRange, // ISARanges + 0x00000000, // AddressGranularity + 0x00000000, // AddressMinimum + 0x00000000, // AddressMaximum + 0x00000000, // AddressTranslation + 0x00000000, // RangeLength + , // ResourceSourceIndex + , // ResourceSource + BAR0) + Interrupt (ResourceConsumer, + Level, ActiveHigh, Shared, , , ) {14} + }) + + Method (_CRS, 0, NotSerialized) + { + If (\ISLP ()) { + CreateDwordField (^RBUF, ^BAR0._MIN, BMIN) + CreateDwordField (^RBUF, ^BAR0._MAX, BMAX) + CreateDwordField (^RBUF, ^BAR0._LEN, BLEN) + + Store (DEFAULT_GPIOSIZE, BLEN) + Store (DEFAULT_GPIOBASE, BMIN) + Store (Subtract (Add (DEFAULT_GPIOBASE, + DEFAULT_GPIOSIZE), 1), BMAX) + } + + Return (RBUF) + } + + Method (_STA, 0, NotSerialized) + { + If (\ISLP ()) { + Return (0xF) + } Else { + Return (0x0) + } + } +} diff --git a/src/soc/intel/broadwell/acpi/sleepstates.asl b/src/soc/intel/broadwell/acpi/sleepstates.asl new file mode 100644 index 0000000000..06bfcb6a58 --- /dev/null +++ b/src/soc/intel/broadwell/acpi/sleepstates.asl @@ -0,0 +1,27 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2007-2009 coresystems GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +Name(\_S0, Package(){0x0,0x0,0x0,0x0}) +// Name(\_S1, Package(){0x1,0x1,0x0,0x0}) +Name(\_S3, Package(){0x5,0x5,0x0,0x0}) +Name(\_S4, Package(){0x6,0x6,0x0,0x0}) +Name(\_S5, Package(){0x7,0x7,0x0,0x0}) + diff --git a/src/soc/intel/broadwell/acpi/smbus.asl b/src/soc/intel/broadwell/acpi/smbus.asl new file mode 100644 index 0000000000..4409308325 --- /dev/null +++ b/src/soc/intel/broadwell/acpi/smbus.asl @@ -0,0 +1,242 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2007-2009 coresystems GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +// Intel SMBus Controller 0:1f.3 + +Device (SBUS) +{ + Name (_ADR, 0x001f0003) + +#ifdef ENABLE_SMBUS_METHODS + OperationRegion (SMBP, PCI_Config, 0x00, 0x100) + Field(SMBP, DWordAcc, NoLock, Preserve) + { + Offset(0x40), + , 2, + I2CE, 1 + } + + OperationRegion (SMBI, SystemIO, SMBUS_IO_BASE, 0x20) + Field (SMBI, ByteAcc, NoLock, Preserve) + { + HSTS, 8, // Host Status + , 8, + HCNT, 8, // Host Control + HCMD, 8, // Host Command + TXSA, 8, // Transmit Slave Address + DAT0, 8, // Host Data 0 + DAT1, 8, // Host Data 1 + HBDB, 8, // Host Block Data Byte + PECK, 8, // Packet Error Check + RXSA, 8, // Receive Slave Address + RXDA, 16, // Receive Slave Data + AUXS, 8, // Auxiliary Status + AUXC, 8, // Auxiliary Control + SLPC, 8, // SMLink Pin Control + SBPC, 8, // SMBus Pin Control + SSTS, 8, // Slave Status + SCMD, 8, // Slave Command + NADR, 8, // Notify Device Address + NDLB, 8, // Notify Data Low Byte + NDLH, 8, // Notify Data High Byte + } + + // Kill all SMBus communication + Method (KILL, 0, Serialized) + { + Or (HCNT, 0x02, HCNT) // Send Kill + Or (HSTS, 0xff, HSTS) // Clean Status + } + + // Check if last operation completed + // return Failure = 0, Success = 1 + Method (CMPL, 0, Serialized) + { + Store (4000, Local0) // Timeout 200ms in 50us steps + While (Local0) { + If (And(HSTS, 0x02)) { // Completion Status? + Return (1) // Operation Completed + } Else { + Stall (50) + Decrement (Local0) + If (LEqual(Local0, 0)) { + KILL() + } + } + } + + Return (0) // Failure + } + + + // Wait for SMBus to become ready + Method (SRDY, 0, Serialized) + { + Store (200, Local0) // Timeout 200ms + While (Local0) { + If (And(HSTS, 0x40)) { // IN_USE? + Sleep(1) // Wait 1ms + Decrement(Local0) // timeout-- + If (LEqual(Local0, 0)) { + Return (1) + } + } Else { + Store (0, Local0) // We're ready + } + } + + Store (4000, Local0) // Timeout 200ms (50us * 4000) + While (Local0) { + If (And (HSTS, 0x01)) { // Host Busy? + Stall(50) // Wait 50us + Decrement(Local0) // timeout-- + If (LEqual(Local0, 0)) { + KILL() + } + } Else { + Return (0) // Success + } + } + + Return (1) // Failure + } + + // SMBus Send Byte + // Arg0: Address + // Arg1: Data + // Return: 1 = Success, 0=Failure + + Method (SSXB, 2, Serialized) + { + + // Is the SMBus Controller Ready? + If (SRDY()) { + Return (0) + } + + // Send Byte + Store (0, I2CE) // SMBus Enable + Store (0xbf, HSTS) + Store (Arg0, TXSA) // Write Address + Store (Arg1, HCMD) // Write Data + + Store (0x48, HCNT) // Start + Byte Data Protocol + + If (CMPL()) { + Or (HSTS, 0xff, HSTS) // Clean up + Return (1) // Success + } + + Return (0) + } + + + // SMBus Receive Byte + // Arg0: Address + // Return: 0xffff = Failure, Data (8bit) = Success + + Method (SRXB, 2, Serialized) + { + + // Is the SMBus Controller Ready? + If (SRDY()) { + Return (0xffff) + } + + // Receive Byte + Store (0, I2CE) // SMBus Enable + Store (0xbf, HSTS) + Store (Or (Arg0, 1), TXSA) // Write Address + + Store (0x44, HCNT) // Start + + If (CMPL()) { + Or (HSTS, 0xff, HSTS) // Clean up + Return (DAT0) // Success + } + + Return (0xffff) + } + + + // SMBus Write Byte + // Arg0: Address + // Arg1: Command + // Arg2: Data + // Return: 1 = Success, 0=Failure + + Method (SWRB, 3, Serialized) + { + + // Is the SMBus Controller Ready? + If (SRDY()) { + Return (0) + } + + // Send Byte + Store (0, I2CE) // SMBus Enable + Store (0xbf, HSTS) + Store (Arg0, TXSA) // Write Address + Store (Arg1, HCMD) // Write Command + Store (Arg2, DAT0) // Write Data + + Store (0x48, HCNT) // Start + Byte Protocol + + If (CMPL()) { + Or (HSTS, 0xff, HSTS) // Clean up + Return (1) // Success + } + + Return (0) + } + + + // SMBus Read Byte + // Arg0: Address + // Arg1: Command + // Return: 0xffff = Failure, Data (8bit) = Success + + Method (SRDB, 2, Serialized) + { + + // Is the SMBus Controller Ready? + If (SRDY()) { + Return (0xffff) + } + + // Receive Byte + Store (0, I2CE) // SMBus Enable + Store (0xbf, HSTS) + Store (Or (Arg0, 1), TXSA) // Write Address + Store (Arg1, HCMD) // Command + + Store (0x48, HCNT) // Start + + If (CMPL()) { + Or (HSTS, 0xff, HSTS) // Clean up + Return (DAT0) // Success + } + + Return (0xffff) + } +#endif +} + diff --git a/src/soc/intel/broadwell/acpi/systemagent.asl b/src/soc/intel/broadwell/acpi/systemagent.asl new file mode 100644 index 0000000000..30ee3e548e --- /dev/null +++ b/src/soc/intel/broadwell/acpi/systemagent.asl @@ -0,0 +1,467 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2007-2009 coresystems GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + + +Name(_HID,EISAID("PNP0A08")) // PCIe +Name(_CID,EISAID("PNP0A03")) // PCI + +Name(_ADR, 0) +Name(_BBN, 0) + +Device (MCHC) +{ + Name(_ADR, 0x00000000) // 0:0.0 + + OperationRegion(MCHP, PCI_Config, 0x00, 0x100) + Field (MCHP, DWordAcc, NoLock, Preserve) + { + Offset (0x40), // EPBAR + EPEN, 1, // Enable + , 11, // + EPBR, 24, // EPBAR + + Offset (0x48), // MCHBAR + MHEN, 1, // Enable + , 13, // + MHBR, 22, // MCHBAR + + Offset (0x60), // PCIe BAR + PXEN, 1, // Enable + PXSZ, 2, // BAR size + , 23, // + PXBR, 10, // PCIe BAR + + Offset (0x68), // DMIBAR + DMEN, 1, // Enable + , 11, // + DMBR, 24, // DMIBAR + + Offset (0x70), // ME Base Address + MEBA, 64, + + // ... + + Offset (0x80), // PAM0 + , 4, + PM0H, 2, + , 2, + Offset (0x81), // PAM1 + PM1L, 2, + , 2, + PM1H, 2, + , 2, + Offset (0x82), // PAM2 + PM2L, 2, + , 2, + PM2H, 2, + , 2, + Offset (0x83), // PAM3 + PM3L, 2, + , 2, + PM3H, 2, + , 2, + Offset (0x84), // PAM4 + PM4L, 2, + , 2, + PM4H, 2, + , 2, + Offset (0x85), // PAM5 + PM5L, 2, + , 2, + PM5H, 2, + , 2, + Offset (0x86), // PAM6 + PM6L, 2, + , 2, + PM6H, 2, + , 2, + + Offset (0xa0), // Top of Used Memory + TOM, 64, + + Offset (0xbc), // Top of Low Used Memory + TLUD, 32, + } + + Mutex (CTCM, 1) /* CTDP Switch Mutex (sync level 1) */ + Name (CTCC, 0) /* CTDP Current Selection */ + Name (CTCN, 0) /* CTDP Nominal Select */ + Name (CTCD, 1) /* CTDP Down Select */ + Name (CTCU, 2) /* CTDP Up Select */ + Name (SPL1, 0) /* Saved PL1 value */ + + OperationRegion (MCHB, SystemMemory, Add(DEFAULT_MCHBAR,0x5000), 0x1000) + Field (MCHB, DWordAcc, Lock, Preserve) + { + Offset (0x930), /* PACKAGE_POWER_SKU */ + CTDN, 15, /* CTDP Nominal PL1 */ + Offset (0x938), /* PACKAGE_POWER_SKU_UNIT */ + PUNI, 4, /* Power Units */ + , 4, + EUNI, 5, /* Energy Units */ + , 3, + TUNI, 4, /* Time Units */ + Offset (0x958), /* PLATFORM_INFO */ + , 40, + LFM_, 8, /* Maximum Efficiency Ratio (LFM) */ + Offset (0x9a0), /* TURBO_POWER_LIMIT1 */ + PL1V, 15, /* Power Limit 1 Value */ + PL1E, 1, /* Power Limit 1 Enable */ + PL1C, 1, /* Power Limit 1 Clamp */ + PL1T, 7, /* Power Limit 1 Time */ + Offset (0x9a4), /* TURBO_POWER_LIMIT2 */ + PL2V, 15, /* Power Limit 2 Value */ + PL2E, 1, /* Power Limit 2 Enable */ + PL2C, 1, /* Power Limit 2 Clamp */ + PL2T, 7, /* Power Limit 2 Time */ + Offset (0xf3c), /* CONFIG_TDP_NOMINAL */ + TARN, 8, /* CTDP Nominal Turbo Activation Ratio */ + Offset (0xf40), /* CONFIG_TDP_LEVEL1 */ + CTDD, 15, /* CTDP Down PL1 */ + , 1, + TARD, 8, /* CTDP Down Turbo Activation Ratio */ + Offset (0xf48), /* MSR_CONFIG_TDP_LEVEL2 */ + CTDU, 15, /* CTDP Up PL1 */ + , 1, + TARU, 8, /* CTDP Up Turbo Activation Ratio */ + Offset (0xf50), /* CONFIG_TDP_CONTROL */ + CTCS, 2, /* CTDP Select */ + Offset (0xf54), /* TURBO_ACTIVATION_RATIO */ + TARS, 8, /* Turbo Activation Ratio Select */ + } + + /* + * Search CPU0 _PSS looking for control=arg0 and then + * return previous P-state entry number for new _PPC + * + * Format of _PSS: + * Name (_PSS, Package () { + * Package (6) { freq, power, tlat, blat, control, status } + * } + */ + External (\_PR.CPU0._PSS) + Method (PSSS, 1, NotSerialized) + { + Store (One, Local0) /* Start at P1 */ + Store (SizeOf (\_PR.CPU0._PSS), Local1) + + While (LLess (Local0, Local1)) { + /* Store _PSS entry Control value to Local2 */ + ShiftRight (DeRefOf (Index (DeRefOf (Index + (\_PR.CPU0._PSS, Local0)), 4)), 8, Local2) + If (LEqual (Local2, Arg0)) { + Return (Subtract (Local0, 1)) + } + Increment (Local0) + } + + Return (0) + } + + /* Calculate PL2 based on chip type */ + Method (CPL2, 1, NotSerialized) + { + If (\ISLP ()) { + /* Haswell ULT PL2 = 25W */ + Return (Multiply (25, 8)) + } Else { + /* Haswell Mobile PL2 = 1.25 * PL1 */ + Return (Divide (Multiply (Arg0, 125), 100)) + } + } + + /* Set Config TDP Down */ + Method (STND, 0, Serialized) + { + If (Acquire (CTCM, 100)) { + Return (0) + } + If (LEqual (CTCD, CTCC)) { + Release (CTCM) + Return (0) + } + + Store ("Set TDP Down", Debug) + + /* Set CTC */ + Store (CTCD, CTCS) + + /* Set TAR */ + Store (TARD, TARS) + + /* Set PPC limit and notify OS */ + Store (PSSS (TARD), PPCM) + PPCN () + + /* Set PL2 */ + Store (CPL2 (CTDD), PL2V) + + /* Set PL1 */ + Store (CTDD, PL1V) + + /* Store the new TDP Down setting */ + Store (CTCD, CTCC) + + Release (CTCM) + Return (1) + } + + /* Set Config TDP Nominal from Down */ + Method (STDN, 0, Serialized) + { + If (Acquire (CTCM, 100)) { + Return (0) + } + If (LEqual (CTCN, CTCC)) { + Release (CTCM) + Return (0) + } + + Store ("Set TDP Nominal", Debug) + + /* Set PL1 */ + Store (CTDN, PL1V) + + /* Set PL2 */ + Store (CPL2 (CTDN), PL2V) + + /* Set PPC limit and notify OS */ + Store (PSSS (TARN), PPCM) + PPCN () + + /* Set TAR */ + Store (TARN, TARS) + + /* Set CTC */ + Store (CTCN, CTCS) + + /* Store the new TDP Nominal setting */ + Store (CTCN, CTCC) + + Release (CTCM) + Return (1) + } + + /* Calculate PL1 value based on requested TDP */ + Method (TDPP, 1, NotSerialized) + { + Return (Multiply (ShiftLeft (Subtract (PUNI, 1), 2), Arg0)) + } + + /* Enable Controllable TDP to limit PL1 to requested value */ + Method (CTLE, 1, Serialized) + { + If (Acquire (CTCM, 100)) { + Return (0) + } + + Store ("Enable PL1 Limit", Debug) + + /* Set _PPC to LFM */ + Store (PSSS (LFM_), Local0) + Add (Local0, 1, PPCM) + \PPCN () + + /* Set TAR to LFM-1 */ + Subtract (LFM_, 1, TARS) + + /* Set PL1 to desired value */ + Store (PL1V, SPL1) + Store (TDPP (Arg0), PL1V) + + /* Set PL1 CLAMP bit */ + Store (One, PL1C) + + Release (CTCM) + Return (1) + } + + /* Disable Controllable TDP */ + Method (CTLD, 0, Serialized) + { + If (Acquire (CTCM, 100)) { + Return (0) + } + + Store ("Disable PL1 Limit", Debug) + + /* Clear PL1 CLAMP bit */ + Store (Zero, PL1C) + + /* Set PL1 to normal value */ + Store (SPL1, PL1V) + + /* Set TAR to 0 */ + Store (Zero, TARS) + + /* Set _PPC to 0 */ + Store (Zero, PPCM) + \PPCN () + + Release (CTCM) + Return (1) + } +} + +// Current Resource Settings + +Method (_CRS, 0, Serialized) +{ + Name (MCRS, ResourceTemplate() + { + // Bus Numbers + WordBusNumber (ResourceProducer, MinFixed, MaxFixed, PosDecode, + 0x0000, 0x0000, 0x00ff, 0x0000, 0x0100,,, PB00) + + // IO Region 0 + DWordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, + 0x0000, 0x0000, 0x0cf7, 0x0000, 0x0cf8,,, PI00) + + // PCI Config Space + Io (Decode16, 0x0cf8, 0x0cf8, 0x0001, 0x0008) + + // IO Region 1 + DWordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, + 0x0000, 0x0d00, 0xffff, 0x0000, 0xf300,,, PI01) + + // VGA memory (0xa0000-0xbffff) + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0x000a0000, 0x000bffff, 0x00000000, + 0x00020000,,, ASEG) + + // OPROM reserved (0xc0000-0xc3fff) + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0x000c0000, 0x000c3fff, 0x00000000, + 0x00004000,,, OPR0) + + // OPROM reserved (0xc4000-0xc7fff) + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0x000c4000, 0x000c7fff, 0x00000000, + 0x00004000,,, OPR1) + + // OPROM reserved (0xc8000-0xcbfff) + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0x000c8000, 0x000cbfff, 0x00000000, + 0x00004000,,, OPR2) + + // OPROM reserved (0xcc000-0xcffff) + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0x000cc000, 0x000cffff, 0x00000000, + 0x00004000,,, OPR3) + + // OPROM reserved (0xd0000-0xd3fff) + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0x000d0000, 0x000d3fff, 0x00000000, + 0x00004000,,, OPR4) + + // OPROM reserved (0xd4000-0xd7fff) + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0x000d4000, 0x000d7fff, 0x00000000, + 0x00004000,,, OPR5) + + // OPROM reserved (0xd8000-0xdbfff) + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0x000d8000, 0x000dbfff, 0x00000000, + 0x00004000,,, OPR6) + + // OPROM reserved (0xdc000-0xdffff) + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0x000dc000, 0x000dffff, 0x00000000, + 0x00004000,,, OPR7) + + // BIOS Extension (0xe0000-0xe3fff) + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0x000e0000, 0x000e3fff, 0x00000000, + 0x00004000,,, ESG0) + + // BIOS Extension (0xe4000-0xe7fff) + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0x000e4000, 0x000e7fff, 0x00000000, + 0x00004000,,, ESG1) + + // BIOS Extension (0xe8000-0xebfff) + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0x000e8000, 0x000ebfff, 0x00000000, + 0x00004000,,, ESG2) + + // BIOS Extension (0xec000-0xeffff) + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0x000ec000, 0x000effff, 0x00000000, + 0x00004000,,, ESG3) + + // System BIOS (0xf0000-0xfffff) + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0x000f0000, 0x000fffff, 0x00000000, + 0x00010000,,, FSEG) + + // PCI Memory Region (Top of memory-0xfebfffff) + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0x00000000, 0xfebfffff, 0x00000000, + 0xfec00000,,, PM01) + + // TPM Area (0xfed40000-0xfed44fff) + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0xfed40000, 0xfed44fff, 0x00000000, + 0x00005000,,, TPMR) + }) + + // Find PCI resource area in MCRS + CreateDwordField(MCRS, PM01._MIN, PMIN) + CreateDwordField(MCRS, PM01._MAX, PMAX) + CreateDwordField(MCRS, PM01._LEN, PLEN) + + // Fix up PCI memory region + // Start with Top of Lower Usable DRAM + Store (^MCHC.TLUD, Local0) + Store (^MCHC.MEBA, Local1) + + // Check if ME base is equal + If (LEqual (Local0, Local1)) { + // Use Top Of Memory instead + Store (^MCHC.TOM, Local0) + } + + Store (Local0, PMIN) + Add(Subtract(PMAX, PMIN), 1, PLEN) + + Return (MCRS) +} + +/* IRQ assignment is mainboard specific. Get it from mainboard ACPI code */ +#include "acpi/haswell_pci_irqs.asl" + + diff --git a/src/soc/intel/broadwell/acpi/xhci.asl b/src/soc/intel/broadwell/acpi/xhci.asl new file mode 100644 index 0000000000..9c9b6b0b65 --- /dev/null +++ b/src/soc/intel/broadwell/acpi/xhci.asl @@ -0,0 +1,410 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2007-2009 coresystems GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +/* Intel Cougar Point USB support */ + +// EHCI Controller 0:1d.0 + +Device (EHCI) +{ + Name(_ADR, 0x001d0000) + + Name (PRWH, Package(){ 0x0d, 3 }) // LPT-H + Name (PRWL, Package(){ 0x6d, 3 }) // LPT-LP + + Method (_PRW, 0) { // Power Resources for Wake + If (\ISLP ()) { + Return (PRWL) + } Else { + Return (PRWH) + } + } + + // Leave USB ports on for to allow Wake from USB + + Method(_S3D,0) // Highest D State in S3 State + { + Return (2) + } + + Method(_S4D,0) // Highest D State in S4 State + { + Return (2) + } + + Device (HUB7) + { + Name (_ADR, 0x00000000) + + // How many are there? + Device (PRT1) { Name (_ADR, 1) } // USB Port 0 + Device (PRT2) { Name (_ADR, 2) } // USB Port 1 + Device (PRT3) { Name (_ADR, 3) } // USB Port 2 + Device (PRT4) { Name (_ADR, 4) } // USB Port 3 + Device (PRT5) { Name (_ADR, 5) } // USB Port 4 + Device (PRT6) { Name (_ADR, 6) } // USB Port 5 + } +} + +// XHCI Controller 0:14.0 + +Device (XHCI) +{ + Name (_ADR, 0x00140000) + + Name (PLSD, 5) // Port Link State - RxDetect + Name (PLSP, 7) // Port Link State - Polling + + OperationRegion (XPRT, PCI_Config, 0x00, 0x100) + Field (XPRT, AnyAcc, NoLock, Preserve) + { + Offset (0x0), + DVID, 16, + Offset (0x10), + , 16, + XMEM, 16, // MEM_BASE + Offset (0x74), + D0D3, 2, + , 6, + PMEE, 1, // PME_EN + , 6, + PMES, 1, // PME_STS + Offset (0xb0), + , 13, + MB13, 1, + MB14, 1, + Offset (0xd0), + PR2R, 32, // USB2PR + PR2M, 32, // USB2PRM + PR3R, 32, // USB3PR + PR3M, 32, // USB3PRM + } + + // Clear status bits + Method (LPCL, 0, Serialized) + { + OperationRegion (XREG, SystemMemory, + ShiftLeft (^XMEM, 16), 0x600) + Field (XREG, DWordAcc, Lock, Preserve) + { + Offset (0x510), // PORTSCNUSB3[0] + PSC0, 32, + Offset (0x520), // PORTSCNUSB3[1] + PSC1, 32, + Offset (0x530), // PORTSCNUSB3[2] + PSC2, 32, + Offset (0x540), // PORTSCNUSB3[3] + PSC3, 32, + } + + // Port Enabled/Disabled (Bit 1) + Name (PEDB, ShiftLeft (1, 1)) + + // Change Status (Bits 23:17) + Name (CHST, ShiftLeft (0x7f, 17)) + + // Port 0 + And (PSC0, Not (PEDB), Local0) + Or (Local0, CHST, PSC0) + + // Port 1 + And (PSC1, Not (PEDB), Local0) + Or (Local0, CHST, PSC1) + + // Port 2 + And (PSC2, Not (PEDB), Local0) + Or (Local0, CHST, PSC2) + + // Port 3 + And (PSC3, Not (PEDB), Local0) + Or (Local0, CHST, PSC3) + } + + Method (LPS0, 0, Serialized) + { + OperationRegion (XREG, SystemMemory, + ShiftLeft (^XMEM, 16), 0x600) + Field (XREG, DWordAcc, Lock, Preserve) + { + Offset (0x510), // PORTSCNUSB3 + , 5, + PLS1, 4, // [8:5] Port Link State + PPR1, 1, // [9] Port Power + , 7, + CSC1, 1, // [17] Connect Status Change + , 1, + WRC1, 1, // [19] Warm Port Reset Change + , 11, + WPR1, 1, // [31] Warm Port Reset + Offset (0x520), // PORTSCNUSB3 + , 5, + PLS2, 4, // [8:5] Port Link State + PPR2, 1, // [9] Port Power + , 7, + CSC2, 1, // [17] Connect Status Change + , 1, + WRC2, 1, // [19] Warm Port Reset Change + , 11, + WPR2, 1, // [31] Warm Port Reset + Offset (0x530), // PORTSCNUSB3 + , 5, + PLS3, 4, // [8:5] Port Link State + PPR3, 1, // [9] Port Power + , 7, + CSC3, 1, // [17] Connect Status Change + , 1, + WRC3, 1, // [19] Warm Port Reset Change + , 11, + WPR3, 1, // [31] Warm Port Reset + Offset (0x540), // PORTSCNUSB3 + , 5, + PLS4, 4, // [8:5] Port Link State + PPR4, 1, // [9] Port Power + , 7, + CSC4, 1, // [17] Connect Status Change + , 1, + WRC4, 1, // [19] Warm Port Reset Change + , 11, + WPR4, 1, // [31] Warm Port Reset + } + + // Wait for all powered ports to finish polling + Store (10, Local0) + While (LOr (LOr (LAnd (LEqual (PPR1, 1), LEqual (PLS1, PLSP)), + LAnd (LEqual (PPR2, 1), LEqual (PLS2, PLSP))), + LOr (LAnd (LEqual (PPR3, 1), LEqual (PLS3, PLSP)), + LAnd (LEqual (PPR4, 1), LEqual (PLS4, PLSP))))) + { + If (LEqual (Local0, 0)) { + Break + } + Decrement (Local0) + Stall (10) + } + + // For each USB3 Port: + // If port is disconnected (PLS=5 PP=1 CSC=0) + // 1) Issue warm reset (WPR=1) + // 2) Poll for warm reset complete (WRC=0) + // 3) Write 1 to port status to clear + + // Local# indicate if port is reset + Store (0, Local1) + Store (0, Local2) + Store (0, Local3) + Store (0, Local4) + + If (LAnd (LEqual (PLS1, PLSD), + LAnd (LEqual (CSC1, 0), LEqual (PPR1, 1)))) { + Store (1, WPR1) // Issue warm reset + Store (1, Local1) + } + If (LAnd (LEqual (PLS2, PLSD), + LAnd (LEqual (CSC2, 0), LEqual (PPR2, 1)))) { + Store (1, WPR2) // Issue warm reset + Store (1, Local2) + } + If (LAnd (LEqual (PLS3, PLSD), + LAnd (LEqual (CSC3, 0), LEqual (PPR3, 1)))) { + Store (1, WPR3) // Issue warm reset + Store (1, Local3) + } + If (LAnd (LEqual (PLS4, PLSD), + LAnd (LEqual (CSC4, 0), LEqual (PPR4, 1)))) { + Store (1, WPR4) // Issue warm reset + Store (1, Local4) + } + + // Poll for warm reset complete on all ports that were reset + Store (10, Local0) + While (LOr (LOr (LAnd (LEqual (Local1, 1), LEqual (WRC1, 0)), + LAnd (LEqual (Local2, 1), LEqual (WRC2, 0))), + LOr (LAnd (LEqual (Local3, 1), LEqual (WRC3, 0)), + LAnd (LEqual (Local4, 1), LEqual (WRC4, 0))))) + { + If (LEqual (Local0, 0)) { + Break + } + Decrement (Local0) + Stall (10) + } + + // Clear status bits in all ports + LPCL () + } + + Method (_PSC, 0, NotSerialized) + { + Return (^D0D3) + } + + Method (_PS0, 0, Serialized) + { + If (LEqual (^DVID, 0xFFFF)) { + Return () + } + If (LOr (LEqual (^XMEM, 0xFFFF), LEqual (^XMEM, 0x0000))) { + Return () + } + + OperationRegion (XREG, SystemMemory, + Add (ShiftLeft (^XMEM, 16), 0x8000), 0x200) + Field (XREG, DWordAcc, Lock, Preserve) + { + Offset (0x0e0), // AUX Reset Control 1 + , 15, + AX15, 1, + Offset (0x154), // AUX Domain PM Control Register 2 + , 31, + CLK2, 1, + Offset (0x16c), // AUX Clock Control + , 2, + CLK0, 1, + , 11, + CLK1, 1, // USB3 Port Aux/Core Clock Gating Enable + } + + // If device is in D3, set back to D0 + Store (^D0D3, Local0) + if (LEqual (Local0, 3)) { + Store (0, ^D0D3) + } + + If (\ISLP ()) { + // Clear PCI 0xB0[14:13] + Store (0, ^MB13) + Store (0, ^MB14) + + // Clear MMIO 0x816C[14,2] + Store (0, CLK0) + Store (0, CLK1) + } + + // Set MMIO 0x8154[31] + Store (1, CLK2) + + If (\ISLP ()) { + // Handle per-port reset if needed + LPS0 () + + // Set MMIO 0x80e0[15] + Store (1, AX15) + } + + Return () + } + + Method (_PS3, 0, Serialized) + { + If (LEqual (^DVID, 0xFFFF)) { + Return () + } + If (LOr (LEqual (^XMEM, 0xFFFF), LEqual (^XMEM, 0x0000))) { + Return () + } + + OperationRegion (XREG, SystemMemory, + Add (ShiftLeft (^XMEM, 16), 0x8000), 0x200) + Field (XREG, DWordAcc, Lock, Preserve) + { + Offset (0x0e0), // AUX Reset Control 1 + , 15, + AX15, 1, + Offset (0x154), // AUX Domain PM Control Register 2 + , 31, + CLK2, 1, + Offset (0x16c), // AUX Clock Control + , 2, + CLK0, 1, + , 11, + CLK1, 1, // USB3 Port Aux/Core Clock Gating Enable + } + + Store (1, ^PMES) // Clear PME Status + Store (1, ^PMEE) // Enable PME + + // If device is in D3, set back to D0 + Store (^D0D3, Local0) + if (LEqual (Local0, 3)) { + Store (0, ^D0D3) + } + + If (\ISLP ()) { + // Set PCI 0xB0[14:13] + Store (1, ^MB13) + Store (1, ^MB14) + + // Set MMIO 0x816C[14,2] + Store (1, CLK0) + Store (1, CLK1) + } + + // Clear MMIO 0x8154[31] + Store (0, CLK2) + + If (\ISLP ()) { + // Clear MMIO 0x80e0[15] + Store (0, AX15) + } + + // Put device in D3 + Store (3, ^D0D3) + + Return () + } + + Name (PRWH, Package(){ 0x0d, 3 }) // LPT-H + Name (PRWL, Package(){ 0x6d, 3 }) // LPT-LP + + Method (_PRW, 0) { // Power Resources for Wake + If (\ISLP ()) { + Return (PRWL) + } Else { + Return (PRWH) + } + } + + // Leave USB ports on for to allow Wake from USB + + Method(_S3D,0) // Highest D State in S3 State + { + Return (3) + } + + Method(_S4D,0) // Highest D State in S4 State + { + Return (3) + } + + Device (HUB7) + { + Name (_ADR, 0x00000000) + + // How many are there? + Device (PRT1) { Name (_ADR, 1) } // USB Port 0 + Device (PRT2) { Name (_ADR, 2) } // USB Port 1 + Device (PRT3) { Name (_ADR, 3) } // USB Port 2 + Device (PRT4) { Name (_ADR, 4) } // USB Port 3 + Device (PRT5) { Name (_ADR, 5) } // USB Port 4 + Device (PRT6) { Name (_ADR, 6) } // USB Port 5 + } +} + diff --git a/src/soc/intel/broadwell/bootblock/cpu.c b/src/soc/intel/broadwell/bootblock/cpu.c new file mode 100644 index 0000000000..e502cfa392 --- /dev/null +++ b/src/soc/intel/broadwell/bootblock/cpu.c @@ -0,0 +1,139 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include + +#include +#include "haswell.h" + +#if CONFIG_SOUTHBRIDGE_INTEL_LYNXPOINT +/* Needed for RCBA access to set Soft Reset Data register */ +#include +#else +#error "CPU must be paired with Intel LynxPoint southbridge" +#endif + +static void set_var_mtrr( + unsigned reg, unsigned base, unsigned size, unsigned type) + +{ + /* Bit Bit 32-35 of MTRRphysMask should be set to 1 */ + /* FIXME: It only support 4G less range */ + msr_t basem, maskm; + basem.lo = base | type; + basem.hi = 0; + wrmsr(MTRRphysBase_MSR(reg), basem); + maskm.lo = ~(size - 1) | MTRRphysMaskValid; + maskm.hi = (1 << (CONFIG_CPU_ADDR_BITS - 32)) - 1; + wrmsr(MTRRphysMask_MSR(reg), maskm); +} + +static void enable_rom_caching(void) +{ + msr_t msr; + + disable_cache(); + /* Why only top 4MiB ? */ + set_var_mtrr(1, 0xffc00000, 4*1024*1024, MTRR_TYPE_WRPROT); + enable_cache(); + + /* Enable Variable MTRRs */ + msr.hi = 0x00000000; + msr.lo = 0x00000800; + wrmsr(MTRRdefType_MSR, msr); +} + +static void set_flex_ratio_to_tdp_nominal(void) +{ + msr_t flex_ratio, msr; + u32 soft_reset; + u8 nominal_ratio; + + /* Check for Flex Ratio support */ + flex_ratio = rdmsr(MSR_FLEX_RATIO); + if (!(flex_ratio.lo & FLEX_RATIO_EN)) + return; + + /* Check for >0 configurable TDPs */ + msr = rdmsr(MSR_PLATFORM_INFO); + if (((msr.hi >> 1) & 3) == 0) + return; + + /* Use nominal TDP ratio for flex ratio */ + msr = rdmsr(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; + + /* Set flex ratio to nominal TDP ratio */ + flex_ratio.lo &= ~0xff00; + flex_ratio.lo |= nominal_ratio << 8; + flex_ratio.lo |= FLEX_RATIO_LOCK; + wrmsr(MSR_FLEX_RATIO, flex_ratio); + + /* Set flex ratio in soft reset data register bits 11:6. + * RCBA region is enabled in southbridge bootblock */ + soft_reset = RCBA32(SOFT_RESET_DATA); + soft_reset &= ~(0x3f << 6); + soft_reset |= (nominal_ratio & 0x3f) << 6; + RCBA32(SOFT_RESET_DATA) = soft_reset; + + /* Set soft reset control to use register value */ + RCBA32_OR(SOFT_RESET_CTRL, 1); + + /* Issue warm reset, will be "CPU only" due to soft reset data */ + outb(0x0, 0xcf9); + outb(0x6, 0xcf9); + while (1) { + asm("hlt"); + } +} + +static void check_for_clean_reset(void) +{ + msr_t msr; + msr = rdmsr(MTRRdefType_MSR); + + /* Use the MTRR default type MSR as a proxy for detecting INIT#. + * Reset the system if any known bits are set in that MSR. That is + * an indication of the CPU not being properly reset. */ + if (msr.lo & (MTRRdefTypeEn | MTRRdefTypeFixEn)) { + outb(0x0, 0xcf9); + outb(0x6, 0xcf9); + while (1) { + asm("hlt"); + } + } +} + +static void bootblock_cpu_init(void) +{ + /* Set flex ratio and reset if needed */ + set_flex_ratio_to_tdp_nominal(); + check_for_clean_reset(); + enable_rom_caching(); + intel_update_microcode_from_cbfs(); +} diff --git a/src/soc/intel/broadwell/bootblock/pch.c b/src/soc/intel/broadwell/bootblock/pch.c new file mode 100644 index 0000000000..96291189cd --- /dev/null +++ b/src/soc/intel/broadwell/bootblock/pch.c @@ -0,0 +1,100 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "pch.h" + +static void store_initial_timestamp(void) +{ + /* On Cougar Point we have two 32bit scratchpad registers available: + * D0:F0 0xdc (SKPAD) + * D31:F2 0xd0 (SATA SP) + */ + tsc_t tsc = rdtsc(); + pci_write_config32(PCI_DEV(0, 0x00, 0), 0xdc, tsc.lo); + pci_write_config32(PCI_DEV(0, 0x1f, 2), 0xd0, tsc.hi); +} + +/* + * Enable Prefetching and Caching. + */ +static void enable_spi_prefetch(void) +{ + u8 reg8; + device_t dev; + + dev = PCI_DEV(0, 0x1f, 0); + + reg8 = pci_read_config8(dev, 0xdc); + reg8 &= ~(3 << 2); + reg8 |= (2 << 2); /* Prefetching and Caching Enabled */ + pci_write_config8(dev, 0xdc, reg8); +} + + +static void map_rcba(void) +{ + device_t dev = PCI_DEV(0, 0x1f, 0); + + pci_write_config32(dev, RCBA, DEFAULT_RCBA | 1); +} + +static void enable_port80_on_lpc(void) +{ + /* Enable port 80 POST on LPC. The chipset does this by deafult, + * but it doesn't appear to hurt anything. */ + u32 gcs = RCBA32(GCS); + gcs = gcs & ~0x4; + RCBA32(GCS) = gcs; +} + +static void set_spi_speed(void) +{ + u32 fdod; + u8 ssfc; + + /* Observe SPI Descriptor Component Section 0 */ + SPIBAR32(FDOC) = 0x1000; + + /* Extract the Write/Erase SPI Frequency from descriptor */ + fdod = SPIBAR32(FDOD); + fdod >>= 24; + fdod &= 7; + + /* Set Software Sequence frequency to match */ + ssfc = SPIBAR8(SSFC + 2); + ssfc &= ~7; + ssfc |= fdod; + SPIBAR8(SSFC + 2) = ssfc; +} + +static void bootblock_southbridge_init(void) +{ +#if CONFIG_COLLECT_TIMESTAMPS + store_initial_timestamp(); +#endif + map_rcba(); + enable_spi_prefetch(); + enable_port80_on_lpc(); + set_spi_speed(); + + /* Enable upper 128bytes of CMOS */ + RCBA32(RC) = (1 << 2); +} diff --git a/src/soc/intel/broadwell/bootblock/systemagent.c b/src/soc/intel/broadwell/bootblock/systemagent.c new file mode 100644 index 0000000000..743007e157 --- /dev/null +++ b/src/soc/intel/broadwell/bootblock/systemagent.c @@ -0,0 +1,26 @@ +#include + +/* Just re-define this instead of including haswell.h. It blows up romcc. */ +#define PCIEXBAR 0x60 + +static void bootblock_northbridge_init(void) +{ + uint32_t reg; + + /* + * The "io" variant of the config access is explicitly used to + * setup the PCIEXBAR because CONFIG_MMCONF_SUPPORT_DEFAULT is set to + * to true. That way all subsequent non-explicit config accesses use + * MCFG. This code also assumes that bootblock_northbridge_init() is + * the first thing called in the non-asm boot block code. The final + * assumption is that no assembly code is using the + * CONFIG_MMCONF_SUPPORT_DEFAULT option to do PCI config acceses. + * + * The PCIEXBAR is assumed to live in the memory mapped IO space under + * 4GiB. + */ + reg = 0; + pci_io_write_config32(PCI_DEV(0,0,0), PCIEXBAR + 4, reg); + reg = CONFIG_MMCONF_BASE_ADDRESS | 4 | 1; /* 64MiB - 0-63 buses. */ + pci_io_write_config32(PCI_DEV(0,0,0), PCIEXBAR, reg); +} diff --git a/src/soc/intel/broadwell/bootblock/timestamp.inc b/src/soc/intel/broadwell/bootblock/timestamp.inc new file mode 100644 index 0000000000..f565775ed8 --- /dev/null +++ b/src/soc/intel/broadwell/bootblock/timestamp.inc @@ -0,0 +1,19 @@ +/* Store the initial timestamp for booting in mmx registers. This works + * because the bootblock isn't being compiled with MMX support so mm0 and + * mm1 will be preserved into romstage. */ + .code32 + +.global stash_timestamp +stash_timestamp: + + /* Save the BIST value */ + movl %eax, %ebp + + finit + rdtsc + movd %eax, %mm0 + movd %edx, %mm1 + + /* Restore the BIST value to %eax */ + movl %ebp, %eax + diff --git a/src/soc/intel/broadwell/broadwell/cpu.h b/src/soc/intel/broadwell/broadwell/cpu.h new file mode 100644 index 0000000000..e57673d335 --- /dev/null +++ b/src/soc/intel/broadwell/broadwell/cpu.h @@ -0,0 +1,218 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 The ChromiumOS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef _CPU_INTEL_HASWELL_H +#define _CPU_INTEL_HASWELL_H + +#include + +/* Haswell CPU types */ +#define HASWELL_FAMILY_MOBILE 0x306c0 +#define HASWELL_FAMILY_ULT 0x40650 + +/* Haswell CPU steppings */ +#define HASWELL_STEPPING_MOBILE_A0 1 +#define HASWELL_STEPPING_MOBILE_B0 2 +#define HASWELL_STEPPING_MOBILE_C0 3 +#define HASWELL_STEPPING_MOBILE_D0 4 +#define HASWELL_STEPPING_ULT_B0 0 +#define HASWELL_STEPPING_ULT_C0 1 + +/* Haswell bus clock is fixed at 100MHz */ +#define HASWELL_BCLK 100 + +#define CORE_THREAD_COUNT_MSR 0x35 +#define IA32_FEATURE_CONTROL 0x3a +#define CPUID_VMX (1 << 5) +#define CPUID_SMX (1 << 6) +#define MSR_FEATURE_CONFIG 0x13c +#define MSR_FLEX_RATIO 0x194 +#define FLEX_RATIO_LOCK (1 << 20) +#define FLEX_RATIO_EN (1 << 16) +#define IA32_PLATFORM_DCA_CAP 0x1f8 +#define IA32_MISC_ENABLE 0x1a0 +#define MSR_TEMPERATURE_TARGET 0x1a2 +#define IA32_PERF_CTL 0x199 +#define IA32_THERM_INTERRUPT 0x19b +#define IA32_ENERGY_PERFORMANCE_BIAS 0x1b0 +#define ENERGY_POLICY_PERFORMANCE 0 +#define ENERGY_POLICY_NORMAL 6 +#define ENERGY_POLICY_POWERSAVE 15 +#define IA32_PACKAGE_THERM_INTERRUPT 0x1b2 +#define MSR_LT_LOCK_MEMORY 0x2e7 +#define IA32_MC0_STATUS 0x401 + +#define MSR_PIC_MSG_CONTROL 0x2e +#define MSR_PLATFORM_INFO 0xce +#define PLATFORM_INFO_SET_TDP (1 << 29) +#define MSR_PMG_CST_CONFIG_CONTROL 0xe2 +#define MSR_PMG_IO_CAPTURE_BASE 0xe4 + +#define MSR_MISC_PWR_MGMT 0x1aa +#define MISC_PWR_MGMT_EIST_HW_DIS (1 << 0) +#define MSR_TURBO_RATIO_LIMIT 0x1ad +#define MSR_POWER_CTL 0x1fc + +#define MSR_C_STATE_LATENCY_CONTROL_0 0x60a +#define MSR_C_STATE_LATENCY_CONTROL_1 0x60b +#define MSR_C_STATE_LATENCY_CONTROL_2 0x60c +#define MSR_C_STATE_LATENCY_CONTROL_3 0x633 +#define MSR_C_STATE_LATENCY_CONTROL_4 0x634 +#define MSR_C_STATE_LATENCY_CONTROL_5 0x635 +#define IRTL_VALID (1 << 15) +#define IRTL_1_NS (0 << 10) +#define IRTL_32_NS (1 << 10) +#define IRTL_1024_NS (2 << 10) +#define IRTL_32768_NS (3 << 10) +#define IRTL_1048576_NS (4 << 10) +#define IRTL_33554432_NS (5 << 10) +#define IRTL_RESPONSE_MASK (0x3ff) + +/* long duration in low dword, short duration in high dword */ +#define MSR_PKG_POWER_LIMIT 0x610 +#define PKG_POWER_LIMIT_MASK 0x7fff +#define PKG_POWER_LIMIT_EN (1 << 15) +#define PKG_POWER_LIMIT_CLAMP (1 << 16) +#define PKG_POWER_LIMIT_TIME_SHIFT 17 +#define PKG_POWER_LIMIT_TIME_MASK 0x7f + +#define MSR_VR_CURRENT_CONFIG 0x601 +#define MSR_VR_MISC_CONFIG 0x603 +#define MSR_PKG_POWER_SKU_UNIT 0x606 +#define MSR_PKG_POWER_SKU 0x614 +#define MSR_DDR_RAPL_LIMIT 0x618 +#define MSR_VR_MISC_CONFIG2 0x636 +#define MSR_PP0_POWER_LIMIT 0x638 +#define MSR_PP1_POWER_LIMIT 0x640 + +#define MSR_CONFIG_TDP_NOMINAL 0x648 +#define MSR_CONFIG_TDP_LEVEL1 0x649 +#define MSR_CONFIG_TDP_LEVEL2 0x64a +#define MSR_CONFIG_TDP_CONTROL 0x64b +#define MSR_TURBO_ACTIVATION_RATIO 0x64c + +/* P-state configuration */ +#define PSS_MAX_ENTRIES 8 +#define PSS_RATIO_STEP 2 +#define PSS_LATENCY_TRANSITION 10 +#define PSS_LATENCY_BUSMASTER 10 + +/* PCODE MMIO communications live in the MCHBAR. */ +#define BIOS_MAILBOX_INTERFACE 0x5da4 +#define MAILBOX_RUN_BUSY (1 << 31) +#define MAILBOX_BIOS_CMD_READ_PCS 1 +#define MAILBOX_BIOS_CMD_WRITE_PCS 2 +#define MAILBOX_BIOS_CMD_READ_CALIBRATION 0x509 +#define MAILBOX_BIOS_CMD_FSM_MEASURE_INTVL 0x909 +#define MAILBOX_BIOS_CMD_READ_PCH_POWER 0xa +#define MAILBOX_BIOS_CMD_READ_PCH_POWER_EXT 0xb +/* Errors are returned back in bits 7:0. */ +#define MAILBOX_BIOS_ERROR_NONE 0 +#define MAILBOX_BIOS_ERROR_INVALID_COMMAND 1 +#define MAILBOX_BIOS_ERROR_TIMEOUT 2 +#define MAILBOX_BIOS_ERROR_ILLEGAL_DATA 3 +#define MAILBOX_BIOS_ERROR_RESERVED 4 +#define MAILBOX_BIOS_ERROR_ILLEGAL_VR_ID 5 +#define MAILBOX_BIOS_ERROR_VR_INTERFACE_LOCKED 6 +#define MAILBOX_BIOS_ERROR_VR_ERROR 7 +/* Data is passed through bits 31:0 of the data register. */ +#define BIOS_MAILBOX_DATA 0x5da0 + +/* Region of SMM space is reserved for multipurpose use. It falls below + * the IED region and above the SMM handler. */ +#define RESERVED_SMM_SIZE CONFIG_SMM_RESERVED_SIZE +#define RESERVED_SMM_OFFSET \ + (CONFIG_SMM_TSEG_SIZE - CONFIG_IED_REGION_SIZE - RESERVED_SMM_SIZE) + +/* Sanity check config options. */ +#if (CONFIG_SMM_TSEG_SIZE <= (CONFIG_IED_REGION_SIZE + RESERVED_SMM_SIZE)) +# error "CONFIG_SMM_TSEG_SIZE <= (CONFIG_IED_REGION_SIZE + RESERVED_SMM_SIZE)" +#endif +#if (CONFIG_SMM_TSEG_SIZE < 0x800000) +# error "CONFIG_SMM_TSEG_SIZE must at least be 8MiB" +#endif +#if ((CONFIG_SMM_TSEG_SIZE & (CONFIG_SMM_TSEG_SIZE - 1)) != 0) +# error "CONFIG_SMM_TSEG_SIZE is not a power of 2" +#endif +#if ((CONFIG_IED_REGION_SIZE & (CONFIG_IED_REGION_SIZE - 1)) != 0) +# error "CONFIG_IED_REGION_SIZE is not a power of 2" +#endif + +#if !defined(__ROMCC__) // FIXME romcc should handle below constructs + +#if defined(__PRE_RAM__) +struct pei_data; +struct rcba_config_instruction; +struct romstage_params { + struct pei_data *pei_data; + const void *gpio_map; + const struct rcba_config_instruction *rcba_config; + unsigned long bist; + void (*copy_spd)(struct pei_data *); +}; +void mainboard_romstage_entry(unsigned long bist); +void romstage_common(const struct romstage_params *params); +/* romstage_main is called from the cache-as-ram assembly file. The return + * value is the stack value to be used for romstage once cache-as-ram is + * torn down. The following values are pushed onto the stack to setup the + * MTRRs: + * +0: Number of MTRRs + * +4: MTTR base 0 31:0 + * +8: MTTR base 0 63:32 + * +12: MTTR mask 0 31:0 + * +16: MTTR mask 0 63:32 + * +20: MTTR base 1 31:0 + * +24: MTTR base 1 63:32 + * +28: MTTR mask 1 31:0 + * +32: MTTR mask 1 63:32 + * ... + */ +void * asmlinkage romstage_main(unsigned long bist); +/* romstage_after_car() is the C function called after cache-as-ram has + * been torn down. It is responsible for loading the ramstage. */ +void romstage_after_car(void); +#endif + +#ifdef __SMM__ +/* Lock MSRs */ +void intel_cpu_haswell_finalize_smm(void); +#else +/* Configure power limits for turbo mode */ +void set_power_limits(u8 power_limit_1_time); +int cpu_config_tdp_levels(void); +/* Returns 0 on success, < 0 on failure. */ +int smm_initialize(void); +void smm_relocate(void); +struct bus; +void bsp_init_and_start_aps(struct bus *cpu_bus); +/* Determine if HyperThreading is disabled. The variable is not valid until + * setup_ap_init() has been called. */ +extern int ht_disabled; +#endif + +/* CPU identification */ +int haswell_family_model(void); +int haswell_stepping(void); +int haswell_is_ult(void); + +#endif + +#endif diff --git a/src/soc/intel/broadwell/broadwell/gpio.h b/src/soc/intel/broadwell/broadwell/gpio.h new file mode 100644 index 0000000000..38da9e2c5c --- /dev/null +++ b/src/soc/intel/broadwell/broadwell/gpio.h @@ -0,0 +1,170 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef INTEL_LYNXPOINT_LP_GPIO_H +#define INTEL_LYNXPOINT_LP_GPIO_H + +/* LynxPoint LP GPIOBASE Registers */ +#define GPIO_OWNER(set) (0x00 + ((set) * 4)) +#define GPIO_PIRQ_APIC_EN 0x10 +#define GPIO_BLINK 0x18 +#define GPIO_SER_BLINK 0x1c +#define GPIO_SER_BLINK_CS 0x20 +#define GPIO_SER_BLINK_DATA 0x24 +#define GPIO_ROUTE(set) (0x30 + ((set) * 4)) +#define GPIO_RESET(set) (0x60 + ((set) * 4)) +#define GPIO_GLOBAL_CONFIG 0x7c +#define GPIO_IRQ_IS(set) (0x80 + ((set) * 4)) +#define GPIO_IRQ_IE(set) (0x90 + ((set) * 4)) +#define GPIO_CONFIG0(gpio) (0x100 + ((gpio) * 8)) +#define GPIO_CONFIG1(gpio) (0x104 + ((gpio) * 8)) + +#define MAX_GPIO_NUMBER 94 /* zero based */ +#define GPIO_LIST_END 0xffffffff + +/* conf0 */ + +#define GPIO_MODE_NATIVE (0 << 0) +#define GPIO_MODE_GPIO (1 << 0) + +#define GPIO_DIR_OUTPUT (0 << 2) +#define GPIO_DIR_INPUT (1 << 2) + +#define GPIO_NO_INVERT (0 << 3) +#define GPIO_INVERT (1 << 3) + +#define GPIO_IRQ_EDGE (0 << 4) +#define GPIO_IRQ_LEVEL (1 << 4) + +#define GPI_LEVEL (1 << 30) + +#define GPO_LEVEL_SHIFT 31 +#define GPO_LEVEL_MASK (1 << GPO_LEVEL_SHIFT) +#define GPO_LEVEL_LOW (0 << GPO_LEVEL_SHIFT) +#define GPO_LEVEL_HIGH (1 << GPO_LEVEL_SHIFT) + +/* conf1 */ + +#define GPIO_PULL_NONE (0 << 0) +#define GPIO_PULL_DOWN (1 << 0) +#define GPIO_PULL_UP (2 << 0) + +#define GPIO_SENSE_ENABLE (0 << 2) +#define GPIO_SENSE_DISABLE (1 << 2) + +/* owner */ + +#define GPIO_OWNER_ACPI 0 +#define GPIO_OWNER_GPIO 1 + +/* route */ + +#define GPIO_ROUTE_SCI 0 +#define GPIO_ROUTE_SMI 1 + +/* irqen */ + +#define GPIO_IRQ_DISABLE 0 +#define GPIO_IRQ_ENABLE 1 + +/* blink */ + +#define GPO_NO_BLINK 0 +#define GPO_BLINK 1 + +/* reset */ + +#define GPIO_RESET_PWROK 0 +#define GPIO_RESET_RSMRST 1 + +/* pirq route to io-apic */ + +#define GPIO_PIRQ_APIC_MASK 0 +#define GPIO_PIRQ_APIC_ROUTE 1 + +#define LP_GPIO_END \ + { .conf0 = GPIO_LIST_END } + +#define LP_GPIO_NATIVE \ + { .conf0 = GPIO_MODE_NATIVE } + +#define LP_GPIO_UNUSED \ + { .conf0 = GPIO_MODE_GPIO | GPIO_DIR_INPUT, \ + .owner = GPIO_OWNER_GPIO, \ + .conf1 = GPIO_SENSE_DISABLE } + +#define LP_GPIO_ACPI_SCI \ + { .conf0 = GPIO_MODE_GPIO | GPIO_DIR_INPUT | GPIO_INVERT, \ + .owner = GPIO_OWNER_ACPI, \ + .route = GPIO_ROUTE_SCI } + +#define LP_GPIO_ACPI_SMI \ + { .conf0 = GPIO_MODE_GPIO | GPIO_DIR_INPUT | GPIO_INVERT, \ + .owner = GPIO_OWNER_ACPI, \ + .route = GPIO_ROUTE_SMI } + +#define LP_GPIO_INPUT \ + { .conf0 = GPIO_MODE_GPIO | GPIO_DIR_INPUT, \ + .owner = GPIO_OWNER_GPIO } + +#define LP_GPIO_INPUT_INVERT \ + { .conf0 = GPIO_MODE_GPIO | GPIO_DIR_INPUT | GPIO_INVERT, \ + .owner = GPIO_OWNER_GPIO } + +#define LP_GPIO_IRQ_EDGE \ + { .conf0 = GPIO_MODE_GPIO | GPIO_DIR_INPUT | GPIO_IRQ_EDGE, \ + .owner = GPIO_OWNER_GPIO, \ + .irqen = GPIO_IRQ_ENABLE } + +#define LP_GPIO_IRQ_LEVEL \ + { .conf0 = GPIO_MODE_GPIO | GPIO_DIR_INPUT | GPIO_IRQ_LEVEL, \ + .owner = GPIO_OWNER_GPIO, \ + .irqen = GPIO_IRQ_ENABLE } + +#define LP_GPIO_PIRQ \ + { .conf0 = GPIO_MODE_GPIO | GPIO_DIR_INPUT, \ + .owner = GPIO_OWNER_GPIO, \ + .pirq = GPIO_PIRQ_APIC_ROUTE } + +#define LP_GPIO_OUT_HIGH \ + { .conf0 = GPIO_MODE_GPIO | GPIO_DIR_OUTPUT | GPO_LEVEL_HIGH, \ + .owner = GPIO_OWNER_GPIO, \ + .conf1 = GPIO_SENSE_DISABLE } + +#define LP_GPIO_OUT_LOW \ + { .conf0 = GPIO_MODE_GPIO | GPIO_DIR_OUTPUT | GPO_LEVEL_LOW, \ + .owner = GPIO_OWNER_GPIO, \ + .conf1 = GPIO_SENSE_DISABLE } + +struct pch_lp_gpio_map { + u8 gpio; + u32 conf0; + u32 conf1; + u8 owner; + u8 route; + u8 irqen; + u8 reset; + u8 blink; + u8 pirq; +} __attribute__ ((packed)); + +/* Configure GPIOs with mainboard provided settings */ +void setup_pch_lp_gpios(const struct pch_lp_gpio_map map[]); + +#endif diff --git a/src/soc/intel/broadwell/broadwell/me.h b/src/soc/intel/broadwell/broadwell/me.h new file mode 100644 index 0000000000..a72778b88f --- /dev/null +++ b/src/soc/intel/broadwell/broadwell/me.h @@ -0,0 +1,508 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 The Chromium OS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef _INTEL_ME_H +#define _INTEL_ME_H + +#define ME_RETRY 100000 /* 1 second */ +#define ME_DELAY 10 /* 10 us */ + +/* + * Management Engine PCI registers + */ + +#define PCI_CPU_DEVICE PCI_DEV(0,0,0) +#define PCI_CPU_MEBASE_L 0x70 /* Set by MRC */ +#define PCI_CPU_MEBASE_H 0x74 /* Set by MRC */ + +#define PCI_ME_HFS 0x40 +#define ME_HFS_CWS_RESET 0 +#define ME_HFS_CWS_INIT 1 +#define ME_HFS_CWS_REC 2 +#define ME_HFS_CWS_NORMAL 5 +#define ME_HFS_CWS_WAIT 6 +#define ME_HFS_CWS_TRANS 7 +#define ME_HFS_CWS_INVALID 8 +#define ME_HFS_STATE_PREBOOT 0 +#define ME_HFS_STATE_M0_UMA 1 +#define ME_HFS_STATE_M3 4 +#define ME_HFS_STATE_M0 5 +#define ME_HFS_STATE_BRINGUP 6 +#define ME_HFS_STATE_ERROR 7 +#define ME_HFS_ERROR_NONE 0 +#define ME_HFS_ERROR_UNCAT 1 +#define ME_HFS_ERROR_IMAGE 3 +#define ME_HFS_ERROR_DEBUG 4 +#define ME_HFS_MODE_NORMAL 0 +#define ME_HFS_MODE_DEBUG 2 +#define ME_HFS_MODE_DIS 3 +#define ME_HFS_MODE_OVER_JMPR 4 +#define ME_HFS_MODE_OVER_MEI 5 +#define ME_HFS_BIOS_DRAM_ACK 1 +#define ME_HFS_ACK_NO_DID 0 +#define ME_HFS_ACK_RESET 1 +#define ME_HFS_ACK_PWR_CYCLE 2 +#define ME_HFS_ACK_S3 3 +#define ME_HFS_ACK_S4 4 +#define ME_HFS_ACK_S5 5 +#define ME_HFS_ACK_GBL_RESET 6 +#define ME_HFS_ACK_CONTINUE 7 + +struct me_hfs { + u32 working_state: 4; + u32 mfg_mode: 1; + u32 fpt_bad: 1; + u32 operation_state: 3; + u32 fw_init_complete: 1; + u32 ft_bup_ld_flr: 1; + u32 update_in_progress: 1; + u32 error_code: 4; + u32 operation_mode: 4; + u32 reserved: 4; + u32 boot_options_present: 1; + u32 ack_data: 3; + u32 bios_msg_ack: 4; +} __attribute__ ((packed)); + +#define PCI_ME_UMA 0x44 + +struct me_uma { + u32 size: 6; + u32 reserved_1: 10; + u32 valid: 1; + u32 reserved_0: 14; + u32 set_to_one: 1; +} __attribute__ ((packed)); + +#define PCI_ME_H_GS 0x4c +#define ME_INIT_DONE 1 +#define ME_INIT_STATUS_SUCCESS 0 +#define ME_INIT_STATUS_NOMEM 1 +#define ME_INIT_STATUS_ERROR 2 +#define ME_INIT_STATUS_SUCCESS_OTHER 3 /* SEE ME9 BWG */ + +struct me_did { + u32 uma_base: 16; + u32 reserved: 7; + u32 rapid_start: 1; + u32 status: 4; + u32 init_done: 4; +} __attribute__ ((packed)); + +/* + * Apparently the GMES register is renamed to HFS2 (or HFSTS2 according + * to ME9 BWG). Sadly the PCH EDS and the ME BWG do not match on nomenclature. + */ +#define PCI_ME_HFS2 0x48 +/* Infrastructure Progress Values */ +#define ME_HFS2_PHASE_ROM 0 +#define ME_HFS2_PHASE_BUP 1 +#define ME_HFS2_PHASE_UKERNEL 2 +#define ME_HFS2_PHASE_POLICY 3 +#define ME_HFS2_PHASE_MODULE_LOAD 4 +#define ME_HFS2_PHASE_UNKNOWN 5 +#define ME_HFS2_PHASE_HOST_COMM 6 +/* Current State - Based on Infra Progress values. */ +/* ROM State */ +#define ME_HFS2_STATE_ROM_BEGIN 0 +#define ME_HFS2_STATE_ROM_DISABLE 6 +/* BUP State */ +#define ME_HFS2_STATE_BUP_INIT 0 +#define ME_HFS2_STATE_BUP_DIS_HOST_WAKE 1 +#define ME_HFS2_STATE_BUP_FLOW_DET 4 +#define ME_HFS2_STATE_BUP_VSCC_ERR 8 +#define ME_HFS2_STATE_BUP_CHECK_STRAP 0xa +#define ME_HFS2_STATE_BUP_PWR_OK_TIMEOUT 0xb +#define ME_HFS2_STATE_BUP_MANUF_OVRD_STRAP 0xd +#define ME_HFS2_STATE_BUP_M3 0x11 +#define ME_HFS2_STATE_BUP_M0 0x12 +#define ME_HFS2_STATE_BUP_FLOW_DET_ERR 0x13 +#define ME_HFS2_STATE_BUP_M3_CLK_ERR 0x15 +#define ME_HFS2_STATE_BUP_CPU_RESET_DID_TIMEOUT_MEM_MISSING 0x17 +#define ME_HFS2_STATE_BUP_M3_KERN_LOAD 0x18 +#define ME_HFS2_STATE_BUP_T32_MISSING 0x1c +#define ME_HFS2_STATE_BUP_WAIT_DID 0x1f +#define ME_HFS2_STATE_BUP_WAIT_DID_FAIL 0x20 +#define ME_HFS2_STATE_BUP_DID_NO_FAIL 0x21 +#define ME_HFS2_STATE_BUP_ENABLE_UMA 0x22 +#define ME_HFS2_STATE_BUP_ENABLE_UMA_ERR 0x23 +#define ME_HFS2_STATE_BUP_SEND_DID_ACK 0x24 +#define ME_HFS2_STATE_BUP_SEND_DID_ACK_ERR 0x25 +#define ME_HFS2_STATE_BUP_M0_CLK 0x26 +#define ME_HFS2_STATE_BUP_M0_CLK_ERR 0x27 +#define ME_HFS2_STATE_BUP_TEMP_DIS 0x28 +#define ME_HFS2_STATE_BUP_M0_KERN_LOAD 0x32 +/* Policy Module State */ +#define ME_HFS2_STATE_POLICY_ENTRY 0 +#define ME_HFS2_STATE_POLICY_RCVD_S3 3 +#define ME_HFS2_STATE_POLICY_RCVD_S4 4 +#define ME_HFS2_STATE_POLICY_RCVD_S5 5 +#define ME_HFS2_STATE_POLICY_RCVD_UPD 6 +#define ME_HFS2_STATE_POLICY_RCVD_PCR 7 +#define ME_HFS2_STATE_POLICY_RCVD_NPCR 8 +#define ME_HFS2_STATE_POLICY_RCVD_HOST_WAKE 9 +#define ME_HFS2_STATE_POLICY_RCVD_AC_DC 0xa +#define ME_HFS2_STATE_POLICY_RCVD_DID 0xb +#define ME_HFS2_STATE_POLICY_VSCC_NOT_FOUND 0xc +#define ME_HFS2_STATE_POLICY_VSCC_INVALID 0xd +#define ME_HFS2_STATE_POLICY_FPB_ERR 0xe +#define ME_HFS2_STATE_POLICY_DESCRIPTOR_ERR 0xf +#define ME_HFS2_STATE_POLICY_VSCC_NO_MATCH 0x10 +/* Current PM Event Values */ +#define ME_HFS2_PMEVENT_CLEAN_MOFF_MX_WAKE 0 +#define ME_HFS2_PMEVENT_MOFF_MX_WAKE_ERROR 1 +#define ME_HFS2_PMEVENT_CLEAN_GLOBAL_RESET 2 +#define ME_HFS2_PMEVENT_CLEAN_GLOBAL_RESET_ERROR 3 +#define ME_HFS2_PMEVENT_CLEAN_ME_RESET 4 +#define ME_HFS2_PMEVENT_ME_RESET_EXCEPTION 5 +#define ME_HFS2_PMEVENT_PSEUDO_ME_RESET 6 +#define ME_HFS2_PMEVENT_S0MO_SXM3 7 +#define ME_HFS2_PMEVENT_SXM3_S0M0 8 +#define ME_HFS2_PMEVENT_NON_PWR_CYCLE_RESET 9 +#define ME_HFS2_PMEVENT_PWR_CYCLE_RESET_M3 0xa +#define ME_HFS2_PMEVENT_PWR_CYCLE_RESET_MOFF 0xb +#define ME_HFS2_PMEVENT_SXMX_SXMOFF 0xc + +struct me_hfs2 { + u32 bist_in_progress: 1; + u32 reserved1: 2; + u32 invoke_mebx: 1; + u32 cpu_replaced_sts: 1; + u32 mbp_rdy: 1; + u32 mfs_failure: 1; + u32 warm_reset_request: 1; + u32 cpu_replaced_valid: 1; + u32 reserved2: 4; + u32 mbp_cleared: 1; + u32 reserved3: 2; + u32 current_state: 8; + u32 current_pmevent: 4; + u32 progress_code: 4; +} __attribute__ ((packed)); + +#define PCI_ME_H_GS2 0x70 +#define PCI_ME_MBP_GIVE_UP 0x01 + +#define PCI_ME_HERES 0xbc +#define PCI_ME_EXT_SHA1 0x00 +#define PCI_ME_EXT_SHA256 0x02 +#define PCI_ME_HER(x) (0xc0+(4*(x))) + +struct me_heres { + u32 extend_reg_algorithm: 4; + u32 reserved: 26; + u32 extend_feature_present: 1; + u32 extend_reg_valid: 1; +} __attribute__ ((packed)); + +/* + * Management Engine MEI registers + */ + +#define MEI_H_CB_WW 0x00 +#define MEI_H_CSR 0x04 +#define MEI_ME_CB_RW 0x08 +#define MEI_ME_CSR_HA 0x0c + +struct mei_csr { + u32 interrupt_enable: 1; + u32 interrupt_status: 1; + u32 interrupt_generate: 1; + u32 ready: 1; + u32 reset: 1; + u32 reserved: 3; + u32 buffer_read_ptr: 8; + u32 buffer_write_ptr: 8; + u32 buffer_depth: 8; +} __attribute__ ((packed)); + +#define MEI_ADDRESS_CORE 0x01 +#define MEI_ADDRESS_AMT 0x02 +#define MEI_ADDRESS_RESERVED 0x03 +#define MEI_ADDRESS_WDT 0x04 +#define MEI_ADDRESS_MKHI 0x07 +#define MEI_ADDRESS_ICC 0x08 +#define MEI_ADDRESS_THERMAL 0x09 + +#define MEI_HOST_ADDRESS 0 + +struct mei_header { + u32 client_address: 8; + u32 host_address: 8; + u32 length: 9; + u32 reserved: 6; + u32 is_complete: 1; +} __attribute__ ((packed)); + +#define MKHI_GROUP_ID_CBM 0x00 +#define MKHI_GROUP_ID_FWCAPS 0x03 +#define MKHI_GROUP_ID_MDES 0x08 +#define MKHI_GROUP_ID_GEN 0xff + +#define MKHI_GLOBAL_RESET 0x0b + +#define MKHI_FWCAPS_GET_RULE 0x02 + +#define MKHI_MDES_ENABLE 0x09 + +#define MKHI_GET_FW_VERSION 0x02 +#define MKHI_END_OF_POST 0x0c +#define MKHI_FEATURE_OVERRIDE 0x14 + +struct mkhi_header { + u32 group_id: 8; + u32 command: 7; + u32 is_response: 1; + u32 reserved: 8; + u32 result: 8; +} __attribute__ ((packed)); + +struct me_fw_version { + u16 code_minor; + u16 code_major; + u16 code_build_number; + u16 code_hot_fix; + u16 recovery_minor; + u16 recovery_major; + u16 recovery_build_number; + u16 recovery_hot_fix; +} __attribute__ ((packed)); + +/* ICC Messages */ +#define ICC_SET_CLOCK_ENABLES 0x3 +#define ICC_API_VERSION_LYNXPOINT 0x00030000 + +struct icc_header { + u32 api_version; + u32 icc_command; + u32 icc_status; + u32 length; + u32 reserved; +} __attribute__ ((packed)); + +struct icc_clock_enables_msg { + u32 clock_enables; + u32 clock_mask; + u32 no_response: 1; + u32 reserved: 31; +} __attribute__ ((packed)); + +#define HECI_EOP_STATUS_SUCCESS 0x0 +#define HECI_EOP_PERFORM_GLOBAL_RESET 0x1 + +#define CBM_RR_GLOBAL_RESET 0x01 + +#define GLOBAL_RESET_BIOS_MRC 0x01 +#define GLOBAL_RESET_BIOS_POST 0x02 +#define GLOBAL_RESET_MEBX 0x03 + +struct me_global_reset { + u8 request_origin; + u8 reset_type; +} __attribute__ ((packed)); + +typedef enum { + ME_NORMAL_BIOS_PATH, + ME_S3WAKE_BIOS_PATH, + ME_ERROR_BIOS_PATH, + ME_RECOVERY_BIOS_PATH, + ME_DISABLE_BIOS_PATH, + ME_FIRMWARE_UPDATE_BIOS_PATH, +} me_bios_path; + +/* Defined in me_status.c for both romstage and ramstage */ +void intel_me_status(struct me_hfs *hfs, struct me_hfs2 *hfs2); + +#ifdef __PRE_RAM__ +void intel_early_me_status(void); +int intel_early_me_init(void); +int intel_early_me_uma_size(void); +int intel_early_me_init_done(u8 status); +#endif + +#ifdef __SMM__ +void intel_me_finalize_smm(void); +void intel_me8_finalize_smm(void); +#endif + +/* + * ME to BIOS Payload Datastructures and definitions. The ordering of the + * structures follows the ordering in the ME9 BWG. + */ + +#define MBP_APPID_KERNEL 1 +#define MBP_APPID_INTEL_AT 3 +#define MBP_APPID_HWA 4 +#define MBP_APPID_ICC 5 +#define MBP_APPID_NFC 6 +/* Kernel items: */ +#define MBP_KERNEL_FW_VER_ITEM 1 +#define MBP_KERNEL_FW_CAP_ITEM 2 +#define MBP_KERNEL_ROM_BIST_ITEM 3 +#define MBP_KERNEL_PLAT_KEY_ITEM 4 +#define MBP_KERNEL_FW_TYPE_ITEM 5 +#define MBP_KERNEL_MFS_FAILURE_ITEM 6 +#define MBP_KERNEL_PLAT_TIME_ITEM 7 +/* Intel AT items: */ +#define MBP_INTEL_AT_STATE_ITEM 1 +/* ICC Items: */ +#define MBP_ICC_PROFILE_ITEM 1 +/* HWA Items: */ +#define MBP_HWA_REQUEST_ITEM 1 +/* NFC Items: */ +#define MBP_NFC_SUPPORT_DATA_ITEM 1 + +#define MBP_MAKE_IDENT(appid, item) ((appid << 8) | item) +#define MBP_IDENT(appid, item) \ + MBP_MAKE_IDENT(MBP_APPID_##appid, MBP_##appid##_##item##_ITEM) + +typedef struct { + u32 mbp_size : 8; + u32 num_entries : 8; + u32 rsvd : 16; +} __attribute__ ((packed)) mbp_header; + +typedef struct { + u32 app_id : 8; + u32 item_id : 8; + u32 length : 8; + u32 rsvd : 8; +} __attribute__ ((packed)) mbp_item_header; + +typedef struct { + u32 major_version : 16; + u32 minor_version : 16; + u32 hotfix_version : 16; + u32 build_version : 16; +} __attribute__ ((packed)) mbp_fw_version_name; + +typedef struct { + u32 full_net : 1; + u32 std_net : 1; + u32 manageability : 1; + u32 reserved_2 : 2; + u32 intel_at : 1; + u32 intel_cls : 1; + u32 reserved : 3; + u32 intel_mpc : 1; + u32 icc_over_clocking : 1; + u32 pavp : 1; + u32 reserved_1 : 4; + u32 ipv6 : 1; + u32 kvm : 1; + u32 och : 1; + u32 vlan : 1; + u32 tls : 1; + u32 reserved_4 : 1; + u32 wlan : 1; + u32 reserved_5 : 8; +} __attribute__ ((packed)) mbp_mefwcaps; + +typedef struct { + u16 device_id; + u16 fuse_test_flags; + u32 umchid[4]; +} __attribute__ ((packed)) mbp_rom_bist_data; + +typedef struct { + u32 key[8]; +} mbp_platform_key; + +typedef struct { + u32 mobile: 1; + u32 desktop: 1; + u32 server: 1; + u32 workstation: 1; + u32 corporate: 1; + u32 consumer: 1; + u32 regular_super_sku: 1; + u32 rsvd: 1; + u32 image_type: 4; + u32 brand: 4; + u32 rsvd1: 16; +} __attribute__ ((packed)) mbp_me_firmware_type; + +typedef struct { + mbp_me_firmware_type rule_data; + u8 available; +} mbp_plat_type; + +typedef struct { + u16 icc_start_address; + u16 mask; +} __attribute__ ((packed)) icc_address_mask; + +typedef struct { + u8 num_icc_profiles; + u8 icc_profile_soft_strap; + u8 icc_profile_index; + u8 reserved; + u32 icc_reg_bundles; + icc_address_mask icc_address_mask[0]; +} __attribute__ ((packed)) mbp_icc_profile; + +typedef struct { + u16 lock_state : 1; + u16 authenticate_module : 1; + u16 s3authentication : 1; + u16 flash_wear_out : 1; + u16 flash_variable_security : 1; + u16 reserved : 11; +} __attribute__ ((packed)) tdt_state_flag; + +typedef struct { + u8 state; + u8 last_theft_trigger; + tdt_state_flag flags; +} __attribute__ ((packed)) mbp_at_state; + +typedef struct { + u32 wake_event_mrst_time_ms; + u32 mrst_pltrst_time_ms; + u32 pltrst_cpurst_time_ms; +} __attribute__ ((packed)) mbp_plat_time; + +typedef struct { + u32 device_type : 2; + u32 reserved : 30; +} __attribute__ ((packed)) mbp_nfc_data; + +typedef struct { + mbp_fw_version_name *fw_version_name; + mbp_mefwcaps *fw_capabilities; + mbp_rom_bist_data *rom_bist_data; + mbp_platform_key *platform_key; + mbp_plat_type *fw_plat_type; + mbp_icc_profile *icc_profile; + mbp_at_state *at_state; + u32 *mfsintegrity; + mbp_plat_time *plat_time; + mbp_nfc_data *nfc_data; +} me_bios_payload; + +struct me_fwcaps { + u32 id; + u8 length; + mbp_mefwcaps caps_sku; + u8 reserved[3]; +} __attribute__ ((packed)); + +#endif /* _INTEL_ME_H */ diff --git a/src/soc/intel/broadwell/broadwell/nvs.h b/src/soc/intel/broadwell/broadwell/nvs.h new file mode 100644 index 0000000000..4283ca13d5 --- /dev/null +++ b/src/soc/intel/broadwell/broadwell/nvs.h @@ -0,0 +1,136 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008-2009 coresystems GmbH + * Copyright (C) 2011 Google Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "vendorcode/google/chromeos/gnvs.h" +typedef struct { + /* Miscellaneous */ + u16 osys; /* 0x00 - Operating System */ + u8 smif; /* 0x02 - SMI function call ("TRAP") */ + u8 prm0; /* 0x03 - SMI function call parameter */ + u8 prm1; /* 0x04 - SMI function call parameter */ + u8 scif; /* 0x05 - SCI function call (via _L00) */ + u8 prm2; /* 0x06 - SCI function call parameter */ + u8 prm3; /* 0x07 - SCI function call parameter */ + u8 lckf; /* 0x08 - Global Lock function for EC */ + u8 prm4; /* 0x09 - Lock function parameter */ + u8 prm5; /* 0x0a - Lock function parameter */ + u32 p80d; /* 0x0b - Debug port (IO 0x80) value */ + u8 lids; /* 0x0f - LID state (open = 1) */ + u8 pwrs; /* 0x10 - Power state (AC = 1) */ + /* Thermal policy */ + u8 tlvl; /* 0x11 - Throttle Level Limit */ + u8 flvl; /* 0x12 - Current FAN Level */ + u8 tcrt; /* 0x13 - Critical Threshold */ + u8 tpsv; /* 0x14 - Passive Threshold */ + u8 tmax; /* 0x15 - CPU Tj_max */ + u8 f0of; /* 0x16 - FAN 0 OFF Threshold */ + u8 f0on; /* 0x17 - FAN 0 ON Threshold */ + u8 f0pw; /* 0x18 - FAN 0 PWM value */ + u8 f1of; /* 0x19 - FAN 1 OFF Threshold */ + u8 f1on; /* 0x1a - FAN 1 ON Threshold */ + u8 f1pw; /* 0x1b - FAN 1 PWM value */ + u8 f2of; /* 0x1c - FAN 2 OFF Threshold */ + u8 f2on; /* 0x1d - FAN 2 ON Threshold */ + u8 f2pw; /* 0x1e - FAN 2 PWM value */ + u8 f3of; /* 0x1f - FAN 3 OFF Threshold */ + u8 f3on; /* 0x20 - FAN 3 ON Threshold */ + u8 f3pw; /* 0x21 - FAN 3 PWM value */ + u8 f4of; /* 0x22 - FAN 4 OFF Threshold */ + u8 f4on; /* 0x23 - FAN 4 ON Threshold */ + u8 f4pw; /* 0x24 - FAN 4 PWM value */ + u8 tmps; /* 0x25 - Temperature Sensor ID */ + u8 rsvd3[2]; + /* Processor Identification */ + u8 apic; /* 0x28 - APIC enabled */ + u8 mpen; /* 0x29 - MP capable/enabled */ + u8 pcp0; /* 0x2a - PDC CPU/CORE 0 */ + u8 pcp1; /* 0x2b - PDC CPU/CORE 1 */ + u8 ppcm; /* 0x2c - Max. PPC state */ + u8 pcnt; /* 0x2d - Processor Count */ + u8 rsvd4[4]; + /* Super I/O & CMOS config */ + u8 natp; /* 0x32 - SIO type */ + u8 s5u0; /* 0x33 - Enable USB0 in S5 */ + u8 s5u1; /* 0x34 - Enable USB1 in S5 */ + u8 s3u0; /* 0x35 - Enable USB0 in S3 */ + u8 s3u1; /* 0x36 - Enable USB1 in S3 */ + u8 s33g; /* 0x37 - Enable S3 in 3G */ + u32 cmem; /* 0x38 - CBMEM TOC */ + /* Integrated Graphics Device */ + u8 igds; /* 0x3c - IGD state */ + u8 tlst; /* 0x3d - Display Toggle List Pointer */ + u8 cadl; /* 0x3e - currently attached devices */ + u8 padl; /* 0x3f - previously attached devices */ + u16 cste; /* 0x40 - current display state */ + u16 nste; /* 0x42 - next display state */ + u16 sste; /* 0x44 - set display state */ + u8 ndid; /* 0x46 - number of device ids */ + u32 did[5]; /* 0x47 - 5b device id 1..5 */ + /* TPM support */ + u8 tpmp; /* 0x5b - TPM Present */ + u8 tpme; /* 0x5c - TPM Enable */ + u8 rsvd5[3]; + /* LynxPoint Serial IO device BARs */ + u32 s0b[8]; /* 0x60 - 0x7f - BAR0 */ + u32 s1b[8]; /* 0x80 - 0x9f - BAR1 */ + u32 cbmc; /* 0xa0 - 0xa3 - coreboot memconsole */ + u8 rsvd6[16]; + /* IGD OpRegion (not implemented yet) */ + u32 aslb; /* 0xb4 - IGD OpRegion Base Address */ + u8 ibtt; /* 0xb8 - IGD boot type */ + u8 ipat; /* 0xb9 - IGD panel type */ + u8 itvf; /* 0xba - IGD TV format */ + u8 itvm; /* 0xbb - IGD TV minor format */ + u8 ipsc; /* 0xbc - IGD Panel Scaling */ + u8 iblc; /* 0xbd - IGD BLC configuration */ + u8 ibia; /* 0xbe - IGD BIA configuration */ + u8 issc; /* 0xbf - IGD SSC configuration */ + u8 i409; /* 0xc0 - IGD 0409 modified settings */ + u8 i509; /* 0xc1 - IGD 0509 modified settings */ + u8 i609; /* 0xc2 - IGD 0609 modified settings */ + u8 i709; /* 0xc3 - IGD 0709 modified settings */ + u8 idmm; /* 0xc4 - IGD Power Conservation */ + u8 idms; /* 0xc5 - IGD DVMT memory size */ + u8 if1e; /* 0xc6 - IGD Function 1 Enable */ + u8 hvco; /* 0xc7 - IGD HPLL VCO */ + u32 nxd[8]; /* 0xc8 - IGD next state DIDx for _DGS */ + u8 isci; /* 0xe8 - IGD SMI/SCI mode (0: SCI) */ + u8 pavp; /* 0xe9 - IGD PAVP data */ + u8 rsvd12; /* 0xea - rsvd */ + u8 oscc; /* 0xeb - PCIe OSC control */ + u8 npce; /* 0xec - native pcie support */ + u8 plfl; /* 0xed - platform flavor */ + u8 brev; /* 0xee - board revision */ + u8 dpbm; /* 0xef - digital port b mode */ + u8 dpcm; /* 0xf0 - digital port c mode */ + u8 dpdm; /* 0xf1 - digital port c mode */ + u8 alfp; /* 0xf2 - active lfp */ + u8 imon; /* 0xf3 - current graphics turbo imon value */ + u8 mmio; /* 0xf4 - 64bit mmio support */ + u8 rsvd13[11]; /* 0xf5 - rsvd */ + + /* ChromeOS specific (starts at 0x100)*/ + chromeos_acpi_t chromeos; +} __attribute__((packed)) global_nvs_t; + +#ifdef __SMM__ +/* Used in SMM to find the ACPI GNVS address */ +global_nvs_t *smm_get_gnvs(void); +#endif diff --git a/src/soc/intel/broadwell/broadwell/pch.h b/src/soc/intel/broadwell/broadwell/pch.h new file mode 100644 index 0000000000..982ccb3d8b --- /dev/null +++ b/src/soc/intel/broadwell/broadwell/pch.h @@ -0,0 +1,848 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008-2009 coresystems GmbH + * Copyright (C) 2012 The Chromium OS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SOUTHBRIDGE_INTEL_LYNXPOINT_PCH_H +#define SOUTHBRIDGE_INTEL_LYNXPOINT_PCH_H + +/* + * Lynx Point PCH PCI Devices: + * + * Bus 0:Device 31:Function 0 LPC Controller1 + * Bus 0:Device 31:Function 2 SATA Controller #1 + * Bus 0:Device 31:Function 3 SMBus Controller + * Bus 0:Device 31:Function 5 SATA Controller #22 + * Bus 0:Device 31:Function 6 Thermal Subsystem + * Bus 0:Device 29:Function 03 USB EHCI Controller #1 + * Bus 0:Device 26:Function 03 USB EHCI Controller #2 + * Bus 0:Device 28:Function 0 PCI Express* Port 1 + * Bus 0:Device 28:Function 1 PCI Express Port 2 + * Bus 0:Device 28:Function 2 PCI Express Port 3 + * Bus 0:Device 28:Function 3 PCI Express Port 4 + * Bus 0:Device 28:Function 4 PCI Express Port 5 + * Bus 0:Device 28:Function 5 PCI Express Port 6 + * Bus 0:Device 28:Function 6 PCI Express Port 7 + * Bus 0:Device 28:Function 7 PCI Express Port 8 + * Bus 0:Device 27:Function 0 Intel High Definition Audio Controller + * Bus 0:Device 25:Function 0 Gigabit Ethernet Controller + * Bus 0:Device 22:Function 0 Intel Management Engine Interface #1 + * Bus 0:Device 22:Function 1 Intel Management Engine Interface #2 + * Bus 0:Device 22:Function 2 IDE-R + * Bus 0:Device 22:Function 3 KT + * Bus 0:Device 20:Function 0 xHCI Controller +*/ + +/* PCH types */ +#define PCH_TYPE_LPT 0x8c +#define PCH_TYPE_LPT_LP 0x9c + +/* PCH stepping values for LPC device */ +#define LPT_H_STEP_B0 0x02 +#define LPT_H_STEP_C0 0x03 +#define LPT_H_STEP_C1 0x04 +#define LPT_H_STEP_C2 0x05 +#define LPT_LP_STEP_B0 0x02 +#define LPT_LP_STEP_B1 0x03 +#define LPT_LP_STEP_B2 0x04 + +/* + * It does not matter where we put the SMBus I/O base, as long as we + * keep it consistent and don't interfere with other devices. Stage2 + * will relocate this anyways. + * Our solution is to have SMB initialization move the I/O to SMBUS_IO_BASE + * again. But handling static BARs is a generic problem that should be + * solved in the device allocator. + */ +#define SMBUS_IO_BASE 0x0400 +#define SMBUS_SLAVE_ADDR 0x24 + +#if CONFIG_INTEL_LYNXPOINT_LP +#define DEFAULT_PMBASE 0x1000 +#define DEFAULT_GPIOBASE 0x1400 +#define DEFAULT_GPIOSIZE 0x400 +#else +#define DEFAULT_PMBASE 0x500 +#define DEFAULT_GPIOBASE 0x480 +#define DEFAULT_GPIOSIZE 0x80 +#endif + +#define HPET_ADDR 0xfed00000 +#define DEFAULT_RCBA 0xfed1c000 + +#ifndef __ACPI__ + +#if defined (__SMM__) && !defined(__ASSEMBLER__) +void intel_pch_finalize_smm(void); +void usb_ehci_sleep_prepare(device_t dev, u8 slp_typ); +void usb_ehci_disable(device_t dev); +void usb_xhci_sleep_prepare(device_t dev, u8 slp_typ); +void usb_xhci_route_all(void); +#endif + + +/* State Machine configuration. */ +#define RCBA_REG_SIZE_MASK 0x8000 +#define RCBA_REG_SIZE_16 0x8000 +#define RCBA_REG_SIZE_32 0x0000 +#define RCBA_COMMAND_MASK 0x000f +#define RCBA_COMMAND_SET 0x0001 +#define RCBA_COMMAND_READ 0x0002 +#define RCBA_COMMAND_RMW 0x0003 +#define RCBA_COMMAND_END 0x0007 + +#define RCBA_ENCODE_COMMAND(command_, reg_, mask_, or_value_) \ + { .command = command_, \ + .reg = reg_, \ + .mask = mask_, \ + .or_value = or_value_ \ + } +#define RCBA_SET_REG_32(reg_, value_) \ + RCBA_ENCODE_COMMAND(RCBA_REG_SIZE_32|RCBA_COMMAND_SET, reg_, 0, value_) +#define RCBA_READ_REG_32(reg_) \ + RCBA_ENCODE_COMMAND(RCBA_REG_SIZE_32|RCBA_COMMAND_READ, reg_, 0, 0) +#define RCBA_RMW_REG_32(reg_, mask_, or_) \ + RCBA_ENCODE_COMMAND(RCBA_REG_SIZE_32|RCBA_COMMAND_RMW, reg_, mask_, or_) +#define RCBA_SET_REG_16(reg_, value_) \ + RCBA_ENCODE_COMMAND(RCBA_REG_SIZE_16|RCBA_COMMAND_SET, reg_, 0, value_) +#define RCBA_READ_REG_16(reg_) \ + RCBA_ENCODE_COMMAND(RCBA_REG_SIZE_16|RCBA_COMMAND_READ, reg_, 0, 0) +#define RCBA_RMW_REG_16(reg_, mask_, or_) \ + RCBA_ENCODE_COMMAND(RCBA_REG_SIZE_16|RCBA_COMMAND_RMW, reg_, mask_, or_) +#define RCBA_END_CONFIG \ + RCBA_ENCODE_COMMAND(RCBA_COMMAND_END, 0, 0, 0) + +struct rcba_config_instruction +{ + u16 command; + u16 reg; + u32 mask; + u32 or_value; +}; + +#if !defined(__ASSEMBLER__) +void pch_config_rcba(const struct rcba_config_instruction *rcba_config); +int pch_silicon_revision(void); +int pch_silicon_type(void); +int pch_is_lp(void); +u16 get_pmbase(void); +u16 get_gpiobase(void); + +/* Power Management register handling in pmutil.c */ +/* PM1_CNT */ +void enable_pm1_control(u32 mask); +void disable_pm1_control(u32 mask); +/* PM1 */ +u16 clear_pm1_status(void); +void enable_pm1(u16 events); +u32 clear_smi_status(void); +/* SMI */ +void enable_smi(u32 mask); +void disable_smi(u32 mask); +/* ALT_GP_SMI */ +u32 clear_alt_smi_status(void); +void enable_alt_smi(u32 mask); +/* TCO */ +u32 clear_tco_status(void); +void enable_tco_sci(void); +/* GPE0 */ +u32 clear_gpe_status(void); +void clear_gpe_enable(void); +void enable_all_gpe(u32 set1, u32 set2, u32 set3, u32 set4); +void disable_all_gpe(void); +void enable_gpe(u32 mask); +void disable_gpe(u32 mask); +/* + * get GPIO pin value + */ +int get_gpio(int gpio_num); +/* + * Get a number comprised of multiple GPIO values. gpio_num_array points to + * the array of gpio pin numbers to scan, terminated by -1. + */ +unsigned get_gpios(const int *gpio_num_array); +/* + * Set GPIO pin value. + */ +void set_gpio(int gpio_num, int value); +/* Return non-zero if gpio is set to native function. 0 otherwise. */ +int gpio_is_native(int gpio_num); + +#if !defined(__PRE_RAM__) && !defined(__SMM__) +#include +#include +#include "chip.h" +void pch_enable(device_t dev); +void pch_disable_devfn(device_t dev); +u32 pch_iobp_read(u32 address); +void pch_iobp_write(u32 address, u32 data); +void pch_iobp_update(u32 address, u32 andvalue, u32 orvalue); +#if CONFIG_ELOG +void pch_log_state(void); +#endif +void acpi_create_intel_hpet(acpi_hpet_t * hpet); +void acpi_create_serialio_ssdt(acpi_header_t *ssdt); + +/* These helpers are for performing SMM relocation. */ +void southbridge_trigger_smi(void); +void southbridge_clear_smi_status(void); +/* The initialization of the southbridge is split into 2 compoments. One is + * for clearing the state in the SMM registers. The other is for enabling + * SMIs. They are split so that other work between the 2 actions. */ +void southbridge_smm_clear_state(void); +void southbridge_smm_enable_smi(void); +#else +void enable_smbus(void); +void enable_usb_bar(void); +int smbus_read_byte(unsigned device, unsigned address); +int early_spi_read(u32 offset, u32 size, u8 *buffer); +int early_pch_init(const void *gpio_map, + const struct rcba_config_instruction *rcba_config); +void pch_enable_lpc(void); +#endif /* !__PRE_RAM__ && !__SMM__ */ +#endif /* __ASSEMBLER__ */ + +#define MAINBOARD_POWER_OFF 0 +#define MAINBOARD_POWER_ON 1 +#define MAINBOARD_POWER_KEEP 2 + +#ifndef CONFIG_MAINBOARD_POWER_ON_AFTER_POWER_FAIL +#define CONFIG_MAINBOARD_POWER_ON_AFTER_POWER_FAIL MAINBOARD_POWER_ON +#endif + +/* PCI Configuration Space (D30:F0): PCI2PCI */ +#define PSTS 0x06 +#define SMLT 0x1b +#define SECSTS 0x1e +#define INTR 0x3c +#define BCTRL 0x3e +#define SBR (1 << 6) +#define SEE (1 << 1) +#define PERE (1 << 0) + +/* Power Management Control and Status */ +#define PCH_PCS 0x84 +#define PCH_PCS_PS_D3HOT 3 + +#define PCH_EHCI1_DEV PCI_DEV(0, 0x1d, 0) +#define PCH_EHCI2_DEV PCI_DEV(0, 0x1a, 0) +#define PCH_XHCI_DEV PCI_DEV(0, 0x14, 0) +#define PCH_ME_DEV PCI_DEV(0, 0x16, 0) +#define PCH_PCIE_DEV_SLOT 28 + +/* PCI Configuration Space (D31:F0): LPC */ +#define PCH_LPC_DEV PCI_DEV(0, 0x1f, 0) +#define SERIRQ_CNTL 0x64 + +#define GEN_PMCON_1 0xa0 +#define GEN_PMCON_2 0xa2 +#define GEN_PMCON_3 0xa4 +#define PMIR 0xac +#define PMIR_CF9LOCK (1 << 31) +#define PMIR_CF9GR (1 << 20) + +/* GEN_PMCON_3 bits */ +#define RTC_BATTERY_DEAD (1 << 2) +#define RTC_POWER_FAILED (1 << 1) +#define SLEEP_AFTER_POWER_FAIL (1 << 0) + +#define PMBASE 0x40 +#define ACPI_CNTL 0x44 +#define BIOS_CNTL 0xDC +#define GPIO_BASE 0x48 /* LPC GPIO Base Address Register */ +#define GPIO_CNTL 0x4C /* LPC GPIO Control Register */ +#define GPIO_ROUT 0xb8 + +#define PIRQA_ROUT 0x60 +#define PIRQB_ROUT 0x61 +#define PIRQC_ROUT 0x62 +#define PIRQD_ROUT 0x63 +#define PIRQE_ROUT 0x68 +#define PIRQF_ROUT 0x69 +#define PIRQG_ROUT 0x6A +#define PIRQH_ROUT 0x6B + +#define LPC_IO_DEC 0x80 /* IO Decode Ranges Register */ +#define LPC_EN 0x82 /* LPC IF Enables Register */ +#define CNF2_LPC_EN (1 << 13) /* 0x4e/0x4f */ +#define CNF1_LPC_EN (1 << 12) /* 0x2e/0x2f */ +#define MC_LPC_EN (1 << 11) /* 0x62/0x66 */ +#define KBC_LPC_EN (1 << 10) /* 0x60/0x64 */ +#define GAMEH_LPC_EN (1 << 9) /* 0x208/0x20f */ +#define GAMEL_LPC_EN (1 << 8) /* 0x200/0x207 */ +#define FDD_LPC_EN (1 << 3) /* LPC_IO_DEC[12] */ +#define LPT_LPC_EN (1 << 2) /* LPC_IO_DEC[9:8] */ +#define COMB_LPC_EN (1 << 1) /* LPC_IO_DEC[6:4] */ +#define COMA_LPC_EN (1 << 0) /* LPC_IO_DEC[2:0] */ +#define LPC_GEN1_DEC 0x84 /* LPC IF Generic Decode Range 1 */ +#define LPC_GEN2_DEC 0x88 /* LPC IF Generic Decode Range 2 */ +#define LPC_GEN3_DEC 0x8c /* LPC IF Generic Decode Range 3 */ +#define LPC_GEN4_DEC 0x90 /* LPC IF Generic Decode Range 4 */ +#define LGMR 0x98 /* LPC Generic Memory Range */ + +/* PCI Configuration Space (D31:F1): IDE */ +#define PCH_IDE_DEV PCI_DEV(0, 0x1f, 1) +#define PCH_SATA_DEV PCI_DEV(0, 0x1f, 2) +#define PCH_SATA2_DEV PCI_DEV(0, 0x1f, 5) +#define INTR_LN 0x3c +#define IDE_TIM_PRI 0x40 /* IDE timings, primary */ +#define IDE_DECODE_ENABLE (1 << 15) +#define IDE_SITRE (1 << 14) +#define IDE_ISP_5_CLOCKS (0 << 12) +#define IDE_ISP_4_CLOCKS (1 << 12) +#define IDE_ISP_3_CLOCKS (2 << 12) +#define IDE_RCT_4_CLOCKS (0 << 8) +#define IDE_RCT_3_CLOCKS (1 << 8) +#define IDE_RCT_2_CLOCKS (2 << 8) +#define IDE_RCT_1_CLOCKS (3 << 8) +#define IDE_DTE1 (1 << 7) +#define IDE_PPE1 (1 << 6) +#define IDE_IE1 (1 << 5) +#define IDE_TIME1 (1 << 4) +#define IDE_DTE0 (1 << 3) +#define IDE_PPE0 (1 << 2) +#define IDE_IE0 (1 << 1) +#define IDE_TIME0 (1 << 0) +#define IDE_TIM_SEC 0x42 /* IDE timings, secondary */ + +#define IDE_SDMA_CNT 0x48 /* Synchronous DMA control */ +#define IDE_SSDE1 (1 << 3) +#define IDE_SSDE0 (1 << 2) +#define IDE_PSDE1 (1 << 1) +#define IDE_PSDE0 (1 << 0) + +#define IDE_SDMA_TIM 0x4a + +#define IDE_CONFIG 0x54 /* IDE I/O Configuration Register */ +#define SIG_MODE_SEC_NORMAL (0 << 18) +#define SIG_MODE_SEC_TRISTATE (1 << 18) +#define SIG_MODE_SEC_DRIVELOW (2 << 18) +#define SIG_MODE_PRI_NORMAL (0 << 16) +#define SIG_MODE_PRI_TRISTATE (1 << 16) +#define SIG_MODE_PRI_DRIVELOW (2 << 16) +#define FAST_SCB1 (1 << 15) +#define FAST_SCB0 (1 << 14) +#define FAST_PCB1 (1 << 13) +#define FAST_PCB0 (1 << 12) +#define SCB1 (1 << 3) +#define SCB0 (1 << 2) +#define PCB1 (1 << 1) +#define PCB0 (1 << 0) + +#define SATA_SIRI 0xa0 /* SATA Indexed Register Index */ +#define SATA_SIRD 0xa4 /* SATA Indexed Register Data */ +#define SATA_SP 0xd0 /* Scratchpad */ + +/* SATA IOBP Registers */ +#define SATA_IOBP_SP0G3IR 0xea000151 +#define SATA_IOBP_SP1G3IR 0xea000051 +#define SATA_IOBP_SP0DTLE_DATA 0xea002550 +#define SATA_IOBP_SP0DTLE_EDGE 0xea002554 +#define SATA_IOBP_SP1DTLE_DATA 0xea002750 +#define SATA_IOBP_SP1DTLE_EDGE 0xea002754 + +#define SATA_DTLE_MASK 0xF +#define SATA_DTLE_DATA_SHIFT 24 +#define SATA_DTLE_EDGE_SHIFT 16 + +/* EHCI PCI Registers */ +#define EHCI_PWR_CTL_STS 0x54 +#define PWR_CTL_SET_MASK 0x3 +#define PWR_CTL_SET_D0 0x0 +#define PWR_CTL_SET_D3 0x3 +#define PWR_CTL_ENABLE_PME (1 << 8) +#define PWR_CTL_STATUS_PME (1 << 15) + +/* EHCI Memory Registers */ +#define EHCI_USB_CMD 0x20 +#define EHCI_USB_CMD_RUN (1 << 0) +#define EHCI_USB_CMD_PSE (1 << 4) +#define EHCI_USB_CMD_ASE (1 << 5) +#define EHCI_PORTSC(port) (0x64 + (port * 4)) +#define EHCI_PORTSC_ENABLED (1 << 2) +#define EHCI_PORTSC_SUSPEND (1 << 7) + +/* XHCI PCI Registers */ +#define XHCI_PWR_CTL_STS 0x74 +#define XHCI_USB2PR 0xd0 +#define XHCI_USB2PRM 0xd4 +#define XHCI_USB2PR_HCSEL 0x7fff +#define XHCI_USB3PR 0xd8 +#define XHCI_USB3PR_SSEN 0x3f +#define XHCI_USB3PRM 0xdc +#define XHCI_USB3FUS 0xe0 +#define XHCI_USB3FUS_SS_MASK 3 +#define XHCI_USB3FUS_SS_SHIFT 3 +#define XHCI_USB3PDO 0xe8 + +/* XHCI Memory Registers */ +#define XHCI_USB3_PORTSC(port) ((pch_is_lp() ? 0x510 : 0x570) + (port * 0x10)) +#define XHCI_USB3_PORTSC_CHST (0x7f << 17) +#define XHCI_USB3_PORTSC_WCE (1 << 25) /* Wake on Connect */ +#define XHCI_USB3_PORTSC_WDE (1 << 26) /* Wake on Disconnect */ +#define XHCI_USB3_PORTSC_WOE (1 << 27) /* Wake on Overcurrent */ +#define XHCI_USB3_PORTSC_WRC (1 << 19) /* Warm Reset Complete */ +#define XHCI_USB3_PORTSC_LWS (1 << 16) /* Link Write Strobe */ +#define XHCI_USB3_PORTSC_PED (1 << 1) /* Port Enabled/Disabled */ +#define XHCI_USB3_PORTSC_WPR (1 << 31) /* Warm Port Reset */ +#define XHCI_USB3_PORTSC_PLS (0xf << 5) /* Port Link State */ +#define XHCI_PLSR_DISABLED (4 << 5) /* Port is disabled */ +#define XHCI_PLSR_RXDETECT (5 << 5) /* Port is disconnected */ +#define XHCI_PLSR_POLLING (7 << 5) /* Port is polling */ +#define XHCI_PLSW_ENABLE (5 << 5) /* Transition from disabled */ + +/* Serial IO IOBP Registers */ +#define SIO_IOBP_PORTCTRL0 0xcb000000 /* SDIO D23:F0 */ +#define SIO_IOBP_PORTCTRL0_ACPI_IRQ_EN (1 << 5) +#define SIO_IOBP_PORTCTRL0_PCI_CONF_DIS (1 << 4) +#define SIO_IOBP_PORTCTRL1 0xcb000014 /* SDIO D23:F0 */ +#define SIO_IOBP_PORTCTRL1_SNOOP_SELECT(x) (((x) & 3) << 13) +#define SIO_IOBP_GPIODF 0xcb000154 +#define SIO_IOBP_GPIODF_SDIO_IDLE_DET_EN (1 << 4) +#define SIO_IOBP_GPIODF_DMA_IDLE_DET_EN (1 << 3) +#define SIO_IOBP_GPIODF_UART_IDLE_DET_EN (1 << 2) +#define SIO_IOBP_GPIODF_I2C_IDLE_DET_EN (1 << 1) +#define SIO_IOBP_GPIODF_SPI_IDLE_DET_EN (1 << 0) +#define SIO_IOBP_PORTCTRL2 0xcb000240 /* DMA D21:F0 */ +#define SIO_IOBP_PORTCTRL3 0xcb000248 /* I2C0 D21:F1 */ +#define SIO_IOBP_PORTCTRL4 0xcb000250 /* I2C1 D21:F2 */ +#define SIO_IOBP_PORTCTRL5 0xcb000258 /* SPI0 D21:F3 */ +#define SIO_IOBP_PORTCTRL6 0xcb000260 /* SPI1 D21:F4 */ +#define SIO_IOBP_PORTCTRL7 0xcb000268 /* UART0 D21:F5 */ +#define SIO_IOBP_PORTCTRL8 0xcb000270 /* UART1 D21:F6 */ +#define SIO_IOBP_PORTCTRLX(x) (0xcb000240 + ((x) * 8)) +/* PORTCTRL 2-8 have the same layout */ +#define SIO_IOBP_PORTCTRL_ACPI_IRQ_EN (1 << 21) +#define SIO_IOBP_PORTCTRL_PCI_CONF_DIS (1 << 20) +#define SIO_IOBP_PORTCTRL_SNOOP_SELECT(x) (((x) & 3) << 18) +#define SIO_IOBP_PORTCTRL_INT_PIN(x) (((x) & 0xf) << 2) +#define SIO_IOBP_PORTCTRL_PM_CAP_PRSNT (1 << 1) +#define SIO_IOBP_FUNCDIS0 0xce00aa07 /* DMA D21:F0 */ +#define SIO_IOBP_FUNCDIS1 0xce00aa47 /* I2C0 D21:F1 */ +#define SIO_IOBP_FUNCDIS2 0xce00aa87 /* I2C1 D21:F2 */ +#define SIO_IOBP_FUNCDIS3 0xce00aac7 /* SPI0 D21:F3 */ +#define SIO_IOBP_FUNCDIS4 0xce00ab07 /* SPI1 D21:F4 */ +#define SIO_IOBP_FUNCDIS5 0xce00ab47 /* UART0 D21:F5 */ +#define SIO_IOBP_FUNCDIS6 0xce00ab87 /* UART1 D21:F6 */ +#define SIO_IOBP_FUNCDIS7 0xce00ae07 /* SDIO D23:F0 */ +#define SIO_IOBP_FUNCDIS_DIS (1 << 8) + +/* Serial IO Devices */ +#define SIO_ID_SDMA 0 /* D21:F0 */ +#define SIO_ID_I2C0 1 /* D21:F1 */ +#define SIO_ID_I2C1 2 /* D21:F2 */ +#define SIO_ID_SPI0 3 /* D21:F3 */ +#define SIO_ID_SPI1 4 /* D21:F4 */ +#define SIO_ID_UART0 5 /* D21:F5 */ +#define SIO_ID_UART1 6 /* D21:F6 */ +#define SIO_ID_SDIO 7 /* D23:F0 */ + +#define SIO_REG_PPR_CLOCK 0x800 +#define SIO_REG_PPR_CLOCK_EN (1 << 0) +#define SIO_REG_PPR_RST 0x804 +#define SIO_REG_PPR_RST_ASSERT 0x3 +#define SIO_REG_PPR_GEN 0x808 +#define SIO_REG_PPR_GEN_LTR_MODE_MASK (1 << 2) +#define SIO_REG_PPR_GEN_VOLTAGE_MASK (1 << 3) +#define SIO_REG_PPR_GEN_VOLTAGE(x) ((x & 1) << 3) +#define SIO_REG_AUTO_LTR 0x814 + +#define SIO_REG_SDIO_PPR_GEN 0x1008 +#define SIO_REG_SDIO_PPR_SW_LTR 0x1010 +#define SIO_REG_SDIO_PPR_CMD12 0x3c +#define SIO_REG_SDIO_PPR_CMD12_B30 (1 << 30) + +#define SIO_PIN_INTA 1 /* IRQ5 in ACPI mode */ +#define SIO_PIN_INTB 2 /* IRQ6 in ACPI mode */ +#define SIO_PIN_INTC 3 /* IRQ7 in ACPI mode */ +#define SIO_PIN_INTD 4 /* IRQ13 in ACPI mode */ + +/* PCI Configuration Space (D31:F3): SMBus */ +#define PCH_SMBUS_DEV PCI_DEV(0, 0x1f, 3) +#define SMB_BASE 0x20 +#define HOSTC 0x40 +#define SMB_RCV_SLVA 0x09 + +/* HOSTC bits */ +#define I2C_EN (1 << 2) +#define SMB_SMI_EN (1 << 1) +#define HST_EN (1 << 0) + +/* SMBus I/O bits. */ +#define SMBHSTSTAT 0x0 +#define SMBHSTCTL 0x2 +#define SMBHSTCMD 0x3 +#define SMBXMITADD 0x4 +#define SMBHSTDAT0 0x5 +#define SMBHSTDAT1 0x6 +#define SMBBLKDAT 0x7 +#define SMBTRNSADD 0x9 +#define SMBSLVDATA 0xa +#define SMLINK_PIN_CTL 0xe +#define SMBUS_PIN_CTL 0xf + +#define SMBUS_TIMEOUT (10 * 1000 * 100) + + +/* Southbridge IO BARs */ + +#define GPIOBASE 0x48 + +#define PMBASE 0x40 + +/* Root Complex Register Block */ +#define RCBA 0xf0 + +#define RCBA8(x) *((volatile u8 *)(DEFAULT_RCBA + x)) +#define RCBA16(x) *((volatile u16 *)(DEFAULT_RCBA + x)) +#define RCBA32(x) *((volatile u32 *)(DEFAULT_RCBA + x)) + +#define RCBA_AND_OR(bits, x, and, or) \ + RCBA##bits(x) = ((RCBA##bits(x) & (and)) | (or)) +#define RCBA8_AND_OR(x, and, or) RCBA_AND_OR(8, x, and, or) +#define RCBA16_AND_OR(x, and, or) RCBA_AND_OR(16, x, and, or) +#define RCBA32_AND_OR(x, and, or) RCBA_AND_OR(32, x, and, or) +#define RCBA32_OR(x, or) RCBA_AND_OR(32, x, ~0UL, or) + +#define VCH 0x0000 /* 32bit */ +#define VCAP1 0x0004 /* 32bit */ +#define VCAP2 0x0008 /* 32bit */ +#define PVC 0x000c /* 16bit */ +#define PVS 0x000e /* 16bit */ + +#define V0CAP 0x0010 /* 32bit */ +#define V0CTL 0x0014 /* 32bit */ +#define V0STS 0x001a /* 16bit */ + +#define V1CAP 0x001c /* 32bit */ +#define V1CTL 0x0020 /* 32bit */ +#define V1STS 0x0026 /* 16bit */ + +#define RCTCL 0x0100 /* 32bit */ +#define ESD 0x0104 /* 32bit */ +#define ULD 0x0110 /* 32bit */ +#define ULBA 0x0118 /* 64bit */ + +#define RP1D 0x0120 /* 32bit */ +#define RP1BA 0x0128 /* 64bit */ +#define RP2D 0x0130 /* 32bit */ +#define RP2BA 0x0138 /* 64bit */ +#define RP3D 0x0140 /* 32bit */ +#define RP3BA 0x0148 /* 64bit */ +#define RP4D 0x0150 /* 32bit */ +#define RP4BA 0x0158 /* 64bit */ +#define HDD 0x0160 /* 32bit */ +#define HDBA 0x0168 /* 64bit */ +#define RP5D 0x0170 /* 32bit */ +#define RP5BA 0x0178 /* 64bit */ +#define RP6D 0x0180 /* 32bit */ +#define RP6BA 0x0188 /* 64bit */ + +#define RPC 0x0400 /* 32bit */ +#define RPFN 0x0404 /* 32bit */ + +/* Root Port configuratinon space hide */ +#define RPFN_HIDE(port) (1 << (((port) * 4) + 3)) +/* Get the function number assigned to a Root Port */ +#define RPFN_FNGET(reg,port) (((reg) >> ((port) * 4)) & 7) +/* Set the function number for a Root Port */ +#define RPFN_FNSET(port,func) (((func) & 7) << ((port) * 4)) +/* Root Port function number mask */ +#define RPFN_FNMASK(port) (7 << ((port) * 4)) + +#define TRSR 0x1e00 /* 8bit */ +#define TRCR 0x1e10 /* 64bit */ +#define TWDR 0x1e18 /* 64bit */ + +#define IOTR0 0x1e80 /* 64bit */ +#define IOTR1 0x1e88 /* 64bit */ +#define IOTR2 0x1e90 /* 64bit */ +#define IOTR3 0x1e98 /* 64bit */ + +#define TCTL 0x3000 /* 8bit */ + +#define NOINT 0 +#define INTA 1 +#define INTB 2 +#define INTC 3 +#define INTD 4 + +#define DIR_IDR 12 /* Interrupt D Pin Offset */ +#define DIR_ICR 8 /* Interrupt C Pin Offset */ +#define DIR_IBR 4 /* Interrupt B Pin Offset */ +#define DIR_IAR 0 /* Interrupt A Pin Offset */ + +#define PIRQA 0 +#define PIRQB 1 +#define PIRQC 2 +#define PIRQD 3 +#define PIRQE 4 +#define PIRQF 5 +#define PIRQG 6 +#define PIRQH 7 + +/* 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 D31IP 0x3100 /* 32bit */ +#define D31IP_TTIP 24 /* Thermal Throttle Pin */ +#define D31IP_SIP2 20 /* SATA Pin 2 */ +#define D31IP_SMIP 12 /* SMBUS Pin */ +#define D31IP_SIP 8 /* SATA Pin */ +#define D30IP 0x3104 /* 32bit */ +#define D30IP_PIP 0 /* PCI Bridge Pin */ +#define D29IP 0x3108 /* 32bit */ +#define D29IP_E1P 0 /* EHCI #1 Pin */ +#define D28IP 0x310c /* 32bit */ +#define D28IP_P8IP 28 /* PCI Express Port 8 */ +#define D28IP_P7IP 24 /* PCI Express Port 7 */ +#define D28IP_P6IP 20 /* PCI Express Port 6 */ +#define D28IP_P5IP 16 /* PCI Express Port 5 */ +#define D28IP_P4IP 12 /* PCI Express Port 4 */ +#define D28IP_P3IP 8 /* PCI Express Port 3 */ +#define D28IP_P2IP 4 /* PCI Express Port 2 */ +#define D28IP_P1IP 0 /* PCI Express Port 1 */ +#define D27IP 0x3110 /* 32bit */ +#define D27IP_ZIP 0 /* HD Audio Pin */ +#define D26IP 0x3114 /* 32bit */ +#define D26IP_E2P 0 /* EHCI #2 Pin */ +#define D25IP 0x3118 /* 32bit */ +#define D25IP_LIP 0 /* GbE LAN Pin */ +#define D22IP 0x3124 /* 32bit */ +#define D22IP_KTIP 12 /* KT Pin */ +#define D22IP_IDERIP 8 /* IDE-R Pin */ +#define D22IP_MEI2IP 4 /* MEI #2 Pin */ +#define D22IP_MEI1IP 0 /* MEI #1 Pin */ +#define D20IP 0x3128 /* 32bit */ +#define D20IP_XHCI 0 /* XHCI Pin */ +#define D31IR 0x3140 /* 16bit */ +#define D30IR 0x3142 /* 16bit */ +#define D29IR 0x3144 /* 16bit */ +#define D28IR 0x3146 /* 16bit */ +#define D27IR 0x3148 /* 16bit */ +#define D26IR 0x314c /* 16bit */ +#define D25IR 0x3150 /* 16bit */ +#define D23IR 0x3158 /* 16bit */ +#define D22IR 0x315c /* 16bit */ +#define D20IR 0x3160 /* 16bit */ +#define D21IR 0x3164 /* 16bit */ +#define D19IR 0x3168 /* 16bit */ +#define ACPIIRQEN 0x31e0 /* 32bit */ +#define OIC 0x31fe /* 16bit */ +#define PMSYNC_CONFIG 0x33c4 /* 32bit */ +#define PMSYNC_CONFIG2 0x33cc /* 32bit */ +#define SOFT_RESET_CTRL 0x38f4 +#define SOFT_RESET_DATA 0x38f8 + +#define DIR_ROUTE(a,b,c,d) \ + (((d) << DIR_IDR) | ((c) << DIR_ICR) | \ + ((b) << DIR_IBR) | ((a) << DIR_IAR)) + +#define RC 0x3400 /* 32bit */ +#define HPTC 0x3404 /* 32bit */ +#define GCS 0x3410 /* 32bit */ +#define BUC 0x3414 /* 32bit */ +#define PCH_DISABLE_GBE (1 << 5) +#define FD 0x3418 /* 32bit */ +#define DISPBDF 0x3424 /* 16bit */ +#define FD2 0x3428 /* 32bit */ +#define CG 0x341c /* 32bit */ + +/* Function Disable 1 RCBA 0x3418 */ +#define PCH_DISABLE_ALWAYS (1 << 0) +#define PCH_DISABLE_ADSPD (1 << 1) +#define PCH_DISABLE_SATA1 (1 << 2) +#define PCH_DISABLE_SMBUS (1 << 3) +#define PCH_DISABLE_HD_AUDIO (1 << 4) +#define PCH_DISABLE_EHCI2 (1 << 13) +#define PCH_DISABLE_LPC (1 << 14) +#define PCH_DISABLE_EHCI1 (1 << 15) +#define PCH_DISABLE_PCIE(x) (1 << (16 + x)) +#define PCH_DISABLE_THERMAL (1 << 24) +#define PCH_DISABLE_SATA2 (1 << 25) +#define PCH_DISABLE_XHCI (1 << 27) + +/* Function Disable 2 RCBA 0x3428 */ +#define PCH_DISABLE_KT (1 << 4) +#define PCH_DISABLE_IDER (1 << 3) +#define PCH_DISABLE_MEI2 (1 << 2) +#define PCH_DISABLE_MEI1 (1 << 1) +#define PCH_ENABLE_DBDF (1 << 0) + +/* ICH7 PMBASE */ +#define PM1_STS 0x00 +#define WAK_STS (1 << 15) +#define PCIEXPWAK_STS (1 << 14) +#define PRBTNOR_STS (1 << 11) +#define RTC_STS (1 << 10) +#define PWRBTN_STS (1 << 8) +#define GBL_STS (1 << 5) +#define BM_STS (1 << 4) +#define TMROF_STS (1 << 0) +#define PM1_EN 0x02 +#define PCIEXPWAK_DIS (1 << 14) +#define RTC_EN (1 << 10) +#define PWRBTN_EN (1 << 8) +#define GBL_EN (1 << 5) +#define TMROF_EN (1 << 0) +#define PM1_CNT 0x04 +#define SLP_EN (1 << 13) +#define SLP_TYP (7 << 10) +#define SLP_TYP_S0 0 +#define SLP_TYP_S1 1 +#define SLP_TYP_S3 5 +#define SLP_TYP_S4 6 +#define SLP_TYP_S5 7 +#define GBL_RLS (1 << 2) +#define BM_RLD (1 << 1) +#define SCI_EN (1 << 0) +#define PM1_TMR 0x08 +#define PROC_CNT 0x10 +#define LV2 0x14 +#define LV3 0x15 +#define LV4 0x16 +#define PM2_CNT 0x50 // mobile only +#define GPE0_STS 0x20 +#define PME_B0_STS (1 << 13) +#define PME_STS (1 << 11) +#define BATLOW_STS (1 << 10) +#define PCI_EXP_STS (1 << 9) +#define RI_STS (1 << 8) +#define SMB_WAK_STS (1 << 7) +#define TCOSCI_STS (1 << 6) +#define SWGPE_STS (1 << 2) +#define HOT_PLUG_STS (1 << 1) +#define GPE0_STS_2 0x24 +#define GPE0_EN 0x28 +#define PME_B0_EN (1 << 13) +#define PME_EN (1 << 11) +#define TCOSCI_EN (1 << 6) +#define GPE0_EN_2 0x2c +#define SMI_EN 0x30 +#define INTEL_USB2_EN (1 << 18) // Intel-Specific USB2 SMI logic +#define LEGACY_USB2_EN (1 << 17) // Legacy USB2 SMI logic +#define PERIODIC_EN (1 << 14) // SMI on PERIODIC_STS in SMI_STS +#define TCO_EN (1 << 13) // Enable TCO Logic (BIOSWE et al) +#define MCSMI_EN (1 << 11) // Trap microcontroller range access +#define BIOS_RLS (1 << 7) // asserts SCI on bit set +#define SWSMI_TMR_EN (1 << 6) // start software smi timer on bit set +#define APMC_EN (1 << 5) // Writes to APM_CNT cause SMI# +#define SLP_SMI_EN (1 << 4) // Write to SLP_EN in PM1_CNT asserts SMI# +#define LEGACY_USB_EN (1 << 3) // Legacy USB circuit SMI logic +#define BIOS_EN (1 << 2) // Assert SMI# on setting GBL_RLS bit +#define EOS (1 << 1) // End of SMI (deassert SMI#) +#define GBL_SMI_EN (1 << 0) // SMI# generation at all? +#define SMI_STS 0x34 +#define ALT_GP_SMI_EN 0x38 +#define ALT_GP_SMI_STS 0x3a +#define GPE_CNTL 0x42 +#define DEVACT_STS 0x44 +#define SS_CNT 0x50 +#define C3_RES 0x54 +#define TCO1_STS 0x64 +#define DMISCI_STS (1 << 9) +#define TCO2_STS 0x66 +#define ALT_GP_SMI_EN2 0x5c +#define ALT_GP_SMI_STS2 0x5e + +/* Lynxpoint LP */ +#define LP_GPE0_STS_1 0x80 /* GPIO 0-31 */ +#define LP_GPE0_STS_2 0x84 /* GPIO 32-63 */ +#define LP_GPE0_STS_3 0x88 /* GPIO 64-94 */ +#define LP_GPE0_STS_4 0x8c /* Standard GPE */ +#define LP_GPE0_EN_1 0x90 +#define LP_GPE0_EN_2 0x94 +#define LP_GPE0_EN_3 0x98 +#define LP_GPE0_EN_4 0x9c + +/* + * SPI Opcode Menu setup for SPIBAR lockdown + * should support most common flash chips. + */ + +#define SPIBAR_OFFSET 0x3800 +#define SPIBAR8(x) RCBA8(x + SPIBAR_OFFSET) +#define SPIBAR16(x) RCBA16(x + SPIBAR_OFFSET) +#define SPIBAR32(x) RCBA32(x + SPIBAR_OFFSET) + +/* Reigsters within the SPIBAR */ +#define SSFC 0x91 +#define FDOC 0xb0 +#define FDOD 0xb4 + +#define SPI_OPMENU_0 0x01 /* WRSR: Write Status Register */ +#define SPI_OPTYPE_0 0x01 /* Write, no address */ + +#define SPI_OPMENU_1 0x02 /* BYPR: Byte Program */ +#define SPI_OPTYPE_1 0x03 /* Write, address required */ + +#define SPI_OPMENU_2 0x03 /* READ: Read Data */ +#define SPI_OPTYPE_2 0x02 /* Read, address required */ + +#define SPI_OPMENU_3 0x05 /* RDSR: Read Status Register */ +#define SPI_OPTYPE_3 0x00 /* Read, no address */ + +#define SPI_OPMENU_4 0x20 /* SE20: Sector Erase 0x20 */ +#define SPI_OPTYPE_4 0x03 /* Write, address required */ + +#define SPI_OPMENU_5 0x9f /* RDID: Read ID */ +#define SPI_OPTYPE_5 0x00 /* Read, no address */ + +#define SPI_OPMENU_6 0xd8 /* BED8: Block Erase 0xd8 */ +#define SPI_OPTYPE_6 0x03 /* Write, address required */ + +#define SPI_OPMENU_7 0x0b /* FAST: Fast Read */ +#define SPI_OPTYPE_7 0x02 /* Read, address required */ + +#define SPI_OPMENU_UPPER ((SPI_OPMENU_7 << 24) | (SPI_OPMENU_6 << 16) | \ + (SPI_OPMENU_5 << 8) | SPI_OPMENU_4) +#define SPI_OPMENU_LOWER ((SPI_OPMENU_3 << 24) | (SPI_OPMENU_2 << 16) | \ + (SPI_OPMENU_1 << 8) | SPI_OPMENU_0) + +#define SPI_OPTYPE ((SPI_OPTYPE_7 << 14) | (SPI_OPTYPE_6 << 12) | \ + (SPI_OPTYPE_5 << 10) | (SPI_OPTYPE_4 << 8) | \ + (SPI_OPTYPE_3 << 6) | (SPI_OPTYPE_2 << 4) | \ + (SPI_OPTYPE_1 << 2) | (SPI_OPTYPE_0)) + +#define SPI_OPPREFIX ((0x50 << 8) | 0x06) /* EWSR and WREN */ + +#define SPIBAR_HSFS 0x3804 /* SPI hardware sequence status */ +#define SPIBAR_HSFS_SCIP (1 << 5) /* SPI Cycle In Progress */ +#define SPIBAR_HSFS_AEL (1 << 2) /* SPI Access Error Log */ +#define SPIBAR_HSFS_FCERR (1 << 1) /* SPI Flash Cycle Error */ +#define SPIBAR_HSFS_FDONE (1 << 0) /* SPI Flash Cycle Done */ +#define SPIBAR_HSFC 0x3806 /* SPI hardware sequence control */ +#define SPIBAR_HSFC_BYTE_COUNT(c) (((c - 1) & 0x3f) << 8) +#define SPIBAR_HSFC_CYCLE_READ (0 << 1) /* Read cycle */ +#define SPIBAR_HSFC_CYCLE_WRITE (2 << 1) /* Write cycle */ +#define SPIBAR_HSFC_CYCLE_ERASE (3 << 1) /* Erase cycle */ +#define SPIBAR_HSFC_GO (1 << 0) /* GO: start SPI transaction */ +#define SPIBAR_FADDR 0x3808 /* SPI flash address */ +#define SPIBAR_FDATA(n) (0x3810 + (4 * n)) /* SPI flash data */ + +#endif /* __ACPI__ */ +#endif /* SOUTHBRIDGE_INTEL_LYNXPOINT_PCH_H */ diff --git a/src/soc/intel/broadwell/broadwell/smbus.h b/src/soc/intel/broadwell/broadwell/smbus.h new file mode 100644 index 0000000000..81e594942d --- /dev/null +++ b/src/soc/intel/broadwell/broadwell/smbus.h @@ -0,0 +1,100 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2005 Yinghai Lu + * Copyright (C) 2009 coresystems GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "pch.h" + +static void smbus_delay(void) +{ + inb(0x80); +} + +static int smbus_wait_until_ready(u16 smbus_base) +{ + unsigned loops = SMBUS_TIMEOUT; + unsigned char byte; + do { + smbus_delay(); + if (--loops == 0) + break; + byte = inb(smbus_base + SMBHSTSTAT); + } while (byte & 1); + return loops ? 0 : -1; +} + +static int smbus_wait_until_done(u16 smbus_base) +{ + unsigned loops = SMBUS_TIMEOUT; + unsigned char byte; + do { + smbus_delay(); + if (--loops == 0) + break; + byte = inb(smbus_base + SMBHSTSTAT); + } while ((byte & 1) || (byte & ~((1 << 6) | (1 << 0))) == 0); + return loops ? 0 : -1; +} + +static int do_smbus_read_byte(unsigned smbus_base, unsigned device, unsigned address) +{ + unsigned char global_status_register; + unsigned char byte; + + if (smbus_wait_until_ready(smbus_base) < 0) { + return SMBUS_WAIT_UNTIL_READY_TIMEOUT; + } + /* Setup transaction */ + /* Disable interrupts */ + outb(inb(smbus_base + SMBHSTCTL) & (~1), smbus_base + SMBHSTCTL); + /* Set the device I'm talking too */ + outb(((device & 0x7f) << 1) | 1, smbus_base + SMBXMITADD); + /* Set the command/address... */ + outb(address & 0xff, smbus_base + SMBHSTCMD); + /* Set up for a byte data read */ + outb((inb(smbus_base + SMBHSTCTL) & 0xe3) | (0x2 << 2), + (smbus_base + SMBHSTCTL)); + /* Clear any lingering errors, so the transaction will run */ + outb(inb(smbus_base + SMBHSTSTAT), smbus_base + SMBHSTSTAT); + + /* Clear the data byte... */ + outb(0, smbus_base + SMBHSTDAT0); + + /* Start the command */ + outb((inb(smbus_base + SMBHSTCTL) | 0x40), + smbus_base + SMBHSTCTL); + + /* Poll for transaction completion */ + if (smbus_wait_until_done(smbus_base) < 0) { + return SMBUS_WAIT_UNTIL_DONE_TIMEOUT; + } + + global_status_register = inb(smbus_base + SMBHSTSTAT); + + /* Ignore the "In Use" status... */ + global_status_register &= ~(3 << 5); + + /* Read results of transaction */ + byte = inb(smbus_base + SMBHSTDAT0); + if (global_status_register != (1 << 1)) { + return SMBUS_ERROR; + } + return byte; +} + diff --git a/src/soc/intel/broadwell/broadwell/systemagent.h b/src/soc/intel/broadwell/broadwell/systemagent.h new file mode 100644 index 0000000000..55f6f284e4 --- /dev/null +++ b/src/soc/intel/broadwell/broadwell/systemagent.h @@ -0,0 +1,236 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2007-2008 coresystems GmbH + * Copyright (C) 2011 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __NORTHBRIDGE_INTEL_HASWELL_HASWELL_H__ +#define __NORTHBRIDGE_INTEL_HASWELL_HASWELL_H__ 1 + +/* Chipset types */ +#define HASWELL_MOBILE 0 +#define HASWELL_DESKTOP 1 +#define HASWELL_SERVER 2 + +/* Intel Enhanced Debug region */ +#define IED_SIZE CONFIG_IED_REGION_SIZE + +/* Northbridge BARs */ +#define DEFAULT_PCIEXBAR CONFIG_MMCONF_BASE_ADDRESS /* 4 KB per PCIe device */ +#define DEFAULT_MCHBAR 0xfed10000 /* 16 KB */ +#define DEFAULT_DMIBAR 0xfed18000 /* 4 KB */ +#define DEFAULT_EPBAR 0xfed19000 /* 4 KB */ + +#include + +/* Everything below this line is ignored in the DSDT */ +#ifndef __ACPI__ + +/* Device 0:0.0 PCI configuration space (Host Bridge) */ + +#define EPBAR 0x40 +#define MCHBAR 0x48 +#define PCIEXBAR 0x60 +#define DMIBAR 0x68 + +#define GGC 0x50 /* GMCH Graphics Control */ + +#define DEVEN 0x54 /* Device Enable */ +#define DEVEN_D7EN (1 << 14) +#define DEVEN_D4EN (1 << 7) +#define DEVEN_D3EN (1 << 5) +#define DEVEN_D2EN (1 << 4) +#define DEVEN_D1F0EN (1 << 3) +#define DEVEN_D1F1EN (1 << 2) +#define DEVEN_D1F2EN (1 << 1) +#define DEVEN_D0EN (1 << 0) + +#define PAM0 0x80 +#define PAM1 0x81 +#define PAM2 0x82 +#define PAM3 0x83 +#define PAM4 0x84 +#define PAM5 0x85 +#define PAM6 0x86 + +#define LAC 0x87 /* Legacy Access Control */ +#define SMRAM 0x88 /* System Management RAM Control */ +#define D_OPEN (1 << 6) +#define D_CLS (1 << 5) +#define D_LCK (1 << 4) +#define G_SMRAME (1 << 3) +#define C_BASE_SEG ((0 << 2) | (1 << 1) | (0 << 0)) + +#define MESEG_BASE 0x70 /* Management Engine Base. */ +#define MESEG_LIMIT 0x78 /* Management Engine Limit. */ +#define REMAPBASE 0x90 /* Remap base. */ +#define REMAPLIMIT 0x98 /* Remap limit. */ +#define TOM 0xa0 /* Top of DRAM in memory controller space. */ +#define TOUUD 0xa8 /* Top of Upper Usable DRAM */ +#define BDSM 0xb0 /* Base Data Stolen Memory */ +#define BGSM 0xb4 /* Base GTT Stolen Memory */ +#define TSEG 0xb8 /* TSEG base */ +#define TOLUD 0xbc /* Top of Low Used Memory */ + +#define SKPAD 0xdc /* Scratchpad Data */ + +/* Device 0:1.0 PCI configuration space (PCI Express) */ + +#define BCTRL1 0x3e /* 16bit */ + + +/* Device 0:2.0 PCI configuration space (Graphics Device) */ + +#define MSAC 0x62 /* Multi Size Aperture Control */ +#define SWSCI 0xe8 /* SWSCI enable */ +#define ASLS 0xfc /* OpRegion Base */ + +/* + * MCHBAR + */ + +#define MCHBAR8(x) *((volatile u8 *)(DEFAULT_MCHBAR + x)) +#define MCHBAR16(x) *((volatile u16 *)(DEFAULT_MCHBAR + x)) +#define MCHBAR32(x) *((volatile u32 *)(DEFAULT_MCHBAR + x)) +#define MCHBAR32_OR(x, or) MCHBAR32(x) = (MCHBAR32(x) | (or)) + +#define BIOS_RESET_CPL 0x5da8 /* 8bit */ + +/* Some power MSRs are also represented in MCHBAR */ +#define MCH_PKG_POWER_LIMIT_LO 0x59a0 +#define MCH_PKG_POWER_LIMIT_HI 0x59a4 +#define MCH_DDR_POWER_LIMIT_LO 0x58e0 +#define MCH_DDR_POWER_LIMIT_HI 0x58e4 + +/* + * EPBAR - Egress Port Root Complex Register Block + */ + +#define EPBAR8(x) *((volatile u8 *)(DEFAULT_EPBAR + x)) +#define EPBAR16(x) *((volatile u16 *)(DEFAULT_EPBAR + x)) +#define EPBAR32(x) *((volatile u32 *)(DEFAULT_EPBAR + x)) + +#define EPPVCCAP1 0x004 /* 32bit */ +#define EPPVCCAP2 0x008 /* 32bit */ + +#define EPVC0RCAP 0x010 /* 32bit */ +#define EPVC0RCTL 0x014 /* 32bit */ +#define EPVC0RSTS 0x01a /* 16bit */ + +#define EPVC1RCAP 0x01c /* 32bit */ +#define EPVC1RCTL 0x020 /* 32bit */ +#define EPVC1RSTS 0x026 /* 16bit */ + +#define EPVC1MTS 0x028 /* 32bit */ +#define EPVC1IST 0x038 /* 64bit */ + +#define EPESD 0x044 /* 32bit */ + +#define EPLE1D 0x050 /* 32bit */ +#define EPLE1A 0x058 /* 64bit */ +#define EPLE2D 0x060 /* 32bit */ +#define EPLE2A 0x068 /* 64bit */ + +#define PORTARB 0x100 /* 256bit */ + +/* + * DMIBAR + */ + +#define DMIBAR8(x) *((volatile u8 *)(DEFAULT_DMIBAR + x)) +#define DMIBAR16(x) *((volatile u16 *)(DEFAULT_DMIBAR + x)) +#define DMIBAR32(x) *((volatile u32 *)(DEFAULT_DMIBAR + x)) + +#define DMIVCECH 0x000 /* 32bit */ +#define DMIPVCCAP1 0x004 /* 32bit */ +#define DMIPVCCAP2 0x008 /* 32bit */ + +#define DMIPVCCCTL 0x00c /* 16bit */ + +#define DMIVC0RCAP 0x010 /* 32bit */ +#define DMIVC0RCTL0 0x014 /* 32bit */ +#define DMIVC0RSTS 0x01a /* 16bit */ + +#define DMIVC1RCAP 0x01c /* 32bit */ +#define DMIVC1RCTL 0x020 /* 32bit */ +#define DMIVC1RSTS 0x026 /* 16bit */ + +#define DMILE1D 0x050 /* 32bit */ +#define DMILE1A 0x058 /* 64bit */ +#define DMILE2D 0x060 /* 32bit */ +#define DMILE2A 0x068 /* 64bit */ + +#define DMILCAP 0x084 /* 32bit */ +#define DMILCTL 0x088 /* 16bit */ +#define DMILSTS 0x08a /* 16bit */ + +#define DMICTL1 0x0f0 /* 32bit */ +#define DMICTL2 0x0fc /* 32bit */ + +#define DMICC 0x208 /* 32bit */ + +#define DMIDRCCFG 0xeb4 /* 32bit */ + +#ifndef __ASSEMBLER__ +static inline void barrier(void) { asm("" ::: "memory"); } + +struct ied_header { + char signature[10]; + u32 size; + u8 reserved[34]; +} __attribute__ ((packed)); + +#define PCI_DEVICE_ID_HSW_MOBILE 0x0c04 +#define PCI_DEVICE_ID_HSW_ULT 0x0a04 + +#ifdef __SMM__ +void intel_northbridge_haswell_finalize_smm(void); +#else /* !__SMM__ */ +void haswell_early_initialization(int chipset_type); +void haswell_late_initialization(void); +void set_translation_table(int start, int end, u64 base, int inc); + +/* debugging functions */ +void print_pci_devices(void); +void dump_pci_device(unsigned dev); +void dump_pci_devices(void); +void dump_spd_registers(void); +void dump_mem(unsigned start, unsigned end); +void report_platform_info(void); +#endif /* !__SMM__ */ + + +#define MRC_DATA_ALIGN 0x1000 +#define MRC_DATA_SIGNATURE (('M'<<0)|('R'<<8)|('C'<<16)|('D'<<24)) + +struct mrc_data_container { + u32 mrc_signature; // "MRCD" + u32 mrc_data_size; // Actual total size of this structure + u32 mrc_checksum; // IP style checksum + u32 reserved; // For header alignment + u8 mrc_data[0]; // Variable size, platform/run time dependent. +} __attribute__ ((packed)); + +struct mrc_data_container *find_current_mrc_cache(void); +#if !defined(__PRE_RAM__) +#include "gma.h" +int init_igd_opregion(igd_opregion_t *igd_opregion); +#endif + +#endif +#endif +#endif diff --git a/src/soc/intel/broadwell/chip.h b/src/soc/intel/broadwell/chip.h new file mode 100644 index 0000000000..d60504c60a --- /dev/null +++ b/src/soc/intel/broadwell/chip.h @@ -0,0 +1,43 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2007-2008 coresystems GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * Digital Port Hotplug Enable: + * 0x04 = Enabled, 2ms short pulse + * 0x05 = Enabled, 4.5ms short pulse + * 0x06 = Enabled, 6ms short pulse + * 0x07 = Enabled, 100ms short pulse + */ +struct northbridge_intel_haswell_config { + u8 gpu_dp_b_hotplug; /* Digital Port B Hotplug Config */ + u8 gpu_dp_c_hotplug; /* Digital Port C Hotplug Config */ + u8 gpu_dp_d_hotplug; /* Digital Port D Hotplug Config */ + + u8 gpu_panel_port_select; /* 0=LVDS 1=DP_B 2=DP_C 3=DP_D */ + u8 gpu_panel_power_cycle_delay; /* T4 time sequence */ + u16 gpu_panel_power_up_delay; /* T1+T2 time sequence */ + u16 gpu_panel_power_down_delay; /* T3 time sequence */ + u16 gpu_panel_power_backlight_on_delay; /* T5 time sequence */ + u16 gpu_panel_power_backlight_off_delay; /* Tx time sequence */ + + u32 gpu_cpu_backlight; /* CPU Backlight PWM value */ + u32 gpu_pch_backlight; /* PCH Backlight PWM value */ +}; + +extern struct chip_operations northbridge_intel_haswell_ops; diff --git a/src/soc/intel/broadwell/cpu.c b/src/soc/intel/broadwell/cpu.c new file mode 100644 index 0000000000..2e49b261db --- /dev/null +++ b/src/soc/intel/broadwell/cpu.c @@ -0,0 +1,841 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2007-2009 coresystems GmbH + * Copyright (C) 2011 The ChromiumOS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "haswell.h" +#include "chip.h" + +/* Intel suggested latency times in units of 1024ns. */ +#define C_STATE_LATENCY_CONTROL_0_LIMIT 0x42 +#define C_STATE_LATENCY_CONTROL_1_LIMIT 0x73 +#define C_STATE_LATENCY_CONTROL_2_LIMIT 0x91 +#define C_STATE_LATENCY_CONTROL_3_LIMIT 0xe4 +#define C_STATE_LATENCY_CONTROL_4_LIMIT 0x145 +#define C_STATE_LATENCY_CONTROL_5_LIMIT 0x1ef + +#define C_STATE_LATENCY_MICRO_SECONDS(limit, base) \ + (((1 << ((base)*5)) * (limit)) / 1000) +#define C_STATE_LATENCY_FROM_LAT_REG(reg) \ + C_STATE_LATENCY_MICRO_SECONDS(C_STATE_LATENCY_CONTROL_ ##reg## _LIMIT, \ + (IRTL_1024_NS >> 10)) + +/* + * List of suported C-states in this processor. Only the ULT parts support C8, + * C9, and C10. + */ +enum { + C_STATE_C0, /* 0 */ + C_STATE_C1, /* 1 */ + C_STATE_C1E, /* 2 */ + C_STATE_C3, /* 3 */ + C_STATE_C6_SHORT_LAT, /* 4 */ + C_STATE_C6_LONG_LAT, /* 5 */ + C_STATE_C7_SHORT_LAT, /* 6 */ + C_STATE_C7_LONG_LAT, /* 7 */ + C_STATE_C7S_SHORT_LAT, /* 8 */ + C_STATE_C7S_LONG_LAT, /* 9 */ + C_STATE_C8, /* 10 */ + C_STATE_C9, /* 11 */ + C_STATE_C10, /* 12 */ + NUM_C_STATES +}; + +#define MWAIT_RES(state, sub_state) \ + { \ + .addrl = (((state) << 4) | (sub_state)), \ + .space_id = ACPI_ADDRESS_SPACE_FIXED, \ + .bit_width = ACPI_FFIXEDHW_VENDOR_INTEL, \ + .bit_offset = ACPI_FFIXEDHW_CLASS_MWAIT, \ + .access_size = ACPI_FFIXEDHW_FLAG_HW_COORD, \ + } + +static acpi_cstate_t cstate_map[NUM_C_STATES] = { + [C_STATE_C0] = { }, + [C_STATE_C1] = { + .latency = 0, + .power = 1000, + .resource = MWAIT_RES(0,0), + }, + [C_STATE_C1E] = { + .latency = 0, + .power = 1000, + .resource = MWAIT_RES(0,1), + }, + [C_STATE_C3] = { + .latency = C_STATE_LATENCY_FROM_LAT_REG(0), + .power = 900, + .resource = MWAIT_RES(1, 0), + }, + [C_STATE_C6_SHORT_LAT] = { + .latency = C_STATE_LATENCY_FROM_LAT_REG(1), + .power = 800, + .resource = MWAIT_RES(2, 0), + }, + [C_STATE_C6_LONG_LAT] = { + .latency = C_STATE_LATENCY_FROM_LAT_REG(2), + .power = 800, + .resource = MWAIT_RES(2, 1), + }, + [C_STATE_C7_SHORT_LAT] = { + .latency = C_STATE_LATENCY_FROM_LAT_REG(1), + .power = 700, + .resource = MWAIT_RES(3, 0), + }, + [C_STATE_C7_LONG_LAT] = { + .latency = C_STATE_LATENCY_FROM_LAT_REG(2), + .power = 700, + .resource = MWAIT_RES(3, 1), + }, + [C_STATE_C7S_SHORT_LAT] = { + .latency = C_STATE_LATENCY_FROM_LAT_REG(1), + .power = 700, + .resource = MWAIT_RES(3, 2), + }, + [C_STATE_C7S_LONG_LAT] = { + .latency = C_STATE_LATENCY_FROM_LAT_REG(2), + .power = 700, + .resource = MWAIT_RES(3, 3), + }, + [C_STATE_C8] = { + .latency = C_STATE_LATENCY_FROM_LAT_REG(3), + .power = 600, + .resource = MWAIT_RES(4, 0), + }, + [C_STATE_C9] = { + .latency = C_STATE_LATENCY_FROM_LAT_REG(4), + .power = 500, + .resource = MWAIT_RES(5, 0), + }, + [C_STATE_C10] = { + .latency = C_STATE_LATENCY_FROM_LAT_REG(5), + .power = 400, + .resource = MWAIT_RES(6, 0), + }, +}; + +/* 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 haswell_family_model(void) +{ + return cpuid_eax(1) & 0x0fff0ff0; +} + +int haswell_stepping(void) +{ + return cpuid_eax(1) & 0xf; +} + +/* Dynamically determine if the part is ULT. */ +int haswell_is_ult(void) +{ + static int ult = -1; + + if (ult < 0) + ult = !!(haswell_family_model() == HASWELL_FAMILY_ULT); + + return ult; +} + +/* 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 (!(MCHBAR32(BIOS_MAILBOX_INTERFACE) & MAILBOX_RUN_BUSY)) + return 0; + wait_count += delay_step; + udelay(delay_step); + } while (wait_count < 1000); + + return -1; +} + +static void calibrate_24mhz_bclk(void) +{ + int err_code; + + if (pcode_ready() < 0) { + printk(BIOS_ERR, "PCODE: mailbox timeout on wait ready.\n"); + return; + } + + /* A non-zero value initiates the PCODE calibration. */ + MCHBAR32(BIOS_MAILBOX_DATA) = ~0; + MCHBAR32(BIOS_MAILBOX_INTERFACE) = + MAILBOX_RUN_BUSY | MAILBOX_BIOS_CMD_FSM_MEASURE_INTVL; + + if (pcode_ready() < 0) { + printk(BIOS_ERR, "PCODE: mailbox timeout on completion.\n"); + return; + } + + err_code = MCHBAR32(BIOS_MAILBOX_INTERFACE) & 0xff; + + printk(BIOS_DEBUG, "PCODE: 24MHz BLCK calibration response: %d\n", + err_code); + + /* Read the calibrated value. */ + MCHBAR32(BIOS_MAILBOX_INTERFACE) = + MAILBOX_RUN_BUSY | MAILBOX_BIOS_CMD_READ_CALIBRATION; + + if (pcode_ready() < 0) { + printk(BIOS_ERR, "PCODE: mailbox timeout on read.\n"); + return; + } + + printk(BIOS_DEBUG, "PCODE: 24MHz BLCK calibration value: 0x%08x\n", + MCHBAR32(BIOS_MAILBOX_DATA)); +} + +static u32 pcode_mailbox_read(u32 command) +{ + if (pcode_ready() < 0) { + printk(BIOS_ERR, "PCODE: mailbox timeout on wait ready.\n"); + return 0; + } + + /* Send command and start transaction */ + MCHBAR32(BIOS_MAILBOX_INTERFACE) = command | MAILBOX_RUN_BUSY; + + if (pcode_ready() < 0) { + printk(BIOS_ERR, "PCODE: mailbox timeout on completion.\n"); + return 0; + } + + /* Read mailbox */ + return MCHBAR32(BIOS_MAILBOX_DATA); +} + +static void initialize_vr_config(void) +{ + msr_t msr; + + printk(BIOS_DEBUG, "Initializing VR config.\n"); + + /* Configure VR_CURRENT_CONFIG. */ + msr = rdmsr(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 |= (0x0f << (32 - 32)); /* PSI1 threshold - 15A. */ + + if (haswell_is_ult()) + msr.hi |= (1 << (62 - 32)); /* Enable PSI4 */ + /* Leave the max instantaneous current limit (12:0) to default. */ + wrmsr(MSR_VR_CURRENT_CONFIG, msr); + + /* Configure VR_MISC_CONFIG MSR. */ + msr = rdmsr(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 exit ramp rate to fast. */ + msr.hi |= (1 << (50 - 32)); + /* 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 to be fast ramp rate / 4 */ + msr.hi &= ~(0x3 << (53 - 32)); + msr.hi |= (0x01 << (53 - 32)); + /* Set MIN_VID (31:24) to allow CPU to have full control. */ + msr.lo &= ~0xff000000; + wrmsr(MSR_VR_MISC_CONFIG, msr); + + /* Configure VR_MISC_CONFIG2 MSR. */ + if (haswell_is_ult()) { + msr = rdmsr(MSR_VR_MISC_CONFIG2); + msr.lo &= ~0xffff; + /* Allow CPU to control minimum voltage completely (15:8) and + * set the fast ramp voltage to 1110mV (0x6f in 10mV steps). */ + msr.lo |= 0x006f; + wrmsr(MSR_VR_MISC_CONFIG2, msr); + } +} + +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); + + printk(BIOS_INFO, "PCH Power: PCODE Levels 0x%08x 0x%08x\n", + pch_power, pch_power_ext); + + pmsync = RCBA32(PMSYNC_CONFIG); + pmsync2 = RCBA32(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); + } + RCBA32(PMSYNC_CONFIG) = pmsync; + + /* 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); + } + RCBA32(PMSYNC_CONFIG2) = pmsync2; +} + +int cpu_config_tdp_levels(void) +{ + msr_t platform_info; + + /* Bits 34:33 indicate how many levels supported */ + platform_info = rdmsr(MSR_PLATFORM_INFO); + return (platform_info.hi >> 1) & 3; +} + +/* + * Configure processor power limits if possible + * This must be done AFTER set of BIOS_RESET_CPL + */ +void set_power_limits(u8 power_limit_1_time) +{ + msr_t msr = rdmsr(MSR_PLATFORM_INFO); + msr_t limit; + unsigned power_unit; + unsigned tdp, min_power, max_power, max_time; + u8 power_limit_1_val; + + if (power_limit_1_time > ARRAY_SIZE(power_limit_time_sec_to_msr)) + power_limit_1_time = 28; + + if (!(msr.lo & PLATFORM_INFO_SET_TDP)) + return; + + /* Get units */ + msr = rdmsr(MSR_PKG_POWER_SKU_UNIT); + power_unit = 2 << ((msr.lo & 0xf) - 1); + + /* Get power defaults for this SKU */ + msr = rdmsr(MSR_PKG_POWER_SKU); + tdp = msr.lo & 0x7fff; + min_power = (msr.lo >> 16) & 0x7fff; + max_power = msr.hi & 0x7fff; + max_time = (msr.hi >> 16) & 0x7f; + + printk(BIOS_DEBUG, "CPU TDP: %u Watts\n", tdp / power_unit); + + if (power_limit_time_msr_to_sec[max_time] > power_limit_1_time) + power_limit_1_time = power_limit_time_msr_to_sec[max_time]; + + if (min_power > 0 && tdp < min_power) + tdp = min_power; + + if (max_power > 0 && tdp > max_power) + tdp = max_power; + + power_limit_1_val = power_limit_time_sec_to_msr[power_limit_1_time]; + + /* Set long term power limit to TDP */ + limit.lo = 0; + limit.lo |= tdp & PKG_POWER_LIMIT_MASK; + limit.lo |= PKG_POWER_LIMIT_EN; + limit.lo |= (power_limit_1_val & PKG_POWER_LIMIT_TIME_MASK) << + PKG_POWER_LIMIT_TIME_SHIFT; + + /* Set short term power limit to 1.25 * TDP */ + limit.hi = 0; + limit.hi |= ((tdp * 125) / 100) & PKG_POWER_LIMIT_MASK; + limit.hi |= PKG_POWER_LIMIT_EN; + /* Power limit 2 time is only programmable on server SKU */ + + wrmsr(MSR_PKG_POWER_LIMIT, limit); + + /* Set power limit values in MCHBAR as well */ + MCHBAR32(MCH_PKG_POWER_LIMIT_LO) = limit.lo; + MCHBAR32(MCH_PKG_POWER_LIMIT_HI) = limit.hi; + + /* Set DDR RAPL power limit by copying from MMIO to MSR */ + msr.lo = MCHBAR32(MCH_DDR_POWER_LIMIT_LO); + msr.hi = MCHBAR32(MCH_DDR_POWER_LIMIT_HI); + wrmsr(MSR_DDR_RAPL_LIMIT, msr); + + /* Use nominal TDP values for CPUs with configurable TDP */ + if (cpu_config_tdp_levels()) { + msr = rdmsr(MSR_CONFIG_TDP_NOMINAL); + limit.hi = 0; + limit.lo = msr.lo & 0xff; + wrmsr(MSR_TURBO_ACTIVATION_RATIO, limit); + } +} + +static void configure_c_states(void) +{ + msr_t msr; + + msr = rdmsr(MSR_PMG_CST_CONFIG_CONTROL); + 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. */ + wrmsr(MSR_PMG_CST_CONFIG_CONTROL, msr); + + msr = rdmsr(MSR_PMG_IO_CAPTURE_BASE); + msr.lo &= ~0xffff; + msr.lo |= (get_pmbase() + 0x14); // LVL_2 base address + /* The deepest package c-state defaults to factory-configured value. */ + wrmsr(MSR_PMG_IO_CAPTURE_BASE, msr); + + msr = rdmsr(MSR_MISC_PWR_MGMT); + msr.lo &= ~(1 << 0); // Enable P-state HW_ALL coordination + wrmsr(MSR_MISC_PWR_MGMT, msr); + + msr = rdmsr(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# + wrmsr(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; + wrmsr(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; + wrmsr(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; + wrmsr(MSR_C_STATE_LATENCY_CONTROL_2, msr); + + /* Haswell ULT only supoprts the 3-5 latency response registers.*/ + if (haswell_is_ult()) { + /* 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; + wrmsr(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; + wrmsr(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; + wrmsr(MSR_C_STATE_LATENCY_CONTROL_5, msr); + } +} + +static void configure_thermal_target(void) +{ + struct cpu_intel_haswell_config *conf; + device_t lapic; + msr_t msr; + + /* Find pointer to CPU configuration */ + lapic = dev_find_lapic(SPEEDSTEP_APIC_MAGIC); + if (!lapic || !lapic->chip_info) + return; + conf = lapic->chip_info; + + /* Set TCC activaiton offset if supported */ + msr = rdmsr(MSR_PLATFORM_INFO); + if ((msr.lo & (1 << 30)) && conf->tcc_offset) { + msr = rdmsr(MSR_TEMPERATURE_TARGET); + msr.lo &= ~(0xf << 24); /* Bits 27:24 */ + msr.lo |= (conf->tcc_offset & 0xf) << 24; + wrmsr(MSR_TEMPERATURE_TARGET, msr); + } +} + +static void configure_misc(void) +{ + msr_t msr; + + msr = rdmsr(IA32_MISC_ENABLE); + msr.lo |= (1 << 0); /* Fast String enable */ + msr.lo |= (1 << 3); /* TM1/TM2/EMTTM enable */ + msr.lo |= (1 << 16); /* Enhanced SpeedStep Enable */ + wrmsr(IA32_MISC_ENABLE, msr); + + /* Disable Thermal interrupts */ + msr.lo = 0; + msr.hi = 0; + wrmsr(IA32_THERM_INTERRUPT, msr); + + /* Enable package critical interrupt only */ + msr.lo = 1 << 4; + msr.hi = 0; + wrmsr(IA32_PACKAGE_THERM_INTERRUPT, msr); +} + +static void enable_lapic_tpr(void) +{ + msr_t msr; + + msr = rdmsr(MSR_PIC_MSG_CONTROL); + msr.lo &= ~(1 << 10); /* Enable APIC TPR updates */ + wrmsr(MSR_PIC_MSG_CONTROL, msr); +} + +static void configure_dca_cap(void) +{ + struct cpuid_result cpuid_regs; + msr_t msr; + + /* Check feature flag in CPUID.(EAX=1):ECX[18]==1 */ + cpuid_regs = cpuid(1); + if (cpuid_regs.ecx & (1 << 18)) { + msr = rdmsr(IA32_PLATFORM_DCA_CAP); + msr.lo |= 1; + wrmsr(IA32_PLATFORM_DCA_CAP, msr); + } +} + +static void set_max_ratio(void) +{ + msr_t msr, perf_ctl; + + perf_ctl.hi = 0; + + /* Check for configurable TDP option */ + if (cpu_config_tdp_levels()) { + /* Set to nominal TDP ratio */ + msr = rdmsr(MSR_CONFIG_TDP_NOMINAL); + perf_ctl.lo = (msr.lo & 0xff) << 8; + } else { + /* Platform Info bits 15:8 give max ratio */ + msr = rdmsr(MSR_PLATFORM_INFO); + perf_ctl.lo = msr.lo & 0xff00; + } + wrmsr(IA32_PERF_CTL, perf_ctl); + + printk(BIOS_DEBUG, "haswell: frequency set to %d\n", + ((perf_ctl.lo >> 8) & 0xff) * HASWELL_BCLK); +} + +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 = rdmsr(IA32_ENERGY_PERFORMANCE_BIAS); + msr.lo &= ~0xf; + msr.lo |= policy & 0xf; + wrmsr(IA32_ENERGY_PERFORMANCE_BIAS, msr); + + printk(BIOS_DEBUG, "haswell: energy policy set to %u\n", + policy); +} + +static void configure_mca(void) +{ + msr_t msr; + const unsigned int mcg_cap_msr = 0x179; + int i; + int num_banks; + + msr = rdmsr(mcg_cap_msr); + num_banks = msr.lo & 0xff; + msr.lo = 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++) + wrmsr(IA32_MC0_STATUS + (i * 4), msr); +} + +#if CONFIG_USBDEBUG +static unsigned ehci_debug_addr; +#endif + +static void bsp_init_before_ap_bringup(struct bus *cpu_bus) +{ +#if CONFIG_USBDEBUG + // Is this caution really needed? + if(!ehci_debug_addr) + ehci_debug_addr = get_ehci_debug(); + set_ehci_debug(0); +#endif + + /* Setup MTRRs based on physical address size. */ + x86_setup_fixed_mtrrs(); + x86_setup_var_mtrrs(cpuid_eax(0x80000008) & 0xff, 2); + x86_mtrr_check(); + +#if CONFIG_USBDEBUG + set_ehci_debug(ehci_debug_addr); +#endif + + initialize_vr_config(); + + if (haswell_is_ult()) { + calibrate_24mhz_bclk(); + configure_pch_power_sharing(); + } +} + +/* All CPUs including BSP will run the following function. */ +static void haswell_init(device_t cpu) +{ + /* Clear out pending MCEs */ + configure_mca(); + + /* Enable the local cpu apics */ + enable_lapic_tpr(); + setup_lapic(); + + /* Configure C States */ + configure_c_states(); + + /* Configure Enhanced SpeedStep and Thermal Sensors */ + configure_misc(); + + /* Thermal throttle activation offset */ + configure_thermal_target(); + + /* Enable Direct Cache Access */ + configure_dca_cap(); + + /* Set energy policy */ + set_energy_perf_bias(ENERGY_POLICY_NORMAL); + + /* Set Max Ratio */ + set_max_ratio(); + + /* Enable Turbo */ + enable_turbo(); +} + +/* MP initialization support. */ +static const void *microcode_patch; +int ht_disabled; + +static int adjust_apic_id_ht_disabled(int index, int apic_id) +{ + return 2 * index; +} + +static void relocate_and_load_microcode(void *unused) +{ + /* Relocate the SMM handler. */ + smm_relocate(); + + /* After SMM relocation a 2nd microcode load is required. */ + intel_microcode_load_unlocked(microcode_patch); +} + +static void enable_smis(void *unused) +{ + /* Now that all APs have been relocated as well as the BSP let SMIs + * start flowing. */ + southbridge_smm_enable_smi(); + + /* Lock down the SMRAM space. */ + smm_lock(); +} + +static struct mp_flight_record mp_steps[] = { + MP_FR_NOBLOCK_APS(relocate_and_load_microcode, NULL, + relocate_and_load_microcode, NULL), + MP_FR_BLOCK_APS(mp_initialize_cpu, NULL, mp_initialize_cpu, NULL), + /* Wait for APs to finish initialization before proceeding. */ + MP_FR_BLOCK_APS(NULL, NULL, enable_smis, NULL), +}; + +void bsp_init_and_start_aps(struct bus *cpu_bus) +{ + int num_threads; + int num_cores; + msr_t msr; + struct mp_params mp_params; + + msr = rdmsr(CORE_THREAD_COUNT_MSR); + num_threads = (msr.lo >> 0) & 0xffff; + num_cores = (msr.lo >> 16) & 0xffff; + printk(BIOS_DEBUG, "CPU has %u cores, %u threads enabled.\n", + num_cores, num_threads); + + ht_disabled = num_threads == num_cores; + + /* Perform any necesarry BSP initialization before APs are brought up. + * This call alos allows the BSP to prepare for any secondary effects + * from calling cpu_initialize() such as smm_init(). */ + bsp_init_before_ap_bringup(cpu_bus); + + microcode_patch = intel_microcode_find(); + + mp_params.num_cpus = num_threads; + mp_params.parallel_microcode_load = 1; + if (ht_disabled) + mp_params.adjust_apic_id = adjust_apic_id_ht_disabled; + else + mp_params.adjust_apic_id = NULL; + mp_params.flight_plan = &mp_steps[0]; + mp_params.num_records = ARRAY_SIZE(mp_steps); + mp_params.microcode_pointer = microcode_patch; + + /* Load relocation and permeanent handlers. Then initiate relocation. */ + if (smm_initialize()) + printk(BIOS_CRIT, "SMM Initialiazation failed...\n"); + + if (mp_init(cpu_bus, &mp_params)) { + printk(BIOS_ERR, "MP initialization failure.\n"); + } + + /* Enable ROM caching if option was selected. */ + x86_mtrr_enable_rom_caching(); +} + +static struct device_operations cpu_dev_ops = { + .init = haswell_init, +}; + +static struct cpu_device_id cpu_table[] = { + { X86_VENDOR_INTEL, 0x306c1 }, /* Intel Haswell 4+2 A0 */ + { X86_VENDOR_INTEL, 0x306c2 }, /* Intel Haswell 4+2 B0 */ + { X86_VENDOR_INTEL, 0x40650 }, /* Intel Haswell ULT B0 */ + { X86_VENDOR_INTEL, 0x40651 }, /* Intel Haswell ULT B1 */ + { 0, 0 }, +}; + +static const struct cpu_driver driver __cpu_driver = { + .ops = &cpu_dev_ops, + .id_table = cpu_table, + .cstates = cstate_map, +}; + diff --git a/src/soc/intel/broadwell/ehci.c b/src/soc/intel/broadwell/ehci.c new file mode 100644 index 0000000000..e621e25fa7 --- /dev/null +++ b/src/soc/intel/broadwell/ehci.c @@ -0,0 +1,235 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008-2009 coresystems GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include "pch.h" +#include +#include + +#ifdef __SMM__ + +void usb_ehci_disable(device_t dev) +{ + u16 reg16; + u32 reg32; + + /* Set 0xDC[0]=1 */ + pci_or_config32(dev, 0xdc, (1 << 0)); + + /* Set D3Hot state and disable PME */ + reg16 = pci_read_config16(dev, EHCI_PWR_CTL_STS); + reg16 &= ~(PWR_CTL_ENABLE_PME | PWR_CTL_SET_MASK); + reg16 |= PWR_CTL_SET_D3; + pci_write_config16(dev, EHCI_PWR_CTL_STS, reg16); + + /* Clear memory and bus master */ + pci_write_config32(dev, PCI_BASE_ADDRESS_0, 0); + reg32 = pci_read_config32(dev, PCI_COMMAND); + reg32 &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + pci_write_config32(dev, PCI_COMMAND, reg32); + + /* Disable device */ + switch (dev) { + case PCH_EHCI1_DEV: + RCBA32_OR(FD, PCH_DISABLE_EHCI1); + break; + case PCH_EHCI2_DEV: + RCBA32_OR(FD, PCH_DISABLE_EHCI2); + break; + } +} + +/* Handler for EHCI controller on entry to S3/S4/S5 */ +void usb_ehci_sleep_prepare(device_t dev, u8 slp_typ) +{ + u32 reg32; + u32 bar0_base; + u16 pwr_state; + u16 pci_cmd; + + /* Check if the controller is disabled or not present */ + bar0_base = pci_read_config32(dev, PCI_BASE_ADDRESS_0); + if (bar0_base == 0 || bar0_base == 0xffffffff) + return; + pci_cmd = pci_read_config32(dev, PCI_COMMAND); + + switch (slp_typ) { + case SLP_TYP_S4: + case SLP_TYP_S5: + /* Check if controller is in D3 power state */ + pwr_state = pci_read_config16(dev, EHCI_PWR_CTL_STS); + if ((pwr_state & PWR_CTL_SET_MASK) == PWR_CTL_SET_D3) { + /* Put in D0 */ + u32 new_state = pwr_state & ~PWR_CTL_SET_MASK; + new_state |= PWR_CTL_SET_D0; + pci_write_config16(dev, EHCI_PWR_CTL_STS, new_state); + + /* Make sure memory bar is set */ + pci_write_config32(dev, PCI_BASE_ADDRESS_0, bar0_base); + + /* Make sure memory space is enabled */ + pci_write_config16(dev, PCI_COMMAND, pci_cmd | + PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY); + } + + /* + * If Run/Stop (bit0) is clear in USB2.0_CMD: + * - Clear Async Schedule Enable (bit5) and + * - Clear Periodic Schedule Enable (bit4) and + * - Set Run/Stop (bit0) + */ + reg32 = read32(bar0_base + EHCI_USB_CMD); + if (reg32 & EHCI_USB_CMD_RUN) { + reg32 &= ~(EHCI_USB_CMD_PSE | EHCI_USB_CMD_ASE); + reg32 |= EHCI_USB_CMD_RUN; + write32(bar0_base + EHCI_USB_CMD, reg32); + } + + /* Check for Port Enabled in PORTSC(0) (RMH) */ + reg32 = read32(bar0_base + EHCI_PORTSC(0)); + if (reg32 & EHCI_PORTSC_ENABLED) { + /* Set suspend bit in PORTSC if not already set */ + if (!(reg32 & EHCI_PORTSC_SUSPEND)) { + reg32 |= EHCI_PORTSC_SUSPEND; + write32(bar0_base + EHCI_PORTSC(0), reg32); + } + + /* Delay 25ms !! */ + udelay(25 * 1000); + + /* Clear Run/Stop bit */ + reg32 = read32(bar0_base + EHCI_USB_CMD); + reg32 &= EHCI_USB_CMD_RUN; + write32(bar0_base + EHCI_USB_CMD, reg32); + } + + /* Restore state to D3 if that is what it was at the start */ + if ((pwr_state & PWR_CTL_SET_MASK) == PWR_CTL_SET_D3) { + /* Restore pci command reg */ + pci_write_config16(dev, PCI_COMMAND, pci_cmd); + + /* Enable D3 */ + pci_write_config16(dev, EHCI_PWR_CTL_STS, pwr_state); + } + } +} + +#else /* !__SMM__ */ + +static void usb_ehci_clock_gating(struct device *dev) +{ + u32 reg32; + + /* IOBP 0xE5004001[7:6] = 11b */ + pch_iobp_update(0xe5004001, ~0, (1 << 7)|(1 << 6)); + + /* Dx:F0:DCh[5,2,1] = 111b + * Dx:F0:DCh[0] = 1b when EHCI controller is disabled */ + reg32 = pci_read_config32(dev, 0xdc); + reg32 |= (1 << 5) | (1 << 2) | (1 << 1); + pci_write_config32(dev, 0xdc, reg32); + + /* Dx:F0:78h[1:0] = 11b */ + reg32 = pci_read_config32(dev, 0x78); + reg32 |= (1 << 1) | (1 << 0); + pci_write_config32(dev, 0x78, reg32); +} + +static void usb_ehci_init(struct device *dev) +{ + printk(BIOS_DEBUG, "EHCI: Setting up controller.. "); + + usb_ehci_clock_gating(dev); + + /* Disable Wake on Disconnect in RMH */ + RCBA32_OR(0x35b0, 0x00000022); + + printk(BIOS_DEBUG, "done.\n"); +} + +static void usb_ehci_set_subsystem(device_t dev, unsigned vendor, unsigned device) +{ + u8 access_cntl; + + access_cntl = pci_read_config8(dev, 0x80); + + /* Enable writes to protected registers. */ + pci_write_config8(dev, 0x80, access_cntl | 1); + + if (!vendor || !device) { + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + pci_read_config32(dev, PCI_VENDOR_ID)); + } else { + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + ((device & 0xffff) << 16) | (vendor & 0xffff)); + } + + /* Restore protection. */ + pci_write_config8(dev, 0x80, access_cntl); +} + +static void usb_ehci_set_resources(struct device *dev) +{ +#if CONFIG_USBDEBUG + struct resource *res; + u32 base; + u32 usb_debug; + + usb_debug = get_ehci_debug(); + set_ehci_debug(0); +#endif + pci_dev_set_resources(dev); + +#if CONFIG_USBDEBUG + res = find_resource(dev, 0x10); + set_ehci_debug(usb_debug); + if (!res) return; + base = res->base; + set_ehci_base(base); + report_resource_stored(dev, res, ""); +#endif +} + +static struct pci_operations lops_pci = { + .set_subsystem = &usb_ehci_set_subsystem, +}; + +static struct device_operations usb_ehci_ops = { + .read_resources = pci_dev_read_resources, + .set_resources = usb_ehci_set_resources, + .enable_resources = pci_dev_enable_resources, + .init = usb_ehci_init, + .scan_bus = 0, + .ops_pci = &lops_pci, +}; + +static const unsigned short pci_device_ids[] = { 0x9c26, 0x8c26, 0x8c2d, 0 }; + +static const struct pci_driver pch_usb_ehci __pci_driver = { + .ops = &usb_ehci_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .devices = pci_device_ids, +}; + +#endif /* !__SMM__ */ diff --git a/src/soc/intel/broadwell/elog.c b/src/soc/intel/broadwell/elog.c new file mode 100644 index 0000000000..9ba3a981d3 --- /dev/null +++ b/src/soc/intel/broadwell/elog.c @@ -0,0 +1,187 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 The ChromiumOS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pch.h" + +static void pch_log_standard_gpe(u32 gpe0_sts_reg, u32 gpe0_en_reg) +{ + u32 gpe0_en = inl(get_pmbase() + gpe0_en_reg); + u32 gpe0_sts = inl(get_pmbase() + gpe0_sts_reg) & gpe0_en; + + /* PME (TODO: determine wake device) */ + if (gpe0_sts & (1 << 11)) + elog_add_event_wake(ELOG_WAKE_SOURCE_PME, 0); + + /* Internal PME (TODO: determine wake device) */ + if (gpe0_sts & (1 << 13)) + elog_add_event_wake(ELOG_WAKE_SOURCE_PME_INTERNAL, 0); + + /* SMBUS Wake */ + if (gpe0_sts & (1 << 7)) + elog_add_event_wake(ELOG_WAKE_SOURCE_SMBUS, 0); +} + +static void pch_log_gpio_gpe(u32 gpe0_sts_reg, u32 gpe0_en_reg, int start) +{ + /* GPE Bank 1 is GPIO 0-31 */ + u32 gpe0_en = inl(get_pmbase() + gpe0_en_reg); + u32 gpe0_sts = inl(get_pmbase() + gpe0_sts_reg) & gpe0_en; + int i; + + for (i = 0; i <= 31; i++) { + if (gpe0_sts & (1 << i)) + elog_add_event_wake(ELOG_WAKE_SOURCE_GPIO, i + start); + } +} + +static void pch_log_gpe(void) +{ + int i; + u16 pmbase = get_pmbase(); + u32 gpe0_sts, gpe0_en; + int gpe0_high_gpios[] = { + [0] = 27, + [24] = 17, + [25] = 19, + [26] = 21, + [27] = 22, + [28] = 43, + [29] = 56, + [30] = 57, + [31] = 60 + }; + + pch_log_standard_gpe(GPE0_EN, GPE0_STS); + + /* GPIO 0-15 */ + gpe0_en = inw(pmbase + GPE0_EN + 2); + gpe0_sts = inw(pmbase + GPE0_STS + 2) & gpe0_en; + for (i = 0; i <= 15; i++) { + if (gpe0_sts & (1 << i)) + elog_add_event_wake(ELOG_WAKE_SOURCE_GPIO, i); + } + + /* + * Now check and log upper status bits + */ + + gpe0_en = inl(pmbase + GPE0_EN_2); + gpe0_sts = inl(pmbase + GPE0_STS_2) & gpe0_en; + + for (i = 0; i <= 31; i++) { + if (!gpe0_high_gpios[i]) + continue; + if (gpe0_sts & (1 << i)) + elog_add_event_wake(ELOG_WAKE_SOURCE_GPIO, + gpe0_high_gpios[i]); + } +} + +static void pch_lp_log_gpe(void) +{ + /* Standard GPE are in GPE set 4 */ + pch_log_standard_gpe(LP_GPE0_STS_4, LP_GPE0_EN_4); + + /* Log GPIO events in set 1-3 */ + pch_log_gpio_gpe(LP_GPE0_STS_1, LP_GPE0_EN_1, 0); + pch_log_gpio_gpe(LP_GPE0_STS_2, LP_GPE0_EN_2, 32); + pch_log_gpio_gpe(LP_GPE0_STS_3, LP_GPE0_EN_3, 64); +} + +void pch_log_state(void) +{ + u16 pm1_sts, gen_pmcon_3, tco2_sts; + u8 gen_pmcon_2; + struct device *lpc = dev_find_slot(0, PCI_DEVFN(0x1f, 0)); + if (!lpc) + return; + + pm1_sts = inw(get_pmbase() + PM1_STS); + tco2_sts = inw(get_pmbase() + TCO2_STS); + gen_pmcon_2 = pci_read_config8(lpc, GEN_PMCON_2); + gen_pmcon_3 = pci_read_config16(lpc, GEN_PMCON_3); + + /* PWR_FLR Power Failure */ + if (gen_pmcon_2 & (1 << 0)) + elog_add_event(ELOG_TYPE_POWER_FAIL); + + /* SUS Well Power Failure */ + if (gen_pmcon_3 & (1 << 14)) + elog_add_event(ELOG_TYPE_SUS_POWER_FAIL); + + /* SYS_PWROK Failure */ + if (gen_pmcon_2 & (1 << 1)) + elog_add_event(ELOG_TYPE_SYS_PWROK_FAIL); + + /* PWROK Failure */ + if (gen_pmcon_2 & (1 << 0)) + elog_add_event(ELOG_TYPE_PWROK_FAIL); + + /* Second TCO Timeout */ + if (tco2_sts & (1 << 1)) + elog_add_event(ELOG_TYPE_TCO_RESET); + + /* Power Button Override */ + if (pm1_sts & (1 << 11)) + elog_add_event(ELOG_TYPE_POWER_BUTTON_OVERRIDE); + + /* System Reset Status (reset button pushed) */ + if (gen_pmcon_2 & (1 << 4)) + elog_add_event(ELOG_TYPE_RESET_BUTTON); + + /* General Reset Status */ + if (gen_pmcon_3 & (1 << 9)) + elog_add_event(ELOG_TYPE_SYSTEM_RESET); + + /* ACPI Wake */ + if (pm1_sts & (1 << 15)) + elog_add_event_byte(ELOG_TYPE_ACPI_WAKE, + acpi_slp_type == 3 ? 3 : 5); + + /* + * Wake sources + */ + + /* Power Button */ + if (pm1_sts & (1 << 8)) + elog_add_event_wake(ELOG_WAKE_SOURCE_PWRBTN, 0); + + /* RTC */ + if (pm1_sts & (1 << 10)) + elog_add_event_wake(ELOG_WAKE_SOURCE_RTC, 0); + + /* PCI Express (TODO: determine wake device) */ + if (pm1_sts & (1 << 14)) + elog_add_event_wake(ELOG_WAKE_SOURCE_PCIE, 0); + + /* GPE */ + if (pch_is_lp()) + pch_lp_log_gpe(); + else + pch_log_gpe(); +} diff --git a/src/soc/intel/broadwell/finalize.c b/src/soc/intel/broadwell/finalize.c new file mode 100644 index 0000000000..457f4af0fb --- /dev/null +++ b/src/soc/intel/broadwell/finalize.c @@ -0,0 +1,56 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 The Chromium OS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "haswell.h" + +#define PCI_DEV_HSW PCI_DEV(0, 0, 0) + +void intel_northbridge_haswell_finalize_smm(void) +{ + pci_or_config16(PCI_DEV_HSW, 0x50, 1 << 0); /* GGC */ + pci_or_config32(PCI_DEV_HSW, 0x5c, 1 << 0); /* DPR */ + pci_or_config32(PCI_DEV_HSW, 0x78, 1 << 10); /* ME */ + pci_or_config32(PCI_DEV_HSW, 0x90, 1 << 0); /* REMAPBASE */ + pci_or_config32(PCI_DEV_HSW, 0x98, 1 << 0); /* REMAPLIMIT */ + pci_or_config32(PCI_DEV_HSW, 0xa0, 1 << 0); /* TOM */ + pci_or_config32(PCI_DEV_HSW, 0xa8, 1 << 0); /* TOUUD */ + pci_or_config32(PCI_DEV_HSW, 0xb0, 1 << 0); /* BDSM */ + pci_or_config32(PCI_DEV_HSW, 0xb4, 1 << 0); /* BGSM */ + pci_or_config32(PCI_DEV_HSW, 0xb8, 1 << 0); /* TSEGMB */ + pci_or_config32(PCI_DEV_HSW, 0xbc, 1 << 0); /* TOLUD */ + + MCHBAR32_OR(0x5500, 1 << 0); /* PAVP */ + MCHBAR32_OR(0x5f00, 1 << 31); /* SA PM */ + MCHBAR32_OR(0x6020, 1 << 0); /* UMA GFX */ + MCHBAR32_OR(0x63fc, 1 << 0); /* VTDTRK */ + MCHBAR32_OR(0x6800, 1 << 31); + MCHBAR32_OR(0x7000, 1 << 31); + MCHBAR32_OR(0x77fc, 1 << 0); + + /* Memory Controller Lockdown */ + MCHBAR8(0x50fc) = 0x8f; + + /* Read+write the following */ + MCHBAR32(0x6030) = MCHBAR32(0x6030); + MCHBAR32(0x6034) = MCHBAR32(0x6034); + MCHBAR32(0x6008) = MCHBAR32(0x6008); +} diff --git a/src/soc/intel/broadwell/gpio.c b/src/soc/intel/broadwell/gpio.c new file mode 100644 index 0000000000..20a9e0389f --- /dev/null +++ b/src/soc/intel/broadwell/gpio.c @@ -0,0 +1,168 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include + +#include "pch.h" +#include "lp_gpio.h" + +static u16 get_gpio_base(void) +{ +#if defined(__PRE_RAM__) || defined(__SMM__) + return pci_read_config16(PCH_LPC_DEV, GPIO_BASE) & 0xfffc; +#else + return pci_read_config16(dev_find_slot(0, PCI_DEVFN(0x1f, 0)), + GPIO_BASE) & 0xfffc; +#endif +} + + +/* + * This function will return a number that indicates which PIRQ + * this GPIO maps to. If this is not a PIRQ capable GPIO then + * it will return -1. The GPIO to PIRQ mapping is not linear. + */ +static int lp_gpio_to_pirq(int gpio) +{ + switch (gpio) { + case 8: return 0; /* PIRQI */ + case 9: return 1; /* PIRQJ */ + case 10: return 2; /* PIRQK */ + case 13: return 3; /* PIRQL */ + case 14: return 4; /* PIRQM */ + case 45: return 5; /* PIRQN */ + case 46: return 6; /* PIRQO */ + case 47: return 7; /* PIRQP */ + case 48: return 8; /* PIRQQ */ + case 49: return 9; /* PIRQR */ + case 50: return 10; /* PIRQS */ + case 51: return 11; /* PIRQT */ + case 52: return 12; /* PIRQU */ + case 53: return 13; /* PIRQV */ + case 54: return 14; /* PIRQW */ + case 55: return 15; /* PIRQX */ + default: return -1; + }; +} + +void setup_pch_lp_gpios(const struct pch_lp_gpio_map map[]) +{ + u16 gpio_base = get_gpio_base(); + const struct pch_lp_gpio_map *config; + u32 owner[3] = {0}; + u32 route[3] = {0}; + u32 irqen[3] = {0}; + u32 reset[3] = {0}; + u32 blink = 0; + u16 pirq2apic = 0; + int set, bit, gpio = 0; + + for (config = map; config->conf0 != GPIO_LIST_END; config++, gpio++) { + if (gpio > MAX_GPIO_NUMBER) + break; + + /* Setup Configuration registers 1 and 2 */ + outl(config->conf0, gpio_base + GPIO_CONFIG0(gpio)); + outl(config->conf1, gpio_base + GPIO_CONFIG1(gpio)); + + /* Determine set and bit based on GPIO number */ + set = gpio >> 5; + bit = gpio % 32; + + /* Apply settings to set specific bits */ + owner[set] |= config->owner << bit; + route[set] |= config->route << bit; + irqen[set] |= config->irqen << bit; + reset[set] |= config->reset << bit; + + if (set == 0) + blink |= config->blink << bit; + + /* PIRQ to IO-APIC map */ + if (config->pirq == GPIO_PIRQ_APIC_ROUTE) { + set = lp_gpio_to_pirq(gpio); + if (set >= 0) + pirq2apic |= 1 << set; + } + } + + for (set = 0; set <= 2; set++) { + outl(owner[set], gpio_base + GPIO_OWNER(set)); + outl(route[set], gpio_base + GPIO_ROUTE(set)); + outl(irqen[set], gpio_base + GPIO_IRQ_IE(set)); + outl(reset[set], gpio_base + GPIO_RESET(set)); + } + + outl(blink, gpio_base + GPIO_BLINK); + outl(pirq2apic, gpio_base + GPIO_PIRQ_APIC_EN); +} + +int get_gpio(int gpio_num) +{ + u16 gpio_base = get_gpio_base(); + + if (gpio_num > MAX_GPIO_NUMBER) + return 0; + + return !!(inl(gpio_base + GPIO_CONFIG0(gpio_num)) & GPI_LEVEL); +} + +/* + * get a number comprised of multiple GPIO values. gpio_num_array points to + * the array of gpio pin numbers to scan, terminated by -1. + */ +unsigned get_gpios(const int *gpio_num_array) +{ + int gpio; + unsigned bitmask = 1; + unsigned vector = 0; + + while (bitmask && + ((gpio = *gpio_num_array++) != -1)) { + if (get_gpio(gpio)) + vector |= bitmask; + bitmask <<= 1; + } + return vector; +} + +void set_gpio(int gpio_num, int value) +{ + u16 gpio_base = get_gpio_base(); + u32 conf0; + + if (gpio_num > MAX_GPIO_NUMBER) + return; + + conf0 = inl(gpio_base + GPIO_CONFIG0(gpio_num)); + conf0 &= ~GPO_LEVEL_MASK; + conf0 |= value << GPO_LEVEL_SHIFT; + outl(conf0, gpio_base + GPIO_CONFIG0(gpio_num)); +} + +int gpio_is_native(int gpio_num) +{ + u16 gpio_base = get_gpio_base(); + + return !(inl(gpio_base + GPIO_CONFIG0(gpio_num)) & 1); +} diff --git a/src/soc/intel/broadwell/hda.c b/src/soc/intel/broadwell/hda.c new file mode 100644 index 0000000000..efb8e9868f --- /dev/null +++ b/src/soc/intel/broadwell/hda.c @@ -0,0 +1,196 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008 Advanced Micro Devices, Inc. + * Copyright (C) 2008-2009 coresystems GmbH + * Copyright (C) 2011 The ChromiumOS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include "pch.h" +#include "hda_verb.h" + +const u32 * cim_verb_data = NULL; +u32 cim_verb_data_size = 0; +const u32 * pc_beep_verbs = NULL; +u32 pc_beep_verbs_size = 0; + +static void codecs_init(u32 base, u32 codec_mask) +{ + int i; + + /* Can support up to 4 codecs */ + for (i = 3; i >= 0; i--) { + if (codec_mask & (1 << i)) + hda_codec_init(base, i, + cim_verb_data_size, + cim_verb_data); + } + + if (pc_beep_verbs_size && pc_beep_verbs) + hda_codec_write(base, pc_beep_verbs_size, pc_beep_verbs); +} + +static void azalia_pch_init(struct device *dev, u32 base) +{ + u8 reg8; + u16 reg16; + u32 reg32; + + if (RCBA32(0x2030) & (1 << 31)) { + reg32 = pci_mmio_read_config32(dev, 0x120); + reg32 &= 0xf8ffff01; + reg32 |= (1 << 25); + reg32 |= RCBA32(0x2030) & 0xfe; + pci_mmio_write_config32(dev, 0x120, reg32); + + if (!pch_is_lp()) { + reg16 = pci_mmio_read_config16(dev, 0x78); + reg16 &= ~(1 << 11); + pci_mmio_write_config16(dev, 0x78, reg16); + } + } else + printk(BIOS_DEBUG, "Azalia: V1CTL disabled.\n"); + + reg32 = pci_mmio_read_config32(dev, 0x114); + reg32 &= ~0xfe; + pci_mmio_write_config32(dev, 0x114, reg32); + + // Set VCi enable bit + if (pci_mmio_read_config32(dev, 0x120) & ((1 << 24) | + (1 << 25) | (1 << 26))) { + reg32 = pci_mmio_read_config32(dev, 0x120); + if (pch_is_lp()) + reg32 &= ~(1 << 31); + else + reg32 |= (1 << 31); + pci_mmio_write_config32(dev, 0x120, reg32); + } + + reg8 = pci_read_config8(dev, 0x43); + if (pch_is_lp()) + reg8 &= ~(1 << 6); + else + reg8 |= (1 << 4); + pci_write_config8(dev, 0x43, reg8); + + if (!pch_is_lp()) { + reg32 = pci_read_config32(dev, 0xc0); + reg32 |= (1 << 17); + pci_write_config32(dev, 0xc0, reg32); + } + + /* Additional programming steps */ + reg32 = pci_read_config32(dev, 0xc4); + if (pch_is_lp()) + reg32 |= (1 << 24); + else + reg32 |= (1 << 14); + pci_write_config32(dev, 0xc4, reg32); + + if (!pch_is_lp()) { + reg32 = pci_read_config32(dev, 0xd0); + reg32 &= ~(1 << 31); + pci_write_config32(dev, 0xd0, reg32); + } + + reg8 = pci_read_config8(dev, 0x40); // Audio Control + reg8 |= 1; // Select Azalia mode + pci_write_config8(dev, 0x40, reg8); + + reg8 = pci_read_config8(dev, 0x4d); // Docking Status + reg8 &= ~(1 << 7); // Docking not supported + pci_write_config8(dev, 0x4d, reg8); + + if (pch_is_lp()) { + reg16 = read32(base + 0x0012); + reg16 |= (1 << 0); + write32(base + 0x0012, reg16); + + /* disable Auto Voltage Detector */ + reg8 = pci_read_config8(dev, 0x42); + reg8 |= (1 << 2); + pci_write_config8(dev, 0x42, reg8); + } +} + +static void azalia_init(struct device *dev) +{ + u32 base; + struct resource *res; + u32 codec_mask; + u32 reg32; + + /* Find base address */ + res = find_resource(dev, PCI_BASE_ADDRESS_0); + if (!res) + return; + + base = (u32)res->base; + printk(BIOS_DEBUG, "Azalia: base = %08x\n", (u32)base); + + /* Set Bus Master */ + reg32 = pci_read_config32(dev, PCI_COMMAND); + pci_write_config32(dev, PCI_COMMAND, reg32 | PCI_COMMAND_MASTER); + + azalia_pch_init(dev, base); + + codec_mask = hda_codec_detect(base); + + if (codec_mask) { + printk(BIOS_DEBUG, "Azalia: codec_mask = %02x\n", codec_mask); + codecs_init(base, codec_mask); + } +} + +static void azalia_set_subsystem(device_t dev, unsigned vendor, unsigned device) +{ + if (!vendor || !device) { + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + pci_read_config32(dev, PCI_VENDOR_ID)); + } else { + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + ((device & 0xffff) << 16) | (vendor & 0xffff)); + } +} + +static struct pci_operations azalia_pci_ops = { + .set_subsystem = azalia_set_subsystem, +}; + +static struct device_operations azalia_ops = { + .read_resources = pci_dev_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .init = azalia_init, + .scan_bus = 0, + .ops_pci = &azalia_pci_ops, +}; + +static const unsigned short pci_device_ids[] = { 0x8c20, 0x9c20, 0 }; + +static const struct pci_driver pch_azalia __pci_driver = { + .ops = &azalia_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .devices = pci_device_ids, +}; + diff --git a/src/soc/intel/broadwell/igd.c b/src/soc/intel/broadwell/igd.c new file mode 100644 index 0000000000..f8351e2238 --- /dev/null +++ b/src/soc/intel/broadwell/igd.c @@ -0,0 +1,538 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2012 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "chip.h" +#include "haswell.h" + +#if CONFIG_CHROMEOS +#include +#endif + +struct gt_reg { + u32 reg; + u32 andmask; + u32 ormask; +}; + +static const struct gt_reg haswell_gt_setup[] = { + /* Enable Counters */ + { 0x0a248, 0x00000000, 0x00000016 }, + { 0x0a000, 0x00000000, 0x00070020 }, + { 0x0a180, 0xff3fffff, 0x15000000 }, + /* Enable DOP Clock Gating */ + { 0x09424, 0x00000000, 0x000003fd }, + /* Enable Unit Level Clock Gating */ + { 0x09400, 0x00000000, 0x00000080 }, + { 0x09404, 0x00000000, 0x40401000 }, + { 0x09408, 0x00000000, 0x00000000 }, + { 0x0940c, 0x00000000, 0x02000001 }, + { 0x0a008, 0x00000000, 0x08000000 }, + /* Wake Rate Limits */ + { 0x0a090, 0xffffffff, 0x00000000 }, + { 0x0a098, 0xffffffff, 0x03e80000 }, + { 0x0a09c, 0xffffffff, 0x00280000 }, + { 0x0a0a8, 0xffffffff, 0x0001e848 }, + { 0x0a0ac, 0xffffffff, 0x00000019 }, + /* Render/Video/Blitter Idle Max Count */ + { 0x02054, 0x00000000, 0x0000000a }, + { 0x12054, 0x00000000, 0x0000000a }, + { 0x22054, 0x00000000, 0x0000000a }, + /* RC Sleep / RCx Thresholds */ + { 0x0a0b0, 0xffffffff, 0x00000000 }, + { 0x0a0b4, 0xffffffff, 0x000003e8 }, + { 0x0a0b8, 0xffffffff, 0x0000c350 }, + /* RP Settings */ + { 0x0a010, 0xffffffff, 0x000f4240 }, + { 0x0a014, 0xffffffff, 0x12060000 }, + { 0x0a02c, 0xffffffff, 0x0000e808 }, + { 0x0a030, 0xffffffff, 0x0003bd08 }, + { 0x0a068, 0xffffffff, 0x000101d0 }, + { 0x0a06c, 0xffffffff, 0x00055730 }, + { 0x0a070, 0xffffffff, 0x0000000a }, + /* RP Control */ + { 0x0a024, 0x00000000, 0x00000b92 }, + /* HW RC6 Control */ + { 0x0a090, 0x00000000, 0x88040000 }, + /* Video Frequency Request */ + { 0x0a00c, 0x00000000, 0x08000000 }, + { 0 }, +}; + +static const struct gt_reg haswell_gt_lock[] = { + { 0x0a248, 0xffffffff, 0x80000000 }, + { 0x0a004, 0xffffffff, 0x00000010 }, + { 0x0a080, 0xffffffff, 0x00000004 }, + { 0x0a180, 0xffffffff, 0x80000000 }, + { 0 }, +}; + +/* some vga option roms are used for several chipsets but they only have one + * PCI ID in their header. If we encounter such an option rom, we need to do + * the mapping ourselfes + */ + +u32 map_oprom_vendev(u32 vendev) +{ + u32 new_vendev=vendev; + + switch (vendev) { + case 0x80860402: /* GT1 Desktop */ + case 0x80860406: /* GT1 Mobile */ + case 0x8086040a: /* GT1 Server */ + case 0x80860a06: /* GT1 ULT */ + + case 0x80860412: /* GT2 Desktop */ + case 0x80860416: /* GT2 Mobile */ + case 0x8086041a: /* GT2 Server */ + case 0x80860a16: /* GT2 ULT */ + + case 0x80860422: /* GT3 Desktop */ + case 0x80860426: /* GT3 Mobile */ + case 0x8086042a: /* GT3 Server */ + case 0x80860a26: /* GT3 ULT */ + + new_vendev=0x80860406; /* GT1 Mobile */ + break; + } + + return new_vendev; +} + +/* GTT is the Global Translation Table for the graphics pipeline. + * It is used to translate graphics addresses to physical + * memory addresses. As in the CPU, GTTs map 4K pages. + * The setgtt function adds a further bit of flexibility: + * it allows you to set a range (the first two parameters) to point + * to a physical address (third parameter);the physical address is + * incremented by a count (fourth parameter) for each GTT in the + * range. + * Why do it this way? For ultrafast startup, + * we can point all the GTT entries to point to one page, + * and set that page to 0s: + * memset(physbase, 0, 4096); + * setgtt(0, 4250, physbase, 0); + * this takes about 2 ms, and is a win because zeroing + * the page takes a up to 200 ms. + * This call sets the GTT to point to a linear range of pages + * starting at physbase. + */ + +#define GTT_PTE_BASE (2 << 20) + +void +set_translation_table(int start, int end, u64 base, int inc) +{ + int i; + + for(i = start; i < end; i++){ + u64 physical_address = base + i*inc; + /* swizzle the 32:39 bits to 4:11 */ + u32 word = physical_address | ((physical_address >> 28) & 0xff0) | 1; + /* note: we've confirmed by checking + * the values that mrc does no + * useful setup before we run this. + */ + gtt_write(GTT_PTE_BASE + i * 4, word); + gtt_read(GTT_PTE_BASE + i * 4); + } +} + +static struct resource *gtt_res = NULL; + +unsigned long gtt_read(unsigned long reg) +{ + u32 val; + val = read32(gtt_res->base + reg); + return val; + +} + +void gtt_write(unsigned long reg, unsigned long data) +{ + write32(gtt_res->base + reg, data); +} + +static inline void gtt_rmw(u32 reg, u32 andmask, u32 ormask) +{ + u32 val = gtt_read(reg); + val &= andmask; + val |= ormask; + gtt_write(reg, val); +} + +static inline void gtt_write_regs(const struct gt_reg *gt) +{ + for (; gt && gt->reg; gt++) { + if (gt->andmask) + gtt_rmw(gt->reg, gt->andmask, gt->ormask); + else + gtt_write(gt->reg, gt->ormask); + } +} + +#define GTT_RETRY 1000 +int gtt_poll(u32 reg, u32 mask, u32 value) +{ + unsigned try = GTT_RETRY; + u32 data; + + while (try--) { + data = gtt_read(reg); + if ((data & mask) == value) + return 1; + udelay(10); + } + + printk(BIOS_ERR, "GT init timeout\n"); + return 0; +} + +static void power_well_enable(void) +{ + gtt_write(HSW_PWR_WELL_CTL1, HSW_PWR_WELL_ENABLE); + gtt_poll(HSW_PWR_WELL_CTL1, HSW_PWR_WELL_STATE, HSW_PWR_WELL_STATE); +#if CONFIG_MAINBOARD_DO_NATIVE_VGA_INIT + /* In the native graphics case, we've got about 20 ms. + * after we power up the the AUX channel until we can talk to it. + * So get that going right now. We can't turn on the panel, yet, just VDD. + */ + gtt_write(PCH_PP_CONTROL, PCH_PP_UNLOCK| EDP_FORCE_VDD | PANEL_POWER_RESET); +#endif +} + +static void gma_pm_init_pre_vbios(struct device *dev) +{ + printk(BIOS_DEBUG, "GT Power Management Init\n"); + + gtt_res = find_resource(dev, PCI_BASE_ADDRESS_0); + if (!gtt_res || !gtt_res->base) + return; + + power_well_enable(); + + /* + * Enable RC6 + */ + + /* Enable Force Wake */ + gtt_write(0x0a180, 1 << 5); + gtt_write(0x0a188, 0x00010001); + gtt_poll(0x130044, 1 << 0, 1 << 0); + + /* GT Settings */ + gtt_write_regs(haswell_gt_setup); + + /* Wait for Mailbox Ready */ + gtt_poll(0x138124, (1 << 31), (0 << 31)); + /* Mailbox Data - RC6 VIDS */ + gtt_write(0x138128, 0x00000000); + /* Mailbox Command */ + gtt_write(0x138124, 0x80000004); + /* Wait for Mailbox Ready */ + gtt_poll(0x138124, (1 << 31), (0 << 31)); + + /* Enable PM Interrupts */ + gtt_write(GEN6_PMIER, GEN6_PM_MBOX_EVENT | GEN6_PM_THERMAL_EVENT | + GEN6_PM_RP_DOWN_TIMEOUT | GEN6_PM_RP_UP_THRESHOLD | + GEN6_PM_RP_DOWN_THRESHOLD | GEN6_PM_RP_UP_EI_EXPIRED | + GEN6_PM_RP_DOWN_EI_EXPIRED); + + /* Enable RC6 in idle */ + gtt_write(0x0a094, 0x00040000); + + /* PM Lock Settings */ + gtt_write_regs(haswell_gt_lock); +} + +static void init_display_planes(void) +{ + int pipe, plane; + + /* Disable cursor mode */ + for (pipe = PIPE_A; pipe <= PIPE_C; pipe++) { + gtt_write(CURCNTR_IVB(pipe), CURSOR_MODE_DISABLE); + gtt_write(CURBASE_IVB(pipe), 0x00000000); + } + + /* Disable primary plane and set surface base address*/ + for (plane = PLANE_A; plane <= PLANE_C; plane++) { + gtt_write(DSPCNTR(plane), DISPLAY_PLANE_DISABLE); + gtt_write(DSPSURF(plane), 0x00000000); + } + + /* Disable VGA display */ + gtt_write(CPU_VGACNTRL, CPU_VGA_DISABLE); +} + +static void gma_setup_panel(struct device *dev) +{ + struct northbridge_intel_haswell_config *conf = dev->chip_info; + u32 reg32; + + printk(BIOS_DEBUG, "GT Power Management Init (post VBIOS)\n"); + + /* Setup Digital Port Hotplug */ + reg32 = gtt_read(PCH_PORT_HOTPLUG); + if (!reg32) { + reg32 = (conf->gpu_dp_b_hotplug & 0x7) << 2; + reg32 |= (conf->gpu_dp_c_hotplug & 0x7) << 10; + reg32 |= (conf->gpu_dp_d_hotplug & 0x7) << 18; + gtt_write(PCH_PORT_HOTPLUG, reg32); + } + + /* Setup Panel Power On Delays */ + reg32 = gtt_read(PCH_PP_ON_DELAYS); + if (!reg32) { + reg32 = (conf->gpu_panel_port_select & 0x3) << 30; + reg32 |= (conf->gpu_panel_power_up_delay & 0x1fff) << 16; + reg32 |= (conf->gpu_panel_power_backlight_on_delay & 0x1fff); + gtt_write(PCH_PP_ON_DELAYS, reg32); + } + + /* Setup Panel Power Off Delays */ + reg32 = gtt_read(PCH_PP_OFF_DELAYS); + if (!reg32) { + reg32 = (conf->gpu_panel_power_down_delay & 0x1fff) << 16; + reg32 |= (conf->gpu_panel_power_backlight_off_delay & 0x1fff); + gtt_write(PCH_PP_OFF_DELAYS, reg32); + } + + /* Setup Panel Power Cycle Delay */ + if (conf->gpu_panel_power_cycle_delay) { + reg32 = gtt_read(PCH_PP_DIVISOR); + reg32 &= ~0xff; + reg32 |= conf->gpu_panel_power_cycle_delay & 0xff; + gtt_write(PCH_PP_DIVISOR, reg32); + } + + /* Enable Backlight if needed */ + if (conf->gpu_cpu_backlight) { + gtt_write(BLC_PWM_CPU_CTL2, BLC_PWM2_ENABLE); + gtt_write(BLC_PWM_CPU_CTL, conf->gpu_cpu_backlight); + } + if (conf->gpu_pch_backlight) { + gtt_write(BLC_PWM_PCH_CTL1, BLM_PCH_PWM_ENABLE); + gtt_write(BLC_PWM_PCH_CTL2, conf->gpu_pch_backlight); + } + + /* Get display,pipeline,and DDI registers into a basic sane state */ + power_well_enable(); + + init_display_planes(); + + /* DDI-A params set: + bit 0: Display detected (RO) + bit 4: DDI A supports 4 lanes and DDI E is not used + bit 7: DDI buffer is idle + */ + gtt_write(DDI_BUF_CTL_A, DDI_BUF_IS_IDLE | DDI_A_4_LANES | DDI_INIT_DISPLAY_DETECTED); + + /* Set FDI registers - is this required? */ + gtt_write(_FDI_RXA_MISC, 0x00200090); + gtt_write(_FDI_RXA_MISC, 0x0a000000); + + /* Enable the handshake with PCH display when processing reset */ + gtt_write(NDE_RSTWRN_OPT, RST_PCH_HNDSHK_EN); + + /* undocumented */ + gtt_write(0x42090, 0x04000000); + gtt_write(0x9840, 0x00000000); + gtt_write(0x42090, 0xa4000000); + + gtt_write(SOUTH_DSPCLK_GATE_D, PCH_LP_PARTITION_LEVEL_DISABLE); + + /* undocumented */ + gtt_write(0x42080, 0x00004000); + + /* Prepare DDI buffers for DP and FDI */ + intel_prepare_ddi(); + + /* Hot plug detect buffer enabled for port A */ + gtt_write(DIGITAL_PORT_HOTPLUG_CNTRL, DIGITAL_PORTA_HOTPLUG_ENABLE); + + /* Enable HPD buffer for digital port D and B */ + gtt_write(PCH_PORT_HOTPLUG, PORTD_HOTPLUG_ENABLE | PORTB_HOTPLUG_ENABLE); + + /* Bits 4:0 - Power cycle delay (default 0x6 --> 500ms) + Bits 31:8 - Reference divider (0x0004af ----> 24MHz) + */ + gtt_write(PCH_PP_DIVISOR, 0x0004af06); +} + +static void gma_pm_init_post_vbios(struct device *dev) +{ + int cdclk = 0; + int devid = pci_read_config16(dev, PCI_DEVICE_ID); + int gpu_is_ulx = 0; + + if (devid == 0x0a0e || devid == 0x0a1e) + gpu_is_ulx = 1; + + /* CD Frequency */ + if ((gtt_read(0x42014) & 0x1000000) || gpu_is_ulx || haswell_is_ult()) + cdclk = 0; /* fixed frequency */ + else + cdclk = 2; /* variable frequency */ + + if (gpu_is_ulx || cdclk != 0) + gtt_rmw(0x130040, 0xf7ffffff, 0x04000000); + else + gtt_rmw(0x130040, 0xf3ffffff, 0x00000000); + + /* More magic */ + if (haswell_is_ult() || gpu_is_ulx) { + if (!gpu_is_ulx) + gtt_write(0x138128, 0x00000000); + else + gtt_write(0x138128, 0x00000001); + gtt_write(0x13812c, 0x00000000); + gtt_write(0x138124, 0x80000017); + } + + /* Disable Force Wake */ + gtt_write(0x0a188, 0x00010000); + gtt_poll(0x130044, 1 << 0, 0 << 0); + gtt_write(0x0a188, 0x00000001); +} + +static void gma_func0_init(struct device *dev) +{ +#if CONFIG_MAINBOARD_DO_NATIVE_VGA_INIT + struct northbridge_intel_haswell_config *conf = dev->chip_info; + struct intel_dp dp; +#endif + int lightup_ok = 0; + u32 reg32; + /* IGD needs to be Bus Master */ + reg32 = pci_read_config32(dev, PCI_COMMAND); + reg32 |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO; + pci_write_config32(dev, PCI_COMMAND, reg32); + + /* Init graphics power management */ + gma_pm_init_pre_vbios(dev); + + /* Post VBIOS init */ + gma_setup_panel(dev); + +#if CONFIG_MAINBOARD_DO_NATIVE_VGA_INIT + printk(BIOS_SPEW, "NATIVE graphics, run native enable\n"); + /* Default set to 1 since it might be required for + stuff like seabios */ + unsigned int init_fb = 1; + + /* the BAR for graphics space is a well known number for + * sandy and ivy. And the resource code renumbers it. + * So it's almost like having two hardcodes. + */ + dp.graphics = (void *)((uintptr_t)dev->resource_list[1].base); + dp.physbase = pci_read_config32(dev, 0x5c) & ~0xf; + dp.panel_power_down_delay = conf->gpu_panel_power_down_delay; + dp.panel_power_up_delay = conf->gpu_panel_power_up_delay; + dp.panel_power_cycle_delay = conf->gpu_panel_power_cycle_delay; + +#ifdef CONFIG_CHROMEOS + init_fb = developer_mode_enabled() || recovery_mode_enabled(); +#endif + lightup_ok = panel_lightup(&dp, init_fb); +#endif + if (! lightup_ok) { + printk(BIOS_SPEW, "FUI did not run; using VBIOS\n"); + mdelay(CONFIG_PRE_GRAPHICS_DELAY); + pci_dev_init(dev); + } + + /* Post VBIOS init */ + gma_pm_init_post_vbios(dev); +} + +static void gma_set_subsystem(device_t dev, unsigned vendor, unsigned device) +{ + if (!vendor || !device) { + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + pci_read_config32(dev, PCI_VENDOR_ID)); + } else { + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + ((device & 0xffff) << 16) | (vendor & 0xffff)); + } +} + +static void gma_read_resources(struct device *dev) +{ + pci_dev_read_resources(dev); + +#if CONFIG_MARK_GRAPHICS_MEM_WRCOMB + struct resource *res; + + /* Set the graphics memory to write combining. */ + res = find_resource(dev, PCI_BASE_ADDRESS_2); + if (res == NULL) { + printk(BIOS_DEBUG, "gma: memory resource not found.\n"); + return; + } + res->flags |= IORESOURCE_WRCOMB; +#endif +} + +static struct pci_operations gma_pci_ops = { + .set_subsystem = gma_set_subsystem, +}; + +static struct device_operations gma_func0_ops = { + .read_resources = gma_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .init = gma_func0_init, + .scan_bus = 0, + .enable = 0, + .ops_pci = &gma_pci_ops, +}; + +static const unsigned short pci_device_ids[] = { + 0x0402, /* Desktop GT1 */ + 0x0412, /* Desktop GT2 */ + 0x0422, /* Desktop GT3 */ + 0x0406, /* Mobile GT1 */ + 0x0416, /* Mobile GT2 */ + 0x0426, /* Mobile GT3 */ + 0x0d16, /* Mobile 4+3 GT1 */ + 0x0d26, /* Mobile 4+3 GT2 */ + 0x0d36, /* Mobile 4+3 GT3 */ + 0x0a06, /* ULT GT1 */ + 0x0a16, /* ULT GT2 */ + 0x0a26, /* ULT GT3 */ + 0, +}; + +static const struct pci_driver pch_lpc __pci_driver = { + .ops = &gma_func0_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .devices = pci_device_ids, +}; diff --git a/src/soc/intel/broadwell/lpc.c b/src/soc/intel/broadwell/lpc.c new file mode 100644 index 0000000000..5d8527680f --- /dev/null +++ b/src/soc/intel/broadwell/lpc.c @@ -0,0 +1,797 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008-2009 coresystems GmbH + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nvs.h" +#include "pch.h" + +#define NMI_OFF 0 + +#define ENABLE_ACPI_MODE_IN_COREBOOT 0 + +typedef struct southbridge_intel_lynxpoint_config config_t; + +static void pch_enable_apic(struct device *dev) +{ + int i; + u32 reg32; + volatile u32 *ioapic_index = (volatile u32 *)(IO_APIC_ADDR); + volatile u32 *ioapic_data = (volatile u32 *)(IO_APIC_ADDR + 0x10); + + /* Enable ACPI I/O and power management. + * Set SCI IRQ to IRQ9 + */ + pci_write_config8(dev, ACPI_CNTL, 0x80); + + *ioapic_index = 0; + *ioapic_data = (1 << 25); + + /* affirm full set of redirection table entries ("write once") */ + *ioapic_index = 1; + reg32 = *ioapic_data; + if (pch_is_lp()) { + /* PCH-LP has 39 redirection entries */ + reg32 &= ~0x00ff0000; + reg32 |= 0x00270000; + } + *ioapic_index = 1; + *ioapic_data = reg32; + + *ioapic_index = 0; + reg32 = *ioapic_data; + printk(BIOS_DEBUG, "Southbridge APIC ID = %x\n", (reg32 >> 24) & 0x0f); + if (reg32 != (1 << 25)) + die("APIC Error\n"); + + printk(BIOS_SPEW, "Dumping IOAPIC registers\n"); + for (i=0; i<3; i++) { + *ioapic_index = i; + printk(BIOS_SPEW, " reg 0x%04x:", i); + reg32 = *ioapic_data; + printk(BIOS_SPEW, " 0x%08x\n", reg32); + } + + *ioapic_index = 3; /* Select Boot Configuration register. */ + *ioapic_data = 1; /* Use Processor System Bus to deliver interrupts. */ +} + +static void pch_enable_serial_irqs(struct device *dev) +{ + /* Set packet length and toggle silent mode bit for one frame. */ + pci_write_config8(dev, SERIRQ_CNTL, + (1 << 7) | (1 << 6) | ((21 - 17) << 2) | (0 << 0)); +#if !CONFIG_SERIRQ_CONTINUOUS_MODE + pci_write_config8(dev, SERIRQ_CNTL, + (1 << 7) | (0 << 6) | ((21 - 17) << 2) | (0 << 0)); +#endif +} + +/* PIRQ[n]_ROUT[3:0] - PIRQ Routing Control + * 0x00 - 0000 = Reserved + * 0x01 - 0001 = Reserved + * 0x02 - 0010 = Reserved + * 0x03 - 0011 = IRQ3 + * 0x04 - 0100 = IRQ4 + * 0x05 - 0101 = IRQ5 + * 0x06 - 0110 = IRQ6 + * 0x07 - 0111 = IRQ7 + * 0x08 - 1000 = Reserved + * 0x09 - 1001 = IRQ9 + * 0x0A - 1010 = IRQ10 + * 0x0B - 1011 = IRQ11 + * 0x0C - 1100 = IRQ12 + * 0x0D - 1101 = Reserved + * 0x0E - 1110 = IRQ14 + * 0x0F - 1111 = IRQ15 + * PIRQ[n]_ROUT[7] - PIRQ Routing Control + * 0x80 - The PIRQ is not routed. + */ + +static void pch_pirq_init(device_t dev) +{ + device_t irq_dev; + /* Get the chip configuration */ + config_t *config = dev->chip_info; + + pci_write_config8(dev, PIRQA_ROUT, config->pirqa_routing); + pci_write_config8(dev, PIRQB_ROUT, config->pirqb_routing); + pci_write_config8(dev, PIRQC_ROUT, config->pirqc_routing); + pci_write_config8(dev, PIRQD_ROUT, config->pirqd_routing); + + pci_write_config8(dev, PIRQE_ROUT, config->pirqe_routing); + pci_write_config8(dev, PIRQF_ROUT, config->pirqf_routing); + pci_write_config8(dev, PIRQG_ROUT, config->pirqg_routing); + pci_write_config8(dev, PIRQH_ROUT, config->pirqh_routing); + + /* Eric Biederman once said we should let the OS do this. + * I am not so sure anymore he was right. + */ + + for(irq_dev = all_devices; irq_dev; irq_dev = irq_dev->next) { + u8 int_pin=0, int_line=0; + + if (!irq_dev->enabled || irq_dev->path.type != DEVICE_PATH_PCI) + continue; + + int_pin = pci_read_config8(irq_dev, PCI_INTERRUPT_PIN); + + switch (int_pin) { + case 1: /* INTA# */ int_line = config->pirqa_routing; break; + case 2: /* INTB# */ int_line = config->pirqb_routing; break; + case 3: /* INTC# */ int_line = config->pirqc_routing; break; + case 4: /* INTD# */ int_line = config->pirqd_routing; break; + } + + if (!int_line) + continue; + + pci_write_config8(irq_dev, PCI_INTERRUPT_LINE, int_line); + } +} + +static void pch_gpi_routing(device_t dev) +{ + /* Get the chip configuration */ + config_t *config = dev->chip_info; + u32 reg32 = 0; + + /* An array would be much nicer here, or some + * other method of doing this. + */ + reg32 |= (config->gpi0_routing & 0x03) << 0; + reg32 |= (config->gpi1_routing & 0x03) << 2; + reg32 |= (config->gpi2_routing & 0x03) << 4; + reg32 |= (config->gpi3_routing & 0x03) << 6; + reg32 |= (config->gpi4_routing & 0x03) << 8; + reg32 |= (config->gpi5_routing & 0x03) << 10; + reg32 |= (config->gpi6_routing & 0x03) << 12; + reg32 |= (config->gpi7_routing & 0x03) << 14; + reg32 |= (config->gpi8_routing & 0x03) << 16; + reg32 |= (config->gpi9_routing & 0x03) << 18; + reg32 |= (config->gpi10_routing & 0x03) << 20; + reg32 |= (config->gpi11_routing & 0x03) << 22; + reg32 |= (config->gpi12_routing & 0x03) << 24; + reg32 |= (config->gpi13_routing & 0x03) << 26; + reg32 |= (config->gpi14_routing & 0x03) << 28; + reg32 |= (config->gpi15_routing & 0x03) << 30; + + pci_write_config32(dev, 0xb8, reg32); +} + +static void pch_power_options(device_t dev) +{ + u8 reg8; + u16 reg16; + u32 reg32; + const char *state; + /* Get the chip configuration */ + config_t *config = dev->chip_info; + u16 pmbase = get_pmbase(); + int pwr_on=CONFIG_MAINBOARD_POWER_ON_AFTER_POWER_FAIL; + int nmi_option; + + /* Which state do we want to goto after g3 (power restored)? + * 0 == S0 Full On + * 1 == S5 Soft Off + * + * If the option is not existent (Laptops), use Kconfig setting. + */ + get_option(&pwr_on, "power_on_after_fail"); + pwr_on = MAINBOARD_POWER_KEEP; + + reg16 = pci_read_config16(dev, GEN_PMCON_3); + reg16 &= 0xfffe; + switch (pwr_on) { + case MAINBOARD_POWER_OFF: + reg16 |= 1; + state = "off"; + break; + case MAINBOARD_POWER_ON: + reg16 &= ~1; + state = "on"; + break; + case MAINBOARD_POWER_KEEP: + reg16 &= ~1; + state = "state keep"; + break; + default: + state = "undefined"; + } + + reg16 &= ~(3 << 4); /* SLP_S4# Assertion Stretch 4s */ + reg16 |= (1 << 3); /* SLP_S4# Assertion Stretch Enable */ + + reg16 &= ~(1 << 10); + reg16 |= (1 << 11); /* SLP_S3# Min Assertion Width 50ms */ + + reg16 |= (1 << 12); /* Disable SLP stretch after SUS well */ + + pci_write_config16(dev, GEN_PMCON_3, reg16); + printk(BIOS_INFO, "Set power %s after power failure.\n", state); + + /* Set up NMI on errors. */ + reg8 = inb(0x61); + reg8 &= 0x0f; /* Higher Nibble must be 0 */ + reg8 &= ~(1 << 3); /* IOCHK# NMI Enable */ + // reg8 &= ~(1 << 2); /* PCI SERR# Enable */ + reg8 |= (1 << 2); /* PCI SERR# Disable for now */ + outb(reg8, 0x61); + + reg8 = inb(0x70); + nmi_option = NMI_OFF; + get_option(&nmi_option, "nmi"); + if (nmi_option) { + printk(BIOS_INFO, "NMI sources enabled.\n"); + reg8 &= ~(1 << 7); /* Set NMI. */ + } else { + printk(BIOS_INFO, "NMI sources disabled.\n"); + reg8 |= ( 1 << 7); /* Can't mask NMI from PCI-E and NMI_NOW */ + } + outb(reg8, 0x70); + + /* Enable CPU_SLP# and Intel Speedstep, set SMI# rate down */ + reg16 = pci_read_config16(dev, GEN_PMCON_1); + reg16 &= ~(3 << 0); // SMI# rate 1 minute + reg16 &= ~(1 << 10); // Disable BIOS_PCI_EXP_EN for native PME + pci_write_config16(dev, GEN_PMCON_1, reg16); + + /* + * Set the board's GPI routing on LynxPoint-H. + * This is done as part of GPIO configuration on LynxPoint-LP. + */ + if (pch_is_lp()) + pch_gpi_routing(dev); + + /* GPE setup based on device tree configuration */ + enable_all_gpe(config->gpe0_en_1, config->gpe0_en_2, + config->gpe0_en_3, config->gpe0_en_4); + + /* SMI setup based on device tree configuration */ + enable_alt_smi(config->alt_gp_smi_en); + + /* Set up power management block and determine sleep mode */ + reg32 = inl(pmbase + 0x04); // PM1_CNT + reg32 &= ~(7 << 10); // SLP_TYP + reg32 |= (1 << 0); // SCI_EN + outl(reg32, pmbase + 0x04); + + /* Clear magic status bits to prevent unexpected wake */ + reg32 = RCBA32(0x3310); + reg32 |= (1 << 4)|(1 << 5)|(1 << 0); + RCBA32(0x3310) = reg32; + + reg32 = RCBA32(0x3f02); + reg32 &= ~0xf; + RCBA32(0x3f02) = reg32; +} + +static void pch_rtc_init(struct device *dev) +{ + u8 reg8; + int rtc_failed; + + reg8 = pci_read_config8(dev, GEN_PMCON_3); + rtc_failed = reg8 & RTC_BATTERY_DEAD; + if (rtc_failed) { + reg8 &= ~RTC_BATTERY_DEAD; + pci_write_config8(dev, GEN_PMCON_3, reg8); +#if CONFIG_ELOG + elog_add_event(ELOG_TYPE_RTC_RESET); +#endif + } + printk(BIOS_DEBUG, "rtc_failed = 0x%x\n", rtc_failed); + + rtc_init(rtc_failed); +} + +/* LynxPoint PCH Power Management init */ +static void lpt_pm_init(struct device *dev) +{ + printk(BIOS_DEBUG, "LynxPoint PM init\n"); +} + +const struct rcba_config_instruction lpt_lp_pm_rcba[] = { + RCBA_RMW_REG_32(0x232c, ~1, 0x00000000), + RCBA_RMW_REG_32(0x1100, ~0xc000, 0xc000), + RCBA_RMW_REG_32(0x1100, ~0, 0x00000100), + RCBA_RMW_REG_32(0x1100, ~0, 0x0000003f), + RCBA_RMW_REG_32(0x2320, ~0x60, 0x10), + RCBA_RMW_REG_32(0x3314, 0, 0x00012fff), + RCBA_RMW_REG_32(0x3318, 0, 0x0dcf0400), + RCBA_RMW_REG_32(0x3324, 0, 0x04000000), + RCBA_RMW_REG_32(0x3368, 0, 0x00041400), + RCBA_RMW_REG_32(0x3388, 0, 0x3f8ddbff), + RCBA_RMW_REG_32(0x33ac, 0, 0x00007001), + RCBA_RMW_REG_32(0x33b0, 0, 0x00181900), + RCBA_RMW_REG_32(0x33c0, 0, 0x00060A00), + RCBA_RMW_REG_32(0x33d0, 0, 0x06200840), + RCBA_RMW_REG_32(0x3a28, 0, 0x01010101), + RCBA_RMW_REG_32(0x3a2c, 0, 0x04040404), + RCBA_RMW_REG_32(0x2b1c, 0, 0x03808033), + RCBA_RMW_REG_32(0x2b34, 0, 0x80000009), + RCBA_RMW_REG_32(0x3348, 0, 0x022ddfff), + RCBA_RMW_REG_32(0x334c, 0, 0x00000001), + RCBA_RMW_REG_32(0x3358, 0, 0x0001c000), + RCBA_RMW_REG_32(0x3380, 0, 0x3f8ddbff), + RCBA_RMW_REG_32(0x3384, 0, 0x0001c7e1), + RCBA_RMW_REG_32(0x338c, 0, 0x0001c7e1), + RCBA_RMW_REG_32(0x3398, 0, 0x0001c000), + RCBA_RMW_REG_32(0x33a8, 0, 0x00181900), + RCBA_RMW_REG_32(0x33dc, 0, 0x00080000), + RCBA_RMW_REG_32(0x33e0, 0, 0x00000001), + RCBA_RMW_REG_32(0x3a20, 0, 0x00000404), + RCBA_RMW_REG_32(0x3a24, 0, 0x01010101), + RCBA_RMW_REG_32(0x3a30, 0, 0x01010101), + RCBA_RMW_REG_32(0x0410, ~0, 0x00000003), + RCBA_RMW_REG_32(0x2618, ~0, 0x08000000), + RCBA_RMW_REG_32(0x2300, ~0, 0x00000002), + RCBA_RMW_REG_32(0x2600, ~0, 0x00000008), + RCBA_RMW_REG_32(0x33b4, 0, 0x00007001), + RCBA_RMW_REG_32(0x3350, 0, 0x022ddfff), + RCBA_RMW_REG_32(0x3354, 0, 0x00000001), + RCBA_RMW_REG_32(0x33d4, ~0, 0x08000000), /* Power Optimizer */ + RCBA_RMW_REG_32(0x33c8, ~0, 0x08000080), /* Power Optimizer */ + RCBA_RMW_REG_32(0x2b10, 0, 0x0000883c), /* Power Optimizer */ + RCBA_RMW_REG_32(0x2b14, 0, 0x1e0a4616), /* Power Optimizer */ + RCBA_RMW_REG_32(0x2b24, 0, 0x40000005), /* Power Optimizer */ + RCBA_RMW_REG_32(0x2b20, 0, 0x0005db01), /* Power Optimizer */ + RCBA_RMW_REG_32(0x3a80, 0, 0x05145005), + RCBA_END_CONFIG +}; + +/* LynxPoint LP PCH Power Management init */ +static void lpt_lp_pm_init(struct device *dev) +{ + struct southbridge_intel_lynxpoint_config *config = dev->chip_info; + u32 data; + + printk(BIOS_DEBUG, "LynxPoint LP PM init\n"); + + pci_write_config8(dev, 0xa9, 0x46); + + pch_config_rcba(lpt_lp_pm_rcba); + + pci_write_config32(dev, 0xac, + pci_read_config32(dev, 0xac) | (1 << 21)); + + pch_iobp_update(0xED00015C, ~(1<<11), 0x00003700); + pch_iobp_update(0xED000118, ~0UL, 0x00c00000); + pch_iobp_update(0xED000120, ~0UL, 0x00240000); + pch_iobp_update(0xCA000000, ~0UL, 0x00000009); + + /* Set RCBA CIR28 0x3A84 based on SATA port enables */ + data = 0x00001005; + /* Port 3 and 2 disabled */ + if ((config->sata_port_map & ((1 << 3)|(1 << 2))) == 0) + data |= (1 << 24) | (1 << 26); + /* Port 1 and 0 disabled */ + if ((config->sata_port_map & ((1 << 1)|(1 << 0))) == 0) + data |= (1 << 20) | (1 << 18); + RCBA32(0x3a84) = data; + + /* Set RCBA 0x2b1c[29]=1 if DSP disabled */ + if (RCBA32(FD) & PCH_DISABLE_ADSPD) + RCBA32_OR(0x2b1c, (1 << 29)); + + /* Lock */ + RCBA32_OR(0x3a6c, 0x00000001); + + /* Set RCBA 0x33D4 after other setup */ + RCBA32_OR(0x33d4, 0x2fff2fb1); + + /* Set RCBA 0x33C8[15]=1 as last step */ + RCBA32_OR(0x33c8, (1 << 15)); +} + +static void enable_hpet(void) +{ + u32 reg32; + + /* Move HPET to default address 0xfed00000 and enable it */ + reg32 = RCBA32(HPTC); + reg32 |= (1 << 7); // HPET Address Enable + reg32 &= ~(3 << 0); + RCBA32(HPTC) = reg32; + /* Read it back to stick. It's affected by posted write syndrome. */ + reg32 = RCBA32(HPTC); +} + +static void enable_clock_gating(device_t dev) +{ + /* LynxPoint Mobile */ + u32 reg32; + u16 reg16; + + /* DMI */ + RCBA32_AND_OR(0x2234, ~0UL, 0xf); + reg16 = pci_read_config16(dev, GEN_PMCON_1); + reg16 |= (1 << 11) | (1 << 12) | (1 << 14); + reg16 |= (1 << 2); // PCI CLKRUN# Enable + pci_write_config16(dev, GEN_PMCON_1, reg16); + RCBA32_OR(0x900, (1 << 14)); + + reg32 = RCBA32(CG); + reg32 |= (1 << 22); // HDA Dynamic + reg32 |= (1 << 31); // LPC Dynamic + reg32 |= (1 << 16); // PCIe Dynamic + reg32 |= (1 << 27); // HPET Dynamic + reg32 |= (1 << 28); // GPIO Dynamic + RCBA32(CG) = reg32; + + RCBA32_OR(0x38c0, 0x7); // SPI Dynamic +} + +static void enable_lp_clock_gating(device_t dev) +{ + /* LynxPoint LP */ + u32 reg32; + u16 reg16; + + /* DMI */ + RCBA32_AND_OR(0x2234, ~0UL, 0xf); + reg16 = pci_read_config16(dev, GEN_PMCON_1); + reg16 &= ~((1 << 11) | (1 << 14)); + reg16 |= (1 << 5) | (1 << 6) | (1 << 7) | (1 << 12) | (1 << 13); + reg16 |= (1 << 2); // PCI CLKRUN# Enable + pci_write_config16(dev, GEN_PMCON_1, reg16); + + reg32 = pci_read_config32(dev, 0x64); + reg32 |= (1 << 6); + pci_write_config32(dev, 0x64, reg32); + + /* + * 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) + */ + RCBA32_AND_OR(0x2614, 0x8bffffff, 0x0a206500); + + /* Check for LPT-LP B2 stepping and 0:31.0@0xFA > 4 */ + if (pci_read_config8(dev_find_slot(0, PCI_DEVFN(2, 0)), 0x8) >= 0x0b) + RCBA32_OR(0x2614, (1<<26)); + + RCBA32_OR(0x900, 0x0000031f); + + reg32 = RCBA32(CG); + if (RCBA32(0x3454) & (1 << 4)) + reg32 &= ~(1 << 29); // LPC Dynamic + else + reg32 |= (1 << 29); // LPC Dynamic + reg32 |= (1 << 31); // LP LPC + reg32 |= (1 << 30); // LP BLA + reg32 |= (1 << 28); // GPIO Dynamic + reg32 |= (1 << 27); // HPET Dynamic + reg32 |= (1 << 26); // Generic Platform Event Clock + if (RCBA32(BUC) & PCH_DISABLE_GBE) + reg32 |= (1 << 23); // GbE Static + reg32 |= (1 << 22); // HDA Dynamic + reg32 |= (1 << 16); // PCI Dynamic + RCBA32(CG) = reg32; + + RCBA32_OR(0x3434, 0x7); // LP LPC + + RCBA32_AND_OR(0x333c, 0xffcfffff, 0x00c00000); // SATA + + RCBA32_OR(0x38c0, 0x3c07); // SPI Dynamic + + pch_iobp_update(0xCF000000, ~0UL, 0x00007001); + pch_iobp_update(0xCE00C000, ~1UL, 0x00000000); // bit0=0 in BWG 1.4.0 +} + +static void pch_set_acpi_mode(void) +{ +#if CONFIG_HAVE_SMI_HANDLER + if (acpi_slp_type != 3) { +#if ENABLE_ACPI_MODE_IN_COREBOOT + printk(BIOS_DEBUG, "Enabling ACPI via APMC:\n"); + outb(APM_CNT_ACPI_ENABLE, APM_CNT); + printk(BIOS_DEBUG, "done.\n"); +#else + printk(BIOS_DEBUG, "Disabling ACPI via APMC:\n"); + outb(APM_CNT_ACPI_DISABLE, APM_CNT); + printk(BIOS_DEBUG, "done.\n"); +#endif + } +#endif /* CONFIG_HAVE_SMI_HANDLER */ +} + +static void pch_disable_smm_only_flashing(struct device *dev) +{ + u8 reg8; + + printk(BIOS_SPEW, "Enabling BIOS updates outside of SMM... "); + reg8 = pci_read_config8(dev, 0xdc); /* BIOS_CNTL */ + reg8 &= ~(1 << 5); + pci_write_config8(dev, 0xdc, reg8); +} + +static void pch_fixups(struct device *dev) +{ + u8 gen_pmcon_2; + + /* Indicate DRAM init done for MRC S3 to know it can resume */ + gen_pmcon_2 = pci_read_config8(dev, GEN_PMCON_2); + gen_pmcon_2 |= (1 << 7); + pci_write_config8(dev, GEN_PMCON_2, gen_pmcon_2); + + /* + * Enable DMI ASPM in the PCH + */ + RCBA32_AND_OR(0x2304, ~(1 << 10), 0); + RCBA32_OR(0x21a4, (1 << 11)|(1 << 10)); + RCBA32_OR(0x21a8, 0x3); +} + +static void lpc_init(struct device *dev) +{ + printk(BIOS_DEBUG, "pch: lpc_init\n"); + + /* Set the value for PCI command register. */ + pci_write_config16(dev, PCI_COMMAND, 0x000f); + + /* IO APIC initialization. */ + pch_enable_apic(dev); + + pch_enable_serial_irqs(dev); + + /* Setup the PIRQ. */ + pch_pirq_init(dev); + + /* Setup power options. */ + pch_power_options(dev); + + /* Initialize power management */ + if (pch_is_lp()) { + lpt_lp_pm_init(dev); + enable_lp_clock_gating(dev); + } else { + lpt_pm_init(dev); + enable_clock_gating(dev); + } + + /* Initialize the real time clock. */ + pch_rtc_init(dev); + + /* Initialize ISA DMA. */ + isa_dma_init(); + + /* Initialize the High Precision Event Timers, if present. */ + enable_hpet(); + + setup_i8259(); + + /* Interrupt 9 should be level triggered (SCI) */ + i8259_configure_irq_trigger(9, 1); + + pch_disable_smm_only_flashing(dev); + + pch_set_acpi_mode(); + + pch_fixups(dev); +} + +static void pch_lpc_add_mmio_resources(device_t dev) +{ + u32 reg; + struct resource *res; + const u32 default_decode_base = IO_APIC_ADDR; + + /* + * Just report all resources from IO-APIC base to 4GiB. Don't mark + * them reserved as that may upset the OS if this range is marked + * as reserved in the e820. + */ + res = new_resource(dev, OIC); + res->base = default_decode_base; + res->size = 0 - default_decode_base; + res->flags = IORESOURCE_MEM | IORESOURCE_ASSIGNED | IORESOURCE_FIXED; + + /* RCBA */ + if (DEFAULT_RCBA < default_decode_base) { + res = new_resource(dev, RCBA); + res->base = DEFAULT_RCBA; + res->size = 16 * 1024; + res->flags = IORESOURCE_MEM | IORESOURCE_ASSIGNED | + IORESOURCE_FIXED | IORESOURCE_RESERVE; + } + + /* Check LPC Memory Decode register. */ + reg = pci_read_config32(dev, LGMR); + if (reg & 1) { + reg &= ~0xffff; + if (reg < default_decode_base) { + res = new_resource(dev, LGMR); + res->base = reg; + res->size = 16 * 1024; + res->flags = IORESOURCE_MEM | IORESOURCE_ASSIGNED | + IORESOURCE_FIXED | IORESOURCE_RESERVE; + } + } +} + +/* Default IO range claimed by the LPC device. The upper bound is exclusive. */ +#define LPC_DEFAULT_IO_RANGE_LOWER 0 +#define LPC_DEFAULT_IO_RANGE_UPPER 0x1000 + +static inline int pch_io_range_in_default(u16 base, u16 size) +{ + /* Does it start above the range? */ + if (base >= LPC_DEFAULT_IO_RANGE_UPPER) + return 0; + + /* Is it entirely contained? */ + if (base >= LPC_DEFAULT_IO_RANGE_LOWER && + (base + size) < LPC_DEFAULT_IO_RANGE_UPPER) + return 1; + + /* This will return not in range for partial overlaps. */ + return 0; +} + +/* + * Note: this function assumes there is no overlap with the default LPC device's + * claimed range: LPC_DEFAULT_IO_RANGE_LOWER -> LPC_DEFAULT_IO_RANGE_UPPER. + */ +static void pch_lpc_add_io_resource(device_t dev, u16 base, u16 size, int index) +{ + struct resource *res; + + if (pch_io_range_in_default(base, size)) + return; + + res = new_resource(dev, index); + res->base = base; + res->size = size; + res->flags = IORESOURCE_IO | IORESOURCE_ASSIGNED | IORESOURCE_FIXED; +} + +static void pch_lpc_add_gen_io_resources(device_t dev, int reg_value, int index) +{ + /* + * Check if the register is enabled. If so and the base exceeds the + * device's deafult claim range add the resoure. + */ + if (reg_value & 1) { + u16 base = reg_value & 0xfffc; + u16 size = (0x3 | ((reg_value >> 16) & 0xfc)) + 1; + pch_lpc_add_io_resource(dev, base, size, index); + } +} + +static void pch_lpc_add_io_resources(device_t dev) +{ + struct resource *res; + config_t *config = dev->chip_info; + + /* Add the default claimed IO range for the LPC device. */ + res = new_resource(dev, 0); + res->base = LPC_DEFAULT_IO_RANGE_LOWER; + res->size = LPC_DEFAULT_IO_RANGE_UPPER - LPC_DEFAULT_IO_RANGE_LOWER; + res->flags = IORESOURCE_IO | IORESOURCE_ASSIGNED | IORESOURCE_FIXED; + + /* GPIOBASE */ + pch_lpc_add_io_resource(dev, get_gpiobase(), DEFAULT_GPIOSIZE, + GPIO_BASE); + + /* PMBASE */ + pch_lpc_add_io_resource(dev, get_pmbase(), 256, PMBASE); + + /* LPC Generic IO Decode range. */ + pch_lpc_add_gen_io_resources(dev, config->gen1_dec, LPC_GEN1_DEC); + pch_lpc_add_gen_io_resources(dev, config->gen2_dec, LPC_GEN2_DEC); + pch_lpc_add_gen_io_resources(dev, config->gen3_dec, LPC_GEN3_DEC); + pch_lpc_add_gen_io_resources(dev, config->gen4_dec, LPC_GEN4_DEC); +} + +static void pch_lpc_read_resources(device_t dev) +{ + global_nvs_t *gnvs; + + /* Get the normal PCI resources of this device. */ + pci_dev_read_resources(dev); + + /* Add non-standard MMIO resources. */ + pch_lpc_add_mmio_resources(dev); + + /* Add IO resources. */ + pch_lpc_add_io_resources(dev); + + /* Allocate ACPI NVS in CBMEM */ + gnvs = cbmem_add(CBMEM_ID_ACPI_GNVS, sizeof(global_nvs_t)); + if (acpi_slp_type != 3 && gnvs) + memset(gnvs, 0, sizeof(global_nvs_t)); +} + +static void pch_lpc_enable(device_t dev) +{ + /* Enable PCH Display Port */ + RCBA16(DISPBDF) = 0x0010; + RCBA32_OR(FD2, PCH_ENABLE_DBDF); + + pch_enable(dev); +} + +static void set_subsystem(device_t dev, unsigned vendor, unsigned device) +{ + if (!vendor || !device) { + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + pci_read_config32(dev, PCI_VENDOR_ID)); + } else { + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + ((device & 0xffff) << 16) | (vendor & 0xffff)); + } +} + +static struct pci_operations pci_ops = { + .set_subsystem = set_subsystem, +}; + +static struct device_operations device_ops = { + .read_resources = pch_lpc_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .init = lpc_init, + .enable = pch_lpc_enable, + .scan_bus = scan_static_bus, + .ops_pci = &pci_ops, +}; + + +/* IDs for LPC device of Intel 8 Series Chipset (Lynx Point) */ +static const unsigned short pci_device_ids[] = { + 0x8c41, /* Mobile Full Featured Engineering Sample. */ + 0x8c42, /* Desktop Full Featured Engineering Sample. */ + 0x8c44, /* Z87 SKU */ + 0x8c46, /* Z85 SKU */ + 0x8c49, /* HM86 SKU */ + 0x8c4a, /* H87 SKU */ + 0x8c4b, /* HM87 SKU */ + 0x8c4c, /* Q85 SKU */ + 0x8c4e, /* Q87 SKU */ + 0x8c4f, /* QM87 SKU */ + 0x9c41, /* LP Full Featured Engineering Sample */ + 0x9c43, /* LP Premium SKU */ + 0x9c45, /* LP Mainstream SKU */ + 0x9c47, /* LP Value SKU */ + 0 }; + +static const struct pci_driver pch_lpc __pci_driver = { + .ops = &device_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .devices = pci_device_ids, +}; + + diff --git a/src/soc/intel/broadwell/me.c b/src/soc/intel/broadwell/me.c new file mode 100644 index 0000000000..d3dfcfb152 --- /dev/null +++ b/src/soc/intel/broadwell/me.c @@ -0,0 +1,1071 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 The Chromium OS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +/* + * This is a ramstage driver for the Intel Management Engine found in the + * 6-series chipset. It handles the required boot-time messages over the + * MMIO-based Management Engine Interface to tell the ME that the BIOS is + * finished with POST. Additional messages are defined for debug but are + * not used unless the console loglevel is high enough. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "me.h" +#include "pch.h" + +#if CONFIG_CHROMEOS +#include +#include +#endif + +#ifndef __SMM__ +/* Path that the BIOS should take based on ME state */ +static const char *me_bios_path_values[] = { + [ME_NORMAL_BIOS_PATH] = "Normal", + [ME_S3WAKE_BIOS_PATH] = "S3 Wake", + [ME_ERROR_BIOS_PATH] = "Error", + [ME_RECOVERY_BIOS_PATH] = "Recovery", + [ME_DISABLE_BIOS_PATH] = "Disable", + [ME_FIRMWARE_UPDATE_BIOS_PATH] = "Firmware Update", +}; +static int intel_me_read_mbp(me_bios_payload *mbp_data, device_t dev); +#endif + +/* MMIO base address for MEI interface */ +static u32 mei_base_address; +void intel_me_mbp_clear(device_t dev); + +#if CONFIG_DEBUG_INTEL_ME +static void mei_dump(void *ptr, int dword, int offset, const char *type) +{ + struct mei_csr *csr; + + printk(BIOS_SPEW, "%-9s[%02x] : ", type, offset); + + switch (offset) { + case MEI_H_CSR: + case MEI_ME_CSR_HA: + csr = ptr; + if (!csr) { + printk(BIOS_SPEW, "ERROR: 0x%08x\n", dword); + break; + } + printk(BIOS_SPEW, "cbd=%u cbrp=%02u cbwp=%02u ready=%u " + "reset=%u ig=%u is=%u ie=%u\n", csr->buffer_depth, + csr->buffer_read_ptr, csr->buffer_write_ptr, + csr->ready, csr->reset, csr->interrupt_generate, + csr->interrupt_status, csr->interrupt_enable); + break; + case MEI_ME_CB_RW: + case MEI_H_CB_WW: + printk(BIOS_SPEW, "CB: 0x%08x\n", dword); + break; + default: + printk(BIOS_SPEW, "0x%08x\n", offset); + break; + } +} +#else +# define mei_dump(ptr,dword,offset,type) do {} while (0) +#endif + +/* + * ME/MEI access helpers using memcpy to avoid aliasing. + */ + +static inline void mei_read_dword_ptr(void *ptr, int offset) +{ + u32 dword = read32(mei_base_address + offset); + memcpy(ptr, &dword, sizeof(dword)); + mei_dump(ptr, dword, offset, "READ"); +} + +static inline void mei_write_dword_ptr(void *ptr, int offset) +{ + u32 dword = 0; + memcpy(&dword, ptr, sizeof(dword)); + write32(mei_base_address + offset, dword); + mei_dump(ptr, dword, offset, "WRITE"); +} + +static inline void pci_read_dword_ptr(device_t dev, void *ptr, int offset) +{ + u32 dword = pci_read_config32(dev, offset); + memcpy(ptr, &dword, sizeof(dword)); + mei_dump(ptr, dword, offset, "PCI READ"); +} + +static inline void read_host_csr(struct mei_csr *csr) +{ + mei_read_dword_ptr(csr, MEI_H_CSR); +} + +static inline void write_host_csr(struct mei_csr *csr) +{ + mei_write_dword_ptr(csr, MEI_H_CSR); +} + +static inline void read_me_csr(struct mei_csr *csr) +{ + mei_read_dword_ptr(csr, MEI_ME_CSR_HA); +} + +static inline void write_cb(u32 dword) +{ + write32(mei_base_address + MEI_H_CB_WW, dword); + mei_dump(NULL, dword, MEI_H_CB_WW, "WRITE"); +} + +static inline u32 read_cb(void) +{ + u32 dword = read32(mei_base_address + MEI_ME_CB_RW); + mei_dump(NULL, dword, MEI_ME_CB_RW, "READ"); + return dword; +} + +/* Wait for ME ready bit to be asserted */ +static int mei_wait_for_me_ready(void) +{ + struct mei_csr me; + unsigned try = ME_RETRY; + + while (try--) { + read_me_csr(&me); + if (me.ready) + return 0; + udelay(ME_DELAY); + } + + printk(BIOS_ERR, "ME: failed to become ready\n"); + return -1; +} + +static void mei_reset(void) +{ + struct mei_csr host; + + if (mei_wait_for_me_ready() < 0) + return; + + /* Reset host and ME circular buffers for next message */ + read_host_csr(&host); + host.reset = 1; + host.interrupt_generate = 1; + write_host_csr(&host); + + if (mei_wait_for_me_ready() < 0) + return; + + /* Re-init and indicate host is ready */ + read_host_csr(&host); + host.interrupt_generate = 1; + host.ready = 1; + host.reset = 0; + write_host_csr(&host); +} + +static int mei_send_packet(struct mei_header *mei, void *req_data) +{ + struct mei_csr host; + unsigned ndata, n; + u32 *data; + + /* Number of dwords to write */ + ndata = mei->length >> 2; + + /* Pad non-dword aligned request message length */ + if (mei->length & 3) + ndata++; + if (!ndata) { + printk(BIOS_DEBUG, "ME: request has no data\n"); + return -1; + } + ndata++; /* Add MEI header */ + + /* + * Make sure there is still room left in the circular buffer. + * Reset the buffer pointers if the requested message will not fit. + */ + read_host_csr(&host); + if ((host.buffer_depth - host.buffer_write_ptr) < ndata) { + printk(BIOS_ERR, "ME: circular buffer full, resetting...\n"); + mei_reset(); + read_host_csr(&host); + } + + /* Ensure the requested length will fit in the circular buffer. */ + if ((host.buffer_depth - host.buffer_write_ptr) < ndata) { + printk(BIOS_ERR, "ME: message (%u) too large for buffer (%u)\n", + ndata + 2, host.buffer_depth); + return -1; + } + + /* Write MEI header */ + mei_write_dword_ptr(mei, MEI_H_CB_WW); + ndata--; + + /* Write message data */ + data = req_data; + for (n = 0; n < ndata; ++n) + write_cb(*data++); + + /* Generate interrupt to the ME */ + read_host_csr(&host); + host.interrupt_generate = 1; + write_host_csr(&host); + + /* Make sure ME is ready after sending request data */ + return mei_wait_for_me_ready(); +} + +static int mei_send_data(u8 me_address, u8 host_address, + void *req_data, int req_bytes) +{ + struct mei_header header = { + .client_address = me_address, + .host_address = host_address, + }; + struct mei_csr host; + int current = 0; + u8 *req_ptr = req_data; + + while (!header.is_complete) { + int remain = req_bytes - current; + int buf_len; + + read_host_csr(&host); + buf_len = host.buffer_depth - host.buffer_write_ptr; + + if (buf_len > remain) { + /* Send all remaining data as final message */ + header.length = req_bytes - current; + header.is_complete = 1; + } else { + /* Send as much data as the buffer can hold */ + header.length = buf_len; + } + + mei_send_packet(&header, req_ptr); + + req_ptr += header.length; + current += header.length; + } + + return 0; +} + +static int mei_send_header(u8 me_address, u8 host_address, + void *header, int header_len, int complete) +{ + struct mei_header mei = { + .client_address = me_address, + .host_address = host_address, + .length = header_len, + .is_complete = complete, + }; + return mei_send_packet(&mei, header); +} + +static int mei_recv_msg(void *header, int header_bytes, + void *rsp_data, int rsp_bytes) +{ + struct mei_header mei_rsp; + struct mei_csr me, host; + unsigned ndata, n; + unsigned expected; + u32 *data; + + /* Total number of dwords to read from circular buffer */ + expected = (rsp_bytes + sizeof(mei_rsp) + header_bytes) >> 2; + if (rsp_bytes & 3) + expected++; + + if (mei_wait_for_me_ready() < 0) + return -1; + + /* + * The interrupt status bit does not appear to indicate that the + * message has actually been received. Instead we wait until the + * expected number of dwords are present in the circular buffer. + */ + for (n = ME_RETRY; n; --n) { + read_me_csr(&me); + if ((me.buffer_write_ptr - me.buffer_read_ptr) >= expected) + break; + udelay(ME_DELAY); + } + if (!n) { + printk(BIOS_ERR, "ME: timeout waiting for data: expected " + "%u, available %u\n", expected, + me.buffer_write_ptr - me.buffer_read_ptr); + return -1; + } + + /* Read and verify MEI response header from the ME */ + mei_read_dword_ptr(&mei_rsp, MEI_ME_CB_RW); + if (!mei_rsp.is_complete) { + printk(BIOS_ERR, "ME: response is not complete\n"); + return -1; + } + + /* Handle non-dword responses and expect at least the header */ + ndata = mei_rsp.length >> 2; + if (mei_rsp.length & 3) + ndata++; + if (ndata != (expected - 1)) { + printk(BIOS_ERR, "ME: response is missing data %d != %d\n", + ndata, (expected - 1)); + return -1; + } + + /* Read response header from the ME */ + data = header; + for (n = 0; n < (header_bytes >> 2); ++n) + *data++ = read_cb(); + ndata -= header_bytes >> 2; + + /* Make sure caller passed a buffer with enough space */ + if (ndata != (rsp_bytes >> 2)) { + printk(BIOS_ERR, "ME: not enough room in response buffer: " + "%u != %u\n", ndata, rsp_bytes >> 2); + return -1; + } + + /* Read response data from the circular buffer */ + data = rsp_data; + for (n = 0; n < ndata; ++n) + *data++ = read_cb(); + + /* Tell the ME that we have consumed the response */ + read_host_csr(&host); + host.interrupt_status = 1; + host.interrupt_generate = 1; + write_host_csr(&host); + + return mei_wait_for_me_ready(); +} + +static inline int mei_sendrecv_mkhi(struct mkhi_header *mkhi, + void *req_data, int req_bytes, + void *rsp_data, int rsp_bytes) +{ + struct mkhi_header mkhi_rsp; + + /* Send header */ + if (mei_send_header(MEI_ADDRESS_MKHI, MEI_HOST_ADDRESS, + mkhi, sizeof(*mkhi), req_bytes ? 0 : 1) < 0) + return -1; + + /* Send data if available */ + if (req_bytes && mei_send_data(MEI_ADDRESS_MKHI, MEI_HOST_ADDRESS, + req_data, req_bytes) < 0) + return -1; + + /* Return now if no response expected */ + if (!rsp_bytes) + return 0; + + /* Read header and data */ + if (mei_recv_msg(&mkhi_rsp, sizeof(mkhi_rsp), + rsp_data, rsp_bytes) < 0) + return -1; + + if (!mkhi_rsp.is_response || + mkhi->group_id != mkhi_rsp.group_id || + mkhi->command != mkhi_rsp.command) { + printk(BIOS_ERR, "ME: invalid response, group %u ?= %u," + "command %u ?= %u, is_response %u\n", mkhi->group_id, + mkhi_rsp.group_id, mkhi->command, mkhi_rsp.command, + mkhi_rsp.is_response); + return -1; + } + + return 0; +} + +static inline int mei_sendrecv_icc(struct icc_header *icc, + void *req_data, int req_bytes, + void *rsp_data, int rsp_bytes) +{ + struct icc_header icc_rsp; + + /* Send header */ + if (mei_send_header(MEI_ADDRESS_ICC, MEI_HOST_ADDRESS, + icc, sizeof(*icc), req_bytes ? 0 : 1) < 0) + return -1; + + /* Send data if available */ + if (req_bytes && mei_send_data(MEI_ADDRESS_ICC, MEI_HOST_ADDRESS, + req_data, req_bytes) < 0) + return -1; + + /* Read header and data, if needed */ + if (rsp_bytes && mei_recv_msg(&icc_rsp, sizeof(icc_rsp), + rsp_data, rsp_bytes) < 0) + return -1; + + return 0; +} + +/* + * mbp give up routine. This path is taken if hfs.mpb_rdy is 0 or the read + * state machine on the BIOS end doesn't match the ME's state machine. + */ +static void intel_me_mbp_give_up(device_t dev) +{ + struct mei_csr csr; + + pci_write_config32(dev, PCI_ME_H_GS2, PCI_ME_MBP_GIVE_UP); + + read_host_csr(&csr); + csr.reset = 1; + csr.interrupt_generate = 1; + write_host_csr(&csr); +} + +/* + * mbp clear routine. This will wait for the ME to indicate that + * the MBP has been read and cleared. + */ +void intel_me_mbp_clear(device_t dev) +{ + int count; + struct me_hfs2 hfs2; + + /* Wait for the mbp_cleared indicator */ + for (count = ME_RETRY; count > 0; --count) { + pci_read_dword_ptr(dev, &hfs2, PCI_ME_HFS2); + if (hfs2.mbp_cleared) + break; + udelay(ME_DELAY); + } + + if (count == 0) { + printk(BIOS_WARNING, "ME: Timeout waiting for mbp_cleared\n"); + intel_me_mbp_give_up(dev); + } else { + printk(BIOS_INFO, "ME: MBP cleared\n"); + } +} + +#if (CONFIG_DEFAULT_CONSOLE_LOGLEVEL >= BIOS_DEBUG) && !defined(__SMM__) +static inline void print_cap(const char *name, int state) +{ + printk(BIOS_DEBUG, "ME Capability: %-41s : %sabled\n", + name, state ? " en" : "dis"); +} + +static void me_print_fw_version(mbp_fw_version_name *vers_name) +{ + if (!vers_name) { + printk(BIOS_ERR, "ME: mbp missing version report\n"); + return; + } + + printk(BIOS_DEBUG, "ME: found version %d.%d.%d.%d\n", + vers_name->major_version, vers_name->minor_version, + vers_name->hotfix_version, vers_name->build_version); +} + +#if CONFIG_DEBUG_INTEL_ME +/* Get ME Firmware Capabilities */ +static int mkhi_get_fwcaps(mbp_mefwcaps *cap) +{ + u32 rule_id = 0; + struct me_fwcaps cap_msg; + struct mkhi_header mkhi = { + .group_id = MKHI_GROUP_ID_FWCAPS, + .command = MKHI_FWCAPS_GET_RULE, + }; + + /* Send request and wait for response */ + if (mei_sendrecv_mkhi(&mkhi, &rule_id, sizeof(u32), + &cap_msg, sizeof(cap_msg)) < 0) { + printk(BIOS_ERR, "ME: GET FWCAPS message failed\n"); + return -1; + } + *cap = cap_msg.caps_sku; + return 0; +} + +/* Get ME Firmware Capabilities */ +static void me_print_fwcaps(mbp_mefwcaps *cap) +{ + mbp_mefwcaps local_caps; + if (!cap) { + cap = &local_caps; + printk(BIOS_ERR, "ME: mbp missing fwcaps report\n"); + if (mkhi_get_fwcaps(cap)) + return; + } + + print_cap("Full Network manageability", cap->full_net); + print_cap("Regular Network manageability", cap->std_net); + print_cap("Manageability", cap->manageability); + print_cap("IntelR Anti-Theft (AT)", cap->intel_at); + print_cap("IntelR Capability Licensing Service (CLS)", cap->intel_cls); + print_cap("IntelR Power Sharing Technology (MPC)", cap->intel_mpc); + print_cap("ICC Over Clocking", cap->icc_over_clocking); + print_cap("Protected Audio Video Path (PAVP)", cap->pavp); + print_cap("IPV6", cap->ipv6); + print_cap("KVM Remote Control (KVM)", cap->kvm); + print_cap("Outbreak Containment Heuristic (OCH)", cap->och); + print_cap("Virtual LAN (VLAN)", cap->vlan); + print_cap("TLS", cap->tls); + print_cap("Wireless LAN (WLAN)", cap->wlan); +} +#endif +#endif + +#if CONFIG_CHROMEOS && 0 /* DISABLED */ +/* Tell ME to issue a global reset */ +static int mkhi_global_reset(void) +{ + struct me_global_reset reset = { + .request_origin = GLOBAL_RESET_BIOS_POST, + .reset_type = CBM_RR_GLOBAL_RESET, + }; + struct mkhi_header mkhi = { + .group_id = MKHI_GROUP_ID_CBM, + .command = MKHI_GLOBAL_RESET, + }; + + /* Send request and wait for response */ + printk(BIOS_NOTICE, "ME: %s\n", __FUNCTION__); + if (mei_sendrecv_mkhi(&mkhi, &reset, sizeof(reset), NULL, 0) < 0) { + /* No response means reset will happen shortly... */ + hlt(); + } + + /* If the ME responded it rejected the reset request */ + printk(BIOS_ERR, "ME: Global Reset failed\n"); + return -1; +} +#endif + +#ifdef __SMM__ + +/* Send END OF POST message to the ME */ +static int mkhi_end_of_post(void) +{ + struct mkhi_header mkhi = { + .group_id = MKHI_GROUP_ID_GEN, + .command = MKHI_END_OF_POST, + }; + u32 eop_ack; + + /* Send request and wait for response */ + printk(BIOS_NOTICE, "ME: %s\n", __FUNCTION__); + if (mei_sendrecv_mkhi(&mkhi, NULL, 0, &eop_ack, sizeof(eop_ack)) < 0) { + printk(BIOS_ERR, "ME: END OF POST message failed\n"); + return -1; + } + + printk(BIOS_INFO, "ME: END OF POST message successful (%d)\n", eop_ack); + return 0; +} + +void intel_me_finalize_smm(void) +{ + struct me_hfs hfs; + u32 reg32; + + mei_base_address = + pci_read_config32(PCH_ME_DEV, PCI_BASE_ADDRESS_0) & ~0xf; + + /* S3 path will have hidden this device already */ + if (!mei_base_address || mei_base_address == 0xfffffff0) + return; + +#if CONFIG_ME_MBP_CLEAR_LATE + /* Wait for ME MBP Cleared indicator */ + intel_me_mbp_clear(PCH_ME_DEV); +#endif + + /* Make sure ME is in a mode that expects EOP */ + reg32 = pci_read_config32(PCH_ME_DEV, PCI_ME_HFS); + memcpy(&hfs, ®32, sizeof(u32)); + + /* Abort and leave device alone if not normal mode */ + if (hfs.fpt_bad || + hfs.working_state != ME_HFS_CWS_NORMAL || + hfs.operation_mode != ME_HFS_MODE_NORMAL) + return; + + /* Try to send EOP command so ME stops accepting other commands */ + mkhi_end_of_post(); + + /* Make sure IO is disabled */ + reg32 = pci_read_config32(PCH_ME_DEV, PCI_COMMAND); + reg32 &= ~(PCI_COMMAND_MASTER | + PCI_COMMAND_MEMORY | PCI_COMMAND_IO); + pci_write_config32(PCH_ME_DEV, PCI_COMMAND, reg32); + + /* Hide the PCI device */ + RCBA32_OR(FD2, PCH_DISABLE_MEI1); +} + +#else /* !__SMM__ */ + +static int me_icc_set_clock_enables(u32 mask) +{ + struct icc_clock_enables_msg clk = { + .clock_enables = 0, /* Turn off specified clocks */ + .clock_mask = mask, + .no_response = 1, /* Do not expect response */ + }; + struct icc_header icc = { + .api_version = ICC_API_VERSION_LYNXPOINT, + .icc_command = ICC_SET_CLOCK_ENABLES, + .length = sizeof(clk), + }; + + /* Send request and wait for response */ + if (mei_sendrecv_icc(&icc, &clk, sizeof(clk), NULL, 0) < 0) { + printk(BIOS_ERR, "ME: ICC SET CLOCK ENABLES message failed\n"); + return -1; + } else { + printk(BIOS_INFO, "ME: ICC SET CLOCK ENABLES 0x%08x\n", mask); + } + + return 0; +} + +/* Determine the path that we should take based on ME status */ +static me_bios_path intel_me_path(device_t dev) +{ + me_bios_path path = ME_DISABLE_BIOS_PATH; + struct me_hfs hfs; + struct me_hfs2 hfs2; + + pci_read_dword_ptr(dev, &hfs, PCI_ME_HFS); + pci_read_dword_ptr(dev, &hfs2, PCI_ME_HFS2); + + /* Check and dump status */ + intel_me_status(&hfs, &hfs2); + + /* Check Current Working State */ + switch (hfs.working_state) { + case ME_HFS_CWS_NORMAL: + path = ME_NORMAL_BIOS_PATH; + break; + case ME_HFS_CWS_REC: + path = ME_RECOVERY_BIOS_PATH; + break; + default: + path = ME_DISABLE_BIOS_PATH; + break; + } + + /* Check Current Operation Mode */ + switch (hfs.operation_mode) { + case ME_HFS_MODE_NORMAL: + break; + case ME_HFS_MODE_DEBUG: + case ME_HFS_MODE_DIS: + case ME_HFS_MODE_OVER_JMPR: + case ME_HFS_MODE_OVER_MEI: + default: + path = ME_DISABLE_BIOS_PATH; + break; + } + + /* Check for any error code and valid firmware and MBP */ + if (hfs.error_code || hfs.fpt_bad) + path = ME_ERROR_BIOS_PATH; + + /* Check if the MBP is ready */ + if (!hfs2.mbp_rdy) { + printk(BIOS_CRIT, "%s: mbp is not ready!\n", + __FUNCTION__); + path = ME_ERROR_BIOS_PATH; + } + +#if CONFIG_ELOG + if (path != ME_NORMAL_BIOS_PATH) { + struct elog_event_data_me_extended data = { + .current_working_state = hfs.working_state, + .operation_state = hfs.operation_state, + .operation_mode = hfs.operation_mode, + .error_code = hfs.error_code, + .progress_code = hfs2.progress_code, + .current_pmevent = hfs2.current_pmevent, + .current_state = hfs2.current_state, + }; + elog_add_event_byte(ELOG_TYPE_MANAGEMENT_ENGINE, path); + elog_add_event_raw(ELOG_TYPE_MANAGEMENT_ENGINE_EXT, + &data, sizeof(data)); + } +#endif + + return path; +} + +/* Prepare ME for MEI messages */ +static int intel_mei_setup(device_t dev) +{ + struct resource *res; + struct mei_csr host; + u32 reg32; + + /* Find the MMIO base for the ME interface */ + res = find_resource(dev, PCI_BASE_ADDRESS_0); + if (!res || res->base == 0 || res->size == 0) { + printk(BIOS_DEBUG, "ME: MEI resource not present!\n"); + return -1; + } + mei_base_address = res->base; + + /* Ensure Memory and Bus Master bits are set */ + reg32 = pci_read_config32(dev, PCI_COMMAND); + reg32 |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; + pci_write_config32(dev, PCI_COMMAND, reg32); + + /* Clean up status for next message */ + read_host_csr(&host); + host.interrupt_generate = 1; + host.ready = 1; + host.reset = 0; + write_host_csr(&host); + + return 0; +} + +/* Read the Extend register hash of ME firmware */ +static int intel_me_extend_valid(device_t dev) +{ + struct me_heres status; + u32 extend[8] = {0}; + int i, count = 0; + + pci_read_dword_ptr(dev, &status, PCI_ME_HERES); + if (!status.extend_feature_present) { + printk(BIOS_ERR, "ME: Extend Feature not present\n"); + return -1; + } + + if (!status.extend_reg_valid) { + printk(BIOS_ERR, "ME: Extend Register not valid\n"); + return -1; + } + + switch (status.extend_reg_algorithm) { + case PCI_ME_EXT_SHA1: + count = 5; + printk(BIOS_DEBUG, "ME: Extend SHA-1: "); + break; + case PCI_ME_EXT_SHA256: + count = 8; + printk(BIOS_DEBUG, "ME: Extend SHA-256: "); + break; + default: + printk(BIOS_ERR, "ME: Extend Algorithm %d unknown\n", + status.extend_reg_algorithm); + return -1; + } + + for (i = 0; i < count; ++i) { + extend[i] = pci_read_config32(dev, PCI_ME_HER(i)); + printk(BIOS_DEBUG, "%08x", extend[i]); + } + printk(BIOS_DEBUG, "\n"); + +#if CONFIG_CHROMEOS + /* Save hash in NVS for the OS to verify */ + chromeos_set_me_hash(extend, count); +#endif + + return 0; +} + +/* Check whether ME is present and do basic init */ +static void intel_me_init(device_t dev) +{ + struct southbridge_intel_lynxpoint_config *config = dev->chip_info; + me_bios_path path = intel_me_path(dev); + me_bios_payload mbp_data; + + /* Do initial setup and determine the BIOS path */ + printk(BIOS_NOTICE, "ME: BIOS path: %s\n", me_bios_path_values[path]); + + if (path == ME_NORMAL_BIOS_PATH) { + /* Validate the extend register */ + intel_me_extend_valid(dev); + } + + memset(&mbp_data, 0, sizeof(mbp_data)); + + /* + * According to the ME9 BWG, BIOS is required to fetch MBP data in + * all boot flows except S3 Resume. + */ + + /* Prepare MEI MMIO interface */ + if (intel_mei_setup(dev) < 0) + return; + + if (intel_me_read_mbp(&mbp_data, dev)) + return; + +#if (CONFIG_DEFAULT_CONSOLE_LOGLEVEL >= BIOS_DEBUG) + me_print_fw_version(mbp_data.fw_version_name); +#if CONFIG_DEBUG_INTEL_ME + me_print_fwcaps(mbp_data.fw_capabilities); +#endif + + if (mbp_data.plat_time) { + printk(BIOS_DEBUG, "ME: Wake Event to ME Reset: %u ms\n", + mbp_data.plat_time->wake_event_mrst_time_ms); + printk(BIOS_DEBUG, "ME: ME Reset to Platform Reset: %u ms\n", + mbp_data.plat_time->mrst_pltrst_time_ms); + printk(BIOS_DEBUG, "ME: Platform Reset to CPU Reset: %u ms\n", + mbp_data.plat_time->pltrst_cpurst_time_ms); + } +#endif + + /* Set clock enables according to devicetree */ + if (config && config->icc_clock_disable) + me_icc_set_clock_enables(config->icc_clock_disable); + + /* + * Leave the ME unlocked. It will be locked via SMI command later. + */ +} + +static void set_subsystem(device_t dev, unsigned vendor, unsigned device) +{ + if (!vendor || !device) { + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + pci_read_config32(dev, PCI_VENDOR_ID)); + } else { + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + ((device & 0xffff) << 16) | (vendor & 0xffff)); + } +} + +static struct pci_operations pci_ops = { + .set_subsystem = set_subsystem, +}; + +static void intel_me_enable(device_t dev) +{ +#if CONFIG_HAVE_ACPI_RESUME + /* Avoid talking to the device in S3 path */ + if (acpi_slp_type == 3) { + dev->enabled = 0; + pch_disable_devfn(dev); + } +#endif +} + +static struct device_operations device_ops = { + .read_resources = pci_dev_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .enable = intel_me_enable, + .init = intel_me_init, + .ops_pci = &pci_ops, +}; + +static const unsigned short pci_device_ids[] = { + 0x8c3a, /* Mobile */ + 0x9c3a, /* Low Power */ + 0 +}; + +static const struct pci_driver intel_me __pci_driver = { + .ops = &device_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .devices= pci_device_ids, +}; + +/****************************************************************************** + * */ +static u32 me_to_host_words_pending(void) +{ + struct mei_csr me; + read_me_csr(&me); + if (!me.ready) + return 0; + return (me.buffer_write_ptr - me.buffer_read_ptr) & + (me.buffer_depth - 1); +} + +#if 0 +/* This function is not yet being used, keep it in for the future. */ +static u32 host_to_me_words_room(void) +{ + struct mei_csr csr; + + read_me_csr(&csr); + if (!csr.ready) + return 0; + + read_host_csr(&csr); + return (csr.buffer_read_ptr - csr.buffer_write_ptr - 1) & + (csr.buffer_depth - 1); +} +#endif + +struct mbp_payload { + mbp_header header; + u32 data[0]; +}; + +/* + * mbp seems to be following its own flow, let's retrieve it in a dedicated + * function. + */ +static int intel_me_read_mbp(me_bios_payload *mbp_data, device_t dev) +{ + mbp_header mbp_hdr; + u32 me2host_pending; + struct mei_csr host; + struct me_hfs2 hfs2; + struct mbp_payload *mbp; + int i; + + pci_read_dword_ptr(dev, &hfs2, PCI_ME_HFS2); + + if (!hfs2.mbp_rdy) { + printk(BIOS_ERR, "ME: MBP not ready\n"); + goto mbp_failure; + } + + me2host_pending = me_to_host_words_pending(); + if (!me2host_pending) { + printk(BIOS_ERR, "ME: no mbp data!\n"); + goto mbp_failure; + } + + /* we know for sure that at least the header is there */ + mei_read_dword_ptr(&mbp_hdr, MEI_ME_CB_RW); + + if ((mbp_hdr.num_entries > (mbp_hdr.mbp_size / 2)) || + (me2host_pending < mbp_hdr.mbp_size)) { + printk(BIOS_ERR, "ME: mbp of %d entries, total size %d words" + " buffer contains %d words\n", + mbp_hdr.num_entries, mbp_hdr.mbp_size, + me2host_pending); + goto mbp_failure; + } + mbp = malloc(mbp_hdr.mbp_size * sizeof(u32)); + if (!mbp) + goto mbp_failure; + + mbp->header = mbp_hdr; + me2host_pending--; + + i = 0; + while (i != me2host_pending) { + mei_read_dword_ptr(&mbp->data[i], MEI_ME_CB_RW); + i++; + } + + /* Signal to the ME that the host has finished reading the MBP. */ + read_host_csr(&host); + host.interrupt_generate = 1; + write_host_csr(&host); + +#if !CONFIG_ME_MBP_CLEAR_LATE + /* Wait for the mbp_cleared indicator. */ + intel_me_mbp_clear(dev); +#endif + + /* Dump out the MBP contents. */ +#if (CONFIG_DEFAULT_CONSOLE_LOGLEVEL >= BIOS_DEBUG) + printk(BIOS_INFO, "ME MBP: Header: items: %d, size dw: %d\n", + mbp->header.num_entries, mbp->header.mbp_size); +#if CONFIG_DEBUG_INTEL_ME + for (i = 0; i < mbp->header.mbp_size - 1; i++) { + printk(BIOS_INFO, "ME MBP: %04x: 0x%08x\n", i, mbp->data[i]); + } +#endif +#endif + + #define ASSIGN_FIELD_PTR(field_,val_) \ + { \ + mbp_data->field_ = (typeof(mbp_data->field_))(void *)val_; \ + break; \ + } + /* Setup the pointers in the me_bios_payload structure. */ + for (i = 0; i < mbp->header.mbp_size - 1;) { + mbp_item_header *item = (void *)&mbp->data[i]; + + switch(MBP_MAKE_IDENT(item->app_id, item->item_id)) { + case MBP_IDENT(KERNEL, FW_VER): + ASSIGN_FIELD_PTR(fw_version_name, &mbp->data[i+1]); + + case MBP_IDENT(ICC, PROFILE): + ASSIGN_FIELD_PTR(icc_profile, &mbp->data[i+1]); + + case MBP_IDENT(INTEL_AT, STATE): + ASSIGN_FIELD_PTR(at_state, &mbp->data[i+1]); + + case MBP_IDENT(KERNEL, FW_CAP): + ASSIGN_FIELD_PTR(fw_capabilities, &mbp->data[i+1]); + + case MBP_IDENT(KERNEL, ROM_BIST): + ASSIGN_FIELD_PTR(rom_bist_data, &mbp->data[i+1]); + + case MBP_IDENT(KERNEL, PLAT_KEY): + ASSIGN_FIELD_PTR(platform_key, &mbp->data[i+1]); + + case MBP_IDENT(KERNEL, FW_TYPE): + ASSIGN_FIELD_PTR(fw_plat_type, &mbp->data[i+1]); + + case MBP_IDENT(KERNEL, MFS_FAILURE): + ASSIGN_FIELD_PTR(mfsintegrity, &mbp->data[i+1]); + + case MBP_IDENT(KERNEL, PLAT_TIME): + ASSIGN_FIELD_PTR(plat_time, &mbp->data[i+1]); + + case MBP_IDENT(NFC, SUPPORT_DATA): + ASSIGN_FIELD_PTR(nfc_data, &mbp->data[i+1]); + + default: + printk(BIOS_ERR, "ME MBP: unknown item 0x%x @ " + "dw offset 0x%x\n", mbp->data[i], i); + break; + } + i += item->length; + } + #undef ASSIGN_FIELD_PTR + + return 0; + +mbp_failure: + intel_me_mbp_give_up(dev); + return -1; +} + +#endif /* !__SMM__ */ diff --git a/src/soc/intel/broadwell/me_status.c b/src/soc/intel/broadwell/me_status.c new file mode 100644 index 0000000000..d09be7b30c --- /dev/null +++ b/src/soc/intel/broadwell/me_status.c @@ -0,0 +1,215 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 The Chromium OS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include +#include +#include "me.h" + +#if (CONFIG_DEFAULT_CONSOLE_LOGLEVEL >= BIOS_DEBUG) +/* HFS1[3:0] Current Working State Values */ +static const char *me_cws_values[] = { + [ME_HFS_CWS_RESET] = "Reset", + [ME_HFS_CWS_INIT] = "Initializing", + [ME_HFS_CWS_REC] = "Recovery", + [ME_HFS_CWS_NORMAL] = "Normal", + [ME_HFS_CWS_WAIT] = "Platform Disable Wait", + [ME_HFS_CWS_TRANS] = "OP State Transition", + [ME_HFS_CWS_INVALID] = "Invalid CPU Plugged In" +}; + +/* HFS1[8:6] Current Operation State Values */ +static const char *me_opstate_values[] = { + [ME_HFS_STATE_PREBOOT] = "Preboot", + [ME_HFS_STATE_M0_UMA] = "M0 with UMA", + [ME_HFS_STATE_M3] = "M3 without UMA", + [ME_HFS_STATE_M0] = "M0 without UMA", + [ME_HFS_STATE_BRINGUP] = "Bring up", + [ME_HFS_STATE_ERROR] = "M0 without UMA but with error" +}; + +/* HFS[19:16] Current Operation Mode Values */ +static const char *me_opmode_values[] = { + [ME_HFS_MODE_NORMAL] = "Normal", + [ME_HFS_MODE_DEBUG] = "Debug", + [ME_HFS_MODE_DIS] = "Soft Temporary Disable", + [ME_HFS_MODE_OVER_JMPR] = "Security Override via Jumper", + [ME_HFS_MODE_OVER_MEI] = "Security Override via MEI Message" +}; + +/* HFS[15:12] Error Code Values */ +static const char *me_error_values[] = { + [ME_HFS_ERROR_NONE] = "No Error", + [ME_HFS_ERROR_UNCAT] = "Uncategorized Failure", + [ME_HFS_ERROR_IMAGE] = "Image Failure", + [ME_HFS_ERROR_DEBUG] = "Debug Failure" +}; + +/* HFS2[31:28] ME Progress Code */ +static const char *me_progress_values[] = { + [ME_HFS2_PHASE_ROM] = "ROM Phase", + [ME_HFS2_PHASE_BUP] = "BUP Phase", + [ME_HFS2_PHASE_UKERNEL] = "uKernel Phase", + [ME_HFS2_PHASE_POLICY] = "Policy Module", + [ME_HFS2_PHASE_MODULE_LOAD] = "Module Loading", + [ME_HFS2_PHASE_UNKNOWN] = "Unknown", + [ME_HFS2_PHASE_HOST_COMM] = "Host Communication" +}; + +/* HFS2[27:24] Power Management Event */ +static const char *me_pmevent_values[] = { + [ME_HFS2_PMEVENT_CLEAN_MOFF_MX_WAKE] = "Clean Moff->Mx wake", + [ME_HFS2_PMEVENT_MOFF_MX_WAKE_ERROR] = "Moff->Mx wake after an error", + [ME_HFS2_PMEVENT_CLEAN_GLOBAL_RESET] = "Clean global reset", + [ME_HFS2_PMEVENT_CLEAN_GLOBAL_RESET_ERROR] = "Global reset after an error", + [ME_HFS2_PMEVENT_CLEAN_ME_RESET] = "Clean Intel ME reset", + [ME_HFS2_PMEVENT_ME_RESET_EXCEPTION] = "Intel ME reset due to exception", + [ME_HFS2_PMEVENT_PSEUDO_ME_RESET] = "Pseudo-global reset", + [ME_HFS2_PMEVENT_S0MO_SXM3] = "S0/M0->Sx/M3", + [ME_HFS2_PMEVENT_SXM3_S0M0] = "Sx/M3->S0/M0", + [ME_HFS2_PMEVENT_NON_PWR_CYCLE_RESET] = "Non-power cycle reset", + [ME_HFS2_PMEVENT_PWR_CYCLE_RESET_M3] = "Power cycle reset through M3", + [ME_HFS2_PMEVENT_PWR_CYCLE_RESET_MOFF] = "Power cycle reset through Moff", + [ME_HFS2_PMEVENT_SXMX_SXMOFF] = "Sx/Mx->Sx/Moff" +}; + +/* Progress Code 0 states */ +static const char *me_progress_rom_values[] = { + [ME_HFS2_STATE_ROM_BEGIN] = "BEGIN", + [ME_HFS2_STATE_ROM_DISABLE] = "DISABLE" +}; + +/* Progress Code 1 states */ +static const char *me_progress_bup_values[] = { + [ME_HFS2_STATE_BUP_INIT] = "Initialization starts", + [ME_HFS2_STATE_BUP_DIS_HOST_WAKE] = "Disable the host wake event", + [ME_HFS2_STATE_BUP_FLOW_DET] = "Flow determination start process", + [ME_HFS2_STATE_BUP_VSCC_ERR] = "Error reading/matching the VSCC table in the descriptor", + [ME_HFS2_STATE_BUP_CHECK_STRAP] = "Check to see if straps say ME DISABLED", + [ME_HFS2_STATE_BUP_PWR_OK_TIMEOUT] = "Timeout waiting for PWROK", + [ME_HFS2_STATE_BUP_MANUF_OVRD_STRAP] = "Possibly handle BUP manufacturing override strap", + [ME_HFS2_STATE_BUP_M3] = "Bringup in M3", + [ME_HFS2_STATE_BUP_M0] = "Bringup in M0", + [ME_HFS2_STATE_BUP_FLOW_DET_ERR] = "Flow detection error", + [ME_HFS2_STATE_BUP_M3_CLK_ERR] = "M3 clock switching error", + [ME_HFS2_STATE_BUP_CPU_RESET_DID_TIMEOUT_MEM_MISSING] = "Host error - CPU reset timeout, DID timeout, memory missing", + [ME_HFS2_STATE_BUP_M3_KERN_LOAD] = "M3 kernel load", + [ME_HFS2_STATE_BUP_T32_MISSING] = "T34 missing - cannot program ICC", + [ME_HFS2_STATE_BUP_WAIT_DID] = "Waiting for DID BIOS message", + [ME_HFS2_STATE_BUP_WAIT_DID_FAIL] = "Waiting for DID BIOS message failure", + [ME_HFS2_STATE_BUP_DID_NO_FAIL] = "DID reported no error", + [ME_HFS2_STATE_BUP_ENABLE_UMA] = "Enabling UMA", + [ME_HFS2_STATE_BUP_ENABLE_UMA_ERR] = "Enabling UMA error", + [ME_HFS2_STATE_BUP_SEND_DID_ACK] = "Sending DID Ack to BIOS", + [ME_HFS2_STATE_BUP_SEND_DID_ACK_ERR] = "Sending DID Ack to BIOS error", + [ME_HFS2_STATE_BUP_M0_CLK] = "Switching clocks in M0", + [ME_HFS2_STATE_BUP_M0_CLK_ERR] = "Switching clocks in M0 error", + [ME_HFS2_STATE_BUP_TEMP_DIS] = "ME in temp disable", + [ME_HFS2_STATE_BUP_M0_KERN_LOAD] = "M0 kernel load", +}; + +/* Progress Code 3 states */ +static const char *me_progress_policy_values[] = { + [ME_HFS2_STATE_POLICY_ENTRY] = "Entery into Policy Module", + [ME_HFS2_STATE_POLICY_RCVD_S3] = "Received S3 entry", + [ME_HFS2_STATE_POLICY_RCVD_S4] = "Received S4 entry", + [ME_HFS2_STATE_POLICY_RCVD_S5] = "Received S5 entry", + [ME_HFS2_STATE_POLICY_RCVD_UPD] = "Received UPD entry", + [ME_HFS2_STATE_POLICY_RCVD_PCR] = "Received PCR entry", + [ME_HFS2_STATE_POLICY_RCVD_NPCR] = "Received NPCR entry", + [ME_HFS2_STATE_POLICY_RCVD_HOST_WAKE] = "Received host wake", + [ME_HFS2_STATE_POLICY_RCVD_AC_DC] = "Received AC<>DC switch", + [ME_HFS2_STATE_POLICY_RCVD_DID] = "Received DRAM Init Done", + [ME_HFS2_STATE_POLICY_VSCC_NOT_FOUND] = "VSCC Data not found for flash device", + [ME_HFS2_STATE_POLICY_VSCC_INVALID] = "VSCC Table is not valid", + [ME_HFS2_STATE_POLICY_FPB_ERR] = "Flash Partition Boundary is outside address space", + [ME_HFS2_STATE_POLICY_DESCRIPTOR_ERR] = "ME cannot access the chipset descriptor region", + [ME_HFS2_STATE_POLICY_VSCC_NO_MATCH] = "Required VSCC values for flash parts do not match", +}; +#endif + +void intel_me_status(struct me_hfs *hfs, struct me_hfs2 *hfs2) +{ +#if (CONFIG_DEFAULT_CONSOLE_LOGLEVEL >= BIOS_DEBUG) + /* Check Current States */ + printk(BIOS_DEBUG, "ME: FW Partition Table : %s\n", + hfs->fpt_bad ? "BAD" : "OK"); + printk(BIOS_DEBUG, "ME: Bringup Loader Failure : %s\n", + hfs->ft_bup_ld_flr ? "YES" : "NO"); + printk(BIOS_DEBUG, "ME: Firmware Init Complete : %s\n", + hfs->fw_init_complete ? "YES" : "NO"); + printk(BIOS_DEBUG, "ME: Manufacturing Mode : %s\n", + hfs->mfg_mode ? "YES" : "NO"); + printk(BIOS_DEBUG, "ME: Boot Options Present : %s\n", + hfs->boot_options_present ? "YES" : "NO"); + printk(BIOS_DEBUG, "ME: Update In Progress : %s\n", + hfs->update_in_progress ? "YES" : "NO"); + printk(BIOS_DEBUG, "ME: Current Working State : %s\n", + me_cws_values[hfs->working_state]); + printk(BIOS_DEBUG, "ME: Current Operation State : %s\n", + me_opstate_values[hfs->operation_state]); + printk(BIOS_DEBUG, "ME: Current Operation Mode : %s\n", + me_opmode_values[hfs->operation_mode]); + printk(BIOS_DEBUG, "ME: Error Code : %s\n", + me_error_values[hfs->error_code]); + printk(BIOS_DEBUG, "ME: Progress Phase : %s\n", + me_progress_values[hfs2->progress_code]); + printk(BIOS_DEBUG, "ME: Power Management Event : %s\n", + me_pmevent_values[hfs2->current_pmevent]); + + printk(BIOS_DEBUG, "ME: Progress Phase State : "); + switch (hfs2->progress_code) { + case ME_HFS2_PHASE_ROM: /* ROM Phase */ + printk(BIOS_DEBUG, "%s", + me_progress_rom_values[hfs2->current_state]); + break; + + case ME_HFS2_PHASE_BUP: /* Bringup Phase */ + if (hfs2->current_state < ARRAY_SIZE(me_progress_bup_values) + && me_progress_bup_values[hfs2->current_state]) + printk(BIOS_DEBUG, "%s", + me_progress_bup_values[hfs2->current_state]); + else + printk(BIOS_DEBUG, "0x%02x", hfs2->current_state); + break; + + case ME_HFS2_PHASE_POLICY: /* Policy Module Phase */ + if (hfs2->current_state < ARRAY_SIZE(me_progress_policy_values) + && me_progress_policy_values[hfs2->current_state]) + printk(BIOS_DEBUG, "%s", + me_progress_policy_values[hfs2->current_state]); + else + printk(BIOS_DEBUG, "0x%02x", hfs2->current_state); + break; + + case ME_HFS2_PHASE_HOST_COMM: /* Host Communication Phase */ + if (!hfs2->current_state) + printk(BIOS_DEBUG, "Host communication established"); + else + printk(BIOS_DEBUG, "0x%02x", hfs2->current_state); + break; + + default: + printk(BIOS_DEBUG, "Unknown phase: 0x%02x sate: 0x%02x", + hfs2->progress_code, hfs2->current_state); + } + printk(BIOS_DEBUG, "\n"); +#endif +} diff --git a/src/soc/intel/broadwell/microcode/microcode_blob.c b/src/soc/intel/broadwell/microcode/microcode_blob.c new file mode 100644 index 0000000000..18a1327633 --- /dev/null +++ b/src/soc/intel/broadwell/microcode/microcode_blob.c @@ -0,0 +1,23 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +unsigned microcode[] = { +#include "microcode_blob.h" +}; + diff --git a/src/soc/intel/broadwell/microcode/microcode_blob.h b/src/soc/intel/broadwell/microcode/microcode_blob.h new file mode 100644 index 0000000000..5776154a49 --- /dev/null +++ b/src/soc/intel/broadwell/microcode/microcode_blob.h @@ -0,0 +1,33 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if CONFIG_INTEL_LYNXPOINT_LP +#include "microcode-M7240650_ffff000a.h" +#include "microcode-M7240651_00000015.h" +#else +#include "microcode-M32306c1_ffff000d.h" +#include "microcode-M32306c2_ffff0003.h" +#include "microcode-M32306c3_00000012.h" +#include "microcode-M3240660_ffff000b.h" +#endif + /* Dummy terminator */ + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, diff --git a/src/soc/intel/broadwell/minihd.c b/src/soc/intel/broadwell/minihd.c new file mode 100644 index 0000000000..cef61263be --- /dev/null +++ b/src/soc/intel/broadwell/minihd.c @@ -0,0 +1,140 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008 Advanced Micro Devices, Inc. + * Copyright (C) 2008-2009 coresystems GmbH + * Copyright (C) 2011 The ChromiumOS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const u32 minihd_verb_table[] = { + /* coreboot specific header */ + 0x80862807, // Codec Vendor / Device ID: Intel Haswell Mini-HD + 0x00000000, // Subsystem ID + 0x00000004, // Number of jacks + + /* Enable 3rd Pin and Converter Widget */ + 0x00878101, + + /* Pin Widget 5 - PORT B */ + 0x00571C10, + 0x00571D00, + 0x00571E56, + 0x00571F18, + + /* Pin Widget 6 - PORT C */ + 0x00671C20, + 0x00671D00, + 0x00671E56, + 0x00671F18, + + /* Pin Widget 7 - PORT D */ + 0x00771C30, + 0x00771D00, + 0x00771E56, + 0x00771F18, + + /* Disable 3rd Pin and Converter Widget */ + 0x00878100, + + /* Dummy entries to fill out the table */ + 0x00878100, + 0x00878100, +}; + +static void minihd_init(struct device *dev) +{ + struct resource *res; + u32 base, reg32; + int codec_mask, i; + + /* Find base address */ + res = find_resource(dev, PCI_BASE_ADDRESS_0); + if (!res) + return; + + base = (u32)res->base; + printk(BIOS_DEBUG, "Mini-HD: base = %08x\n", (u32)base); + + /* Set Bus Master */ + reg32 = pci_read_config32(dev, PCI_COMMAND); + pci_write_config32(dev, PCI_COMMAND, reg32 | PCI_COMMAND_MASTER); + + /* Mini-HD configuration */ + reg32 = read32(base + 0x100c); + reg32 &= 0xfffc0000; + reg32 |= 0x4; + write32(base + 0x100c, reg32); + + reg32 = read32(base + 0x1010); + reg32 &= 0xfffc0000; + reg32 |= 0x4b; + write32(base + 0x1010, reg32); + + /* Init the codec and write the verb table */ + codec_mask = hda_codec_detect(base); + + if (codec_mask) { + for (i = 3; i >= 0; i--) { + if (codec_mask & (1 << i)) + hda_codec_init(base, i, + sizeof(minihd_verb_table), + minihd_verb_table); + } + } +} + +static void minihd_set_subsystem(device_t dev, unsigned vendor, unsigned device) +{ + if (!vendor || !device) { + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + pci_read_config32(dev, PCI_VENDOR_ID)); + } else { + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + ((device & 0xffff) << 16) | (vendor & 0xffff)); + } +} + +static struct pci_operations minihd_pci_ops = { + .set_subsystem = minihd_set_subsystem, +}; + +static struct device_operations minihd_ops = { + .read_resources = pci_dev_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .init = minihd_init, + .scan_bus = 0, + .ops_pci = &minihd_pci_ops, +}; + +static const unsigned short pci_device_ids[] = { 0x0a0c, 0 }; + +static const struct pci_driver haswell_minihd __pci_driver = { + .ops = &minihd_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .devices = pci_device_ids, +}; + diff --git a/src/soc/intel/broadwell/monotonic_timer.c b/src/soc/intel/broadwell/monotonic_timer.c new file mode 100644 index 0000000000..c51bcbd0c9 --- /dev/null +++ b/src/soc/intel/broadwell/monotonic_timer.c @@ -0,0 +1,62 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2013 Google, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include +#include +#include + +#define MSR_COUNTER_24_MHz 0x637 +static struct monotonic_counter { + int initialized; + struct mono_time time; + uint32_t last_value; +} mono_counter; + +static inline uint32_t read_counter_msr(void) +{ + /* Even though the MSR is 64-bit it is assumed that the hardware + * is polled frequently enough to only use the lower 32-bits. */ + msr_t counter_msr; + + counter_msr = rdmsr(MSR_COUNTER_24_MHz); + + return counter_msr.lo; +} + +void timer_monotonic_get(struct mono_time *mt) +{ + uint32_t current_tick; + uint32_t usecs_elapsed; + + if (!mono_counter.initialized) { + mono_counter.last_value = read_counter_msr(); + mono_counter.initialized = 1; + } + + current_tick = read_counter_msr(); + usecs_elapsed = (current_tick - mono_counter.last_value) / 24; + + /* Update current time and tick values only if a full tick occurred. */ + if (usecs_elapsed) { + mono_time_add_usecs(&mono_counter.time, usecs_elapsed); + mono_counter.last_value = current_tick; + } + + /* Save result. */ + *mt = mono_counter.time; +} diff --git a/src/soc/intel/broadwell/pch.c b/src/soc/intel/broadwell/pch.c new file mode 100644 index 0000000000..d073a4b9a7 --- /dev/null +++ b/src/soc/intel/broadwell/pch.c @@ -0,0 +1,324 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008-2009 coresystems GmbH + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include "pch.h" + +static device_t pch_get_lpc_device(void) +{ +#ifdef __SMM__ + return PCI_DEV(0, 0x1f, 0); +#else + return dev_find_slot(0, PCI_DEVFN(0x1f, 0)); +#endif +} + +int pch_silicon_revision(void) +{ + static int pch_revision_id = -1; + + if (pch_revision_id < 0) + pch_revision_id = pci_read_config8(pch_get_lpc_device(), + PCI_REVISION_ID); + return pch_revision_id; +} + +int pch_silicon_type(void) +{ + static int pch_type = -1; + + if (pch_type < 0) + pch_type = pci_read_config8(pch_get_lpc_device(), + PCI_DEVICE_ID + 1); + return pch_type; +} + +int pch_is_lp(void) +{ + return pch_silicon_type() == PCH_TYPE_LPT_LP; +} + +u16 get_pmbase(void) +{ + static u16 pmbase; + + if (!pmbase) + pmbase = pci_read_config16(pch_get_lpc_device(), + PMBASE) & 0xfffc; + return pmbase; +} + +u16 get_gpiobase(void) +{ + static u16 gpiobase; + + if (!gpiobase) + gpiobase = pci_read_config16(pch_get_lpc_device(), + GPIOBASE) & 0xfffc; + return gpiobase; +} + +#ifndef __SMM__ + +/* Put device in D3Hot Power State */ +static void pch_enable_d3hot(device_t dev) +{ + u32 reg32 = pci_read_config32(dev, PCH_PCS); + reg32 |= PCH_PCS_PS_D3HOT; + pci_write_config32(dev, PCH_PCS, reg32); +} + +/* Set bit in Function Disble register to hide this device */ +void pch_disable_devfn(device_t dev) +{ + switch (dev->path.pci.devfn) { + case PCI_DEVFN(19, 0): /* Audio DSP */ + RCBA32_OR(FD, PCH_DISABLE_ADSPD); + break; + case PCI_DEVFN(20, 0): /* XHCI */ + RCBA32_OR(FD, PCH_DISABLE_XHCI); + break; + case PCI_DEVFN(21, 0): /* DMA */ + pch_enable_d3hot(dev); + pch_iobp_update(SIO_IOBP_FUNCDIS0, ~0UL, SIO_IOBP_FUNCDIS_DIS); + break; + case PCI_DEVFN(21, 1): /* I2C0 */ + pch_enable_d3hot(dev); + pch_iobp_update(SIO_IOBP_FUNCDIS1, ~0UL, SIO_IOBP_FUNCDIS_DIS); + break; + case PCI_DEVFN(21, 2): /* I2C1 */ + pch_enable_d3hot(dev); + pch_iobp_update(SIO_IOBP_FUNCDIS2, ~0UL, SIO_IOBP_FUNCDIS_DIS); + break; + case PCI_DEVFN(21, 3): /* SPI0 */ + pch_enable_d3hot(dev); + pch_iobp_update(SIO_IOBP_FUNCDIS3, ~0UL, SIO_IOBP_FUNCDIS_DIS); + break; + case PCI_DEVFN(21, 4): /* SPI1 */ + pch_enable_d3hot(dev); + pch_iobp_update(SIO_IOBP_FUNCDIS4, ~0UL, SIO_IOBP_FUNCDIS_DIS); + break; + case PCI_DEVFN(21, 5): /* UART0 */ + pch_enable_d3hot(dev); + pch_iobp_update(SIO_IOBP_FUNCDIS5, ~0UL, SIO_IOBP_FUNCDIS_DIS); + break; + case PCI_DEVFN(21, 6): /* UART1 */ + pch_enable_d3hot(dev); + pch_iobp_update(SIO_IOBP_FUNCDIS6, ~0UL, SIO_IOBP_FUNCDIS_DIS); + break; + case PCI_DEVFN(22, 0): /* MEI #1 */ + RCBA32_OR(FD2, PCH_DISABLE_MEI1); + break; + case PCI_DEVFN(22, 1): /* MEI #2 */ + RCBA32_OR(FD2, PCH_DISABLE_MEI2); + break; + case PCI_DEVFN(22, 2): /* IDE-R */ + RCBA32_OR(FD2, PCH_DISABLE_IDER); + break; + case PCI_DEVFN(22, 3): /* KT */ + RCBA32_OR(FD2, PCH_DISABLE_KT); + break; + case PCI_DEVFN(23, 0): /* SDIO */ + pch_enable_d3hot(dev); + pch_iobp_update(SIO_IOBP_FUNCDIS7, ~0UL, SIO_IOBP_FUNCDIS_DIS); + break; + case PCI_DEVFN(25, 0): /* Gigabit Ethernet */ + RCBA32_OR(BUC, PCH_DISABLE_GBE); + break; + case PCI_DEVFN(26, 0): /* EHCI #2 */ + RCBA32_OR(FD, PCH_DISABLE_EHCI2); + break; + case PCI_DEVFN(27, 0): /* HD Audio Controller */ + RCBA32_OR(FD, PCH_DISABLE_HD_AUDIO); + break; + case PCI_DEVFN(28, 0): /* PCI Express Root Port 1 */ + case PCI_DEVFN(28, 1): /* PCI Express Root Port 2 */ + case PCI_DEVFN(28, 2): /* PCI Express Root Port 3 */ + case PCI_DEVFN(28, 3): /* PCI Express Root Port 4 */ + case PCI_DEVFN(28, 4): /* PCI Express Root Port 5 */ + case PCI_DEVFN(28, 5): /* PCI Express Root Port 6 */ + case PCI_DEVFN(28, 6): /* PCI Express Root Port 7 */ + case PCI_DEVFN(28, 7): /* PCI Express Root Port 8 */ + RCBA32_OR(FD, PCH_DISABLE_PCIE(PCI_FUNC(dev->path.pci.devfn))); + break; + case PCI_DEVFN(29, 0): /* EHCI #1 */ + RCBA32_OR(FD, PCH_DISABLE_EHCI1); + break; + case PCI_DEVFN(31, 0): /* LPC */ + RCBA32_OR(FD, PCH_DISABLE_LPC); + break; + case PCI_DEVFN(31, 2): /* SATA #1 */ + RCBA32_OR(FD, PCH_DISABLE_SATA1); + break; + case PCI_DEVFN(31, 3): /* SMBUS */ + RCBA32_OR(FD, PCH_DISABLE_SMBUS); + break; + case PCI_DEVFN(31, 5): /* SATA #2 */ + RCBA32_OR(FD, PCH_DISABLE_SATA2); + break; + case PCI_DEVFN(31, 6): /* Thermal Subsystem */ + RCBA32_OR(FD, PCH_DISABLE_THERMAL); + break; + } +} + +#define IOBP_RETRY 1000 +static inline int iobp_poll(void) +{ + unsigned try; + + for (try = IOBP_RETRY; try > 0; try--) { + u16 status = RCBA16(IOBPS); + if ((status & IOBPS_READY) == 0) + return 1; + udelay(10); + } + + printk(BIOS_ERR, "IOBP: timeout waiting for transaction to complete\n"); + return 0; +} + +u32 pch_iobp_read(u32 address) +{ + u16 status; + + if (!iobp_poll()) + return 0; + + /* Set the address */ + RCBA32(IOBPIRI) = address; + + /* READ OPCODE */ + status = RCBA16(IOBPS); + status &= ~IOBPS_MASK; + status |= IOBPS_READ; + RCBA16(IOBPS) = status; + + /* Undocumented magic */ + RCBA16(IOBPU) = IOBPU_MAGIC; + + /* Set ready bit */ + status = RCBA16(IOBPS); + status |= IOBPS_READY; + RCBA16(IOBPS) = status; + + if (!iobp_poll()) + return 0; + + /* Check for successful transaction */ + status = RCBA16(IOBPS); + if (status & IOBPS_TX_MASK) { + printk(BIOS_ERR, "IOBP: read 0x%08x failed\n", address); + return 0; + } + + /* Read IOBP data */ + return RCBA32(IOBPD); +} + +void pch_iobp_write(u32 address, u32 data) +{ + u16 status; + + if (!iobp_poll()) + return; + + /* Set the address */ + RCBA32(IOBPIRI) = address; + + /* WRITE OPCODE */ + status = RCBA16(IOBPS); + status &= ~IOBPS_MASK; + status |= IOBPS_WRITE; + RCBA16(IOBPS) = status; + + RCBA32(IOBPD) = data; + + /* Undocumented magic */ + RCBA16(IOBPU) = IOBPU_MAGIC; + + /* Set ready bit */ + status = RCBA16(IOBPS); + status |= IOBPS_READY; + RCBA16(IOBPS) = status; + + if (!iobp_poll()) + return; + + /* Check for successful transaction */ + status = RCBA16(IOBPS); + if (status & IOBPS_TX_MASK) { + printk(BIOS_ERR, "IOBP: write 0x%08x failed\n", address); + return; + } + + printk(BIOS_INFO, "IOBP: set 0x%08x to 0x%08x\n", address, data); +} + +void pch_iobp_update(u32 address, u32 andvalue, u32 orvalue) +{ + u32 data = pch_iobp_read(address); + + /* Update the data */ + data &= andvalue; + data |= orvalue; + + pch_iobp_write(address, data); +} + +void pch_enable(device_t dev) +{ + u32 reg32; + + /* PCH PCIe Root Ports are handled in PCIe driver. */ + if (PCI_SLOT(dev->path.pci.devfn) == PCH_PCIE_DEV_SLOT) + return; + + if (!dev->enabled) { + printk(BIOS_DEBUG, "%s: Disabling device\n", dev_path(dev)); + + /* Ensure memory, io, and bus master are all disabled */ + reg32 = pci_read_config32(dev, PCI_COMMAND); + reg32 &= ~(PCI_COMMAND_MASTER | + PCI_COMMAND_MEMORY | PCI_COMMAND_IO); + pci_write_config32(dev, PCI_COMMAND, reg32); + + /* Disable this device if possible */ + pch_disable_devfn(dev); + } else { + /* Enable SERR */ + reg32 = pci_read_config32(dev, PCI_COMMAND); + reg32 |= PCI_COMMAND_SERR; + pci_write_config32(dev, PCI_COMMAND, reg32); + } +} + +struct chip_operations southbridge_intel_lynxpoint_ops = { + CHIP_NAME("Intel Series 8 (Lynx Point) Southbridge") + .enable_dev = pch_enable, +}; + +#endif /* __SMM__ */ diff --git a/src/soc/intel/broadwell/pcie.c b/src/soc/intel/broadwell/pcie.c new file mode 100644 index 0000000000..581b79d1a4 --- /dev/null +++ b/src/soc/intel/broadwell/pcie.c @@ -0,0 +1,779 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008-2009 coresystems GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include "pch.h" + +static void pcie_update_cfg8(device_t dev, int reg, u8 mask, u8 or); +static void pcie_update_cfg(device_t dev, int reg, u32 mask, u32 or); + +/* LynxPoint-LP has 6 root ports while non-LP has 8. */ +#define MAX_NUM_ROOT_PORTS 8 +#define H_NUM_ROOT_PORTS MAX_NUM_ROOT_PORTS +#define LP_NUM_ROOT_PORTS (MAX_NUM_ROOT_PORTS - 2) + +struct root_port_config { + /* RPFN is a write-once register so keep a copy until it is written */ + u32 orig_rpfn; + u32 new_rpfn; + u32 pin_ownership; + u32 strpfusecfg1; + u32 strpfusecfg2; + u32 strpfusecfg3; + u32 b0d28f0_32c; + u32 b0d28f4_32c; + u32 b0d28f5_32c; + int coalesce; + int gbe_port; + int num_ports; + device_t ports[MAX_NUM_ROOT_PORTS]; +}; + +static struct root_port_config rpc; + +static inline int max_root_ports(void) +{ + if (pch_is_lp()) + return LP_NUM_ROOT_PORTS; + else + return H_NUM_ROOT_PORTS; +} + +static inline int root_port_is_first(device_t dev) +{ + return PCI_FUNC(dev->path.pci.devfn) == 0; +} + +static inline int root_port_is_last(device_t dev) +{ + return PCI_FUNC(dev->path.pci.devfn) == (rpc.num_ports - 1); +} + +/* Root ports are numbered 1..N in the documentation. */ +static inline int root_port_number(device_t dev) +{ + return PCI_FUNC(dev->path.pci.devfn) + 1; +} + +static void root_port_config_update_gbe_port(void) +{ + /* Is the Gbe Port enabled? */ + if (!((rpc.strpfusecfg1 >> 19) & 1)) + return; + + if (pch_is_lp()) { + switch ((rpc.strpfusecfg1 >> 16) & 0x7) { + case 0: + rpc.gbe_port = 3; + break; + case 1: + rpc.gbe_port = 4; + break; + case 2: + case 3: + case 4: + case 5: + /* Lanes 0-4 of Root Port 5. */ + rpc.gbe_port = 5; + break; + default: + printk(BIOS_DEBUG, "Invalid GbE Port Selection.\n"); + } + } else { + /* Non-LP has 1:1 mapping with root ports. */ + rpc.gbe_port = ((rpc.strpfusecfg1 >> 16) & 0x7) + 1; + } +} + +static void root_port_init_config(device_t dev) +{ + int rp; + + if (root_port_is_first(dev)) { + rpc.orig_rpfn = RCBA32(RPFN); + rpc.new_rpfn = rpc.orig_rpfn; + rpc.num_ports = max_root_ports(); + rpc.gbe_port = -1; + + rpc.pin_ownership = pci_read_config32(dev, 0x410); + root_port_config_update_gbe_port(); + + if (dev->chip_info != NULL) { + struct southbridge_intel_lynxpoint_config *config; + + config = dev->chip_info; + rpc.coalesce = config->pcie_port_coalesce; + } + } + + rp = root_port_number(dev); + if (rp > rpc.num_ports) { + printk(BIOS_ERR, "Found Root Port %d, expecting %d\n", + rp, rpc.num_ports); + return; + } + + /* Read the fuse configuration and pin ownership. */ + switch (rp) { + case 1: + rpc.strpfusecfg1 = pci_read_config32(dev, 0xfc); + rpc.b0d28f0_32c = pci_read_config32(dev, 0x32c); + break; + case 5: + rpc.strpfusecfg2 = pci_read_config32(dev, 0xfc); + rpc.b0d28f4_32c = pci_read_config32(dev, 0x32c); + break; + case 6: + rpc.b0d28f5_32c = pci_read_config32(dev, 0x32c); + rpc.strpfusecfg3 = pci_read_config32(dev, 0xfc); + break; + default: + break; + } + + /* Cache pci device. */ + rpc.ports[rp - 1] = dev; +} + +/* Update devicetree with new Root Port function number assignment */ +static void pch_pcie_device_set_func(int index, int pci_func) +{ + device_t dev; + unsigned new_devfn; + + dev = rpc.ports[index]; + + /* Set the new PCI function field for this Root Port. */ + rpc.new_rpfn &= ~RPFN_FNMASK(index); + rpc.new_rpfn |= RPFN_FNSET(index, pci_func); + + /* Determine the new devfn for this port */ + new_devfn = PCI_DEVFN(PCH_PCIE_DEV_SLOT, pci_func); + + if (dev->path.pci.devfn != new_devfn) { + printk(BIOS_DEBUG, + "PCH: PCIe map %02x.%1x -> %02x.%1x\n", + PCI_SLOT(dev->path.pci.devfn), + PCI_FUNC(dev->path.pci.devfn), + PCI_SLOT(new_devfn), PCI_FUNC(new_devfn)); + + dev->path.pci.devfn = new_devfn; + } +} + +static void pcie_enable_clock_gating(void) +{ + int i; + int is_lp; + int enabled_ports; + + is_lp = pch_is_lp(); + enabled_ports = 0; + + for (i = 0; i < rpc.num_ports; i++) { + device_t dev; + int rp; + + dev = rpc.ports[i]; + rp = root_port_number(dev); + + if (!dev->enabled) { + /* Configure shared resource clock gating. */ + if (rp == 1 || rp == 5 || (rp == 6 && is_lp)) + pcie_update_cfg8(dev, 0xe1, 0xc3, 0x3c); + + if (!is_lp) { + if (rp == 1 && !rpc.ports[1]->enabled && + !rpc.ports[2]->enabled && + !rpc.ports[3]->enabled) { + pcie_update_cfg8(dev, 0xe2, ~1, 1); + pcie_update_cfg8(dev, 0xe1, 0x7f, 0x80); + } + if (rp == 5 && !rpc.ports[5]->enabled && + !rpc.ports[6]->enabled && + !rpc.ports[7]->enabled) { + pcie_update_cfg8(dev, 0xe2, ~1, 1); + pcie_update_cfg8(dev, 0xe1, 0x7f, 0x80); + } + continue; + } + + pcie_update_cfg8(dev, 0xe2, ~(3 << 4), (3 << 4)); + pcie_update_cfg(dev, 0x420, ~(1 << 31), (1 << 31)); + + /* Per-Port CLKREQ# handling. */ + if (is_lp && gpio_is_native(18 + rp - 1)) + pcie_update_cfg(dev, 0x420, ~0, (3 << 29)); + + /* Enable static clock gating. */ + if (rp == 1 && !rpc.ports[1]->enabled && + !rpc.ports[2]->enabled && !rpc.ports[3]->enabled) { + pcie_update_cfg8(dev, 0xe2, ~1, 1); + pcie_update_cfg8(dev, 0xe1, 0x7f, 0x80); + } else if (rp == 5 || rp == 6) { + pcie_update_cfg8(dev, 0xe2, ~1, 1); + pcie_update_cfg8(dev, 0xe1, 0x7f, 0x80); + } + continue; + } + + enabled_ports++; + + /* Enable dynamic clock gating. */ + pcie_update_cfg8(dev, 0xe1, 0xfc, 0x03); + + if (is_lp) { + pcie_update_cfg8(dev, 0xe2, ~(1 << 6), (1 << 6)); + pcie_update_cfg8(dev, 0xe8, ~(3 << 2), (2 << 2)); + } + + /* Update PECR1 register. */ + pcie_update_cfg8(dev, 0xe8, ~0, 1); + + pcie_update_cfg8(dev, 0x324, ~(1 << 5), (1 < 5)); + + /* Per-Port CLKREQ# handling. */ + if (is_lp && gpio_is_native(18 + rp - 1)) + pcie_update_cfg(dev, 0x420, ~0, (3 << 29)); + + /* Configure shared resource clock gating. */ + if (rp == 1 || rp == 5 || (rp == 6 && is_lp)) + pcie_update_cfg8(dev, 0xe1, 0xc3, 0x3c); + } + + if (!enabled_ports && is_lp) + pcie_update_cfg8(rpc.ports[0], 0xe1, ~(1 << 6), (1 << 6)); +} + +static void root_port_commit_config(void) +{ + int i; + + /* If the first root port is disabled the coalesce ports. */ + if (!rpc.ports[0]->enabled) + rpc.coalesce = 1; + + /* Perform clock gating configuration. */ + pcie_enable_clock_gating(); + + for (i = 0; i < rpc.num_ports; i++) { + device_t dev; + u32 reg32; + + dev = rpc.ports[i]; + + if (dev == NULL) { + printk(BIOS_ERR, "Root Port %d device is NULL?\n", i+1); + continue; + } + + if (dev->enabled) + continue; + + printk(BIOS_DEBUG, "%s: Disabling device\n", dev_path(dev)); + + /* Ensure memory, io, and bus master are all disabled */ + reg32 = pci_read_config32(dev, PCI_COMMAND); + reg32 &= ~(PCI_COMMAND_MASTER | + PCI_COMMAND_MEMORY | PCI_COMMAND_IO); + pci_write_config32(dev, PCI_COMMAND, reg32); + + /* Disable this device if possible */ + pch_disable_devfn(dev); + } + + if (rpc.coalesce) { + int current_func; + + /* For all Root Ports N enabled ports get assigned the lower + * PCI function number. The disabled ones get upper PCI + * function numbers. */ + current_func = 0; + for (i = 0; i < rpc.num_ports; i++) { + if (!rpc.ports[i]->enabled) + continue; + pch_pcie_device_set_func(i, current_func); + current_func++; + } + + /* Allocate the disabled devices' PCI function number. */ + for (i = 0; i < rpc.num_ports; i++) { + if (rpc.ports[i]->enabled) + continue; + pch_pcie_device_set_func(i, current_func); + current_func++; + } + } + + printk(BIOS_SPEW, "PCH: RPFN 0x%08x -> 0x%08x\n", + rpc.orig_rpfn, rpc.new_rpfn); + RCBA32(RPFN) = rpc.new_rpfn; +} + +static void root_port_mark_disable(device_t dev) +{ + /* Mark device as disabled. */ + dev->enabled = 0; + /* Mark device to be hidden. */ + rpc.new_rpfn |= RPFN_HIDE(PCI_FUNC(dev->path.pci.devfn)); +} + +static void root_port_check_disable(device_t dev) +{ + int rp; + int is_lp; + + /* Device already disabled. */ + if (!dev->enabled) { + root_port_mark_disable(dev); + return; + } + + rp = root_port_number(dev); + + /* Is the GbE port mapped to this Root Port? */ + if (rp == rpc.gbe_port) { + root_port_mark_disable(dev); + return; + } + + is_lp = pch_is_lp(); + + /* Check Root Port Configuration. */ + switch (rp) { + case 2: + /* Root Port 2 is disabled for all lane configurations + * but config 00b (4x1 links). */ + if ((rpc.strpfusecfg1 >> 14) & 0x3) { + root_port_mark_disable(dev); + return; + } + break; + case 3: + /* Root Port 3 is disabled in config 11b (1x4 links). */ + if (((rpc.strpfusecfg1 >> 14) & 0x3) == 0x3) { + root_port_mark_disable(dev); + return; + } + break; + case 4: + /* Root Port 4 is disabled in configs 11b (1x4 links) + * and 10b (2x2 links). */ + if ((rpc.strpfusecfg1 >> 14) & 0x2) { + root_port_mark_disable(dev); + return; + } + break; + case 6: + if (is_lp) + break; + /* Root Port 6 is disabled for all lane configurations + * but config 00b (4x1 links). */ + if ((rpc.strpfusecfg2 >> 14) & 0x3) { + root_port_mark_disable(dev); + return; + } + break; + case 7: + if (is_lp) + break; + /* Root Port 3 is disabled in config 11b (1x4 links). */ + if (((rpc.strpfusecfg2 >> 14) & 0x3) == 0x3) { + root_port_mark_disable(dev); + return; + } + break; + case 8: + if (is_lp) + break; + /* Root Port 8 is disabled in configs 11b (1x4 links) + * and 10b (2x2 links). */ + if ((rpc.strpfusecfg2 >> 14) & 0x2) { + root_port_mark_disable(dev); + return; + } + break; + } + + /* Check Pin Ownership. */ + if (is_lp) { + switch (rp) { + case 1: + /* Bit 0 is Root Port 1 ownership. */ + if ((rpc.pin_ownership & 0x1) == 0) { + root_port_mark_disable(dev); + return; + } + break; + case 2: + /* Bit 2 is Root Port 2 ownership. */ + if ((rpc.pin_ownership & 0x4) == 0) { + root_port_mark_disable(dev); + return; + } + break; + case 6: + /* Bits 7:4 are Root Port 6 pin-lane ownership. */ + if ((rpc.pin_ownership & 0xf0) == 0) { + root_port_mark_disable(dev); + return; + } + break; + } + } else { + switch (rp) { + case 1: + /* Bits 4 and 0 are Root Port 1 ownership. */ + if ((rpc.pin_ownership & 0x11) == 0) { + root_port_mark_disable(dev); + return; + } + break; + case 2: + /* Bits 5 and 2 are Root Port 2 ownership. */ + if ((rpc.pin_ownership & 0x24) == 0) { + root_port_mark_disable(dev); + return; + } + break; + } + } +} + +static void pcie_update_cfg8(device_t dev, int reg, u8 mask, u8 or) +{ + u8 reg8; + + reg8 = pci_read_config8(dev, reg); + reg8 &= mask; + reg8 |= or; + pci_write_config8(dev, reg, reg8); +} + +static void pcie_update_cfg(device_t dev, int reg, u32 mask, u32 or) +{ + u32 reg32; + + reg32 = pci_read_config32(dev, reg); + reg32 &= mask; + reg32 |= or; + pci_write_config32(dev, reg, reg32); +} + +static void pcie_add_0x0202000_iobp(u32 reg) +{ + u32 reg32; + + reg32 = pch_iobp_read(reg); + reg32 += (0x2 << 16) | (0x2 << 8); + pch_iobp_write(reg, reg32); +} + +static void pch_pcie_early(struct device *dev) +{ + int rp; + int do_aspm; + int is_lp; + struct southbridge_intel_lynxpoint_config *config = dev->chip_info; + + rp = root_port_number(dev); + do_aspm = 0; + is_lp = pch_is_lp(); + + if (is_lp) { + switch (rp) { + case 1: + case 2: + case 3: + case 4: + /* Bits 31:28 of b0d28f0 0x32c register correspnd to + * Root Ports 4:1. */ + do_aspm = !!(rpc.b0d28f0_32c & (1 << (28 + rp - 1))); + break; + case 5: + /* Bit 28 of b0d28f4 0x32c register correspnd to + * Root Ports 4:1. */ + do_aspm = !!(rpc.b0d28f4_32c & (1 << 28)); + break; + case 6: + /* Bit 28 of b0d28f5 0x32c register correspnd to + * Root Ports 4:1. */ + do_aspm = !!(rpc.b0d28f5_32c & (1 << 28)); + break; + } + } else { + switch (rp) { + case 1: + case 2: + case 3: + case 4: + /* Bits 31:28 of b0d28f0 0x32c register correspnd to + * Root Ports 4:1. */ + do_aspm = !!(rpc.b0d28f0_32c & (1 << (28 + rp - 1))); + break; + case 5: + case 6: + case 7: + case 8: + /* Bit 31:28 of b0d28f4 0x32c register correspnd to + * Root Ports 8:5. */ + do_aspm = !!(rpc.b0d28f4_32c & (1 << (28 + rp - 5))); + break; + } + } + + /* Allow ASPM to be forced on in devicetree */ + if (config && (config->pcie_port_force_aspm & (1 << (rp - 1)))) + do_aspm = 1; + + printk(BIOS_DEBUG, "PCIe Root Port %d ASPM is %sabled\n", + rp, do_aspm ? "en" : "dis"); + + if (do_aspm) { + /* Set ASPM bits in MPC2 register. */ + pcie_update_cfg(dev, 0xd4, ~(0x3 << 2), (1 << 4) | (0x2 << 2)); + + /* Set unique clock exit latency in MPC register. */ + pcie_update_cfg(dev, 0xd8, ~(0x7 << 18), (0x7 << 18)); + + /* Set L1 exit latency in LCAP register. */ + pcie_update_cfg(dev, 0x4c, ~(0x7 << 15), (0x4 << 15)); + + if (is_lp) { + switch (rp) { + case 1: + pcie_add_0x0202000_iobp(0xe9002440); + break; + case 2: + pcie_add_0x0202000_iobp(0xe9002640); + break; + case 3: + pcie_add_0x0202000_iobp(0xe9000840); + break; + case 4: + pcie_add_0x0202000_iobp(0xe9000a40); + break; + case 5: + pcie_add_0x0202000_iobp(0xe9000c40); + pcie_add_0x0202000_iobp(0xe9000e40); + pcie_add_0x0202000_iobp(0xe9001040); + pcie_add_0x0202000_iobp(0xe9001240); + break; + case 6: + /* Update IOBP based on lane ownership. */ + if (rpc.pin_ownership & (1 << 4)) + pcie_add_0x0202000_iobp(0xea002040); + if (rpc.pin_ownership & (1 << 5)) + pcie_add_0x0202000_iobp(0xea002240); + if (rpc.pin_ownership & (1 << 6)) + pcie_add_0x0202000_iobp(0xea002440); + if (rpc.pin_ownership & (1 << 7)) + pcie_add_0x0202000_iobp(0xea002640); + break; + } + } else { + switch (rp) { + case 1: + if ((rpc.pin_ownership & 0x3) == 1) + pcie_add_0x0202000_iobp(0xe9002e40); + else + pcie_add_0x0202000_iobp(0xea002040); + break; + case 2: + if ((rpc.pin_ownership & 0xc) == 0x4) + pcie_add_0x0202000_iobp(0xe9002c40); + else + pcie_add_0x0202000_iobp(0xea002240); + break; + case 3: + pcie_add_0x0202000_iobp(0xe9002a40); + break; + case 4: + pcie_add_0x0202000_iobp(0xe9002840); + break; + case 5: + pcie_add_0x0202000_iobp(0xe9002640); + break; + case 6: + pcie_add_0x0202000_iobp(0xe9002440); + break; + case 7: + pcie_add_0x0202000_iobp(0xe9002240); + break; + case 8: + pcie_add_0x0202000_iobp(0xe9002040); + break; + } + } + + pcie_update_cfg(dev, 0x338, ~(1 << 26), 0); + } + + /* Enable LTR in Root Port. */ + pcie_update_cfg(dev, 0x64, ~(1 << 11), (1 << 11)); + pcie_update_cfg(dev, 0x68, ~(1 << 10), (1 << 10)); + + pcie_update_cfg(dev, 0x318, ~(0xffff << 16), (0x1414 << 16)); + + /* Set L1 exit latency in LCAP register. */ + if (!do_aspm && (pci_read_config8(dev, 0xf5) & 0x1)) + pcie_update_cfg(dev, 0x4c, ~(0x7 << 15), (0x4 << 15)); + else + pcie_update_cfg(dev, 0x4c, ~(0x7 << 15), (0x2 << 15)); + + pcie_update_cfg(dev, 0x314, 0x0, 0x743a361b); + + /* Set Common Clock Exit Latency in MPC register. */ + pcie_update_cfg(dev, 0xd8, ~(0x7 << 15), (0x3 << 15)); + + pcie_update_cfg(dev, 0x33c, ~0x00ffffff, 0x854c74); + + /* Set Invalid Recieve Range Check Enable in MPC register. */ + pcie_update_cfg(dev, 0xd8, ~0, (1 << 25)); + + pcie_update_cfg8(dev, 0xf5, 0x3f, 0); + + if (rp == 1 || rp == 5 || (is_lp && rp == 6)) + pcie_update_cfg8(dev, 0xf7, ~0xc, 0); + + /* Set EOI forwarding disable. */ + pcie_update_cfg(dev, 0xd4, ~0, (1 << 1)); + + /* Set something involving advanced error reporting. */ + pcie_update_cfg(dev, 0x100, ~((1 << 20) - 1), 0x10001); + + if (is_lp) + pcie_update_cfg(dev, 0x100, ~0, (1 << 29)); + + /* Read and write back write-once capability registers. */ + pcie_update_cfg(dev, 0x34, ~0, 0); + pcie_update_cfg(dev, 0x40, ~0, 0); + pcie_update_cfg(dev, 0x80, ~0, 0); + pcie_update_cfg(dev, 0x90, ~0, 0); +} + +static void pci_init(struct device *dev) +{ + u16 reg16; + u32 reg32; + + printk(BIOS_DEBUG, "Initializing PCH PCIe bridge.\n"); + + /* Enable SERR */ + reg32 = pci_read_config32(dev, PCI_COMMAND); + reg32 |= PCI_COMMAND_SERR; + pci_write_config32(dev, PCI_COMMAND, reg32); + + /* Enable Bus Master */ + reg32 = pci_read_config32(dev, PCI_COMMAND); + reg32 |= PCI_COMMAND_MASTER; + pci_write_config32(dev, PCI_COMMAND, reg32); + + /* Set Cache Line Size to 0x10 */ + // This has no effect but the OS might expect it + pci_write_config8(dev, 0x0c, 0x10); + + reg16 = pci_read_config16(dev, 0x3e); + reg16 &= ~(1 << 0); /* disable parity error response */ + // reg16 &= ~(1 << 1); /* disable SERR */ + reg16 |= (1 << 2); /* ISA enable */ + pci_write_config16(dev, 0x3e, reg16); + +#ifdef EVEN_MORE_DEBUG + reg32 = pci_read_config32(dev, 0x20); + printk(BIOS_SPEW, " MBL = 0x%08x\n", reg32); + reg32 = pci_read_config32(dev, 0x24); + printk(BIOS_SPEW, " PMBL = 0x%08x\n", reg32); + reg32 = pci_read_config32(dev, 0x28); + printk(BIOS_SPEW, " PMBU32 = 0x%08x\n", reg32); + reg32 = pci_read_config32(dev, 0x2c); + printk(BIOS_SPEW, " PMLU32 = 0x%08x\n", reg32); +#endif + + /* Clear errors in status registers */ + reg16 = pci_read_config16(dev, 0x06); + pci_write_config16(dev, 0x06, reg16); + reg16 = pci_read_config16(dev, 0x1e); + pci_write_config16(dev, 0x1e, reg16); +} + +static void pch_pcie_enable(device_t dev) +{ + /* Add this device to the root port config structure. */ + root_port_init_config(dev); + + /* Check to see if this Root Port should be disabled. */ + root_port_check_disable(dev); + + /* Power Management init before enumeration */ + if (dev->enabled) + pch_pcie_early(dev); + + /* + * When processing the last PCIe root port we can now + * update the Root Port Function Number and Hide register. + */ + if (root_port_is_last(dev)) + root_port_commit_config(); +} + +static void pcie_set_subsystem(device_t dev, unsigned vendor, unsigned device) +{ + /* NOTE: This is not the default position! */ + if (!vendor || !device) { + pci_write_config32(dev, 0x94, + pci_read_config32(dev, 0)); + } else { + pci_write_config32(dev, 0x94, + ((device & 0xffff) << 16) | (vendor & 0xffff)); + } +} + +static struct pci_operations pci_ops = { + .set_subsystem = pcie_set_subsystem, +}; + +static struct device_operations device_ops = { + .read_resources = pci_bus_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_bus_enable_resources, + .init = pci_init, + .enable = pch_pcie_enable, + .scan_bus = pciexp_scan_bridge, + .ops_pci = &pci_ops, +}; + +static const unsigned short pci_device_ids[] = { + /* Lynxpoint Mobile */ + 0x8c10, 0x8c12, 0x8c14, 0x8c16, 0x8c18, 0x8c1a, 0x8c1c, 0x8c1e, + /* Lynxpoint Low Power */ + 0x9c10, 0x9c12, 0x9c14, 0x9c16, 0x9c18, 0x9c1a, + 0 +}; + +static const struct pci_driver pch_pcie __pci_driver = { + .ops = &device_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .devices = pci_device_ids, +}; diff --git a/src/soc/intel/broadwell/pei_data.h b/src/soc/intel/broadwell/pei_data.h new file mode 100644 index 0000000000..f92c0a68e0 --- /dev/null +++ b/src/soc/intel/broadwell/pei_data.h @@ -0,0 +1,115 @@ +/* + * coreboot UEFI PEI wrapper + * + * Copyright (c) 2011, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PEI_DATA_H +#define PEI_DATA_H + +typedef void (*tx_byte_func)(unsigned char byte); +#define PEI_VERSION 15 + +#define MAX_USB2_PORTS 16 +#define MAX_USB3_PORTS 16 +#define USB_OC_PIN_SKIP 8 + +enum usb2_port_location { + USB_PORT_BACK_PANEL = 0, + USB_PORT_FRONT_PANEL, + USB_PORT_DOCK, + USB_PORT_MINI_PCIE, + USB_PORT_FLEX, + USB_PORT_INTERNAL, + USB_PORT_SKIP +}; + +/* Usb Port Length: + * [16:4] = length in inches in octal format + * [3:0] = decimal point + */ +struct usb2_port_setting { + uint16_t length; + uint8_t enable; + uint8_t over_current_pin; + uint8_t location; +} __attribute__((packed)); + +struct usb3_port_setting { + uint8_t enable; + uint8_t over_current_pin; +} __attribute__((packed)); + +struct pei_data +{ + uint32_t pei_version; + uint32_t mchbar; + uint32_t dmibar; + uint32_t epbar; + uint32_t pciexbar; + uint16_t smbusbar; + uint32_t wdbbar; + uint32_t wdbsize; + uint32_t hpet_address; + uint32_t rcba; + uint32_t pmbase; + uint32_t gpiobase; + uint32_t temp_mmio_base; + uint32_t system_type; // 0 Mobile, 1 Desktop/Server + uint32_t tseg_size; + uint8_t spd_addresses[4]; + int boot_mode; + int ec_present; + int gbe_enable; + // 0 = leave channel enabled + // 1 = disable dimm 0 on channel + // 2 = disable dimm 1 on channel + // 3 = disable dimm 0+1 on channel + int dimm_channel0_disabled; + int dimm_channel1_disabled; + /* Enable 2x Refresh Mode */ + int ddr_refresh_2x; + int dq_pins_interleaved; + /* Data read from flash and passed into MRC */ + unsigned char *mrc_input; + unsigned int mrc_input_len; + /* Data from MRC that should be saved to flash */ + unsigned char *mrc_output; + unsigned int mrc_output_len; + /* + * Max frequency DDR3 could be ran at. Could be one of four values: 800, + * 1067, 1333, 1600 + */ + uint32_t max_ddr3_freq; + /* Route all USB ports to XHCI controller in resume path */ + int usb_xhci_on_resume; + struct usb2_port_setting usb2_ports[MAX_USB2_PORTS]; + struct usb3_port_setting usb3_ports[MAX_USB3_PORTS]; + uint8_t spd_data[4][256]; + tx_byte_func tx_byte; +} __attribute__((packed)); + +#endif diff --git a/src/soc/intel/broadwell/pmutil.c b/src/soc/intel/broadwell/pmutil.c new file mode 100644 index 0000000000..1b95457fdd --- /dev/null +++ b/src/soc/intel/broadwell/pmutil.c @@ -0,0 +1,560 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +/* + * Helper functions for dealing with power management registers + * and the differences between LynxPoint-H and LynxPoint-LP. + */ + +#include +#include +#include +#include +#include +#include "pch.h" + +#if CONFIG_INTEL_LYNXPOINT_LP +#include "lp_gpio.h" +#endif + +/* These defines are here to handle the LP variant code dynamically. If these + * values are defined in lp_gpio.h but when a non-LP board is being built, the + * build will fail. */ +#define GPIO_ALT_GPI_SMI_STS 0x50 +#define GPIO_ALT_GPI_SMI_EN 0x54 + +/* Print status bits with descriptive names */ +static void print_status_bits(u32 status, const char *bit_names[]) +{ + int i; + + if (!status) + return; + + for (i=31; i>=0; i--) { + if (status & (1 << i)) { + if (bit_names[i]) + printk(BIOS_DEBUG, "%s ", bit_names[i]); + else + printk(BIOS_DEBUG, "BIT%d ", i); + } + } +} + +/* Print status bits as GPIO numbers */ +static void print_gpio_status(u32 status, int start) +{ + int i; + + if (!status) + return; + + for (i=31; i>=0; i--) { + if (status & (1 << i)) + printk(BIOS_DEBUG, "GPIO%d ", start + i); + } +} + + +/* + * PM1_CNT + */ + +/* Enable events in PM1 control register */ +void enable_pm1_control(u32 mask) +{ + u32 pm1_cnt = inl(get_pmbase() + PM1_CNT); + pm1_cnt |= mask; + outl(pm1_cnt, get_pmbase() + PM1_CNT); +} + +/* Disable events in PM1 control register */ +void disable_pm1_control(u32 mask) +{ + u32 pm1_cnt = inl(get_pmbase() + PM1_CNT); + pm1_cnt &= ~mask; + outl(pm1_cnt, get_pmbase() + PM1_CNT); +} + + +/* + * PM1 + */ + +/* Clear and return PM1 status register */ +static u16 reset_pm1_status(void) +{ + u16 pm1_sts = inw(get_pmbase() + PM1_STS); + outw(pm1_sts, get_pmbase() + PM1_STS); + return pm1_sts; +} + +/* Print PM1 status bits */ +static u16 print_pm1_status(u16 pm1_sts) +{ + const char *pm1_sts_bits[] = { + [0] = "TMROF", + [4] = "BM", + [5] = "GBL", + [8] = "PWRBTN", + [10] = "RTC", + [11] = "PRBTNOR", + [14] = "PCIEXPWAK", + [15] = "WAK", + }; + + if (!pm1_sts) + return 0; + + printk(BIOS_SPEW, "PM1_STS: "); + print_status_bits(pm1_sts, pm1_sts_bits); + printk(BIOS_SPEW, "\n"); + + return pm1_sts; +} + +/* Print, clear, and return PM1 status */ +u16 clear_pm1_status(void) +{ + return print_pm1_status(reset_pm1_status()); +} + +/* Set the PM1 register to events */ +void enable_pm1(u16 events) +{ + outw(events, get_pmbase() + PM1_EN); +} + + +/* + * SMI + */ + +/* Clear and return SMI status register */ +static u32 reset_smi_status(void) +{ + u32 smi_sts = inl(get_pmbase() + SMI_STS); + outl(smi_sts, get_pmbase() + SMI_STS); + return smi_sts; +} + +/* Print SMI status bits */ +static u32 print_smi_status(u32 smi_sts) +{ + const char *smi_sts_bits[] = { + [2] = "BIOS", + [3] = "LEGACY_USB", + [4] = "SLP_SMI", + [5] = "APM", + [6] = "SWSMI_TMR", + [8] = "PM1", + [9] = "GPE0", + [10] = "GPI", + [11] = "MCSMI", + [12] = "DEVMON", + [13] = "TCO", + [14] = "PERIODIC", + [15] = "SERIRQ_SMI", + [16] = "SMBUS_SMI", + [17] = "LEGACY_USB2", + [18] = "INTEL_USB2", + [20] = "PCI_EXP_SMI", + [21] = "MONITOR", + [26] = "SPI", + [27] = "GPIO_UNLOCK" + }; + + if (!smi_sts) + return 0; + + printk(BIOS_DEBUG, "SMI_STS: "); + print_status_bits(smi_sts, smi_sts_bits); + printk(BIOS_DEBUG, "\n"); + + return smi_sts; +} + +/* Print, clear, and return SMI status */ +u32 clear_smi_status(void) +{ + return print_smi_status(reset_smi_status()); +} + +/* Enable SMI event */ +void enable_smi(u32 mask) +{ + u32 smi_en = inl(get_pmbase() + SMI_EN); + smi_en |= mask; + outl(smi_en, get_pmbase() + SMI_EN); +} + +/* Disable SMI event */ +void disable_smi(u32 mask) +{ + u32 smi_en = inl(get_pmbase() + SMI_EN); + smi_en &= ~mask; + outl(smi_en, get_pmbase() + SMI_EN); +} + + +/* + * ALT_GP_SMI + */ + +/* Clear GPIO SMI status and return events that are enabled and active */ +static u32 reset_alt_smi_status(void) +{ + u32 alt_sts, alt_en; + + if (pch_is_lp()) { + /* LynxPoint-LP moves this to GPIO region as dword */ + alt_sts = inl(get_gpiobase() + GPIO_ALT_GPI_SMI_STS); + outl(alt_sts, get_gpiobase() + GPIO_ALT_GPI_SMI_STS); + + alt_en = inl(get_gpiobase() + GPIO_ALT_GPI_SMI_EN); + } else { + u16 pmbase = get_pmbase(); + + /* LynxPoint-H adds a second enable/status word */ + alt_sts = inw(pmbase + ALT_GP_SMI_STS2); + outw(alt_sts & 0xffff, pmbase + ALT_GP_SMI_STS2); + + alt_sts <<= 16; + alt_sts |= inw(pmbase + ALT_GP_SMI_STS); + outw(alt_sts & 0xffff, pmbase + ALT_GP_SMI_STS); + + alt_en = inw(pmbase + ALT_GP_SMI_EN2); + alt_en <<= 16; + alt_en |= inw(pmbase + ALT_GP_SMI_EN); + } + + /* Only report enabled events */ + return alt_sts & alt_en; +} + +/* Print GPIO SMI status bits */ +static u32 print_alt_smi_status(u32 alt_sts) +{ + if (!alt_sts) + return 0; + + printk(BIOS_DEBUG, "ALT_STS: "); + + if (pch_is_lp()) { + /* First 16 events are GPIO 32-47 */ + print_gpio_status(alt_sts & 0xffff, 32); + } else { + const char *alt_sts_bits_high[] = { + [0] = "GPIO17", + [1] = "GPIO19", + [2] = "GPIO21", + [3] = "GPIO22", + [4] = "GPIO43", + [5] = "GPIO56", + [6] = "GPIO57", + [7] = "GPIO60", + }; + + /* First 16 events are GPIO 0-15 */ + print_gpio_status(alt_sts & 0xffff, 0); + print_status_bits(alt_sts >> 16, alt_sts_bits_high); + } + + printk(BIOS_DEBUG, "\n"); + + return alt_sts; +} + +/* Print, clear, and return GPIO SMI status */ +u32 clear_alt_smi_status(void) +{ + return print_alt_smi_status(reset_alt_smi_status()); +} + +/* Enable GPIO SMI events */ +void enable_alt_smi(u32 mask) +{ + if (pch_is_lp()) { + u32 alt_en; + + alt_en = inl(get_gpiobase() + GPIO_ALT_GPI_SMI_EN); + alt_en |= mask; + outl(alt_en, get_gpiobase() + GPIO_ALT_GPI_SMI_EN); + } else { + u16 pmbase = get_pmbase(); + u16 alt_en; + + /* Lower enable register */ + alt_en = inw(pmbase + ALT_GP_SMI_EN); + alt_en |= mask & 0xffff; + outw(alt_en, pmbase + ALT_GP_SMI_EN); + + /* Upper enable register */ + alt_en = inw(pmbase + ALT_GP_SMI_EN2); + alt_en |= (mask >> 16) & 0xffff; + outw(alt_en, pmbase + ALT_GP_SMI_EN2); + } +} + + +/* + * TCO + */ + +/* Clear TCO status and return events that are enabled and active */ +static u32 reset_tco_status(void) +{ + u32 tcobase = get_pmbase() + 0x60; + u32 tco_sts = inl(tcobase + 0x04); + u32 tco_en = inl(get_pmbase() + 0x68); + + /* Don't clear BOOT_STS before SECOND_TO_STS */ + outl(tco_sts & ~(1 << 18), tcobase + 0x04); + + /* Clear BOOT_STS */ + if (tco_sts & (1 << 18)) + outl(tco_sts & (1 << 18), tcobase + 0x04); + + return tco_sts & tco_en; +} + +/* Print TCO status bits */ +static u32 print_tco_status(u32 tco_sts) +{ + const char *tco_sts_bits[] = { + [0] = "NMI2SMI", + [1] = "SW_TCO", + [2] = "TCO_INT", + [3] = "TIMEOUT", + [7] = "NEWCENTURY", + [8] = "BIOSWR", + [9] = "DMISCI", + [10] = "DMISMI", + [12] = "DMISERR", + [13] = "SLVSEL", + [16] = "INTRD_DET", + [17] = "SECOND_TO", + [18] = "BOOT", + [20] = "SMLINK_SLV" + }; + + if (!tco_sts) + return 0; + + printk(BIOS_DEBUG, "TCO_STS: "); + print_status_bits(tco_sts, tco_sts_bits); + printk(BIOS_DEBUG, "\n"); + + return tco_sts; +} + +/* Print, clear, and return TCO status */ +u32 clear_tco_status(void) +{ + return print_tco_status(reset_tco_status()); +} + +/* Enable TCO SCI */ +void enable_tco_sci(void) +{ + u16 gpe0_sts = pch_is_lp() ? LP_GPE0_STS_4 : GPE0_STS; + + /* Clear pending events */ + outl(get_pmbase() + gpe0_sts, TCOSCI_STS); + + /* Enable TCO SCI events */ + enable_gpe(TCOSCI_EN); +} + + +/* + * GPE0 + */ + +/* Clear a GPE0 status and return events that are enabled and active */ +static u32 reset_gpe_status(u16 sts_reg, u16 en_reg) +{ + u32 gpe0_sts = inl(get_pmbase() + sts_reg); + u32 gpe0_en = inl(get_pmbase() + en_reg); + + outl(gpe0_sts, get_pmbase() + sts_reg); + + /* Only report enabled events */ + return gpe0_sts & gpe0_en; +} + +/* Print GPE0 status bits */ +static u32 print_gpe_status(u32 gpe0_sts, const char *bit_names[]) +{ + if (!gpe0_sts) + return 0; + + printk(BIOS_DEBUG, "GPE0_STS: "); + print_status_bits(gpe0_sts, bit_names); + printk(BIOS_DEBUG, "\n"); + + return gpe0_sts; +} + +/* Print GPE0 GPIO status bits */ +static u32 print_gpe_gpio(u32 gpe0_sts, int start) +{ + if (!gpe0_sts) + return 0; + + printk(BIOS_DEBUG, "GPE0_STS: "); + print_gpio_status(gpe0_sts, start); + printk(BIOS_DEBUG, "\n"); + + return gpe0_sts; +} + +/* Print, clear, and return LynxPoint-H GPE0 status */ +static u32 clear_lpt_gpe_status(void) +{ + const char *gpe0_sts_bits_low[] = { + [1] = "HOTPLUG", + [2] = "SWGPE", + [6] = "TCO_SCI", + [7] = "SMB_WAK", + [8] = "RI", + [9] = "PCI_EXP", + [10] = "BATLOW", + [11] = "PME", + [13] = "PME_B0", + [16] = "GPIO0", + [17] = "GPIO1", + [18] = "GPIO2", + [19] = "GPIO3", + [20] = "GPIO4", + [21] = "GPIO5", + [22] = "GPIO6", + [23] = "GPIO7", + [24] = "GPIO8", + [25] = "GPIO9", + [26] = "GPIO10", + [27] = "GPIO11", + [28] = "GPIO12", + [29] = "GPIO13", + [30] = "GPIO14", + [31] = "GPIO15", + }; + const char *gpe0_sts_bits_high[] = { + [3] = "GPIO27", + [6] = "WADT", + [24] = "GPIO17", + [25] = "GPIO19", + [26] = "GPIO21", + [27] = "GPIO22", + [28] = "GPIO43", + [29] = "GPIO56", + [30] = "GPIO57", + [31] = "GPIO60", + }; + + /* High bits */ + print_gpe_status(reset_gpe_status(GPE0_STS_2, GPE0_EN_2), + gpe0_sts_bits_high); + + /* Standard GPE and GPIO 0-31 */ + return print_gpe_status(reset_gpe_status(GPE0_STS, GPE0_EN), + gpe0_sts_bits_low); +} + +/* Print, clear, and return LynxPoint-LP GPE0 status */ +static u32 clear_lpt_lp_gpe_status(void) +{ + const char *gpe0_sts_4_bits[] = { + [1] = "HOTPLUG", + [2] = "SWGPE", + [6] = "TCO_SCI", + [7] = "SMB_WAK", + [9] = "PCI_EXP", + [10] = "BATLOW", + [11] = "PME", + [12] = "ME", + [13] = "PME_B0", + [16] = "GPIO27", + [18] = "WADT" + }; + + /* GPIO 0-31 */ + print_gpe_gpio(reset_gpe_status(LP_GPE0_STS_1, LP_GPE0_EN_1), 0); + + /* GPIO 32-63 */ + print_gpe_gpio(reset_gpe_status(LP_GPE0_STS_2, LP_GPE0_EN_2), 32); + + /* GPIO 64-94 */ + print_gpe_gpio(reset_gpe_status(LP_GPE0_STS_3, LP_GPE0_EN_3), 64); + + /* Standard GPE */ + return print_gpe_status(reset_gpe_status(LP_GPE0_STS_4, LP_GPE0_EN_4), + gpe0_sts_4_bits); +} + +/* Clear all GPE status and return "standard" GPE event status */ +u32 clear_gpe_status(void) +{ + if (pch_is_lp()) + return clear_lpt_lp_gpe_status(); + else + return clear_lpt_gpe_status(); +} + +/* Enable all requested GPE */ +void enable_all_gpe(u32 set1, u32 set2, u32 set3, u32 set4) +{ + u16 pmbase = get_pmbase(); + + if (pch_is_lp()) { + outl(set1, pmbase + LP_GPE0_EN_1); + outl(set2, pmbase + LP_GPE0_EN_2); + outl(set3, pmbase + LP_GPE0_EN_3); + outl(set4, pmbase + LP_GPE0_EN_4); + } else { + outl(set1, pmbase + GPE0_EN); + outl(set2, pmbase + GPE0_EN_2); + } +} + +/* Disable all GPE */ +void disable_all_gpe(void) +{ + enable_all_gpe(0, 0, 0, 0); +} + +/* Enable a standard GPE */ +void enable_gpe(u32 mask) +{ + u32 gpe0_reg = pch_is_lp() ? LP_GPE0_EN_4 : GPE0_EN; + u32 gpe0_en = inl(get_pmbase() + gpe0_reg); + gpe0_en |= mask; + outl(gpe0_en, get_pmbase() + gpe0_reg); +} + +/* Disable a standard GPE */ +void disable_gpe(u32 mask) +{ + u32 gpe0_reg = pch_is_lp() ? LP_GPE0_EN_4 : GPE0_EN; + u32 gpe0_en = inl(get_pmbase() + gpe0_reg); + gpe0_en &= ~mask; + outl(gpe0_en, get_pmbase() + gpe0_reg); +} diff --git a/src/soc/intel/broadwell/reset.c b/src/soc/intel/broadwell/reset.c new file mode 100644 index 0000000000..429aad0ccc --- /dev/null +++ b/src/soc/intel/broadwell/reset.c @@ -0,0 +1,32 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008-2009 coresystems GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include + +void soft_reset(void) +{ + outb(0x04, 0xcf9); +} + +void hard_reset(void) +{ + outb(0x06, 0xcf9); +} diff --git a/src/soc/intel/broadwell/romstage/cache_as_ram.inc b/src/soc/intel/broadwell/romstage/cache_as_ram.inc new file mode 100644 index 0000000000..8601f46a9b --- /dev/null +++ b/src/soc/intel/broadwell/romstage/cache_as_ram.inc @@ -0,0 +1,331 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2000,2007 Ronald G. Minnich + * Copyright (C) 2007-2008 coresystems GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include + +/* The full cache-as-ram size includes the cache-as-ram portion from coreboot + * and the space used by the reference code. These 2 values combined should + * be a power of 2 because the MTRR setup assumes that. */ +#define CACHE_AS_RAM_SIZE \ + (CONFIG_DCACHE_RAM_SIZE + CONFIG_DCACHE_RAM_MRC_VAR_SIZE) +#define CACHE_AS_RAM_BASE CONFIG_DCACHE_RAM_BASE + +/* Cache 4GB - MRC_SIZE_KB for MRC */ +#define CACHE_MRC_BYTES ((CONFIG_CACHE_MRC_SIZE_KB << 10) - 1) +#define CACHE_MRC_BASE (0xFFFFFFFF - CACHE_MRC_BYTES) +#define CACHE_MRC_MASK (~CACHE_MRC_BYTES) + +#define CPU_MAXPHYSADDR CONFIG_CPU_ADDR_BITS +#define CPU_PHYSMASK_HI (1 << (CPU_MAXPHYSADDR - 32) - 1) + +#define NoEvictMod_MSR 0x2e0 + + /* Save the BIST result. */ + movl %eax, %ebp + +cache_as_ram: + post_code(0x20) + + /* Send INIT IPI to all excluding ourself. */ + movl $0x000C4500, %eax + movl $0xFEE00300, %esi + movl %eax, (%esi) + + /* All CPUs need to be in Wait for SIPI state */ +wait_for_sipi: + movl (%esi), %eax + bt $12, %eax + jc wait_for_sipi + + post_code(0x21) + /* Zero out all fixed range and variable range MTRRs. */ + movl $mtrr_table, %esi + movl $((mtrr_table_end - mtrr_table) / 2), %edi + xorl %eax, %eax + xorl %edx, %edx +clear_mtrrs: + movw (%esi), %bx + movzx %bx, %ecx + wrmsr + add $2, %esi + dec %edi + jnz clear_mtrrs + + post_code(0x22) + /* Configure the default memory type to uncacheable. */ + movl $MTRRdefType_MSR, %ecx + rdmsr + andl $(~0x00000cff), %eax + wrmsr + + post_code(0x23) + /* Set Cache-as-RAM base address. */ + movl $(MTRRphysBase_MSR(0)), %ecx + movl $(CACHE_AS_RAM_BASE | MTRR_TYPE_WRBACK), %eax + xorl %edx, %edx + wrmsr + + post_code(0x24) + /* Set Cache-as-RAM mask. */ + movl $(MTRRphysMask_MSR(0)), %ecx + movl $(~(CACHE_AS_RAM_SIZE - 1) | MTRRphysMaskValid), %eax + movl $CPU_PHYSMASK_HI, %edx + wrmsr + + post_code(0x25) + + /* Enable MTRR. */ + movl $MTRRdefType_MSR, %ecx + rdmsr + orl $MTRRdefTypeEn, %eax + wrmsr + + /* Enable cache (CR0.CD = 0, CR0.NW = 0). */ + movl %cr0, %eax + andl $(~(CR0_CacheDisable | CR0_NoWriteThrough)), %eax + invd + movl %eax, %cr0 + + /* enable the 'no eviction' mode */ + movl $NoEvictMod_MSR, %ecx + rdmsr + orl $1, %eax + andl $~2, %eax + wrmsr + + /* Clear the cache memory region. This will also fill up the cache */ + movl $CACHE_AS_RAM_BASE, %esi + movl %esi, %edi + movl $(CACHE_AS_RAM_SIZE / 4), %ecx + // movl $0x23322332, %eax + xorl %eax, %eax + rep stosl + + /* enable the 'no eviction run' state */ + movl $NoEvictMod_MSR, %ecx + rdmsr + orl $3, %eax + wrmsr + + post_code(0x26) + /* Enable Cache-as-RAM mode by disabling cache. */ + movl %cr0, %eax + orl $CR0_CacheDisable, %eax + movl %eax, %cr0 + + /* Enable cache for our code in Flash because we do XIP here */ + movl $MTRRphysBase_MSR(1), %ecx + xorl %edx, %edx + /* + * IMPORTANT: The following calculation _must_ be done at runtime. See + * http://www.coreboot.org/pipermail/coreboot/2010-October/060855.html + */ + movl $copy_and_run, %eax + andl $(~(CONFIG_XIP_ROM_SIZE - 1)), %eax + orl $MTRR_TYPE_WRPROT, %eax + wrmsr + + movl $MTRRphysMask_MSR(1), %ecx + movl $CPU_PHYSMASK_HI, %edx + movl $(~(CONFIG_XIP_ROM_SIZE - 1) | MTRRphysMaskValid), %eax + wrmsr + + post_code(0x27) +#if CONFIG_CACHE_MRC_BIN + /* Enable caching for ram init code to run faster */ + movl $MTRRphysBase_MSR(2), %ecx + movl $(CACHE_MRC_BASE | MTRR_TYPE_WRPROT), %eax + xorl %edx, %edx + wrmsr + movl $MTRRphysMask_MSR(2), %ecx + movl $(CACHE_MRC_MASK | MTRRphysMaskValid), %eax + movl $CPU_PHYSMASK_HI, %edx + wrmsr +#endif + + post_code(0x28) + /* Enable cache. */ + movl %cr0, %eax + andl $(~(CR0_CacheDisable | CR0_NoWriteThrough)), %eax + movl %eax, %cr0 + + /* Setup the stack. */ + movl $(CONFIG_DCACHE_RAM_BASE + CONFIG_DCACHE_RAM_SIZE), %eax + movl %eax, %esp + + /* Restore the BIST result. */ + movl %ebp, %eax + movl %esp, %ebp + pushl %eax + +before_romstage: + post_code(0x29) + /* Call romstage.c main function. */ + call romstage_main + /* Save return value from romstage_main. It contains the stack to use + * after cache-as-ram is torn down. It also contains the information + * for setting up MTTRs. */ + movl %eax, %ebx + + post_code(0x2f) + + /* Copy global variable space (for USBDEBUG) to memory */ +#if CONFIG_USBDEBUG + cld + movl $(CONFIG_DCACHE_RAM_BASE + CONFIG_DCACHE_RAM_SIZE - 24), %esi + movl $(CONFIG_RAMTOP - 24), %edi + movl $24, %ecx + rep movsb +#endif + + post_code(0x30) + + /* Disable cache. */ + movl %cr0, %eax + orl $CR0_CacheDisable, %eax + movl %eax, %cr0 + + post_code(0x31) + + /* Disable MTRR. */ + movl $MTRRdefType_MSR, %ecx + rdmsr + andl $(~MTRRdefTypeEn), %eax + wrmsr + + post_code(0x31) + + /* Disable the no eviction run state */ + movl $NoEvictMod_MSR, %ecx + rdmsr + andl $~2, %eax + wrmsr + + invd + + /* Disable the no eviction mode */ + rdmsr + andl $~1, %eax + wrmsr + +#if CONFIG_CACHE_MRC_BIN + /* Clear MTRR that was used to cache MRC */ + xorl %eax, %eax + xorl %edx, %edx + movl $MTRRphysBase_MSR(2), %ecx + wrmsr + movl $MTRRphysMask_MSR(2), %ecx + wrmsr +#endif + + post_code(0x33) + + /* Enable cache. */ + movl %cr0, %eax + andl $~(CR0_CacheDisable | CR0_NoWriteThrough), %eax + movl %eax, %cr0 + + post_code(0x36) + + /* Disable cache. */ + movl %cr0, %eax + orl $CR0_CacheDisable, %eax + movl %eax, %cr0 + + post_code(0x38) + + /* Setup stack as indicated by return value from ramstage_main(). */ + movl %ebx, %esp + + /* Get number of MTTRs. */ + popl %ebx + movl $MTRRphysBase_MSR(0), %ecx +1: + testl %ebx, %ebx + jz 1f + + /* Low 32 bits of MTTR base. */ + popl %eax + /* Upper 32 bits of MTTR base. */ + popl %edx + /* Write MTRR base. */ + wrmsr + inc %ecx + /* Low 32 bits of MTTR mask. */ + popl %eax + /* Upper 32 bits of MTTR mask. */ + popl %edx + /* Write MTRR mask. */ + wrmsr + inc %ecx + + dec %ebx + jmp 1b +1: + post_code(0x39) + + /* And enable cache again after setting MTRRs. */ + movl %cr0, %eax + andl $~(CR0_CacheDisable | CR0_NoWriteThrough), %eax + movl %eax, %cr0 + + post_code(0x3a) + + /* Enable MTRR. */ + movl $MTRRdefType_MSR, %ecx + rdmsr + orl $MTRRdefTypeEn, %eax + wrmsr + + post_code(0x3b) + + /* Invalidate the cache again. */ + invd + + post_code(0x3c) + +__main: + post_code(POST_PREPARE_RAMSTAGE) + cld /* Clear direction flag. */ + call romstage_after_car + +.Lhlt: + post_code(POST_DEAD_CODE) + hlt + jmp .Lhlt + +mtrr_table: + /* Fixed MTRRs */ + .word 0x250, 0x258, 0x259 + .word 0x268, 0x269, 0x26A + .word 0x26B, 0x26C, 0x26D + .word 0x26E, 0x26F + /* Variable MTRRs */ + .word 0x200, 0x201, 0x202, 0x203 + .word 0x204, 0x205, 0x206, 0x207 + .word 0x208, 0x209, 0x20A, 0x20B + .word 0x20C, 0x20D, 0x20E, 0x20F + .word 0x210, 0x211, 0x212, 0x213 +mtrr_table_end: + diff --git a/src/soc/intel/broadwell/romstage/pch.c b/src/soc/intel/broadwell/romstage/pch.c new file mode 100644 index 0000000000..29443a4e20 --- /dev/null +++ b/src/soc/intel/broadwell/romstage/pch.c @@ -0,0 +1,163 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include +#include +#include +#include +#include +#include "pch.h" +#include "chip.h" + +#if CONFIG_INTEL_LYNXPOINT_LP +#include "lp_gpio.h" +#else +#include "gpio.h" +#endif + +#include + +const struct rcba_config_instruction pch_early_config[] = { + /* Enable IOAPIC */ + RCBA_SET_REG_16(OIC, 0x0100), + /* PCH BWG says to read back the IOAPIC enable register */ + RCBA_READ_REG_16(OIC), + + RCBA_END_CONFIG, +}; + +int pch_is_lp(void) +{ + u8 id = pci_read_config8(PCH_LPC_DEV, PCI_DEVICE_ID + 1); + return id == PCH_TYPE_LPT_LP; +} + +static void pch_enable_bars(void) +{ + /* Setting up Southbridge. In the northbridge code. */ + pci_write_config32(PCH_LPC_DEV, RCBA, DEFAULT_RCBA | 1); + + pci_write_config32(PCH_LPC_DEV, PMBASE, DEFAULT_PMBASE | 1); + /* Enable ACPI BAR */ + pci_write_config8(PCH_LPC_DEV, ACPI_CNTL, 0x80); + + pci_write_config32(PCH_LPC_DEV, GPIO_BASE, DEFAULT_GPIOBASE|1); + + /* Enable GPIO functionality. */ + pci_write_config8(PCH_LPC_DEV, GPIO_CNTL, 0x10); +} + +static void pch_generic_setup(void) +{ + printk(BIOS_DEBUG, "Disabling Watchdog reboot..."); + RCBA32(GCS) = RCBA32(GCS) | (1 << 5); /* No reset */ + outw((1 << 11), DEFAULT_PMBASE | 0x60 | 0x08); /* halt timer */ + printk(BIOS_DEBUG, " done.\n"); +} + +static int sleep_type_s3(void) +{ + u32 pm1_cnt; + u16 pm1_sts; + int is_s3 = 0; + + /* Check PM1_STS[15] to see if we are waking from Sx */ + pm1_sts = inw(DEFAULT_PMBASE + PM1_STS); + if (pm1_sts & WAK_STS) { + /* Read PM1_CNT[12:10] to determine which Sx state */ + pm1_cnt = inl(DEFAULT_PMBASE + PM1_CNT); + if (((pm1_cnt >> 10) & 7) == SLP_TYP_S3) { + /* Clear SLP_TYPE. */ + outl(pm1_cnt & ~(7 << 10), DEFAULT_PMBASE + PM1_CNT); + is_s3 = 1; + } + } + return is_s3; +} + +void pch_enable_lpc(void) +{ + const struct device *dev = dev_find_slot(0, PCI_DEVFN(0x1f, 0)); + const struct southbridge_intel_lynxpoint_config *config = NULL; + + /* Set COM1/COM2 decode range */ + pci_write_config16(PCH_LPC_DEV, LPC_IO_DEC, 0x0010); + + /* Enable SuperIO + MC + COM1 + PS/2 Keyboard/Mouse */ + u16 lpc_config = CNF1_LPC_EN | CNF2_LPC_EN | GAMEL_LPC_EN | + COMA_LPC_EN | KBC_LPC_EN | MC_LPC_EN; + pci_write_config16(PCH_LPC_DEV, LPC_EN, lpc_config); + + /* Set up generic decode ranges */ + if (!dev) + return; + if (dev->chip_info) + config = dev->chip_info; + if (!config) + return; + + pci_write_config32(PCH_LPC_DEV, LPC_GEN1_DEC, config->gen1_dec); + pci_write_config32(PCH_LPC_DEV, LPC_GEN2_DEC, config->gen2_dec); + pci_write_config32(PCH_LPC_DEV, LPC_GEN3_DEC, config->gen3_dec); + pci_write_config32(PCH_LPC_DEV, LPC_GEN4_DEC, config->gen4_dec); +} + +int early_pch_init(const void *gpio_map, + const struct rcba_config_instruction *rcba_config) +{ + int wake_from_s3; + + pch_enable_lpc(); + + pch_enable_bars(); + +#if CONFIG_INTEL_LYNXPOINT_LP + setup_pch_lp_gpios(gpio_map); +#else + setup_pch_gpios(gpio_map); +#endif + +#if CONFIG_CHROMEOS + save_chromeos_gpios(); +#endif + + console_init(); + + pch_generic_setup(); + + /* Enable SMBus for reading SPDs. */ + enable_smbus(); + + /* Early PCH RCBA settings */ + pch_config_rcba(pch_early_config); + + /* Mainboard RCBA settings */ + pch_config_rcba(rcba_config); + + wake_from_s3 = sleep_type_s3(); + +#if CONFIG_ELOG_BOOT_COUNT + if (!wake_from_s3) + boot_count_increment(); +#endif + + /* Report if we are waking from s3. */ + return wake_from_s3; +} diff --git a/src/soc/intel/broadwell/romstage/raminit.c b/src/soc/intel/broadwell/romstage/raminit.c new file mode 100644 index 0000000000..1b1a42b365 --- /dev/null +++ b/src/soc/intel/broadwell/romstage/raminit.c @@ -0,0 +1,221 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "raminit.h" +#include "pei_data.h" +#include "haswell.h" + +#if CONFIG_CHROMEOS +#include +#else +#define recovery_mode_enabled(x) 0 +#endif + +void save_mrc_data(struct pei_data *pei_data) +{ + struct mrc_data_container *mrcdata; + int output_len = ALIGN(pei_data->mrc_output_len, 16); + + /* Save the MRC S3 restore data to cbmem */ + mrcdata = cbmem_add + (CBMEM_ID_MRCDATA, + output_len + sizeof(struct mrc_data_container)); + + printk(BIOS_DEBUG, "Relocate MRC DATA from %p to %p (%u bytes)\n", + pei_data->mrc_output, mrcdata, output_len); + + mrcdata->mrc_signature = MRC_DATA_SIGNATURE; + mrcdata->mrc_data_size = output_len; + mrcdata->reserved = 0; + memcpy(mrcdata->mrc_data, pei_data->mrc_output, + pei_data->mrc_output_len); + + /* Zero the unused space in aligned buffer. */ + if (output_len > pei_data->mrc_output_len) + memset(mrcdata->mrc_data+pei_data->mrc_output_len, 0, + output_len - pei_data->mrc_output_len); + + mrcdata->mrc_checksum = compute_ip_checksum(mrcdata->mrc_data, + mrcdata->mrc_data_size); +} + +static void prepare_mrc_cache(struct pei_data *pei_data) +{ + struct mrc_data_container *mrc_cache; + + // preset just in case there is an error + pei_data->mrc_input = NULL; + pei_data->mrc_input_len = 0; + + if ((mrc_cache = find_current_mrc_cache()) == NULL) { + /* error message printed in find_current_mrc_cache */ + return; + } + + pei_data->mrc_input = mrc_cache->mrc_data; + pei_data->mrc_input_len = mrc_cache->mrc_data_size; + + printk(BIOS_DEBUG, "%s: at %p, size %x checksum %04x\n", + __func__, pei_data->mrc_input, + pei_data->mrc_input_len, mrc_cache->mrc_checksum); +} + +static const char* ecc_decoder[] = { + "inactive", + "active on IO", + "disabled on IO", + "active" +}; + +/* + * Dump in the log memory controller configuration as read from the memory + * controller registers. + */ +static void report_memory_config(void) +{ + u32 addr_decoder_common, addr_decode_ch[2]; + int i; + + addr_decoder_common = MCHBAR32(0x5000); + addr_decode_ch[0] = MCHBAR32(0x5004); + addr_decode_ch[1] = MCHBAR32(0x5008); + + printk(BIOS_DEBUG, "memcfg DDR3 clock %d MHz\n", + (MCHBAR32(0x5e04) * 13333 * 2 + 50)/100); + printk(BIOS_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]; + printk(BIOS_DEBUG, "memcfg channel[%d] config (%8.8x):\n", + i, ch_conf); + printk(BIOS_DEBUG, " ECC %s\n", + ecc_decoder[(ch_conf >> 24) & 3]); + printk(BIOS_DEBUG, " enhanced interleave mode %s\n", + ((ch_conf >> 22) & 1) ? "on" : "off"); + printk(BIOS_DEBUG, " rank interleave %s\n", + ((ch_conf >> 21) & 1) ? "on" : "off"); + printk(BIOS_DEBUG, " DIMMA %d MB width %s %s rank%s\n", + ((ch_conf >> 0) & 0xff) * 256, + ((ch_conf >> 19) & 1) ? "x16" : "x8 or x32", + ((ch_conf >> 17) & 1) ? "dual" : "single", + ((ch_conf >> 16) & 1) ? "" : ", selected"); + printk(BIOS_DEBUG, " DIMMB %d MB width %s %s rank%s\n", + ((ch_conf >> 8) & 0xff) * 256, + ((ch_conf >> 19) & 1) ? "x16" : "x8 or x32", + ((ch_conf >> 18) & 1) ? "dual" : "single", + ((ch_conf >> 16) & 1) ? ", selected" : ""); + } +} + +/** + * Find PEI executable in coreboot filesystem and execute it. + * + * @param pei_data: configuration data for UEFI PEI reference code + */ +void sdram_initialize(struct pei_data *pei_data) +{ + unsigned long entry; + + printk(BIOS_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 == 2) + prepare_mrc_cache(pei_data); + + /* If MRC data is not found we cannot continue S3 resume. */ + if (pei_data->boot_mode == 2 && !pei_data->mrc_input) { + post_code(POST_RESUME_FAILURE); + printk(BIOS_DEBUG, "Giving up in sdram_initialize: " + "No MRC data\n"); + outb(0x6, 0xcf9); + while(1) { + hlt(); + } + } + + /* Pass console handler in pei_data */ + pei_data->tx_byte = console_tx_byte; + + /* Locate and call UEFI System Agent binary. */ + entry = (unsigned long)cbfs_get_file_content( + CBFS_DEFAULT_MEDIA, "mrc.bin", 0xab); + if (entry) { + int rv; + asm volatile ( + "call *%%ecx\n\t" + :"=a" (rv) : "c" (entry), "a" (pei_data)); + if (rv) { + switch (rv) { + case -1: + printk(BIOS_ERR, "PEI version mismatch.\n"); + break; + case -2: + printk(BIOS_ERR, "Invalid memory frequency.\n"); + break; + default: + printk(BIOS_ERR, "MRC returned %x.\n", rv); + } + die("Nonzero MRC return value.\n"); + } + } else { + die("UEFI PEI System Agent not found.\n"); + } + + /* For reference print the System Agent version + * after executing the UEFI PEI stage. + */ + u32 version = MCHBAR32(0x5034); + printk(BIOS_DEBUG, "System Agent Version %d.%d.%d Build %d\n", + version >> 24 , (version >> 16) & 0xff, + (version >> 8) & 0xff, version & 0xff); + + report_memory_config(); +} + +void *cbmem_top(void) +{ + /* Top of cbmem is at lowest usable DRAM address below 4GiB. */ + return (void *)get_top_of_ram(); +} + +unsigned long get_top_of_ram(void) +{ + /* + * Base of TSEG is top of usable DRAM below 4GiB. The register has + * 1 MiB alignement. + */ + u32 tom = pci_read_config32(PCI_DEV(0,0,0), TSEG); + return (unsigned long) tom & ~((1 << 20) - 1); +} diff --git a/src/soc/intel/broadwell/romstage/report_platform.c b/src/soc/intel/broadwell/romstage/report_platform.c new file mode 100644 index 0000000000..8bb4a05fbc --- /dev/null +++ b/src/soc/intel/broadwell/romstage/report_platform.c @@ -0,0 +1,115 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include "southbridge/intel/lynxpoint/pch.h" +#include +#include +#include "haswell.h" + +static void report_cpu_info(void) +{ + struct cpuid_result cpuidr; + u32 i, index; + char cpu_string[50], *cpu_name = cpu_string; /* 48 bytes are reported */ + int vt, txt, aes; + msr_t microcode_ver; + const char *mode[] = {"NOT ", ""}; + + index = 0x80000000; + cpuidr = cpuid(index); + if (cpuidr.eax < 0x80000004) { + strcpy(cpu_string, "Platform info not available"); + } else { + u32 *p = (u32*) cpu_string; + for (i = 2; i <= 4 ; i++) { + cpuidr = cpuid(index + i); + *p++ = cpuidr.eax; + *p++ = cpuidr.ebx; + *p++ = cpuidr.ecx; + *p++ = cpuidr.edx; + } + } + /* Skip leading spaces in CPU name string */ + while (cpu_name[0] == ' ') + cpu_name++; + + microcode_ver.lo = 0; + microcode_ver.hi = 0; + wrmsr(0x8B, microcode_ver); + cpuidr = cpuid(1); + microcode_ver = rdmsr(0x8b); + printk(BIOS_DEBUG, "CPU id(%x) ucode:%08x %s\n", cpuidr.eax, microcode_ver.hi, cpu_name); + aes = (cpuidr.ecx & (1 << 25)) ? 1 : 0; + txt = (cpuidr.ecx & (1 << 6)) ? 1 : 0; + vt = (cpuidr.ecx & (1 << 5)) ? 1 : 0; + printk(BIOS_DEBUG, "AES %ssupported, TXT %ssupported, VT %ssupported\n", + mode[aes], mode[txt], mode[vt]); +} + +/* The PCI id name match comes from Intel document 472178 */ +static struct { + u16 dev_id; + const char *dev_name; +} pch_table [] = { + {0x8c41, "Mobile Engineering Sample"}, + {0x8c42, "Desktop Engineering Sample"}, + {0x8c44, "Z87"}, + {0x8c46, "Z85"}, + {0x8c49, "HM86"}, + {0x8c4a, "H87"}, + {0x8c4b, "HM87"}, + {0x8c4c, "Q85"}, + {0x8c4e, "Q87"}, + {0x8c4f, "QM87"}, + {0x8c50, "B85"}, + {0x8c52, "C222"}, + {0x8c54, "C224"}, + {0x8c56, "C226"}, + {0x8c5c, "H81"}, + {0x9c41, "LP Full Featured Engineering Sample"}, + {0x9c43, "LP Premium"}, + {0x9c45, "LP Mainstream"}, + {0x9c47, "LP Value"}, +}; + +static void report_pch_info(void) +{ + int i; + u16 dev_id = pci_read_config16(PCH_LPC_DEV, 2); + + + const char *pch_type = "Unknown"; + for (i = 0; i < ARRAY_SIZE(pch_table); i++) { + if (pch_table[i].dev_id == dev_id) { + pch_type = pch_table[i].dev_name; + break; + } + } + printk (BIOS_DEBUG, "PCH type: %s, device id: %x, rev id %x\n", + pch_type, dev_id, pci_read_config8(PCH_LPC_DEV, 8)); +} + +void report_platform_info(void) +{ + report_cpu_info(); + report_pch_info(); +} diff --git a/src/soc/intel/broadwell/romstage/romstage.c b/src/soc/intel/broadwell/romstage/romstage.c new file mode 100644 index 0000000000..3d5bb6fe09 --- /dev/null +++ b/src/soc/intel/broadwell/romstage/romstage.c @@ -0,0 +1,352 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 ChromeOS Authors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if CONFIG_EC_GOOGLE_CHROMEEC +#include +#endif +#include "haswell.h" +#include "northbridge/intel/haswell/haswell.h" +#include "northbridge/intel/haswell/raminit.h" +#include "southbridge/intel/lynxpoint/pch.h" +#include "southbridge/intel/lynxpoint/me.h" + + +static inline void reset_system(void) +{ + hard_reset(); + while (1) { + hlt(); + } +} + +/* The cache-as-ram assembly file calls romstage_main() after setting up + * cache-as-ram. romstage_main() will then call the mainboards's + * mainboard_romstage_entry() function. That function then calls + * romstage_common() below. The reason for the back and forth is to provide + * common entry point from cache-as-ram while still allowing for code sharing. + * Because we can't use global variables the stack is used for allocations -- + * thus the need to call back and forth. */ + + +static inline u32 *stack_push(u32 *stack, u32 value) +{ + stack = &stack[-1]; + *stack = value; + return stack; +} + +/* Romstage needs quite a bit of stack for decompressing images since the lzma + * lib keeps its state on the stack during romstage. */ +#define ROMSTAGE_RAM_STACK_SIZE 0x5000 +static unsigned long choose_top_of_stack(void) +{ + unsigned long stack_top; +#if CONFIG_DYNAMIC_CBMEM + /* cbmem_add() does a find() before add(). */ + stack_top = (unsigned long)cbmem_add(CBMEM_ID_ROMSTAGE_RAM_STACK, + ROMSTAGE_RAM_STACK_SIZE); + stack_top += ROMSTAGE_RAM_STACK_SIZE; +#else + stack_top = ROMSTAGE_STACK; +#endif + return stack_top; +} + +/* setup_romstage_stack_after_car() determines the stack to use after + * cache-as-ram is torn down as well as the MTRR settings to use. */ +static void *setup_romstage_stack_after_car(void) +{ + unsigned long top_of_stack; + int num_mtrrs; + u32 *slot; + u32 mtrr_mask_upper; + u32 top_of_ram; + + /* Top of stack needs to be aligned to a 4-byte boundary. */ + top_of_stack = choose_top_of_stack() & ~3; + slot = (void *)top_of_stack; + num_mtrrs = 0; + + /* The upper bits of the MTRR mask need to set according to the number + * of physical address bits. */ + mtrr_mask_upper = (1 << ((cpuid_eax(0x80000008) & 0xff) - 32)) - 1; + + /* The order for each MTTR is value then base with upper 32-bits of + * each value coming before the lower 32-bits. The reasoning for + * this ordering is to create a stack layout like the following: + * +0: Number of MTRRs + * +4: MTTR base 0 31:0 + * +8: MTTR base 0 63:32 + * +12: MTTR mask 0 31:0 + * +16: MTTR mask 0 63:32 + * +20: MTTR base 1 31:0 + * +24: MTTR base 1 63:32 + * +28: MTTR mask 1 31:0 + * +32: MTTR mask 1 63:32 + */ + + /* Cache the ROM as WP just below 4GiB. */ + slot = stack_push(slot, mtrr_mask_upper); /* upper mask */ + slot = stack_push(slot, ~(CONFIG_ROM_SIZE - 1) | MTRRphysMaskValid); + slot = stack_push(slot, 0); /* upper base */ + slot = stack_push(slot, ~(CONFIG_ROM_SIZE - 1) | MTRR_TYPE_WRPROT); + num_mtrrs++; + + /* Cache RAM as WB from 0 -> CONFIG_RAMTOP. */ + slot = stack_push(slot, mtrr_mask_upper); /* upper mask */ + slot = stack_push(slot, ~(CONFIG_RAMTOP - 1) | MTRRphysMaskValid); + slot = stack_push(slot, 0); /* upper base */ + slot = stack_push(slot, 0 | MTRR_TYPE_WRBACK); + num_mtrrs++; + + top_of_ram = get_top_of_ram(); + /* Cache 8MiB below the top of ram. On haswell systems the top of + * ram under 4GiB is the start of the TSEG region. It is required to + * be 8MiB aligned. Set this area as cacheable so it can be used later + * for ramstage before setting up the entire RAM as cacheable. */ + slot = stack_push(slot, mtrr_mask_upper); /* upper mask */ + slot = stack_push(slot, ~((8 << 20) - 1) | MTRRphysMaskValid); + slot = stack_push(slot, 0); /* upper base */ + slot = stack_push(slot, (top_of_ram - (8 << 20)) | MTRR_TYPE_WRBACK); + num_mtrrs++; + + /* Cache 8MiB at the top of ram. Top of ram on haswell systems + * is where the TSEG region resides. However, it is not restricted + * to SMM mode until SMM has been relocated. By setting the region + * to cacheable it provides faster access when relocating the SMM + * handler as well as using the TSEG region for other purposes. */ + slot = stack_push(slot, mtrr_mask_upper); /* upper mask */ + slot = stack_push(slot, ~((8 << 20) - 1) | MTRRphysMaskValid); + slot = stack_push(slot, 0); /* upper base */ + slot = stack_push(slot, top_of_ram | MTRR_TYPE_WRBACK); + num_mtrrs++; + + /* Save the number of MTTRs to setup. Return the stack location + * pointing to the number of MTRRs. */ + slot = stack_push(slot, num_mtrrs); + + return slot; +} + +void * asmlinkage romstage_main(unsigned long bist) +{ + int i; + void *romstage_stack_after_car; + const int num_guards = 4; + const u32 stack_guard = 0xdeadbeef; + u32 *stack_base = (void *)(CONFIG_DCACHE_RAM_BASE + + CONFIG_DCACHE_RAM_SIZE - + CONFIG_DCACHE_RAM_ROMSTAGE_STACK_SIZE); + + printk(BIOS_DEBUG, "Setting up stack guards.\n"); + for (i = 0; i < num_guards; i++) + stack_base[i] = stack_guard; + + mainboard_romstage_entry(bist); + + /* Check the stack. */ + for (i = 0; i < num_guards; i++) { + if (stack_base[i] == stack_guard) + continue; + printk(BIOS_DEBUG, "Smashed stack detected in romstage!\n"); + } + + /* Get the stack to use after cache-as-ram is torn down. */ + romstage_stack_after_car = setup_romstage_stack_after_car(); + + return romstage_stack_after_car; +} + +void romstage_common(const struct romstage_params *params) +{ + int boot_mode; + int wake_from_s3; + struct romstage_handoff *handoff; + +#if CONFIG_COLLECT_TIMESTAMPS + uint64_t start_romstage_time; + uint64_t before_dram_time; + uint64_t after_dram_time; + uint64_t base_time = + (uint64_t)pci_read_config32(PCI_DEV(0, 0x1f, 2), 0xd0) << 32 || + pci_read_config32(PCI_DEV(0, 0x00, 0), 0xdc); +#endif + +#if CONFIG_COLLECT_TIMESTAMPS + start_romstage_time = timestamp_get(); +#endif + + if (params->bist == 0) + enable_lapic(); + + wake_from_s3 = early_pch_init(params->gpio_map, params->rcba_config); + +#if CONFIG_EC_GOOGLE_CHROMEEC + /* Ensure the EC is in the right mode for recovery */ + google_chromeec_early_init(); +#endif + + /* Halt if there was a built in self test failure */ + report_bist_failure(params->bist); + + /* Perform some early chipset initialization required + * before RAM initialization can work + */ + haswell_early_initialization(HASWELL_MOBILE); + printk(BIOS_DEBUG, "Back from haswell_early_initialization()\n"); + + if (wake_from_s3) { +#if CONFIG_HAVE_ACPI_RESUME + printk(BIOS_DEBUG, "Resume from S3 detected.\n"); +#else + printk(BIOS_DEBUG, "Resume from S3 detected, but disabled.\n"); + wake_from_s3 = 0; +#endif + } + + /* There are hard coded assumptions of 2 meaning s3 wake. Normalize + * the users of the 2 literal here based off wake_from_s3. */ + boot_mode = wake_from_s3 ? 2 : 0; + + /* Prepare USB controller early in S3 resume */ + if (wake_from_s3) + enable_usb_bar(); + + post_code(0x3a); + params->pei_data->boot_mode = boot_mode; +#if CONFIG_COLLECT_TIMESTAMPS + before_dram_time = timestamp_get(); +#endif + + report_platform_info(); + + if (params->copy_spd != NULL) + params->copy_spd(params->pei_data); + + sdram_initialize(params->pei_data); + +#if CONFIG_COLLECT_TIMESTAMPS + after_dram_time = timestamp_get(); +#endif + post_code(0x3b); + + intel_early_me_status(); + + quick_ram_check(); + post_code(0x3e); + + if (!wake_from_s3) { + cbmem_initialize_empty(); + /* Save data returned from MRC on non-S3 resumes. */ + save_mrc_data(params->pei_data); + } else if (cbmem_initialize()) { + #if CONFIG_HAVE_ACPI_RESUME + /* Failed S3 resume, reset to come up cleanly */ + reset_system(); + #endif + } + + handoff = romstage_handoff_find_or_add(); + if (handoff != NULL) + handoff->s3_resume = wake_from_s3; + else + printk(BIOS_DEBUG, "Romstage handoff structure not added!\n"); + + post_code(0x3f); +#if CONFIG_CHROMEOS + init_chromeos(boot_mode); +#endif +#if CONFIG_COLLECT_TIMESTAMPS + timestamp_init(base_time); + timestamp_add(TS_START_ROMSTAGE, start_romstage_time ); + timestamp_add(TS_BEFORE_INITRAM, before_dram_time ); + timestamp_add(TS_AFTER_INITRAM, after_dram_time ); + timestamp_add_now(TS_END_ROMSTAGE); +#endif +} + +static inline void prepare_for_resume(struct romstage_handoff *handoff) +{ +/* Only need to save memory when ramstage isn't relocatable. */ +#if !CONFIG_RELOCATABLE_RAMSTAGE +#if CONFIG_HAVE_ACPI_RESUME + /* Back up the OS-controlled memory where ramstage will be loaded. */ + if (handoff != NULL && handoff->s3_resume) { + void *src = (void *)CONFIG_RAMBASE; + void *dest = cbmem_find(CBMEM_ID_RESUME); + if (dest != NULL) + memcpy(dest, src, HIGH_MEMORY_SAVE); + } +#endif +#endif +} + +void romstage_after_car(void) +{ + struct romstage_handoff *handoff; + + handoff = romstage_handoff_find_or_add(); + + prepare_for_resume(handoff); + + vboot_verify_firmware(handoff); + + /* Load the ramstage. */ + copy_and_run(); +} + + +#if CONFIG_RELOCATABLE_RAMSTAGE +#include + +struct ramstage_cache *ramstage_cache_location(long *size) +{ + /* The ramstage cache lives in the TSEG region at RESERVED_SMM_OFFSET. + * The top of ram is defined to be the TSEG base address. */ + *size = RESERVED_SMM_SIZE; + return (void *)(get_top_of_ram() + RESERVED_SMM_OFFSET); +} + +void ramstage_cache_invalid(struct ramstage_cache *cache) +{ +#if CONFIG_RESET_ON_INVALID_RAMSTAGE_CACHE + reset_system(); +#endif +} +#endif diff --git a/src/soc/intel/broadwell/romstage/smbus.c b/src/soc/intel/broadwell/romstage/smbus.c new file mode 100644 index 0000000000..9de97e7fe2 --- /dev/null +++ b/src/soc/intel/broadwell/romstage/smbus.c @@ -0,0 +1,62 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008-2009 coresystems GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include "pch.h" +#include "smbus.h" + +void enable_smbus(void) +{ + device_t dev; + + /* Set the SMBus device statically. */ + dev = PCI_DEV(0x0, 0x1f, 0x3); + + /* Check to make sure we've got the right device. */ + if (pci_read_config16(dev, 0x0) != 0x8086) { + die("SMBus controller not found!"); + } + + /* Set SMBus I/O base. */ + pci_write_config32(dev, SMB_BASE, + SMBUS_IO_BASE | PCI_BASE_ADDRESS_SPACE_IO); + + /* Set SMBus enable. */ + pci_write_config8(dev, HOSTC, HST_EN); + + /* Set SMBus I/O space enable. */ + pci_write_config16(dev, PCI_COMMAND, PCI_COMMAND_IO); + + /* Disable interrupt generation. */ + outb(0, SMBUS_IO_BASE + SMBHSTCTL); + + /* Clear any lingering errors, so transactions can run. */ + outb(inb(SMBUS_IO_BASE + SMBHSTSTAT), SMBUS_IO_BASE + SMBHSTSTAT); + print_debug("SMBus controller enabled.\n"); +} + +int smbus_read_byte(unsigned device, unsigned address) +{ + return do_smbus_read_byte(SMBUS_IO_BASE, device, address); +} + diff --git a/src/soc/intel/broadwell/romstage/spi.c b/src/soc/intel/broadwell/romstage/spi.c new file mode 100644 index 0000000000..6f57f637a9 --- /dev/null +++ b/src/soc/intel/broadwell/romstage/spi.c @@ -0,0 +1,114 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include "pch.h" + +#define SPI_DELAY 10 /* 10us */ +#define SPI_RETRY 200000 /* 2s */ + +static int early_spi_read_block(u32 offset, u8 size, u8 *buffer) +{ + u32 *ptr32 = (u32*)buffer; + u32 i; + + /* Clear status bits */ + RCBA16(SPIBAR_HSFS) |= SPIBAR_HSFS_AEL | SPIBAR_HSFS_FCERR | + SPIBAR_HSFS_FDONE; + + if (RCBA16(SPIBAR_HSFS) & SPIBAR_HSFS_SCIP) { + printk(BIOS_ERR, "SPI ERROR: transaction in progress\n"); + return -1; + } + + /* Set flash address */ + RCBA32(SPIBAR_FADDR) = offset; + + /* Setup read transaction */ + RCBA16(SPIBAR_HSFC) = SPIBAR_HSFC_BYTE_COUNT(size) | + SPIBAR_HSFC_CYCLE_READ; + + /* Start transactinon */ + RCBA16(SPIBAR_HSFC) |= SPIBAR_HSFC_GO; + + /* Wait for completion */ + for (i = 0; i < SPI_RETRY; i++) { + if (RCBA16(SPIBAR_HSFS) & SPIBAR_HSFS_SCIP) { + /* Cycle in progress, wait 1ms */ + udelay(SPI_DELAY); + continue; + } + + if (RCBA16(SPIBAR_HSFS) & SPIBAR_HSFS_AEL) { + printk(BIOS_ERR, "SPI ERROR: Access Error\n"); + return -1; + + } + + if (RCBA16(SPIBAR_HSFS) & SPIBAR_HSFS_FCERR) { + printk(BIOS_ERR, "SPI ERROR: Flash Cycle Error\n"); + return -1; + } + break; + } + + if (i >= SPI_RETRY) { + printk(BIOS_ERR, "SPI ERROR: Timeout\n"); + return -1; + } + + /* Read the data */ + for (i = 0; i < size; i+=sizeof(u32)) { + if (size-i >= 4) { + /* reading >= dword */ + *ptr32++ = RCBA32(SPIBAR_FDATA(i/sizeof(u32))); + } else { + /* reading < dword */ + u8 j, *ptr8 = (u8*)ptr32; + u32 temp = RCBA32(SPIBAR_FDATA(i/sizeof(u32))); + for (j = 0; j < (size-i); j++) { + *ptr8++ = temp & 0xff; + temp >>= 8; + } + } + } + + return size; +} + +int early_spi_read(u32 offset, u32 size, u8 *buffer) +{ + u32 current = 0; + + while (size > 0) { + u8 count = (size < 64) ? size : 64; + if (early_spi_read_block(offset + current, count, + buffer + current) < 0) + return -1; + size -= count; + current += count; + } + + return 0; +} diff --git a/src/soc/intel/broadwell/romstage/systemagent.c b/src/soc/intel/broadwell/romstage/systemagent.c new file mode 100644 index 0000000000..7f9f1876b1 --- /dev/null +++ b/src/soc/intel/broadwell/romstage/systemagent.c @@ -0,0 +1,111 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2007-2010 coresystems GmbH + * Copyright (C) 2011 Google Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include "haswell.h" + +static void haswell_setup_bars(void) +{ + printk(BIOS_DEBUG, "Setting up static northbridge registers..."); + /* Set up all hardcoded northbridge BARs */ + pci_write_config32(PCI_DEV(0, 0x00, 0), EPBAR, DEFAULT_EPBAR | 1); + pci_write_config32(PCI_DEV(0, 0x00, 0), EPBAR + 4, (0LL+DEFAULT_EPBAR) >> 32); + pci_write_config32(PCI_DEV(0, 0x00, 0), MCHBAR, DEFAULT_MCHBAR | 1); + pci_write_config32(PCI_DEV(0, 0x00, 0), MCHBAR + 4, (0LL+DEFAULT_MCHBAR) >> 32); + pci_write_config32(PCI_DEV(0, 0x00, 0), DMIBAR, DEFAULT_DMIBAR | 1); + pci_write_config32(PCI_DEV(0, 0x00, 0), DMIBAR + 4, (0LL+DEFAULT_DMIBAR) >> 32); + + /* Set C0000-FFFFF to access RAM on both reads and writes */ + pci_write_config8(PCI_DEV(0, 0x00, 0), PAM0, 0x30); + pci_write_config8(PCI_DEV(0, 0x00, 0), PAM1, 0x33); + pci_write_config8(PCI_DEV(0, 0x00, 0), PAM2, 0x33); + pci_write_config8(PCI_DEV(0, 0x00, 0), PAM3, 0x33); + pci_write_config8(PCI_DEV(0, 0x00, 0), PAM4, 0x33); + pci_write_config8(PCI_DEV(0, 0x00, 0), PAM5, 0x33); + pci_write_config8(PCI_DEV(0, 0x00, 0), PAM6, 0x33); + + printk(BIOS_DEBUG, " done.\n"); +} + +static void haswell_setup_graphics(void) +{ + u32 reg32; + u16 reg16; + u8 reg8; + + printk(BIOS_DEBUG, "Initializing Graphics...\n"); + + /* Setup IGD memory by setting GGC[7:3] = 1 for 32MB */ + reg16 = pci_read_config16(PCI_DEV(0,0,0), GGC); + reg16 &= ~0x00f8; + reg16 |= 1 << 3; + /* Program GTT memory by setting GGC[9:8] = 2MB */ + reg16 &= ~0x0300; + reg16 |= 2 << 8; + /* Enable VGA decode */ + reg16 &= ~0x0002; + pci_write_config16(PCI_DEV(0,0,0), GGC, reg16); + + /* Enable 256MB aperture */ + reg8 = pci_read_config8(PCI_DEV(0, 2, 0), MSAC); + reg8 &= ~0x06; + reg8 |= 0x02; + pci_write_config8(PCI_DEV(0, 2, 0), MSAC, reg8); + + /* Erratum workarounds */ + reg32 = MCHBAR32(0x5f00); + reg32 |= (1 << 9)|(1 << 10); + MCHBAR32(0x5f00) = reg32; + + /* Enable SA Clock Gating */ + reg32 = MCHBAR32(0x5f00); + MCHBAR32(0x5f00) = reg32 | 1; + + /* GPU RC6 workaround for sighting 366252 */ + reg32 = MCHBAR32(0x5d14); + reg32 |= (1 << 31); + MCHBAR32(0x5d14) = reg32; + + /* VLW */ + reg32 = MCHBAR32(0x6120); + reg32 &= ~(1 << 0); + MCHBAR32(0x6120) = reg32; + + reg32 = MCHBAR32(0x5418); + reg32 |= (1 << 4) | (1 << 5); + MCHBAR32(0x5418) = reg32; +} + +void haswell_early_initialization(int chipset_type) +{ + /* Setup all BARs required for early PCIe and raminit */ + haswell_setup_bars(); + + /* Device Enable: IGD and Mini-HD Audio */ + pci_write_config32(PCI_DEV(0, 0, 0), DEVEN, + DEVEN_D0EN | DEVEN_D2EN | DEVEN_D3EN); + + haswell_setup_graphics(); +} diff --git a/src/soc/intel/broadwell/sata.c b/src/soc/intel/broadwell/sata.c new file mode 100644 index 0000000000..c2005402b1 --- /dev/null +++ b/src/soc/intel/broadwell/sata.c @@ -0,0 +1,366 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008-2009 coresystems GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include "pch.h" + +typedef struct southbridge_intel_lynxpoint_config config_t; + +static inline u32 sir_read(struct device *dev, int idx) +{ + pci_write_config32(dev, SATA_SIRI, idx); + return pci_read_config32(dev, SATA_SIRD); +} + +static inline void sir_write(struct device *dev, int idx, u32 value) +{ + pci_write_config32(dev, SATA_SIRI, idx); + pci_write_config32(dev, SATA_SIRD, value); +} + +static void sata_init(struct device *dev) +{ + u32 reg32; + u16 reg16; + /* Get the chip configuration */ + config_t *config = dev->chip_info; + + printk(BIOS_DEBUG, "SATA: Initializing...\n"); + + if (config == NULL) { + printk(BIOS_ERR, "SATA: ERROR: Device not in devicetree.cb!\n"); + return; + } + + /* SATA configuration */ + + /* Enable BARs */ + pci_write_config16(dev, PCI_COMMAND, 0x0007); + + if (config->ide_legacy_combined) { + printk(BIOS_DEBUG, "SATA: Controller in combined mode.\n"); + + /* No AHCI: clear AHCI base */ + pci_write_config32(dev, 0x24, 0x00000000); + /* And without AHCI BAR no memory decoding */ + reg16 = pci_read_config16(dev, PCI_COMMAND); + reg16 &= ~PCI_COMMAND_MEMORY; + pci_write_config16(dev, PCI_COMMAND, reg16); + + pci_write_config8(dev, 0x09, 0x80); + + /* Set timings */ + pci_write_config16(dev, IDE_TIM_PRI, IDE_DECODE_ENABLE | + IDE_ISP_5_CLOCKS | IDE_RCT_4_CLOCKS); + pci_write_config16(dev, IDE_TIM_SEC, IDE_DECODE_ENABLE | + IDE_ISP_3_CLOCKS | IDE_RCT_1_CLOCKS | + IDE_PPE0 | IDE_IE0 | IDE_TIME0); + + /* Sync DMA */ + pci_write_config16(dev, IDE_SDMA_CNT, IDE_SSDE0); + pci_write_config16(dev, IDE_SDMA_TIM, 0x0200); + + /* Set IDE I/O Configuration */ + reg32 = SIG_MODE_PRI_NORMAL | FAST_PCB1 | FAST_PCB0 | PCB1 | PCB0; + pci_write_config32(dev, IDE_CONFIG, reg32); + + /* Port enable */ + reg16 = pci_read_config16(dev, 0x92); + reg16 &= ~0x3f; + reg16 |= config->sata_port_map; + pci_write_config16(dev, 0x92, reg16); + + /* SATA Initialization register */ + pci_write_config32(dev, 0x94, + ((config->sata_port_map ^ 0x3f) << 24) | 0x183); + } else if(config->sata_ahci) { + u32 abar; + + printk(BIOS_DEBUG, "SATA: Controller in AHCI mode.\n"); + + /* Set Interrupt Line */ + /* Interrupt Pin is set by D31IP.PIP */ + pci_write_config8(dev, INTR_LN, 0x0a); + + /* Set timings */ + pci_write_config16(dev, IDE_TIM_PRI, IDE_DECODE_ENABLE | + IDE_ISP_3_CLOCKS | IDE_RCT_1_CLOCKS | + IDE_PPE0 | IDE_IE0 | IDE_TIME0); + pci_write_config16(dev, IDE_TIM_SEC, IDE_DECODE_ENABLE | + IDE_ISP_5_CLOCKS | IDE_RCT_4_CLOCKS); + + /* Sync DMA */ + pci_write_config16(dev, IDE_SDMA_CNT, IDE_PSDE0); + pci_write_config16(dev, IDE_SDMA_TIM, 0x0001); + + /* Set IDE I/O Configuration */ + reg32 = SIG_MODE_PRI_NORMAL | FAST_PCB1 | FAST_PCB0 | PCB1 | PCB0; + pci_write_config32(dev, IDE_CONFIG, reg32); + + /* for AHCI, Port Enable is managed in memory mapped space */ + reg16 = pci_read_config16(dev, 0x92); + reg16 &= ~0x3f; + reg16 |= 0x8000 | config->sata_port_map; + pci_write_config16(dev, 0x92, reg16); + udelay(2); + + /* Setup register 98h */ + reg32 = pci_read_config16(dev, 0x98); + reg32 |= 1 << 19; /* BWG step 6 */ + reg32 |= 1 << 22; /* BWG step 5 */ + reg32 &= ~(0x3f << 7); + reg32 |= 0x04 << 7; /* BWG step 7 */ + reg32 |= 1 << 20; /* BWG step 8 */ + reg32 &= ~(0x03 << 5); + reg32 |= 1 << 5; /* BWG step 9 */ + reg32 |= 1 << 18; /* BWG step 10 */ + reg32 |= 1 << 29; /* BWG step 11 */ + if (pch_is_lp()) { + reg32 &= ~((1 << 31) | (1 << 30)); + reg32 |= 1 << 23; + reg32 |= 1 << 24; /* Disable listen mode (hotplug) */ + } + pci_write_config32(dev, 0x98, reg32); + + /* Setup register 9Ch */ + reg16 = 0; /* Disable alternate ID */ + reg16 = 1 << 5; /* BWG step 12 */ + pci_write_config16(dev, 0x9c, reg16); + + /* SATA Initialization register */ + reg32 = 0x183; + reg32 |= (config->sata_port_map ^ 0x3f) << 24; + reg32 |= (config->sata_devslp_mux & 1) << 15; + pci_write_config32(dev, 0x94, reg32); + + /* Initialize AHCI memory-mapped space */ + abar = pci_read_config32(dev, PCI_BASE_ADDRESS_5); + printk(BIOS_DEBUG, "ABAR: %08X\n", abar); + /* CAP (HBA Capabilities) : enable power management */ + reg32 = read32(abar + 0x00); + reg32 |= 0x0c006000; // set PSC+SSC+SALP+SSS + reg32 &= ~0x00020060; // clear SXS+EMS+PMS + if (pch_is_lp()) + reg32 |= (1 << 18); // SAM: SATA AHCI MODE ONLY + write32(abar + 0x00, reg32); + /* PI (Ports implemented) */ + write32(abar + 0x0c, config->sata_port_map); + (void) read32(abar + 0x0c); /* Read back 1 */ + (void) read32(abar + 0x0c); /* Read back 2 */ + /* CAP2 (HBA Capabilities Extended)*/ + reg32 = read32(abar + 0x24); + /* Enable DEVSLP */ + if (pch_is_lp()) { + if (config->sata_devslp_disable) + reg32 &= ~(1 << 3); + else + reg32 |= (1 << 5)|(1 << 4)|(1 << 3)|(1 << 2); + } else { + reg32 &= ~0x00000002; + } + write32(abar + 0x24, reg32); + } else { + printk(BIOS_DEBUG, "SATA: Controller in plain mode.\n"); + + /* No AHCI: clear AHCI base */ + pci_write_config32(dev, 0x24, 0x00000000); + + /* And without AHCI BAR no memory decoding */ + reg16 = pci_read_config16(dev, PCI_COMMAND); + reg16 &= ~PCI_COMMAND_MEMORY; + pci_write_config16(dev, PCI_COMMAND, reg16); + + /* Native mode capable on both primary and secondary (0xa) + * or'ed with enabled (0x50) = 0xf + */ + pci_write_config8(dev, 0x09, 0x8f); + + /* Set Interrupt Line */ + /* Interrupt Pin is set by D31IP.PIP */ + pci_write_config8(dev, INTR_LN, 0xff); + + /* Set timings */ + pci_write_config16(dev, IDE_TIM_PRI, IDE_DECODE_ENABLE | + IDE_ISP_3_CLOCKS | IDE_RCT_1_CLOCKS | + IDE_PPE0 | IDE_IE0 | IDE_TIME0); + pci_write_config16(dev, IDE_TIM_SEC, IDE_DECODE_ENABLE | + IDE_SITRE | IDE_ISP_3_CLOCKS | + IDE_RCT_1_CLOCKS | IDE_IE0 | IDE_TIME0); + + /* Sync DMA */ + pci_write_config16(dev, IDE_SDMA_CNT, IDE_SSDE0 | IDE_PSDE0); + pci_write_config16(dev, IDE_SDMA_TIM, 0x0201); + + /* Set IDE I/O Configuration */ + reg32 = SIG_MODE_PRI_NORMAL | FAST_PCB1 | FAST_PCB0 | PCB1 | PCB0; + pci_write_config32(dev, IDE_CONFIG, reg32); + + /* Port enable */ + reg16 = pci_read_config16(dev, 0x92); + reg16 &= ~0x3f; + reg16 |= config->sata_port_map; + pci_write_config16(dev, 0x92, reg16); + + /* SATA Initialization register */ + pci_write_config32(dev, 0x94, + ((config->sata_port_map ^ 0x3f) << 24) | 0x183); + } + + /* Set Gen3 Transmitter settings if needed */ + if (config->sata_port0_gen3_tx) + pch_iobp_update(SATA_IOBP_SP0G3IR, 0, + config->sata_port0_gen3_tx); + + if (config->sata_port1_gen3_tx) + pch_iobp_update(SATA_IOBP_SP1G3IR, 0, + config->sata_port1_gen3_tx); + + /* Set Gen3 DTLE DATA / EDGE registers if needed */ + if (config->sata_port0_gen3_dtle) { + pch_iobp_update(SATA_IOBP_SP0DTLE_DATA, + ~(SATA_DTLE_MASK << SATA_DTLE_DATA_SHIFT), + (config->sata_port0_gen3_dtle & SATA_DTLE_MASK) + << SATA_DTLE_DATA_SHIFT); + + pch_iobp_update(SATA_IOBP_SP0DTLE_EDGE, + ~(SATA_DTLE_MASK << SATA_DTLE_EDGE_SHIFT), + (config->sata_port0_gen3_dtle & SATA_DTLE_MASK) + << SATA_DTLE_EDGE_SHIFT); + } + + if (config->sata_port1_gen3_dtle) { + pch_iobp_update(SATA_IOBP_SP1DTLE_DATA, + ~(SATA_DTLE_MASK << SATA_DTLE_DATA_SHIFT), + (config->sata_port1_gen3_dtle & SATA_DTLE_MASK) + << SATA_DTLE_DATA_SHIFT); + + pch_iobp_update(SATA_IOBP_SP1DTLE_EDGE, + ~(SATA_DTLE_MASK << SATA_DTLE_EDGE_SHIFT), + (config->sata_port1_gen3_dtle & SATA_DTLE_MASK) + << SATA_DTLE_EDGE_SHIFT); + } + + /* Additional Programming Requirements */ + /* Power Optimizer */ + + /* Step 1 */ + if (pch_is_lp()) + sir_write(dev, 0x64, 0x883c9003); + else + sir_write(dev, 0x64, 0x883c9001); + + /* Step 2: SIR 68h[15:0] = 880Ah */ + reg32 = sir_read(dev, 0x68); + reg32 &= 0xffff0000; + reg32 |= 0x880a; + sir_write(dev, 0x68, reg32); + + /* Step 3: SIR 60h[3] = 1 */ + reg32 = sir_read(dev, 0x60); + reg32 |= (1 << 3); + sir_write(dev, 0x60, reg32); + + /* Step 4: SIR 60h[0] = 1 */ + reg32 = sir_read(dev, 0x60); + reg32 |= (1 << 0); + sir_write(dev, 0x60, reg32); + + /* Step 5: SIR 60h[1] = 1 */ + reg32 = sir_read(dev, 0x60); + reg32 |= (1 << 1); + sir_write(dev, 0x60, reg32); + + /* Clock Gating */ + sir_write(dev, 0x70, 0x3f00bf1f); + if (pch_is_lp()) { + sir_write(dev, 0x54, 0xcf000f0f); + sir_write(dev, 0x58, 0x00190000); + } + + reg32 = pci_read_config32(dev, 0x300); + reg32 |= (1 << 17) | (1 << 16); + reg32 |= (1 << 31) | (1 << 30) | (1 << 29); + pci_write_config32(dev, 0x300, reg32); +} + +static void sata_enable(device_t dev) +{ + /* Get the chip configuration */ + config_t *config = dev->chip_info; + u16 map = 0; + + if (!config) + return; + + /* + * Set SATA controller mode early so the resource allocator can + * properly assign IO/Memory resources for the controller. + */ + if (config->sata_ahci) + map = 0x0060; + + map |= (config->sata_port_map ^ 0x3f) << 8; + + pci_write_config16(dev, 0x90, map); +} + +static void sata_set_subsystem(device_t dev, unsigned vendor, unsigned device) +{ + if (!vendor || !device) { + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + pci_read_config32(dev, PCI_VENDOR_ID)); + } else { + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + ((device & 0xffff) << 16) | (vendor & 0xffff)); + } +} + +static struct pci_operations sata_pci_ops = { + .set_subsystem = sata_set_subsystem, +}; + +static struct device_operations sata_ops = { + .read_resources = pci_dev_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .init = sata_init, + .enable = sata_enable, + .scan_bus = 0, + .ops_pci = &sata_pci_ops, +}; + +static const unsigned short pci_device_ids[] = { + 0x8c00, 0x8c02, 0x8c04, 0x8c06, 0x8c08, 0x8c0e, /* Desktop */ + 0x8c01, 0x8c03, 0x8c05, 0x8c07, 0x8c09, 0x8c0f, /* Mobile */ + 0x9c03, 0x9c05, 0x9c07, 0x9c0f, /* Low Power */ + 0 +}; + +static const struct pci_driver pch_sata __pci_driver = { + .ops = &sata_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .devices = pci_device_ids, +}; + diff --git a/src/soc/intel/broadwell/serialio.c b/src/soc/intel/broadwell/serialio.c new file mode 100644 index 0000000000..8257cc2f6f --- /dev/null +++ b/src/soc/intel/broadwell/serialio.c @@ -0,0 +1,294 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "pch.h" +#include "nvs.h" + +/* Set D3Hot Power State in ACPI mode */ +static void serialio_enable_d3hot(struct device *dev) +{ + u32 reg32 = pci_read_config32(dev, PCH_PCS); + reg32 |= PCH_PCS_PS_D3HOT; + pci_write_config32(dev, PCH_PCS, reg32); +} + +/* Enable clock in PCI mode */ +static void serialio_enable_clock(struct resource *bar0) +{ + u32 reg32 = read32(bar0->base + SIO_REG_PPR_CLOCK); + reg32 |= SIO_REG_PPR_CLOCK_EN; + write32(bar0->base + SIO_REG_PPR_CLOCK, reg32); +} + +/* Put Serial IO D21:F0-F6 device into desired mode. */ +static void serialio_d21_mode(int sio_index, int int_pin, int acpi_mode) +{ + u32 portctrl = SIO_IOBP_PORTCTRL_PM_CAP_PRSNT; + + /* Snoop select 1. */ + portctrl |= SIO_IOBP_PORTCTRL_SNOOP_SELECT(1); + + /* Set interrupt pin. */ + portctrl |= SIO_IOBP_PORTCTRL_INT_PIN(int_pin); + + if (acpi_mode) { + /* Enable ACPI interrupt mode. */ + portctrl |= SIO_IOBP_PORTCTRL_ACPI_IRQ_EN; + + /* Disable PCI config space. */ + portctrl |= SIO_IOBP_PORTCTRL_PCI_CONF_DIS; + } + + pch_iobp_update(SIO_IOBP_PORTCTRLX(sio_index), 0, portctrl); +} + +/* Put Serial IO D23:F0 device into desired mode. */ +static void serialio_d23_mode(int acpi_mode) +{ + u32 portctrl = 0; + + /* Snoop select 1. */ + pch_iobp_update(SIO_IOBP_PORTCTRL1, 0, + SIO_IOBP_PORTCTRL1_SNOOP_SELECT(1)); + + if (acpi_mode) { + /* Enable ACPI interrupt mode. */ + portctrl |= SIO_IOBP_PORTCTRL0_ACPI_IRQ_EN; + + /* Disable PCI config space. */ + portctrl |= SIO_IOBP_PORTCTRL0_PCI_CONF_DIS; + } + + pch_iobp_update(SIO_IOBP_PORTCTRL0, 0, portctrl); +} + +/* Enable LTR Auto Mode for D21:F1-F6. */ +static void serialio_d21_ltr(struct resource *bar0) +{ + u32 reg; + + /* 1. Program BAR0 + 808h[2] = 0b */ + reg = read32(bar0->base + SIO_REG_PPR_GEN); + reg &= ~SIO_REG_PPR_GEN_LTR_MODE_MASK; + write32(bar0->base + SIO_REG_PPR_GEN, reg); + + /* 2. Program BAR0 + 804h[1:0] = 00b */ + reg = read32(bar0->base + SIO_REG_PPR_RST); + reg &= ~SIO_REG_PPR_RST_ASSERT; + write32(bar0->base + SIO_REG_PPR_RST, reg); + + /* 3. Program BAR0 + 804h[1:0] = 11b */ + reg = read32(bar0->base + SIO_REG_PPR_RST); + reg |= SIO_REG_PPR_RST_ASSERT; + write32(bar0->base + SIO_REG_PPR_RST, reg); + + /* 4. Program BAR0 + 814h[31:0] = 00000000h */ + write32(bar0->base + SIO_REG_AUTO_LTR, 0); +} + +/* Enable LTR Auto Mode for D23:F0. */ +static void serialio_d23_ltr(struct resource *bar0) +{ + u32 reg; + + /* Program BAR0 + 1008h[2] = 1b */ + reg = read32(bar0->base + SIO_REG_SDIO_PPR_GEN); + reg |= SIO_REG_PPR_GEN_LTR_MODE_MASK; + write32(bar0->base + SIO_REG_SDIO_PPR_GEN, reg); + + /* Program BAR0 + 1010h = 0x00000000 */ + write32(bar0->base + SIO_REG_SDIO_PPR_SW_LTR, 0); + + /* Program BAR0 + 3Ch[30] = 1b */ + reg = read32(bar0->base + SIO_REG_SDIO_PPR_CMD12); + reg |= SIO_REG_SDIO_PPR_CMD12_B30; + write32(bar0->base + SIO_REG_SDIO_PPR_CMD12, reg); +} + +/* Select I2C voltage of 1.8V or 3.3V. */ +static void serialio_i2c_voltage_sel(struct resource *bar0, u8 voltage) +{ + u32 reg32 = read32(bar0->base + SIO_REG_PPR_GEN); + reg32 &= ~SIO_REG_PPR_GEN_VOLTAGE_MASK; + reg32 |= SIO_REG_PPR_GEN_VOLTAGE(voltage); + write32(bar0->base + SIO_REG_PPR_GEN, reg32); +} + +/* Init sequence to be run once, done as part of D21:F0 (SDMA) init. */ +static void serialio_init_once(int acpi_mode) +{ + if (acpi_mode) { + /* Enable ACPI IRQ for IRQ13, IRQ7, IRQ6, IRQ5 in RCBA. */ + RCBA32_OR(ACPIIRQEN, (1 << 13)|(1 << 7)|(1 << 6)|(1 << 5)); + } + + /* Program IOBP CB000154h[12,9:8,4:0] = 1001100011111b. */ + pch_iobp_update(SIO_IOBP_GPIODF, ~0x0000131f, 0x0000131f); + + /* Program IOBP CB000180h[5:0] = 111111b (undefined register) */ + pch_iobp_update(0xcb000180, ~0x0000003f, 0x0000003f); +} + +static void serialio_init(struct device *dev) +{ + struct southbridge_intel_lynxpoint_config *config = dev->chip_info; + struct resource *bar0, *bar1; + int sio_index = -1; + u32 reg32; + + printk(BIOS_DEBUG, "Initializing Serial IO device\n"); + + /* Ensure memory and bus master are enabled */ + reg32 = pci_read_config32(dev, PCI_COMMAND); + reg32 |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; + pci_write_config32(dev, PCI_COMMAND, reg32); + + /* Find BAR0 and BAR1 */ + bar0 = find_resource(dev, PCI_BASE_ADDRESS_0); + if (!bar0) + return; + bar1 = find_resource(dev, PCI_BASE_ADDRESS_1); + if (!bar1) + return; + + if (!config->sio_acpi_mode) + serialio_enable_clock(bar0); + else if (dev->path.pci.devfn != PCI_DEVFN(21, 0)) + serialio_enable_d3hot(dev); /* all but SDMA */ + + switch (dev->path.pci.devfn) { + case PCI_DEVFN(21, 0): /* SDMA */ + sio_index = SIO_ID_SDMA; + serialio_init_once(config->sio_acpi_mode); + serialio_d21_mode(sio_index, SIO_PIN_INTB, + config->sio_acpi_mode); + break; + case PCI_DEVFN(21, 1): /* I2C0 */ + sio_index = SIO_ID_I2C0; + serialio_d21_ltr(bar0); + serialio_i2c_voltage_sel(bar0, config->sio_i2c0_voltage); + serialio_d21_mode(sio_index, SIO_PIN_INTC, + config->sio_acpi_mode); + break; + case PCI_DEVFN(21, 2): /* I2C1 */ + sio_index = SIO_ID_I2C1; + serialio_d21_ltr(bar0); + serialio_i2c_voltage_sel(bar0, config->sio_i2c1_voltage); + serialio_d21_mode(sio_index, SIO_PIN_INTC, + config->sio_acpi_mode); + break; + case PCI_DEVFN(21, 3): /* SPI0 */ + sio_index = SIO_ID_SPI0; + serialio_d21_ltr(bar0); + serialio_d21_mode(sio_index, SIO_PIN_INTC, + config->sio_acpi_mode); + break; + case PCI_DEVFN(21, 4): /* SPI1 */ + sio_index = SIO_ID_SPI1; + serialio_d21_ltr(bar0); + serialio_d21_mode(sio_index, SIO_PIN_INTC, + config->sio_acpi_mode); + break; + case PCI_DEVFN(21, 5): /* UART0 */ + sio_index = SIO_ID_UART0; + serialio_d21_ltr(bar0); + serialio_d21_mode(sio_index, SIO_PIN_INTD, + config->sio_acpi_mode); + break; + case PCI_DEVFN(21, 6): /* UART1 */ + sio_index = SIO_ID_UART1; + serialio_d21_ltr(bar0); + serialio_d21_mode(sio_index, SIO_PIN_INTD, + config->sio_acpi_mode); + break; + case PCI_DEVFN(23, 0): /* SDIO */ + sio_index = SIO_ID_SDIO; + serialio_d23_ltr(bar0); + serialio_d23_mode(config->sio_acpi_mode); + break; + default: + return; + } + + if (config->sio_acpi_mode) { + global_nvs_t *gnvs; + + /* Find ACPI NVS to update BARs */ + gnvs = (global_nvs_t *)cbmem_find(CBMEM_ID_ACPI_GNVS); + if (!gnvs) { + printk(BIOS_ERR, "Unable to locate Global NVS\n"); + return; + } + + /* Save BAR0 and BAR1 to ACPI NVS */ + gnvs->s0b[sio_index] = (u32)bar0->base; + gnvs->s1b[sio_index] = (u32)bar1->base; + } +} + +static void serialio_set_subsystem(device_t dev, unsigned vendor, + unsigned device) +{ + if (!vendor || !device) { + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + pci_read_config32(dev, PCI_VENDOR_ID)); + } else { + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + ((device & 0xffff) << 16) | (vendor & 0xffff)); + } +} + +static struct pci_operations pci_ops = { + .set_subsystem = serialio_set_subsystem, +}; + +static struct device_operations device_ops = { + .read_resources = pci_dev_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .init = serialio_init, + .ops_pci = &pci_ops, +}; + +static const unsigned short pci_device_ids[] = { + 0x9c60, /* 0:15.0 - SDMA */ + 0x9c61, /* 0:15.1 - I2C0 */ + 0x9c62, /* 0:15.2 - I2C1 */ + 0x9c65, /* 0:15.3 - SPI0 */ + 0x9c66, /* 0:15.4 - SPI1 */ + 0x9c63, /* 0:15.5 - UART0 */ + 0x9c64, /* 0:15.6 - UART1 */ + 0x9c35, /* 0:17.0 - SDIO */ + 0 +}; + +static const struct pci_driver pch_pcie __pci_driver = { + .ops = &device_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .devices = pci_device_ids, +}; diff --git a/src/soc/intel/broadwell/smbus.c b/src/soc/intel/broadwell/smbus.c new file mode 100644 index 0000000000..fa6860568f --- /dev/null +++ b/src/soc/intel/broadwell/smbus.c @@ -0,0 +1,170 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008-2009 coresystems GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "pch.h" +#include "smbus.h" + +static void pch_smbus_init(device_t dev) +{ + struct resource *res; + u16 reg16; + + /* Enable clock gating */ + reg16 = pci_read_config32(dev, 0x80); + reg16 &= ~((1 << 8)|(1 << 10)|(1 << 12)|(1 << 14)); + pci_write_config32(dev, 0x80, reg16); + + /* Set Receive Slave Address */ + res = find_resource(dev, PCI_BASE_ADDRESS_4); + if (res) + outb(SMBUS_SLAVE_ADDR, res->base + SMB_RCV_SLVA); +} + +static int lsmbus_read_byte(device_t dev, u8 address) +{ + u16 device; + struct resource *res; + struct bus *pbus; + + device = dev->path.i2c.device; + pbus = get_pbus_smbus(dev); + res = find_resource(pbus->dev, PCI_BASE_ADDRESS_4); + + return do_smbus_read_byte(res->base, device, address); +} + +static int do_smbus_write_byte(unsigned smbus_base, unsigned device, + unsigned address, unsigned data) +{ + unsigned char global_status_register; + + if (smbus_wait_until_ready(smbus_base) < 0) + return SMBUS_WAIT_UNTIL_READY_TIMEOUT; + + /* Setup transaction */ + /* Disable interrupts */ + outb(inb(smbus_base + SMBHSTCTL) & (~1), smbus_base + SMBHSTCTL); + /* Set the device I'm talking too */ + outb(((device & 0x7f) << 1) & ~0x01, smbus_base + SMBXMITADD); + /* Set the command/address... */ + outb(address & 0xff, smbus_base + SMBHSTCMD); + /* Set up for a byte data read */ + outb((inb(smbus_base + SMBHSTCTL) & 0xe3) | (0x2 << 2), + (smbus_base + SMBHSTCTL)); + /* Clear any lingering errors, so the transaction will run */ + outb(inb(smbus_base + SMBHSTSTAT), smbus_base + SMBHSTSTAT); + + /* Clear the data byte... */ + outb(data, smbus_base + SMBHSTDAT0); + + /* Start the command */ + outb((inb(smbus_base + SMBHSTCTL) | 0x40), + smbus_base + SMBHSTCTL); + + /* Poll for transaction completion */ + if (smbus_wait_until_done(smbus_base) < 0) { + printk(BIOS_ERR, "SMBUS transaction timeout\n"); + return SMBUS_WAIT_UNTIL_DONE_TIMEOUT; + } + + global_status_register = inb(smbus_base + SMBHSTSTAT); + + /* Ignore the "In Use" status... */ + global_status_register &= ~(3 << 5); + + /* Read results of transaction */ + if (global_status_register != (1 << 1)) { + printk(BIOS_ERR, "SMBUS transaction error\n"); + return SMBUS_ERROR; + } + + return 0; +} + +static int lsmbus_write_byte(device_t dev, u8 address, u8 data) +{ + u16 device; + struct resource *res; + struct bus *pbus; + + device = dev->path.i2c.device; + pbus = get_pbus_smbus(dev); + res = find_resource(pbus->dev, PCI_BASE_ADDRESS_4); + return do_smbus_write_byte(res->base, device, address, data); +} + +static struct smbus_bus_operations lops_smbus_bus = { + .read_byte = lsmbus_read_byte, + .write_byte = lsmbus_write_byte, +}; + +static void smbus_set_subsystem(device_t dev, unsigned vendor, unsigned device) +{ + if (!vendor || !device) { + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + pci_read_config32(dev, PCI_VENDOR_ID)); + } else { + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + ((device & 0xffff) << 16) | (vendor & 0xffff)); + } +} + +static struct pci_operations smbus_pci_ops = { + .set_subsystem = smbus_set_subsystem, +}; + +static void smbus_read_resources(device_t dev) +{ + struct resource *res = new_resource(dev, PCI_BASE_ADDRESS_4); + res->base = SMBUS_IO_BASE; + res->size = 32; + res->limit = res->base + res->size - 1; + res->flags = IORESOURCE_IO | IORESOURCE_FIXED | IORESOURCE_RESERVE | + IORESOURCE_STORED | IORESOURCE_ASSIGNED; + + /* Also add MMIO resource */ + res = pci_get_resource(dev, PCI_BASE_ADDRESS_0); +} + +static struct device_operations smbus_ops = { + .read_resources = smbus_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .scan_bus = scan_static_bus, + .init = pch_smbus_init, + .ops_smbus_bus = &lops_smbus_bus, + .ops_pci = &smbus_pci_ops, +}; + +static const unsigned short pci_device_ids[] = { 0x1c22, 0x1e22, 0x9c22, 0 }; + +static const struct pci_driver pch_smbus __pci_driver = { + .ops = &smbus_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .devices = pci_device_ids, +}; diff --git a/src/soc/intel/broadwell/smi.c b/src/soc/intel/broadwell/smi.c new file mode 100644 index 0000000000..b88a70f735 --- /dev/null +++ b/src/soc/intel/broadwell/smi.c @@ -0,0 +1,126 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008-2009 coresystems GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include "pch.h" + +void southbridge_smm_clear_state(void) +{ + u32 smi_en; + +#if CONFIG_ELOG + /* Log events from chipset before clearing */ + pch_log_state(); +#endif + printk(BIOS_DEBUG, "Initializing Southbridge SMI..."); + printk(BIOS_SPEW, " ... pmbase = 0x%04x\n", get_pmbase()); + + smi_en = inl(get_pmbase() + SMI_EN); + if (smi_en & APMC_EN) { + printk(BIOS_INFO, "SMI# handler already enabled?\n"); + return; + } + + printk(BIOS_DEBUG, "\n"); + + /* Dump and clear status registers */ + clear_smi_status(); + clear_pm1_status(); + clear_tco_status(); + clear_gpe_status(); +} + +void southbridge_smm_enable_smi(void) +{ + printk(BIOS_DEBUG, "Enabling SMIs.\n"); + /* Configure events */ + enable_pm1(PWRBTN_EN | GBL_EN); + disable_gpe(PME_B0_EN); + + /* Enable SMI generation: + * - on APMC writes (io 0xb2) + * - on writes to SLP_EN (sleep states) + * - on writes to GBL_RLS (bios commands) + * No SMIs: + * - on microcontroller writes (io 0x62/0x66) + * - on TCO events + */ + enable_smi(APMC_EN | SLP_SMI_EN | GBL_SMI_EN | EOS); +} + +void southbridge_trigger_smi(void) +{ + /** + * There are several methods of raising a controlled SMI# via + * software, among them: + * - Writes to io 0xb2 (APMC) + * - Writes to the Local Apic ICR with Delivery mode SMI. + * + * Using the local apic is a bit more tricky. According to + * AMD Family 11 Processor BKDG no destination shorthand must be + * used. + * The whole SMM initialization is quite a bit hardware specific, so + * I'm not too worried about the better of the methods at the moment + */ + + /* raise an SMI interrupt */ + printk(BIOS_SPEW, " ... raise SMI#\n"); + outb(0x00, 0xb2); +} + +void southbridge_clear_smi_status(void) +{ + /* Clear SMI status */ + clear_smi_status(); + + /* Clear PM1 status */ + clear_pm1_status(); + + /* Set EOS bit so other SMIs can occur. */ + enable_smi(EOS); +} + +void smm_setup_structures(void *gnvs, void *tcg, void *smi1) +{ + /* + * Issue SMI to set the gnvs pointer in SMM. + * tcg and smi1 are unused. + * + * EAX = APM_CNT_GNVS_UPDATE + * EBX = gnvs pointer + * EDX = APM_CNT + */ + asm volatile ( + "outb %%al, %%dx\n\t" + : /* ignore result */ + : "a" (APM_CNT_GNVS_UPDATE), + "b" ((u32)gnvs), + "d" (APM_CNT) + ); +} diff --git a/src/soc/intel/broadwell/smihandler.c b/src/soc/intel/broadwell/smihandler.c new file mode 100644 index 0000000000..00e4a839f4 --- /dev/null +++ b/src/soc/intel/broadwell/smihandler.c @@ -0,0 +1,546 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008-2009 coresystems GmbH + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pch.h" + +#include "nvs.h" + + +static u8 smm_initialized = 0; + +/* GNVS needs to be updated by an 0xEA PM Trap (B2) after it has been located + * by coreboot. + */ +static global_nvs_t *gnvs; +global_nvs_t *smm_get_gnvs(void) +{ + return gnvs; +} + +int southbridge_io_trap_handler(int smif) +{ + switch (smif) { + case 0x32: + printk(BIOS_DEBUG, "OS Init\n"); + /* gnvs->smif: + * On success, the IO Trap Handler returns 0 + * On failure, the IO Trap Handler returns a value != 0 + */ + gnvs->smif = 0; + return 1; /* IO trap handled */ + } + + /* Not handled */ + return 0; +} + +/** + * @brief Set the EOS bit + */ +void southbridge_smi_set_eos(void) +{ + enable_smi(EOS); +} + +static void busmaster_disable_on_bus(int bus) +{ + int slot, func; + unsigned int val; + unsigned char hdr; + + for (slot = 0; slot < 0x20; slot++) { + for (func = 0; func < 8; func++) { + u32 reg32; + device_t dev = PCI_DEV(bus, slot, func); + + val = pci_read_config32(dev, PCI_VENDOR_ID); + + if (val == 0xffffffff || val == 0x00000000 || + val == 0x0000ffff || val == 0xffff0000) + continue; + + /* Disable Bus Mastering for this one device */ + reg32 = pci_read_config32(dev, PCI_COMMAND); + reg32 &= ~PCI_COMMAND_MASTER; + pci_write_config32(dev, PCI_COMMAND, reg32); + + /* If this is a bridge, then follow it. */ + hdr = pci_read_config8(dev, PCI_HEADER_TYPE); + hdr &= 0x7f; + if (hdr == PCI_HEADER_TYPE_BRIDGE || + hdr == PCI_HEADER_TYPE_CARDBUS) { + unsigned int buses; + buses = pci_read_config32(dev, PCI_PRIMARY_BUS); + busmaster_disable_on_bus((buses >> 8) & 0xff); + } + } + } +} + + +static void southbridge_smi_sleep(void) +{ + u8 reg8; + u32 reg32; + u8 slp_typ; + u8 s5pwr = CONFIG_MAINBOARD_POWER_ON_AFTER_POWER_FAIL; + u16 pmbase = get_pmbase(); + + // save and recover RTC port values + u8 tmp70, tmp72; + tmp70 = inb(0x70); + tmp72 = inb(0x72); + get_option(&s5pwr, "power_on_after_fail"); + outb(tmp70, 0x70); + outb(tmp72, 0x72); + + /* First, disable further SMIs */ + disable_smi(SLP_SMI_EN); + + /* Figure out SLP_TYP */ + reg32 = inl(pmbase + PM1_CNT); + printk(BIOS_SPEW, "SMI#: SLP = 0x%08x\n", reg32); + slp_typ = (reg32 >> 10) & 7; + + /* Do any mainboard sleep handling */ + mainboard_smi_sleep(slp_typ-2); + + /* USB sleep preparations */ +#if !CONFIG_FINALIZE_USB_ROUTE_XHCI + usb_ehci_sleep_prepare(PCH_EHCI1_DEV, slp_typ); + usb_ehci_sleep_prepare(PCH_EHCI2_DEV, slp_typ); +#endif + usb_xhci_sleep_prepare(PCH_XHCI_DEV, slp_typ); + +#if CONFIG_ELOG_GSMI + /* Log S3, S4, and S5 entry */ + if (slp_typ >= 5) + elog_add_event_byte(ELOG_TYPE_ACPI_ENTER, slp_typ-2); +#endif + + /* Next, do the deed. + */ + + switch (slp_typ) { + case SLP_TYP_S0: + printk(BIOS_DEBUG, "SMI#: Entering S0 (On)\n"); + break; + case SLP_TYP_S1: + printk(BIOS_DEBUG, "SMI#: Entering S1 (Assert STPCLK#)\n"); + break; + case SLP_TYP_S3: + printk(BIOS_DEBUG, "SMI#: Entering S3 (Suspend-To-RAM)\n"); + + /* Invalidate the cache before going to S3 */ + wbinvd(); + break; + case SLP_TYP_S4: + printk(BIOS_DEBUG, "SMI#: Entering S4 (Suspend-To-Disk)\n"); + break; + case SLP_TYP_S5: + printk(BIOS_DEBUG, "SMI#: Entering S5 (Soft Power off)\n"); + + /* Disable all GPE */ + disable_all_gpe(); + + /* Always set the flag in case CMOS was changed on runtime. For + * "KEEP", switch to "OFF" - KEEP is software emulated + */ + reg8 = pci_read_config8(PCI_DEV(0, 0x1f, 0), GEN_PMCON_3); + if (s5pwr == MAINBOARD_POWER_ON) { + reg8 &= ~1; + } else { + reg8 |= 1; + } + pci_write_config8(PCI_DEV(0, 0x1f, 0), GEN_PMCON_3, reg8); + + /* also iterates over all bridges on bus 0 */ + busmaster_disable_on_bus(0); + break; + default: + printk(BIOS_DEBUG, "SMI#: ERROR: SLP_TYP reserved\n"); + break; + } + + /* Write back to the SLP register to cause the originally intended + * event again. We need to set BIT13 (SLP_EN) though to make the + * sleep happen. + */ + enable_pm1_control(SLP_EN); + + /* Make sure to stop executing code here for S3/S4/S5 */ + if (slp_typ > 1) + hlt(); + + /* In most sleep states, the code flow of this function ends at + * the line above. However, if we entered sleep state S1 and wake + * up again, we will continue to execute code in this function. + */ + reg32 = inl(pmbase + PM1_CNT); + if (reg32 & SCI_EN) { + /* The OS is not an ACPI OS, so we set the state to S0 */ + disable_pm1_control(SLP_EN | SLP_TYP); + } +} + +/* + * Look for Synchronous IO SMI and use save state from that + * core in case we are not running on the same core that + * initiated the IO transaction. + */ +static em64t101_smm_state_save_area_t *smi_apmc_find_state_save(u8 cmd) +{ + em64t101_smm_state_save_area_t *state; + int node; + + /* Check all nodes looking for the one that issued the IO */ + for (node = 0; node < CONFIG_MAX_CPUS; node++) { + state = smm_get_save_state(node); + + /* Check for Synchronous IO (bit0==1) */ + if (!(state->io_misc_info & (1 << 0))) + continue; + + /* Make sure it was a write (bit4==0) */ + if (state->io_misc_info & (1 << 4)) + continue; + + /* Check for APMC IO port */ + if (((state->io_misc_info >> 16) & 0xff) != APM_CNT) + continue; + + /* Check AX against the requested command */ + if ((state->rax & 0xff) != cmd) + continue; + + return state; + } + + return NULL; +} + +#if CONFIG_ELOG_GSMI +static void southbridge_smi_gsmi(void) +{ + u32 *ret, *param; + u8 sub_command; + em64t101_smm_state_save_area_t *io_smi = + smi_apmc_find_state_save(ELOG_GSMI_APM_CNT); + + if (!io_smi) + return; + + /* Command and return value in EAX */ + ret = (u32*)&io_smi->rax; + sub_command = (u8)(*ret >> 8); + + /* Parameter buffer in EBX */ + param = (u32*)&io_smi->rbx; + + /* drivers/elog/gsmi.c */ + *ret = gsmi_exec(sub_command, param); +} +#endif + +static void southbridge_smi_apmc(void) +{ + u8 reg8; + em64t101_smm_state_save_area_t *state; + + /* Emulate B2 register as the FADT / Linux expects it */ + + reg8 = inb(APM_CNT); + switch (reg8) { + case APM_CNT_CST_CONTROL: + /* Calling this function seems to cause + * some kind of race condition in Linux + * and causes a kernel oops + */ + printk(BIOS_DEBUG, "C-state control\n"); + break; + case APM_CNT_PST_CONTROL: + /* Calling this function seems to cause + * some kind of race condition in Linux + * and causes a kernel oops + */ + printk(BIOS_DEBUG, "P-state control\n"); + break; + case APM_CNT_ACPI_DISABLE: + disable_pm1_control(SCI_EN); + printk(BIOS_DEBUG, "SMI#: ACPI disabled.\n"); + break; + case APM_CNT_ACPI_ENABLE: + enable_pm1_control(SCI_EN); + printk(BIOS_DEBUG, "SMI#: ACPI enabled.\n"); + break; + case APM_CNT_GNVS_UPDATE: + if (smm_initialized) { + printk(BIOS_DEBUG, + "SMI#: SMM structures already initialized!\n"); + return; + } + state = smi_apmc_find_state_save(reg8); + if (state) { + /* EBX in the state save contains the GNVS pointer */ + gnvs = (global_nvs_t *)((u32)state->rbx); + smm_initialized = 1; + printk(BIOS_DEBUG, "SMI#: Setting GNVS to %p\n", gnvs); + } + break; + case 0xca: + usb_xhci_route_all(); + break; +#if CONFIG_ELOG_GSMI + case ELOG_GSMI_APM_CNT: + southbridge_smi_gsmi(); + break; +#endif + } + + mainboard_smi_apmc(reg8); +} + +static void southbridge_smi_pm1(void) +{ + u16 pm1_sts = clear_pm1_status(); + + /* While OSPM is not active, poweroff immediately + * on a power button event. + */ + if (pm1_sts & PWRBTN_STS) { + // power button pressed +#if CONFIG_ELOG_GSMI + elog_add_event(ELOG_TYPE_POWER_BUTTON); +#endif + disable_pm1_control(-1UL); + enable_pm1_control(SLP_EN | (SLP_TYP_S5 << 10)); + } +} + +static void southbridge_smi_gpe0(void) +{ + clear_gpe_status(); +} + +static void southbridge_smi_gpi(void) +{ + mainboard_smi_gpi(clear_alt_smi_status()); + + /* Clear again after mainboard handler */ + clear_alt_smi_status(); +} + +static void southbridge_smi_mc(void) +{ + u32 reg32; + + reg32 = inl(get_pmbase() + SMI_EN); + + /* Are microcontroller SMIs enabled? */ + if ((reg32 & MCSMI_EN) == 0) + return; + + printk(BIOS_DEBUG, "Microcontroller SMI.\n"); +} + + + +static void southbridge_smi_tco(void) +{ + u32 tco_sts = clear_tco_status(); + + /* Any TCO event? */ + if (!tco_sts) + return; + + if (tco_sts & (1 << 8)) { // BIOSWR + u8 bios_cntl; + + bios_cntl = pci_read_config16(PCI_DEV(0, 0x1f, 0), 0xdc); + + if (bios_cntl & 1) { + /* BWE is RW, so the SMI was caused by a + * write to BWE, not by a write to the BIOS + */ + + /* This is the place where we notice someone + * is trying to tinker with the BIOS. We are + * trying to be nice and just ignore it. A more + * resolute answer would be to power down the + * box. + */ + printk(BIOS_DEBUG, "Switching back to RO\n"); + pci_write_config32(PCI_DEV(0, 0x1f, 0), 0xdc, + (bios_cntl & ~1)); + } /* No else for now? */ + } else if (tco_sts & (1 << 3)) { /* TIMEOUT */ + /* Handle TCO timeout */ + printk(BIOS_DEBUG, "TCO Timeout.\n"); + } +} + +static void southbridge_smi_periodic(void) +{ + u32 reg32; + + reg32 = inl(get_pmbase() + SMI_EN); + + /* Are periodic SMIs enabled? */ + if ((reg32 & PERIODIC_EN) == 0) + return; + + printk(BIOS_DEBUG, "Periodic SMI.\n"); +} + +static void southbridge_smi_monitor(void) +{ +#define IOTRAP(x) (trap_sts & (1 << x)) + u32 trap_sts, trap_cycle; + u32 data, mask = 0; + int i; + + trap_sts = RCBA32(0x1e00); // TRSR - Trap Status Register + RCBA32(0x1e00) = trap_sts; // Clear trap(s) in TRSR + + trap_cycle = RCBA32(0x1e10); + for (i=16; i<20; i++) { + if (trap_cycle & (1 << i)) + mask |= (0xff << ((i - 16) << 2)); + } + + + /* IOTRAP(3) SMI function call */ + if (IOTRAP(3)) { + if (gnvs && gnvs->smif) + io_trap_handler(gnvs->smif); // call function smif + return; + } + + /* IOTRAP(2) currently unused + * IOTRAP(1) currently unused */ + + /* IOTRAP(0) SMIC */ + if (IOTRAP(0)) { + if (!(trap_cycle & (1 << 24))) { // It's a write + printk(BIOS_DEBUG, "SMI1 command\n"); + data = RCBA32(0x1e18); + data &= mask; + // if (smi1) + // southbridge_smi_command(data); + // return; + } + // Fall through to debug + } + + printk(BIOS_DEBUG, " trapped io address = 0x%x\n", + trap_cycle & 0xfffc); + for (i=0; i < 4; i++) + if(IOTRAP(i)) printk(BIOS_DEBUG, " TRAP = %d\n", i); + printk(BIOS_DEBUG, " AHBE = %x\n", (trap_cycle >> 16) & 0xf); + printk(BIOS_DEBUG, " MASK = 0x%08x\n", mask); + printk(BIOS_DEBUG, " read/write: %s\n", + (trap_cycle & (1 << 24)) ? "read" : "write"); + + if (!(trap_cycle & (1 << 24))) { + /* Write Cycle */ + data = RCBA32(0x1e18); + printk(BIOS_DEBUG, " iotrap written data = 0x%08x\n", data); + } +#undef IOTRAP +} + +typedef void (*smi_handler_t)(void); + +static smi_handler_t southbridge_smi[32] = { + NULL, // [0] reserved + NULL, // [1] reserved + NULL, // [2] BIOS_STS + NULL, // [3] LEGACY_USB_STS + southbridge_smi_sleep, // [4] SLP_SMI_STS + southbridge_smi_apmc, // [5] APM_STS + NULL, // [6] SWSMI_TMR_STS + NULL, // [7] reserved + southbridge_smi_pm1, // [8] PM1_STS + southbridge_smi_gpe0, // [9] GPE0_STS + southbridge_smi_gpi, // [10] GPI_STS + southbridge_smi_mc, // [11] MCSMI_STS + NULL, // [12] DEVMON_STS + southbridge_smi_tco, // [13] TCO_STS + southbridge_smi_periodic, // [14] PERIODIC_STS + NULL, // [15] SERIRQ_SMI_STS + NULL, // [16] SMBUS_SMI_STS + NULL, // [17] LEGACY_USB2_STS + NULL, // [18] INTEL_USB2_STS + NULL, // [19] reserved + NULL, // [20] PCI_EXP_SMI_STS + southbridge_smi_monitor, // [21] MONITOR_STS + NULL, // [22] reserved + NULL, // [23] reserved + NULL, // [24] reserved + NULL, // [25] EL_SMI_STS + NULL, // [26] SPI_STS + NULL, // [27] reserved + NULL, // [28] reserved + NULL, // [29] reserved + NULL, // [30] reserved + NULL // [31] reserved +}; + +/** + * @brief Interrupt handler for SMI# + * + * @param smm_revision revision of the smm state save map + */ + +void southbridge_smi_handler(void) +{ + int i; + u32 smi_sts; + + /* We need to clear the SMI status registers, or we won't see what's + * happening in the following calls. + */ + smi_sts = clear_smi_status(); + + /* Call SMI sub handler for each of the status bits */ + for (i = 0; i < 31; i++) { + if (smi_sts & (1 << i)) { + if (southbridge_smi[i]) { + southbridge_smi[i](); + } else { + printk(BIOS_DEBUG, + "SMI_STS[%d] occured, but no " + "handler available.\n", i); + } + } + } +} diff --git a/src/soc/intel/broadwell/smmrelocate.c b/src/soc/intel/broadwell/smmrelocate.c new file mode 100644 index 0000000000..aeaf1ff574 --- /dev/null +++ b/src/soc/intel/broadwell/smmrelocate.c @@ -0,0 +1,479 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2013 ChromeOS Authors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "haswell.h" + +#define EMRRphysBase_MSR 0x1f4 +#define EMRRphysMask_MSR 0x1f5 +#define UNCORE_EMRRphysBase_MSR 0x2f4 +#define UNCORE_EMRRphysMask_MSR 0x2f5 +#define SMM_MCA_CAP_MSR 0x17d +#define SMM_CPU_SVRSTR_BIT 57 +#define SMM_CPU_SVRSTR_MASK (1 << (SMM_CPU_SVRSTR_BIT - 32)) +#define SMM_FEATURE_CONTROL_MSR 0x4e0 +#define SMM_CPU_SAVE_EN (1 << 1) +/* SMM save state MSRs */ +#define SMBASE_MSR 0xc20 +#define IEDBASE_MSR 0xc22 + +#define SMRR_SUPPORTED (1<<11) +#define EMRR_SUPPORTED (1<<12) + +struct smm_relocation_params { + u32 smram_base; + u32 smram_size; + u32 ied_base; + u32 ied_size; + msr_t smrr_base; + msr_t smrr_mask; + msr_t emrr_base; + msr_t emrr_mask; + msr_t uncore_emrr_base; + msr_t uncore_emrr_mask; + /* The smm_save_state_in_msrs field indicates if SMM save state + * locations live in MSRs. This indicates to the CPUs how to adjust + * the SMMBASE and IEDBASE */ + int smm_save_state_in_msrs; +}; + +/* This gets filled in and used during relocation. */ +static struct smm_relocation_params smm_reloc_params; + +static inline void write_smrr(struct smm_relocation_params *relo_params) +{ + printk(BIOS_DEBUG, "Writing SMRR. base = 0x%08x, mask=0x%08x\n", + relo_params->smrr_base.lo, relo_params->smrr_mask.lo); + wrmsr(SMRRphysBase_MSR, relo_params->smrr_base); + wrmsr(SMRRphysMask_MSR, relo_params->smrr_mask); +} + +static inline void write_emrr(struct smm_relocation_params *relo_params) +{ + printk(BIOS_DEBUG, "Writing EMRR. base = 0x%08x, mask=0x%08x\n", + relo_params->emrr_base.lo, relo_params->emrr_mask.lo); + wrmsr(EMRRphysBase_MSR, relo_params->emrr_base); + wrmsr(EMRRphysMask_MSR, relo_params->emrr_mask); +} + +static inline void write_uncore_emrr(struct smm_relocation_params *relo_params) +{ + printk(BIOS_DEBUG, + "Writing UNCORE_EMRR. base = 0x%08x, mask=0x%08x\n", + relo_params->uncore_emrr_base.lo, + relo_params->uncore_emrr_mask.lo); + wrmsr(UNCORE_EMRRphysBase_MSR, relo_params->uncore_emrr_base); + wrmsr(UNCORE_EMRRphysMask_MSR, relo_params->uncore_emrr_mask); +} + +static void update_save_state(int cpu, + struct smm_relocation_params *relo_params, + const struct smm_runtime *runtime) +{ + u32 smbase; + u32 iedbase; + + /* The relocated handler runs with all CPUs concurrently. Therefore + * stagger the entry points adjusting SMBASE downwards by save state + * size * CPU num. */ + smbase = relo_params->smram_base - cpu * runtime->save_state_size; + iedbase = relo_params->ied_base; + + printk(BIOS_DEBUG, "New SMBASE=0x%08x IEDBASE=0x%08x\n", + smbase, iedbase); + + /* All threads need to set IEDBASE and SMBASE to the relocated + * handler region. However, the save state location depends on the + * smm_save_state_in_msrs field in the relocation parameters. If + * smm_save_state_in_msrs is non-zero then the CPUs are relocating + * the SMM handler in parallel, and each CPUs save state area is + * located in their respective MSR space. If smm_save_state_in_msrs + * is zero then the SMM relocation is happening serially so the + * save state is at the same default location for all CPUs. */ + if (relo_params->smm_save_state_in_msrs) { + msr_t smbase_msr; + msr_t iedbase_msr; + + smbase_msr.lo = smbase; + smbase_msr.hi = 0; + + /* According the BWG the IEDBASE MSR is in bits 63:32. It's + * not clear why it differs from the SMBASE MSR. */ + iedbase_msr.lo = 0; + iedbase_msr.hi = iedbase; + + wrmsr(SMBASE_MSR, smbase_msr); + wrmsr(IEDBASE_MSR, iedbase_msr); + } else { + em64t101_smm_state_save_area_t *save_state; + + save_state = (void *)(runtime->smbase + SMM_DEFAULT_SIZE - + runtime->save_state_size); + + save_state->smbase = smbase; + save_state->iedbase = iedbase; + } +} + +/* Returns 1 if SMM MSR save state was set. */ +static int bsp_setup_msr_save_state(struct smm_relocation_params *relo_params) +{ + msr_t smm_mca_cap; + + smm_mca_cap = rdmsr(SMM_MCA_CAP_MSR); + if (smm_mca_cap.hi & SMM_CPU_SVRSTR_MASK) { + msr_t smm_feature_control; + + smm_feature_control = rdmsr(SMM_FEATURE_CONTROL_MSR); + smm_feature_control.hi = 0; + smm_feature_control.lo |= SMM_CPU_SAVE_EN; + wrmsr(SMM_FEATURE_CONTROL_MSR, smm_feature_control); + relo_params->smm_save_state_in_msrs = 1; + } + return relo_params->smm_save_state_in_msrs; +} + +/* The relocation work is actually performed in SMM context, but the code + * resides in the ramstage module. This occurs by trampolining from the default + * SMRAM entry point to here. */ +static void asmlinkage cpu_smm_do_relocation(void *arg) +{ + msr_t mtrr_cap; + struct smm_relocation_params *relo_params; + const struct smm_module_params *p; + const struct smm_runtime *runtime; + int cpu; + + p = arg; + runtime = p->runtime; + relo_params = p->arg; + cpu = p->cpu; + + if (cpu >= CONFIG_MAX_CPUS) { + printk(BIOS_CRIT, + "Invalid CPU number assigned in SMM stub: %d\n", cpu); + return; + } + + printk(BIOS_DEBUG, "In relocation handler: cpu %d\n", cpu); + + /* Determine if the processor supports saving state in MSRs. If so, + * enable it before the non-BSPs run so that SMM relocation can occur + * in parallel in the non-BSP CPUs. */ + if (cpu == 0) { + /* If smm_save_state_in_msrs is 1 then that means this is the + * 2nd time through the relocation handler for the BSP. + * Parallel SMM handler relocation is taking place. However, + * it is desired to access other CPUs save state in the real + * SMM handler. Therefore, disable the SMM save state in MSRs + * feature. */ + if (relo_params->smm_save_state_in_msrs) { + msr_t smm_feature_control; + + smm_feature_control = rdmsr(SMM_FEATURE_CONTROL_MSR); + smm_feature_control.lo &= ~SMM_CPU_SAVE_EN; + wrmsr(SMM_FEATURE_CONTROL_MSR, smm_feature_control); + } else if (bsp_setup_msr_save_state(relo_params)) + /* Just return from relocation handler if MSR save + * state is enabled. In that case the BSP will come + * back into the relocation handler to setup the new + * SMBASE as well disabling SMM save state in MSRs. */ + return; + } + + /* Make appropriate changes to the save state map. */ + update_save_state(cpu, relo_params, runtime); + + /* Write EMRR and SMRR MSRs based on indicated support. */ + mtrr_cap = rdmsr(MTRRcap_MSR); + if (mtrr_cap.lo & SMRR_SUPPORTED) + write_smrr(relo_params); + + if (mtrr_cap.lo & EMRR_SUPPORTED) { + write_emrr(relo_params); + /* UNCORE_EMRR msrs are package level. Therefore, only + * configure these MSRs on the BSP. */ + if (cpu == 0) + write_uncore_emrr(relo_params); + } +} + +static u32 northbridge_get_base_reg(device_t dev, int reg) +{ + u32 value; + + value = pci_read_config32(dev, reg); + /* Base registers are at 1MiB granularity. */ + value &= ~((1 << 20) - 1); + return value; +} + +static void fill_in_relocation_params(device_t dev, + struct smm_relocation_params *params) +{ + u32 tseg_size; + u32 tsegmb; + u32 bgsm; + u32 emrr_base; + u32 emrr_size; + int phys_bits; + /* All range registers are aligned to 4KiB */ + const u32 rmask = ~((1 << 12) - 1); + + /* Some of the range registers are dependent on the number of physical + * address bits supported. */ + phys_bits = cpuid_eax(0x80000008) & 0xff; + + /* The range bounded by the TSEGMB and BGSM registers encompasses the + * SMRAM range as well as the IED range. However, the SMRAM available + * to the handler is 4MiB since the IEDRAM lives TSEGMB + 4MiB. + */ + tsegmb = northbridge_get_base_reg(dev, TSEG); + bgsm = northbridge_get_base_reg(dev, BGSM); + tseg_size = bgsm - tsegmb; + + params->smram_base = tsegmb; + params->smram_size = 4 << 20; + params->ied_base = tsegmb + params->smram_size; + params->ied_size = tseg_size - params->smram_size; + + /* Adjust available SMM handler memory size. */ + params->smram_size -= RESERVED_SMM_SIZE; + + /* SMRR has 32-bits of valid address aligned to 4KiB. */ + params->smrr_base.lo = (params->smram_base & rmask) | MTRR_TYPE_WRBACK; + params->smrr_base.hi = 0; + params->smrr_mask.lo = (~(tseg_size - 1) & rmask) | MTRRphysMaskValid; + params->smrr_mask.hi = 0; + + /* The EMRR and UNCORE_EMRR are at IEDBASE + 2MiB */ + emrr_base = (params->ied_base + (2 << 20)) & rmask; + emrr_size = params->ied_size - (2 << 20); + + /* EMRR has 46 bits of valid address aligned to 4KiB. It's dependent + * on the number of physical address bits supported. */ + params->emrr_base.lo = emrr_base | MTRR_TYPE_WRBACK; + params->emrr_base.hi = 0; + params->emrr_mask.lo = (~(emrr_size - 1) & rmask) | MTRRphysMaskValid; + params->emrr_mask.hi = (1 << (phys_bits - 32)) - 1; + + /* UNCORE_EMRR has 39 bits of valid address aligned to 4KiB. */ + params->uncore_emrr_base.lo = emrr_base; + params->uncore_emrr_base.hi = 0; + params->uncore_emrr_mask.lo = (~(emrr_size - 1) & rmask) | + MTRRphysMaskValid; + params->uncore_emrr_mask.hi = (1 << (39 - 32)) - 1; +} + +static void adjust_apic_id_map(struct smm_loader_params *smm_params) +{ + struct smm_runtime *runtime; + int i; + + /* Adjust the APIC id map if HT is disabled. */ + if (!ht_disabled) + return; + + runtime = smm_params->runtime; + + /* The APIC ids increment by 2 when HT is disabled. */ + for (i = 0; i < CONFIG_MAX_CPUS; i++) + runtime->apic_id_to_cpu[i] = runtime->apic_id_to_cpu[i] * 2; +} + +static int install_relocation_handler(int num_cpus, + struct smm_relocation_params *relo_params) +{ + /* The default SMM entry can happen in parallel or serially. If the + * default SMM entry is done in parallel the BSP has already setup + * the saving state to each CPU's MSRs. At least one save state size + * is required for the initial SMM entry for the BSP to determine if + * parallel SMM relocation is even feasible. Set the stack size to + * the save state size, and call into the do_relocation handler. */ + int save_state_size = sizeof(em64t101_smm_state_save_area_t); + struct smm_loader_params smm_params = { + .per_cpu_stack_size = save_state_size, + .num_concurrent_stacks = num_cpus, + .per_cpu_save_state_size = save_state_size, + .num_concurrent_save_states = 1, + .handler = (smm_handler_t)&cpu_smm_do_relocation, + .handler_arg = (void *)relo_params, + }; + + if (smm_setup_relocation_handler(&smm_params)) + return -1; + + adjust_apic_id_map(&smm_params); + + return 0; +} + +static void setup_ied_area(struct smm_relocation_params *params) +{ + char *ied_base; + + struct ied_header ied = { + .signature = "INTEL RSVD", + .size = params->ied_size, + .reserved = {0}, + }; + + ied_base = (void *)params->ied_base; + + /* Place IED header at IEDBASE. */ + memcpy(ied_base, &ied, sizeof(ied)); + + /* Zero out 32KiB at IEDBASE + 1MiB */ + memset(ied_base + (1 << 20), 0, (32 << 10)); + + /* According to the BWG MP init section 2MiB of memory at IEDBASE + + * 2MiB should be zeroed as well. However, I suspect what is inteneded + * is to clear the memory covered by EMRR. TODO(adurbin): figure out if * this is really required. */ + //memset(ied_base + (2 << 20), 0, (2 << 20)); +} + +static int install_permanent_handler(int num_cpus, + struct smm_relocation_params *relo_params) +{ + /* There are num_cpus concurrent stacks and num_cpus concurrent save + * state areas. Lastly, set the stack size to the save state size. */ + int save_state_size = sizeof(em64t101_smm_state_save_area_t); + struct smm_loader_params smm_params = { + .per_cpu_stack_size = save_state_size, + .num_concurrent_stacks = num_cpus, + .per_cpu_save_state_size = save_state_size, + .num_concurrent_save_states = num_cpus, + }; + + printk(BIOS_DEBUG, "Installing SMM handler to 0x%08x\n", + relo_params->smram_base); + if (smm_load_module((void *)relo_params->smram_base, + relo_params->smram_size, &smm_params)) + return -1; + + adjust_apic_id_map(&smm_params); + + return 0; +} + +static int cpu_smm_setup(void) +{ + device_t dev; + int num_cpus; + msr_t msr; + + printk(BIOS_DEBUG, "Setting up SMI for CPU\n"); + + dev = dev_find_slot(0, PCI_DEVFN(0, 0)); + + fill_in_relocation_params(dev, &smm_reloc_params); + + setup_ied_area(&smm_reloc_params); + + msr = rdmsr(CORE_THREAD_COUNT_MSR); + num_cpus = msr.lo & 0xffff; + if (num_cpus > CONFIG_MAX_CPUS) { + printk(BIOS_CRIT, + "Error: Hardware CPUs (%d) > MAX_CPUS (%d)\n", + num_cpus, CONFIG_MAX_CPUS); + } + + if (install_relocation_handler(num_cpus, &smm_reloc_params)) { + printk(BIOS_CRIT, "SMM Relocation handler install failed.\n"); + return -1; + } + + if (install_permanent_handler(num_cpus, &smm_reloc_params)) { + printk(BIOS_CRIT, "SMM Permanent handler install failed.\n"); + return -1; + } + + /* Ensure the SMM handlers hit DRAM before performing first SMI. */ + /* TODO(adurbin): Is this really needed? */ + wbinvd(); + + return 0; +} + +int smm_initialize(void) +{ + /* Return early if CPU SMM setup failed. */ + if (cpu_smm_setup()) + return -1; + + /* Clear the SMM state in the southbridge. */ + southbridge_smm_clear_state(); + + /* Run the relocation handler. */ + smm_initiate_relocation(); + + if (smm_reloc_params.smm_save_state_in_msrs) { + printk(BIOS_DEBUG, "Doing parallel SMM relocation.\n"); + } + + return 0; +} + +void smm_relocate(void) +{ + /* + * If smm_save_state_in_msrs is non-zero then parallel SMM relocation + * shall take place. Run the relocation handler a second time on the + * BSP to do * the final move. For APs, a relocation handler always + * needs to be run. + */ + if (smm_reloc_params.smm_save_state_in_msrs) + smm_initiate_relocation_parallel(); + else if (!boot_cpu()) + smm_initiate_relocation(); +} + +void smm_init(void) +{ + /* smm_init() is normally called from initialize_cpus() in + * lapic_cpu_init.c. However, that path is no longer used. Don't reuse + * the function name because that would cause confusion. + * The smm_initialize() function above is used to setup SMM at the + * appropriate time. */ +} + +void smm_lock(void) +{ + /* LOCK the SMM memory window and enable normal SMM. + * After running this function, only a full reset can + * make the SMM registers writable again. + */ + printk(BIOS_DEBUG, "Locking SMM.\n"); + pci_write_config8(dev_find_slot(0, PCI_DEVFN(0, 0)), SMRAM, + D_LCK | G_SMRAME | C_BASE_SEG); +} + diff --git a/src/soc/intel/broadwell/spi.c b/src/soc/intel/broadwell/spi.c new file mode 100644 index 0000000000..5325a23434 --- /dev/null +++ b/src/soc/intel/broadwell/spi.c @@ -0,0 +1,645 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* This file is derived from the flashrom project. */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define min(a, b) ((a)<(b)?(a):(b)) + +#ifdef __SMM__ +#define pci_read_config_byte(dev, reg, targ)\ + *(targ) = pci_read_config8(dev, reg) +#define pci_read_config_word(dev, reg, targ)\ + *(targ) = pci_read_config16(dev, reg) +#define pci_read_config_dword(dev, reg, targ)\ + *(targ) = pci_read_config32(dev, reg) +#define pci_write_config_byte(dev, reg, val)\ + pci_write_config8(dev, reg, val) +#define pci_write_config_word(dev, reg, val)\ + pci_write_config16(dev, reg, val) +#define pci_write_config_dword(dev, reg, val)\ + pci_write_config32(dev, reg, val) +#else /* !__SMM__ */ +#include +#include +#define pci_read_config_byte(dev, reg, targ)\ + *(targ) = pci_read_config8(dev, reg) +#define pci_read_config_word(dev, reg, targ)\ + *(targ) = pci_read_config16(dev, reg) +#define pci_read_config_dword(dev, reg, targ)\ + *(targ) = pci_read_config32(dev, reg) +#define pci_write_config_byte(dev, reg, val)\ + pci_write_config8(dev, reg, val) +#define pci_write_config_word(dev, reg, val)\ + pci_write_config16(dev, reg, val) +#define pci_write_config_dword(dev, reg, val)\ + pci_write_config32(dev, reg, val) +#endif /* !__SMM__ */ + +typedef struct spi_slave ich_spi_slave; + +static int ichspi_lock = 0; + +typedef struct ich9_spi_regs { + uint32_t bfpr; + uint16_t hsfs; + uint16_t hsfc; + uint32_t faddr; + uint32_t _reserved0; + uint32_t fdata[16]; + uint32_t frap; + uint32_t freg[5]; + uint32_t _reserved1[3]; + uint32_t pr[5]; + uint32_t _reserved2[2]; + uint8_t ssfs; + uint8_t ssfc[3]; + uint16_t preop; + uint16_t optype; + uint8_t opmenu[8]; + uint32_t bbar; + uint8_t _reserved3[12]; + uint32_t fdoc; + uint32_t fdod; + uint8_t _reserved4[8]; + uint32_t afc; + uint32_t lvscc; + uint32_t uvscc; + uint8_t _reserved5[4]; + uint32_t fpb; + uint8_t _reserved6[28]; + uint32_t srdl; + uint32_t srdc; + uint32_t srd; +} __attribute__((packed)) ich9_spi_regs; + +typedef struct ich_spi_controller { + int locked; + + uint8_t *opmenu; + int menubytes; + uint16_t *preop; + uint16_t *optype; + uint32_t *addr; + uint8_t *data; + unsigned databytes; + uint8_t *status; + uint16_t *control; + uint32_t *bbar; +} ich_spi_controller; + +static ich_spi_controller cntlr; + +enum { + SPIS_SCIP = 0x0001, + SPIS_GRANT = 0x0002, + SPIS_CDS = 0x0004, + SPIS_FCERR = 0x0008, + SSFS_AEL = 0x0010, + SPIS_LOCK = 0x8000, + SPIS_RESERVED_MASK = 0x7ff0, + SSFS_RESERVED_MASK = 0x7fe2 +}; + +enum { + SPIC_SCGO = 0x000002, + SPIC_ACS = 0x000004, + SPIC_SPOP = 0x000008, + SPIC_DBC = 0x003f00, + SPIC_DS = 0x004000, + SPIC_SME = 0x008000, + SSFC_SCF_MASK = 0x070000, + SSFC_RESERVED = 0xf80000 +}; + +enum { + HSFS_FDONE = 0x0001, + HSFS_FCERR = 0x0002, + HSFS_AEL = 0x0004, + HSFS_BERASE_MASK = 0x0018, + HSFS_BERASE_SHIFT = 3, + HSFS_SCIP = 0x0020, + HSFS_FDOPSS = 0x2000, + HSFS_FDV = 0x4000, + HSFS_FLOCKDN = 0x8000 +}; + +enum { + HSFC_FGO = 0x0001, + HSFC_FCYCLE_MASK = 0x0006, + HSFC_FCYCLE_SHIFT = 1, + HSFC_FDBC_MASK = 0x3f00, + HSFC_FDBC_SHIFT = 8, + HSFC_FSMIE = 0x8000 +}; + +enum { + SPI_OPCODE_TYPE_READ_NO_ADDRESS = 0, + SPI_OPCODE_TYPE_WRITE_NO_ADDRESS = 1, + SPI_OPCODE_TYPE_READ_WITH_ADDRESS = 2, + SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS = 3 +}; + +#if CONFIG_DEBUG_SPI_FLASH + +static u8 readb_(const void *addr) +{ + u8 v = read8((unsigned long)addr); + printk(BIOS_DEBUG, "read %2.2x from %4.4x\n", + v, ((unsigned) addr & 0xffff) - 0xf020); + return v; +} + +static u16 readw_(const void *addr) +{ + u16 v = read16((unsigned long)addr); + printk(BIOS_DEBUG, "read %4.4x from %4.4x\n", + v, ((unsigned) addr & 0xffff) - 0xf020); + return v; +} + +static u32 readl_(const void *addr) +{ + u32 v = read32((unsigned long)addr); + printk(BIOS_DEBUG, "read %8.8x from %4.4x\n", + v, ((unsigned) addr & 0xffff) - 0xf020); + return v; +} + +static void writeb_(u8 b, const void *addr) +{ + write8((unsigned long)addr, b); + printk(BIOS_DEBUG, "wrote %2.2x to %4.4x\n", + b, ((unsigned) addr & 0xffff) - 0xf020); +} + +static void writew_(u16 b, const void *addr) +{ + write16((unsigned long)addr, b); + printk(BIOS_DEBUG, "wrote %4.4x to %4.4x\n", + b, ((unsigned) addr & 0xffff) - 0xf020); +} + +static void writel_(u32 b, const void *addr) +{ + write32((unsigned long)addr, b); + printk(BIOS_DEBUG, "wrote %8.8x to %4.4x\n", + b, ((unsigned) addr & 0xffff) - 0xf020); +} + +#else /* CONFIG_DEBUG_SPI_FLASH ^^^ enabled vvv NOT enabled */ + +#define readb_(a) read8((uint32_t)a) +#define readw_(a) read16((uint32_t)a) +#define readl_(a) read32((uint32_t)a) +#define writeb_(val, addr) write8((uint32_t)addr, val) +#define writew_(val, addr) write16((uint32_t)addr, val) +#define writel_(val, addr) write32((uint32_t)addr, val) + +#endif /* CONFIG_DEBUG_SPI_FLASH ^^^ NOT enabled */ + +static void write_reg(const void *value, void *dest, uint32_t size) +{ + const uint8_t *bvalue = value; + uint8_t *bdest = dest; + + while (size >= 4) { + writel_(*(const uint32_t *)bvalue, bdest); + bdest += 4; bvalue += 4; size -= 4; + } + while (size) { + writeb_(*bvalue, bdest); + bdest++; bvalue++; size--; + } +} + +static void read_reg(const void *src, void *value, uint32_t size) +{ + const uint8_t *bsrc = src; + uint8_t *bvalue = value; + + while (size >= 4) { + *(uint32_t *)bvalue = readl_(bsrc); + bsrc += 4; bvalue += 4; size -= 4; + } + while (size) { + *bvalue = readb_(bsrc); + bsrc++; bvalue++; size--; + } +} + +static void ich_set_bbar(uint32_t minaddr) +{ + const uint32_t bbar_mask = 0x00ffff00; + uint32_t ichspi_bbar; + + minaddr &= bbar_mask; + ichspi_bbar = readl_(cntlr.bbar) & ~bbar_mask; + ichspi_bbar |= minaddr; + writel_(ichspi_bbar, cntlr.bbar); +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs) +{ + ich_spi_slave *slave = malloc(sizeof(*slave)); + + if (!slave) { + printk(BIOS_DEBUG, "ICH SPI: Bad allocation\n"); + return NULL; + } + + memset(slave, 0, sizeof(*slave)); + + slave->bus = bus; + slave->cs = cs; + return slave; +} + +void spi_init(void) +{ + uint8_t *rcrb; /* Root Complex Register Block */ + uint32_t rcba; /* Root Complex Base Address */ + uint8_t bios_cntl; + device_t dev; + ich9_spi_regs *ich9_spi; + +#ifdef __SMM__ + dev = PCI_DEV(0, 31, 0); +#else + dev = dev_find_slot(0, PCI_DEVFN(31, 0)); +#endif + + pci_read_config_dword(dev, 0xf0, &rcba); + /* Bits 31-14 are the base address, 13-1 are reserved, 0 is enable. */ + rcrb = (uint8_t *)(rcba & 0xffffc000); + ich9_spi = (ich9_spi_regs *)(rcrb + 0x3800); + ichspi_lock = readw_(&ich9_spi->hsfs) & HSFS_FLOCKDN; + cntlr.opmenu = ich9_spi->opmenu; + cntlr.menubytes = sizeof(ich9_spi->opmenu); + cntlr.optype = &ich9_spi->optype; + cntlr.addr = &ich9_spi->faddr; + cntlr.data = (uint8_t *)ich9_spi->fdata; + cntlr.databytes = sizeof(ich9_spi->fdata); + cntlr.status = &ich9_spi->ssfs; + cntlr.control = (uint16_t *)ich9_spi->ssfc; + cntlr.bbar = &ich9_spi->bbar; + cntlr.preop = &ich9_spi->preop; + ich_set_bbar(0); + + /* Disable the BIOS write protect so write commands are allowed. */ + pci_read_config_byte(dev, 0xdc, &bios_cntl); + bios_cntl &= ~(1 << 5); + pci_write_config_byte(dev, 0xdc, bios_cntl | 0x1); +} + +static void spi_init_cb(void *unused) +{ + spi_init(); +} + +BOOT_STATE_INIT_ENTRIES(spi_init_bscb) = { + BOOT_STATE_INIT_ENTRY(BS_DEV_INIT, BS_ON_ENTRY, spi_init_cb, NULL), +}; + +int spi_claim_bus(struct spi_slave *slave) +{ + /* Handled by ICH automatically. */ + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + /* Handled by ICH automatically. */ +} + +typedef struct spi_transaction { + const uint8_t *out; + uint32_t bytesout; + uint8_t *in; + uint32_t bytesin; + uint8_t type; + uint8_t opcode; + uint32_t offset; +} spi_transaction; + +static inline void spi_use_out(spi_transaction *trans, unsigned bytes) +{ + trans->out += bytes; + trans->bytesout -= bytes; +} + +static inline void spi_use_in(spi_transaction *trans, unsigned bytes) +{ + trans->in += bytes; + trans->bytesin -= bytes; +} + +static void spi_setup_type(spi_transaction *trans) +{ + trans->type = 0xFF; + + /* Try to guess spi type from read/write sizes. */ + if (trans->bytesin == 0) { + if (trans->bytesout > 4) + /* + * If bytesin = 0 and bytesout > 4, we presume this is + * a write data operation, which is accompanied by an + * address. + */ + trans->type = SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS; + else + trans->type = SPI_OPCODE_TYPE_WRITE_NO_ADDRESS; + return; + } + + if (trans->bytesout == 1) { /* and bytesin is > 0 */ + trans->type = SPI_OPCODE_TYPE_READ_NO_ADDRESS; + return; + } + + if (trans->bytesout == 4) { /* and bytesin is > 0 */ + trans->type = SPI_OPCODE_TYPE_READ_WITH_ADDRESS; + } + + /* Fast read command is called with 5 bytes instead of 4 */ + if (trans->out[0] == SPI_OPCODE_FAST_READ && trans->bytesout == 5) { + trans->type = SPI_OPCODE_TYPE_READ_WITH_ADDRESS; + --trans->bytesout; + } +} + +static int spi_setup_opcode(spi_transaction *trans) +{ + uint16_t optypes; + uint8_t opmenu[cntlr.menubytes]; + + trans->opcode = trans->out[0]; + spi_use_out(trans, 1); + if (!ichspi_lock) { + /* The lock is off, so just use index 0. */ + writeb_(trans->opcode, cntlr.opmenu); + optypes = readw_(cntlr.optype); + optypes = (optypes & 0xfffc) | (trans->type & 0x3); + writew_(optypes, cntlr.optype); + return 0; + } else { + /* The lock is on. See if what we need is on the menu. */ + uint8_t optype; + uint16_t opcode_index; + + /* Write Enable is handled as atomic prefix */ + if (trans->opcode == SPI_OPCODE_WREN) + return 0; + + read_reg(cntlr.opmenu, opmenu, sizeof(opmenu)); + for (opcode_index = 0; opcode_index < cntlr.menubytes; + opcode_index++) { + if (opmenu[opcode_index] == trans->opcode) + break; + } + + if (opcode_index == cntlr.menubytes) { + printk(BIOS_DEBUG, "ICH SPI: Opcode %x not found\n", + trans->opcode); + return -1; + } + + optypes = readw_(cntlr.optype); + optype = (optypes >> (opcode_index * 2)) & 0x3; + if (trans->type == SPI_OPCODE_TYPE_WRITE_NO_ADDRESS && + optype == SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS && + trans->bytesout >= 3) { + /* We guessed wrong earlier. Fix it up. */ + trans->type = optype; + } + if (optype != trans->type) { + printk(BIOS_DEBUG, "ICH SPI: Transaction doesn't fit type %d\n", + optype); + return -1; + } + return opcode_index; + } +} + +static int spi_setup_offset(spi_transaction *trans) +{ + /* Separate the SPI address and data. */ + switch (trans->type) { + case SPI_OPCODE_TYPE_READ_NO_ADDRESS: + case SPI_OPCODE_TYPE_WRITE_NO_ADDRESS: + return 0; + case SPI_OPCODE_TYPE_READ_WITH_ADDRESS: + case SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS: + trans->offset = ((uint32_t)trans->out[0] << 16) | + ((uint32_t)trans->out[1] << 8) | + ((uint32_t)trans->out[2] << 0); + spi_use_out(trans, 3); + return 1; + default: + printk(BIOS_DEBUG, "Unrecognized SPI transaction type %#x\n", trans->type); + return -1; + } +} + +/* + * Wait for up to 60ms til status register bit(s) turn 1 (in case wait_til_set + * below is True) or 0. In case the wait was for the bit(s) to set - write + * those bits back, which would cause resetting them. + * + * Return the last read status value on success or -1 on failure. + */ +static int ich_status_poll(u16 bitmask, int wait_til_set) +{ + int timeout = 6000; /* This will result in 60 ms */ + u16 status = 0; + + while (timeout--) { + status = readw_(cntlr.status); + if (wait_til_set ^ ((status & bitmask) == 0)) { + if (wait_til_set) + writew_((status & bitmask), cntlr.status); + return status; + } + udelay(10); + } + + printk(BIOS_DEBUG, "ICH SPI: SCIP timeout, read %x, expected %x\n", + status, bitmask); + return -1; +} + +int spi_xfer(struct spi_slave *slave, const void *dout, + unsigned int bytesout, void *din, unsigned int bytesin) +{ + uint16_t control; + int16_t opcode_index; + int with_address; + int status; + + spi_transaction trans = { + dout, bytesout, + din, bytesin, + 0xff, 0xff, 0 + }; + + /* There has to always at least be an opcode. */ + if (!bytesout || !dout) { + printk(BIOS_DEBUG, "ICH SPI: No opcode for transfer\n"); + return -1; + } + /* Make sure if we read something we have a place to put it. */ + if (bytesin != 0 && !din) { + printk(BIOS_DEBUG, "ICH SPI: Read but no target buffer\n"); + return -1; + } + + if (ich_status_poll(SPIS_SCIP, 0) == -1) + return -1; + + writew_(SPIS_CDS | SPIS_FCERR, cntlr.status); + + spi_setup_type(&trans); + if ((opcode_index = spi_setup_opcode(&trans)) < 0) + return -1; + if ((with_address = spi_setup_offset(&trans)) < 0) + return -1; + + if (trans.opcode == SPI_OPCODE_WREN) { + /* + * Treat Write Enable as Atomic Pre-Op if possible + * in order to prevent the Management Engine from + * issuing a transaction between WREN and DATA. + */ + if (!ichspi_lock) + writew_(trans.opcode, cntlr.preop); + return 0; + } + + /* Preset control fields */ + control = SPIC_SCGO | ((opcode_index & 0x07) << 4); + + /* Issue atomic preop cycle if needed */ + if (readw_(cntlr.preop)) + control |= SPIC_ACS; + + if (!trans.bytesout && !trans.bytesin) { + /* SPI addresses are 24 bit only */ + if (with_address) + writel_(trans.offset & 0x00FFFFFF, cntlr.addr); + + /* + * This is a 'no data' command (like Write Enable), its + * bitesout size was 1, decremented to zero while executing + * spi_setup_opcode() above. Tell the chip to send the + * command. + */ + writew_(control, cntlr.control); + + /* wait for the result */ + status = ich_status_poll(SPIS_CDS | SPIS_FCERR, 1); + if (status == -1) + return -1; + + if (status & SPIS_FCERR) { + printk(BIOS_DEBUG, "ICH SPI: Command transaction error\n"); + return -1; + } + + return 0; + } + + /* + * Check if this is a write command atempting to transfer more bytes + * than the controller can handle. Iterations for writes are not + * supported here because each SPI write command needs to be preceded + * and followed by other SPI commands, and this sequence is controlled + * by the SPI chip driver. + */ + if (trans.bytesout > cntlr.databytes) { + printk(BIOS_DEBUG, "ICH SPI: Too much to write. Does your SPI chip driver use" + " CONTROLLER_PAGE_LIMIT?\n"); + return -1; + } + + /* + * Read or write up to databytes bytes at a time until everything has + * been sent. + */ + while (trans.bytesout || trans.bytesin) { + uint32_t data_length; + + /* SPI addresses are 24 bit only */ + writel_(trans.offset & 0x00FFFFFF, cntlr.addr); + + if (trans.bytesout) + data_length = min(trans.bytesout, cntlr.databytes); + else + data_length = min(trans.bytesin, cntlr.databytes); + + /* Program data into FDATA0 to N */ + if (trans.bytesout) { + write_reg(trans.out, cntlr.data, data_length); + spi_use_out(&trans, data_length); + if (with_address) + trans.offset += data_length; + } + + /* Add proper control fields' values */ + control &= ~((cntlr.databytes - 1) << 8); + control |= SPIC_DS; + control |= (data_length - 1) << 8; + + /* write it */ + writew_(control, cntlr.control); + + /* Wait for Cycle Done Status or Flash Cycle Error. */ + status = ich_status_poll(SPIS_CDS | SPIS_FCERR, 1); + if (status == -1) + return -1; + + if (status & SPIS_FCERR) { + printk(BIOS_DEBUG, "ICH SPI: Data transaction error\n"); + return -1; + } + + if (trans.bytesin) { + read_reg(cntlr.data, trans.in, data_length); + spi_use_in(&trans, data_length); + if (with_address) + trans.offset += data_length; + } + } + + /* Clear atomic preop now that xfer is done */ + writew_(0, cntlr.preop); + + return 0; +} diff --git a/src/soc/intel/broadwell/spi_loading.c b/src/soc/intel/broadwell/spi_loading.c new file mode 100644 index 0000000000..aeab29e450 --- /dev/null +++ b/src/soc/intel/broadwell/spi_loading.c @@ -0,0 +1,94 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2013 ChromeOS Authors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#define CACHELINE_SIZE 64 +#define INTRA_CACHELINE_MASK (CACHELINE_SIZE - 1) +#define CACHELINE_MASK (~INTRA_CACHELINE_MASK) + +/* Mirror the payload file to the default SMM location if it is small enough. + * The default SMM region can be used since no one is using the memory at this + * location at this stage in the boot. */ +static inline void *spi_mirror(void *file_start, int file_len) +{ + int alignment_diff; + char *src; + char *dest = (void *)SMM_DEFAULT_BASE; + + alignment_diff = (INTRA_CACHELINE_MASK & (long)file_start); + + /* Adjust file length so that the start and end points are aligned to a + * cacheline. Coupled with the ROM caching in the CPU the SPI hardware + * will read and cache full length cachelines. It will also prefetch + * data as well. Once things are mirrored in memory all accesses should + * hit the CPUs cache. */ + file_len += alignment_diff; + file_len = ALIGN(file_len, CACHELINE_SIZE); + + printk(BIOS_DEBUG, "Payload aligned size: 0x%x\n", file_len); + + /* Just pass back the pointer to ROM space if the file is larger + * than the RAM mirror region. */ + if (file_len > SMM_DEFAULT_SIZE) + return file_start; + + src = (void *)(CACHELINE_MASK & (long)file_start); + /* Note that if mempcy is not using 32-bit moves the performance will + * degrade because the SPI hardware prefetchers look for + * cacheline-aligned 32-bit accesses to kick in. */ + memcpy(dest, src, file_len); + + /* Provide pointer into mirrored space. */ + return &dest[alignment_diff]; +} + +void *cbfs_load_payload(struct cbfs_media *media, const char *name) +{ + int file_len; + void *file_start; + struct cbfs_file *file; + + file_start = vboot_get_payload(&file_len); + + if (file_start != NULL) + return spi_mirror(file_start, file_len); + + file = cbfs_get_file(media, name); + + if (file == NULL) + return NULL; + + if (ntohl(file->type) != CBFS_TYPE_PAYLOAD) + return NULL; + + file_len = ntohl(file->len); + + file_start = CBFS_SUBHEADER(file); + + return spi_mirror(file_start, file_len); +} diff --git a/src/soc/intel/broadwell/systemagent.c b/src/soc/intel/broadwell/systemagent.c new file mode 100644 index 0000000000..ec8fdd5702 --- /dev/null +++ b/src/soc/intel/broadwell/systemagent.c @@ -0,0 +1,542 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2007-2009 coresystems GmbH + * Copyright (C) 2011 The ChromiumOS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "chip.h" +#include "haswell.h" + +static int get_pcie_bar(device_t dev, unsigned int index, u32 *base, u32 *len) +{ + u32 pciexbar_reg; + + *base = 0; + *len = 0; + + pciexbar_reg = pci_read_config32(dev, index); + + if (!(pciexbar_reg & (1 << 0))) + return 0; + + switch ((pciexbar_reg >> 1) & 3) { + case 0: // 256MB + *base = pciexbar_reg & ((1 << 31)|(1 << 30)|(1 << 29)|(1 << 28)); + *len = 256 * 1024 * 1024; + return 1; + case 1: // 128M + *base = pciexbar_reg & ((1 << 31)|(1 << 30)|(1 << 29)|(1 << 28)|(1 << 27)); + *len = 128 * 1024 * 1024; + return 1; + case 2: // 64M + *base = pciexbar_reg & ((1 << 31)|(1 << 30)|(1 << 29)|(1 << 28)|(1 << 27)|(1 << 26)); + *len = 64 * 1024 * 1024; + return 1; + } + + return 0; +} + +static void pci_domain_set_resources(device_t dev) +{ + assign_resources(dev->link_list); +} + + /* TODO We could determine how many PCIe busses we need in + * the bar. For now that number is hardcoded to a max of 64. + * See e7525/northbridge.c for an example. + */ +static struct device_operations pci_domain_ops = { + .read_resources = pci_domain_read_resources, + .set_resources = pci_domain_set_resources, + .enable_resources = NULL, + .init = NULL, + .scan_bus = pci_domain_scan_bus, +#if CONFIG_MMCONF_SUPPORT_DEFAULT + .ops_pci_bus = &pci_ops_mmconf, +#else + .ops_pci_bus = &pci_cf8_conf1, +#endif +}; + +static int get_bar(device_t dev, unsigned int index, u32 *base, u32 *len) +{ + u32 bar; + + bar = pci_read_config32(dev, index); + + /* If not enabled don't report it. */ + if (!(bar & 0x1)) + return 0; + + /* Knock down the enable bit. */ + *base = bar & ~1; + + return 1; +} + +/* There are special BARs that actually are programmed in the MCHBAR. These + * Intel special features, but they do consume resources that need to be + * accounted for. */ +static int get_bar_in_mchbar(device_t dev, unsigned int index, u32 *base, + u32 *len) +{ + u32 bar; + + bar = MCHBAR32(index); + + /* If not enabled don't report it. */ + if (!(bar & 0x1)) + return 0; + + /* Knock down the enable bit. */ + *base = bar & ~1; + + return 1; +} + +struct fixed_mmio_descriptor { + unsigned int index; + u32 size; + int (*get_resource)(device_t dev, unsigned int index, + u32 *base, u32 *size); + const char *description; +}; + +#define SIZE_KB(x) ((x)*1024) +struct fixed_mmio_descriptor mc_fixed_resources[] = { + { PCIEXBAR, SIZE_KB(0), get_pcie_bar, "PCIEXBAR" }, + { MCHBAR, SIZE_KB(32), get_bar, "MCHBAR" }, + { DMIBAR, SIZE_KB(4), get_bar, "DMIBAR" }, + { EPBAR, SIZE_KB(4), get_bar, "EPBAR" }, + { 0x5420, SIZE_KB(4), get_bar_in_mchbar, "GDXCBAR" }, + { 0x5408, SIZE_KB(16), get_bar_in_mchbar, "EDRAMBAR" }, +}; +#undef SIZE_KB + +/* + * Add all known fixed MMIO ranges that hang off the host bridge/memory + * controller device. + */ +static void mc_add_fixed_mmio_resources(device_t dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mc_fixed_resources); i++) { + u32 base; + u32 size; + struct resource *resource; + unsigned int index; + + size = mc_fixed_resources[i].size; + index = mc_fixed_resources[i].index; + if (!mc_fixed_resources[i].get_resource(dev, index, + &base, &size)) + continue; + + resource = new_resource(dev, mc_fixed_resources[i].index); + resource->flags = IORESOURCE_MEM | IORESOURCE_FIXED | + IORESOURCE_STORED | IORESOURCE_RESERVE | + IORESOURCE_ASSIGNED; + resource->base = base; + resource->size = size; + printk(BIOS_DEBUG, "%s: Adding %s @ %x 0x%08lx-0x%08lx.\n", + __func__, mc_fixed_resources[i].description, index, + (unsigned long)base, (unsigned long)(base + size - 1)); + } +} + +/* Host Memory Map: + * + * +--------------------------+ TOUUD + * | | + * +--------------------------+ 4GiB + * | PCI Address Space | + * +--------------------------+ TOLUD (also maps into MC address space) + * | iGD | + * +--------------------------+ BDSM + * | GTT | + * +--------------------------+ BGSM + * | TSEG | + * +--------------------------+ TSEGMB + * | Usage DRAM | + * +--------------------------+ 0 + * + * Some of the base registers above can be equal making the size of those + * regions 0. The reason is because the memory controller internally subtracts + * the base registers from each other to determine sizes of the regions. In + * other words, the memory map is in a fixed order no matter what. + */ + +struct map_entry { + int reg; + int is_64_bit; + int is_limit; + const char *description; +}; + +static void read_map_entry(device_t dev, struct map_entry *entry, + uint64_t *result) +{ + uint64_t value; + uint64_t mask; + + /* All registers are on a 1MiB granularity. */ + mask = ((1ULL<<20)-1); + mask = ~mask; + + value = 0; + + if (entry->is_64_bit) { + value = pci_read_config32(dev, entry->reg + 4); + value <<= 32; + } + + value |= pci_read_config32(dev, entry->reg); + value &= mask; + + if (entry->is_limit) + value |= ~mask; + + *result = value; +} + +#define MAP_ENTRY(reg_, is_64_, is_limit_, desc_) \ + { \ + .reg = reg_, \ + .is_64_bit = is_64_, \ + .is_limit = is_limit_, \ + .description = desc_, \ + } + +#define MAP_ENTRY_BASE_64(reg_, desc_) \ + MAP_ENTRY(reg_, 1, 0, desc_) +#define MAP_ENTRY_LIMIT_64(reg_, desc_) \ + MAP_ENTRY(reg_, 1, 1, desc_) +#define MAP_ENTRY_BASE_32(reg_, desc_) \ + MAP_ENTRY(reg_, 0, 0, desc_) + +enum { + TOM_REG, + TOUUD_REG, + MESEG_BASE_REG, + MESEG_LIMIT_REG, + REMAP_BASE_REG, + REMAP_LIMIT_REG, + TOLUD_REG, + BGSM_REG, + BDSM_REG, + TSEG_REG, + // Must be last. + NUM_MAP_ENTRIES +}; + +static struct map_entry memory_map[NUM_MAP_ENTRIES] = { + [TOM_REG] = MAP_ENTRY_BASE_64(TOM, "TOM"), + [TOUUD_REG] = MAP_ENTRY_BASE_64(TOUUD, "TOUUD"), + [MESEG_BASE_REG] = MAP_ENTRY_BASE_64(MESEG_BASE, "MESEG_BASE"), + [MESEG_LIMIT_REG] = MAP_ENTRY_LIMIT_64(MESEG_LIMIT, "MESEG_LIMIT"), + [REMAP_BASE_REG] = MAP_ENTRY_BASE_64(REMAPBASE, "REMAP_BASE"), + [REMAP_LIMIT_REG] = MAP_ENTRY_LIMIT_64(REMAPLIMIT, "REMAP_LIMIT"), + [TOLUD_REG] = MAP_ENTRY_BASE_32(TOLUD, "TOLUD"), + [BDSM_REG] = MAP_ENTRY_BASE_32(BDSM, "BDSM"), + [BGSM_REG] = MAP_ENTRY_BASE_32(BGSM, "BGSM"), + [TSEG_REG] = MAP_ENTRY_BASE_32(TSEG, "TESGMB"), +}; + +static void mc_read_map_entries(device_t dev, uint64_t *values) +{ + int i; + for (i = 0; i < NUM_MAP_ENTRIES; i++) { + read_map_entry(dev, &memory_map[i], &values[i]); + } +} + +static void mc_report_map_entries(device_t dev, uint64_t *values) +{ + int i; + for (i = 0; i < NUM_MAP_ENTRIES; i++) { + printk(BIOS_DEBUG, "MC MAP: %s: 0x%llx\n", + memory_map[i].description, values[i]); + } + /* One can validate the BDSM and BGSM against the GGC. */ + printk(BIOS_DEBUG, "MC MAP: GGC: 0x%x\n", pci_read_config16(dev, GGC)); +} + +static void mc_add_dram_resources(device_t dev) +{ + unsigned long base_k, size_k; + unsigned long touud_k; + unsigned long index; + struct resource *resource; + uint64_t mc_values[NUM_MAP_ENTRIES]; + + /* Read in the MAP registers and report their values. */ + mc_read_map_entries(dev, &mc_values[0]); + mc_report_map_entries(dev, &mc_values[0]); + + /* + * These are the host memory ranges that should be added: + * - 0 -> SMM_DEFAULT_BASE : cacheable + * - SMM_DEFAULT_BASE -> SMM_DEFAULT_BASE + SMM_DEFAULT_SIZE : + * cacheable and reserved + * - SMM_DEFAULT_BASE + SMM_DEFAULT_SIZE -> 0xa0000 : cacheable + * - 0xc0000 -> TSEG : cacheable + * - TESG -> BGSM: cacheable with standard MTRRs and reserved + * - BGSM -> TOLUD: not cacheable with standard MTRRs and reserved + * - 4GiB -> TOUUD: cacheable + * + * The default SMRAM space is reserved so that the range doesn't + * have to be saved during S3 Resume. Once marked reserved the OS + * cannot use the memory. This is a bit of an odd place to reserve + * the region, but the CPU devices don't have dev_ops->read_resources() + * called on them. + * + * The range 0xa0000 -> 0xc0000 does not have any resources + * associated with it to handle legacy VGA memory. If this range + * is not omitted the mtrr code will setup the area as cacheable + * causing VGA access to not work. + * + * The TSEG region is mapped as cacheable so that one can perform + * SMRAM relocation faster. Once the SMRR is enabled the SMRR takes + * precedence over the existing MTRRs covering this region. + * + * It should be noted that cacheable entry types need to be added in + * order. The reason is that the current MTRR code assumes this and + * falls over itself if it isn't. + * + * The resource index starts low and should not meet or exceed + * PCI_BASE_ADDRESS_0. + */ + index = 0; + + /* 0 - > SMM_DEFAULT_BASE */ + base_k = 0; + size_k = SMM_DEFAULT_BASE >> 10; + ram_resource(dev, index++, base_k, size_k); + + /* SMM_DEFAULT_BASE -> SMM_DEFAULT_BASE + SMM_DEFAULT_SIZE */ + resource = new_resource(dev, index++); + resource->base = SMM_DEFAULT_BASE; + resource->size = SMM_DEFAULT_SIZE; + resource->flags = IORESOURCE_MEM | IORESOURCE_FIXED | + IORESOURCE_CACHEABLE | IORESOURCE_STORED | + IORESOURCE_RESERVE | IORESOURCE_ASSIGNED; + + /* SMM_DEFAULT_BASE + SMM_DEFAULT_SIZE -> 0xa0000 */ + base_k = (SMM_DEFAULT_BASE + SMM_DEFAULT_SIZE) >> 10; + size_k = (0xa0000 >> 10) - base_k; + ram_resource(dev, index++, base_k, size_k); + + /* 0xc0000 -> TSEG */ + base_k = 0xc0000 >> 10; + size_k = (unsigned long)(mc_values[TSEG_REG] >> 10) - base_k; + ram_resource(dev, index++, base_k, size_k); + + /* TSEG -> BGSM */ + resource = new_resource(dev, index++); + resource->base = mc_values[TSEG_REG]; + resource->size = mc_values[BGSM_REG] - resource->base; + resource->flags = IORESOURCE_MEM | IORESOURCE_FIXED | + IORESOURCE_STORED | IORESOURCE_RESERVE | + IORESOURCE_ASSIGNED | IORESOURCE_CACHEABLE; + + /* BGSM -> TOLUD */ + resource = new_resource(dev, index++); + resource->base = mc_values[BGSM_REG]; + resource->size = mc_values[TOLUD_REG] - resource->base; + resource->flags = IORESOURCE_MEM | IORESOURCE_FIXED | + IORESOURCE_STORED | IORESOURCE_RESERVE | + IORESOURCE_ASSIGNED; + + /* 4GiB -> TOUUD */ + base_k = 4096 * 1024; /* 4GiB */ + touud_k = mc_values[TOUUD_REG] >> 10; + size_k = touud_k - base_k; + if (touud_k > base_k) + ram_resource(dev, index++, base_k, size_k); + + /* Reserve everything between A segment and 1MB: + * + * 0xa0000 - 0xbffff: legacy VGA + * 0xc0000 - 0xfffff: RAM + */ + mmio_resource(dev, index++, (0xa0000 >> 10), (0xc0000 - 0xa0000) >> 10); + reserved_ram_resource(dev, index++, (0xc0000 >> 10), + (0x100000 - 0xc0000) >> 10); +#if CONFIG_CHROMEOS_RAMOOPS + reserved_ram_resource(dev, index++, + CONFIG_CHROMEOS_RAMOOPS_RAM_START >> 10, + CONFIG_CHROMEOS_RAMOOPS_RAM_SIZE >> 10); +#endif +} + +static void mc_read_resources(device_t dev) +{ + /* Read standard PCI resources. */ + pci_dev_read_resources(dev); + + /* Add all fixed MMIO resources. */ + mc_add_fixed_mmio_resources(dev); + + /* Calculate and add DRAM resources. */ + mc_add_dram_resources(dev); +} + +static void intel_set_subsystem(device_t dev, unsigned vendor, unsigned device) +{ + if (!vendor || !device) { + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + pci_read_config32(dev, PCI_VENDOR_ID)); + } else { + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + ((device & 0xffff) << 16) | (vendor & 0xffff)); + } +} + +static void northbridge_init(struct device *dev) +{ + u8 bios_reset_cpl, pair; + + /* Enable Power Aware Interrupt Routing */ + pair = MCHBAR8(0x5418); + pair &= ~0x7; /* Clear 2:0 */ + pair |= 0x4; /* Fixed Priority */ + MCHBAR8(0x5418) = pair; + + /* + * Set bits 0+1 of BIOS_RESET_CPL to indicate to the CPU + * that BIOS has initialized memory and power management + */ + bios_reset_cpl = MCHBAR8(BIOS_RESET_CPL); + bios_reset_cpl |= 3; + MCHBAR8(BIOS_RESET_CPL) = bios_reset_cpl; + printk(BIOS_DEBUG, "Set BIOS_RESET_CPL\n"); + + /* Configure turbo power limits 1ms after reset complete bit */ + mdelay(1); + set_power_limits(28); + + /* Set here before graphics PM init */ + MCHBAR32(0x5500) = 0x00100001; +} + +void *cbmem_top(void) +{ + u32 reg; + + /* The top the reserve regions fall just below the TSEG region. */ + reg = pci_read_config32(dev_find_slot(0, PCI_DEVFN(0, 0)), TSEG); + + return (void *)(reg & ~((1 << 20) - 1)); +} + +static void northbridge_enable(device_t dev) +{ +#if CONFIG_HAVE_ACPI_RESUME + struct romstage_handoff *handoff; + + handoff = cbmem_find(CBMEM_ID_ROMSTAGE_INFO); + + if (handoff == NULL) { + printk(BIOS_DEBUG, "Unknown boot method, assuming normal.\n"); + acpi_slp_type = 0; + } else if (handoff->s3_resume) { + printk(BIOS_DEBUG, "S3 Resume.\n"); + acpi_slp_type = 3; + } else { + printk(BIOS_DEBUG, "Normal boot.\n"); + acpi_slp_type = 0; + } +#endif +} + +static struct pci_operations intel_pci_ops = { + .set_subsystem = intel_set_subsystem, +}; + +static struct device_operations mc_ops = { + .read_resources = mc_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .init = northbridge_init, + .enable = northbridge_enable, + .scan_bus = 0, + .ops_pci = &intel_pci_ops, +}; + +static const struct pci_driver mc_driver_hsw_mobile __pci_driver = { + .ops = &mc_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_HSW_MOBILE, +}; + +static const struct pci_driver mc_driver_hsw_ult __pci_driver = { + .ops = &mc_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_HSW_ULT, +}; + +static void cpu_bus_init(device_t dev) +{ + bsp_init_and_start_aps(dev->link_list); +} + +static void cpu_bus_noop(device_t dev) +{ +} + +static struct device_operations cpu_bus_ops = { + .read_resources = cpu_bus_noop, + .set_resources = cpu_bus_noop, + .enable_resources = cpu_bus_noop, + .init = cpu_bus_init, + .scan_bus = 0, +}; + +static void enable_dev(device_t dev) +{ + /* Set the operations if it is a special bus type */ + if (dev->path.type == DEVICE_PATH_DOMAIN) { + dev->ops = &pci_domain_ops; + } else if (dev->path.type == DEVICE_PATH_CPU_CLUSTER) { + dev->ops = &cpu_bus_ops; + } +} + +struct chip_operations northbridge_intel_haswell_ops = { + CHIP_NAME("Intel i7 (Haswell) integrated Northbridge") + .enable_dev = enable_dev, +}; diff --git a/src/soc/intel/broadwell/tsc_freq.c b/src/soc/intel/broadwell/tsc_freq.c new file mode 100644 index 0000000000..0a7805319d --- /dev/null +++ b/src/soc/intel/broadwell/tsc_freq.c @@ -0,0 +1,31 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2013 Google, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include "cpu/intel/haswell/haswell.h" + +unsigned long tsc_freq_mhz(void) +{ + msr_t platform_info; + + platform_info = rdmsr(MSR_PLATFORM_INFO); + return HASWELL_BCLK * ((platform_info.lo >> 8) & 0xff); +} diff --git a/src/soc/intel/broadwell/xhci.c b/src/soc/intel/broadwell/xhci.c new file mode 100644 index 0000000000..41c29f9f10 --- /dev/null +++ b/src/soc/intel/broadwell/xhci.c @@ -0,0 +1,403 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include "pch.h" + +typedef struct southbridge_intel_lynxpoint_config config_t; + +static u32 usb_xhci_mem_base(device_t dev) +{ + u32 mem_base = pci_read_config32(dev, PCI_BASE_ADDRESS_0); + + /* Check if the controller is disabled or not present */ + if (mem_base == 0 || mem_base == 0xffffffff) + return 0; + + return mem_base & ~0xf; +} + +static int usb_xhci_port_count_usb3(device_t dev) +{ + if (pch_is_lp()) { + /* LynxPoint-LP has 4 SS ports */ + return 4; + } else { + /* LynxPoint-H can have 0, 2, 4, or 6 SS ports */ + u32 mem_base = usb_xhci_mem_base(dev); + u32 fus = read32(mem_base + XHCI_USB3FUS); + fus >>= XHCI_USB3FUS_SS_SHIFT; + fus &= XHCI_USB3FUS_SS_MASK; + switch (fus) { + case 3: return 0; + case 2: return 2; + case 1: return 4; + case 0: default: return 6; + } + } + return 0; +} + +static void usb_xhci_reset_status_usb3(u32 mem_base, int port) +{ + u32 portsc = mem_base + XHCI_USB3_PORTSC(port); + u32 status = read32(portsc); + /* Do not set Port Enabled/Disabled field */ + status &= ~XHCI_USB3_PORTSC_PED; + /* Clear all change status bits */ + status |= XHCI_USB3_PORTSC_CHST; + write32(portsc, status); +} + +static void usb_xhci_reset_port_usb3(u32 mem_base, int port) +{ + u32 portsc = mem_base + XHCI_USB3_PORTSC(port); + write32(portsc, read32(portsc) | XHCI_USB3_PORTSC_WPR); +} + +#define XHCI_RESET_DELAY_US 1000 /* 1ms */ +#define XHCI_RESET_TIMEOUT 100 /* 100ms */ + +/* + * 1) Wait until port is done polling + * 2) If port is disconnected + * a) Issue warm port reset + * b) Poll for warm reset complete + * c) Write 1 to port change status bits + */ +static void usb_xhci_reset_usb3(device_t dev, int all) +{ + u32 status, port_disabled; + int timeout, port; + int port_count = usb_xhci_port_count_usb3(dev); + u32 mem_base = usb_xhci_mem_base(dev); + + if (!mem_base || !port_count) + return; + + /* Get mask of disabled ports */ + port_disabled = pci_read_config32(dev, XHCI_USB3PDO); + + /* Wait until all enabled ports are done polling */ + for (timeout = XHCI_RESET_TIMEOUT; timeout; timeout--) { + int complete = 1; + for (port = 0; port < port_count; port++) { + /* Skip disabled ports */ + if (port_disabled & (1 << port)) + continue; + /* Read port link status field */ + status = read32(mem_base + XHCI_USB3_PORTSC(port)); + status &= XHCI_USB3_PORTSC_PLS; + if (status == XHCI_PLSR_POLLING) + complete = 0; + } + /* Exit if all ports not polling */ + if (complete) + break; + udelay(XHCI_RESET_DELAY_US); + } + + /* Reset all requested ports */ + for (port = 0; port < port_count; port++) { + u32 portsc = mem_base + XHCI_USB3_PORTSC(port); + /* Skip disabled ports */ + if (port_disabled & (1 << port)) + continue; + status = read32(portsc) & XHCI_USB3_PORTSC_PLS; + /* Reset all or only disconnected ports */ + if (all || (status == XHCI_PLSR_RXDETECT || + status == XHCI_PLSR_POLLING)) + usb_xhci_reset_port_usb3(mem_base, port); + else + port_disabled |= 1 << port; + } + + /* Wait for warm reset complete on all reset ports */ + for (timeout = XHCI_RESET_TIMEOUT; timeout; timeout--) { + int complete = 1; + for (port = 0; port < port_count; port++) { + /* Only check ports that were reset */ + if (port_disabled & (1 << port)) + continue; + /* Check if warm reset is complete */ + status = read32(mem_base + XHCI_USB3_PORTSC(port)); + if (!(status & XHCI_USB3_PORTSC_WRC)) + complete = 0; + } + /* Check for warm reset complete in any port */ + if (complete) + break; + udelay(XHCI_RESET_DELAY_US); + } + + /* Clear port change status bits */ + for (port = 0; port < port_count; port++) + usb_xhci_reset_status_usb3(mem_base, port); +} + +#ifdef __SMM__ + +/* Handler for XHCI controller on entry to S3/S4/S5 */ +void usb_xhci_sleep_prepare(device_t dev, u8 slp_typ) +{ + u16 reg16; + u32 reg32; + u32 mem_base = usb_xhci_mem_base(dev); + + if (!mem_base || slp_typ < 3) + return; + + if (pch_is_lp()) { + /* Set D0 state */ + reg16 = pci_read_config16(dev, XHCI_PWR_CTL_STS); + reg16 &= ~PWR_CTL_SET_MASK; + reg16 |= PWR_CTL_SET_D0; + pci_write_config16(dev, XHCI_PWR_CTL_STS, reg16); + + /* Clear PCI 0xB0[14:13] */ + reg32 = pci_read_config32(dev, 0xb0); + reg32 &= ~((1 << 14) | (1 << 13)); + pci_write_config32(dev, 0xb0, reg32); + + /* Clear MMIO 0x816c[14,2] */ + reg32 = read32(mem_base + 0x816c); + reg32 &= ~((1 << 14) | (1 << 2)); + write32(mem_base + 0x816c, reg32); + + /* Reset disconnected USB3 ports */ + usb_xhci_reset_usb3(dev, 0); + + /* Set MMIO 0x80e0[15] */ + reg32 = read32(mem_base + 0x80e0); + reg32 |= (1 << 15); + write32(mem_base + 0x80e0, reg32); + } + + /* Set D3Hot state and enable PME */ + pci_or_config16(dev, XHCI_PWR_CTL_STS, PWR_CTL_SET_D3); + pci_or_config16(dev, XHCI_PWR_CTL_STS, PWR_CTL_STATUS_PME); + pci_or_config16(dev, XHCI_PWR_CTL_STS, PWR_CTL_ENABLE_PME); +} + +/* Route all ports to XHCI controller */ +void usb_xhci_route_all(void) +{ + u32 port_mask, route; + u16 reg16; + + /* Skip if EHCI is already disabled */ + if (RCBA32(FD) & PCH_DISABLE_EHCI1) + return; + + /* Set D0 state */ + reg16 = pci_read_config16(PCH_XHCI_DEV, XHCI_PWR_CTL_STS); + reg16 &= ~PWR_CTL_SET_MASK; + reg16 |= PWR_CTL_SET_D0; + pci_write_config16(PCH_XHCI_DEV, XHCI_PWR_CTL_STS, reg16); + + /* Set USB3 superspeed enable */ + port_mask = pci_read_config32(PCH_XHCI_DEV, XHCI_USB3PRM); + route = pci_read_config32(PCH_XHCI_DEV, XHCI_USB3PR); + route &= ~XHCI_USB3PR_SSEN; + route |= XHCI_USB3PR_SSEN & port_mask; + pci_write_config32(PCH_XHCI_DEV, XHCI_USB3PR, route); + + /* Route USB2 ports to XHCI controller */ + port_mask = pci_read_config32(PCH_XHCI_DEV, XHCI_USB2PRM); + route = pci_read_config32(PCH_XHCI_DEV, XHCI_USB2PR); + route &= ~XHCI_USB2PR_HCSEL; + route |= XHCI_USB2PR_HCSEL & port_mask; + pci_write_config32(PCH_XHCI_DEV, XHCI_USB2PR, route); + + /* Disable EHCI controller */ + usb_ehci_disable(PCH_EHCI1_DEV); + + /* LynxPoint-H has a second EHCI controller */ + if (!pch_is_lp()) + usb_ehci_disable(PCH_EHCI2_DEV); + + /* Reset and clear port change status */ + usb_xhci_reset_usb3(PCH_XHCI_DEV, 1); +} + +#else /* !__SMM__ */ + +static void usb_xhci_clock_gating(device_t dev) +{ + u32 reg32; + u16 reg16; + + /* IOBP 0xE5004001[7:6] = 11b */ + pch_iobp_update(0xe5004001, ~0, (1 << 7)|(1 << 6)); + + reg32 = pci_read_config32(dev, 0x40); + reg32 &= ~(1 << 23); /* unsupported request */ + + if (pch_is_lp()) { + /* D20:F0:40h[18,17,8] = 111b */ + reg32 |= (1 << 18) | (1 << 17) | (1 << 8); + /* D20:F0:40h[21,20,19] = 110b to enable XHCI Idle L1 */ + reg32 &= ~(1 << 19); + reg32 |= (1 << 21) | (1 << 20); + } else { + /* D20:F0:40h[21,20,18,17,8] = 11111b */ + reg32 |= (1 << 21)|(1 << 20)|(1 << 18)|(1 << 17)|(1 << 8); + } + + /* Avoid writing upper byte as it is write-once */ + pci_write_config16(dev, 0x40, (u16)(reg32 & 0xffff)); + pci_write_config8(dev, 0x40 + 2, (u8)((reg32 >> 16) & 0xff)); + + /* D20:F0:44h[9,7,3] = 111b */ + reg16 = pci_read_config16(dev, 0x44); + reg16 |= (1 << 9) | (1 << 7) | (1 << 3); + pci_write_config16(dev, 0x44, reg16); + + reg32 = pci_read_config32(dev, 0xa0); + if (pch_is_lp()) { + /* D20:F0:A0h[18] = 1 */ + reg32 |= (1 << 18); + } else { + /* D20:F0:A0h[6] = 1 */ + reg32 |= (1 << 6); + } + pci_write_config32(dev, 0xa0, reg32); + + /* D20:F0:A4h[13] = 0 */ + reg32 = pci_read_config32(dev, 0xa4); + reg32 &= ~(1 << 13); + pci_write_config32(dev, 0xa4, reg32); +} + +static void usb_xhci_init(device_t dev) +{ + u32 reg32; + u16 reg16; + u32 mem_base = usb_xhci_mem_base(dev); + config_t *config = dev->chip_info; + + /* D20:F0:74h[1:0] = 00b (set D0 state) */ + reg16 = pci_read_config16(dev, XHCI_PWR_CTL_STS); + reg16 &= ~PWR_CTL_SET_MASK; + reg16 |= PWR_CTL_SET_D0; + pci_write_config16(dev, XHCI_PWR_CTL_STS, reg16); + + /* Enable clock gating first */ + usb_xhci_clock_gating(dev); + + reg32 = read32(mem_base + 0x8144); + if (pch_is_lp()) { + /* XHCIBAR + 8144h[8,7,6] = 111b */ + reg32 |= (1 << 8) | (1 << 7) | (1 << 6); + } else { + /* XHCIBAR + 8144h[8,7,6] = 100b */ + reg32 &= ~((1 << 7) | (1 << 6)); + reg32 |= (1 << 8); + } + write32(mem_base + 0x8144, reg32); + + if (pch_is_lp()) { + /* XHCIBAR + 816Ch[19:0] = 000e0038h */ + reg32 = read32(mem_base + 0x816c); + reg32 &= ~0x000fffff; + reg32 |= 0x000e0038; + write32(mem_base + 0x816c, reg32); + + /* D20:F0:B0h[17,14,13] = 100b */ + reg32 = pci_read_config32(dev, 0xb0); + reg32 &= ~((1 << 14) | (1 << 13)); + reg32 |= (1 << 17); + pci_write_config32(dev, 0xb0, reg32); + } + + reg32 = pci_read_config32(dev, 0x50); + if (pch_is_lp()) { + /* D20:F0:50h[28:0] = 0FCE2E5Fh */ + reg32 &= ~0x1fffffff; + reg32 |= 0x0fce2e5f; + } else { + /* D20:F0:50h[26:0] = 07886E9Fh */ + reg32 &= ~0x07ffffff; + reg32 |= 0x07886e9f; + } + pci_write_config32(dev, 0x50, reg32); + + /* D20:F0:44h[31] = 1 (Access Control Bit) */ + reg32 = pci_read_config32(dev, 0x44); + reg32 |= (1 << 31); + pci_write_config32(dev, 0x44, reg32); + + /* D20:F0:40h[31,23] = 10b (OC Configuration Done) */ + reg32 = pci_read_config32(dev, 0x40); + reg32 &= ~(1 << 23); /* unsupported request */ + reg32 |= (1 << 31); + pci_write_config32(dev, 0x40, reg32); + +#if CONFIG_HAVE_ACPI_RESUME + if (acpi_slp_type == 3) { + /* Reset ports that are disabled or + * polling before returning to the OS. */ + usb_xhci_reset_usb3(dev, 0); + } else +#endif + /* Route all ports to XHCI */ + if (config->xhci_default) + outb(0xca, 0xb2); +} + +static void usb_xhci_set_subsystem(device_t dev, unsigned vendor, + unsigned device) +{ + if (!vendor || !device) { + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + pci_read_config32(dev, PCI_VENDOR_ID)); + } else { + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + ((device & 0xffff) << 16) | (vendor & 0xffff)); + } +} + +static struct pci_operations lops_pci = { + .set_subsystem = &usb_xhci_set_subsystem, +}; + +static struct device_operations usb_xhci_ops = { + .read_resources = pci_dev_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .init = usb_xhci_init, + .ops_pci = &lops_pci, +}; + +static const unsigned short pci_device_ids[] = { 0x8c31, /* LynxPoint-H */ + 0x9c31, /* LynxPoint-LP */ + 0 }; + +static const struct pci_driver pch_usb_xhci __pci_driver = { + .ops = &usb_xhci_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .devices = pci_device_ids, +}; +#endif /* !__SMM__ */