diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/net/thunderbolt/main.c | 4 | ||||
| -rw-r--r-- | drivers/thunderbolt/dma_test.c | 4 | ||||
| -rw-r--r-- | drivers/thunderbolt/domain.c | 4 | ||||
| -rw-r--r-- | drivers/thunderbolt/nhi.c | 2 | ||||
| -rw-r--r-- | drivers/thunderbolt/pci.c | 28 | ||||
| -rw-r--r-- | drivers/thunderbolt/stream.c | 4 | ||||
| -rw-r--r-- | drivers/thunderbolt/switch.c | 11 | ||||
| -rw-r--r-- | drivers/thunderbolt/tb.c | 21 | ||||
| -rw-r--r-- | drivers/thunderbolt/tb.h | 1 | ||||
| -rw-r--r-- | drivers/thunderbolt/xdomain.c | 4 |
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; |
