]> mj.ucw.cz Git - pciutils.git/blob - lmr/margin_results.c
lspci: add VirtIO SharedMemory capability support
[pciutils.git] / lmr / margin_results.c
1 /*
2  *      The PCI Utilities -- Display/save margining results
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 <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <time.h>
15
16 #include "lmr.h"
17
18 enum lane_rating {
19   FAIL = 0,
20   PASS,
21   PERFECT,
22   INIT,
23 };
24
25 static char *const grades[] = { "Fail", "Pass", "Perfect" };
26 static char *const sts_strings[] = { "NAK", "LIM", "THR" };
27
28 static enum lane_rating
29 rate_lane(double value, double min, double recommended, enum lane_rating cur_rate)
30 {
31   enum lane_rating res = PERFECT;
32   if (value < recommended)
33     res = PASS;
34   if (value < min)
35     res = FAIL;
36   if (cur_rate == INIT)
37     return res;
38   if (res < cur_rate)
39     return res;
40   else
41     return cur_rate;
42 }
43
44 void
45 margin_results_print_brief(struct margin_results *results, u8 recvs_n,
46                            struct margin_link_args *args)
47 {
48   struct margin_res_lane *lane;
49   struct margin_results *res;
50   struct margin_params params;
51
52   enum lane_rating lane_rating;
53
54   u8 link_speed;
55
56   struct margin_recv_args grade_args;
57   bool spec_ref_only;
58
59   double ew_min;
60   double ew_rec;
61   double eh_min;
62   double eh_rec;
63
64   char *no_test_msgs[] = { "",
65                            "Margining Ready bit is Clear",
66                            "Error during caps reading",
67                            "Margining prerequisites are not satisfied (16/32 GT/s, D0)",
68                            "Invalid lanes specified with arguments",
69                            "Invalid receivers specified with arguments",
70                            "Couldn't disable ASPM" };
71
72   for (int i = 0; i < recvs_n; i++)
73     {
74       res = &(results[i]);
75       params = res->params;
76       link_speed = res->link_speed - 4;
77
78       if (res->test_status != MARGIN_TEST_OK)
79         {
80           if (res->test_status < MARGIN_TEST_PREREQS)
81             printf("Rx(%X) -", 10 + res->recvn - 1);
82           printf(" Couldn't run test (%s)\n\n", no_test_msgs[res->test_status]);
83           continue;
84         }
85
86       spec_ref_only = true;
87       grade_args = args->recv_args[res->recvn - 1];
88       if (grade_args.t.criteria != 0)
89         {
90           spec_ref_only = false;
91           ew_min = grade_args.t.criteria;
92           ew_rec = grade_args.t.criteria;
93         }
94       else
95         {
96           ew_min = margin_ew_min[link_speed];
97           ew_rec = margin_ew_rec[link_speed];
98         }
99
100       if (grade_args.v.criteria != 0)
101         {
102           spec_ref_only = false;
103           eh_min = grade_args.v.criteria;
104           eh_rec = grade_args.v.criteria;
105         }
106       else
107         {
108           eh_min = margin_eh_min[link_speed];
109           eh_rec = margin_eh_rec[link_speed];
110         }
111
112       printf("Rx(%X) - Grading criteria:\n", 10 + res->recvn - 1);
113       if (spec_ref_only)
114         {
115           printf("\tUsing spec only:\n");
116           printf("\tEW: minimum - %.2f ps; recommended - %.2f ps\n", ew_min, ew_rec);
117           printf("\tEH: minimum - %.2f mV; recommended - %.2f mV\n\n", eh_min, eh_rec);
118         }
119       else
120         {
121           printf("\tEW: pass - %.2f ps\n", ew_min);
122           printf("\tEH: pass - %.2f mV\n\n", eh_min);
123         }
124
125       if (!params.ind_left_right_tim)
126         {
127           printf("Rx(%X) - EW: independent left/right timing margin is not supported:\n",
128                  10 + res->recvn - 1);
129           if (grade_args.t.one_side_is_whole)
130             printf("\tmanual setting - the entire margin across the eye "
131                    "is what is reported by one side margining\n\n");
132           else
133             printf("\tdefault - calculating EW as double one side result\n\n");
134         }
135
136       if (params.volt_support && !params.ind_up_down_volt)
137         {
138           printf("Rx(%X) - EH: independent up and down voltage margining is not supported:\n",
139                  10 + res->recvn - 1);
140           if (grade_args.v.one_side_is_whole)
141             printf("\tmanual setting - the entire margin across the eye "
142                    "is what is reported by one side margining\n\n");
143           else
144             printf("\tdefault - calculating EH as double one side result\n\n");
145         }
146
147       if (res->lane_reversal)
148         printf("Rx(%X) - Lane Reversal\n", 10 + res->recvn - 1);
149
150       if (!res->tim_off_reported)
151         printf("Rx(%X) - Attention: Vendor chose not to report the Max Timing Offset.\n"
152                "Utility used its max possible value (50%% UI) for calculations of %% UI and ps.\n"
153                "Keep in mind that for timing results of this receiver only steps values are "
154                "reliable.\n\n",
155                10 + res->recvn - 1);
156       if (params.volt_support && !res->volt_off_reported)
157         printf("Rx(%X) - Attention: Vendor chose not to report the Max Voltage Offset.\n"
158                "Utility used its max possible value (500 mV) for calculations of mV.\n"
159                "Keep in mind that for voltage results of this receiver only steps values are "
160                "reliable.\n\n",
161                10 + res->recvn - 1);
162
163       for (int j = 0; j < res->lanes_n; j++)
164         {
165           if (spec_ref_only)
166             lane_rating = INIT;
167           else
168             lane_rating = PASS;
169
170           lane = &(res->lanes[j]);
171           double left_ps = lane->steps[TIM_LEFT] * res->tim_coef / 100.0 * margin_ui[link_speed];
172           double right_ps = lane->steps[TIM_RIGHT] * res->tim_coef / 100.0 * margin_ui[link_speed];
173           double up_volt = lane->steps[VOLT_UP] * res->volt_coef;
174           double down_volt = lane->steps[VOLT_DOWN] * res->volt_coef;
175
176           double ew = left_ps;
177           if (params.ind_left_right_tim)
178             ew += right_ps;
179           else if (!grade_args.t.one_side_is_whole)
180             ew *= 2.0;
181
182           double eh = 0.0;
183           if (params.volt_support)
184             {
185               eh += up_volt;
186               if (params.ind_up_down_volt)
187                 eh += down_volt;
188               else if (!grade_args.v.one_side_is_whole)
189                 eh *= 2.0;
190             }
191
192           lane_rating = rate_lane(ew, ew_min, ew_rec, lane_rating);
193           if (params.volt_support)
194             lane_rating = rate_lane(eh, eh_min, eh_rec, lane_rating);
195
196           printf("Rx(%X) Lane %2d: %s\t (W %4.1f%% UI - %5.2fps", 10 + res->recvn - 1, lane->lane,
197                  grades[lane_rating], ew / margin_ui[link_speed] * 100.0, ew);
198           if (params.volt_support)
199             printf(", H %5.1f mV", eh);
200           if (params.ind_left_right_tim)
201             printf(")  (L %4.1f%% UI - %5.2fps - %2dst %s)  (R %4.1f%% UI - %5.2fps - %2dst %s)",
202                    left_ps / margin_ui[link_speed] * 100.0, left_ps, lane->steps[TIM_LEFT],
203                    sts_strings[lane->statuses[TIM_LEFT]], right_ps / margin_ui[link_speed] * 100.0,
204                    right_ps, lane->steps[TIM_RIGHT], sts_strings[lane->statuses[TIM_RIGHT]]);
205           else
206             printf(")  (T %4.1f%% UI - %5.2fps - %2dst %s)",
207                    left_ps / margin_ui[link_speed] * 100.0, left_ps, lane->steps[TIM_LEFT],
208                    sts_strings[lane->statuses[TIM_LEFT]]);
209           if (params.volt_support)
210             {
211               if (params.ind_up_down_volt)
212                 printf("  (U %5.1f mV - %3dst %s)  (D %5.1f mV - %3dst %s)", up_volt,
213                        lane->steps[VOLT_UP], sts_strings[lane->statuses[VOLT_UP]], down_volt,
214                        lane->steps[VOLT_DOWN], sts_strings[lane->statuses[VOLT_DOWN]]);
215               else
216                 printf("  (V %5.1f mV - %3dst %s)", up_volt, lane->steps[VOLT_UP],
217                        sts_strings[lane->statuses[VOLT_UP]]);
218             }
219           printf("\n");
220         }
221       printf("\n");
222     }
223 }
224
225 void
226 margin_results_save_csv(struct margin_results *results, u8 recvs_n, struct margin_link *link)
227 {
228   char timestamp[64];
229   time_t tim = time(NULL);
230   strftime(timestamp, sizeof(timestamp), "%Y-%m-%dT%H:%M:%S", gmtime(&tim));
231
232   char *dir = link->args.common->dir_for_csv;
233   size_t pathlen = strlen(dir) + 128;
234   char *path = xmalloc(pathlen);
235   FILE *csv;
236
237   struct margin_res_lane *lane;
238   struct margin_results *res;
239   struct margin_params params;
240
241   enum lane_rating lane_rating;
242   u8 link_speed;
243
244   struct margin_recv_args grade_args;
245   bool spec_ref_only;
246
247   double ew_min;
248   double ew_rec;
249   double eh_min;
250   double eh_rec;
251
252   struct pci_dev *port;
253
254   for (int i = 0; i < recvs_n; i++)
255     {
256       res = &(results[i]);
257       params = res->params;
258       link_speed = res->link_speed - 4;
259
260       if (res->test_status != MARGIN_TEST_OK)
261         continue;
262
263       port = res->recvn == 6 ? link->up_port.dev : link->down_port.dev;
264       snprintf(path, pathlen, "%s/lmr_%0*x.%02x.%02x.%x_Rx%X_%s.csv", dir,
265                port->domain_16 == 0xffff ? 8 : 4, port->domain, port->bus, port->dev, port->func,
266                10 + res->recvn - 1, timestamp);
267       csv = fopen(path, "w");
268       if (!csv)
269         die("Error while saving %s\n", path);
270
271       fprintf(csv, "Lane,EW Min,EW Rec,EW,EH Min,EH Rec,EH,Lane Status,Left %% UI,Left "
272                    "ps,Left Steps,Left Status,Right %% UI,Right ps,Right Steps,Right Status,Up "
273                    "mV,Up Steps,Up Status,Down mV,Down Steps,Down Status\n");
274
275       spec_ref_only = true;
276       grade_args = link->args.recv_args[res->recvn - 1];
277       if (grade_args.t.criteria != 0)
278         {
279           spec_ref_only = false;
280           ew_min = grade_args.t.criteria;
281           ew_rec = grade_args.t.criteria;
282         }
283       else
284         {
285           ew_min = margin_ew_min[link_speed];
286           ew_rec = margin_ew_rec[link_speed];
287         }
288       if (grade_args.v.criteria != 0)
289         {
290           spec_ref_only = false;
291           eh_min = grade_args.v.criteria;
292           eh_rec = grade_args.v.criteria;
293         }
294       else
295         {
296           eh_min = margin_eh_min[link_speed];
297           eh_rec = margin_eh_rec[link_speed];
298         }
299
300       for (int j = 0; j < res->lanes_n; j++)
301         {
302           if (spec_ref_only)
303             lane_rating = INIT;
304           else
305             lane_rating = PASS;
306
307           lane = &(res->lanes[j]);
308           double left_ps = lane->steps[TIM_LEFT] * res->tim_coef / 100.0 * margin_ui[link_speed];
309           double right_ps = lane->steps[TIM_RIGHT] * res->tim_coef / 100.0 * margin_ui[link_speed];
310           double up_volt = lane->steps[VOLT_UP] * res->volt_coef;
311           double down_volt = lane->steps[VOLT_DOWN] * res->volt_coef;
312
313           double ew = left_ps;
314           if (params.ind_left_right_tim)
315             ew += right_ps;
316           else if (!grade_args.t.one_side_is_whole)
317             ew *= 2.0;
318
319           double eh = 0.0;
320           if (params.volt_support)
321             {
322               eh += up_volt;
323               if (params.ind_up_down_volt)
324                 eh += down_volt;
325               else if (!grade_args.v.one_side_is_whole)
326                 eh *= 2.0;
327             }
328
329           lane_rating = rate_lane(ew, ew_min, ew_rec, lane_rating);
330           if (params.volt_support)
331             lane_rating = rate_lane(eh, eh_min, eh_rec, lane_rating);
332
333           fprintf(csv, "%d,%f,", lane->lane, ew_min);
334           if (spec_ref_only)
335             fprintf(csv, "%f,", ew_rec);
336           else
337             fprintf(csv, "NA,");
338           fprintf(csv, "%f,", ew);
339           if (params.volt_support)
340             {
341               fprintf(csv, "%f,", eh_min);
342               if (spec_ref_only)
343                 fprintf(csv, "%f,", eh_rec);
344               else
345                 fprintf(csv, "NA,");
346               fprintf(csv, "%f,", eh);
347             }
348           else
349             fprintf(csv, "NA,NA,NA,");
350           fprintf(csv, "%s,", grades[lane_rating]);
351
352           fprintf(csv, "%f,%f,%d,%s,", left_ps * 100.0 / margin_ui[link_speed], left_ps,
353                   lane->steps[TIM_LEFT], sts_strings[lane->statuses[TIM_LEFT]]);
354
355           if (params.ind_left_right_tim)
356             fprintf(csv, "%f,%f,%d,%s,", right_ps * 100.0 / margin_ui[link_speed], right_ps,
357                     lane->steps[TIM_RIGHT], sts_strings[lane->statuses[TIM_RIGHT]]);
358           else
359             {
360               for (int k = 0; k < 4; k++)
361                 fprintf(csv, "NA,");
362             }
363           if (params.volt_support)
364             {
365               fprintf(csv, "%f,%d,%s,", up_volt, lane->steps[VOLT_UP],
366                       sts_strings[lane->statuses[VOLT_UP]]);
367               if (params.ind_up_down_volt)
368                 fprintf(csv, "%f,%d,%s\n", down_volt, lane->steps[VOLT_DOWN],
369                         sts_strings[lane->statuses[VOLT_DOWN]]);
370               else
371                 fprintf(csv, "NA,NA,NA\n");
372             }
373           else
374             {
375               for (int k = 0; k < 5; k++)
376                 fprintf(csv, "NA,");
377               fprintf(csv, "NA\n");
378             }
379         }
380       fclose(csv);
381     }
382   free(path);
383 }