99
1010#include <linux/bitfield.h>
1111#include <linux/debugfs.h>
12+ #include <linux/delay.h>
1213#include <linux/pm_runtime.h>
1314#include <linux/uaccess.h>
1415
3435
3536#define COUNTER_SET_LEN 3
3637
38+ /*
39+ * USB4 spec doesn't specify dwell range, the range of 100 ms to 500 ms
40+ * probed to give good results.
41+ */
42+ #define MIN_DWELL_TIME 100 /* ms */
43+ #define MAX_DWELL_TIME 500 /* ms */
44+ #define DWELL_SAMPLE_INTERVAL 10
45+
3746/* Sideband registers and their sizes as defined in the USB4 spec */
3847struct sb_reg {
3948 unsigned int reg ;
@@ -399,6 +408,9 @@ static ssize_t retimer_sb_regs_write(struct file *file,
399408 * range (in mV).
400409 * @time_steps: Number of time margin steps
401410 * @max_time_offset: Maximum time margin offset (in mUI)
411+ * @voltage_time_offset: Offset for voltage / time for software margining
412+ * @dwell_time: Dwell time for software margining (in ms)
413+ * @error_counter: Error counter operation for software margining
402414 * @optional_voltage_offset_range: Enable optional extended voltage range
403415 * @software: %true if software margining is used instead of hardware
404416 * @time: %true if time margining is used instead of voltage
@@ -422,12 +434,33 @@ struct tb_margining {
422434 unsigned int max_voltage_offset_optional_range ;
423435 unsigned int time_steps ;
424436 unsigned int max_time_offset ;
437+ unsigned int voltage_time_offset ;
438+ unsigned int dwell_time ;
439+ enum usb4_margin_sw_error_counter error_counter ;
425440 bool optional_voltage_offset_range ;
426441 bool software ;
427442 bool time ;
428443 bool right_high ;
429444};
430445
446+ static int margining_modify_error_counter (struct tb_margining * margining ,
447+ u32 lanes , enum usb4_margin_sw_error_counter error_counter )
448+ {
449+ struct usb4_port_margining_params params = { 0 };
450+ struct tb_port * port = margining -> port ;
451+ u32 result ;
452+
453+ if (error_counter != USB4_MARGIN_SW_ERROR_COUNTER_CLEAR &&
454+ error_counter != USB4_MARGIN_SW_ERROR_COUNTER_STOP )
455+ return - EOPNOTSUPP ;
456+
457+ params .error_counter = error_counter ;
458+ params .lanes = lanes ;
459+
460+ return usb4_port_sw_margin (port , margining -> target , margining -> index ,
461+ & params , & result );
462+ }
463+
431464static bool supports_software (const struct tb_margining * margining )
432465{
433466 return margining -> caps [0 ] & USB4_MARGIN_CAP_0_MODES_SW ;
@@ -689,9 +722,165 @@ static int margining_lanes_show(struct seq_file *s, void *not_used)
689722DEBUGFS_ATTR_RW (margining_lanes );
690723
691724static ssize_t
692- margining_optional_voltage_offset_write (struct file * file ,
693- const char __user * user_buf ,
694- size_t count , loff_t * ppos )
725+ margining_voltage_time_offset_write (struct file * file ,
726+ const char __user * user_buf ,
727+ size_t count , loff_t * ppos )
728+ {
729+ struct seq_file * s = file -> private_data ;
730+ struct tb_margining * margining = s -> private ;
731+ struct tb * tb = margining -> port -> sw -> tb ;
732+ unsigned int max_margin ;
733+ unsigned int val ;
734+ int ret ;
735+
736+ ret = kstrtouint_from_user (user_buf , count , 10 , & val );
737+ if (ret )
738+ return ret ;
739+
740+ scoped_cond_guard (mutex_intr , return - ERESTARTSYS , & tb -> lock ) {
741+ if (!margining -> software )
742+ return - EOPNOTSUPP ;
743+
744+ if (margining -> time )
745+ max_margin = margining -> time_steps ;
746+ else
747+ if (margining -> optional_voltage_offset_range )
748+ max_margin = margining -> voltage_steps_optional_range ;
749+ else
750+ max_margin = margining -> voltage_steps ;
751+
752+ margining -> voltage_time_offset = clamp (val , 0 , max_margin );
753+ }
754+
755+ return count ;
756+ }
757+
758+ static int margining_voltage_time_offset_show (struct seq_file * s ,
759+ void * not_used )
760+ {
761+ const struct tb_margining * margining = s -> private ;
762+ struct tb * tb = margining -> port -> sw -> tb ;
763+
764+ scoped_cond_guard (mutex_intr , return - ERESTARTSYS , & tb -> lock ) {
765+ if (!margining -> software )
766+ return - EOPNOTSUPP ;
767+
768+ seq_printf (s , "%d\n" , margining -> voltage_time_offset );
769+ }
770+
771+ return 0 ;
772+ }
773+ DEBUGFS_ATTR_RW (margining_voltage_time_offset );
774+
775+ static ssize_t
776+ margining_error_counter_write (struct file * file , const char __user * user_buf ,
777+ size_t count , loff_t * ppos )
778+ {
779+ enum usb4_margin_sw_error_counter error_counter ;
780+ struct seq_file * s = file -> private_data ;
781+ struct tb_margining * margining = s -> private ;
782+ struct tb * tb = margining -> port -> sw -> tb ;
783+ char * buf ;
784+
785+ buf = validate_and_copy_from_user (user_buf , & count );
786+ if (IS_ERR (buf ))
787+ return PTR_ERR (buf );
788+
789+ buf [count - 1 ] = '\0' ;
790+
791+ if (!strcmp (buf , "nop" ))
792+ error_counter = USB4_MARGIN_SW_ERROR_COUNTER_NOP ;
793+ else if (!strcmp (buf , "clear" ))
794+ error_counter = USB4_MARGIN_SW_ERROR_COUNTER_CLEAR ;
795+ else if (!strcmp (buf , "start" ))
796+ error_counter = USB4_MARGIN_SW_ERROR_COUNTER_START ;
797+ else if (!strcmp (buf , "stop" ))
798+ error_counter = USB4_MARGIN_SW_ERROR_COUNTER_STOP ;
799+ else
800+ return - EINVAL ;
801+
802+ scoped_cond_guard (mutex_intr , return - ERESTARTSYS , & tb -> lock ) {
803+ if (!margining -> software )
804+ return - EOPNOTSUPP ;
805+
806+ margining -> error_counter = error_counter ;
807+ }
808+
809+ return count ;
810+ }
811+
812+ static int margining_error_counter_show (struct seq_file * s , void * not_used )
813+ {
814+ const struct tb_margining * margining = s -> private ;
815+ struct tb * tb = margining -> port -> sw -> tb ;
816+
817+ scoped_cond_guard (mutex_intr , return - ERESTARTSYS , & tb -> lock ) {
818+ if (!margining -> software )
819+ return - EOPNOTSUPP ;
820+
821+ switch (margining -> error_counter ) {
822+ case USB4_MARGIN_SW_ERROR_COUNTER_NOP :
823+ seq_puts (s , "[nop] clear start stop\n" );
824+ break ;
825+ case USB4_MARGIN_SW_ERROR_COUNTER_CLEAR :
826+ seq_puts (s , "nop [clear] start stop\n" );
827+ break ;
828+ case USB4_MARGIN_SW_ERROR_COUNTER_START :
829+ seq_puts (s , "nop clear [start] stop\n" );
830+ break ;
831+ case USB4_MARGIN_SW_ERROR_COUNTER_STOP :
832+ seq_puts (s , "nop clear start [stop]\n" );
833+ break ;
834+ }
835+ }
836+
837+ return 0 ;
838+ }
839+ DEBUGFS_ATTR_RW (margining_error_counter );
840+
841+ static ssize_t
842+ margining_dwell_time_write (struct file * file , const char __user * user_buf ,
843+ size_t count , loff_t * ppos )
844+ {
845+ struct seq_file * s = file -> private_data ;
846+ struct tb_margining * margining = s -> private ;
847+ struct tb * tb = margining -> port -> sw -> tb ;
848+ unsigned int val ;
849+ int ret ;
850+
851+ ret = kstrtouint_from_user (user_buf , count , 10 , & val );
852+ if (ret )
853+ return ret ;
854+
855+ scoped_cond_guard (mutex_intr , return - ERESTARTSYS , & tb -> lock ) {
856+ if (!margining -> software )
857+ return - EOPNOTSUPP ;
858+
859+ margining -> dwell_time = clamp (val , MIN_DWELL_TIME , MAX_DWELL_TIME );
860+ }
861+
862+ return count ;
863+ }
864+
865+ static int margining_dwell_time_show (struct seq_file * s , void * not_used )
866+ {
867+ struct tb_margining * margining = s -> private ;
868+ struct tb * tb = margining -> port -> sw -> tb ;
869+
870+ scoped_cond_guard (mutex_intr , return - ERESTARTSYS , & tb -> lock ) {
871+ if (!margining -> software )
872+ return - EOPNOTSUPP ;
873+
874+ seq_printf (s , "%d\n" , margining -> dwell_time );
875+ }
876+
877+ return 0 ;
878+ }
879+ DEBUGFS_ATTR_RW (margining_dwell_time );
880+
881+ static ssize_t
882+ margining_optional_voltage_offset_write (struct file * file , const char __user * user_buf ,
883+ size_t count , loff_t * ppos )
695884{
696885 struct seq_file * s = file -> private_data ;
697886 struct tb_margining * margining = s -> private ;
@@ -796,6 +985,51 @@ static int margining_mode_show(struct seq_file *s, void *not_used)
796985}
797986DEBUGFS_ATTR_RW (margining_mode );
798987
988+ static int margining_run_sw (struct tb_margining * margining ,
989+ struct usb4_port_margining_params * params )
990+ {
991+ u32 nsamples = margining -> dwell_time / DWELL_SAMPLE_INTERVAL ;
992+ int ret , i ;
993+
994+ ret = usb4_port_sw_margin (margining -> port , margining -> target , margining -> index ,
995+ params , margining -> results );
996+ if (ret )
997+ goto out_stop ;
998+
999+ for (i = 0 ; i <= nsamples ; i ++ ) {
1000+ u32 errors = 0 ;
1001+
1002+ ret = usb4_port_sw_margin_errors (margining -> port , margining -> target ,
1003+ margining -> index , & margining -> results [1 ]);
1004+ if (ret )
1005+ break ;
1006+
1007+ if (margining -> lanes == USB4_MARGIN_SW_LANE_0 )
1008+ errors = FIELD_GET (USB4_MARGIN_SW_ERR_COUNTER_LANE_0_MASK ,
1009+ margining -> results [1 ]);
1010+ else if (margining -> lanes == USB4_MARGIN_SW_LANE_1 )
1011+ errors = FIELD_GET (USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK ,
1012+ margining -> results [1 ]);
1013+ else if (margining -> lanes == USB4_MARGIN_SW_ALL_LANES )
1014+ errors = margining -> results [1 ];
1015+
1016+ /* Any errors stop the test */
1017+ if (errors )
1018+ break ;
1019+
1020+ fsleep (DWELL_SAMPLE_INTERVAL * USEC_PER_MSEC );
1021+ }
1022+
1023+ out_stop :
1024+ /*
1025+ * Stop the counters but don't clear them to allow the
1026+ * different error counter configurations.
1027+ */
1028+ margining_modify_error_counter (margining , margining -> lanes ,
1029+ USB4_MARGIN_SW_ERROR_COUNTER_STOP );
1030+ return ret ;
1031+ }
1032+
7991033static int margining_run_write (void * data , u64 val )
8001034{
8011035 struct tb_margining * margining = data ;
@@ -836,11 +1070,15 @@ static int margining_run_write(void *data, u64 val)
8361070 clx = ret ;
8371071 }
8381072
1073+ /* Clear the results */
1074+ memset (margining -> results , 0 , sizeof (margining -> results ));
1075+
8391076 if (margining -> software ) {
8401077 struct usb4_port_margining_params params = {
8411078 .error_counter = USB4_MARGIN_SW_ERROR_COUNTER_CLEAR ,
8421079 .lanes = margining -> lanes ,
8431080 .time = margining -> time ,
1081+ .voltage_time_offset = margining -> voltage_time_offset ,
8441082 .right_high = margining -> right_high ,
8451083 .optional_voltage_offset_range = margining -> optional_voltage_offset_range ,
8461084 };
@@ -850,14 +1088,7 @@ static int margining_run_write(void *data, u64 val)
8501088 margining -> time ? "time" : "voltage" , dev_name (dev ),
8511089 margining -> lanes );
8521090
853- ret = usb4_port_sw_margin (port , margining -> target , margining -> index , & params ,
854- & margining -> results [0 ]);
855- if (ret )
856- goto out_clx ;
857-
858- ret = usb4_port_sw_margin_errors (port , margining -> target ,
859- margining -> index ,
860- & margining -> results [0 ]);
1091+ ret = margining_run_sw (margining , & params );
8611092 } else {
8621093 struct usb4_port_margining_params params = {
8631094 .ber_level = margining -> ber_level ,
@@ -867,10 +1098,6 @@ static int margining_run_write(void *data, u64 val)
8671098 .optional_voltage_offset_range = margining -> optional_voltage_offset_range ,
8681099 };
8691100
870- /* Clear the results */
871- margining -> results [0 ] = 0 ;
872- margining -> results [1 ] = 0 ;
873-
8741101 tb_port_dbg (port ,
8751102 "running hardware %s lane margining for %s lanes %u\n" ,
8761103 margining -> time ? "time" : "voltage" , dev_name (dev ),
@@ -880,7 +1107,6 @@ static int margining_run_write(void *data, u64 val)
8801107 margining -> results );
8811108 }
8821109
883- out_clx :
8841110 if (down_sw )
8851111 tb_switch_clx_enable (down_sw , clx );
8861112out_unlock :
@@ -909,6 +1135,13 @@ static ssize_t margining_results_write(struct file *file,
9091135 margining -> results [0 ] = 0 ;
9101136 margining -> results [1 ] = 0 ;
9111137
1138+ if (margining -> software ) {
1139+ /* Clear the error counters */
1140+ margining_modify_error_counter (margining ,
1141+ USB4_MARGIN_SW_ALL_LANES ,
1142+ USB4_MARGIN_SW_ERROR_COUNTER_CLEAR );
1143+ }
1144+
9121145 mutex_unlock (& tb -> lock );
9131146 return count ;
9141147}
@@ -998,6 +1231,24 @@ static int margining_results_show(struct seq_file *s, void *not_used)
9981231 voltage_margin_show (s , margining , val );
9991232 }
10001233 }
1234+ } else {
1235+ u32 lane_errors , result ;
1236+
1237+ seq_printf (s , "0x%08x\n" , margining -> results [1 ]);
1238+ result = FIELD_GET (USB4_MARGIN_SW_LANES_MASK , margining -> results [0 ]);
1239+
1240+ if (result == USB4_MARGIN_SW_LANE_0 ||
1241+ result == USB4_MARGIN_SW_ALL_LANES ) {
1242+ lane_errors = FIELD_GET (USB4_MARGIN_SW_ERR_COUNTER_LANE_0_MASK ,
1243+ margining -> results [1 ]);
1244+ seq_printf (s , "# lane 0 errors: %u\n" , lane_errors );
1245+ }
1246+ if (result == USB4_MARGIN_SW_LANE_1 ||
1247+ result == USB4_MARGIN_SW_ALL_LANES ) {
1248+ lane_errors = FIELD_GET (USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK ,
1249+ margining -> results [1 ]);
1250+ seq_printf (s , "# lane 1 errors: %u\n" , lane_errors );
1251+ }
10011252 }
10021253
10031254 mutex_unlock (& tb -> lock );
@@ -1211,9 +1462,21 @@ static struct tb_margining *margining_alloc(struct tb_port *port,
12111462 debugfs_create_file ("margin" , 0600 , dir , margining ,
12121463 & margining_margin_fops );
12131464
1465+ margining -> error_counter = USB4_MARGIN_SW_ERROR_COUNTER_CLEAR ;
1466+ margining -> dwell_time = MIN_DWELL_TIME ;
1467+
12141468 if (supports_optional_voltage_offset_range (margining ))
12151469 debugfs_create_file ("optional_voltage_offset" , DEBUGFS_MODE , dir , margining ,
12161470 & margining_optional_voltage_offset_fops );
1471+
1472+ if (supports_software (margining )) {
1473+ debugfs_create_file ("voltage_time_offset" , DEBUGFS_MODE , dir , margining ,
1474+ & margining_voltage_time_offset_fops );
1475+ debugfs_create_file ("error_counter" , DEBUGFS_MODE , dir , margining ,
1476+ & margining_error_counter_fops );
1477+ debugfs_create_file ("dwell_time" , DEBUGFS_MODE , dir , margining ,
1478+ & margining_dwell_time_fops );
1479+ }
12171480 return margining ;
12181481}
12191482
0 commit comments