aboutsummaryrefslogtreecommitdiff
path: root/src/northbridge/intel/haswell/early_init.c
diff options
context:
space:
mode:
authorTristan Corrick <tristan@corrick.kiwi>2018-12-17 22:10:21 +1300
committerPatrick Georgi <pgeorgi@google.com>2019-01-03 18:11:54 +0000
commit334be3289d6ca16e806bd1e2aef87637cebb3122 (patch)
treee9e50ce6ab73cd5d146be5aa0f7f25b584ac3c7f /src/northbridge/intel/haswell/early_init.c
parent05b75241565ae91d0b8ee8b45eb2d8ac55291297 (diff)
nb/intel/haswell: Add support for PEG
This means that any PCIe device placed in a PEG slot should now work. During S3 resume, link training sometimes does not complete before device enumeration. However, no tangible issues have been observed. Fixing it would introduce a rather large delay in S3 resume. There are a few minor shortcomings: - Using PEG for display output is not yet supported. - Only PEG2 is supported. An extra (unknown) training sequence is said to be needed for PEG3. - The ACPI _PRT method is not yet generated, so legacy interrupt routing doesn't work for devices with multiple functions. Tested on an ASRock H81M-HDS. Using a Radeon HD 6450 graphics card works under GNU/Linux, with PRIME [1]. An x1 PCIe card was also tested in the PEG slot, and it appears functional. [1]: https://wiki.archlinux.org/index.php/PRIME Change-Id: I786ecb6eccad8de89778af7e736ed664323e220e Signed-off-by: Tristan Corrick <tristan@corrick.kiwi> Reviewed-on: https://review.coreboot.org/c/30272 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
Diffstat (limited to 'src/northbridge/intel/haswell/early_init.c')
-rw-r--r--src/northbridge/intel/haswell/early_init.c70
1 files changed, 67 insertions, 3 deletions
diff --git a/src/northbridge/intel/haswell/early_init.c b/src/northbridge/intel/haswell/early_init.c
index e31bb0e72c..75fc7a240d 100644
--- a/src/northbridge/intel/haswell/early_init.c
+++ b/src/northbridge/intel/haswell/early_init.c
@@ -19,9 +19,12 @@
#include <console/console.h>
#include <arch/io.h>
#include <device/pci_def.h>
+#include <device/pci_ops.h>
#include <elog.h>
#include "haswell.h"
+static bool peg_hidden[3];
+
static void haswell_setup_bars(void)
{
printk(BIOS_DEBUG, "Setting up static northbridge registers...");
@@ -45,13 +48,13 @@ static void haswell_setup_bars(void)
printk(BIOS_DEBUG, " done.\n");
}
-static void haswell_setup_graphics(void)
+static void haswell_setup_igd(void)
{
bool igd_enabled;
u16 ggc;
u8 reg8;
- printk(BIOS_DEBUG, "Initializing Graphics...\n");
+ printk(BIOS_DEBUG, "Initializing IGD...\n");
igd_enabled = !!(pci_read_config32(PCI_DEV(0, 0, 0), DEVEN)
& DEVEN_D2EN);
@@ -79,6 +82,66 @@ static void haswell_setup_graphics(void)
pci_write_config8(PCI_DEV(0, 2, 0), MSAC, reg8);
}
+static void start_peg2_link_training(const pci_devfn_t dev)
+{
+ u32 mask;
+
+ switch (dev) {
+ case PCI_DEV(0, 1, 2):
+ mask = DEVEN_D1F2EN;
+ break;
+ case PCI_DEV(0, 1, 1):
+ mask = DEVEN_D1F1EN;
+ break;
+ case PCI_DEV(0, 1, 0):
+ mask = DEVEN_D1F0EN;
+ break;
+ default:
+ printk(BIOS_ERR, "Link training tried on a non-PEG device!\n");
+ return;
+ }
+
+ pci_update_config32(dev, 0xc24, ~(1 << 16), 1 << 5);
+ printk(BIOS_DEBUG, "Started PEG1%d link training.\n", PCI_FUNC(dev));
+
+ /*
+ * The PEG device is hidden while the MRC runs. This is because the
+ * MRC makes configurations that are not ideal if it sees a VGA
+ * device in a PEG slot, and it locks registers preventing changes
+ * to these configurations.
+ */
+ pci_update_config32(PCI_DEV(0, 0, 0), DEVEN, ~mask, 0);
+ peg_hidden[PCI_FUNC(dev)] = true;
+ printk(BIOS_DEBUG, "Temporarily hiding PEG1%d.\n", PCI_FUNC(dev));
+}
+
+void haswell_unhide_peg(void)
+{
+ u32 deven = pci_read_config32(PCI_DEV(0, 0, 0), DEVEN);
+
+ for (u8 fn = 0; fn <= 2; fn++) {
+ if (peg_hidden[fn]) {
+ deven |= DEVEN_D1F0EN >> fn;
+ peg_hidden[fn] = false;
+ printk(BIOS_DEBUG, "Unhiding PEG1%d.\n", fn);
+ }
+ }
+
+ pci_write_config32(PCI_DEV(0, 0, 0), DEVEN, deven);
+}
+
+static void haswell_setup_peg(void)
+{
+ u32 deven = pci_read_config32(PCI_DEV(0, 0, 0), DEVEN);
+
+ if (deven & DEVEN_D1F2EN)
+ start_peg2_link_training(PCI_DEV(0, 1, 2));
+ if (deven & DEVEN_D1F1EN)
+ start_peg2_link_training(PCI_DEV(0, 1, 1));
+ if (deven & DEVEN_D1F0EN)
+ start_peg2_link_training(PCI_DEV(0, 1, 0));
+}
+
static void haswell_setup_misc(void)
{
u32 reg32;
@@ -140,7 +203,8 @@ void haswell_early_initialization(int chipset_type)
/* Setup IOMMU BARs */
haswell_setup_iommu();
- haswell_setup_graphics();
+ haswell_setup_peg();
+ haswell_setup_igd();
haswell_setup_misc();
}