# Conflicts:
#	drivers/thunderbolt/nhi.c
This commit is contained in:
Mark Brown
2026-07-31 16:03:26 +01:00
11 changed files with 76 additions and 18 deletions
+2 -2
View File
@@ -1340,7 +1340,7 @@ static void tbnet_generate_mac(struct net_device *dev)
dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
}
static int tbnet_probe(struct tb_service *svc, const struct tb_service_id *id)
static int tbnet_probe(struct tb_service *svc)
{
struct tb_xdomain *xd = tb_service_parent(svc);
struct net_device *dev;
@@ -1460,7 +1460,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(tbnet_pm_ops, tbnet_suspend, tbnet_resume);
static const struct tb_service_id tbnet_ids[] = {
{ TB_SERVICE("network", 1) },
{ },
{ }
};
MODULE_DEVICE_TABLE(tbsvc, tbnet_ids);
+2 -2
View File
@@ -636,7 +636,7 @@ static void dma_test_debugfs_init(struct tb_service *svc)
debugfs_create_file("test", 0200, debugfs_dir, svc, &test_fops);
}
static int dma_test_probe(struct tb_service *svc, const struct tb_service_id *id)
static int dma_test_probe(struct tb_service *svc)
{
struct tb_xdomain *xd = tb_service_parent(svc);
struct dma_test *dt;
@@ -689,7 +689,7 @@ static const struct dev_pm_ops dma_test_pm_ops = {
static const struct tb_service_id dma_test_ids[] = {
{ TB_SERVICE("dma_test", 1) },
{ },
{ }
};
MODULE_DEVICE_TABLE(tbsvc, dma_test_ids);
+1 -3
View File
@@ -77,12 +77,10 @@ static int tb_service_probe(struct device *dev)
{
struct tb_service *svc = tb_to_service(dev);
struct tb_service_driver *driver;
const struct tb_service_id *id;
driver = container_of(dev->driver, struct tb_service_driver, driver);
id = __tb_service_match(dev, &driver->driver);
return driver->probe(svc, id);
return driver->probe(svc);
}
static void tb_service_remove(struct device *dev)
+2
View File
@@ -1235,6 +1235,8 @@ int nhi_probe(struct tb_nhi *nhi)
dev_dbg(dev, "NHI initialized, starting thunderbolt\n");
nhi->host_reset = host_reset;
res = tb_domain_add(tb, host_reset);
if (res) {
/*
+24 -7
View File
@@ -112,7 +112,6 @@ static int nhi_pci_init_msi(struct tb_nhi *nhi)
{
struct tb_nhi_pci *nhi_pci = nhi_to_pci(nhi);
struct pci_dev *pdev = to_pci_dev(nhi->dev);
struct device *dev = &pdev->dev;
int res, irq, nvec;
ida_init(&nhi_pci->msix_ida);
@@ -139,7 +138,7 @@ static int nhi_pci_init_msi(struct tb_nhi *nhi)
res = devm_request_irq(&pdev->dev, irq, nhi_msi,
IRQF_NO_SUSPEND, "thunderbolt", nhi);
if (res)
return dev_err_probe(dev, res, "request_irq failed, aborting\n");
return res;
}
return 0;
@@ -230,7 +229,7 @@ static void nhi_pci_ring_release_msix(struct tb_ring *ring)
ring->irq = 0;
}
static void nhi_pci_shutdown(struct tb_nhi *nhi)
static void nhi_pci_release_irq(struct tb_nhi *nhi)
{
struct tb_nhi_pci *nhi_pci = nhi_to_pci(nhi);
struct pci_dev *pdev = to_pci_dev(nhi->dev);
@@ -256,7 +255,7 @@ static const struct tb_nhi_ops pci_nhi_default_ops = {
.post_nvm_auth = nhi_pci_complete_dma_port,
.request_ring_irq = nhi_pci_ring_request_msix,
.release_ring_irq = nhi_pci_ring_release_msix,
.shutdown = nhi_pci_shutdown,
.shutdown = nhi_pci_release_irq,
.is_present = nhi_pci_is_present,
.init_interrupts = nhi_pci_init_msi,
};
@@ -424,7 +423,7 @@ static int icl_nhi_resume(struct tb_nhi *nhi)
static void icl_nhi_shutdown(struct tb_nhi *nhi)
{
nhi_pci_shutdown(nhi);
nhi_pci_release_irq(nhi);
icl_nhi_force_power(nhi, false);
}
@@ -479,11 +478,19 @@ static int nhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return nhi_probe(&nhi_pci->nhi);
}
static void nhi_pci_remove(struct pci_dev *pdev)
static void nhi_pci_do_remove(struct pci_dev *pdev, bool reset)
{
struct tb *tb = pci_get_drvdata(pdev);
struct tb_nhi *nhi = tb->nhi;
/*
* On system shutdown/reboot force a host router reset so the
* connection manager asserts DPR on connected Thunderbolt 3 devices
* before the router tree is removed (see tb_stop()).
*/
if (reset)
nhi->host_reset = true;
pm_runtime_get_sync(&pdev->dev);
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_forbid(&pdev->dev);
@@ -493,6 +500,16 @@ static void nhi_pci_remove(struct pci_dev *pdev)
nhi_shutdown(nhi);
}
static void nhi_pci_remove(struct pci_dev *pdev)
{
nhi_pci_do_remove(pdev, false);
}
static void nhi_pci_shutdown(struct pci_dev *pdev)
{
nhi_pci_do_remove(pdev, true);
}
static struct pci_device_id nhi_ids[] = {
/*
* We have to specify class, the TB bridges use the same device and
@@ -593,7 +610,7 @@ static struct pci_driver nhi_driver = {
.id_table = nhi_ids,
.probe = nhi_pci_probe,
.remove = nhi_pci_remove,
.shutdown = nhi_pci_remove,
.shutdown = nhi_pci_shutdown,
.driver.pm = &nhi_pm_ops,
};
+2 -2
View File
@@ -1540,7 +1540,7 @@ static void tbstream_group_detach_stream(struct tbstream *stream)
config_group_put(&sg->group);
}
static int tbstream_probe(struct tb_service *svc, const struct tb_service_id *id)
static int tbstream_probe(struct tb_service *svc)
{
struct tbstream *stream;
@@ -1630,7 +1630,7 @@ static const struct dev_pm_ops tbstream_pm_ops = {
static const struct tb_service_id tbstream_ids[] = {
{ TB_SERVICE("stream", 1) },
{ },
{ }
};
MODULE_DEVICE_TABLE(tbsvc, tbstream_ids);
+10 -1
View File
@@ -682,7 +682,16 @@ int tb_port_disable(struct tb_port *port)
return __tb_port_enable(port, false);
}
static int tb_port_reset(struct tb_port *port)
/**
* tb_port_reset() - Reset the port
* @port: Port to reset
*
* Resets @port. For USB4 ports this issues a USB4 port reset and for
* legacy ports the link controller port is reset.
*
* Return: %0 on success, negative errno otherwise.
*/
int tb_port_reset(struct tb_port *port)
{
if (tb_switch_is_usb4(port->sw))
return port->cap_usb4 ? usb4_port_reset(port) : 0;
+21
View File
@@ -2941,7 +2941,9 @@ static void tb_handle_event(struct tb *tb, enum tb_cfg_pkg_type type,
static void tb_stop(struct tb *tb)
{
struct tb_cm *tcm = tb_priv(tb);
struct tb_nhi *nhi = tb->nhi;
struct tb_tunnel *tunnel;
struct tb_port *port;
struct tb_tunnel *n;
cancel_delayed_work(&tcm->remove_work);
@@ -2956,6 +2958,25 @@ static void tb_stop(struct tb *tb)
tb_tunnel_deactivate(tunnel);
tb_tunnel_put(tunnel);
}
/*
* Signal disconnect to connected devices before the router tree is
* removed below. A Thunderbolt 3 device directly connected to a USB4
* host otherwise never receives a disconnect indication, leaving
* firmware to poll the dead link for up to ~60 s which on some
* platforms turns the shutdown into a warm reset. Asserting
* PORT_CS_19.DPR drives SBTX low (USB4 spec section 6.9) so the device
* detects SBRX low and goes to Uninitialized Unplugged immediately.
*/
if (nhi->host_reset) {
tb_switch_for_each_port(tb->root_switch, port) {
if (!tb_port_is_null(port) || !tb_port_has_remote(port))
continue;
if (tb_switch_is_usb4(port->remote->sw))
continue;
if (tb_port_reset(port))
tb_port_dbg(port, "downstream port reset failed, continuing\n");
}
}
tb_switch_remove(tb->root_switch);
tb->root_switch = NULL;
tcm->hotplug_active = false; /* signal tb_handle_hotplug to quit */
+1
View File
@@ -1103,6 +1103,7 @@ int tb_port_clear_counter(struct tb_port *port, int counter);
int tb_port_unlock(struct tb_port *port);
int tb_port_enable(struct tb_port *port);
int tb_port_disable(struct tb_port *port);
int tb_port_reset(struct tb_port *port);
int tb_port_alloc_in_hopid(struct tb_port *port, int hopid, int max_hopid);
void tb_port_release_in_hopid(struct tb_port *port, int hopid);
int tb_port_alloc_out_hopid(struct tb_port *port, int hopid, int max_hopid);
+4
View File
@@ -968,6 +968,9 @@ tb_xdp_schedule_request(struct tb *tb, const struct tb_xdp_header *hdr,
*/
int tb_register_service_driver(struct tb_service_driver *drv)
{
if (!drv->probe)
return -EINVAL;
drv->driver.bus = &tb_bus_type;
return driver_register(&drv->driver);
}
@@ -1811,6 +1814,7 @@ static void tb_xdomain_state_work(struct work_struct *work)
tb_xdomain_failed(xd);
} else {
xd->state = XDOMAIN_STATE_ENUMERATED;
tb_xdomain_queue_properties_changed(xd);
}
break;
+7 -1
View File
@@ -465,7 +465,7 @@ static inline struct tb_service *tb_to_service(struct device *dev)
*/
struct tb_service_driver {
struct device_driver driver;
int (*probe)(struct tb_service *svc, const struct tb_service_id *id);
int (*probe)(struct tb_service *svc);
void (*remove)(struct tb_service *svc);
void (*shutdown)(struct tb_service *svc);
const struct tb_service_id *id_table;
@@ -514,6 +514,11 @@ void tb_service_properties_changed(struct tb_service *svc);
* @hop_count: Number of rings (end point hops) supported by NHI.
* @quirks: NHI specific quirks if any
* @domain_released: Completed when domain has been fully released
* @host_reset: Host router was reset on driver load, or forced on system
* shutdown/reboot. When set, tb_stop() asserts DPR on connected
* downstream ports to signal disconnect before tearing down the
* router tree. Only Thunderbolt 3 devices are reset; USB4
* routers are skipped.
*/
struct tb_nhi {
spinlock_t lock;
@@ -528,6 +533,7 @@ struct tb_nhi {
u32 hop_count;
unsigned long quirks;
struct completion domain_released;
bool host_reset;
};
/**