#endif
}
+/*
+ * Some dGPUs expose their display endpoint below an internal PCIe switch.
+ * Use the switch upstream port to query the host-facing link.
+ */
+static struct pci_dev *amdgpu_device_get_aspm_pdev(struct amdgpu_device *adev)
+{
+ struct pci_dev *swds, *swus;
+
+ swds = pci_upstream_bridge(adev->pdev);
+ if (!swds ||
+ (swds->vendor != PCI_VENDOR_ID_ATI &&
+ swds->vendor != PCI_VENDOR_ID_AMD) ||
+ pci_pcie_type(swds) != PCI_EXP_TYPE_DOWNSTREAM)
+ return adev->pdev;
+
+ swus = pci_upstream_bridge(swds);
+ if (!swus ||
+ (swus->vendor != PCI_VENDOR_ID_ATI &&
+ swus->vendor != PCI_VENDOR_ID_AMD) ||
+ pci_pcie_type(swus) != PCI_EXP_TYPE_UPSTREAM)
+ return adev->pdev;
+
+ return swus;
+}
+
/**
* amdgpu_device_should_use_aspm - check if the device should program ASPM
*
*/
bool amdgpu_device_should_use_aspm(struct amdgpu_device *adev)
{
+ struct pci_dev *aspm_pdev, *parent;
+ bool enabled;
+
switch (amdgpu_aspm) {
case -1:
break;
return false;
if (amdgpu_device_aspm_support_quirk(adev))
return false;
- return pcie_aspm_enabled(adev->pdev);
+
+ /*
+ * pcie_aspm_enabled() checks the link between its argument and
+ * the immediate upstream bridge. Use SWUS for dGPUs with an
+ * internal switch so that this is the host-facing link.
+ */
+ aspm_pdev = amdgpu_device_get_aspm_pdev(adev);
+ parent = pci_upstream_bridge(aspm_pdev);
+ if (!parent) {
+ dev_dbg(adev->dev, "ASPM: no upstream PCIe link for %s\n",
+ pci_name(aspm_pdev));
+ return false;
+ }
+
+ enabled = pcie_aspm_enabled(aspm_pdev);
+ /* Report the exact link used for the automatic ASPM decision. */
+ dev_dbg(adev->dev, "ASPM: link %s <-> %s is %s\n",
+ pci_name(parent), pci_name(aspm_pdev),
+ enabled ? "enabled" : "disabled");
+
+ return enabled;
}
/* if we get transitioned to only one device, take VGA back */