Skip to content

Commit ff6ab05

Browse files
committed
thunderbolt: Add receiver lane margining support for retimers
Retimers support lane margining as well so make this available through debugfs in the same way as we do for the USB4 ports. When this is enabled we also expose retimers on the other side of the cable because typically margining is implemented only on direction towards the cable. However, for the retimers on the other side of the cable we do not allow NVM upgrade to avoid confusing the existing userspace (the same retimer may now appear twice with different name) and is probably not a good idea anyway. Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
1 parent 0890fc3 commit ff6ab05

File tree

6 files changed

+128
-48
lines changed

6 files changed

+128
-48
lines changed

drivers/thunderbolt/Kconfig

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,15 @@ config USB4_DEBUGFS_WRITE
3232
this for production systems or distro kernels.
3333

3434
config USB4_DEBUGFS_MARGINING
35-
bool "Expose receiver lane margining operations under USB4 ports (DANGEROUS)"
35+
bool "Expose receiver lane margining operations under USB4 ports and retimers (DANGEROUS)"
3636
depends on DEBUG_FS
3737
depends on USB4_DEBUGFS_WRITE
3838
help
39-
Enables hardware and software based receiver lane margining support
40-
under each USB4 port. Used for electrical quality and robustness
41-
validation during manufacturing. Should not be enabled by distro
42-
kernels.
39+
Enables hardware and software based receiver lane margining
40+
support under each USB4 port and retimer, including retimers
41+
on the other side of the cable. Used for electrical quality
42+
and robustness validation during manufacturing. Should not be
43+
enabled by distro kernels.
4344

4445
config USB4_KUNIT_TEST
4546
bool "KUnit tests" if !KUNIT_ALL_TESTS

drivers/thunderbolt/debugfs.c

