Skip to content

Commit 59a54c5

Browse files
Sanath Swesteri
Sanath S
authored andcommitted
thunderbolt: Reset topology created by the boot firmware
Boot firmware (typically BIOS) might have created tunnels of its own. The tunnel configuration that it does might be sub-optimal. For instance it may only support HBR2 monitors so the DisplayPort tunnels it created may limit Linux graphics drivers. In addition there is an issue on some AMD based systems where the BIOS does not allocate enough PCIe resources for future topology extension. By resetting the USB4 topology the PCIe links will be reset as well allowing Linux to re-allocate. This aligns the behavior with Windows Connection Manager. We already issued host router reset for USB4 v2 routers, now extend it to USB4 v1 routers as well. For pre-USB4 (that's Apple systems) we leave it as is and continue to discover the existing tunnels. Suggested-by: Mario Limonciello <[email protected]> Signed-off-by: Sanath S <[email protected]> Signed-off-by: Mika Westerberg <[email protected]>
1 parent ec8162b commit 59a54c5

File tree

5 files changed

+38
-18
lines changed

5 files changed

+38
-18
lines changed

drivers/thunderbolt/domain.c

+3-2
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,7 @@ struct tb *tb_domain_alloc(struct tb_nhi *nhi, int timeout_msec, size_t privsize
423423
/**
424424
* tb_domain_add() - Add domain to the system
425425
* @tb: Domain to add
426+
* @reset: Issue reset to the host router
426427
*
427428
* Starts the domain and adds it to the system. Hotplugging devices will
428429
* work after this has been returned successfully. In order to remove
@@ -431,7 +432,7 @@ struct tb *tb_domain_alloc(struct tb_nhi *nhi, int timeout_msec, size_t privsize
431432
*
432433
* Return: %0 in case of success and negative errno in case of error
433434
*/
434-
int tb_domain_add(struct tb *tb)
435+
int tb_domain_add(struct tb *tb, bool reset)
435436
{
436437
int ret;
437438

@@ -460,7 +461,7 @@ int tb_domain_add(struct tb *tb)
460461

461462
/* Start the domain */
462463
if (tb->cm_ops->start) {
463-
ret = tb->cm_ops->start(tb);
464+
ret = tb->cm_ops->start(tb, reset);
464465
if (ret)
465466
goto err_domain_del;
466467
}

drivers/thunderbolt/icm.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -2144,7 +2144,7 @@ static int icm_runtime_resume(struct tb *tb)
21442144
return 0;
21452145
}
21462146

2147-
static int icm_start(struct tb *tb)
2147+
static int icm_start(struct tb *tb, bool not_used)
21482148
{
21492149
struct icm *icm = tb_priv(tb);
21502150
int ret;

drivers/thunderbolt/nhi.c

+13-6
Original file line numberDiff line numberDiff line change
@@ -1221,19 +1221,19 @@ static void nhi_check_iommu(struct tb_nhi *nhi)
12211221
str_enabled_disabled(port_ok));
12221222
}
12231223

1224-
static void nhi_reset(struct tb_nhi *nhi)
1224+
static bool nhi_reset(struct tb_nhi *nhi)
12251225
{
12261226
ktime_t timeout;
12271227
u32 val;
12281228

12291229
val = ioread32(nhi->iobase + REG_CAPS);
12301230
/* Reset only v2 and later routers */
12311231
if (FIELD_GET(REG_CAPS_VERSION_MASK, val) < REG_CAPS_VERSION_2)
1232-
return;
1232+
return false;
12331233

12341234
if (!host_reset) {
12351235
dev_dbg(&nhi->pdev->dev, "skipping host router reset\n");
1236-
return;
1236+
return false;
12371237
}
12381238

12391239
iowrite32(REG_RESET_HRR, nhi->iobase + REG_RESET);
@@ -1244,12 +1244,14 @@ static void nhi_reset(struct tb_nhi *nhi)
12441244
val = ioread32(nhi->iobase + REG_RESET);
12451245
if (!(val & REG_RESET_HRR)) {
12461246
dev_warn(&nhi->pdev->dev, "host router reset successful\n");
1247-
return;
1247+
return true;
12481248
}
12491249
usleep_range(10, 20);
12501250
} while (ktime_before(ktime_get(), timeout));
12511251

12521252
dev_warn(&nhi->pdev->dev, "timeout resetting host router\n");
1253+
1254+
return false;
12531255
}
12541256

