1 /*
2 * The PCI Utilities -- Log margining process
3 *
4 * Copyright (c) 2023-2024 KNS Group LLC (YADRO)
5 *
6 * Can be freely distributed and used under the terms of the GNU GPL v2+.
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 #include <stdarg.h>
12 #include <stdio.h>
13
14 #include "lmr.h"
15
16 bool margin_global_logging = false;
17 bool margin_print_domain = true;
18
19 void
margin_log(char * format,...)20 margin_log(char *format, ...)
21 {
22 va_list arg;
23 va_start(arg, format);
24 if (margin_global_logging)
25 vprintf(format, arg);
26 va_end(arg);
27 }
28
29 void
margin_log_bdfs(struct pci_dev * down,struct pci_dev * up)30 margin_log_bdfs(struct pci_dev *down, struct pci_dev *up)
31 {
32 if (margin_print_domain)
33 margin_log("%x:%x:%x.%x -> %x:%x:%x.%x", down->domain, down->bus, down->dev, down->func,
34 up->domain, up->bus, up->dev, up->func);
35 else
36 margin_log("%x:%x.%x -> %x:%x.%x", down->bus, down->dev, down->func, up->bus, up->dev,
37 up->func);
38 }
39
40 void
margin_gen_bdfs(struct pci_dev * down,struct pci_dev * up,char * dest,size_t maxlen)41 margin_gen_bdfs(struct pci_dev *down, struct pci_dev *up, char *dest, size_t maxlen)
42 {
43 if (margin_print_domain)
44 snprintf(dest, maxlen, "%x:%x:%x.%x -> %x:%x:%x.%x", down->domain, down->bus, down->dev,
45 down->func, up->domain, up->bus, up->dev, up->func);
46 else
47 snprintf(dest, maxlen, "%x:%x.%x -> %x:%x.%x", down->bus, down->dev, down->func, up->bus,
48 up->dev, up->func);
49 }
50
51 void
margin_log_link(struct margin_link * link)52 margin_log_link(struct margin_link *link)
53 {
54 margin_log("Link ");
55 margin_log_bdfs(link->down_port.dev, link->up_port.dev);
56 margin_log("\nNegotiated Link Width: %d\n", link->down_port.neg_width);
57 margin_log("Link Speed: %d.0 GT/s = Gen %d\n", (link->down_port.link_speed - 3) * 16,
58 link->down_port.link_speed);
59 margin_log("Available receivers: ");
60 int receivers_n = 2 + 2 * link->down_port.retimers_n;
61 for (int i = 1; i < receivers_n; i++)
62 margin_log("Rx(%X) - %d, ", 10 + i - 1, i);
63 margin_log("Rx(F) - 6\n");
64 }
65
66 void
margin_log_params(struct margin_params * params)67 margin_log_params(struct margin_params *params)
68 {
69 margin_log("Independent Error Sampler: %d\n", params->ind_error_sampler);
70 margin_log("Sample Reporting Method: %d\n", params->sample_report_method);
71 margin_log("Independent Left and Right Timing Margining: %d\n", params->ind_left_right_tim);
72 margin_log("Voltage Margining Supported: %d\n", params->volt_support);
73 margin_log("Independent Up and Down Voltage Margining: %d\n", params->ind_up_down_volt);
74 margin_log("Number of Timing Steps: %d\n", params->timing_steps);
75 margin_log("Number of Voltage Steps: %d\n", params->volt_steps);
76 margin_log("Max Timing Offset: %d\n", params->timing_offset);
77 margin_log("Max Voltage Offset: %d\n", params->volt_offset);
78 margin_log("Max Lanes: %d\n", params->max_lanes);
79 }
80
81 void
margin_log_recvn(struct margin_recv * recv)82 margin_log_recvn(struct margin_recv *recv)
83 {
84 margin_log("\nReceiver = Rx(%X)\n", 10 + recv->recvn - 1);
85 }
86
87 void
margin_log_receiver(struct margin_recv * recv)88 margin_log_receiver(struct margin_recv *recv)
89 {
90 margin_log("\nError Count Limit = %d\n", recv->error_limit);
91 margin_log("Parallel Lanes: %d\n", recv->parallel_lanes);
92 margin_log("Margining dwell time: %d s\n\n", recv->dwell_time);
93
94 margin_log_params(recv->params);
95
96 if (recv->lane_reversal)
97 {
98 margin_log("\nWarning: device uses Lane Reversal.\n");
99 margin_log("However, utility uses logical lane numbers in arguments and for logging.\n");
100 }
101
102 if (recv->params->timing_offset == 0)
103 margin_log("\nWarning: Vendor chose not to report the Max Timing Offset.\n"
104 "Utility will use its max possible value - 50 (50%% UI).\n");
105 if (recv->params->volt_support && recv->params->volt_offset == 0)
106 margin_log("\nWarning: Vendor chose not to report the Max Voltage Offset.\n"
107 "Utility will use its max possible value - 50 (500 mV).\n");
108 }
109
110 void
margin_log_margining(struct margin_lanes_data arg)111 margin_log_margining(struct margin_lanes_data arg)
112 {
113 char *ind_dirs[] = { "Up", "Down", "Left", "Right" };
114 char *non_ind_dirs[] = { "Voltage", "", "Timing" };
115
116 if (arg.verbosity > 0)
117 {
118 margin_log("\033[2K\rMargining - ");
119 if (arg.ind)
120 margin_log("%s", ind_dirs[arg.dir]);
121 else
122 margin_log("%s", non_ind_dirs[arg.dir]);
123
124 u8 lanes_counter = 0;
125 margin_log(" - Lanes ");
126 margin_log("[%d", arg.lanes_numbers[0]);
127 for (int i = 1; i < arg.lanes_n; i++)
128 {
129 if (arg.lanes_numbers[i] - 1 == arg.lanes_numbers[i - 1])
130 {
131 lanes_counter++;
132 if (lanes_counter == 1)
133 margin_log("-");
134 if (i + 1 == arg.lanes_n)
135 margin_log("%d", arg.lanes_numbers[i]);
136 }
137 else
138 {
139 if (lanes_counter > 0)
140 margin_log("%d", arg.lanes_numbers[i - 1]);
141 margin_log(",%d", arg.lanes_numbers[i]);
142 lanes_counter = 0;
143 }
144 }
145 margin_log("]");
146
147 u64 lane_eta_s = (arg.steps_lane_total - arg.steps_lane_done) * arg.recv->dwell_time;
148 u64 total_eta_s = *arg.steps_utility * arg.recv->dwell_time + lane_eta_s;
149 margin_log(" - ETA: %3ds Steps: %3d Total ETA: %3dm %2ds", lane_eta_s, arg.steps_lane_done,
150 total_eta_s / 60, total_eta_s % 60);
151
152 fflush(stdout);
153 }
154 }
155
156 void
margin_log_hw_quirks(struct margin_recv * recv)157 margin_log_hw_quirks(struct margin_recv *recv)
158 {
159 switch (recv->dev->hw)
160 {
161 case MARGIN_ICE_LAKE_RC:
162 if (recv->recvn == 1)
163 margin_log("\nRx(A) is Intel Ice Lake RC port.\n"
164 "Applying next quirks for margining process:\n"
165 " - Set MaxVoltageOffset to 12 (120 mV);\n"
166 " - Force the use of 'one side is the whole' grading mode.\n");
167 break;
168 default:
169 break;
170 }
171 }
172