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)