9
9
10
10
#include <linux/bitfield.h>
11
11
#include <linux/debugfs.h>
12
+ #include <linux/delay.h>
12
13
#include <linux/pm_runtime.h>
13
14
#include <linux/uaccess.h>
14
15
34
35
35
36
#define COUNTER_SET_LEN 3
36
37
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
+
37
46
/* Sideband registers and their sizes as defined in the USB4 spec */
38
47
struct sb_reg {
39
48
unsigned int reg ;
@@ -399,6 +408,9 @@ static ssize_t retimer_sb_regs_write(struct file *file,
399
408
* range (in mV).
400
409
* @time_steps: Number of time margin steps
401
410
* @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
402
414
* @optional_voltage_offset_range: Enable optional extended voltage range
403
415
* @software: %true if software margining is used instead of hardware
404
416
* @time: %true if time margining is used instead of voltage
@@ -422,12 +434,33 @@ struct tb_margining {
422
434
unsigned int max_voltage_offset_optional_range ;
423
435
unsigned int time_steps ;
424
436
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 ;
425
440
bool optional_voltage_offset_range ;
426
441
bool software ;
427
442
bool time ;
428
443
bool right_high ;
429
444
};
430
445
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
+
431
464
static bool supports_software (const struct tb_margining * margining )
432
465
{
433
466
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)
689
722
DEBUGFS_ATTR_RW (margining_lanes );
690
723
691
724
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 )
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 )
695
884
{
696
885
struct seq_file * s = file -> private_data ;
697
886
struct tb_margining * margining = s -> private ;
@@ -796,6 +985,51 @@ static int margining_mode_show(struct seq_file *s, void *not_used)
796
985
}
797
986
DEBUGFS_ATTR_RW (margining_mode );
798
987
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
+
799
1033
static int margining_run_write (void * data , u64 val )
800
1034
{
801
1035
struct tb_margining * margining = data ;
@@ -836,11 +1070,15 @@ static int margining_run_write(void *data, u64 val)
836
1070
clx = ret ;
837
1071
}
838
1072
1073
+ /* Clear the results */
1074
+ memset (margining -> results , 0 , sizeof (margining -> results ));
1075
+
839
1076
if (margining -> software ) {
840
1077
struct usb4_port_margining_params params = {
841
1078
.error_counter = USB4_MARGIN_SW_ERROR_COUNTER_CLEAR ,
842
1079
.lanes = margining -> lanes ,
843
1080
.time = margining -> time ,
1081
+ .voltage_time_offset = margining -> voltage_time_offset ,
844
1082
.right_high = margining -> right_high ,
845
1083
.optional_voltage_offset_range = margining -> optional_voltage_offset_range ,
846
1084
};
@@ -850,14 +1088,7 @@ static int margining_run_write(void *data, u64 val)
850
1088
margining -> time ? "time" : "voltage" , dev_name (dev ),
851
1089
margining -> lanes );
852
1090
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 );
861
1092
} else {
862
1093
struct usb4_port_margining_params params = {
863
1094
.ber_level = margining -> ber_level ,
@@ -867,10 +1098,6 @@ static int margining_run_write(void *data, u64 val)
867
1098
.optional_voltage_offset_range = margining -> optional_voltage_offset_range ,
868
1099
};
869
1100
870
- /* Clear the results */
871
- margining -> results [0 ] = 0 ;
872
- margining -> results [1 ] = 0 ;
873
-
874
1101
tb_port_dbg (port ,
875
1102
"running hardware %s lane margining for %s lanes %u\n" ,
876
1103
margining -> time ? "time" : "voltage" , dev_name (dev ),
@@ -880,7 +1107,6 @@ static int margining_run_write(void *data, u64 val)
880
1107
margining -> results );
881
1108
}
882
1109
883
- out_clx :
884
1110
if (down_sw )
885
1111
tb_switch_clx_enable (down_sw , clx );
886
1112
out_unlock :
@@ -909,6 +1135,13 @@ static ssize_t margining_results_write(struct file *file,
909
1135
margining -> results [0 ] = 0 ;
910
1136
margining -> results [1 ] = 0 ;
911
1137
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
+
912
1145
mutex_unlock (& tb -> lock );
913
1146
return count ;
914
1147
}
@@ -998,6 +1231,24 @@ static int margining_results_show(struct seq_file *s, void *not_used)
998
1231
voltage_margin_show (s , margining , val );
999
1232
}
1000
1233
}
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
+ }
1001
1252
}
1002
1253
1003
1254
mutex_unlock (& tb -> lock );
@@ -1211,9 +1462,21 @@ static struct tb_margining *margining_alloc(struct tb_port *port,
1211
1462
debugfs_create_file ("margin" , 0600 , dir , margining ,
1212
1463
& margining_margin_fops );
1213
1464
1465
+ margining -> error_counter = USB4_MARGIN_SW_ERROR_COUNTER_CLEAR ;
1466
+ margining -> dwell_time = MIN_DWELL_TIME ;
1467
+
1214
1468
if (supports_optional_voltage_offset_range (margining ))
1215
1469
debugfs_create_file ("optional_voltage_offset" , DEBUGFS_MODE , dir , margining ,
1216
1470
& 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
+ }
1217
1480
return margining ;
1218
1481
}
1219
1482
0 commit comments