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, SCAN };
22 static const char usage_msg[]
24 "pcilmr [--margin] [<margining options>] <downstream component> ...\n"
25 "pcilmr --full [<margining options>]\n"
28 "<device/component>:\t[<domain>:]<bus>:<dev>.<func>\n\n"
30 "--margin\t\tMargin selected Links\n"
31 "--full\t\t\tMargin all ready for testing Links in the system (one by one)\n"
32 "--scan\t\t\tScan for Links available for margining\n\n"
33 "Margining options:\n\n"
34 "Margining Test settings:\n"
35 "-c\t\t\tPrint Device Lane Margining Capabilities only. Do not run margining.\n"
36 "-l <lane>[,<lane>...]\tSpecify lanes for margining. Default: all link lanes.\n"
37 "\t\t\tRemember that Device may use Lane Reversal for Lane numbering.\n"
38 "\t\t\tHowever, utility uses logical lane numbers in arguments and for logging.\n"
39 "\t\t\tUtility will automatically determine Lane Reversal and tune its calls.\n"
40 "-e <errors>\t\tSpecify Error Count Limit for margining. Default: 4.\n"
41 "-r <recvn>[,<recvn>...]\tSpecify Receivers to select margining targets.\n"
42 "\t\t\tDefault: all available Receivers (including Retimers).\n"
43 "-p <parallel_lanes>\tSpecify number of lanes to margin simultaneously.\n"
45 "\t\t\tAccording to spec it's possible for Receiver to margin up\n"
46 "\t\t\tto MaxLanes + 1 lanes simultaneously, but usually this works\n"
47 "\t\t\tbad, so this option is for experiments mostly.\n"
48 "-T\t\t\tTime Margining will continue until the Error Count is no more\n"
49 "\t\t\tthan an Error Count Limit. Use this option to find Link limit.\n"
50 "-V\t\t\tSame as -T option, but for Voltage.\n"
51 "-t <steps>\t\tSpecify maximum number of steps for Time Margining.\n"
52 "-v <steps>\t\tSpecify maximum number of steps for Voltage Margining.\n"
53 "Use only one of -T/-t options at the same time (same for -V/-v).\n"
54 "Without these options utility will use MaxSteps from Device\n"
55 "capabilities as test limit.\n\n";
57 static struct pci_dev *
58 dev_for_filter(struct pci_access *pacc, char *filter)
60 struct pci_filter pci_filter;
62 strncpy(dev, filter, sizeof(dev) - 1);
63 pci_filter_init(pacc, &pci_filter);
64 if (pci_filter_parse_slot(&pci_filter, dev))
65 die("Invalid device ID: %s\n", filter);
67 if (pci_filter.bus == -1 || pci_filter.slot == -1 || pci_filter.func == -1)
68 die("Invalid device ID: %s\n", filter);
70 if (pci_filter.domain == -1)
71 pci_filter.domain = 0;
73 for (struct pci_dev *p = pacc->devices; p; p = p->next)
75 if (pci_filter_match(&pci_filter, p))
79 die("No such PCI device: %s or you don't have enough privileges.\n", filter);
82 static struct pci_dev *
83 find_down_port_for_up(struct pci_access *pacc, struct pci_dev *up)
85 struct pci_dev *down = NULL;
86 for (struct pci_dev *p = pacc->devices; p; p = p->next)
88 if (pci_read_byte(p, PCI_SECONDARY_BUS) == up->bus && up->domain == p->domain)
98 parse_csv_arg(char *arg, u8 *vals)
101 char *token = strtok(arg, ",");
104 vals[cnt] = atoi(token);
106 token = strtok(NULL, ",");
112 scan_links(struct pci_access *pacc, bool only_ready)
115 printf("Links ready for margining:\n");
117 printf("Links with Lane Margining at the Receiver capabilities:\n");
119 for (struct pci_dev *up = pacc->devices; up; up = up->next)
121 if (pci_find_cap(up, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED))
123 struct pci_dev *down = find_down_port_for_up(pacc, up);
125 if (down && margin_verify_link(down, up))
127 margin_log_bdfs(down, up);
128 if (!only_ready && (margin_check_ready_bit(down) || margin_check_ready_bit(up)))
136 printf("Links not found or you don't have enough privileges.\n");
142 find_ready_links(struct pci_access *pacc, struct pci_dev **down_ports, struct pci_dev **up_ports,
146 for (struct pci_dev *up = pacc->devices; up; up = up->next)
148 if (pci_find_cap(up, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED))
150 struct pci_dev *down = find_down_port_for_up(pacc, up);
152 if (down && margin_verify_link(down, up)
153 && (margin_check_ready_bit(down) || margin_check_ready_bit(up)))
158 down_ports[cnt] = down;
168 main(int argc, char **argv)
170 struct pci_access *pacc;
172 struct pci_dev **up_ports;
173 struct pci_dev **down_ports;
176 struct margin_link *links;
177 bool *checks_status_ports;
182 /* each link has several receivers -> several results */
183 struct margin_results **results;
186 struct margin_args *args;
190 u8 parallel_lanes_arg = 1;
198 bool run_margin = true;
206 margin_print_domain = false;
207 for (struct pci_dev *dev = pacc->devices; dev; dev = dev->next)
209 if (dev->domain != 0)
211 margin_print_domain = true;
216 margin_global_logging = true;
218 struct option long_options[]
219 = { { .name = "margin", .has_arg = no_argument, .flag = NULL, .val = 0 },
220 { .name = "scan", .has_arg = no_argument, .flag = NULL, .val = 1 },
221 { .name = "full", .has_arg = no_argument, .flag = NULL, .val = 2 },
225 c = getopt_long(argc, argv, ":", long_options, NULL);
229 case -1: /* no options (strings like component are possible) */
237 scan_links(pacc, false);
243 default: /* unknown option symbol */
249 while ((c = getopt(argc, argv, ":r:e:l:cp:t:v:VT")) != -1)
254 steps_t_arg = atoi(optarg);
260 steps_v_arg = atoi(optarg);
266 parallel_lanes_arg = atoi(optarg);
272 lanes_n = parse_csv_arg(optarg, lanes_arg);
275 error_limit = atoi(optarg);
278 recvs_n = parse_csv_arg(optarg, recvs_arg);
281 die("Invalid arguments\n\n%s", usage_msg);
285 if (mode == FULL && optind != argc)
287 if (mode == MARGIN && optind == argc)
289 if (!status && argc > 1)
290 die("Invalid arguments\n\n%s", usage_msg);
293 printf("%s", usage_msg);
299 ports_n = find_ready_links(pacc, NULL, NULL, true);
302 die("Links not found or you don't have enough privileges.\n");
306 up_ports = xmalloc(ports_n * sizeof(*up_ports));
307 down_ports = xmalloc(ports_n * sizeof(*down_ports));
308 find_ready_links(pacc, down_ports, up_ports, false);
311 else if (mode == MARGIN)
313 ports_n = argc - optind;
314 up_ports = xmalloc(ports_n * sizeof(*up_ports));
315 down_ports = xmalloc(ports_n * sizeof(*down_ports));
318 while (optind != argc)
320 up_ports[cnt] = dev_for_filter(pacc, argv[optind]);
321 down_ports[cnt] = find_down_port_for_up(pacc, up_ports[cnt]);
322 if (!down_ports[cnt])
323 die("Cannot find Upstream Component for the specified device: %s\n", argv[optind]);
329 die("Bug in the args parsing!\n");
331 if (!pci_find_cap(up_ports[0], PCI_CAP_ID_EXP, PCI_CAP_NORMAL))
332 die("Looks like you don't have enough privileges to access "
333 "Device Configuration Space.\nTry to run utility as root.\n");
335 results = xmalloc(ports_n * sizeof(*results));
336 results_n = xmalloc(ports_n * sizeof(*results_n));
337 links = xmalloc(ports_n * sizeof(*links));
338 checks_status_ports = xmalloc(ports_n * sizeof(*checks_status_ports));
339 args = xmalloc(ports_n * sizeof(*args));
341 for (int i = 0; i < ports_n; i++)
343 args[i].error_limit = error_limit;
344 args[i].parallel_lanes = parallel_lanes_arg;
345 args[i].run_margin = run_margin;
346 args[i].verbosity = 1;
347 args[i].steps_t = steps_t_arg;
348 args[i].steps_v = steps_v_arg;
349 for (int j = 0; j < recvs_n; j++)
350 args[i].recvs[j] = recvs_arg[j];
351 args[i].recvs_n = recvs_n;
352 for (int j = 0; j < lanes_n; j++)
353 args[i].lanes[j] = lanes_arg[j];
354 args[i].lanes_n = lanes_n;
355 args[i].steps_utility = &total_steps;
357 enum margin_test_status args_status;
359 if (!margin_fill_link(down_ports[i], up_ports[i], &links[i]))
361 checks_status_ports[i] = false;
362 results[i] = xmalloc(sizeof(*results[i]));
363 results[i]->test_status = MARGIN_TEST_PREREQS;
367 if ((args_status = margin_process_args(&links[i].down_port, &args[i])) != MARGIN_TEST_OK)
369 checks_status_ports[i] = false;
370 results[i] = xmalloc(sizeof(*results[i]));
371 results[i]->test_status = args_status;
375 checks_status_ports[i] = true;
376 struct margin_params params;
378 for (int j = 0; j < args[i].recvs_n; j++)
380 if (margin_read_params(pacc, args[i].recvs[j] == 6 ? up_ports[i] : down_ports[i],
381 args[i].recvs[j], ¶ms))
383 u8 steps_t = steps_t_arg ? steps_t_arg : params.timing_steps;
384 u8 steps_v = steps_v_arg ? steps_v_arg : params.volt_steps;
385 u8 parallel_recv = parallel_lanes_arg > params.max_lanes + 1 ? params.max_lanes + 1 :
389 = args[i].lanes_n / parallel_recv + ((args[i].lanes_n % parallel_recv) > 0);
391 total_steps += steps_t * step_multiplier;
392 if (params.ind_left_right_tim)
393 total_steps += steps_t * step_multiplier;
394 if (params.volt_support)
396 total_steps += steps_v * step_multiplier;
397 if (params.ind_up_down_volt)
398 total_steps += steps_v * step_multiplier;
404 for (int i = 0; i < ports_n; i++)
406 if (checks_status_ports[i])
407 results[i] = margin_test_link(&links[i], &args[i], &results_n[i]);
411 if (results[i]->test_status == MARGIN_TEST_PREREQS)
414 margin_log_bdfs(down_ports[i], up_ports[i]);
415 printf(" is not ready for margining.\n"
416 "Link data rate must be 16 GT/s or 32 GT/s.\n"
417 "Downstream Component must be at D0 PM state.\n");
419 else if (results[i]->test_status == MARGIN_TEST_ARGS_RECVS)
421 margin_log_link(&links[i]);
422 printf("\nInvalid RecNums specified.\n");
424 else if (results[i]->test_status == MARGIN_TEST_ARGS_LANES)
426 margin_log_link(&links[i]);
427 printf("\nInvalid lanes specified.\n");
430 printf("\n----\n\n");
435 printf("Results:\n");
436 printf("\nPass/fail criteria:\nTiming:\n");
437 printf("Minimum Offset (spec): %d %% UI\nRecommended Offset: %d %% UI\n", MARGIN_TIM_MIN,
438 MARGIN_TIM_RECOMMEND);
439 printf("\nVoltage:\nMinimum Offset (spec): %d mV\n\n", MARGIN_VOLT_MIN);
441 "Margining statuses:\nLIM -\tErrorCount exceeded Error Count Limit (found device limit)\n");
442 printf("NAK -\tDevice didn't execute last command, \n\tso result may be less reliable\n");
443 printf("THR -\tThe set (using the utility options) \n\tstep threshold has been reached\n\n");
444 printf("Notations:\nst - steps\n\n");
446 for (int i = 0; i < ports_n; i++)
449 margin_log_bdfs(down_ports[i], up_ports[i]);
451 margin_results_print_brief(results[i], results_n[i]);
456 for (int i = 0; i < ports_n; i++)
457 margin_free_results(results[i], results_n[i]);
463 free(checks_status_ports);