Patrick Rudolph has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/78226?usp=email )
Change subject: sb/intel/bd82x6x: Disable unused PCIe root ports ......................................................................
sb/intel/bd82x6x: Disable unused PCIe root ports
Follow the PCH BIOS spec more closely by porting the broadwell and braswell PCIe downstream device detection.
- Set "Slot Implemented" bit. - Read SLSTS BIT22 to detect connected downstream devices as done on braswell. - Disable unused PCIe slots that are not hotplugable. - Set BIT26 in register 0x338 and wait for bits in register 0x328 to clear as done on broadwell.
Test: Tested on Lenovo X220. Unused root ports are disabled and port that are in used or marked hot-plug are kept enabled.
TODO: Test on more devices and measure powersavings.
Change-Id: I8ccfcab2e0e4faba8322755a4f8c2108d9b007ac Signed-off-by: Patrick Rudolph patrick.rudolph@9elements.com --- M src/southbridge/intel/bd82x6x/pch.c M src/southbridge/intel/bd82x6x/pch.h 2 files changed, 43 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/26/78226/1
diff --git a/src/southbridge/intel/bd82x6x/pch.c b/src/southbridge/intel/bd82x6x/pch.c index ae8ed9b..c9e66de 100644 --- a/src/southbridge/intel/bd82x6x/pch.c +++ b/src/southbridge/intel/bd82x6x/pch.c @@ -6,6 +6,7 @@ #include <device/pci.h> #include <device/pci_ops.h> #include <string.h> +#include <timer.h>
#include "chip.h" #include "pch.h" @@ -296,6 +297,39 @@ sizeof(new_hotplug_map)); }
+static void check_device_present(struct device *dev) +{ + struct southbridge_intel_bd82x6x_config *config = dev->chip_info; + + struct stopwatch timeout; + bool present, hot_plugable; + + /* Set slot implemented. */ + pci_or_config16(dev, D28Fx_XCAP, SI); + + hot_plugable = config && config->pcie_hotplug_map[PCI_FUNC(dev->path.pci.devfn)]; + present = !!(pci_read_config32(dev, D28Fx_SLSTS) & PDS); + + printk(BIOS_DEBUG, "%s: Found %s downstream device on %shot pluggable port\n", + dev_path(dev), present ? "a" : "no", hot_plugable ? "" : "non "); + + if (!present && !hot_plugable) { + /* No device present. */ + stopwatch_init_usecs_expire(&timeout, 50 * 1000); + pci_or_config32(dev, 0x338, 1 << 26); + + while (!stopwatch_expired(&timeout)) { + if ((pci_read_config32(dev, 0x328) & (0x1f << 23)) == 0) + break; + udelay(100); + } + dev->enabled = 0; + } else if (present && !hot_plugable && !dev->enabled) { + /* Port is disabled, but device present. Disable link. */ + pci_or_config32(dev, D28Fx_LCTL, LD); + } +} + /* Special handling for PCIe Root Port devices */ static void pch_pcie_enable(struct device *dev) { @@ -304,6 +338,8 @@ if (!config) return;
+ check_device_present(dev); + /* * Save a copy of the Root Port Function Number map when * starting to walk the list of PCIe Root Ports so it can diff --git a/src/southbridge/intel/bd82x6x/pch.h b/src/southbridge/intel/bd82x6x/pch.h index 6d164ea..727204f 100644 --- a/src/southbridge/intel/bd82x6x/pch.h +++ b/src/southbridge/intel/bd82x6x/pch.h @@ -83,6 +83,13 @@ #define PCH_PCIE_DEV_SLOT 28 #define PCH_PCIE_DEV(_func) PCI_DEV(0, PCH_PCIE_DEV_SLOT, _func)
+#define D28Fx_XCAP 0x42 +# define SI (1 << 8) +#define D28Fx_LCTL 0x50 +# define LD (1 << 4) +#define D28Fx_SLSTS 0x58 +# define PDS (1 << 22) + /* PCI Configuration Space (D20:F0): xHCI */ #define PCH_XHCI_DEV PCI_DEV(0, 0x14, 0)