@@ -394,8 +394,12 @@ static ssize_t retimer_sb_regs_write(struct file *file,
394
394
* @ber_level: Current BER level contour value
395
395
* @voltage_steps: Number of mandatory voltage steps
396
396
* @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).
397
400
* @time_steps: Number of time margin steps
398
401
* @max_time_offset: Maximum time margin offset (in mUI)
402
+ * @optional_voltage_offset_range: Enable optional extended voltage range
399
403
* @software: %true if software margining is used instead of hardware
400
404
* @time: %true if time margining is used instead of voltage
401
405
* @right_high: %false if left/low margin test is performed, %true if
@@ -414,8 +418,11 @@ struct tb_margining {
414
418
unsigned int ber_level ;
415
419
unsigned int voltage_steps ;
416
420
unsigned int max_voltage_offset ;
421
+ unsigned int voltage_steps_optional_range ;
422
+ unsigned int max_voltage_offset_optional_range ;
417
423
unsigned int time_steps ;
418
424
unsigned int max_time_offset ;
425
+ bool optional_voltage_offset_range ;
419
426
bool software ;
420
427
bool time ;
421
428
bool right_high ;
@@ -454,6 +461,12 @@ independent_time_margins(const struct tb_margining *margining)
454
461
return FIELD_GET (USB4_MARGIN_CAP_1_TIME_INDP_MASK , margining -> caps [1 ]);
455
462
}
456
463
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
+
457
470
static ssize_t
458
471
margining_ber_level_write (struct file * file , const char __user * user_buf ,
459
472
size_t count , loff_t * ppos )
@@ -553,6 +566,14 @@ static int margining_caps_show(struct seq_file *s, void *not_used)
553
566
margining -> voltage_steps );
554
567
seq_printf (s , "# maximum voltage offset: %u mV\n" ,
555
568
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
+ }
556
577
557
578
switch (independent_voltage_margins (margining )) {
558
579
case USB4_MARGIN_CAP_0_VOLTAGE_MIN :
@@ -667,6 +688,42 @@ static int margining_lanes_show(struct seq_file *s, void *not_used)
667
688
}
668
689
DEBUGFS_ATTR_RW (margining_lanes );
669
690
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
+
670
727
static ssize_t margining_mode_write (struct file * file ,
671
728
const char __user * user_buf ,
672
729
size_t count , loff_t * ppos )
@@ -785,6 +842,7 @@ static int margining_run_write(void *data, u64 val)
785
842
.lanes = margining -> lanes ,
786
843
.time = margining -> time ,
787
844
.right_high = margining -> right_high ,
845
+ .optional_voltage_offset_range = margining -> optional_voltage_offset_range ,
788
846
};
789
847
790
848
tb_port_dbg (port ,
@@ -806,6 +864,7 @@ static int margining_run_write(void *data, u64 val)
806
864
.lanes = margining -> lanes ,
807
865
.time = margining -> time ,
808
866
.right_high = margining -> right_high ,
867
+ .optional_voltage_offset_range = margining -> optional_voltage_offset_range ,
809
868
};
810
869
811
870
/* Clear the results */
@@ -865,6 +924,8 @@ static void voltage_margin_show(struct seq_file *s,
865
924
if (val & USB4_MARGIN_HW_RES_1_EXCEEDS )
866
925
seq_puts (s , " exceeds maximum" );
867
926
seq_puts (s , "\n" );
927
+ if (margining -> optional_voltage_offset_range )
928
+ seq_puts (s , " optional voltage offset range enabled\n" );
868
929
}
869
930
870
931
static void time_margin_show (struct seq_file * s ,
@@ -1104,6 +1165,15 @@ static struct tb_margining *margining_alloc(struct tb_port *port,
1104
1165
val = FIELD_GET (USB4_MARGIN_CAP_0_MAX_VOLTAGE_OFFSET_MASK , margining -> caps [0 ]);
1105
1166
margining -> max_voltage_offset = 74 + val * 2 ;
1106
1167
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
+
1107
1177
if (supports_time (margining )) {
1108
1178
val = FIELD_GET (USB4_MARGIN_CAP_1_TIME_STEPS_MASK , margining -> caps [1 ]);
1109
1179
margining -> time_steps = val ;
@@ -1140,6 +1210,10 @@ static struct tb_margining *margining_alloc(struct tb_port *port,
1140
1210
independent_time_margins (margining ) == USB4_MARGIN_CAP_1_TIME_LR ))
1141
1211
debugfs_create_file ("margin" , 0600 , dir , margining ,
1142
1212
& 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 );
1143
1217
return margining ;
1144
1218
}
1145
1219
0 commit comments