Skip to content

Commit c596932

Browse files
committed
thunderbolt: Replace PCI quirk for downstream bridges
When resuming from system sleep, the downstream bridges of a Thunderbolt controller must not resume before the NHI has reestablished the PCI tunnels. However the NHI is not a parent of the downstream bridges, so this requirement is not fulfilled automatically by the PM core and we have to open code it instead. Up until now we've done this with a PCI quirk, which has the following disadvantages: - The code for the NHI resides in drivers/thunderbolt/, whereas the code for the downstream bridges resides in drivers/pci/quirks.c, which is somewhat surprising and non-obvious in particular for newcomers. - Whenever support for an additional Thunderbolt controller is added, its PCI device ID needs to be amended in both places, which invites mistakes. E.g. commit a42fb35 ("thunderbolt: Allow loading of module on recent Apple MacBooks with thunderbolt 2 controller") relaxed the subvendor and subdevice ID in the NHI code but forgot to also change the downstream bridge code. - Since the PCI quirk cannot keep any state, it has to search for the NHI over and over again on every resume. Overcome these disadvantages by resuming the NHI from the upstream bridge driver. The upstream bridge *is* a parent of the downstream bridges, so this is functionally equivalent to the PCI quirk. Also needs less code. Cc: Andreas Noever <andreas.noever@gmail.com> Signed-off-by: Lukas Wunner <lukas@wunner.de>
1 parent 0ce4a23 commit c596932

File tree

3 files changed

+38
-67
lines changed

3 files changed

+38
-67
lines changed

drivers/pci/quirks.c

Lines changed: 0 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -3291,59 +3291,6 @@ static void quirk_apple_poweroff_thunderbolt(struct pci_dev *dev)
32913291
DECLARE_PCI_FIXUP_SUSPEND_LATE(PCI_VENDOR_ID_INTEL,
32923292
PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C,
32933293
quirk_apple_poweroff_thunderbolt);
3294-
3295-
/*
3296-
* Apple: Wait for the thunderbolt controller to reestablish pci tunnels.
3297-
*
3298-
* During suspend the thunderbolt controller is reset and all pci
3299-
* tunnels are lost. The NHI driver will try to reestablish all tunnels
3300-
* during resume. We have to manually wait for the NHI since there is
3301-
* no parent child relationship between the NHI and the tunneled
3302-
* bridges.
3303-
*/
3304-
static void quirk_apple_wait_for_thunderbolt(struct pci_dev *dev)
3305-
{
3306-
struct pci_dev *sibling = NULL;
3307-
struct pci_dev *nhi = NULL;
3308-
3309-
if (!dmi_match(DMI_BOARD_VENDOR, "Apple Inc."))
3310-
return;
3311-
if (pci_pcie_type(dev) != PCI_EXP_TYPE_DOWNSTREAM)
3312-
return;
3313-
/*
3314-
* Find the NHI and confirm that we are a bridge on the tb host
3315-
* controller and not on a tb endpoint.
3316-
*/
3317-
sibling = pci_get_slot(dev->bus, 0x0);
3318-
if (sibling == dev)
3319-
goto out; /* we are the downstream bridge to the NHI */
3320-
if (!sibling || !sibling->subordinate)
3321-
goto out;
3322-
nhi = pci_get_slot(sibling->subordinate, 0x0);
3323-
if (!nhi)
3324-
goto out;
3325-
if (nhi->vendor != PCI_VENDOR_ID_INTEL
3326-
|| (nhi->device != PCI_DEVICE_ID_INTEL_LIGHT_RIDGE &&
3327-
nhi->device != PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C &&
3328-
nhi->device != PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI)
3329-
|| nhi->subsystem_vendor != 0x2222
3330-
|| nhi->subsystem_device != 0x1111)
3331-
goto out;
3332-
dev_info(&dev->dev, "quirk: waiting for thunderbolt to reestablish PCI tunnels...\n");
3333-
device_pm_wait_for_dev(&dev->dev, &nhi->dev);
3334-
out:
3335-
pci_dev_put(nhi);
3336-
pci_dev_put(sibling);
3337-
}
3338-
DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,
3339-
PCI_DEVICE_ID_INTEL_LIGHT_RIDGE,
3340-
quirk_apple_wait_for_thunderbolt);
3341-
DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,
3342-
PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C,
3343-
quirk_apple_wait_for_thunderbolt);
3344-
DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,
3345-
PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_BRIDGE,
3346-
quirk_apple_wait_for_thunderbolt);
33473294
#endif
33483295

33493296
static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f,

drivers/thunderbolt/nhi.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,7 @@ static int nhi_suspend_noirq(struct device *dev)
503503
return 0;
504504
}
505505

506-
static int nhi_resume_noirq(struct device *dev)
506+
int nhi_resume_noirq(struct device *dev)
507507
{
508508
struct pci_dev *pdev = to_pci_dev(dev);
509509
struct tb *tb = pci_get_drvdata(pdev);
@@ -627,18 +627,18 @@ static void nhi_remove(struct pci_dev *pdev)
627627
}
628628

