2 * The PCI Utilities -- Margining utility main function
4 * Copyright (c) 2023 KNS Group LLC (YADRO)
6 * Can be freely distributed and used under the terms of the GNU GPL v2+.
8 * SPDX-License-Identifier: GPL-2.0-or-later
18 const char program_name[] = "pcilmr";
20 enum mode { MARGIN, FULL };
22 static const char usage_msg[]
24 "pcilmr [--margin] [<margining options>] <downstream component> ...\n"
25 "pcilmr --full [<margining options>]\n"
27 "<device/component>:\t[<domain>:]<bus>:<dev>.<func>\n\n"
29 "--margin\t\tMargin selected Links\n"
30 "--full\t\t\tMargin all ready for testing Links in the system (one by one)\n"
31 "Margining options:\n\n"
32 "Margining Test settings:\n"
33 "-c\t\t\tPrint Device Lane Margining Capabilities only. Do not run margining.\n"
34 "-l <lane>[,<lane>...]\tSpecify lanes for margining. Default: all link lanes.\n"
35 "\t\t\tRemember that Device may use Lane Reversal for Lane numbering.\n"
36 "\t\t\tHowever, utility uses logical lane numbers in arguments and for logging.\n"
37 "\t\t\tUtility will automatically determine Lane Reversal and tune its calls.\n"
38 "-e <errors>\t\tSpecify Error Count Limit for margining. Default: 4.\n"
39 "-r <recvn>[,<recvn>...]\tSpecify Receivers to select margining targets.\n"
40 "\t\t\tDefault: all available Receivers (including Retimers).\n"
41 "-p <parallel_lanes>\tSpecify number of lanes to margin simultaneously.\n"
43 "\t\t\tAccording to spec it's possible for Receiver to margin up\n"
44 "\t\t\tto MaxLanes + 1 lanes simultaneously, but usually this works\n"
45 "\t\t\tbad, so this option is for experiments mostly.\n"
46 "-T\t\t\tTime Margining will continue until the Error Count is no more\n"
47 "\t\t\tthan an Error Count Limit. Use this option to find Link limit.\n"
48 "-V\t\t\tSame as -T option, but for Voltage.\n"
49 "-t <steps>\t\tSpecify maximum number of steps for Time Margining.\n"
50 "-v <steps>\t\tSpecify maximum number of steps for Voltage Margining.\n"
51 "Use only one of -T/-t options at the same time (same for -V/-v).\n"
52 "Without these options utility will use MaxSteps from Device\n"
53 "capabilities as test limit.\n\n";
55 static struct pci_dev *
56 dev_for_filter(struct pci_access *pacc, char *filter)
58 struct pci_filter pci_filter;
60 strncpy(dev, filter, sizeof(dev) - 1);
61 pci_filter_init(pacc, &pci_filter);
62 if (pci_filter_parse_slot(&pci_filter, dev))
63 die("Invalid device ID: %s\n", filter);
65 if (pci_filter.bus == -1 || pci_filter.slot == -1 || pci_filter.func == -1)
66 die("Invalid device ID: %s\n", filter);
68 if (pci_filter.domain == -1)
69 pci_filter.domain = 0;
71 for (struct pci_dev *p = pacc->devices; p; p = p->next)
73 if (pci_filter_match(&pci_filter, p))
77 die("No such PCI device: %s or you don't have enough privileges.\n", filter);
80 static struct pci_dev *
81 find_down_port_for_up(struct pci_access *pacc, struct pci_dev *up)
83 struct pci_dev *down = NULL;
84 for (struct pci_dev *p = pacc->devices; p; p = p->next)
86 if (pci_read_byte(p, PCI_SECONDARY_BUS) == up->bus && up->domain == p->domain)
96 parse_csv_arg(char *arg, u8 *vals)
99 char *token = strtok(arg, ",");
102 vals[cnt] = atoi(token);
104 token = strtok(NULL, ",");
110 find_ready_links(struct pci_access *pacc, struct pci_dev **down_ports, struct pci_dev **up_ports,
114 for (struct pci_dev *up = pacc->devices; up; up = up->next)
116 if (pci_find_cap(up, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED))
118 struct pci_dev *down = find_down_port_for_up(pacc, up);
120 if (down && margin_verify_link(down, up)
121 && (margin_check_ready_bit(down) || margin_check_ready_bit(up)))
126 down_ports[cnt] = down;
136 main(int argc, char **argv)
138 struct pci_access *pacc;
140 struct pci_dev **up_ports;
141 struct pci_dev **down_ports;
144 struct margin_link *links;
145 bool *checks_status_ports;
150 /* each link has several receivers -> several results */
151 struct margin_results **results;
154 struct margin_args *args;
158 u8 parallel_lanes_arg = 1;
166 bool run_margin = true;
174 margin_print_domain = false;
175 for (struct pci_dev *dev = pacc->devices; dev; dev = dev->next)
177 if (dev->domain != 0)
179 margin_print_domain = true;
184 margin_global_logging = true;
186 struct option long_options[]
187 = { { .name = "margin", .has_arg = no_argument, .flag = NULL, .val = 0 },
188 { .name = "full", .has_arg = no_argument, .flag = NULL, .val = 1 },
192 c = getopt_long(argc, argv, ":", long_options, NULL);
196 case -1: /* no options (strings like component are possible) */
204 default: /* unknown option symbol */
210 while ((c = getopt(argc, argv, ":r:e:l:cp:t:v:VT")) != -1)
215 steps_t_arg = atoi(optarg);
221 steps_v_arg = atoi(optarg);
227 parallel_lanes_arg = atoi(optarg);
233 lanes_n = parse_csv_arg(optarg, lanes_arg);
236 error_limit = atoi(optarg);
239 recvs_n = parse_csv_arg(optarg, recvs_arg);
242 die("Invalid arguments\n\n%s", usage_msg);
246 if (mode == FULL && optind != argc)
248 if (mode == MARGIN && optind == argc)
250 if (!status && argc > 1)
251 die("Invalid arguments\n\n%s", usage_msg);
254 printf("%s", usage_msg);
260 ports_n = find_ready_links(pacc, NULL, NULL, true);
263 die("Links not found or you don't have enough privileges.\n");
267 up_ports = xmalloc(ports_n * sizeof(*up_ports));
268 down_ports = xmalloc(ports_n * sizeof(*down_ports));
269 find_ready_links(pacc, down_ports, up_ports, false);
272 else if (mode == MARGIN)
274 ports_n = argc - optind;
275 up_ports = xmalloc(ports_n * sizeof(*up_ports));
276 down_ports = xmalloc(ports_n * sizeof(*down_ports));
279 while (optind != argc)
281 up_ports[cnt] = dev_for_filter(pacc, argv[optind]);
282 down_ports[cnt] = find_down_port_for_up(pacc, up_ports[cnt]);
283 if (!down_ports[cnt])
284 die("Cannot find Upstream Component for the specified device: %s\n", argv[optind]);
290 die("Bug in the args parsing!\n");
292 if (!pci_find_cap(up_ports[0], PCI_CAP_ID_EXP, PCI_CAP_NORMAL))
293 die("Looks like you don't have enough privileges to access "
294 "Device Configuration Space.\nTry to run utility as root.\n");
296 results = xmalloc(ports_n * sizeof(*results));
297 results_n = xmalloc(ports_n * sizeof(*results_n));
298 links = xmalloc(ports_n * sizeof(*links));
299 checks_status_ports = xmalloc(ports_n * sizeof(*checks_status_ports));
300 args = xmalloc(ports_n * sizeof(*args));
302 for (int i = 0; i < ports_n; i++)
304 args[i].error_limit = error_limit;
305 args[i].parallel_lanes = parallel_lanes_arg;
306 args[i].run_margin = run_margin;
307 args[i].verbosity = 1;
308 args[i].steps_t = steps_t_arg;
309 args[i].steps_v = steps_v_arg;
310 for (int j = 0; j < recvs_n; j++)
311 args[i].recvs[j] = recvs_arg[j];
312 args[i].recvs_n = recvs_n;
313 for (int j = 0; j < lanes_n; j++)
314 args[i].lanes[j] = lanes_arg[j];
315 args[i].lanes_n = lanes_n;
316 args[i].steps_utility = &total_steps;
318 enum margin_test_status args_status;
320 if (!margin_fill_link(down_ports[i], up_ports[i], &links[i]))
322 checks_status_ports[i] = false;
323 results[i] = xmalloc(sizeof(*results[i]));
324 results[i]->test_status = MARGIN_TEST_PREREQS;
328 if ((args_status = margin_process_args(&links[i].down_port, &args[i])) != MARGIN_TEST_OK)
330 checks_status_ports[i] = false;
331 results[i] = xmalloc(sizeof(*results[i]));
332 results[i]->test_status = args_status;
336 checks_status_ports[i] = true;
337 struct margin_params params;
339 for (int j = 0; j < args[i].recvs_n; j++)
341 if (margin_read_params(pacc, args[i].recvs[j] == 6 ? up_ports[i] : down_ports[i],
342 args[i].recvs[j], ¶ms))
344 u8 steps_t = steps_t_arg ? steps_t_arg : params.timing_steps;
345 u8 steps_v = steps_v_arg ? steps_v_arg : params.volt_steps;
346 u8 parallel_recv = parallel_lanes_arg > params.max_lanes + 1 ? params.max_lanes + 1 :
350 = args[i].lanes_n / parallel_recv + ((args[i].lanes_n % parallel_recv) > 0);
352 total_steps += steps_t * step_multiplier;
353 if (params.ind_left_right_tim)
354 total_steps += steps_t * step_multiplier;
355 if (params.volt_support)
357 total_steps += steps_v * step_multiplier;
358 if (params.ind_up_down_volt)
359 total_steps += steps_v * step_multiplier;
365 for (int i = 0; i < ports_n; i++)
367 if (checks_status_ports[i])
368 results[i] = margin_test_link(&links[i], &args[i], &results_n[i]);
372 if (results[i]->test_status == MARGIN_TEST_PREREQS)
375 margin_log_bdfs(down_ports[i], up_ports[i]);
376 printf(" is not ready for margining.\n"
377 "Link data rate must be 16 GT/s or 32 GT/s.\n"
378 "Downstream Component must be at D0 PM state.\n");
380 else if (results[i]->test_status == MARGIN_TEST_ARGS_RECVS)
382 margin_log_link(&links[i]);
383 printf("\nInvalid RecNums specified.\n");
385 else if (results[i]->test_status == MARGIN_TEST_ARGS_LANES)
387 margin_log_link(&links[i]);
388 printf("\nInvalid lanes specified.\n");
391 printf("\n----\n\n");
396 printf("Results:\n");
397 printf("\nPass/fail criteria:\nTiming:\n");
398 printf("Minimum Offset (spec): %d %% UI\nRecommended Offset: %d %% UI\n", MARGIN_TIM_MIN,
399 MARGIN_TIM_RECOMMEND);
400 printf("\nVoltage:\nMinimum Offset (spec): %d mV\n\n", MARGIN_VOLT_MIN);
402 "Margining statuses:\nLIM -\tErrorCount exceeded Error Count Limit (found device limit)\n");
403 printf("NAK -\tDevice didn't execute last command, \n\tso result may be less reliable\n");
404 printf("THR -\tThe set (using the utility options) \n\tstep threshold has been reached\n\n");
405 printf("Notations:\nst - steps\n\n");
407 for (int i = 0; i < ports_n; i++)
410 margin_log_bdfs(down_ports[i], up_ports[i]);
412 margin_results_print_brief(results[i], results_n[i]);
417 for (int i = 0; i < ports_n; i++)
418 margin_free_results(results[i], results_n[i]);
424 free(checks_status_ports);