PCI / thunderbolt: Switch to use device links instead of PCI quirk

On older Apple systems there is currently a PCI quirk in place to block
resume of tunneled PCIe ports until NHI (Thunderbolt controller) is
resumed. This makes sure the PCIe tunnels are re-established before PCI
core notices it.

With device links the same thing can be done without quirks. The driver
core will make sure the supplier (NHI) is resumed before consumers (PCIe
downstream ports).

For this reason switch the Thunderbolt driver to use device links and
remove the PCI quirk.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
This commit is contained in:
Mika Westerberg 2020-05-19 13:55:18 +03:00
Родитель b2911a593a
Коммит 2b9941e089
2 изменённых файлов: 66 добавлений и 57 удалений

Просмотреть файл

@ -3673,63 +3673,6 @@ static void quirk_apple_poweroff_thunderbolt(struct pci_dev *dev)
DECLARE_PCI_FIXUP_SUSPEND_LATE(PCI_VENDOR_ID_INTEL, DECLARE_PCI_FIXUP_SUSPEND_LATE(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C, PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C,
quirk_apple_poweroff_thunderbolt); quirk_apple_poweroff_thunderbolt);
/*
* Apple: Wait for the Thunderbolt controller to reestablish PCI tunnels
*
* During suspend the Thunderbolt controller is reset and all PCI
* tunnels are lost. The NHI driver will try to reestablish all tunnels
* during resume. We have to manually wait for the NHI since there is
* no parent child relationship between the NHI and the tunneled
* bridges.
*/
static void quirk_apple_wait_for_thunderbolt(struct pci_dev *dev)
{
struct pci_dev *sibling = NULL;
struct pci_dev *nhi = NULL;
if (!x86_apple_machine)
return;
if (pci_pcie_type(dev) != PCI_EXP_TYPE_DOWNSTREAM)
return;
/*
* Find the NHI and confirm that we are a bridge on the Thunderbolt
* host controller and not on a Thunderbolt endpoint.
*/
sibling = pci_get_slot(dev->bus, 0x0);
if (sibling == dev)
goto out; /* we are the downstream bridge to the NHI */
if (!sibling || !sibling->subordinate)
goto out;
nhi = pci_get_slot(sibling->subordinate, 0x0);
if (!nhi)
goto out;
if (nhi->vendor != PCI_VENDOR_ID_INTEL
|| (nhi->device != PCI_DEVICE_ID_INTEL_LIGHT_RIDGE &&
nhi->device != PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C &&
nhi->device != PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI &&
nhi->device != PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI)
|| nhi->class != PCI_CLASS_SYSTEM_OTHER << 8)
goto out;
pci_info(dev, "quirk: waiting for Thunderbolt to reestablish PCI tunnels...\n");
device_pm_wait_for_dev(&dev->dev, &nhi->dev);
out:
pci_dev_put(nhi);
pci_dev_put(sibling);
}
DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_LIGHT_RIDGE,
quirk_apple_wait_for_thunderbolt);
DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C,
quirk_apple_wait_for_thunderbolt);
DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_BRIDGE,
quirk_apple_wait_for_thunderbolt);
DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_BRIDGE,
quirk_apple_wait_for_thunderbolt);
#endif #endif
/* /*

Просмотреть файл

@ -17,6 +17,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/property.h> #include <linux/property.h>
#include <linux/platform_data/x86/apple.h>
#include "nhi.h" #include "nhi.h"
#include "nhi_regs.h" #include "nhi_regs.h"
@ -1069,6 +1070,69 @@ static bool nhi_imr_valid(struct pci_dev *pdev)
return true; return true;
} }
/*
* During suspend the Thunderbolt controller is reset and all PCIe
* tunnels are lost. The NHI driver will try to reestablish all tunnels
* during resume. This adds device links between the tunneled PCIe
* downstream ports and the NHI so that the device core will make sure
* NHI is resumed first before the rest.
*/
static void tb_apple_add_links(struct tb_nhi *nhi)
{
struct pci_dev *upstream, *pdev;
if (!x86_apple_machine)
return;
switch (nhi->pdev->device) {
case PCI_DEVICE_ID_INTEL_LIGHT_RIDGE:
case PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C:
case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI:
case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI:
break;
default:
return;
}
upstream = pci_upstream_bridge(nhi->pdev);
while (upstream) {
if (!pci_is_pcie(upstream))
return;
if (pci_pcie_type(upstream) == PCI_EXP_TYPE_UPSTREAM)
break;
upstream = pci_upstream_bridge(upstream);
}
if (!upstream)
return;
/*
* For each hotplug downstream port, create add device link
* back to NHI so that PCIe tunnels can be re-established after
* sleep.
*/
for_each_pci_bridge(pdev, upstream->subordinate) {
const struct device_link *link;
if (!pci_is_pcie(pdev))
continue;
if (pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM ||
!pdev->is_hotplug_bridge)
continue;
link = device_link_add(&pdev->dev, &nhi->pdev->dev,
DL_FLAG_AUTOREMOVE_SUPPLIER |
DL_FLAG_PM_RUNTIME);
if (link) {
dev_dbg(&nhi->pdev->dev, "created link from %s\n",
dev_name(&pdev->dev));
} else {
dev_warn(&nhi->pdev->dev, "device link creation from %s failed\n",
dev_name(&pdev->dev));
}
}
}
static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{ {
struct tb_nhi *nhi; struct tb_nhi *nhi;
@ -1134,6 +1198,8 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return res; return res;
} }
tb_apple_add_links(nhi);
tb = icm_probe(nhi); tb = icm_probe(nhi);
if (!tb) if (!tb)
tb = tb_probe(nhi); tb = tb_probe(nhi);