@@ -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+
457470static ssize_t
458471margining_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}
668689DEBUGFS_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+
670727static 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
870931static 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
0 commit comments