summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/northbridge/intel/haswell/chip.h10
-rw-r--r--src/northbridge/intel/haswell/haswell.h21
-rw-r--r--src/northbridge/intel/haswell/pcie.c94
3 files changed, 125 insertions, 0 deletions
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 <drivers/intel/gma/gma.h>
+#include <types.h>
+
+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 <device/pci.h>
#include <device/pciexp.h>
#include <device/pci_ids.h>
+#include <device/pci_ops.h>
#include <assert.h>
+#include <types.h>
+
+#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)