From 334be3289d6ca16e806bd1e2aef87637cebb3122 Mon Sep 17 00:00:00 2001 From: Tristan Corrick Date: Mon, 17 Dec 2018 22:10:21 +1300 Subject: [PATCH] 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 Reviewed-on: https://review.coreboot.org/c/30272 Tested-by: build bot (Jenkins) Reviewed-by: Arthur Heymans --- src/cpu/intel/haswell/romstage.c | 2 + src/northbridge/intel/haswell/early_init.c | 70 +++++++++++++++++++++- src/northbridge/intel/haswell/haswell.h | 1 + 3 files changed, 70 insertions(+), 3 deletions(-) diff --git a/src/cpu/intel/haswell/romstage.c b/src/cpu/intel/haswell/romstage.c index a25f8836be..688f3579db 100644 --- a/src/cpu/intel/haswell/romstage.c +++ b/src/cpu/intel/haswell/romstage.c @@ -143,6 +143,8 @@ void romstage_common(const struct romstage_params *params) #endif } + haswell_unhide_peg(); + setup_sdram_meminfo(params->pei_data); romstage_handoff_init(wake_from_s3); 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 #include #include +#include #include #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(); } diff --git a/src/northbridge/intel/haswell/haswell.h b/src/northbridge/intel/haswell/haswell.h index bc2fc60e40..f67995d4e9 100644 --- a/src/northbridge/intel/haswell/haswell.h +++ b/src/northbridge/intel/haswell/haswell.h @@ -221,6 +221,7 @@ void intel_northbridge_haswell_finalize_smm(void); void haswell_early_initialization(int chipset_type); void haswell_late_initialization(void); void set_translation_table(int start, int end, u64 base, int inc); +void haswell_unhide_peg(void); /* debugging functions */ void print_pci_devices(void);