Lines changed: 66 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,9 @@ static ssize_t retimer_sb_regs_write(struct file *file,
380380
/**
381381
* struct tb_margining - Lane margining support
382382
* @port: USB4 port through which the margining operations are run
383+
* @target: Sideband target
384+
* @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER
385+
* @dev: Pointer to the device that is the target (USB4 port or retimer)
383386
* @caps: Port lane margining capabilities
384387
* @results: Last lane margining results
385388
* @lanes: %0, %1 or %7 (all)
@@ -397,6 +400,9 @@ static ssize_t retimer_sb_regs_write(struct file *file,
397400
*/
398401
struct tb_margining {
399402
struct tb_port *port;
403+
enum usb4_sb_target target;
404+
u8 index;
405+
struct device *dev;
400406
u32 caps[2];
401407
u32 results[2];
402408
unsigned int lanes;
@@ -736,6 +742,7 @@ static int margining_run_write(void *data, u64 val)
736742
{
737743
struct tb_margining *margining = data;
738744
struct tb_port *port = margining->port;
745+
struct device *dev = margining->dev;
739746
struct tb_switch *sw = port->sw;
740747
struct tb_switch *down_sw;
741748
struct tb *tb = sw->tb;
@@ -744,7 +751,7 @@ static int margining_run_write(void *data, u64 val)
744751
if (val != 1)
745752
return -EINVAL;
746753

747-
pm_runtime_get_sync(&sw->dev);
754+
pm_runtime_get_sync(dev);
748755

749756
if (mutex_lock_interruptible(&tb->lock)) {
750757
ret = -ERESTARTSYS;
@@ -772,24 +779,29 @@ static int margining_run_write(void *data, u64 val)
772779
}
773780

774781
if (margining->software) {
775-
tb_port_dbg(port, "running software %s lane margining for lanes %u\n",
776-
margining->time ? "time" : "voltage", margining->lanes);
777-
ret = usb4_port_sw_margin(port, USB4_SB_TARGET_ROUTER, 0,
782+
tb_port_dbg(port,
783+
"running software %s lane margining for %s lanes %u\n",
784+
margining->time ? "time" : "voltage", dev_name(dev),
785+
margining->lanes);
786+
ret = usb4_port_sw_margin(port, margining->target, margining->index,
778787
margining->lanes, margining->time,
779788
margining->right_high,
780789
USB4_MARGIN_SW_COUNTER_CLEAR);
781790
if (ret)
782791
goto out_clx;
783792

784-
ret = usb4_port_sw_margin_errors(port, USB4_SB_TARGET_ROUTER, 0,
793+
ret = usb4_port_sw_margin_errors(port, margining->target,
794+
margining->index,
785795
&margining->results[0]);
786796
} else {
787-
tb_port_dbg(port, "running hardware %s lane margining for lanes %u\n",
788-
margining->time ? "time" : "voltage", margining->lanes);
797+
tb_port_dbg(port,
798+
"running hardware %s lane margining for %s lanes %u\n",
799+
margining->time ? "time" : "voltage", dev_name(dev),
800+
margining->lanes);
789801
/* Clear the results */
790802
margining->results[0] = 0;
791803
margining->results[1] = 0;
792-
ret = usb4_port_hw_margin(port, USB4_SB_TARGET_ROUTER, 0,
804+
ret = usb4_port_hw_margin(port, margining->target, margining->index,
793805
margining->lanes, margining->ber_level,
794806
margining->time, margining->right_high,
795807
margining->results);
@@ -801,8 +813,8 @@ static int margining_run_write(void *data, u64 val)
801813
out_unlock:
802814
mutex_unlock(&tb->lock);
803815
out_rpm_put:
804-
pm_runtime_mark_last_busy(&sw->dev);
805-
pm_runtime_put_autosuspend(&sw->dev);
816+
pm_runtime_mark_last_busy(dev);
817+
pm_runtime_put_autosuspend(dev);
806818

807819
return ret;
808820
}
@@ -1044,33 +1056,29 @@ static int margining_margin_show(struct seq_file *s, void *not_used)
10441056
}
10451057
DEBUGFS_ATTR_RW(margining_margin);
10461058

1047-
static void margining_port_init(struct tb_port *port)
1059+
static struct tb_margining *margining_alloc(struct tb_port *port,
1060+
struct device *dev,
1061+
enum usb4_sb_target target,
1062+
u8 index, struct dentry *parent)
10481063
{
10491064
struct tb_margining *margining;
1050-
struct dentry *dir, *parent;
1051-
struct usb4_port *usb4;
1052-
char dir_name[10];
1065+
struct dentry *dir;
10531066
unsigned int val;
10541067
int ret;
10551068

1056-
usb4 = port->usb4;
1057-
if (!usb4)
1058-
return;
1059-
1060-
snprintf(dir_name, sizeof(dir_name), "port%d", port->port);
1061-
parent = debugfs_lookup(dir_name, port->sw->debugfs_dir);
1062-
10631069
margining = kzalloc(sizeof(*margining), GFP_KERNEL);
10641070
if (!margining)
1065-
return;
1071+
return NULL;
10661072

10671073
margining->port = port;
1074+
margining->target = target;
1075+
margining->index = index;
1076+
margining->dev = dev;
10681077

1069-
ret = usb4_port_margining_caps(port, USB4_SB_TARGET_ROUTER, 0,
1070-
margining->caps);
1078+
ret = usb4_port_margining_caps(port, target, index, margining->caps);
10711079
if (ret) {
10721080
kfree(margining);
1073-
return;
1081+
return NULL;
10741082
}
10751083

10761084
/* Set the initial mode */
@@ -1124,8 +1132,22 @@ static void margining_port_init(struct tb_port *port)
11241132
independent_time_margins(margining) == USB4_MARGIN_CAP_1_TIME_LR))
11251133
debugfs_create_file("margin", 0600, dir, margining,
11261134
&margining_margin_fops);
1135+
return margining;
1136+
}
11271137

1128-
usb4->margining = margining;
1138+
static void margining_port_init(struct tb_port *port)
1139+
{
1140+
struct dentry *parent;
1141+
char dir_name[10];
1142+
1143+
if (!port->usb4)
1144+
return;
1145+
1146+
snprintf(dir_name, sizeof(dir_name), "port%d", port->port);
1147+
parent = debugfs_lookup(dir_name, port->sw->debugfs_dir);
1148+
port->usb4->margining = margining_alloc(port, &port->usb4->dev,
1149+
USB4_SB_TARGET_ROUTER, 0,
1150+
parent);
11291151
}
11301152

11311153
static void margining_port_remove(struct tb_port *port)
@@ -1199,11 +1221,27 @@ static void margining_xdomain_remove(struct tb_xdomain *xd)
11991221
downstream = tb_port_at(xd->route, parent_sw);
12001222
margining_port_remove(downstream);
12011223
}
1224+
1225+
static void margining_retimer_init(struct tb_retimer *rt, struct dentry *debugfs_dir)
1226+
{
1227+
rt->margining = margining_alloc(rt->port, &rt->dev,
1228+
USB4_SB_TARGET_RETIMER, rt->index,
1229+
debugfs_dir);
1230+
}
1231+
1232+
static void margining_retimer_remove(struct tb_retimer *rt)
1233+
{
1234+
kfree(rt->margining);
1235+
rt->margining = NULL;
1236+
}
12021237
#else
12031238
static inline void margining_switch_init(struct tb_switch *sw) { }
12041239
static inline void margining_switch_remove(struct tb_switch *sw) { }
12051240
static inline void margining_xdomain_init(struct tb_xdomain *xd) { }
12061241
static inline void margining_xdomain_remove(struct tb_xdomain *xd) { }
1242+
static inline void margining_retimer_init(struct tb_retimer *rt,
1243+
struct dentry *debugfs_dir) { }
1244+
static inline void margining_retimer_remove(struct tb_retimer *rt) { }
12071245
#endif
12081246

12091247
static int port_clear_all_counters(struct tb_port *port)
@@ -1864,6 +1902,7 @@ void tb_retimer_debugfs_init(struct tb_retimer *rt)
18641902
debugfs_dir = debugfs_create_dir(dev_name(&rt->dev), tb_debugfs_root);
18651903
debugfs_create_file("sb_regs", DEBUGFS_MODE, debugfs_dir, rt,
18661904
&retimer_sb_regs_fops);
1905+
margining_retimer_init(rt, debugfs_dir);
18671906
}
18681907

18691908
/**
@@ -1875,6 +1914,7 @@ void tb_retimer_debugfs_init(struct tb_retimer *rt)
18751914
void tb_retimer_debugfs_remove(struct tb_retimer *rt)
18761915
{
18771916
debugfs_lookup_and_remove(dev_name(&rt->dev), tb_debugfs_root);
1917+
margining_retimer_remove(rt);
18781918
}
18791919

18801920
void tb_debugfs_init(void)

drivers/thunderbolt/retimer.c

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@
1414
#include "sb_regs.h"
1515
#include "tb.h"
1616

17+
#if IS_ENABLED(CONFIG_USB4_DEBUGFS_MARGINING)
1718
#define TB_MAX_RETIMER_INDEX 6
19+
#else
20+
#define TB_MAX_RETIMER_INDEX 2
21+
#endif
1822

1923
/**
2024
* tb_retimer_nvm_read() - Read contents of retimer NVM
@@ -319,6 +323,8 @@ static ssize_t nvm_version_show(struct device *dev,
319323

320324
if (!rt->nvm)
321325
ret = -EAGAIN;
326+
else if (rt->no_nvm_upgrade)
327+
ret = -EOPNOTSUPP;
322328
else
323329
ret = sysfs_emit(buf, "%x.%x\n", rt->nvm->major, rt->nvm->minor);
324330

@@ -366,7 +372,8 @@ const struct device_type tb_retimer_type = {
366372
.release = tb_retimer_release,
367373
};
368374

369-
static int tb_retimer_add(struct tb_port *port, u8 index, u32 auth_status)
375+
static int tb_retimer_add(struct tb_port *port, u8 index, u32 auth_status,
376+
bool on_board)
370377
{
371378
struct tb_retimer *rt;
372379
u32 vendor, device;
@@ -388,13 +395,6 @@ static int tb_retimer_add(struct tb_port *port, u8 index, u32 auth_status)
388395
return ret;
389396
}
390397

391-
/*
392-
* Check that it supports NVM operations. If not then don't add
393-
* the device at all.
394-
*/
395-
ret = usb4_port_retimer_nvm_sector_size(port, index);
396-
if (ret < 0)
397-
return ret;
398398

399399
rt = kzalloc(sizeof(*rt), GFP_KERNEL);
400400
if (!rt)
@@ -407,6 +407,13 @@ static int tb_retimer_add(struct tb_port *port, u8 index, u32 auth_status)
407407
rt->port = port;
408408
rt->tb = port->sw->tb;
409409

410+
/*
411+
* Only support NVM upgrade for on-board retimers. The retimers
412+
* on the other side of the connection.
413+
*/
414+
if (!on_board || usb4_port_retimer_nvm_sector_size(port, index) <= 0)
415+
rt->no_nvm_upgrade = true;
416+
410417
rt->dev.parent = &port->usb4->dev;
411418
rt->dev.bus = &tb_bus_type;
412419
rt->dev.type = &tb_retimer_type;
@@ -487,7 +494,7 @@ static struct tb_retimer *tb_port_find_retimer(struct tb_port *port, u8 index)
487494
int tb_retimer_scan(struct tb_port *port, bool add)
488495
{
489496
u32 status[TB_MAX_RETIMER_INDEX + 1] = {};
490-
int ret, i, last_idx = 0;
497+
int ret, i, max, last_idx = 0;
491498

492499
/*
493500
* Send broadcast RT to make sure retimer indices facing this
@@ -522,26 +529,28 @@ int tb_retimer_scan(struct tb_port *port, bool add)
522529
break;
523530
}
524531

525-
tb_retimer_unset_inbound_sbtx(port);
526-
527-
if (!last_idx)
528-
return 0;
529-
530-
/* Add on-board retimers if they do not exist already */
532+
max = i;
531533
ret = 0;
532-
for (i = 1; i <= last_idx; i++) {
534+
535+
/* Add retimers if they do not exist already */
536+
for (i = 1; i <= max; i++) {
533537
struct tb_retimer *rt;
534538

539+
/* Skip cable retimers */
540+
if (usb4_port_retimer_is_cable(port, i))
541+
continue;
542+
535543
rt = tb_port_find_retimer(port, i);
536544
if (rt) {
537545
put_device(&rt->dev);
538546
} else if (add) {
539-
ret = tb_retimer_add(port, i, status[i]);
547+
ret = tb_retimer_add(port, i, status[i], i <= last_idx);
540548
if (ret && ret != -EOPNOTSUPP)
541549
break;
542550
}
543551
}
544552

553+
tb_retimer_unset_inbound_sbtx(port);
545554
return ret;
546555
}
547556

drivers/thunderbolt/sb_regs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ enum usb4_sb_opcode {
2626
USB4_SB_OPCODE_SET_INBOUND_SBTX = 0x5055534c, /* "LSUP" */
2727
USB4_SB_OPCODE_UNSET_INBOUND_SBTX = 0x50555355, /* "USUP" */
2828
USB4_SB_OPCODE_QUERY_LAST_RETIMER = 0x5453414c, /* "LAST" */
29+
USB4_SB_OPCODE_QUERY_CABLE_RETIMER = 0x524c4243, /* "CBLR" */
2930
USB4_SB_OPCODE_GET_NVM_SECTOR_SIZE = 0x53534e47, /* "GNSS" */
3031
USB4_SB_OPCODE_NVM_SET_OFFSET = 0x53504f42, /* "BOPS" */
3132
USB4_SB_OPCODE_NVM_BLOCK_WRITE = 0x574b4c42, /* "BLKW" */

drivers/thunderbolt/tb.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ struct usb4_port {
329329
* @nvm: Pointer to the NVM if the retimer has one (%NULL otherwise)
330330
* @no_nvm_upgrade: Prevent NVM upgrade of this retimer
331331
* @auth_status: Status of last NVM authentication
332+
* @margining: Pointer to margining structure if enabled
332333
*/
333334
struct tb_retimer {
334335
struct device dev;
@@ -340,6 +341,9 @@ struct tb_retimer {
340341
struct tb_nvm *nvm;
341342
bool no_nvm_upgrade;
342343
u32 auth_status;
344+
#ifdef CONFIG_USB4_DEBUGFS_MARGINING
345+
struct tb_margining *margining;
346+
#endif
343347
};
344348

345349
/**
@@ -1363,6 +1367,7 @@ int usb4_port_sw_margin_errors(struct tb_port *port, enum usb4_sb_target target,
13631367
int usb4_port_retimer_set_inbound_sbtx(struct tb_port *port, u8 index);
13641368
int usb4_port_retimer_unset_inbound_sbtx(struct tb_port *port, u8 index);
13651369
int usb4_port_retimer_is_last(struct tb_port *port, u8 index);
1370+
int usb4_port_retimer_is_cable(struct tb_port *port, u8 index);
13661371
int usb4_port_retimer_nvm_sector_size(struct tb_port *port, u8 index);
13671372
int usb4_port_retimer_nvm_set_offset(struct tb_port *port, u8 index,
13681373
unsigned int address);

drivers/thunderbolt/usb4.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1830,6 +1830,30 @@ int usb4_port_retimer_is_last(struct tb_port *port, u8 index)
18301830
return ret ? ret : metadata & 1;
18311831
}
18321832

1833+
/**
1834+
* usb4_port_retimer_is_cable() - Is the retimer cable retimer
1835+
* @port: USB4 port
1836+
* @index: Retimer index
1837+
*
1838+
* If the retimer at @index is last cable retimer this function returns
1839+
* %1 and %0 if it is on-board retimer. In case a retimer is not present
1840+
* at @index returns %-ENODEV. Otherwise returns negative errno.
1841+
*/
1842+
int usb4_port_retimer_is_cable(struct tb_port *port, u8 index)
1843+
{
1844+
u32 metadata;
1845+
int ret;
1846+
1847+
ret = usb4_port_retimer_op(port, index, USB4_SB_OPCODE_QUERY_CABLE_RETIMER,
1848+
500);
1849+
if (ret)
1850+
return ret;
1851+
1852+
ret = usb4_port_sb_read(port, USB4_SB_TARGET_RETIMER, index,
1853+
USB4_SB_METADATA, &metadata, sizeof(metadata));
1854+
return ret ? ret : metadata & 1;
1855+
}
1856+
18331857
/**
18341858
* usb4_port_retimer_nvm_sector_size() - Read retimer NVM sector size
18351859
* @port: USB4 port

0 commit comments

Comments
 (0)