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