From ae999503f62ef8a3b9b2756a2810d29c383a009e Mon Sep 17 00:00:00 2001 From: Angel Pons Date: Thu, 5 Nov 2020 01:58:34 +0100 Subject: [PATCH] nb/intel/haswell/pcie.c: Add missing pre-ASPM init Add devicetree configuration parameters for mainboard-specific settings, and provide reasonable defaults, which should usually be good enough. This is based on Haswell SA Reference Code version 1.9.0 (Nov 2014). Tested on Asrock B85M Pro4, registers now have the expected values. Change-Id: I0dcdd4ca431c2ae1e62f2719c376d8bdef3054bd Signed-off-by: Angel Pons Reviewed-on: https://review.coreboot.org/c/coreboot/+/47223 Reviewed-by: Nico Huber Tested-by: build bot (Jenkins) --- src/northbridge/intel/haswell/chip.h | 10 +++ src/northbridge/intel/haswell/haswell.h | 21 ++++++ src/northbridge/intel/haswell/pcie.c | 94 +++++++++++++++++++++++++ 3 files changed, 125 insertions(+) diff --git a/src/northbridge/intel/haswell/chip.h b/src/northbridge/intel/haswell/chip.h index 99102b6770..274e549e2d 100644 --- a/src/northbridge/intel/haswell/chip.h +++ b/src/northbridge/intel/haswell/chip.h @@ -4,6 +4,14 @@ #define NORTHBRIDGE_INTEL_HASWELL_CHIP_H #include +#include + +struct peg_config { + bool is_onboard; + uint8_t power_limit_scale; + uint8_t power_limit_value; + uint16_t phys_slot_number; +}; /* * Digital Port Hotplug Enable: @@ -20,6 +28,8 @@ struct northbridge_intel_haswell_config { /* IGD panel configuration */ struct i915_gpu_panel_config panel_cfg; + struct peg_config peg_cfg[3]; + bool gpu_ddi_e_connected; bool ec_present; diff --git a/src/northbridge/intel/haswell/haswell.h b/src/northbridge/intel/haswell/haswell.h index f158c2199b..b6c2b5f4ea 100644 --- a/src/northbridge/intel/haswell/haswell.h +++ b/src/northbridge/intel/haswell/haswell.h @@ -20,12 +20,33 @@ /* Device 0:1.0 PCI configuration space (PCIe Graphics) */ +#define PEG_CAP 0xa2 +#define PEG_DCAP 0xa4 + +#define PEG_LCAP 0xac + +#define PEG_DSTS 0xaa + +#define PEG_SLOTCAP 0xb4 + #define PEG_DCAP2 0xc4 /* 32bit */ +#define PEG_LCTL2 0xd0 + +#define PEG_VC0RCTL 0x114 + #define PEG_ESD 0x144 /* 32bit */ #define PEG_LE1D 0x150 /* 32bit */ #define PEG_LE1A 0x158 /* 64bit */ +#define PEG_UESTS 0x1c4 +#define PEG_UESEV 0x1cc +#define PEG_CESTS 0x1d0 + +#define PEG_L0SLAT 0x22c + +#define PEG_AFE_PM_TMR 0xc28 + /* Device 0:2.0 PCI configuration space (Graphics Device) */ #define MSAC 0x62 /* Multi Size Aperture Control */ diff --git a/src/northbridge/intel/haswell/pcie.c b/src/northbridge/intel/haswell/pcie.c index a631cc883a..ac6d4ca7c0 100644 --- a/src/northbridge/intel/haswell/pcie.c +++ b/src/northbridge/intel/haswell/pcie.c @@ -5,7 +5,12 @@ #include #include #include +#include #include +#include + +#include "chip.h" +#include "haswell.h" #if CONFIG(HAVE_ACPI_TABLES) static const char *pcie_acpi_name(const struct device *dev) @@ -41,12 +46,101 @@ static const char *pcie_acpi_name(const struct device *dev) } #endif +static void peg_enable(struct device *dev) +{ + const struct northbridge_intel_haswell_config *config = config_of(dev); + + const uint8_t func = PCI_FUNC(PCI_BDF(dev)); + + assert(func < ARRAY_SIZE(config->peg_cfg)); + + const bool slot_implemented = !config->peg_cfg[func].is_onboard; + + if (slot_implemented) { + /* Default is 1, but register is R/WO and needs to be written to once */ + pci_or_config16(dev, PEG_CAP, 1 << 8); + } else { + pci_and_config16(dev, PEG_CAP, ~(1 << 8)); + } + + /* Note: this register is write-once */ + uint32_t slotcap = pci_read_config32(dev, PEG_SLOTCAP); + + /* Physical slot number (zero for ports connected to onboard devices) */ + slotcap &= ~(0x1fff << 19); + if (slot_implemented) { + uint16_t slot_number = config->peg_cfg[func].phys_slot_number & 0x1fff; + if (slot_number == 0) { + /* Slot number must be non-zero and unique */ + slot_number = func + 1; + } + slotcap |= slot_number << 19; + } + + /* Default to 1.0 watt scale */ + slotcap &= ~(3 << 15); + slotcap |= (config->peg_cfg[func].power_limit_scale & 3) << 15; + + uint8_t power_limit_value = config->peg_cfg[func].power_limit_value; + if (power_limit_value == 0) { + /* Default to 75 watts */ + power_limit_value = 75; + } + slotcap &= ~(0xff << 7); + slotcap |= power_limit_value << 7; + + pci_write_config32(dev, PEG_SLOTCAP, slotcap); + + /* Clear errors */ + pci_write_config16(dev, PCI_STATUS, 0xffff); + pci_write_config16(dev, PCI_SEC_STATUS, 0xffff); + pci_write_config16(dev, PEG_DSTS, 0xffff); + pci_write_config32(dev, PEG_UESTS, 0xffffffff); + pci_write_config32(dev, PEG_CESTS, 0xffffffff); + pci_write_config32(dev, 0x1f0, 0xffffffff); + + pci_or_config32(dev, PEG_VC0RCTL, 0x7f << 1); + + /* Advertise OBFF support using WAKE# signaling only */ + pci_or_config32(dev, PEG_DCAP2, 1 << 19); + + pci_or_config32(dev, PEG_UESEV, 1 << 14); + + /* Select -3.5 dB de-emphasis */ + pci_or_config32(dev, PEG_LCTL2, 1 << 6); + + pci_or_config32(dev, PEG_L0SLAT, 1 << 31); + + pci_update_config32(dev, 0x250, ~(7 << 20), 2 << 20); + + pci_or_config32(dev, 0x238, 1 << 29); + + pci_or_config32(dev, 0x1f8, 1 << 16); + + pci_update_config32(dev, PEG_AFE_PM_TMR, ~0x1f, 0x13); + + /* Lock DCAP */ + pci_update_config32(dev, PEG_DCAP, ~0, 0); + + if (func == 0) + pci_or_config32(dev, 0xcd0, 1 << 11); + + /* Enable support for L0s and L1 */ + pci_or_config32(dev, PEG_LCAP, 3 << 10); + + pci_and_config32(dev, 0x200, ~(3 << 26)); + + /* Other fields in this register must not be changed while writing this */ + pci_or_config16(dev, 0x258, 1 << 2); +} + static struct device_operations device_ops = { .read_resources = pci_bus_read_resources, .set_resources = pci_dev_set_resources, .enable_resources = pci_bus_enable_resources, .scan_bus = pciexp_scan_bridge, .reset_bus = pci_bus_reset, + .enable = peg_enable, .init = pci_dev_init, .ops_pci = &pci_dev_ops_pci, #if CONFIG(HAVE_ACPI_TABLES)