Skip to content

Commit 9fafd46

Browse files
renesapiensiwesteri
authored andcommitted
thunderbolt: Add optional voltage offset range for receiver lane margining
Add optional extended voltage offset range support for software and hardware margining as defined by the USB4 specification. If supported, it can be enabled like below: # cd /sys/kernel/debug/thunderbolt/ROUTER/portX/margining/ # echo Y > optional_voltage_offset Signed-off-by: Rene Sapiens <[email protected]> Co-developed-by: R Kannappan <[email protected]> Signed-off-by: R Kannappan <[email protected]> Co-developed-by: Aapo Vienamo <[email protected]> Signed-off-by: Aapo Vienamo <[email protected]> Signed-off-by: Mika Westerberg <[email protected]>
1 parent 81f848d commit 9fafd46

File tree

4 files changed

+85
-0
lines changed

4 files changed

+85
-0
lines changed

drivers/thunderbolt/debugfs.c

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,8 +394,12 @@ static ssize_t retimer_sb_regs_write(struct file *file,
394394
* @ber_level: Current BER level contour value
395395
* @voltage_steps: Number of mandatory voltage steps
396396
* @max_voltage_offset: Maximum mandatory voltage offset (in mV)
397+
* @voltage_steps_optional_range: Number of voltage steps for optional range
398+
* @max_voltage_offset_optional_range: Maximum voltage offset for the optional
399+
* range (in mV).
397400
* @time_steps: Number of time margin steps
398401
* @max_time_offset: Maximum time margin offset (in mUI)
402+
* @optional_voltage_offset_range: Enable optional extended voltage range
399403
* @software: %true if software margining is used instead of hardware
400404
* @time: %true if time margining is used instead of voltage
401405
* @right_high: %false if left/low margin test is performed, %true if
@@ -414,8 +418,11 @@ struct tb_margining {
414418
unsigned int ber_level;
415419
unsigned int voltage_steps;
416420
unsigned int max_voltage_offset;
421+
unsigned int voltage_steps_optional_range;
422+
unsigned int max_voltage_offset_optional_range;
417423
unsigned int time_steps;
418424
unsigned int max_time_offset;
425+
bool optional_voltage_offset_range;
419426
bool software;
420427
bool time;
421428
bool right_high;
@@ -454,6 +461,12 @@ independent_time_margins(const struct tb_margining *margining)
454461
return FIELD_GET(USB4_MARGIN_CAP_1_TIME_INDP_MASK, margining->caps[1]);
455462
}
456463

464+
static bool
465+
supports_optional_voltage_offset_range(const struct tb_margining *margining)
466+
{
467+
return margining->caps[0] & USB4_MARGIN_CAP_0_OPT_VOLTAGE_SUPPORT;
468+
}
469+
457470
static ssize_t
458471
margining_ber_level_write(struct file *file, const char __user *user_buf,
459472
size_t count, loff_t *ppos)
@@ -553,6 +566,14 @@ static int margining_caps_show(struct seq_file *s, void *not_used)
553566
margining->voltage_steps);
554567
seq_printf(s, "# maximum voltage offset: %u mV\n",
555568
margining->max_voltage_offset);
569+
seq_printf(s, "# optional voltage offset range support: %s\n",
570+
str_yes_no(supports_optional_voltage_offset_range(margining)));
571+
if (supports_optional_voltage_offset_range(margining)) {
572+
seq_printf(s, "# voltage margin steps, optional range: %u\n",
573+
margining->voltage_steps_optional_range);
574+
seq_printf(s, "# maximum voltage offset, optional range: %u mV\n",
575+
margining->max_voltage_offset_optional_range);
576+
}
556577

557578
switch (independent_voltage_margins(margining)) {
558579
case USB4_MARGIN_CAP_0_VOLTAGE_MIN:
@@ -667,6 +688,42 @@ static int margining_lanes_show(struct seq_file *s, void *not_used)
667688
}
668689
DEBUGFS_ATTR_RW(margining_lanes);
669690