629629
/*
630-
* The tunneled pci bridges are siblings of us. Use resume_noirq to reenable
631-
* the tunnels asap. A corresponding pci quirk blocks the downstream bridges
632-
* resume_noirq until we are done.
630+
* .resume_noirq and .restore_noirq are not listed here because they're not
631+
* called from the PM core, but from the upstream bridge driver. This ensures
632+
* that the NHI can reestablish the pci tunnels before the downstream bridges
633+
* and the devices attached to them resume. (The NHI is not a parent of the
634+
* downstream bridges, but the upstream bridge is.)
633635
*/
634636
static const struct dev_pm_ops nhi_pm_ops = {
635637
.suspend_noirq = nhi_suspend_noirq,
636-
.resume_noirq = nhi_resume_noirq,
637638
.freeze_noirq = nhi_suspend_noirq, /*
638639
* we just disable hotplug, the
639640
* pci-tunnels stay alive.
640641
*/
641-
.restore_noirq = nhi_resume_noirq,
642642
.runtime_suspend = nhi_suspend_noirq,
643643
.runtime_resume = nhi_resume_noirq,
644644
};

drivers/thunderbolt/upstream.c

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,36 @@
9393

9494
struct tb_upstream {
9595
struct pci_dev *nhi;
96+
struct pci_dev *dsb0;
9697
unsigned long long wake_gpe; /* hotplug interrupt during powerdown */
9798
acpi_handle set_power; /* method to power controller up/down */
9899
acpi_handle get_power; /* method to query power state */
99100
};
100101

102+
int nhi_resume_noirq(struct device *dev);
103+
104+
static int upstream_resume_noirq(struct pcie_device *dev)
105+
{
106+
struct tb_upstream *upstream = get_service_data(dev);
107+
108+
if (!upstream->nhi->dev.driver)
109+
return 0;
110+
111+
/*
112+
* During suspend the thunderbolt controller is reset and all pci
113+
* tunnels are lost. The NHI driver needs to reestablish all tunnels
114+
* before the downstream bridges resume. There is no parent child
115+
* relationship between the NHI and the tunneled bridges, but there is
116+
* between them and the upstream bridge. Hence the NHI needs to be
117+
* resumed by us rather than the PM core.
118+
*/
119+
pci_set_power_state(upstream->dsb0, PCI_D0);
120+
pci_restore_state(upstream->dsb0);
121+
pci_set_power_state(upstream->nhi, PCI_D0);
122+
pci_restore_state(upstream->nhi);
123+
return nhi_resume_noirq(&upstream->nhi->dev);
124+
}
125+
101126
static int upstream_prepare(struct pcie_device *dev)
102127
{
103128
struct tb_upstream *upstream = get_service_data(dev);
@@ -257,7 +282,6 @@ static int upstream_probe(struct pcie_device *dev)
257282
{
258283
struct tb_upstream *upstream;
259284
struct acpi_handle *nhi_handle;
260-
struct pci_dev *dsb0;
261285

262286
/* host controllers only */
263287
if (!dev->port->bus->self ||
@@ -269,13 +293,10 @@ static int upstream_probe(struct pcie_device *dev)
269293
return -ENOMEM;
270294

271295
/* find Downstream Bridge 0 and NHI */
272-
dsb0 = pci_get_slot(dev->port->subordinate, 0);
273-
if (!dsb0 || !dsb0->subordinate) {
274-
pci_dev_put(dsb0);
275-
return -ENODEV;
276-
}
277-
upstream->nhi = pci_get_slot(dsb0->subordinate, 0);
278-
pci_dev_put(dsb0);
296+
upstream->dsb0 = pci_get_slot(dev->port->subordinate, 0);
297+
if (!upstream->dsb0 || !upstream->dsb0->subordinate)
298+
goto err;
299+
upstream->nhi = pci_get_slot(upstream->dsb0->subordinate, 0);
279300
if (!upstream->nhi || !pci_match_id(nhi_ids, upstream->nhi))
280301
goto err;
281302
nhi_handle = ACPI_HANDLE(&upstream->nhi->dev);
@@ -317,6 +338,7 @@ static int upstream_probe(struct pcie_device *dev)
317338

318339
err:
319340
pci_dev_put(upstream->nhi);
341+
pci_dev_put(upstream->dsb0);
320342
return -ENODEV;
321343
}
322344

@@ -329,6 +351,7 @@ static void upstream_remove(struct pcie_device *dev)
329351
dev_err(&dev->device, "cannot remove GPE handler\n");
330352

331353
pci_dev_put(upstream->nhi);
354+
pci_dev_put(upstream->dsb0);
332355
set_service_data(dev, NULL);
333356
}
334357

@@ -342,4 +365,5 @@ struct pcie_port_service_driver upstream_driver = {
342365
.complete = upstream_complete,
343366
.runtime_suspend = upstream_runtime_suspend,
344367
.runtime_resume = upstream_runtime_resume,
368+
.resume_noirq = upstream_resume_noirq,
345369
};

0 commit comments

Comments
 (0)