From 91c38146a4493833a5a7c7a11d251a77b028858d Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Sun, 1 Oct 2023 21:18:31 +0200 Subject: sb/intel/bd82x6x: Disable unused PCIe root ports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow the PCH BIOS spec more closely by porting the broadwell and braswell PCIe downstream device detection. To safe power disable PCIe root ports that have no downstream device connected. By setting the FLAGS_SLOT bit in register PCI_EXP_FLAGS the PCI_EXP_SLTSTA_PDS bit will be updated with in band device detection from the PCIe PHY. While this is primarly used for PCIe hot-plug detection, it is more reliable than probing for downstream devices by reading DID/VID PCI registers. The FLAGS_SLOT bit should stay cleared for integrated devices, as those are known to be present, but to simplify the code all PCIe ports will have the FLAGS_SLOT bit set. There currently used devicetrees might also be lacking integrated devices on the PCH root ports... The SLOTCAP field must be updated by BIOS when the FLAGS_SLOT is set, but it shouldn't be filled for integrated devices. Until now the SLOTCAP field has always been populated and it never was a problem. - Set FLAGS_SLOT "Slot Implemented" bit early. - Read bit PCI_EXP_SLTSTA_PDS 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. Change-Id: I8ccfcab2e0e4faba8322755a4f8c2108d9b007ac Signed-off-by: Patrick Rudolph Reviewed-on: https://review.coreboot.org/c/coreboot/+/78226 Reviewed-by: Kyösti Mälkki Tested-by: build bot (Jenkins) --- src/southbridge/intel/bd82x6x/pch.c | 49 +++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) (limited to 'src/southbridge/intel') diff --git a/src/southbridge/intel/bd82x6x/pch.c b/src/southbridge/intel/bd82x6x/pch.c index ae8ed9b15c..c668f9a6b9 100644 --- a/src/southbridge/intel/bd82x6x/pch.c +++ b/src/southbridge/intel/bd82x6x/pch.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "chip.h" #include "pch.h" @@ -296,6 +297,52 @@ static void pch_pcie_devicetree_update( 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; + uint32_t cap; + + /* Set slot implemented. */ + cap = pci_find_capability(dev, PCI_CAP_ID_PCIE); + pci_or_config16(dev, cap + PCI_EXP_FLAGS, PCI_EXP_FLAGS_SLOT); + + /* + * By setting the PCI_EXP_FLAGS_SLOT bit in register PCI_EXP_FLAGS the + * PCI_EXP_SLTSTA_PDS bit will be updated with in band device + * detection from the PCIe PHY. While this is primarly used for PCIe + * hot-plug detection, it is more reliable than probing for downstream + * devices by reading DID/VID PCI registers of such. + * + * Usually the PCI_EXP_FLAGS_SLOT isn't set for integrated devices, + * but to simplify device detection it's set for all ports. + * + * It also allows to detect device before PCI enumeration has run. + */ + hot_plugable = config && config->pcie_hotplug_map[PCI_FUNC(dev->path.pci.devfn)]; + present = !!(pci_read_config16(dev, cap + PCI_EXP_SLTSTA) & PCI_EXP_SLTSTA_PDS); + + printk(BIOS_DEBUG, "%s: %s downstream device\n", + dev_path(dev), present ? "Found a" : "No"); + + 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 will be disabled, but device present. Disable link. */ + pci_or_config32(dev, cap + PCI_EXP_LNKCTL, PCI_EXP_LNKCTL_LD); + } +} + /* Special handling for PCIe Root Port devices */ static void pch_pcie_enable(struct device *dev) { @@ -304,6 +351,8 @@ static void pch_pcie_enable(struct device *dev) 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 -- cgit v1.2.3