691+
static ssize_t
692+
margining_optional_voltage_offset_write(struct file *file,
693+
const char __user *user_buf,
694+
size_t count, loff_t *ppos)
695+
{
696+
struct seq_file *s = file->private_data;
697+
struct tb_margining *margining = s->private;
698+
struct tb *tb = margining->port->sw->tb;
699+
bool val;
700+
int ret;
701+
702+
ret = kstrtobool_from_user(user_buf, count, &val);
703+
if (ret)
704+
return ret;
705+
706+
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) {
707+
margining->optional_voltage_offset_range = val;
708+
}
709+
710+
return count;
711+
}
712+
713+
static int margining_optional_voltage_offset_show(struct seq_file *s,
714+
void *not_used)
715+
{
716+
struct tb_margining *margining = s->private;
717+
struct tb *tb = margining->port->sw->tb;
718+
719+
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) {
720+
seq_printf(s, "%u\n", margining->optional_voltage_offset_range);
721+
}
722+
723+
return 0;
724+
}
725+
DEBUGFS_ATTR_RW(margining_optional_voltage_offset);
726+
670727
static ssize_t margining_mode_write(struct file *file,
671728
const char __user *user_buf,
672729
size_t count, loff_t *ppos)
@@ -785,6 +842,7 @@ static int margining_run_write(void *data, u64 val)
785842
.lanes = margining->lanes,
786843
.time = margining->time,
787844
.right_high = margining->right_high,
845+
.optional_voltage_offset_range = margining->optional_voltage_offset_range,
788846
};
789847

790848
tb_port_dbg(port,
@@ -806,6 +864,7 @@ static int margining_run_write(void *data, u64 val)
806864
.lanes = margining->lanes,
807865
.time = margining->time,
808866
.right_high = margining->right_high,
867+
.optional_voltage_offset_range = margining->optional_voltage_offset_range,
809868
};
810869

811870
/* Clear the results */
@@ -865,6 +924,8 @@ static void voltage_margin_show(struct seq_file *s,
865924
if (val & USB4_MARGIN_HW_RES_1_EXCEEDS)
866925
seq_puts(s, " exceeds maximum");
867926
seq_puts(s, "\n");
927+
if (margining->optional_voltage_offset_range)
928+
seq_puts(s, " optional voltage offset range enabled\n");
868929
}
869930