12551257
static int nhi_init_msi(struct tb_nhi *nhi)
@@ -1331,6 +1333,7 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
13311333
struct device *dev = &pdev->dev;
13321334
struct tb_nhi *nhi;
13331335
struct tb *tb;
1336+
bool reset;
13341337
int res;
13351338

13361339
if (!nhi_imr_valid(pdev))
@@ -1365,7 +1368,11 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
13651368
nhi_check_quirks(nhi);
13661369
nhi_check_iommu(nhi);
13671370

1368-
nhi_reset(nhi);
1371+
/*
1372+
* Only USB4 v2 hosts support host reset so if we already did
1373+
* that then don't do it again when the domain is initialized.
1374+
*/
1375+
reset = nhi_reset(nhi) ? false : host_reset;
13691376

13701377
res = nhi_init_msi(nhi);
13711378
if (res)
@@ -1392,7 +1399,7 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
13921399

13931400
dev_dbg(dev, "NHI initialized, starting thunderbolt\n");
13941401

1395-
res = tb_domain_add(tb);
1402+
res = tb_domain_add(tb, reset);
13961403
if (res) {
13971404
/*
13981405
* At this point the RX/TX rings might already have been

drivers/thunderbolt/tb.c

+19-7
Original file line numberDiff line numberDiff line change
@@ -2581,7 +2581,7 @@ static int tb_scan_finalize_switch(struct device *dev, void *data)
25812581
return 0;
25822582
}
25832583

2584-
static int tb_start(struct tb *tb)
2584+
static int tb_start(struct tb *tb, bool reset)
25852585
{
25862586
struct tb_cm *tcm = tb_priv(tb);
25872587
int ret;
@@ -2622,12 +2622,24 @@ static int tb_start(struct tb *tb)
26222622
tb_switch_tmu_configure(tb->root_switch, TB_SWITCH_TMU_MODE_LOWRES);
26232623
/* Enable TMU if it is off */
26242624
tb_switch_tmu_enable(tb->root_switch);
2625-
/* Full scan to discover devices added before the driver was loaded. */
2626-
tb_scan_switch(tb->root_switch);
2627-
/* Find out tunnels created by the boot firmware */
2628-
tb_discover_tunnels(tb);
2629-
/* Add DP resources from the DP tunnels created by the boot firmware */
2630-
tb_discover_dp_resources(tb);
2625+
2626+
/*
2627+
* Boot firmware might have created tunnels of its own. Since we
2628+
* cannot be sure they are usable for us, tear them down and
2629+
* reset the ports to handle it as new hotplug for USB4 v1
2630+
* routers (for USB4 v2 and beyond we already do host reset).
2631+
*/
2632+
if (reset && usb4_switch_version(tb->root_switch) == 1) {
2633+
tb_switch_reset(tb->root_switch);
2634+
} else {
2635+
/* Full scan to discover devices added before the driver was loaded. */
2636+
tb_scan_switch(tb->root_switch);
2637+
/* Find out tunnels created by the boot firmware */
2638+
tb_discover_tunnels(tb);
2639+
/* Add DP resources from the DP tunnels created by the boot firmware */
2640+
tb_discover_dp_resources(tb);
2641+
}
2642+
26312643
/*
26322644
* If the boot firmware did not create USB 3.x tunnels create them
26332645
* now for the whole topology.

drivers/thunderbolt/tb.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,7 @@ struct tb_path {
483483
*/
484484
struct tb_cm_ops {
485485
int (*driver_ready)(struct tb *tb);
486-
int (*start)(struct tb *tb);
486+
int (*start)(struct tb *tb, bool reset);
487487
void (*stop)(struct tb *tb);
488488
int (*suspend_noirq)(struct tb *tb);
489489
int (*resume_noirq)(struct tb *tb);
@@ -746,7 +746,7 @@ int tb_xdomain_init(void);
746746
void tb_xdomain_exit(void);
747747

748748
struct tb *tb_domain_alloc(struct tb_nhi *nhi, int timeout_msec, size_t privsize);
749-
int tb_domain_add(struct tb *tb);
749+
int tb_domain_add(struct tb *tb, bool reset);
750750
void tb_domain_remove(struct tb *tb);
751751
int tb_domain_suspend_noirq(struct tb *tb);
752752
int tb_domain_resume_noirq(struct tb *tb);

0 commit comments

Comments
 (0)