summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-07-03 16:21:45 +0100
committerMark Brown <broonie@kernel.org>2026-07-03 16:21:45 +0100
commitfeb1b061d4ddc5c2375f44df6f1fd6ad680ec3de (patch)
tree71983b622e9bb99aebee99c03fd5fe1e5fc486a8 /drivers
parent82704ada8fa0cc0b245e1f0f2e5c8e88118053d1 (diff)
parentd49d71ebec8f03eee2514304279a05a4952fadf7 (diff)
downloadlinux-next-feb1b061d4ddc5c2375f44df6f1fd6ad680ec3de.tar.gz
linux-next-feb1b061d4ddc5c2375f44df6f1fd6ad680ec3de.zip
Merge branch 'next' of https://git.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt.git
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/thunderbolt/main.c4
-rw-r--r--drivers/thunderbolt/dma_test.c4
-rw-r--r--drivers/thunderbolt/domain.c4
-rw-r--r--drivers/thunderbolt/nhi.c2
-rw-r--r--drivers/thunderbolt/pci.c28
-rw-r--r--drivers/thunderbolt/stream.c4
-rw-r--r--drivers/thunderbolt/switch.c11
-rw-r--r--drivers/thunderbolt/tb.c21
-rw-r--r--drivers/thunderbolt/tb.h1
-rw-r--r--drivers/thunderbolt/xdomain.c4
10 files changed, 68 insertions, 15 deletions
diff --git a/drivers/net/thunderbolt/main.c b/drivers/net/thunderbolt/main.c
index 02a91650561a..eab443ffe0fd 100644
--- a/drivers/net/thunderbolt/main.c
+++ b/drivers/net/thunderbolt/main.c
@@ -1339,7 +1339,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;
@@ -1459,7 +1459,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);
diff --git a/drivers/thunderbolt/dma_test.c b/drivers/thunderbolt/dma_test.c
index 7877319b1b03..519c67678b08 100644
--- a/drivers/thunderbolt/dma_test.c
+++ b/drivers/thunderbolt/dma_test.c
@@ -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);
diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c
index 479fa4d265c2..24611f05b3cd 100644
--- a/drivers/thunderbolt/domain.c
+++ b/drivers/thunderbolt/domain.c
@@ -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)
diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c
index 0f795ea58756..698fb124d529 100644
--- a/drivers/thunderbolt/nhi.c
+++ b/drivers/thunderbolt/nhi.c
@@ -1235,6 +1235,8 @@ int nhi_probe(struct tb_nhi *nhi)
init_completion(&nhi->domain_released);
+ nhi->host_reset = host_reset;
+
res = tb_domain_add(tb, host_reset);
if (res) {
/*
diff --git a/drivers/thunderbolt/pci.c b/drivers/thunderbolt/pci.c
index bbd186c29ef7..dbb6badda867 100644
--- a/drivers/thunderbolt/pci.c
+++ b/drivers/thunderbolt/pci.c
@@ -230,7 +230,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 +256,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 +424,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 +479,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 +501,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 +611,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,
};
diff --git a/drivers/thunderbolt/stream.c b/drivers/thunderbolt/stream.c
index 4cc86d8d6491..7368a07934ac 100644
--- a/drivers/thunderbolt/stream.c
+++ b/drivers/thunderbolt/stream.c
@@ -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);
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index a830c82bb905..404c0693df50 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -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;
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index f43f2d952372..47753a5c0f2e 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -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 */
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index ec9192b61bc0..4373336d9425 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -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);
diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c
index 86b2f7474670..c179bd751fe4 100644
--- a/drivers/thunderbolt/xdomain.c
+++ b/drivers/thunderbolt/xdomain.c
@@ -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;