870931
static void time_margin_show(struct seq_file *s,
@@ -1104,6 +1165,15 @@ static struct tb_margining *margining_alloc(struct tb_port *port,
11041165
val = FIELD_GET(USB4_MARGIN_CAP_0_MAX_VOLTAGE_OFFSET_MASK, margining->caps[0]);
11051166
margining->max_voltage_offset = 74 + val * 2;
11061167

1168+
if (supports_optional_voltage_offset_range(margining)) {
1169+
val = FIELD_GET(USB4_MARGIN_CAP_0_VOLT_STEPS_OPT_MASK,
1170+
margining->caps[0]);
1171+
margining->voltage_steps_optional_range = val;
1172+
val = FIELD_GET(USB4_MARGIN_CAP_1_MAX_VOLT_OFS_OPT_MASK,
1173+
margining->caps[1]);
1174+
margining->max_voltage_offset_optional_range = 74 + val * 2;
1175+
}
1176+
11071177
if (supports_time(margining)) {
11081178
val = FIELD_GET(USB4_MARGIN_CAP_1_TIME_STEPS_MASK, margining->caps[1]);
11091179
margining->time_steps = val;
@@ -1140,6 +1210,10 @@ static struct tb_margining *margining_alloc(struct tb_port *port,
11401210
independent_time_margins(margining) == USB4_MARGIN_CAP_1_TIME_LR))
11411211
debugfs_create_file("margin", 0600, dir, margining,
11421212
&margining_margin_fops);
1213+
1214+
if (supports_optional_voltage_offset_range(margining))
1215+
debugfs_create_file("optional_voltage_offset", DEBUGFS_MODE, dir, margining,
1216+
&margining_optional_voltage_offset_fops);
11431217
return margining;
11441218
}
11451219

drivers/thunderbolt/sb_regs.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ enum usb4_sb_opcode {
5757
#define USB4_MARGIN_CAP_0_TIME BIT(5)
5858
#define USB4_MARGIN_CAP_0_VOLTAGE_STEPS_MASK GENMASK(12, 6)
5959
#define USB4_MARGIN_CAP_0_MAX_VOLTAGE_OFFSET_MASK GENMASK(18, 13)
60+
#define USB4_MARGIN_CAP_0_OPT_VOLTAGE_SUPPORT BIT(19)
61+
#define USB4_MARGIN_CAP_0_VOLT_STEPS_OPT_MASK GENMASK(26, 20)
62+
#define USB4_MARGIN_CAP_1_MAX_VOLT_OFS_OPT_MASK GENMASK(7, 0)
6063
#define USB4_MARGIN_CAP_1_TIME_DESTR BIT(8)
6164
#define USB4_MARGIN_CAP_1_TIME_INDP_MASK GENMASK(10, 9)
6265
#define USB4_MARGIN_CAP_1_TIME_MIN 0x0
@@ -72,6 +75,7 @@ enum usb4_sb_opcode {
7275
#define USB4_MARGIN_HW_RH BIT(4)
7376
#define USB4_MARGIN_HW_BER_MASK GENMASK(9, 5)
7477
#define USB4_MARGIN_HW_BER_SHIFT 5
78+
#define USB4_MARGIN_HW_OPT_VOLTAGE BIT(10)
7579

7680
/* Applicable to all margin values */
7781
#define USB4_MARGIN_HW_RES_1_MARGIN_MASK GENMASK(6, 0)
@@ -84,6 +88,7 @@ enum usb4_sb_opcode {
8488
/* USB4_SB_OPCODE_RUN_SW_LANE_MARGINING */
8589
#define USB4_MARGIN_SW_TIME BIT(3)
8690
#define USB4_MARGIN_SW_RH BIT(4)
91+
#define USB4_MARGIN_SW_OPT_VOLTAGE BIT(5)
8792
#define USB4_MARGIN_SW_COUNTER_MASK GENMASK(14, 13)
8893

8994
#endif

drivers/thunderbolt/tb.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1372,13 +1372,15 @@ enum usb4_margin_sw_error_counter {
13721372
* @error_counter: Error counter operation for software margining
13731373
* @ber_level: Current BER level contour value
13741374
* @lanes: %0, %1 or %7 (all)
1375+
* @optional_voltage_offset_range: Enable optional extended voltage range
13751376
* @right_high: %false if left/low margin test is performed, %true if right/high
13761377
* @time: %true if time margining is used instead of voltage
13771378
*/
13781379
struct usb4_port_margining_params {
13791380
enum usb4_margin_sw_error_counter error_counter;
13801381
u32 ber_level;
13811382
u32 lanes;
1383+
bool optional_voltage_offset_range;
13821384
bool right_high;
13831385
bool time;
13841386
};

drivers/thunderbolt/usb4.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1676,6 +1676,8 @@ int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target,
16761676
val |= USB4_MARGIN_HW_RH;
16771677
if (params->ber_level)
16781678
val |= FIELD_PREP(USB4_MARGIN_HW_BER_MASK, params->ber_level);
1679+
if (params->optional_voltage_offset_range)
1680+
val |= USB4_MARGIN_HW_OPT_VOLTAGE;
16791681

16801682
ret = usb4_port_sb_write(port, target, index, USB4_SB_METADATA, &val,
16811683
sizeof(val));
@@ -1716,6 +1718,8 @@ int usb4_port_sw_margin(struct tb_port *port, enum usb4_sb_target target,
17161718
val = params->lanes;
17171719
if (params->time)
17181720
val |= USB4_MARGIN_SW_TIME;
1721+
if (params->optional_voltage_offset_range)
1722+
val |= USB4_MARGIN_SW_OPT_VOLTAGE;
17191723
if (params->right_high)
17201724
val |= USB4_MARGIN_SW_RH;
17211725
val |= FIELD_PREP(USB4_MARGIN_SW_COUNTER_MASK, params->error_counter);

0 commit comments

Comments
 (0)