thunderbolt: Move tb_apple_add_links() to pci.c

Due to historical reasons the software connection manager in tb.c
directly sets up the device link between the tunneled PCIe ports and the
NHI. Now as we are starting to support non-PCIe host interfaces as well
we are trying to keep the connection manager implementation agnostic of
the underlying host interface. For this reason move the device link
creation into pci.c and expose it through a new NHI operation that the
host interface code calls when it sets up the connection manager.

Suggested-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
This commit is contained in:
Atharva Tiwari 2026-08-18 05:50:02 -04:00 committed by Mika Westerberg
parent 2d8b6e38b8
commit 41d5a1fc28
4 changed files with 103 additions and 87 deletions

View File

@ -1245,24 +1245,40 @@ void nhi_reset_interface(struct tb_nhi *nhi)
static struct tb *nhi_select_cm(struct tb_nhi *nhi)
{
bool linked = false;
struct tb *tb;
/*
* USB4 case is simple. If we got control of any of the
* capabilities, we use software CM.
*/
if (tb_acpi_is_native())
return tb_probe(nhi);
if (!tb_acpi_is_native()) {
/*
* Either firmware based CM is running (we did not get
* control from the firmware) or this is pre-USB4 PC so
* try first firmware CM and then fallback to software CM.
*/
tb = icm_probe(nhi);
if (tb)
return tb;
}
/*
* Either firmware based CM is running (we did not get control
* from the firmware) or this is pre-USB4 PC so try first
* firmware CM and then fallback to software CM.
*/
tb = icm_probe(nhi);
tb = tb_probe(nhi);
if (!tb)
tb = tb_probe(nhi);
return NULL;
if (nhi->ops->add_links)
linked = nhi->ops->add_links(nhi);
if (!linked)
linked = tb_acpi_add_links(nhi);
/*
* Device links are needed to make sure we establish tunnels
* before protocol stacks are resumed so complain here if we
* found them missing.
*/
if (!linked)
dev_warn(nhi->dev,
"device links to tunneled native ports are missing!\n");
return tb;
}

View File

@ -43,6 +43,7 @@ extern const struct dev_pm_ops nhi_pm_ops;
/**
* struct tb_nhi_ops - NHI specific optional operations
* @init: NHI specific initialization
* @add_links: Setup device links for tunneling native protocols
* @suspend_noirq: NHI specific suspend_noirq hook
* @resume_noirq: NHI specific resume_noirq hook
* @runtime_suspend: NHI specific runtime_suspend hook
@ -58,6 +59,7 @@ extern const struct dev_pm_ops nhi_pm_ops;
*/
struct tb_nhi_ops {
int (*init)(struct tb_nhi *nhi);
bool (*add_links)(struct tb_nhi *nhi);
int (*suspend_noirq)(struct tb_nhi *nhi, bool wakeup);
int (*resume_noirq)(struct tb_nhi *nhi);
int (*runtime_suspend)(struct tb_nhi *nhi);

View File

@ -18,6 +18,7 @@
#include <linux/property.h>
#include <linux/string_helpers.h>
#include <linux/suspend.h>
#include <linux/platform_data/x86/apple.h>
#include "nhi.h"
#include "nhi_regs.h"
@ -129,6 +130,80 @@ static void nhi_pci_check_iommu(struct tb_nhi_pci *nhi_pci)
str_enabled_disabled(port_ok));
}
static bool add_link(struct tb_nhi *nhi, struct pci_dev *pdev)
{
const struct device_link *link;
link = device_link_add(&pdev->dev, nhi->dev,
DL_FLAG_AUTOREMOVE_SUPPLIER |
DL_FLAG_PM_RUNTIME);
if (!link) {
dev_warn(nhi->dev, "device link creation from %s failed\n",
dev_name(&pdev->dev));
return false;
}
dev_dbg(nhi->dev, "created link from %s\n", dev_name(&pdev->dev));
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 bool nhi_pci_add_links(struct tb_nhi *nhi)
{
struct pci_dev *nhi_pdev = to_pci_dev(nhi->dev);
struct pci_dev *upstream, *pdev;
bool ret;
if (!x86_apple_machine)
return false;
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 false;
}
upstream = pci_upstream_bridge(nhi_pdev);
while (upstream) {
if (!pci_is_pcie(upstream))
return false;
if (pci_pcie_type(upstream) == PCI_EXP_TYPE_UPSTREAM)
break;
upstream = pci_upstream_bridge(upstream);
}
if (!upstream)
return false;
/*
* For each hotplug downstream port, create add device link back
* to NHI so that PCIe tunnels can be re-established after
* sleep.
*/
ret = false;
for_each_pci_bridge(pdev, upstream->subordinate) {
if (!pci_is_pcie(pdev))
continue;
if (pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM ||
!pdev->is_pciehp)
continue;
ret |= add_link(nhi, pdev);
}
return ret;
}
static int nhi_pci_init_msi(struct tb_nhi *nhi)
{
struct tb_nhi_pci *nhi_pci = nhi_to_pci(nhi);
@ -272,6 +347,7 @@ static bool nhi_pci_is_present(struct tb_nhi *nhi)
}
static const struct tb_nhi_ops pci_nhi_default_ops = {
.add_links = nhi_pci_add_links,
.pre_nvm_auth = nhi_pci_start_dma_port,
.post_nvm_auth = nhi_pci_complete_dma_port,
.request_ring_irq = nhi_pci_ring_request_msix,

View File

@ -10,7 +10,6 @@
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/pm_runtime.h>
#include <linux/platform_data/x86/apple.h>
#include "tb.h"
#include "tb_regs.h"
@ -3323,75 +3322,6 @@ static const struct tb_cm_ops tb_cm_ops = {
.disconnect_xdomain_paths = tb_disconnect_xdomain_paths,
};
/*
* 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 bool tb_apple_add_links(struct tb_nhi *nhi)
{
struct pci_dev *nhi_pdev = to_pci_dev(nhi->dev);
struct pci_dev *upstream, *pdev;
bool ret;
if (!x86_apple_machine)
return false;
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 false;
}
upstream = pci_upstream_bridge(nhi_pdev);
while (upstream) {
if (!pci_is_pcie(upstream))
return false;
if (pci_pcie_type(upstream) == PCI_EXP_TYPE_UPSTREAM)
break;
upstream = pci_upstream_bridge(upstream);
}
if (!upstream)
return false;
/*
* For each hotplug downstream port, create add device link
* back to NHI so that PCIe tunnels can be re-established after
* sleep.
*/
ret = false;
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_pciehp)
continue;
link = device_link_add(&pdev->dev, nhi->dev,
DL_FLAG_AUTOREMOVE_SUPPLIER |
DL_FLAG_PM_RUNTIME);
if (link) {
dev_dbg(nhi->dev, "created link from %s\n",
dev_name(&pdev->dev));
ret = true;
} else {
dev_warn(nhi->dev, "device link creation from %s failed\n",
dev_name(&pdev->dev));
}
}
return ret;
}
struct tb *tb_probe(struct tb_nhi *nhi)
{
struct tb_cm *tcm;
@ -3416,13 +3346,5 @@ struct tb *tb_probe(struct tb_nhi *nhi)
tb_dbg(tb, "using software connection manager\n");
/*
* Device links are needed to make sure we establish tunnels
* before the PCIe/USB stack is resumed so complain here if we
* found them missing.
*/
if (!tb_apple_add_links(nhi) && !tb_acpi_add_links(nhi))
tb_warn(tb, "device links to tunneled native ports are missing!\n");
return tb;
}