diff options
author | Aaron Durbin <adurbin@chromium.org> | 2012-10-30 09:03:43 -0500 |
---|---|---|
committer | Ronald G. Minnich <rminnich@gmail.com> | 2013-03-14 01:44:40 +0100 |
commit | 76c3700f02f79b49fec30d6ef18d336f122cbf50 (patch) | |
tree | cb1c750ef3946e2ae462e1847ec89946579274b2 /src/northbridge | |
parent | cc86e63e835ab0bceb62215460a13266a791cdd3 (diff) |
haswell: Add initial support for Haswell platforms
The Haswell parts use a PCH code named Lynx Point (Series 8). Therefore,
the southbridge support is included as well. The basis for this code is
the Sandybridge code. Management Engine, IRQ routing, and ACPI still requires
more attention, but this is a good starting point.
This code partially gets up through the romstage just before training
memory on a Haswell reference board.
Change-Id: If572d6c21ca051b486b82a924ca0ffe05c4d0ad4
Signed-off-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-on: http://review.coreboot.org/2616
Tested-by: build bot (Jenkins)
Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
Diffstat (limited to 'src/northbridge')
22 files changed, 3940 insertions, 0 deletions
diff --git a/src/northbridge/intel/Kconfig b/src/northbridge/intel/Kconfig index f2d745a634..a20f7b0ec3 100644 --- a/src/northbridge/intel/Kconfig +++ b/src/northbridge/intel/Kconfig @@ -13,3 +13,4 @@ source src/northbridge/intel/gm45/Kconfig source src/northbridge/intel/sch/Kconfig source src/northbridge/intel/i5000/Kconfig source src/northbridge/intel/sandybridge/Kconfig +source src/northbridge/intel/haswell/Kconfig diff --git a/src/northbridge/intel/Makefile.inc b/src/northbridge/intel/Makefile.inc index 5888c652e5..62e427dd75 100644 --- a/src/northbridge/intel/Makefile.inc +++ b/src/northbridge/intel/Makefile.inc @@ -14,3 +14,4 @@ subdirs-$(CONFIG_NORTHBRIDGE_INTEL_SCH) += sch subdirs-$(CONFIG_NORTHBRIDGE_INTEL_I5000) += i5000 subdirs-$(CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE) += sandybridge subdirs-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += sandybridge +subdirs-$(CONFIG_NORTHBRIDGE_INTEL_HASWELL) += haswell diff --git a/src/northbridge/intel/haswell/Kconfig b/src/northbridge/intel/haswell/Kconfig new file mode 100644 index 0000000000..7e27cfd118 --- /dev/null +++ b/src/northbridge/intel/haswell/Kconfig @@ -0,0 +1,95 @@ +## +## This file is part of the coreboot project. +## +## Copyright (C) 2010 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 +## + +config NORTHBRIDGE_INTEL_HASWELL + bool + select CACHE_MRC_BIN + select CPU_INTEL_HASWELL + select REQUIRES_BLOB + +if NORTHBRIDGE_INTEL_HASWELL + +config VGA_BIOS_ID + string + default "8086,0166" + +config EXTERNAL_MRC_BLOB + bool + default n + +config CACHE_MRC_SIZE_KB + int + default 512 + +# FIXME: build from rom size +config MRC_CACHE_BASE + hex + default 0xff800000 + +config MRC_CACHE_LOCATION + hex + depends on !CHROMEOS + default 0x370000 + +config MRC_CACHE_SIZE + hex + depends on !CHROMEOS + default 0x10000 + +config DCACHE_RAM_BASE + hex + default 0xff7e0000 + +config DCACHE_RAM_SIZE + hex + default 0x20000 + +config DCACHE_RAM_MRC_VAR_SIZE + hex + default 0x4000 + +config HAVE_MRC + bool "Add a System Agent binary" + help + Select this option to add a System Agent binary to + the resulting coreboot image. + + Note: Without this binary coreboot will not work + +config MRC_FILE + string "Intel System Agent path and filename" + depends on HAVE_MRC + default "mrc.bin" + help + The path and filename of the file to use as System Agent + binary. + +config CBFS_SIZE + hex "Size of CBFS filesystem in ROM" + default 0x100000 + help + On Haswell systems the firmware image has to store a lot more + than just coreboot, including: + - a firmware descriptor + - Intel Management Engine firmware + - MRC cache information + This option allows to limit the size of the CBFS portion in the + firmware image. + +endif diff --git a/src/northbridge/intel/haswell/Makefile.inc b/src/northbridge/intel/haswell/Makefile.inc new file mode 100644 index 0000000000..896360d9ec --- /dev/null +++ b/src/northbridge/intel/haswell/Makefile.inc @@ -0,0 +1,41 @@ +# +# This file is part of the coreboot project. +# +# Copyright (C) 2010 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 +# + +ramstage-y += northbridge.c +ramstage-y += gma.c + +ramstage-$(CONFIG_GENERATE_ACPI_TABLES) += acpi.c +ramstage-y += mrccache.c + +romstage-y += raminit.c +romstage-y += mrccache.c +romstage-y += early_init.c +romstage-y += report_platform.c +romstage-y += ../../../arch/x86/lib/walkcbfs.S + +smm-$(CONFIG_HAVE_SMI_HANDLER) += udelay.c +smm-$(CONFIG_HAVE_SMI_HANDLER) += finalize.c + +# We don't ship that, but booting without it is bound to fail +cbfs-files-$(CONFIG_HAVE_MRC) += mrc.bin +mrc.bin-file := $(call strip_quotes,$(CONFIG_MRC_FILE)) +mrc.bin-position := 0xfffa0000 +mrc.bin-type := 0xab + +$(obj)/northbridge/intel/haswell/acpi.ramstage.o : $(obj)/build.h diff --git a/src/northbridge/intel/haswell/acpi.c b/src/northbridge/intel/haswell/acpi.c new file mode 100644 index 0000000000..c61669995d --- /dev/null +++ b/src/northbridge/intel/haswell/acpi.c @@ -0,0 +1,202 @@ +/* + * 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 + */ + +#include <types.h> +#include <string.h> +#include <console/console.h> +#include <arch/io.h> +#include <arch/acpi.h> +#include <device/device.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include <build.h> +#include "haswell.h" + +unsigned long acpi_fill_mcfg(unsigned long current) +{ + device_t dev; + u32 pciexbar = 0; + u32 pciexbar_reg; + int max_buses; + + dev = dev_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_SB, 0); + if (!dev) + dev = dev_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_IB, 0); + if (!dev) + return current; + + pciexbar_reg=pci_read_config32(dev, PCIEXBAR); + + // MMCFG not supported or not enabled. + if (!(pciexbar_reg & (1 << 0))) + return current; + + switch ((pciexbar_reg >> 1) & 3) { + case 0: // 256MB + pciexbar = pciexbar_reg & ((1 << 31)|(1 << 30)|(1 << 29)|(1 << 28)); + max_buses = 256; + break; + case 1: // 128M + pciexbar = pciexbar_reg & ((1 << 31)|(1 << 30)|(1 << 29)|(1 << 28)|(1 << 27)); + max_buses = 128; + break; + case 2: // 64M + pciexbar = pciexbar_reg & ((1 << 31)|(1 << 30)|(1 << 29)|(1 << 28)|(1 << 27)|(1 << 26)); + max_buses = 64; + break; + default: // RSVD + return current; + } + + if (!pciexbar) + return current; + + current += acpi_create_mcfg_mmconfig((acpi_mcfg_mmconfig_t *) current, + pciexbar, 0x0, 0x0, max_buses - 1); + + return current; +} + +static void *get_intel_vbios(void) +{ + /* This should probably be looking at CBFS or we should always + * deploy the VBIOS on Intel systems, even if we don't run it + * in coreboot (e.g. SeaBIOS only scenarios). + */ + u8 *vbios = (u8 *)0xc0000; + + optionrom_header_t *oprom = (optionrom_header_t *)vbios; + optionrom_pcir_t *pcir = (optionrom_pcir_t *)(vbios + + oprom->pcir_offset); + + + printk(BIOS_DEBUG, "GET_VBIOS: %x %x %x %x %x\n", + oprom->signature, pcir->vendor, pcir->classcode[0], + pcir->classcode[1], pcir->classcode[2]); + + + if ((oprom->signature == OPROM_SIGNATURE) && + (pcir->vendor == PCI_VENDOR_ID_INTEL) && + (pcir->classcode[0] == 0x00) && + (pcir->classcode[1] == 0x00) && + (pcir->classcode[2] == 0x03)) + return (void *)vbios; + + return NULL; +} + +static int init_opregion_vbt(igd_opregion_t *opregion) +{ + void *vbios; + vbios = get_intel_vbios(); + if (!vbios) { + printk(BIOS_DEBUG, "VBIOS not found.\n"); + return 1; + } + + printk(BIOS_DEBUG, " ... VBIOS found at %p\n", vbios); + optionrom_header_t *oprom = (optionrom_header_t *)vbios; + optionrom_vbt_t *vbt = (optionrom_vbt_t *)(vbios + + oprom->vbt_offset); + + if (read32((unsigned long)vbt->hdr_signature) != VBT_SIGNATURE) { + printk(BIOS_DEBUG, "VBT not found!\n"); + return 1; + } + + memcpy(opregion->header.vbios_version, vbt->coreblock_biosbuild, 4); + memcpy(opregion->vbt.gvd1, vbt, vbt->hdr_vbt_size < 7168 ? + vbt->hdr_vbt_size : 7168); + + return 0; +} + + +/* Initialize IGD OpRegion, called from ACPI code */ +int init_igd_opregion(igd_opregion_t *opregion) +{ + device_t igd; + u16 reg16; + + memset((void *)opregion, 0, sizeof(igd_opregion_t)); + + // FIXME if IGD is disabled, we should exit here. + + memcpy(&opregion->header.signature, IGD_OPREGION_SIGNATURE, + sizeof(IGD_OPREGION_SIGNATURE)); + + /* 8kb */ + opregion->header.size = sizeof(igd_opregion_t) / 1024; + opregion->header.version = IGD_OPREGION_VERSION; + + // FIXME We just assume we're mobile for now + opregion->header.mailboxes = MAILBOXES_MOBILE; + + // TODO Initialize Mailbox 1 + + // TODO Initialize Mailbox 3 + opregion->mailbox3.bclp = IGD_BACKLIGHT_BRIGHTNESS; + opregion->mailbox3.pfit = IGD_FIELD_VALID | IGD_PFIT_STRETCH; + opregion->mailbox3.pcft = 0; // should be (IMON << 1) & 0x3e + opregion->mailbox3.cblv = IGD_FIELD_VALID | IGD_INITIAL_BRIGHTNESS; + opregion->mailbox3.bclm[0] = IGD_WORD_FIELD_VALID + 0x0000; + opregion->mailbox3.bclm[1] = IGD_WORD_FIELD_VALID + 0x0a19; + opregion->mailbox3.bclm[2] = IGD_WORD_FIELD_VALID + 0x1433; + opregion->mailbox3.bclm[3] = IGD_WORD_FIELD_VALID + 0x1e4c; + opregion->mailbox3.bclm[4] = IGD_WORD_FIELD_VALID + 0x2866; + opregion->mailbox3.bclm[5] = IGD_WORD_FIELD_VALID + 0x327f; + opregion->mailbox3.bclm[6] = IGD_WORD_FIELD_VALID + 0x3c99; + opregion->mailbox3.bclm[7] = IGD_WORD_FIELD_VALID + 0x46b2; + opregion->mailbox3.bclm[8] = IGD_WORD_FIELD_VALID + 0x50cc; + opregion->mailbox3.bclm[9] = IGD_WORD_FIELD_VALID + 0x5ae5; + opregion->mailbox3.bclm[10] = IGD_WORD_FIELD_VALID + 0x64ff; + + init_opregion_vbt(opregion); + + /* TODO This needs to happen in S3 resume, too. + * Maybe it should move to the finalize handler + */ + igd = dev_find_slot(0, PCI_DEVFN(0x2, 0)); + + pci_write_config32(igd, ASLS, (u32)opregion); + reg16 = pci_read_config16(igd, SWSCI); + reg16 &= ~(1 << 0); + reg16 |= (1 << 15); + pci_write_config16(igd, SWSCI, reg16); + + /* clear dmisci status */ + reg16 = inw(DEFAULT_PMBASE + TCO1_STS); + reg16 |= DMISCI_STS; // reference code does an &= + outw(DEFAULT_PMBASE + TCO1_STS, reg16); + + /* clear acpi tco status */ + outl(DEFAULT_PMBASE + GPE0_STS, TCOSCI_STS); + + /* enable acpi tco scis */ + reg16 = inw(DEFAULT_PMBASE + GPE0_EN); + reg16 |= TCOSCI_EN; + outw(DEFAULT_PMBASE + GPE0_EN, reg16); + + return 0; +} + + diff --git a/src/northbridge/intel/haswell/acpi/haswell.asl b/src/northbridge/intel/haswell/acpi/haswell.asl new file mode 100644 index 0000000000..49d55e7388 --- /dev/null +++ b/src/northbridge/intel/haswell/acpi/haswell.asl @@ -0,0 +1,60 @@ +/* + * 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 + */ + +#include "../haswell.h" +#include "hostbridge.asl" + +/* PCI Device Resource Consumption */ +Device (PDRC) +{ + Name (_HID, EISAID("PNP0C02")) + Name (_UID, 1) + + Name (PDRS, ResourceTemplate() { + Memory32Fixed(ReadWrite, 0xfed1c000, 0x00004000) // RCBA + Memory32Fixed(ReadWrite, DEFAULT_MCHBAR, 0x00004000) + Memory32Fixed(ReadWrite, DEFAULT_DMIBAR, 0x00001000) + Memory32Fixed(ReadWrite, DEFAULT_EPBAR, 0x00001000) + Memory32Fixed(ReadWrite, DEFAULT_PCIEXBAR, 0x04000000) + Memory32Fixed(ReadWrite, 0xfed20000, 0x00020000) // Misc ICH + Memory32Fixed(ReadWrite, 0xfed40000, 0x00005000) // Misc ICH + Memory32Fixed(ReadWrite, 0xfed45000, 0x0004b000) // Misc ICH + +#if CONFIG_CHROMEOS_RAMOOPS + Memory32Fixed(ReadWrite, CONFIG_CHROMEOS_RAMOOPS_RAM_START, + CONFIG_CHROMEOS_RAMOOPS_RAM_SIZE) +#endif + + /* Required for SandyBridge sighting 3715511 */ + /* FIXME: Is this still required? */ + Memory32Fixed(ReadWrite, 0x20000000, 0x00200000) + Memory32Fixed(ReadWrite, 0x40000000, 0x00200000) + }) + + // Current Resource Settings + Method (_CRS, 0, Serialized) + { + Return(PDRS) + } +} + +// Integrated graphics 0:2.0 +#include "igd.asl" diff --git a/src/northbridge/intel/haswell/acpi/hostbridge.asl b/src/northbridge/intel/haswell/acpi/hostbridge.asl new file mode 100644 index 0000000000..681f6dc1fd --- /dev/null +++ b/src/northbridge/intel/haswell/acpi/hostbridge.asl @@ -0,0 +1,385 @@ +/* + * 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 */ + + OperationRegion (MCHB, SystemMemory, DEFAULT_MCHBAR, 0x8000) + Field (MCHB, DWordAcc, Lock, Preserve) + { + Offset (0x5930), + CTDN, 15, /* CTDP Nominal PL1 */ + Offset (0x59a0), + 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 (0x59a4), + 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 (0x5f3c), + TARN, 8, /* CTDP Nominal Turbo Activation Ratio */ + Offset (0x5f40), + CTDD, 15, /* CTDP Down PL1 */ + , 1, + TARD, 8, /* CTDP Down Turbo Activation Ratio */ + Offset (0x5f48), + CTDU, 15, /* CTDP Up PL1 */ + , 1, + TARU, 8, /* CTDP Up Turbo Activation Ratio */ + Offset (0x5f50), + CTCS, 2, /* CTDP Select */ + Offset (0x5f54), + 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) + } + + /* Set 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 to 1.25 * PL1 */ + Divide (Multiply (CTDD, 125), 100, Local0, PL2V) + + /* Set PL1 */ + Store (CTDD, PL1V) + + /* Store the new TDP Down setting */ + Store (CTCD, CTCC) + + Release (CTCM) + Return (1) + } + + /* Set 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 to 1.25 * PL1 */ + Divide (Multiply (CTDN, 125), 100, Local0, 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) + } +} + +// 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/northbridge/intel/haswell/acpi/igd.asl b/src/northbridge/intel/haswell/acpi/igd.asl new file mode 100644 index 0000000000..a6804adb5e --- /dev/null +++ b/src/northbridge/intel/haswell/acpi/igd.asl @@ -0,0 +1,324 @@ +/* + * 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 (GFX0) +{ + Name (_ADR, 0x00020000) + + /* Display Output Switching */ + Method (_DOS, 1) + { + /* Windows 2000 and Windows XP call _DOS to enable/disable + * Display Output Switching during init and while a switch + * is already active + */ + Store (And(Arg0, 7), DSEN) + } + + /* We try to support as many i945 systems as possible, + * so keep the number of DIDs flexible. + */ + Method (_DOD, 0) + { + If (LEqual(NDID, 1)) { + Name(DOD1, Package() { + 0xffffffff + }) + Store (Or(0x00010000, DID1), Index(DOD1, 0)) + Return(DOD1) + } + + If (LEqual(NDID, 2)) { + Name(DOD2, Package() { + 0xffffffff, + 0xffffffff + }) + Store (Or(0x00010000, DID2), Index(DOD2, 0)) + Store (Or(0x00010000, DID2), Index(DOD2, 1)) + Return(DOD2) + } + + If (LEqual(NDID, 3)) { + Name(DOD3, Package() { + 0xffffffff, + 0xffffffff, + 0xffffffff + }) + Store (Or(0x00010000, DID3), Index(DOD3, 0)) + Store (Or(0x00010000, DID3), Index(DOD3, 1)) + Store (Or(0x00010000, DID3), Index(DOD3, 2)) + Return(DOD3) + } + + If (LEqual(NDID, 4)) { + Name(DOD4, Package() { + 0xffffffff, + 0xffffffff, + 0xffffffff, + 0xffffffff + }) + Store (Or(0x00010000, DID4), Index(DOD4, 0)) + Store (Or(0x00010000, DID4), Index(DOD4, 1)) + Store (Or(0x00010000, DID4), Index(DOD4, 2)) + Store (Or(0x00010000, DID4), Index(DOD4, 3)) + Return(DOD4) + } + + If (LGreater(NDID, 4)) { + Name(DOD5, Package() { + 0xffffffff, + 0xffffffff, + 0xffffffff, + 0xffffffff, + 0xffffffff + }) + Store (Or(0x00010000, DID5), Index(DOD5, 0)) + Store (Or(0x00010000, DID5), Index(DOD5, 1)) + Store (Or(0x00010000, DID5), Index(DOD5, 2)) + Store (Or(0x00010000, DID5), Index(DOD5, 3)) + Store (Or(0x00010000, DID5), Index(DOD5, 4)) + Return(DOD5) + } + + /* Some error happened, but we have to return something */ + Return (Package() {0x00000400}) + } + + Device(DD01) + { + /* Device Unique ID */ + Method(_ADR, 0, Serialized) + { + If(LEqual(DID1, 0)) { + Return (1) + } Else { + Return (And(0xffff, DID1)) + } + } + + /* Device Current Status */ + Method(_DCS, 0) + { + TRAP(1) + If (And(CSTE, 1)) { + Return (0x1f) + } + Return(0x1d) + } + + /* Query Device Graphics State */ + Method(_DGS, 0) + { + If (And(NSTE, 1)) { + Return(1) + } + Return(0) + } + + /* Device Set State */ + Method(_DSS, 1) + { + /* If Parameter Arg0 is (1 << 31) | (1 << 30), the + * display switch was completed + */ + If (LEqual(And(Arg0, 0xc0000000), 0xc0000000)) { + Store (NSTE, CSTE) + } + } + } + + Device(DD02) + { + /* Device Unique ID */ + Method(_ADR, 0, Serialized) + { + If(LEqual(DID2, 0)) { + Return (2) + } Else { + Return (And(0xffff, DID2)) + } + } + + /* Device Current Status */ + Method(_DCS, 0) + { + TRAP(1) + If (And(CSTE, 2)) { + Return (0x1f) + } + Return(0x1d) + } + + /* Query Device Graphics State */ + Method(_DGS, 0) + { + If (And(NSTE, 2)) { + Return(1) + } + Return(0) + } + + /* Device Set State */ + Method(_DSS, 1) + { + /* If Parameter Arg0 is (1 << 31) | (1 << 30), the + * display switch was completed + */ + If (LEqual(And(Arg0, 0xc0000000), 0xc0000000)) { + Store (NSTE, CSTE) + } + } + } + + + Device(DD03) + { + /* Device Unique ID */ + Method(_ADR, 0, Serialized) + { + If(LEqual(DID3, 0)) { + Return (3) + } Else { + Return (And(0xffff, DID3)) + } + } + + /* Device Current Status */ + Method(_DCS, 0) + { + TRAP(1) + If (And(CSTE, 4)) { + Return (0x1f) + } + Return(0x1d) + } + + /* Query Device Graphics State */ + Method(_DGS, 0) + { + If (And(NSTE, 4)) { + Return(1) + } + Return(0) + } + + /* Device Set State */ + Method(_DSS, 1) + { + /* If Parameter Arg0 is (1 << 31) | (1 << 30), the + * display switch was completed + */ + If (LEqual(And(Arg0, 0xc0000000), 0xc0000000)) { + Store (NSTE, CSTE) + } + } + } + + + Device(DD04) + { + /* Device Unique ID */ + Method(_ADR, 0, Serialized) + { + If(LEqual(DID4, 0)) { + Return (4) + } Else { + Return (And(0xffff, DID4)) + } + } + + /* Device Current Status */ + Method(_DCS, 0) + { + TRAP(1) + If (And(CSTE, 8)) { + Return (0x1f) + } + Return(0x1d) + } + + /* Query Device Graphics State */ + Method(_DGS, 0) + { + If (And(NSTE, 4)) { + Return(1) + } + Return(0) + } + + /* Device Set State */ + Method(_DSS, 1) + { + /* If Parameter Arg0 is (1 << 31) | (1 << 30), the + * display switch was completed + */ + If (LEqual(And(Arg0, 0xc0000000), 0xc0000000)) { + Store (NSTE, CSTE) + } + } + } + + + Device(DD05) + { + /* Device Unique ID */ + Method(_ADR, 0, Serialized) + { + If(LEqual(DID5, 0)) { + Return (5) + } Else { + Return (And(0xffff, DID5)) + } + } + + /* Device Current Status */ + Method(_DCS, 0) + { + TRAP(1) + If (And(CSTE, 16)) { + Return (0x1f) + } + Return(0x1d) + } + + /* Query Device Graphics State */ + Method(_DGS, 0) + { + If (And(NSTE, 4)) { + Return(1) + } + Return(0) + } + + /* Device Set State */ + Method(_DSS, 1) + { + /* If Parameter Arg0 is (1 << 31) | (1 << 30), the + * display switch was completed + */ + If (LEqual(And(Arg0, 0xc0000000), 0xc0000000)) { + Store (NSTE, CSTE) + } + } + } + +} + diff --git a/src/northbridge/intel/haswell/chip.h b/src/northbridge/intel/haswell/chip.h new file mode 100644 index 0000000000..d60504c60a --- /dev/null +++ b/src/northbridge/intel/haswell/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/northbridge/intel/haswell/early_init.c b/src/northbridge/intel/haswell/early_init.c new file mode 100644 index 0000000000..b524f2ec3e --- /dev/null +++ b/src/northbridge/intel/haswell/early_init.c @@ -0,0 +1,167 @@ +/* + * 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 <stdint.h> +#include <stdlib.h> +#include <console/console.h> +#include <arch/io.h> +#include <arch/romcc_io.h> +#include <device/pci_def.h> +#include <elog.h> +#include "haswell.h" +#include "pcie_config.c" + +static void haswell_setup_bars(void) +{ + /* Setting up Southbridge. In the northbridge code. */ + printk(BIOS_DEBUG, "Setting up static southbridge registers..."); + pci_write_config32(PCI_DEV(0, 0x1f, 0), RCBA, DEFAULT_RCBA | 1); + + pci_write_config32(PCI_DEV(0, 0x1f, 0), PMBASE, DEFAULT_PMBASE | 1); + pci_write_config8(PCI_DEV(0, 0x1f, 0), 0x44 /* ACPI_CNTL */ , 0x80); /* Enable ACPI BAR */ + + printk(BIOS_DEBUG, " done.\n"); + + 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"); + + 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), PCIEXBAR, DEFAULT_PCIEXBAR | 5); /* 64MB - busses 0-63 */ + pci_write_config32(PCI_DEV(0, 0x00, 0), PCIEXBAR + 4, (0LL+DEFAULT_PCIEXBAR) >> 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"); + +#if CONFIG_ELOG_BOOT_COUNT + /* Increment Boot Counter except when resuming from S3 */ + if ((inw(DEFAULT_PMBASE + PM1_STS) & WAK_STS) && + ((inl(DEFAULT_PMBASE + PM1_CNT) >> 10) & 7) == SLP_TYP_S3) + return; + boot_count_increment(); +#endif +} + +static void haswell_setup_graphics(void) +{ + u32 reg32; + u16 reg16; + u8 reg8; + + reg16 = pci_read_config16(PCI_DEV(0,2,0), PCI_DEVICE_ID); + switch (reg16) { + case 0x0102: /* GT1 Desktop */ + case 0x0106: /* GT1 Mobile */ + case 0x010a: /* GT1 Server */ + case 0x0112: /* GT2 Desktop */ + case 0x0116: /* GT2 Mobile */ + case 0x0122: /* GT2 Desktop >=1.3GHz */ + case 0x0126: /* GT2 Mobile >=1.3GHz */ + case 0x0166: /* IvyBridge ??? */ + break; + default: + printk(BIOS_DEBUG, "Graphics not supported by this CPU/chipset.\n"); + return; + } + + 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) +{ + u32 capid0_a; + u8 reg8; + + /* Device ID Override Enable should be done very early */ + capid0_a = pci_read_config32(PCI_DEV(0, 0, 0), 0xe4); + if (capid0_a & (1 << 10)) { + reg8 = pci_read_config8(PCI_DEV(0, 0, 0), 0xf3); + reg8 &= ~7; /* Clear 2:0 */ + + if (chipset_type == HASWELL_MOBILE) + reg8 |= 1; /* Set bit 0 */ + + pci_write_config8(PCI_DEV(0, 0, 0), 0xf3, reg8); + } + + /* Setup all BARs required for early PCIe and raminit */ + haswell_setup_bars(); + + /* Device Enable */ + pci_write_config32(PCI_DEV(0, 0, 0), DEVEN, DEVEN_HOST | DEVEN_IGD); + + haswell_setup_graphics(); +} diff --git a/src/northbridge/intel/haswell/finalize.c b/src/northbridge/intel/haswell/finalize.c new file mode 100644 index 0000000000..01843c9fee --- /dev/null +++ b/src/northbridge/intel/haswell/finalize.c @@ -0,0 +1,58 @@ +/* + * 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 <arch/io.h> +#include <arch/romcc_io.h> +#include <stdlib.h> +#include "pcie_config.c" +#include "haswell.h" + +#define PCI_DEV_SNB PCI_DEV(0, 0, 0) + +void intel_northbridge_haswell_finalize_smm(void) +{ + pcie_or_config16(PCI_DEV_SNB, 0x50, 1 << 0); /* GGC */ + pcie_or_config32(PCI_DEV_SNB, 0x5c, 1 << 0); /* DPR */ + pcie_or_config32(PCI_DEV_SNB, 0x78, 1 << 10); /* ME */ + pcie_or_config32(PCI_DEV_SNB, 0x90, 1 << 0); /* REMAPBASE */ + pcie_or_config32(PCI_DEV_SNB, 0x98, 1 << 0); /* REMAPLIMIT */ + pcie_or_config32(PCI_DEV_SNB, 0xa0, 1 << 0); /* TOM */ + pcie_or_config32(PCI_DEV_SNB, 0xa8, 1 << 0); /* TOUUD */ + pcie_or_config32(PCI_DEV_SNB, 0xb0, 1 << 0); /* BDSM */ + pcie_or_config32(PCI_DEV_SNB, 0xb4, 1 << 0); /* BGSM */ + pcie_or_config32(PCI_DEV_SNB, 0xb8, 1 << 0); /* TSEGMB */ + pcie_or_config32(PCI_DEV_SNB, 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/northbridge/intel/haswell/gma.c b/src/northbridge/intel/haswell/gma.c new file mode 100644 index 0000000000..08c13df8bc --- /dev/null +++ b/src/northbridge/intel/haswell/gma.c @@ -0,0 +1,670 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 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 + */ + +#include <arch/io.h> +#include <console/console.h> +#include <delay.h> +#include <device/device.h> +#include <device/pci.h> +#include <device/pci_ids.h> + +#include "chip.h" +#include "haswell.h" + +struct gt_powermeter { + u16 reg; + u32 value; +}; + +static const struct gt_powermeter snb_pm_gt1[] = { + { 0xa200, 0xcc000000 }, + { 0xa204, 0x07000040 }, + { 0xa208, 0x0000fe00 }, + { 0xa20c, 0x00000000 }, + { 0xa210, 0x17000000 }, + { 0xa214, 0x00000021 }, + { 0xa218, 0x0817fe19 }, + { 0xa21c, 0x00000000 }, + { 0xa220, 0x00000000 }, + { 0xa224, 0xcc000000 }, + { 0xa228, 0x07000040 }, + { 0xa22c, 0x0000fe00 }, + { 0xa230, 0x00000000 }, + { 0xa234, 0x17000000 }, + { 0xa238, 0x00000021 }, + { 0xa23c, 0x0817fe19 }, + { 0xa240, 0x00000000 }, + { 0xa244, 0x00000000 }, + { 0xa248, 0x8000421e }, + { 0 } +}; + +static const struct gt_powermeter snb_pm_gt2[] = { + { 0xa200, 0x330000a6 }, + { 0xa204, 0x402d0031 }, + { 0xa208, 0x00165f83 }, + { 0xa20c, 0xf1000000 }, + { 0xa210, 0x00000000 }, + { 0xa214, 0x00160016 }, + { 0xa218, 0x002a002b }, + { 0xa21c, 0x00000000 }, + { 0xa220, 0x00000000 }, + { 0xa224, 0x330000a6 }, + { 0xa228, 0x402d0031 }, + { 0xa22c, 0x00165f83 }, + { 0xa230, 0xf1000000 }, + { 0xa234, 0x00000000 }, + { 0xa238, 0x00160016 }, + { 0xa23c, 0x002a002b }, + { 0xa240, 0x00000000 }, + { 0xa244, 0x00000000 }, + { 0xa248, 0x8000421e }, + { 0 } +}; + +static const struct gt_powermeter ivb_pm_gt1[] = { + { 0xa800, 0x00000000 }, + { 0xa804, 0x00021c00 }, + { 0xa808, 0x00000403 }, + { 0xa80c, 0x02001700 }, + { 0xa810, 0x05000200 }, + { 0xa814, 0x00000000 }, + { 0xa818, 0x00690500 }, + { 0xa81c, 0x0000007f }, + { 0xa820, 0x01002501 }, + { 0xa824, 0x00000300 }, + { 0xa828, 0x01000331 }, + { 0xa82c, 0x0000000c }, + { 0xa830, 0x00010016 }, + { 0xa834, 0x01100101 }, + { 0xa838, 0x00010103 }, + { 0xa83c, 0x00041300 }, + { 0xa840, 0x00000b30 }, + { 0xa844, 0x00000000 }, + { 0xa848, 0x7f000000 }, + { 0xa84c, 0x05000008 }, + { 0xa850, 0x00000001 }, + { 0xa854, 0x00000004 }, + { 0xa858, 0x00000007 }, + { 0xa85c, 0x00000000 }, + { 0xa860, 0x00010000 }, + { 0xa248, 0x0000221e }, + { 0xa900, 0x00000000 }, + { 0xa904, 0x00001c00 }, + { 0xa908, 0x00000000 }, + { 0xa90c, 0x06000000 }, + { 0xa910, 0x09000200 }, + { 0xa914, 0x00000000 }, + { 0xa918, 0x00590000 }, + { 0xa91c, 0x00000000 }, + { 0xa920, 0x04002501 }, + { 0xa924, 0x00000100 }, + { 0xa928, 0x03000410 }, + { 0xa92c, 0x00000000 }, + { 0xa930, 0x00020000 }, + { 0xa934, 0x02070106 }, + { 0xa938, 0x00010100 }, + { 0xa93c, 0x00401c00 }, + { 0xa940, 0x00000000 }, + { 0xa944, 0x00000000 }, + { 0xa948, 0x10000e00 }, + { 0xa94c, 0x02000004 }, + { 0xa950, 0x00000001 }, + { 0xa954, 0x00000004 }, + { 0xa960, 0x00060000 }, + { 0xaa3c, 0x00001c00 }, + { 0xaa54, 0x00000004 }, + { 0xaa60, 0x00060000 }, + { 0 } +}; + +static const struct gt_powermeter ivb_pm_gt2[] = { + { 0xa800, 0x10000000 }, + { 0xa804, 0x00033800 }, + { 0xa808, 0x00000902 }, + { 0xa80c, 0x0c002f00 }, + { 0xa810, 0x12000400 }, + { 0xa814, 0x00000000 }, + { 0xa818, 0x00d20800 }, + { 0xa81c, 0x00000002 }, + { 0xa820, 0x03004b02 }, + { 0xa824, 0x00000600 }, + { 0xa828, 0x07000773 }, + { 0xa82c, 0x00000000 }, + { 0xa830, 0x00010032 }, + { 0xa834, 0x1520040d }, + { 0xa838, 0x00020105 }, + { 0xa83c, 0x00083700 }, + { 0xa840, 0x0000151d }, + { 0xa844, 0x00000000 }, + { 0xa848, 0x20001b00 }, + { 0xa84c, 0x0a000010 }, + { 0xa850, 0x00000000 }, + { 0xa854, 0x00000008 }, + { 0xa858, 0x00000008 }, + { 0xa85c, 0x00000000 }, + { 0xa860, 0x00020000 }, + { 0xa248, 0x0000221e }, + { 0xa900, 0x00000000 }, + { 0xa904, 0x00003500 }, + { 0xa908, 0x00000000 }, + { 0xa90c, 0x0c000000 }, + { 0xa910, 0x12000500 }, + { 0xa914, 0x00000000 }, + { 0xa918, 0x00b20000 }, + { 0xa91c, 0x00000000 }, + { 0xa920, 0x08004b02 }, + { 0xa924, 0x00000200 }, + { 0xa928, 0x07000820 }, + { 0xa92c, 0x00000000 }, + { 0xa930, 0x00030000 }, + { 0xa934, 0x050f020d }, + { 0xa938, 0x00020300 }, + { 0xa93c, 0x00903900 }, + { 0xa940, 0x00000000 }, + { 0xa944, 0x00000000 }, + { 0xa948, 0x20001b00 }, + { 0xa94c, 0x0a000010 }, + { 0xa950, 0x00000000 }, + { 0xa954, 0x00000008 }, + { 0xa960, 0x00110000 }, + { 0xaa3c, 0x00003900 }, + { 0xaa54, 0x00000008 }, + { 0xaa60, 0x00110000 }, + { 0 } +}; + +static const struct gt_powermeter ivb_pm_gt2_17w[] = { + { 0xa800, 0x20000000 }, + { 0xa804, 0x000e3800 }, + { 0xa808, 0x00000806 }, + { 0xa80c, 0x0c002f00 }, + { 0xa810, 0x0c000800 }, + { 0xa814, 0x00000000 }, + { 0xa818, 0x00d20d00 }, + { 0xa81c, 0x000000ff }, + { 0xa820, 0x03004b02 }, + { 0xa824, 0x00000600 }, + { 0xa828, 0x07000773 }, + { 0xa82c, 0x00000000 }, + { 0xa830, 0x00020032 }, + { 0xa834, 0x1520040d }, + { 0xa838, 0x00020105 }, + { 0xa83c, 0x00083700 }, + { 0xa840, 0x000016ff }, + { 0xa844, 0x00000000 }, + { 0xa848, 0xff000000 }, + { 0xa84c, 0x0a000010 }, + { 0xa850, 0x00000002 }, + { 0xa854, 0x00000008 }, + { 0xa858, 0x0000000f }, + { 0xa85c, 0x00000000 }, + { 0xa860, 0x00020000 }, + { 0xa248, 0x0000221e }, + { 0xa900, 0x00000000 }, + { 0xa904, 0x00003800 }, + { 0xa908, 0x00000000 }, + { 0xa90c, 0x0c000000 }, + { 0xa910, 0x12000800 }, + { 0xa914, 0x00000000 }, + { 0xa918, 0x00b20000 }, + { 0xa91c, 0x00000000 }, + { 0xa920, 0x08004b02 }, + { 0xa924, 0x00000300 }, + { 0xa928, 0x01000820 }, + { 0xa92c, 0x00000000 }, + { 0xa930, 0x00030000 }, + { 0xa934, 0x15150406 }, + { 0xa938, 0x00020300 }, + { 0xa93c, 0x00903900 }, + { 0xa940, 0x00000000 }, + { 0xa944, 0x00000000 }, + { 0xa948, 0x20001b00 }, + { 0xa94c, 0x0a000010 }, + { 0xa950, 0x00000000 }, + { 0xa954, 0x00000008 }, + { 0xa960, 0x00110000 }, + { 0xaa3c, 0x00003900 }, + { 0xaa54, 0x00000008 }, + { 0xaa60, 0x00110000 }, + { 0 } +}; + +static const struct gt_powermeter ivb_pm_gt2_35w[] = { + { 0xa800, 0x00000000 }, + { 0xa804, 0x00030400 }, + { 0xa808, 0x00000806 }, + { 0xa80c, 0x0c002f00 }, + { 0xa810, 0x0c000300 }, + { 0xa814, 0x00000000 }, + { 0xa818, 0x00d20d00 }, + { 0xa81c, 0x000000ff }, + { 0xa820, 0x03004b02 }, + { 0xa824, 0x00000600 }, + { 0xa828, 0x07000773 }, + { 0xa82c, 0x00000000 }, + { 0xa830, 0x00020032 }, + { 0xa834, 0x1520040d }, + { 0xa838, 0x00020105 }, + { 0xa83c, 0x00083700 }, + { 0xa840, 0x000016ff }, + { 0xa844, 0x00000000 }, + { 0xa848, 0xff000000 }, + { 0xa84c, 0x0a000010 }, + { 0xa850, 0x00000001 }, + { 0xa854, 0x00000008 }, + { 0xa858, 0x00000008 }, + { 0xa85c, 0x00000000 }, + { 0xa860, 0x00020000 }, + { 0xa248, 0x0000221e }, + { 0xa900, 0x00000000 }, + { 0xa904, 0x00003800 }, + { 0xa908, 0x00000000 }, + { 0xa90c, 0x0c000000 }, + { 0xa910, 0x12000800 }, + { 0xa914, 0x00000000 }, + { 0xa918, 0x00b20000 }, + { 0xa91c, 0x00000000 }, + { 0xa920, 0x08004b02 }, + { 0xa924, 0x00000300 }, + { 0xa928, 0x01000820 }, + { 0xa92c, 0x00000000 }, + { 0xa930, 0x00030000 }, + { 0xa934, 0x15150406 }, + { 0xa938, 0x00020300 }, + { 0xa93c, 0x00903900 }, + { 0xa940, 0x00000000 }, + { 0xa944, 0x00000000 }, + { 0xa948, 0x20001b00 }, + { 0xa94c, 0x0a000010 }, + { 0xa950, 0x00000000 }, + { 0xa954, 0x00000008 }, + { 0xa960, 0x00110000 }, + { 0xaa3c, 0x00003900 }, + { 0xaa54, 0x00000008 }, + { 0xaa60, 0x00110000 }, + { 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 0x80860102: /* GT1 Desktop */ + case 0x8086010a: /* GT1 Server */ + case 0x80860112: /* GT2 Desktop */ + case 0x80860116: /* GT2 Mobile */ + case 0x80860122: /* GT2 Desktop >=1.3GHz */ + case 0x80860126: /* GT2 Mobile >=1.3GHz */ + case 0x80860166: /* IVB */ + new_vendev=0x80860106; /* GT1 Mobile */ + break; + } + + return new_vendev; +} + +static struct resource *gtt_res = NULL; + +static inline u32 gtt_read(u32 reg) +{ + return read32(gtt_res->base + reg); +} + +static inline void gtt_write(u32 reg, u32 data) +{ + write32(gtt_res->base + reg, data); +} + +static inline void gtt_write_powermeter(const struct gt_powermeter *pm) +{ + for (; pm && pm->reg; pm++) + gtt_write(pm->reg, pm->value); +} + +#define GTT_RETRY 1000 +static 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 gma_pm_init_pre_vbios(struct device *dev) +{ + u32 reg32; + + printk(BIOS_DEBUG, "GT Power Management Init\n"); + + gtt_res = find_resource(dev, PCI_BASE_ADDRESS_0); + if (!gtt_res || !gtt_res->base) + return; + + if (bridge_silicon_revision() < IVB_STEP_C0) { + /* 1: Enable force wake */ + gtt_write(0xa18c, 0x00000001); + gtt_poll(0x130090, (1 << 0), (1 << 0)); + } else { + gtt_write(0xa180, 1 << 5); + gtt_write(0xa188, 0xffff0001); + gtt_poll(0x130040, (1 << 0), (1 << 0)); + } + + if ((bridge_silicon_revision() & BASE_REV_MASK) == BASE_REV_SNB) { + /* 1d: Set GTT+0x42004 [15:14]=11 (SnB C1+) */ + reg32 = gtt_read(0x42004); + reg32 |= (1 << 14) | (1 << 15); + gtt_write(0x42004, reg32); + } + + if (bridge_silicon_revision() >= IVB_STEP_A0) { + /* Display Reset Acknowledge Settings */ + reg32 = gtt_read(0x45010); + reg32 |= (1 << 1) | (1 << 0); + gtt_write(0x45010, reg32); + } + + /* 2: Get GT SKU from GTT+0x911c[13] */ + reg32 = gtt_read(0x911c); + if ((bridge_silicon_revision() & BASE_REV_MASK) == BASE_REV_SNB) { + if (reg32 & (1 << 13)) { + printk(BIOS_DEBUG, "SNB GT1 Power Meter Weights\n"); + gtt_write_powermeter(snb_pm_gt1); + } else { + printk(BIOS_DEBUG, "SNB GT2 Power Meter Weights\n"); + gtt_write_powermeter(snb_pm_gt2); + } + } else { + u32 unit = MCHBAR32(0x5938) & 0xf; + + if (reg32 & (1 << 13)) { + /* GT1 SKU */ + printk(BIOS_DEBUG, "IVB GT1 Power Meter Weights\n"); + gtt_write_powermeter(ivb_pm_gt1); + } else { + /* GT2 SKU */ + u32 tdp = MCHBAR32(0x5930) & 0x7fff; + tdp /= (1 << unit); + + if (tdp <= 17) { + /* <=17W ULV */ + printk(BIOS_DEBUG, "IVB GT2 17W " + "Power Meter Weights\n"); + gtt_write_powermeter(ivb_pm_gt2_17w); + } else if ((tdp >= 25) && (tdp <= 35)) { + /* 25W-35W */ + printk(BIOS_DEBUG, "IVB GT2 25W-35W " + "Power Meter Weights\n"); + gtt_write_powermeter(ivb_pm_gt2_35w); + } else { + /* All others */ + printk(BIOS_DEBUG, "IVB GT2 35W " + "Power Meter Weights\n"); + gtt_write_powermeter(ivb_pm_gt2_35w); + } + } + } + + /* 3: Gear ratio map */ + gtt_write(0xa004, 0x00000010); + + /* 4: GFXPAUSE */ + gtt_write(0xa000, 0x00070020); + + /* 5: Dynamic EU trip control */ + gtt_write(0xa080, 0x00000004); + + /* 6: ECO bits */ + reg32 = gtt_read(0xa180); + reg32 |= (1 << 26) | (1 << 31); + /* (bit 20=1 for SNB step D1+ / IVB A0+) */ + if (bridge_silicon_revision() >= SNB_STEP_D1) + reg32 |= (1 << 20); + gtt_write(0xa180, reg32); + + /* 6a: for SnB step D2+ only */ + if (((bridge_silicon_revision() & BASE_REV_MASK) == BASE_REV_SNB) && + (bridge_silicon_revision() >= SNB_STEP_D2)) { + reg32 = gtt_read(0x9400); + reg32 |= (1 << 7); + gtt_write(0x9400, reg32); + + reg32 = gtt_read(0x941c); + reg32 &= 0xf; + reg32 |= (1 << 1); + gtt_write(0x941c, reg32); + gtt_poll(0x941c, (1 << 1), (0 << 1)); + } + + if ((bridge_silicon_revision() & BASE_REV_MASK) == BASE_REV_IVB) { + reg32 = gtt_read(0x907c); + reg32 |= (1 << 16); + gtt_write(0x907c, reg32); + + /* 6b: Clocking reset controls */ + gtt_write(0x9424, 0x00000001); + } else { + /* 6b: Clocking reset controls */ + gtt_write(0x9424, 0x00000000); + } + + /* 7 */ + if (gtt_poll(0x138124, (1 << 31), (0 << 31))) { + gtt_write(0x138128, 0x00000029); /* Mailbox Data */ + gtt_write(0x138124, 0x80000004); /* Mailbox Cmd for RC6 VID */ + if (gtt_poll(0x138124, (1 << 31), (0 << 31))) + gtt_write(0x138124, 0x8000000a); + gtt_poll(0x138124, (1 << 31), (0 << 31)); + } + + /* 8 */ + gtt_write(0xa090, 0x00000000); /* RC Control */ + gtt_write(0xa098, 0x03e80000); /* RC1e Wake Rate Limit */ + gtt_write(0xa09c, 0x0028001e); /* RC6/6p Wake Rate Limit */ + gtt_write(0xa0a0, 0x0000001e); /* RC6pp Wake Rate Limit */ + gtt_write(0xa0a8, 0x0001e848); /* RC Evaluation Interval */ + gtt_write(0xa0ac, 0x00000019); /* RC Idle Hysteresis */ + + /* 9 */ + gtt_write(0x2054, 0x0000000a); /* Render Idle Max Count */ + gtt_write(0x12054,0x0000000a); /* Video Idle Max Count */ + gtt_write(0x22054,0x0000000a); /* Blitter Idle Max Count */ + + /* 10 */ + gtt_write(0xa0b0, 0x00000000); /* Unblock Ack to Busy */ + gtt_write(0xa0b4, 0x000003e8); /* RC1e Threshold */ + gtt_write(0xa0b8, 0x0000c350); /* RC6 Threshold */ + gtt_write(0xa0bc, 0x000186a0); /* RC6p Threshold */ + gtt_write(0xa0c0, 0x0000fa00); /* RC6pp Threshold */ + + /* 11 */ + gtt_write(0xa010, 0x000f4240); /* RP Down Timeout */ + gtt_write(0xa014, 0x12060000); /* RP Interrupt Limits */ + gtt_write(0xa02c, 0x00015f90); /* RP Up Threshold */ + gtt_write(0xa030, 0x000186a0); /* RP Down Threshold */ + gtt_write(0xa068, 0x000186a0); /* RP Up EI */ + gtt_write(0xa06c, 0x000493e0); /* RP Down EI */ + gtt_write(0xa070, 0x0000000a); /* RP Idle Hysteresis */ + + /* 11a: Enable Render Standby (RC6) */ + if ((bridge_silicon_revision() & BASE_REV_MASK) == BASE_REV_IVB) { + /* + * IvyBridge should also support DeepRenderStandby. + * + * Unfortunately it does not work reliably on all SKUs so + * disable it here and it can be enabled by the kernel. + */ + gtt_write(0xa090, 0x88040000); /* HW RC Control */ + } else { + gtt_write(0xa090, 0x88040000); /* HW RC Control */ + } + + /* 12: Normal Frequency Request */ + /* RPNFREQ_VAL comes from MCHBAR 0x5998 23:16 (8 bits!? use 7) */ + reg32 = MCHBAR32(0x5998); + reg32 >>= 16; + reg32 &= 0xef; + reg32 <<= 25; + gtt_write(0xa008, reg32); + + /* 13: RP Control */ + gtt_write(0xa024, 0x00000592); + + /* 14: Enable PM Interrupts */ + gtt_write(0x4402c, 0x03000076); + + /* Clear 0x6c024 [8:6] */ + reg32 = gtt_read(0x6c024); + reg32 &= ~0x000001c0; + gtt_write(0x6c024, reg32); +} + +static void gma_pm_init_post_vbios(struct device *dev) +{ + struct northbridge_intel_haswell_config *conf = dev->chip_info; + u32 reg32; + + printk(BIOS_DEBUG, "GT Power Management Init (post VBIOS)\n"); + + /* 15: Deassert Force Wake */ + if (bridge_silicon_revision() < IVB_STEP_C0) { + gtt_write(0xa18c, gtt_read(0xa18c) & ~1); + gtt_poll(0x130090, (1 << 0), (0 << 0)); + } else { + gtt_write(0xa188, 0x1fffe); + if (gtt_poll(0x130040, (1 << 0), (0 << 0))) + gtt_write(0xa188, gtt_read(0xa188) | 1); + } + + /* 16: SW RC Control */ + gtt_write(0xa094, 0x00060000); + + /* Setup Digital Port Hotplug */ + reg32 = gtt_read(0xc4030); + 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(0xc4030, reg32); + } + + /* Setup Panel Power On Delays */ + reg32 = gtt_read(0xc7208); + 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(0xc7208, reg32); + } + + /* Setup Panel Power Off Delays */ + reg32 = gtt_read(0xc720c); + if (!reg32) { + reg32 = (conf->gpu_panel_power_down_delay & 0x1fff) << 16; + reg32 |= (conf->gpu_panel_power_backlight_off_delay & 0x1fff); + gtt_write(0xc720c, reg32); + } + + /* Setup Panel Power Cycle Delay */ + if (conf->gpu_panel_power_cycle_delay) { + reg32 = gtt_read(0xc7210); + reg32 &= ~0xff; + reg32 |= conf->gpu_panel_power_cycle_delay & 0xff; + gtt_write(0xc7210, reg32); + } + + /* Enable Backlight if needed */ + if (conf->gpu_cpu_backlight) { + gtt_write(0x48250, (1 << 31)); + gtt_write(0x48254, conf->gpu_cpu_backlight); + } + if (conf->gpu_pch_backlight) { + gtt_write(0xc8250, (1 << 31)); + gtt_write(0xc8254, conf->gpu_pch_backlight); + } +} + +static void gma_func0_init(struct device *dev) +{ + 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); + + /* PCI Init, will run VBIOS */ + 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 struct pci_operations gma_pci_ops = { + .set_subsystem = gma_set_subsystem, +}; + +static struct device_operations gma_func0_ops = { + .read_resources = pci_dev_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[] = { 0x0102, 0x0106, 0x010a, 0x0112, + 0x0116, 0x0122, 0x0126, 0x0166, + 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/northbridge/intel/haswell/gma.h b/src/northbridge/intel/haswell/gma.h new file mode 100644 index 0000000000..bfa43efcae --- /dev/null +++ b/src/northbridge/intel/haswell/gma.h @@ -0,0 +1,168 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 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 + */ + +/* mailbox 0: header */ +typedef struct { + u8 signature[16]; + u32 size; + u32 version; + u8 sbios_version[32]; + u8 vbios_version[16]; + u8 driver_version[16]; + u32 mailboxes; + u8 reserved[164]; +} __attribute__((packed)) opregion_header_t; + +#define IGD_OPREGION_SIGNATURE "IntelGraphicsMem" +#define IGD_OPREGION_VERSION 2 + +#define IGD_MBOX1 (1 << 0) +#define IGD_MBOX2 (1 << 1) +#define IGD_MBOX3 (1 << 2) +#define IGD_MBOX4 (1 << 3) +#define IGD_MBOX5 (1 << 4) + +#define MAILBOXES_MOBILE (IGD_MBOX1 | IGD_MBOX2 | IGD_MBOX3 | \ + IGD_MBOX4 | IGD_MBOX5) +#define MAILBOXES_DESKTOP (IGD_MBOX2 | IGD_MBOX4) + +#define SBIOS_VERSION_SIZE 32 + +/* mailbox 1: public acpi methods */ +typedef struct { + u32 drdy; + u32 csts; + u32 cevt; + u8 reserved1[20]; + u32 didl[8]; + u32 cpdl[8]; + u32 cadl[8]; + u32 nadl[8]; + u32 aslp; + u32 tidx; + u32 chpd; + u32 clid; + u32 cdck; + u32 sxsw; + u32 evts; + u32 cnot; + u32 nrdy; + u8 reserved2[60]; +} __attribute__((packed)) opregion_mailbox1_t; + +/* mailbox 2: software sci interface */ +typedef struct { + u32 scic; + u32 parm; + u32 dslp; + u8 reserved[244]; +} __attribute__((packed)) opregion_mailbox2_t; + +/* mailbox 3: power conservation */ +typedef struct { + u32 ardy; + u32 aslc; + u32 tche; + u32 alsi; + u32 bclp; + u32 pfit; + u32 cblv; + u16 bclm[20]; + u32 cpfm; + u32 epfm; + u8 plut[74]; + u32 pfmb; + u32 ccdv; + u32 pcft; + u8 reserved[94]; +} __attribute__((packed)) opregion_mailbox3_t; + +#define IGD_BACKLIGHT_BRIGHTNESS 0xff +#define IGD_INITIAL_BRIGHTNESS 0x64 + +#define IGD_FIELD_VALID (1 << 31) +#define IGD_WORD_FIELD_VALID (1 << 15) +#define IGD_PFIT_STRETCH 6 + +/* mailbox 4: vbt */ +typedef struct { + u8 gvd1[7168]; +} __attribute__((packed)) opregion_vbt_t; + +/* IGD OpRegion */ +typedef struct { + opregion_header_t header; + opregion_mailbox1_t mailbox1; + opregion_mailbox2_t mailbox2; + opregion_mailbox3_t mailbox3; + opregion_vbt_t vbt; +} __attribute__((packed)) igd_opregion_t; + +/* Intel Video BIOS (Option ROM) */ +typedef struct { + u16 signature; + u8 size; + u8 reserved[21]; + u16 pcir_offset; + u16 vbt_offset; +} __attribute__((packed)) optionrom_header_t; + +#define OPROM_SIGNATURE 0xaa55 + +typedef struct { + u32 signature; + u16 vendor; + u16 device; + u16 reserved1; + u16 length; + u8 revision; + u8 classcode[3]; + u16 imagelength; + u16 coderevision; + u8 codetype; + u8 indicator; + u16 reserved2; +} __attribute__((packed)) optionrom_pcir_t; + +typedef struct { + u8 hdr_signature[20]; + u16 hdr_version; + u16 hdr_size; + u16 hdr_vbt_size; + u8 hdr_vbt_checksum; + u8 hdr_reserved; + u32 hdr_vbt_datablock; + u32 hdr_aim[4]; + u8 datahdr_signature[16]; + u16 datahdr_version; + u16 datahdr_size; + u16 datahdr_datablocksize; + u8 coreblock_id; + u16 coreblock_size; + u16 coreblock_biossize; + u8 coreblock_biostype; + u8 coreblock_releasestatus; + u8 coreblock_hwsupported; + u8 coreblock_integratedhw; + u8 coreblock_biosbuild[4]; + u8 coreblock_biossignon[155]; +} __attribute__((packed)) optionrom_vbt_t; + +#define VBT_SIGNATURE 0x54425624 + diff --git a/src/northbridge/intel/haswell/haswell.h b/src/northbridge/intel/haswell/haswell.h new file mode 100644 index 0000000000..967a186e81 --- /dev/null +++ b/src/northbridge/intel/haswell/haswell.h @@ -0,0 +1,243 @@ +/* + * 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 + +/* Device ID for SandyBridge and IvyBridge */ +#define BASE_REV_SNB 0x00 +#define BASE_REV_IVB 0x50 +#define BASE_REV_MASK 0x50 + +/* SandyBridge CPU stepping */ +#define SNB_STEP_D0 (BASE_REV_SNB + 5) /* Also J0 */ +#define SNB_STEP_D1 (BASE_REV_SNB + 6) +#define SNB_STEP_D2 (BASE_REV_SNB + 7) /* Also J1/Q0 */ + +/* IvyBridge CPU stepping */ +#define IVB_STEP_A0 (BASE_REV_IVB + 0) +#define IVB_STEP_B0 (BASE_REV_IVB + 2) +#define IVB_STEP_C0 (BASE_REV_IVB + 4) +#define IVB_STEP_K0 (BASE_REV_IVB + 5) +#define IVB_STEP_D0 (BASE_REV_IVB + 6) + +/* Intel Enhanced Debug region must be 4MB */ +#define IED_SIZE 0x400000 + +/* 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 <southbridge/intel/lynxpoint/pch.h> + +/* 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 X60BAR 0x60 + +#define GGC 0x50 /* GMCH Graphics Control */ + +#define DEVEN 0x54 /* Device Enable */ +#define DEVEN_PEG60 (1 << 13) +#define DEVEN_IGD (1 << 4) +#define DEVEN_PEG10 (1 << 3) +#define DEVEN_PEG11 (1 << 2) +#define DEVEN_PEG12 (1 << 1) +#define DEVEN_HOST (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 TOM 0xa0 +#define TOUUD 0xa8 /* Top of Upper Usable DRAM */ +#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 SSKPD 0x5d14 /* 16bit (scratchpad) */ +#define BIOS_RESET_CPL 0x5da8 /* 8bit */ + +/* + * 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_SB 0x0104 +#define PCI_DEVICE_ID_IB 0x0154 + +#ifdef __SMM__ +void intel_northbridge_haswell_finalize_smm(void); +#else /* !__SMM__ */ +int bridge_silicon_revision(void); +void haswell_early_initialization(int chipset_type); +void haswell_late_initialization(void); + +/* 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__) +void update_mrc_cache(void); + +#include "gma.h" +int init_igd_opregion(igd_opregion_t *igd_opregion); +#endif + +#endif +#endif +#endif diff --git a/src/northbridge/intel/haswell/mrccache.c b/src/northbridge/intel/haswell/mrccache.c new file mode 100644 index 0000000000..032bae4131 --- /dev/null +++ b/src/northbridge/intel/haswell/mrccache.c @@ -0,0 +1,245 @@ +/* + * 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 <stdint.h> +#include <string.h> +#include <console/console.h> +#include <cbfs.h> +#include <ip_checksum.h> +#include <device/device.h> +#include <cbmem.h> +#include "pei_data.h" +#include "haswell.h" +#include <spi-generic.h> +#include <spi_flash.h> +#if CONFIG_CHROMEOS +#include <vendorcode/google/chromeos/fmap.h> +#endif + +/* convert a pointer to flash area into the offset inside the flash */ +static inline u32 to_flash_offset(void *p) { + return ((u32)p + CONFIG_ROM_SIZE); +} + +static struct mrc_data_container *next_mrc_block( + struct mrc_data_container *mrc_cache) +{ + /* MRC data blocks are aligned within the region */ + u32 mrc_size = sizeof(*mrc_cache) + mrc_cache->mrc_data_size; + if (mrc_size & (MRC_DATA_ALIGN - 1UL)) { + mrc_size &= ~(MRC_DATA_ALIGN - 1UL); + mrc_size += MRC_DATA_ALIGN; + } + + u8 *region_ptr = (u8*)mrc_cache; + region_ptr += mrc_size; + return (struct mrc_data_container *)region_ptr; +} + +static int is_mrc_cache(struct mrc_data_container *mrc_cache) +{ + return (!!mrc_cache) && (mrc_cache->mrc_signature == MRC_DATA_SIGNATURE); +} + +/* Right now, the offsets for the MRC cache area are hard-coded in the + * northbridge Kconfig if CONFIG_CHROMEOS is not set. In order to make + * this more flexible, there are two of options: + * - Have each mainboard Kconfig supply a hard-coded offset + * - Use CBFS + */ +static u32 get_mrc_cache_region(struct mrc_data_container **mrc_region_ptr) +{ + u32 region_size; +#if CONFIG_CHROMEOS + region_size = find_fmap_entry("RW_MRC_CACHE", (void **)mrc_region_ptr); +#else + region_size = CONFIG_MRC_CACHE_SIZE; + *mrc_region_ptr = (struct mrc_data_container *) + (CONFIG_MRC_CACHE_BASE + CONFIG_MRC_CACHE_LOCATION); +#endif + + return region_size; +} + +/* + * Find the largest index block in the MRC cache. Return NULL if non is + * found. + */ +static struct mrc_data_container *find_current_mrc_cache_local + (struct mrc_data_container *mrc_cache, u32 region_size) +{ + u32 region_end; + u32 entry_id = 0; + struct mrc_data_container *mrc_next = mrc_cache; + + region_end = (u32) mrc_cache + region_size; + + /* Search for the last filled entry in the region */ + while (is_mrc_cache(mrc_next)) { + entry_id++; + mrc_cache = mrc_next; + mrc_next = next_mrc_block(mrc_next); + if ((u32)mrc_next >= region_end) { + /* Stay in the MRC data region */ + break; + } + } + + if (entry_id == 0) { + printk(BIOS_ERR, "%s: No valid MRC cache found.\n", __func__); + return NULL; + } + + /* Verify checksum */ + if (mrc_cache->mrc_checksum != + compute_ip_checksum(mrc_cache->mrc_data, + mrc_cache->mrc_data_size)) { + printk(BIOS_ERR, "%s: MRC cache checksum mismatch\n", __func__); + return NULL; + } + + printk(BIOS_DEBUG, "%s: picked entry %u from cache block\n", __func__, + entry_id - 1); + + return mrc_cache; +} + +/* SPI code needs malloc/free. + * Also unknown if writing flash from XIP-flash code is a good idea + */ +#if !defined(__PRE_RAM__) +/* find the first empty block in the MRC cache area. + * If there's none, return NULL. + * + * @mrc_cache_base - base address of the MRC cache area + * @mrc_cache - current entry (for which we need to find next) + * @region_size - total size of the MRC cache area + */ +static struct mrc_data_container *find_next_mrc_cache + (struct mrc_data_container *mrc_cache_base, + struct mrc_data_container *mrc_cache, + u32 region_size) +{ + u32 region_end = (u32) mrc_cache_base + region_size; + + mrc_cache = next_mrc_block(mrc_cache); + if ((u32)mrc_cache >= region_end) { + /* Crossed the boundary */ + mrc_cache = NULL; + printk(BIOS_DEBUG, "%s: no available entries found\n", + __func__); + } else { + printk(BIOS_DEBUG, + "%s: picked next entry from cache block at %p\n", + __func__, mrc_cache); + } + + return mrc_cache; +} + +void update_mrc_cache(void) +{ + printk(BIOS_DEBUG, "Updating MRC cache data.\n"); + struct mrc_data_container *current = cbmem_find(CBMEM_ID_MRCDATA); + struct mrc_data_container *cache, *cache_base; + u32 cache_size; + + if (!current) { + printk(BIOS_ERR, "No MRC cache in cbmem. Can't update flash.\n"); + return; + } + if (current->mrc_data_size == -1) { + printk(BIOS_ERR, "MRC cache data in cbmem invalid.\n"); + return; + } + + cache_size = get_mrc_cache_region(&cache_base); + if (cache_base == NULL) { + printk(BIOS_ERR, "%s: could not find MRC cache area\n", + __func__); + return; + } + + /* + * we need to: + */ + // 0. compare MRC data to last mrc-cache block (exit if same) + cache = find_current_mrc_cache_local(cache_base, cache_size); + + if (cache && (cache->mrc_data_size == current->mrc_data_size) && + (memcmp(cache, current, cache->mrc_data_size) == 0)) { + printk(BIOS_DEBUG, + "MRC data in flash is up to date. No update.\n"); + return; + } + + // 1. use spi_flash_probe() to find the flash, then + spi_init(); + struct spi_flash *flash = spi_flash_probe(0, 0, 1000000, SPI_MODE_3); + if (!flash) { + printk(BIOS_DEBUG, "Could not find SPI device\n"); + return; + } + + // 2. look up the first unused block + if (cache) + cache = find_next_mrc_cache(cache_base, cache, cache_size); + + /* + * 3. if no such place exists, erase entire mrc-cache range & use + * block 0. First time around the erase is not needed, but this is a + * small overhead for simpler code. + */ + if (!cache) { + printk(BIOS_DEBUG, + "Need to erase the MRC cache region of %d bytes at %p\n", + cache_size, cache_base); + + flash->erase(flash, to_flash_offset(cache_base), cache_size); + + /* we will start at the beginning again */ + cache = cache_base; + } + // 4. write mrc data with flash->write() + printk(BIOS_DEBUG, "Finally: write MRC cache update to flash at %p\n", + cache); + flash->write(flash, to_flash_offset(cache), + current->mrc_data_size + sizeof(*current), current); +} +#endif + +struct mrc_data_container *find_current_mrc_cache(void) +{ + struct mrc_data_container *cache_base; + u32 cache_size; + + cache_size = get_mrc_cache_region(&cache_base); + if (cache_base == NULL) { + printk(BIOS_ERR, "%s: could not find MRC cache area\n", + __func__); + return NULL; + } + + /* + * we need to: + */ + // 0. compare MRC data to last mrc-cache block (exit if same) + return find_current_mrc_cache_local(cache_base, cache_size); +} + diff --git a/src/northbridge/intel/haswell/northbridge.c b/src/northbridge/intel/haswell/northbridge.c new file mode 100644 index 0000000000..1c6a8fce8b --- /dev/null +++ b/src/northbridge/intel/haswell/northbridge.c @@ -0,0 +1,512 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2007-2009 coresystems GmbH + * Copyright (C) 2011 The ChromiumOS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <console/console.h> +#include <arch/acpi.h> +#include <arch/io.h> +#include <stdint.h> +#include <delay.h> +#include <cpu/intel/haswell/haswell.h> +#include <cpu/x86/msr.h> +#include <device/device.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include <device/hypertransport.h> +#include <stdlib.h> +#include <string.h> +#include <cpu/cpu.h> +#include <boot/tables.h> +#include <cbmem.h> +#include "chip.h" +#include "haswell.h" + +static int bridge_revision_id = -1; + +int bridge_silicon_revision(void) +{ + if (bridge_revision_id < 0) { + uint8_t stepping = cpuid_eax(1) & 0xf; + uint8_t bridge_id = pci_read_config16( + dev_find_slot(0, PCI_DEVFN(0, 0)), + PCI_DEVICE_ID) & 0xf0; + bridge_revision_id = bridge_id | stepping; + } + return bridge_revision_id; +} + +/* Reserve everything between A segment and 1MB: + * + * 0xa0000 - 0xbffff: legacy VGA + * 0xc0000 - 0xcffff: VGA OPROM (needed by kernel) + * 0xe0000 - 0xfffff: SeaBIOS, if used, otherwise DMI + */ +static const int legacy_hole_base_k = 0xa0000 / 1024; +static const int legacy_hole_size_k = 384; + +void cbmem_post_handling(void) +{ + update_mrc_cache(); +} + +static int get_pcie_bar(u32 *base, u32 *len) +{ + device_t dev; + u32 pciexbar_reg; + + *base = 0; + *len = 0; + + dev = dev_find_slot(0, PCI_DEVFN(0, 0)); + if (!dev) + return 0; + + pciexbar_reg = pci_read_config32(dev, PCIEXBAR); + + 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 add_fixed_resources(struct device *dev, int index) +{ + struct resource *resource; + u32 pcie_config_base, pcie_config_size; + + printk(BIOS_DEBUG, "Adding UMA memory area base=0x%llx " + "size=0x%llx\n", uma_memory_base, uma_memory_size); + resource = new_resource(dev, index); + resource->base = (resource_t) uma_memory_base; + resource->size = (resource_t) uma_memory_size; + resource->flags = IORESOURCE_MEM | IORESOURCE_RESERVE | + IORESOURCE_FIXED | IORESOURCE_STORED | IORESOURCE_ASSIGNED; + + /* Clear these values here so they don't get used by MTRR code */ + uma_memory_base = 0; + uma_memory_size = 0; + + if (get_pcie_bar(&pcie_config_base, &pcie_config_size)) { + printk(BIOS_DEBUG, "Adding PCIe config bar base=0x%08x " + "size=0x%x\n", pcie_config_base, pcie_config_size); + resource = new_resource(dev, index+1); + resource->base = (resource_t) pcie_config_base; + resource->size = (resource_t) pcie_config_size; + resource->flags = IORESOURCE_MEM | IORESOURCE_RESERVE | + IORESOURCE_FIXED | IORESOURCE_STORED | IORESOURCE_ASSIGNED; + } + + mmio_resource(dev, index++, legacy_hole_base_k, legacy_hole_size_k); + +#if CONFIG_CHROMEOS_RAMOOPS + mmio_resource(dev, index++, CONFIG_CHROMEOS_RAMOOPS_RAM_START >> 10, + CONFIG_CHROMEOS_RAMOOPS_RAM_SIZE >> 10); +#endif + + /* Required for SandyBridge sighting 3715511 */ + bad_ram_resource(dev, index++, 0x20000000 >> 10, 0x00200000 >> 10); + bad_ram_resource(dev, index++, 0x40000000 >> 10, 0x00200000 >> 10); +} + +static void pci_domain_set_resources(device_t dev) +{ + uint64_t tom, me_base, touud; + uint32_t tseg_base, uma_size, tolud; + uint16_t ggc; + unsigned long long tomk; + + /* Total Memory 2GB example: + * + * 00000000 0000MB-1992MB 1992MB RAM (writeback) + * 7c800000 1992MB-2000MB 8MB TSEG (SMRR) + * 7d000000 2000MB-2002MB 2MB GFX GTT (uncached) + * 7d200000 2002MB-2034MB 32MB GFX UMA (uncached) + * 7f200000 2034MB TOLUD + * 7f800000 2040MB MEBASE + * 7f800000 2040MB-2048MB 8MB ME UMA (uncached) + * 80000000 2048MB TOM + * 100000000 4096MB-4102MB 6MB RAM (writeback) + * + * Total Memory 4GB example: + * + * 00000000 0000MB-2768MB 2768MB RAM (writeback) + * ad000000 2768MB-2776MB 8MB TSEG (SMRR) + * ad800000 2776MB-2778MB 2MB GFX GTT (uncached) + * ada00000 2778MB-2810MB 32MB GFX UMA (uncached) + * afa00000 2810MB TOLUD + * ff800000 4088MB MEBASE + * ff800000 4088MB-4096MB 8MB ME UMA (uncached) + * 100000000 4096MB TOM + * 100000000 4096MB-5374MB 1278MB RAM (writeback) + * 14fe00000 5368MB TOUUD + */ + + /* Top of Upper Usable DRAM, including remap */ + touud = pci_read_config32(dev, TOUUD+4); + touud <<= 32; + touud |= pci_read_config32(dev, TOUUD); + + /* Top of Lower Usable DRAM */ + tolud = pci_read_config32(dev, TOLUD); + + /* Top of Memory - does not account for any UMA */ + tom = pci_read_config32(dev, 0xa4); + tom <<= 32; + tom |= pci_read_config32(dev, 0xa0); + + printk(BIOS_DEBUG, "TOUUD 0x%llx TOLUD 0x%08x TOM 0x%llx\n", + touud, tolud, tom); + + /* ME UMA needs excluding if total memory <4GB */ + me_base = pci_read_config32(dev, 0x74); + me_base <<= 32; + me_base |= pci_read_config32(dev, 0x70); + + printk(BIOS_DEBUG, "MEBASE 0x%llx\n", me_base); + + tomk = tolud >> 10; + if (me_base == tolud) { + /* ME is from MEBASE-TOM */ + uma_size = (tom - me_base) >> 10; + /* Increment TOLUD to account for ME as RAM */ + tolud += uma_size << 10; + /* UMA starts at old TOLUD */ + uma_memory_base = tomk * 1024ULL; + uma_memory_size = uma_size * 1024ULL; + printk(BIOS_DEBUG, "ME UMA base 0x%llx size %uM\n", + me_base, uma_size >> 10); + } + + /* Graphics memory comes next */ + ggc = pci_read_config16(dev, GGC); + if (!(ggc & 2)) { + printk(BIOS_DEBUG, "IGD decoded, subtracting "); + + /* Graphics memory */ + uma_size = ((ggc >> 3) & 0x1f) * 32 * 1024ULL; + printk(BIOS_DEBUG, "%uM UMA", uma_size >> 10); + tomk -= uma_size; + uma_memory_base = tomk * 1024ULL; + uma_memory_size += uma_size * 1024ULL; + + /* GTT Graphics Stolen Memory Size (GGMS) */ + uma_size = ((ggc >> 8) & 0x3) * 1024ULL; + tomk -= uma_size; + uma_memory_base = tomk * 1024ULL; + uma_memory_size += uma_size * 1024ULL; + printk(BIOS_DEBUG, " and %uM GTT\n", uma_size >> 10); + } + + /* Calculate TSEG size from its base which must be below GTT */ + tseg_base = pci_read_config32(dev, 0xb8); + uma_size = (uma_memory_base - tseg_base) >> 10; + tomk -= uma_size; + uma_memory_base = tomk * 1024ULL; + uma_memory_size += uma_size * 1024ULL; + printk(BIOS_DEBUG, "TSEG base 0x%08x size %uM\n", + tseg_base, uma_size >> 10); + + printk(BIOS_INFO, "Available memory below 4GB: %lluM\n", tomk >> 10); + + /* Report the memory regions */ + ram_resource(dev, 3, 0, legacy_hole_base_k); + ram_resource(dev, 4, legacy_hole_base_k + legacy_hole_size_k, + (tomk - (legacy_hole_base_k + legacy_hole_size_k))); + + /* + * If >= 4GB installed then memory from TOLUD to 4GB + * is remapped above TOM, TOUUD will account for both + */ + touud >>= 10; /* Convert to KB */ + if (touud > 4096 * 1024) { + ram_resource(dev, 5, 4096 * 1024, touud - (4096 * 1024)); + printk(BIOS_INFO, "Available memory above 4GB: %lluM\n", + (touud >> 10) - 4096); + } + + add_fixed_resources(dev, 6); + + assign_resources(dev->link_list); + + /* Leave some space for ACPI, PIRQ and MP tables */ + high_tables_base = (tomk * 1024) - HIGH_MEMORY_SIZE; + high_tables_size = HIGH_MEMORY_SIZE; +} + + /* 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 void mc_read_resources(device_t dev) +{ + struct resource *resource; + + pci_dev_read_resources(dev); + + /* So, this is one of the big mysteries in the coreboot resource + * allocator. This resource should make sure that the address space + * of the PCIe memory mapped config space bar. But it does not. + */ + + /* We use 0xcf as an unused index for our PCIe bar so that we find it again */ + resource = new_resource(dev, 0xcf); + resource->base = DEFAULT_PCIEXBAR; + resource->size = 64 * 1024 * 1024; /* 64MB hard coded PCIe config space */ + resource->flags = + IORESOURCE_MEM | IORESOURCE_FIXED | IORESOURCE_STORED | + IORESOURCE_ASSIGNED; + printk(BIOS_DEBUG, "Adding PCIe enhanced config space BAR 0x%08lx-0x%08lx.\n", + (unsigned long)(resource->base), (unsigned long)(resource->base + resource->size)); +} + +static void mc_set_resources(device_t dev) +{ + struct resource *resource; + + /* Report the PCIe BAR */ + resource = find_resource(dev, 0xcf); + if (resource) { + report_resource_stored(dev, resource, "<mmconfig>"); + } + + /* And call the normal set_resources */ + pci_dev_set_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_dmi_init(struct device *dev) +{ + u32 reg32; + + /* Clear error status bits */ + DMIBAR32(0x1c4) = 0xffffffff; + DMIBAR32(0x1d0) = 0xffffffff; + + /* Steps prior to DMI ASPM */ + if ((bridge_silicon_revision() & BASE_REV_MASK) == BASE_REV_SNB) { + reg32 = DMIBAR32(0x250); + reg32 &= ~((1 << 22)|(1 << 20)); + reg32 |= (1 << 21); + DMIBAR32(0x250) = reg32; + } + + reg32 = DMIBAR32(0x238); + reg32 |= (1 << 29); + DMIBAR32(0x238) = reg32; + + if (bridge_silicon_revision() >= SNB_STEP_D0) { + reg32 = DMIBAR32(0x1f8); + reg32 |= (1 << 16); + DMIBAR32(0x1f8) = reg32; + } else if (bridge_silicon_revision() >= SNB_STEP_D1) { + reg32 = DMIBAR32(0x1f8); + reg32 &= ~(1 << 26); + reg32 |= (1 << 16); + DMIBAR32(0x1f8) = reg32; + + reg32 = DMIBAR32(0x1fc); + reg32 |= (1 << 12) | (1 << 23); + DMIBAR32(0x1fc) = reg32; + } + + /* Enable ASPM on SNB link, should happen before PCH link */ + if ((bridge_silicon_revision() & BASE_REV_MASK) == BASE_REV_SNB) { + reg32 = DMIBAR32(0xd04); + reg32 |= (1 << 4); + DMIBAR32(0xd04) = reg32; + } + + reg32 = DMIBAR32(0x88); + reg32 |= (1 << 1) | (1 << 0); + DMIBAR32(0x88) = reg32; +} + +static void northbridge_init(struct device *dev) +{ + u8 bios_reset_cpl; + u32 bridge_type; + + northbridge_dmi_init(dev); + + bridge_type = MCHBAR32(0x5f10); + bridge_type &= ~0xff; + + if ((bridge_silicon_revision() & BASE_REV_MASK) == BASE_REV_IVB) { + /* Enable Power Aware Interrupt Routing */ + u8 pair = MCHBAR8(0x5418); + pair &= ~0xf; /* Clear 3:0 */ + pair |= 0x4; /* Fixed Priority */ + MCHBAR8(0x5418) = pair; + + /* 30h for IvyBridge */ + bridge_type |= 0x30; + } else { + /* 20h for Sandybridge */ + bridge_type |= 0x20; + } + MCHBAR32(0x5f10) = bridge_type; + + /* + * Set bit 0 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 |= 1; + 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); + + /* + * CPUs with configurable TDP also need power limits set + * in MCHBAR. Use same values from MSR_PKG_POWER_LIMIT. + */ + if (cpu_config_tdp_levels()) { + msr_t msr = rdmsr(MSR_PKG_POWER_LIMIT); + MCHBAR32(0x59A0) = msr.lo; + MCHBAR32(0x59A4) = msr.hi; + } + + /* Set here before graphics PM init */ + MCHBAR32(0x5500) = 0x00100001; +} + +static void northbridge_enable(device_t dev) +{ +#if CONFIG_HAVE_ACPI_RESUME + switch (pci_read_config32(dev, SKPAD)) { + case 0xcafebabe: + printk(BIOS_DEBUG, "Normal boot.\n"); + acpi_slp_type=0; + break; + case 0xcafed00d: + printk(BIOS_DEBUG, "S3 Resume.\n"); + acpi_slp_type=3; + break; + default: + printk(BIOS_DEBUG, "Unknown boot method, assuming normal.\n"); + acpi_slp_type=0; + break; + } +#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 = mc_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_0100 __pci_driver = { + .ops = &mc_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .device = 0x0100, +}; + +static const struct pci_driver mc_driver __pci_driver = { + .ops = &mc_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .device = 0x0104, /* Sandy bridge */ +}; + +static const struct pci_driver mc_driver_1 __pci_driver = { + .ops = &mc_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .device = 0x0154, /* Ivy bridge */ +}; + +static void cpu_bus_init(device_t dev) +{ + initialize_cpus(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/northbridge/intel/haswell/pcie_config.c b/src/northbridge/intel/haswell/pcie_config.c new file mode 100644 index 0000000000..d96854e739 --- /dev/null +++ b/src/northbridge/intel/haswell/pcie_config.c @@ -0,0 +1,89 @@ +/* + * 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 + */ + +#include "haswell.h" + +static inline __attribute__ ((always_inline)) +u8 pcie_read_config8(device_t dev, unsigned int where) +{ + unsigned long addr; + addr = DEFAULT_PCIEXBAR | dev | where; + return read8(addr); +} + +static inline __attribute__ ((always_inline)) +u16 pcie_read_config16(device_t dev, unsigned int where) +{ + unsigned long addr; + addr = DEFAULT_PCIEXBAR | dev | where; + return read16(addr); +} + +static inline __attribute__ ((always_inline)) +u32 pcie_read_config32(device_t dev, unsigned int where) +{ + unsigned long addr; + addr = DEFAULT_PCIEXBAR | dev | where; + return read32(addr); +} + +static inline __attribute__ ((always_inline)) +void pcie_write_config8(device_t dev, unsigned int where, u8 value) +{ + unsigned long addr; + addr = DEFAULT_PCIEXBAR | dev | where; + write8(addr, value); +} + +static inline __attribute__ ((always_inline)) +void pcie_write_config16(device_t dev, unsigned int where, u16 value) +{ + unsigned long addr; + addr = DEFAULT_PCIEXBAR | dev | where; + write16(addr, value); +} + +static inline __attribute__ ((always_inline)) +void pcie_write_config32(device_t dev, unsigned int where, u32 value) +{ + unsigned long addr; + addr = DEFAULT_PCIEXBAR | dev | where; + write32(addr, value); +} + +static inline __attribute__ ((always_inline)) +void pcie_or_config8(device_t dev, unsigned int where, u8 ormask) +{ + u8 value = pcie_read_config8(dev, where); + pcie_write_config8(dev, where, value | ormask); +} + +static inline __attribute__ ((always_inline)) +void pcie_or_config16(device_t dev, unsigned int where, u16 ormask) +{ + u16 value = pcie_read_config16(dev, where); + pcie_write_config16(dev, where, value | ormask); +} + +static inline __attribute__ ((always_inline)) +void pcie_or_config32(device_t dev, unsigned int where, u32 ormask) +{ + u32 value = pcie_read_config32(dev, where); + pcie_write_config32(dev, where, value | ormask); +} diff --git a/src/northbridge/intel/haswell/pei_data.h b/src/northbridge/intel/haswell/pei_data.h new file mode 100644 index 0000000000..8c907c1db8 --- /dev/null +++ b/src/northbridge/intel/haswell/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 4 +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 thermalbase; + uint32_t system_type; // 0 Mobile, 1 Desktop/Server + uint32_t tseg_size; + uint8_t spd_addresses[4]; + uint8_t ts_addresses[4]; + int boot_mode; + int ec_present; + // 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; + /* Seed values saved in CMOS */ + uint32_t scrambler_seed; + uint32_t scrambler_seed_s3; + /* 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; + /* + * USB Port Configuration: + * [0] = enable + * [1] = overcurrent pin + * [2] = length + * + * Ports 0-7 can be mapped to OC0-OC3 + * Ports 8-13 can be mapped to OC4-OC7 + * + * Port Length + * MOBILE: + * < 0x050 = Setting 1 (back panel, 1-5in, lowest tx amplitude) + * < 0x140 = Setting 2 (back panel, 5-14in, highest tx amplitude) + * DESKTOP: + * < 0x080 = Setting 1 (front/back panel, <8in, lowest tx amplitude) + * < 0x130 = Setting 2 (back panel, 8-13in, higher tx amplitude) + * < 0x150 = Setting 3 (back panel, 13-15in, higest tx amplitude) + */ + uint16_t usb_port_config[16][3]; + /* SPD data array for onboard RAM. Specify address 0xf0, + * 0xf1, 0xf2, 0xf3 to index one of the 4 slots in + * spd_address for a given "DIMM". + */ + uint8_t spd_data[4][256]; + tx_byte_func tx_byte; + int ddr3lv_support; + /* pcie_init needs to be set to 1 to have the system agent initialize + * PCIe. Note: This should only be required if your system has Gen3 devices + * and it will increase your boot time by at least 100ms. + */ + int pcie_init; + /* N mode functionality. Leave this setting at 0. + * 0 Auto + * 1 1N + * 2 2N + */ + int nmode; +} __attribute__((packed)); + +#endif diff --git a/src/northbridge/intel/haswell/raminit.c b/src/northbridge/intel/haswell/raminit.c new file mode 100644 index 0000000000..b45682596e --- /dev/null +++ b/src/northbridge/intel/haswell/raminit.c @@ -0,0 +1,307 @@ +/* + * 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 <console/console.h> +#include <string.h> +#include <arch/hlt.h> +#include <arch/io.h> +#include <arch/romcc_io.h> +#include <cbmem.h> +#include <arch/cbfs.h> +#include <cbfs.h> +#include <ip_checksum.h> +#include <pc80/mc146818rtc.h> +#include <device/pci_def.h> +#include "raminit.h" +#include "pei_data.h" +#include "haswell.h" + +/* Management Engine is in the southbridge */ +#include "southbridge/intel/lynxpoint/me.h" +#if CONFIG_CHROMEOS +#include <vendorcode/google/chromeos/chromeos.h> +#else +#define recovery_mode_enabled(x) 0 +#endif + +/* + * MRC scrambler seed offsets should be reserved in + * mainboard cmos.layout and not covered by checksum. + */ +#if CONFIG_USE_OPTION_TABLE +#include "option_table.h" +#define CMOS_OFFSET_MRC_SEED (CMOS_VSTART_mrc_scrambler_seed >> 3) +#define CMOS_OFFSET_MRC_SEED_S3 (CMOS_VSTART_mrc_scrambler_seed_s3 >> 3) +#define CMOS_OFFSET_MRC_SEED_CHK (CMOS_VSTART_mrc_scrambler_seed_chk >> 3) +#else +#define CMOS_OFFSET_MRC_SEED 152 +#define CMOS_OFFSET_MRC_SEED_S3 156 +#define CMOS_OFFSET_MRC_SEED_CHK 160 +#endif + +static void save_mrc_data(struct pei_data *pei_data) +{ + u16 c1, c2, checksum; + +#if CONFIG_EARLY_CBMEM_INIT + struct mrc_data_container *mrcdata; + int output_len = ALIGN(pei_data->mrc_output_len, 16); + + /* Save the MRC S3 restore data to cbmem */ + cbmem_initialize(); + 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); +#endif + + /* Save the MRC seed values to CMOS */ + cmos_write32(CMOS_OFFSET_MRC_SEED, pei_data->scrambler_seed); + printk(BIOS_DEBUG, "Save scrambler seed 0x%08x to CMOS 0x%02x\n", + pei_data->scrambler_seed, CMOS_OFFSET_MRC_SEED); + + cmos_write32(CMOS_OFFSET_MRC_SEED_S3, pei_data->scrambler_seed_s3); + printk(BIOS_DEBUG, "Save s3 scrambler seed 0x%08x to CMOS 0x%02x\n", + pei_data->scrambler_seed_s3, CMOS_OFFSET_MRC_SEED_S3); + + /* Save a simple checksum of the seed values */ + c1 = compute_ip_checksum((u8*)&pei_data->scrambler_seed, + sizeof(u32)); + c2 = compute_ip_checksum((u8*)&pei_data->scrambler_seed_s3, + sizeof(u32)); + checksum = add_ip_checksums(sizeof(u32), c1, c2); + + cmos_write(checksum & 0xff, CMOS_OFFSET_MRC_SEED_CHK); + cmos_write((checksum >> 8) & 0xff, CMOS_OFFSET_MRC_SEED_CHK+1); +} + +static void prepare_mrc_cache(struct pei_data *pei_data) +{ + struct mrc_data_container *mrc_cache; + u16 c1, c2, checksum, seed_checksum; + + // preset just in case there is an error + pei_data->mrc_input = NULL; + pei_data->mrc_input_len = 0; + + /* Read scrambler seeds from CMOS */ + pei_data->scrambler_seed = cmos_read32(CMOS_OFFSET_MRC_SEED); + printk(BIOS_DEBUG, "Read scrambler seed 0x%08x from CMOS 0x%02x\n", + pei_data->scrambler_seed, CMOS_OFFSET_MRC_SEED); + + pei_data->scrambler_seed_s3 = cmos_read32(CMOS_OFFSET_MRC_SEED_S3); + printk(BIOS_DEBUG, "Read S3 scrambler seed 0x%08x from CMOS 0x%02x\n", + pei_data->scrambler_seed_s3, CMOS_OFFSET_MRC_SEED_S3); + + /* Compute seed checksum and compare */ + c1 = compute_ip_checksum((u8*)&pei_data->scrambler_seed, + sizeof(u32)); + c2 = compute_ip_checksum((u8*)&pei_data->scrambler_seed_s3, + sizeof(u32)); + checksum = add_ip_checksums(sizeof(u32), c1, c2); + + seed_checksum = cmos_read(CMOS_OFFSET_MRC_SEED_CHK); + seed_checksum |= cmos_read(CMOS_OFFSET_MRC_SEED_CHK+1) << 8; + + if (checksum != seed_checksum) { + printk(BIOS_ERR, "%s: invalid seed checksum\n", __func__); + pei_data->scrambler_seed = 0; + pei_data->scrambler_seed_s3 = 0; + return; + } + + 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 x%d %s rank%s\n", + ((ch_conf >> 0) & 0xff) * 256, + ((ch_conf >> 19) & 1) ? 16 : 8, + ((ch_conf >> 17) & 1) ? "dual" : "single", + ((ch_conf >> 16) & 1) ? "" : ", selected"); + printk(BIOS_DEBUG, " DIMMB %d MB width x%d %s rank%s\n", + ((ch_conf >> 8) & 0xff) * 256, + ((ch_conf >> 20) & 1) ? 16 : 8, + ((ch_conf >> 18) & 1) ? "dual" : "single", + ((ch_conf >> 16) & 1) ? ", selected" : ""); + } +} + +/** + * 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) +{ + struct sys_info sysinfo; + unsigned long entry; + + report_platform_info(); + + /* Wait for ME to be ready */ + intel_early_me_init(); + intel_early_me_uma_size(); + + printk(BIOS_DEBUG, "Starting UEFI PEI System Agent\n"); + + memset(&sysinfo, 0, sizeof(sysinfo)); + + sysinfo.boot_path = pei_data->boot_mode; + + /* + * 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) { + 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); + + /* Send ME init done for SandyBridge here. This is done + * inside the SystemAgent binary on IvyBridge. */ + if (BASE_REV_SNB == + (pci_read_config16(PCI_CPU_DEVICE, PCI_DEVICE_ID) & BASE_REV_MASK)) + intel_early_me_init_done(ME_INIT_STATUS_SUCCESS); + else + intel_early_me_status(); + + report_memory_config(); + + /* S3 resume: don't save scrambler seed or MRC data */ + if (pei_data->boot_mode != 2) + save_mrc_data(pei_data); +} + +struct cbmem_entry *get_cbmem_toc(void) +{ + return (struct cbmem_entry *)(get_top_of_ram() - HIGH_MEMORY_SIZE); +} + +unsigned long get_top_of_ram(void) +{ + /* Base of TSEG is top of usable DRAM */ + u32 tom = pci_read_config32(PCI_DEV(0,0,0), TSEG); + return (unsigned long) tom; +} diff --git a/src/northbridge/intel/haswell/raminit.h b/src/northbridge/intel/haswell/raminit.h new file mode 100644 index 0000000000..958e983243 --- /dev/null +++ b/src/northbridge/intel/haswell/raminit.h @@ -0,0 +1,36 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2010 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 RAMINIT_H +#define RAMINIT_H + +#include "pei_data.h" + +struct sys_info { + u8 boot_path; +#define BOOT_PATH_NORMAL 0 +#define BOOT_PATH_RESET 1 +#define BOOT_PATH_RESUME 2 +} __attribute__ ((packed)); + +void sdram_initialize(struct pei_data *pei_data); +unsigned long get_top_of_ram(void); +int fixup_haswell_errata(void); + +#endif /* RAMINIT_H */ diff --git a/src/northbridge/intel/haswell/report_platform.c b/src/northbridge/intel/haswell/report_platform.c new file mode 100644 index 0000000000..c65bbe5faa --- /dev/null +++ b/src/northbridge/intel/haswell/report_platform.c @@ -0,0 +1,112 @@ +/* + * 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 <console/console.h> +#include <arch/cpu.h> +#include <string.h> +#include "southbridge/intel/lynxpoint/pch.h" +#include <arch/io.h> +#include <arch/io.h> +#include <cpu/x86/msr.h> +#include <arch/romcc_io.h> +#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"}, + {0x8c46, "Z87"}, + {0x8c49, "Z85"}, + {0x8c4a, "HM86"}, + {0x8c4b, "H87"}, + {0x8c4c, "Q85"}, + {0x8c4e, "Q87"}, + {0x8c4f, "QM87"}, + {0x8c50, "B85"}, + {0x8c52, "C222"}, + {0x8c54, "C224"}, + {0x8c56, "C226"}, + {0x8c5c, "H81"}, +}; + +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/northbridge/intel/haswell/udelay.c b/src/northbridge/intel/haswell/udelay.c new file mode 100644 index 0000000000..864e83915c --- /dev/null +++ b/src/northbridge/intel/haswell/udelay.c @@ -0,0 +1,66 @@ +/* + * 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 + */ + +#include <delay.h> +#include <stdint.h> +#include <cpu/x86/tsc.h> +#include <cpu/x86/msr.h> + +/** + * Intel SandyBridge/IvyBridge CPUs always run the TSC at BCLK=100MHz + */ + +/* Simple 32- to 64-bit multiplication. Uses 16-bit words to avoid overflow. */ +static inline void multiply_to_tsc(tsc_t *const tsc, const u32 a, const u32 b) +{ + tsc->lo = (a & 0xffff) * (b & 0xffff); + tsc->hi = ((tsc->lo >> 16) + + ((a & 0xffff) * (b >> 16)) + + ((b & 0xffff) * (a >> 16))); + tsc->lo = ((tsc->hi & 0xffff) << 16) | (tsc->lo & 0xffff); + tsc->hi = ((a >> 16) * (b >> 16)) + (tsc->hi >> 16); +} + +void udelay(u32 us) +{ + u32 dword; + tsc_t tsc, tsc1, tscd; + msr_t msr; + u32 fsb = 100, divisor; + u32 d; /* ticks per us */ + + msr = rdmsr(0xce); + divisor = (msr.lo >> 8) & 0xff; + + d = fsb * divisor; /* On Core/Core2 this is divided by 4 */ + multiply_to_tsc(&tscd, us, d); + + tsc1 = rdtsc(); + dword = tsc1.lo + tscd.lo; + if ((dword < tsc1.lo) || (dword < tscd.lo)) { + tsc1.hi++; + } + tsc1.lo = dword; + tsc1.hi += tscd.hi; + + do { + tsc = rdtsc(); + } while ((tsc.hi < tsc1.hi) + || ((tsc.hi == tsc1.hi) && (tsc.lo <= tsc1.lo))); +} |