Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'thunderbolt-for-v6.12-rc1' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt into usb-next

Mika writes:

thunderbolt: Changes for v6.12 merge window

This includes following USB4/Thunderbolt changes for the v6.12 merge
window:

- Improvements for software receiver lane margining
- Enable support for optional voltage offset range for receiver lane
margining.

All these have been in linux-next with no reported issues.

* tag 'thunderbolt-for-v6.12-rc1' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt:
thunderbolt: Improve software receiver lane margining
thunderbolt: Add optional voltage offset range for receiver lane margining
thunderbolt: Consolidate margining parameters into a structure
thunderbolt: Add missing usb4_port_sb_read() to usb4_port_sw_margin()

+452 -52
+366 -16
drivers/thunderbolt/debugfs.c
··· 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 33 #define PATH_LEN 2 35 34 36 35 #define COUNTER_SET_LEN 3 36 + 37 + /* 38 + * USB4 spec doesn't specify dwell range, the range of 100 ms to 500 ms 39 + * probed to give good results. 40 + */ 41 + #define MIN_DWELL_TIME 100 /* ms */ 42 + #define MAX_DWELL_TIME 500 /* ms */ 43 + #define DWELL_SAMPLE_INTERVAL 10 37 44 38 45 /* Sideband registers and their sizes as defined in the USB4 spec */ 39 46 struct sb_reg { ··· 403 394 * @ber_level: Current BER level contour value 404 395 * @voltage_steps: Number of mandatory voltage steps 405 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). 406 400 * @time_steps: Number of time margin steps 407 401 * @max_time_offset: Maximum time margin offset (in mUI) 402 + * @voltage_time_offset: Offset for voltage / time for software margining 403 + * @dwell_time: Dwell time for software margining (in ms) 404 + * @error_counter: Error counter operation for software margining 405 + * @optional_voltage_offset_range: Enable optional extended voltage range 408 406 * @software: %true if software margining is used instead of hardware 409 407 * @time: %true if time margining is used instead of voltage 410 408 * @right_high: %false if left/low margin test is performed, %true if ··· 430 414 unsigned int ber_level; 431 415 unsigned int voltage_steps; 432 416 unsigned int max_voltage_offset; 417 + unsigned int voltage_steps_optional_range; 418 + unsigned int max_voltage_offset_optional_range; 433 419 unsigned int time_steps; 434 420 unsigned int max_time_offset; 421 + unsigned int voltage_time_offset; 422 + unsigned int dwell_time; 423 + enum usb4_margin_sw_error_counter error_counter; 424 + bool optional_voltage_offset_range; 435 425 bool software; 436 426 bool time; 437 427 bool right_high; 438 428 }; 429 + 430 + static int margining_modify_error_counter(struct tb_margining *margining, 431 + u32 lanes, enum usb4_margin_sw_error_counter error_counter) 432 + { 433 + struct usb4_port_margining_params params = { 0 }; 434 + struct tb_port *port = margining->port; 435 + u32 result; 436 + 437 + if (error_counter != USB4_MARGIN_SW_ERROR_COUNTER_CLEAR && 438 + error_counter != USB4_MARGIN_SW_ERROR_COUNTER_STOP) 439 + return -EOPNOTSUPP; 440 + 441 + params.error_counter = error_counter; 442 + params.lanes = lanes; 443 + 444 + return usb4_port_sw_margin(port, margining->target, margining->index, 445 + &params, &result); 446 + } 439 447 440 448 static bool supports_software(const struct tb_margining *margining) 441 449 { ··· 492 452 independent_time_margins(const struct tb_margining *margining) 493 453 { 494 454 return FIELD_GET(USB4_MARGIN_CAP_1_TIME_INDP_MASK, margining->caps[1]); 455 + } 456 + 457 + static bool 458 + supports_optional_voltage_offset_range(const struct tb_margining *margining) 459 + { 460 + return margining->caps[0] & USB4_MARGIN_CAP_0_OPT_VOLTAGE_SUPPORT; 495 461 } 496 462 497 463 static ssize_t ··· 599 553 margining->voltage_steps); 600 554 seq_printf(s, "# maximum voltage offset: %u mV\n", 601 555 margining->max_voltage_offset); 556 + seq_printf(s, "# optional voltage offset range support: %s\n", 557 + str_yes_no(supports_optional_voltage_offset_range(margining))); 558 + if (supports_optional_voltage_offset_range(margining)) { 559 + seq_printf(s, "# voltage margin steps, optional range: %u\n", 560 + margining->voltage_steps_optional_range); 561 + seq_printf(s, "# maximum voltage offset, optional range: %u mV\n", 562 + margining->max_voltage_offset_optional_range); 563 + } 602 564 603 565 switch (independent_voltage_margins(margining)) { 604 566 case USB4_MARGIN_CAP_0_VOLTAGE_MIN: ··· 721 667 } 722 668 DEBUGFS_ATTR_RW(margining_lanes); 723 669 670 + static ssize_t 671 + margining_voltage_time_offset_write(struct file *file, 672 + const char __user *user_buf, 673 + size_t count, loff_t *ppos) 674 + { 675 + struct seq_file *s = file->private_data; 676 + struct tb_margining *margining = s->private; 677 + struct tb *tb = margining->port->sw->tb; 678 + unsigned int max_margin; 679 + unsigned int val; 680 + int ret; 681 + 682 + ret = kstrtouint_from_user(user_buf, count, 10, &val); 683 + if (ret) 684 + return ret; 685 + 686 + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { 687 + if (!margining->software) 688 + return -EOPNOTSUPP; 689 + 690 + if (margining->time) 691 + max_margin = margining->time_steps; 692 + else 693 + if (margining->optional_voltage_offset_range) 694 + max_margin = margining->voltage_steps_optional_range; 695 + else 696 + max_margin = margining->voltage_steps; 697 + 698 + margining->voltage_time_offset = clamp(val, 0, max_margin); 699 + } 700 + 701 + return count; 702 + } 703 + 704 + static int margining_voltage_time_offset_show(struct seq_file *s, 705 + void *not_used) 706 + { 707 + const struct tb_margining *margining = s->private; 708 + struct tb *tb = margining->port->sw->tb; 709 + 710 + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { 711 + if (!margining->software) 712 + return -EOPNOTSUPP; 713 + 714 + seq_printf(s, "%d\n", margining->voltage_time_offset); 715 + } 716 + 717 + return 0; 718 + } 719 + DEBUGFS_ATTR_RW(margining_voltage_time_offset); 720 + 721 + static ssize_t 722 + margining_error_counter_write(struct file *file, const char __user *user_buf, 723 + size_t count, loff_t *ppos) 724 + { 725 + enum usb4_margin_sw_error_counter error_counter; 726 + struct seq_file *s = file->private_data; 727 + struct tb_margining *margining = s->private; 728 + struct tb *tb = margining->port->sw->tb; 729 + char *buf; 730 + 731 + buf = validate_and_copy_from_user(user_buf, &count); 732 + if (IS_ERR(buf)) 733 + return PTR_ERR(buf); 734 + 735 + buf[count - 1] = '\0'; 736 + 737 + if (!strcmp(buf, "nop")) 738 + error_counter = USB4_MARGIN_SW_ERROR_COUNTER_NOP; 739 + else if (!strcmp(buf, "clear")) 740 + error_counter = USB4_MARGIN_SW_ERROR_COUNTER_CLEAR; 741 + else if (!strcmp(buf, "start")) 742 + error_counter = USB4_MARGIN_SW_ERROR_COUNTER_START; 743 + else if (!strcmp(buf, "stop")) 744 + error_counter = USB4_MARGIN_SW_ERROR_COUNTER_STOP; 745 + else 746 + return -EINVAL; 747 + 748 + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { 749 + if (!margining->software) 750 + return -EOPNOTSUPP; 751 + 752 + margining->error_counter = error_counter; 753 + } 754 + 755 + return count; 756 + } 757 + 758 + static int margining_error_counter_show(struct seq_file *s, void *not_used) 759 + { 760 + const struct tb_margining *margining = s->private; 761 + struct tb *tb = margining->port->sw->tb; 762 + 763 + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { 764 + if (!margining->software) 765 + return -EOPNOTSUPP; 766 + 767 + switch (margining->error_counter) { 768 + case USB4_MARGIN_SW_ERROR_COUNTER_NOP: 769 + seq_puts(s, "[nop] clear start stop\n"); 770 + break; 771 + case USB4_MARGIN_SW_ERROR_COUNTER_CLEAR: 772 + seq_puts(s, "nop [clear] start stop\n"); 773 + break; 774 + case USB4_MARGIN_SW_ERROR_COUNTER_START: 775 + seq_puts(s, "nop clear [start] stop\n"); 776 + break; 777 + case USB4_MARGIN_SW_ERROR_COUNTER_STOP: 778 + seq_puts(s, "nop clear start [stop]\n"); 779 + break; 780 + } 781 + } 782 + 783 + return 0; 784 + } 785 + DEBUGFS_ATTR_RW(margining_error_counter); 786 + 787 + static ssize_t 788 + margining_dwell_time_write(struct file *file, const char __user *user_buf, 789 + size_t count, loff_t *ppos) 790 + { 791 + struct seq_file *s = file->private_data; 792 + struct tb_margining *margining = s->private; 793 + struct tb *tb = margining->port->sw->tb; 794 + unsigned int val; 795 + int ret; 796 + 797 + ret = kstrtouint_from_user(user_buf, count, 10, &val); 798 + if (ret) 799 + return ret; 800 + 801 + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { 802 + if (!margining->software) 803 + return -EOPNOTSUPP; 804 + 805 + margining->dwell_time = clamp(val, MIN_DWELL_TIME, MAX_DWELL_TIME); 806 + } 807 + 808 + return count; 809 + } 810 + 811 + static int margining_dwell_time_show(struct seq_file *s, void *not_used) 812 + { 813 + struct tb_margining *margining = s->private; 814 + struct tb *tb = margining->port->sw->tb; 815 + 816 + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { 817 + if (!margining->software) 818 + return -EOPNOTSUPP; 819 + 820 + seq_printf(s, "%d\n", margining->dwell_time); 821 + } 822 + 823 + return 0; 824 + } 825 + DEBUGFS_ATTR_RW(margining_dwell_time); 826 + 827 + static ssize_t 828 + margining_optional_voltage_offset_write(struct file *file, const char __user *user_buf, 829 + size_t count, loff_t *ppos) 830 + { 831 + struct seq_file *s = file->private_data; 832 + struct tb_margining *margining = s->private; 833 + struct tb *tb = margining->port->sw->tb; 834 + bool val; 835 + int ret; 836 + 837 + ret = kstrtobool_from_user(user_buf, count, &val); 838 + if (ret) 839 + return ret; 840 + 841 + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { 842 + margining->optional_voltage_offset_range = val; 843 + } 844 + 845 + return count; 846 + } 847 + 848 + static int margining_optional_voltage_offset_show(struct seq_file *s, 849 + void *not_used) 850 + { 851 + struct tb_margining *margining = s->private; 852 + struct tb *tb = margining->port->sw->tb; 853 + 854 + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { 855 + seq_printf(s, "%u\n", margining->optional_voltage_offset_range); 856 + } 857 + 858 + return 0; 859 + } 860 + DEBUGFS_ATTR_RW(margining_optional_voltage_offset); 861 + 724 862 static ssize_t margining_mode_write(struct file *file, 725 863 const char __user *user_buf, 726 864 size_t count, loff_t *ppos) ··· 985 739 } 986 740 DEBUGFS_ATTR_RW(margining_mode); 987 741 742 + static int margining_run_sw(struct tb_margining *margining, 743 + struct usb4_port_margining_params *params) 744 + { 745 + u32 nsamples = margining->dwell_time / DWELL_SAMPLE_INTERVAL; 746 + int ret, i; 747 + 748 + ret = usb4_port_sw_margin(margining->port, margining->target, margining->index, 749 + params, margining->results); 750 + if (ret) 751 + goto out_stop; 752 + 753 + for (i = 0; i <= nsamples; i++) { 754 + u32 errors = 0; 755 + 756 + ret = usb4_port_sw_margin_errors(margining->port, margining->target, 757 + margining->index, &margining->results[1]); 758 + if (ret) 759 + break; 760 + 761 + if (margining->lanes == USB4_MARGIN_SW_LANE_0) 762 + errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_0_MASK, 763 + margining->results[1]); 764 + else if (margining->lanes == USB4_MARGIN_SW_LANE_1) 765 + errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK, 766 + margining->results[1]); 767 + else if (margining->lanes == USB4_MARGIN_SW_ALL_LANES) 768 + errors = margining->results[1]; 769 + 770 + /* Any errors stop the test */ 771 + if (errors) 772 + break; 773 + 774 + fsleep(DWELL_SAMPLE_INTERVAL * USEC_PER_MSEC); 775 + } 776 + 777 + out_stop: 778 + /* 779 + * Stop the counters but don't clear them to allow the 780 + * different error counter configurations. 781 + */ 782 + margining_modify_error_counter(margining, margining->lanes, 783 + USB4_MARGIN_SW_ERROR_COUNTER_STOP); 784 + return ret; 785 + } 786 + 988 787 static int margining_run_write(void *data, u64 val) 989 788 { 990 789 struct tb_margining *margining = data; ··· 1070 779 clx = ret; 1071 780 } 1072 781 782 + /* Clear the results */ 783 + memset(margining->results, 0, sizeof(margining->results)); 784 + 1073 785 if (margining->software) { 786 + struct usb4_port_margining_params params = { 787 + .error_counter = USB4_MARGIN_SW_ERROR_COUNTER_CLEAR, 788 + .lanes = margining->lanes, 789 + .time = margining->time, 790 + .voltage_time_offset = margining->voltage_time_offset, 791 + .right_high = margining->right_high, 792 + .optional_voltage_offset_range = margining->optional_voltage_offset_range, 793 + }; 794 + 1074 795 tb_port_dbg(port, 1075 796 "running software %s lane margining for %s lanes %u\n", 1076 797 margining->time ? "time" : "voltage", dev_name(dev), 1077 798 margining->lanes); 1078 - ret = usb4_port_sw_margin(port, margining->target, margining->index, 1079 - margining->lanes, margining->time, 1080 - margining->right_high, 1081 - USB4_MARGIN_SW_COUNTER_CLEAR); 1082 - if (ret) 1083 - goto out_clx; 1084 799 1085 - ret = usb4_port_sw_margin_errors(port, margining->target, 1086 - margining->index, 1087 - &margining->results[0]); 800 + ret = margining_run_sw(margining, &params); 1088 801 } else { 802 + struct usb4_port_margining_params params = { 803 + .ber_level = margining->ber_level, 804 + .lanes = margining->lanes, 805 + .time = margining->time, 806 + .right_high = margining->right_high, 807 + .optional_voltage_offset_range = margining->optional_voltage_offset_range, 808 + }; 809 + 1089 810 tb_port_dbg(port, 1090 811 "running hardware %s lane margining for %s lanes %u\n", 1091 812 margining->time ? "time" : "voltage", dev_name(dev), 1092 813 margining->lanes); 1093 - /* Clear the results */ 1094 - margining->results[0] = 0; 1095 - margining->results[1] = 0; 1096 - ret = usb4_port_hw_margin(port, margining->target, margining->index, 1097 - margining->lanes, margining->ber_level, 1098 - margining->time, margining->right_high, 814 + 815 + ret = usb4_port_hw_margin(port, margining->target, margining->index, &params, 1099 816 margining->results); 1100 817 } 1101 818 1102 - out_clx: 1103 819 if (down_sw) 1104 820 tb_switch_clx_enable(down_sw, clx); 1105 821 out_unlock: ··· 1135 837 margining->results[0] = 0; 1136 838 margining->results[1] = 0; 1137 839 840 + if (margining->software) { 841 + /* Clear the error counters */ 842 + margining_modify_error_counter(margining, 843 + USB4_MARGIN_SW_ALL_LANES, 844 + USB4_MARGIN_SW_ERROR_COUNTER_CLEAR); 845 + } 846 + 1138 847 mutex_unlock(&tb->lock); 1139 848 return count; 1140 849 } ··· 1157 852 if (val & USB4_MARGIN_HW_RES_1_EXCEEDS) 1158 853 seq_puts(s, " exceeds maximum"); 1159 854 seq_puts(s, "\n"); 855 + if (margining->optional_voltage_offset_range) 856 + seq_puts(s, " optional voltage offset range enabled\n"); 1160 857 } 1161 858 1162 859 static void time_margin_show(struct seq_file *s, ··· 1230 923 seq_puts(s, "# lane 1 low voltage margin: "); 1231 924 voltage_margin_show(s, margining, val); 1232 925 } 926 + } 927 + } else { 928 + u32 lane_errors, result; 929 + 930 + seq_printf(s, "0x%08x\n", margining->results[1]); 931 + result = FIELD_GET(USB4_MARGIN_SW_LANES_MASK, margining->results[0]); 932 + 933 + if (result == USB4_MARGIN_SW_LANE_0 || 934 + result == USB4_MARGIN_SW_ALL_LANES) { 935 + lane_errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_0_MASK, 936 + margining->results[1]); 937 + seq_printf(s, "# lane 0 errors: %u\n", lane_errors); 938 + } 939 + if (result == USB4_MARGIN_SW_LANE_1 || 940 + result == USB4_MARGIN_SW_ALL_LANES) { 941 + lane_errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK, 942 + margining->results[1]); 943 + seq_printf(s, "# lane 1 errors: %u\n", lane_errors); 1233 944 } 1234 945 } 1235 946 ··· 1416 1091 val = FIELD_GET(USB4_MARGIN_CAP_0_MAX_VOLTAGE_OFFSET_MASK, margining->caps[0]); 1417 1092 margining->max_voltage_offset = 74 + val * 2; 1418 1093 1094 + if (supports_optional_voltage_offset_range(margining)) { 1095 + val = FIELD_GET(USB4_MARGIN_CAP_0_VOLT_STEPS_OPT_MASK, 1096 + margining->caps[0]); 1097 + margining->voltage_steps_optional_range = val; 1098 + val = FIELD_GET(USB4_MARGIN_CAP_1_MAX_VOLT_OFS_OPT_MASK, 1099 + margining->caps[1]); 1100 + margining->max_voltage_offset_optional_range = 74 + val * 2; 1101 + } 1102 + 1419 1103 if (supports_time(margining)) { 1420 1104 val = FIELD_GET(USB4_MARGIN_CAP_1_TIME_STEPS_MASK, margining->caps[1]); 1421 1105 margining->time_steps = val; ··· 1461 1127 independent_time_margins(margining) == USB4_MARGIN_CAP_1_TIME_LR)) 1462 1128 debugfs_create_file("margin", 0600, dir, margining, 1463 1129 &margining_margin_fops); 1130 + 1131 + margining->error_counter = USB4_MARGIN_SW_ERROR_COUNTER_CLEAR; 1132 + margining->dwell_time = MIN_DWELL_TIME; 1133 + 1134 + if (supports_optional_voltage_offset_range(margining)) 1135 + debugfs_create_file("optional_voltage_offset", DEBUGFS_MODE, dir, margining, 1136 + &margining_optional_voltage_offset_fops); 1137 + 1138 + if (supports_software(margining)) { 1139 + debugfs_create_file("voltage_time_offset", DEBUGFS_MODE, dir, margining, 1140 + &margining_voltage_time_offset_fops); 1141 + debugfs_create_file("error_counter", DEBUGFS_MODE, dir, margining, 1142 + &margining_error_counter_fops); 1143 + debugfs_create_file("dwell_time", DEBUGFS_MODE, dir, margining, 1144 + &margining_dwell_time_fops); 1145 + } 1464 1146 return margining; 1465 1147 } 1466 1148
+13 -5
drivers/thunderbolt/sb_regs.h
··· 57 57 #define USB4_MARGIN_CAP_0_TIME BIT(5) 58 58 #define USB4_MARGIN_CAP_0_VOLTAGE_STEPS_MASK GENMASK(12, 6) 59 59 #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) 60 63 #define USB4_MARGIN_CAP_1_TIME_DESTR BIT(8) 61 64 #define USB4_MARGIN_CAP_1_TIME_INDP_MASK GENMASK(10, 9) 62 65 #define USB4_MARGIN_CAP_1_TIME_MIN 0x0 ··· 75 72 #define USB4_MARGIN_HW_RH BIT(4) 76 73 #define USB4_MARGIN_HW_BER_MASK GENMASK(9, 5) 77 74 #define USB4_MARGIN_HW_BER_SHIFT 5 75 + #define USB4_MARGIN_HW_OPT_VOLTAGE BIT(10) 78 76 79 77 /* Applicable to all margin values */ 80 78 #define USB4_MARGIN_HW_RES_1_MARGIN_MASK GENMASK(6, 0) ··· 86 82 #define USB4_MARGIN_HW_RES_1_L1_LL_MARGIN_SHIFT 24 87 83 88 84 /* USB4_SB_OPCODE_RUN_SW_LANE_MARGINING */ 85 + #define USB4_MARGIN_SW_LANES_MASK GENMASK(2, 0) 86 + #define USB4_MARGIN_SW_LANE_0 0x0 87 + #define USB4_MARGIN_SW_LANE_1 0x1 88 + #define USB4_MARGIN_SW_ALL_LANES 0x7 89 89 #define USB4_MARGIN_SW_TIME BIT(3) 90 90 #define USB4_MARGIN_SW_RH BIT(4) 91 + #define USB4_MARGIN_SW_OPT_VOLTAGE BIT(5) 92 + #define USB4_MARGIN_SW_VT_MASK GENMASK(12, 6) 91 93 #define USB4_MARGIN_SW_COUNTER_MASK GENMASK(14, 13) 92 - #define USB4_MARGIN_SW_COUNTER_SHIFT 13 93 - #define USB4_MARGIN_SW_COUNTER_NOP 0x0 94 - #define USB4_MARGIN_SW_COUNTER_CLEAR 0x1 95 - #define USB4_MARGIN_SW_COUNTER_START 0x2 96 - #define USB4_MARGIN_SW_COUNTER_STOP 0x3 94 + 95 + #define USB4_MARGIN_SW_ERR_COUNTER_LANE_0_MASK GENMASK(3, 0) 96 + #define USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK GENMASK(7, 4) 97 97 98 98 #endif
+38 -4
drivers/thunderbolt/tb.h
··· 1353 1353 int usb4_port_sb_write(struct tb_port *port, enum usb4_sb_target target, 1354 1354 u8 index, u8 reg, const void *buf, u8 size); 1355 1355 1356 + /** 1357 + * enum usb4_margin_sw_error_counter - Software margining error counter operation 1358 + * @USB4_MARGIN_SW_ERROR_COUNTER_NOP: No change in counter setup 1359 + * @USB4_MARGIN_SW_ERROR_COUNTER_CLEAR: Set the error counter to 0, enable counter 1360 + * @USB4_MARGIN_SW_ERROR_COUNTER_START: Start counter, count from last value 1361 + * @USB4_MARGIN_SW_ERROR_COUNTER_STOP: Stop counter, do not clear value 1362 + */ 1363 + enum usb4_margin_sw_error_counter { 1364 + USB4_MARGIN_SW_ERROR_COUNTER_NOP, 1365 + USB4_MARGIN_SW_ERROR_COUNTER_CLEAR, 1366 + USB4_MARGIN_SW_ERROR_COUNTER_START, 1367 + USB4_MARGIN_SW_ERROR_COUNTER_STOP, 1368 + }; 1369 + 1370 + /** 1371 + * struct usb4_port_margining_params - USB4 margining parameters 1372 + * @error_counter: Error counter operation for software margining 1373 + * @ber_level: Current BER level contour value 1374 + * @lanes: %0, %1 or %7 (all) 1375 + * @voltage_time_offset: Offset for voltage / time for software margining 1376 + * @optional_voltage_offset_range: Enable optional extended voltage range 1377 + * @right_high: %false if left/low margin test is performed, %true if right/high 1378 + * @time: %true if time margining is used instead of voltage 1379 + */ 1380 + struct usb4_port_margining_params { 1381 + enum usb4_margin_sw_error_counter error_counter; 1382 + u32 ber_level; 1383 + u32 lanes; 1384 + u32 voltage_time_offset; 1385 + bool optional_voltage_offset_range; 1386 + bool right_high; 1387 + bool time; 1388 + }; 1389 + 1356 1390 int usb4_port_margining_caps(struct tb_port *port, enum usb4_sb_target target, 1357 1391 u8 index, u32 *caps); 1358 1392 int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target, 1359 - u8 index, unsigned int lanes, unsigned int ber_level, 1360 - bool timing, bool right_high, u32 *results); 1393 + u8 index, const struct usb4_port_margining_params *params, 1394 + u32 *results); 1361 1395 int usb4_port_sw_margin(struct tb_port *port, enum usb4_sb_target target, 1362 - u8 index, unsigned int lanes, bool timing, 1363 - bool right_high, u32 counter); 1396 + u8 index, const struct usb4_port_margining_params *params, 1397 + u32 *results); 1364 1398 int usb4_port_sw_margin_errors(struct tb_port *port, enum usb4_sb_target target, 1365 1399 u8 index, u32 *errors); 1366 1400
+35 -27
drivers/thunderbolt/usb4.c
··· 1653 1653 * @port: USB4 port 1654 1654 * @target: Sideband target 1655 1655 * @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER 1656 - * @lanes: Which lanes to run (must match the port capabilities). Can be 1657 - * %0, %1 or %7. 1658 - * @ber_level: BER level contour value 1659 - * @timing: Perform timing margining instead of voltage 1660 - * @right_high: Use Right/high margin instead of left/low 1656 + * @params: Parameters for USB4 hardware margining 1661 1657 * @results: Array with at least two elements to hold the results 1662 1658 * 1663 1659 * Runs hardware lane margining on USB4 port and returns the result in 1664 1660 * @results. 1665 1661 */ 1666 1662 int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target, 1667 - u8 index, unsigned int lanes, unsigned int ber_level, 1668 - bool timing, bool right_high, u32 *results) 1663 + u8 index, const struct usb4_port_margining_params *params, 1664 + u32 *results) 1669 1665 { 1670 1666 u32 val; 1671 1667 int ret; 1672 1668 1673 - val = lanes; 1674 - if (timing) 1669 + if (WARN_ON_ONCE(!params)) 1670 + return -EINVAL; 1671 + 1672 + val = params->lanes; 1673 + if (params->time) 1675 1674 val |= USB4_MARGIN_HW_TIME; 1676 - if (right_high) 1675 + if (params->right_high) 1677 1676 val |= USB4_MARGIN_HW_RH; 1678 - if (ber_level) 1679 - val |= (ber_level << USB4_MARGIN_HW_BER_SHIFT) & 1680 - USB4_MARGIN_HW_BER_MASK; 1677 + if (params->ber_level) 1678 + 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; 1681 1681 1682 1682 ret = usb4_port_sb_write(port, target, index, USB4_SB_METADATA, &val, 1683 1683 sizeof(val)); ··· 1698 1698 * @port: USB4 port 1699 1699 * @target: Sideband target 1700 1700 * @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER 1701 - * @lanes: Which lanes to run (must match the port capabilities). Can be 1702 - * %0, %1 or %7. 1703 - * @timing: Perform timing margining instead of voltage 1704 - * @right_high: Use Right/high margin instead of left/low 1705 - * @counter: What to do with the error counter 1701 + * @params: Parameters for USB4 software margining 1702 + * @results: Data word for the operation completion data 1706 1703 * 1707 1704 * Runs software lane margining on USB4 port. Read back the error 1708 1705 * counters by calling usb4_port_sw_margin_errors(). Returns %0 in 1709 1706 * success and negative errno otherwise. 1710 1707 */ 1711 1708 int usb4_port_sw_margin(struct tb_port *port, enum usb4_sb_target target, 1712 - u8 index, unsigned int lanes, bool timing, 1713 - bool right_high, u32 counter) 1709 + u8 index, const struct usb4_port_margining_params *params, 1710 + u32 *results) 1714 1711 { 1715 1712 u32 val; 1716 1713 int ret; 1717 1714 1718 - val = lanes; 1719 - if (timing) 1715 + if (WARN_ON_ONCE(!params)) 1716 + return -EINVAL; 1717 + 1718 + val = params->lanes; 1719 + if (params->time) 1720 1720 val |= USB4_MARGIN_SW_TIME; 1721 - if (right_high) 1721 + if (params->optional_voltage_offset_range) 1722 + val |= USB4_MARGIN_SW_OPT_VOLTAGE; 1723 + if (params->right_high) 1722 1724 val |= USB4_MARGIN_SW_RH; 1723 - val |= (counter << USB4_MARGIN_SW_COUNTER_SHIFT) & 1724 - USB4_MARGIN_SW_COUNTER_MASK; 1725 + val |= FIELD_PREP(USB4_MARGIN_SW_COUNTER_MASK, params->error_counter); 1726 + val |= FIELD_PREP(USB4_MARGIN_SW_VT_MASK, params->voltage_time_offset); 1725 1727 1726 1728 ret = usb4_port_sb_write(port, target, index, USB4_SB_METADATA, &val, 1727 1729 sizeof(val)); 1728 1730 if (ret) 1729 1731 return ret; 1730 1732 1731 - return usb4_port_sb_op(port, target, index, 1732 - USB4_SB_OPCODE_RUN_SW_LANE_MARGINING, 2500); 1733 + ret = usb4_port_sb_op(port, target, index, 1734 + USB4_SB_OPCODE_RUN_SW_LANE_MARGINING, 2500); 1735 + if (ret) 1736 + return ret; 1737 + 1738 + return usb4_port_sb_read(port, target, index, USB4_SB_DATA, results, 1739 + sizeof(*results)); 1740 + 1733 1741 } 1734 1742 1735 1743 /**