]> mj.ucw.cz Git - pciutils.git/blob - pcilmr.c
pcilmr: Add support for unique hardware quirks
[pciutils.git] / pcilmr.c
1 /*
2  *      The PCI Utilities -- Margining utility main function
3  *
4  *      Copyright (c) 2023 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 <getopt.h>
12 #include <memory.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15
16 #include "lmr/lmr.h"
17
18 const char program_name[] = "pcilmr";
19
20 static const char usage_msg[]
21   = "Usage:\n"
22     "pcilmr [<margining options>] <downstream component>\n\n"
23     "Device Specifier:\n"
24     "<device/component>:\t[<domain>:]<bus>:<dev>.<func>\n\n"
25     "Margining options:\n\n"
26     "Margining Test settings:\n"
27     "-c\t\t\tPrint Device Lane Margining Capabilities only. Do not run margining.\n"
28     "-l <lane>[,<lane>...]\tSpecify lanes for margining. Default: all link lanes.\n"
29     "\t\t\tRemember that Device may use Lane Reversal for Lane numbering.\n"
30     "\t\t\tHowever, utility uses logical lane numbers in arguments and for logging.\n"
31     "\t\t\tUtility will automatically determine Lane Reversal and tune its calls.\n"
32     "-e <errors>\t\tSpecify Error Count Limit for margining. Default: 4.\n"
33     "-r <recvn>[,<recvn>...]\tSpecify Receivers to select margining targets.\n"
34     "\t\t\tDefault: all available Receivers (including Retimers).\n"
35     "-p <parallel_lanes>\tSpecify number of lanes to margin simultaneously.\n"
36     "\t\t\tDefault: 1.\n"
37     "\t\t\tAccording to spec it's possible for Receiver to margin up\n"
38     "\t\t\tto MaxLanes + 1 lanes simultaneously, but usually this works\n"
39     "\t\t\tbad, so this option is for experiments mostly.\n"
40     "-T\t\t\tTime Margining will continue until the Error Count is no more\n"
41     "\t\t\tthan an Error Count Limit. Use this option to find Link limit.\n"
42     "-V\t\t\tSame as -T option, but for Voltage.\n"
43     "-t <steps>\t\tSpecify maximum number of steps for Time Margining.\n"
44     "-v <steps>\t\tSpecify maximum number of steps for Voltage Margining.\n"
45     "Use only one of -T/-t options at the same time (same for -V/-v).\n"
46     "Without these options utility will use MaxSteps from Device\n"
47     "capabilities as test limit.\n\n";
48
49 static struct pci_dev *
50 dev_for_filter(struct pci_access *pacc, char *filter)
51 {
52   struct pci_filter pci_filter;
53   char dev[17] = { 0 };
54   strncpy(dev, filter, sizeof(dev) - 1);
55   pci_filter_init(pacc, &pci_filter);
56   if (pci_filter_parse_slot(&pci_filter, dev))
57     die("Invalid device ID: %s\n", filter);
58
59   if (pci_filter.bus == -1 || pci_filter.slot == -1 || pci_filter.func == -1)
60     die("Invalid device ID: %s\n", filter);
61
62   if (pci_filter.domain == -1)
63     pci_filter.domain = 0;
64
65   for (struct pci_dev *p = pacc->devices; p; p = p->next)
66     {
67       if (pci_filter_match(&pci_filter, p))
68         return p;
69     }
70
71   die("No such PCI device: %s or you don't have enough privileges.\n", filter);
72 }
73
74 static struct pci_dev *
75 find_down_port_for_up(struct pci_access *pacc, struct pci_dev *up)
76 {
77   struct pci_dev *down = NULL;
78   for (struct pci_dev *p = pacc->devices; p; p = p->next)
79     {
80       if (pci_read_byte(p, PCI_SECONDARY_BUS) == up->bus && up->domain == p->domain)
81         {
82           down = p;
83           break;
84         }
85     }
86   return down;
87 }
88
89 static u8
90 parse_csv_arg(char *arg, u8 *vals)
91 {
92   u8 cnt = 0;
93   char *token = strtok(arg, ",");
94   while (token)
95     {
96       vals[cnt] = atoi(token);
97       cnt++;
98       token = strtok(NULL, ",");
99     }
100   return cnt;
101 }
102
103 int
104 main(int argc, char **argv)
105 {
106   struct pci_access *pacc;
107
108   struct pci_dev *up_port;
109   struct pci_dev *down_port;
110
111   struct margin_link link;
112
113   bool status = true;
114
115   struct margin_results *results;
116   u8 results_n;
117
118   struct margin_args args;
119
120   u8 steps_t_arg = 0;
121   u8 steps_v_arg = 0;
122   u8 parallel_lanes_arg = 1;
123   u8 error_limit = 4;
124
125   u8 lanes_n = 0;
126   u8 recvs_n = 0;
127
128   bool run_margin = true;
129
130   u64 total_steps = 0;
131
132   pacc = pci_alloc();
133   pci_init(pacc);
134   pci_scan_bus(pacc);
135
136   margin_print_domain = false;
137   for (struct pci_dev *dev = pacc->devices; dev; dev = dev->next)
138     {
139       if (dev->domain != 0)
140         {
141           margin_print_domain = true;
142           break;
143         }
144     }
145
146   margin_global_logging = true;
147
148   int c;
149
150   while ((c = getopt(argc, argv, ":r:e:l:cp:t:v:VT")) != -1)
151     {
152       switch (c)
153         {
154           case 't':
155             steps_t_arg = atoi(optarg);
156             break;
157           case 'T':
158             steps_t_arg = 63;
159             break;
160           case 'v':
161             steps_v_arg = atoi(optarg);
162             break;
163           case 'V':
164             steps_v_arg = 127;
165             break;
166           case 'p':
167             parallel_lanes_arg = atoi(optarg);
168             break;
169           case 'c':
170             run_margin = false;
171             break;
172           case 'l':
173             lanes_n = parse_csv_arg(optarg, args.lanes);
174             break;
175           case 'e':
176             error_limit = atoi(optarg);
177             break;
178           case 'r':
179             recvs_n = parse_csv_arg(optarg, args.recvs);
180             break;
181           default:
182             die("Invalid arguments\n\n%s", usage_msg);
183         }
184     }
185
186   if (optind != argc - 1)
187     status = false;
188   if (!status && argc > 1)
189     die("Invalid arguments\n\n%s", usage_msg);
190   if (!status)
191     {
192       printf("%s", usage_msg);
193       exit(0);
194     }
195
196   up_port = dev_for_filter(pacc, argv[argc - 1]);
197
198   down_port = find_down_port_for_up(pacc, up_port);
199   if (!down_port)
200     die("Cannot find Upstream Component for the specified device: %s\n", argv[argc - 1]);
201
202   if (!pci_find_cap(up_port, PCI_CAP_ID_EXP, PCI_CAP_NORMAL))
203     die("Looks like you don't have enough privileges to access "
204         "Device Configuration Space.\nTry to run utility as root.\n");
205
206   if (!margin_fill_link(down_port, up_port, &link))
207     {
208       printf("Link ");
209       margin_log_bdfs(down_port, up_port);
210       printf(" is not ready for margining.\n"
211              "Link data rate must be 16 GT/s or 32 GT/s.\n"
212              "Downstream Component must be at D0 PM state.\n");
213       status = false;
214     }
215
216   if (status)
217     {
218       args.error_limit = error_limit;
219       args.lanes_n = lanes_n;
220       args.recvs_n = recvs_n;
221       args.steps_t = steps_t_arg;
222       args.steps_v = steps_v_arg;
223       args.parallel_lanes = parallel_lanes_arg;
224       args.run_margin = run_margin;
225       args.verbosity = 1;
226       args.steps_utility = &total_steps;
227
228       enum margin_test_status args_status;
229
230       if ((args_status = margin_process_args(&link.down_port, &args)) != MARGIN_TEST_OK)
231         {
232           status = false;
233           margin_log_link(&link);
234           if (args_status == MARGIN_TEST_ARGS_RECVS)
235             margin_log("\nInvalid RecNums specified.\n");
236           else if (args_status == MARGIN_TEST_ARGS_LANES)
237             margin_log("\nInvalid lanes specified.\n");
238         }
239     }
240
241   if (status)
242     {
243       struct margin_params params;
244
245       for (int i = 0; i < args.recvs_n; i++)
246         {
247           if (margin_read_params(pacc, args.recvs[i] == 6 ? up_port : down_port, args.recvs[i],
248                                  &params))
249             {
250               u8 steps_t = steps_t_arg ? steps_t_arg : params.timing_steps;
251               u8 steps_v = steps_v_arg ? steps_v_arg : params.volt_steps;
252               u8 parallel_recv = parallel_lanes_arg > params.max_lanes + 1 ? params.max_lanes + 1 :
253                                                                              parallel_lanes_arg;
254
255               u8 step_multiplier
256                 = args.lanes_n / parallel_recv + ((args.lanes_n % parallel_recv) > 0);
257
258               total_steps += steps_t * step_multiplier;
259               if (params.ind_left_right_tim)
260                 total_steps += steps_t * step_multiplier;
261               if (params.volt_support)
262                 {
263                   total_steps += steps_v * step_multiplier;
264                   if (params.ind_up_down_volt)
265                     total_steps += steps_v * step_multiplier;
266                 }
267             }
268         }
269
270       results = margin_test_link(&link, &args, &results_n);
271     }
272
273   if (status && run_margin)
274     {
275       printf("\nResults:\n");
276       printf("\nPass/fail criteria:\nTiming:\n");
277       printf("Minimum Offset (spec): %d %% UI\nRecommended Offset: %d %% UI\n", MARGIN_TIM_MIN,
278              MARGIN_TIM_RECOMMEND);
279       printf("\nVoltage:\nMinimum Offset (spec): %d mV\n\n", MARGIN_VOLT_MIN);
280       printf(
281         "Margining statuses:\nLIM -\tErrorCount exceeded Error Count Limit (found device limit)\n");
282       printf("NAK -\tDevice didn't execute last command, \n\tso result may be less reliable\n");
283       printf("THR -\tThe set (using the utility options) \n\tstep threshold has been reached\n\n");
284       printf("Notations:\nst - steps\n\n");
285
286       margin_results_print_brief(results, results_n);
287     }
288
289   if (status)
290     margin_free_results(results, results_n);
291
292   pci_cleanup(pacc);
293   return 0;
294 }