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[]
23 = "! Utility requires preliminary preparation of the system. Refer to the pcilmr man page !\n\n"
25 "pcilmr [--margin] [<margining options>] <downstream component> ...\n"
26 "pcilmr --full [<margining options>]\n"
29 "<device/component>:\t[<domain>:]<bus>:<dev>.<func>\n\n"
31 "--margin\t\tMargin selected Links\n"
32 "--full\t\t\tMargin all ready for testing Links in the system (one by one)\n"
33 "--scan\t\t\tScan for Links available for margining\n\n"
34 "Margining options:\n\n"
35 "Margining Test settings:\n"
36 "-c\t\t\tPrint Device Lane Margining Capabilities only. Do not run margining.\n"
37 "-l <lane>[,<lane>...]\tSpecify lanes for margining. Default: all link lanes.\n"
38 "\t\t\tRemember that Device may use Lane Reversal for Lane numbering.\n"
39 "\t\t\tHowever, utility uses logical lane numbers in arguments and for logging.\n"
40 "\t\t\tUtility will automatically determine Lane Reversal and tune its calls.\n"
41 "-e <errors>\t\tSpecify Error Count Limit for margining. Default: 4.\n"
42 "-r <recvn>[,<recvn>...]\tSpecify Receivers to select margining targets.\n"
43 "\t\t\tDefault: all available Receivers (including Retimers).\n"
44 "-p <parallel_lanes>\tSpecify number of lanes to margin simultaneously.\n"
46 "\t\t\tAccording to spec it's possible for Receiver to margin up\n"
47 "\t\t\tto MaxLanes + 1 lanes simultaneously, but usually this works\n"
48 "\t\t\tbad, so this option is for experiments mostly.\n"
49 "-T\t\t\tTime Margining will continue until the Error Count is no more\n"
50 "\t\t\tthan an Error Count Limit. Use this option to find Link limit.\n"
51 "-V\t\t\tSame as -T option, but for Voltage.\n"
52 "-t <steps>\t\tSpecify maximum number of steps for Time Margining.\n"
53 "-v <steps>\t\tSpecify maximum number of steps for Voltage Margining.\n"
54 "Use only one of -T/-t options at the same time (same for -V/-v).\n"
55 "Without these options utility will use MaxSteps from Device\n"
56 "capabilities as test limit.\n\n"
57 "Margining Log settings:\n"
58 "-o <directory>\t\tSave margining results in csv form into the\n"
59 "\t\t\tspecified directory. Utility will generate file with the\n"
60 "\t\t\tname in form of 'lmr_<downstream component>_Rx#_<timestamp>.csv'\n"
61 "\t\t\tfor each successfully tested receiver.\n";
63 static struct pci_dev *
64 dev_for_filter(struct pci_access *pacc, char *filter)
66 struct pci_filter pci_filter;
68 strncpy(dev, filter, sizeof(dev) - 1);
69 pci_filter_init(pacc, &pci_filter);
70 if (pci_filter_parse_slot(&pci_filter, dev))
71 die("Invalid device ID: %s\n", filter);
73 if (pci_filter.bus == -1 || pci_filter.slot == -1 || pci_filter.func == -1)
74 die("Invalid device ID: %s\n", filter);
76 if (pci_filter.domain == -1)
77 pci_filter.domain = 0;
79 for (struct pci_dev *p = pacc->devices; p; p = p->next)
81 if (pci_filter_match(&pci_filter, p))
85 die("No such PCI device: %s or you don't have enough privileges.\n", filter);
88 static struct pci_dev *
89 find_down_port_for_up(struct pci_access *pacc, struct pci_dev *up)
91 struct pci_dev *down = NULL;
92 for (struct pci_dev *p = pacc->devices; p; p = p->next)
94 if (pci_read_byte(p, PCI_SECONDARY_BUS) == up->bus && up->domain == p->domain)
104 parse_csv_arg(char *arg, u8 *vals)
107 char *token = strtok(arg, ",");
110 vals[cnt] = atoi(token);
112 token = strtok(NULL, ",");
118 scan_links(struct pci_access *pacc, bool only_ready)
121 printf("Links ready for margining:\n");
123 printf("Links with Lane Margining at the Receiver capabilities:\n");
125 for (struct pci_dev *up = pacc->devices; up; up = up->next)
127 if (pci_find_cap(up, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED))
129 struct pci_dev *down = find_down_port_for_up(pacc, up);
131 if (down && margin_verify_link(down, up))
133 margin_log_bdfs(down, up);
134 if (!only_ready && (margin_check_ready_bit(down) || margin_check_ready_bit(up)))
142 printf("Links not found or you don't have enough privileges.\n");
148 find_ready_links(struct pci_access *pacc, struct pci_dev **down_ports, struct pci_dev **up_ports,
152 for (struct pci_dev *up = pacc->devices; up; up = up->next)
154 if (pci_find_cap(up, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED))
156 struct pci_dev *down = find_down_port_for_up(pacc, up);
158 if (down && margin_verify_link(down, up)
159 && (margin_check_ready_bit(down) || margin_check_ready_bit(up)))
164 down_ports[cnt] = down;
174 main(int argc, char **argv)
176 struct pci_access *pacc;
178 struct pci_dev **up_ports;
179 struct pci_dev **down_ports;
182 struct margin_link *links;
183 bool *checks_status_ports;
188 /* each link has several receivers -> several results */
189 struct margin_results **results;
192 struct margin_args *args;
196 u8 parallel_lanes_arg = 1;
204 bool run_margin = true;
206 char *dir_for_csv = NULL;
207 bool save_csv = false;
215 margin_print_domain = false;
216 for (struct pci_dev *dev = pacc->devices; dev; dev = dev->next)
218 if (dev->domain != 0)
220 margin_print_domain = true;
225 margin_global_logging = true;
227 struct option long_options[]
228 = { { .name = "margin", .has_arg = no_argument, .flag = NULL, .val = 0 },
229 { .name = "scan", .has_arg = no_argument, .flag = NULL, .val = 1 },
230 { .name = "full", .has_arg = no_argument, .flag = NULL, .val = 2 },
234 c = getopt_long(argc, argv, ":", long_options, NULL);
238 case -1: /* no options (strings like component are possible) */
246 scan_links(pacc, false);
252 default: /* unknown option symbol */
258 while ((c = getopt(argc, argv, ":r:e:l:cp:t:v:VTo:")) != -1)
263 steps_t_arg = atoi(optarg);
269 steps_v_arg = atoi(optarg);
275 parallel_lanes_arg = atoi(optarg);
281 lanes_n = parse_csv_arg(optarg, lanes_arg);
284 error_limit = atoi(optarg);
287 recvs_n = parse_csv_arg(optarg, recvs_arg);
290 dir_for_csv = optarg;
294 die("Invalid arguments\n\n%s", usage_msg);
298 if (mode == FULL && optind != argc)
300 if (mode == MARGIN && optind == argc)
302 if (!status && argc > 1)
303 die("Invalid arguments\n\n%s", usage_msg);
306 printf("%s", usage_msg);
312 ports_n = find_ready_links(pacc, NULL, NULL, true);
315 die("Links not found or you don't have enough privileges.\n");
319 up_ports = xmalloc(ports_n * sizeof(*up_ports));
320 down_ports = xmalloc(ports_n * sizeof(*down_ports));
321 find_ready_links(pacc, down_ports, up_ports, false);
324 else if (mode == MARGIN)
326 ports_n = argc - optind;
327 up_ports = xmalloc(ports_n * sizeof(*up_ports));
328 down_ports = xmalloc(ports_n * sizeof(*down_ports));
331 while (optind != argc)
333 up_ports[cnt] = dev_for_filter(pacc, argv[optind]);
334 down_ports[cnt] = find_down_port_for_up(pacc, up_ports[cnt]);
335 if (!down_ports[cnt])
336 die("Cannot find Upstream Component for the specified device: %s\n", argv[optind]);
342 die("Bug in the args parsing!\n");
344 if (!pci_find_cap(up_ports[0], PCI_CAP_ID_EXP, PCI_CAP_NORMAL))
345 die("Looks like you don't have enough privileges to access "
346 "Device Configuration Space.\nTry to run utility as root.\n");
348 results = xmalloc(ports_n * sizeof(*results));
349 results_n = xmalloc(ports_n * sizeof(*results_n));
350 links = xmalloc(ports_n * sizeof(*links));
351 checks_status_ports = xmalloc(ports_n * sizeof(*checks_status_ports));
352 args = xmalloc(ports_n * sizeof(*args));
354 for (int i = 0; i < ports_n; i++)
356 args[i].error_limit = error_limit;
357 args[i].parallel_lanes = parallel_lanes_arg;
358 args[i].run_margin = run_margin;
359 args[i].verbosity = 1;
360 args[i].steps_t = steps_t_arg;
361 args[i].steps_v = steps_v_arg;
362 for (int j = 0; j < recvs_n; j++)
363 args[i].recvs[j] = recvs_arg[j];
364 args[i].recvs_n = recvs_n;
365 for (int j = 0; j < lanes_n; j++)
366 args[i].lanes[j] = lanes_arg[j];
367 args[i].lanes_n = lanes_n;
368 args[i].steps_utility = &total_steps;
370 enum margin_test_status args_status;
372 if (!margin_fill_link(down_ports[i], up_ports[i], &links[i]))
374 checks_status_ports[i] = false;
375 results[i] = xmalloc(sizeof(*results[i]));
376 results[i]->test_status = MARGIN_TEST_PREREQS;
380 if ((args_status = margin_process_args(&links[i].down_port, &args[i])) != MARGIN_TEST_OK)
382 checks_status_ports[i] = false;
383 results[i] = xmalloc(sizeof(*results[i]));
384 results[i]->test_status = args_status;
388 checks_status_ports[i] = true;
389 struct margin_params params;
391 for (int j = 0; j < args[i].recvs_n; j++)
393 if (margin_read_params(pacc, args[i].recvs[j] == 6 ? up_ports[i] : down_ports[i],
394 args[i].recvs[j], ¶ms))
396 u8 steps_t = steps_t_arg ? steps_t_arg : params.timing_steps;
397 u8 steps_v = steps_v_arg ? steps_v_arg : params.volt_steps;
398 u8 parallel_recv = parallel_lanes_arg > params.max_lanes + 1 ? params.max_lanes + 1 :
402 = args[i].lanes_n / parallel_recv + ((args[i].lanes_n % parallel_recv) > 0);
404 total_steps += steps_t * step_multiplier;
405 if (params.ind_left_right_tim)
406 total_steps += steps_t * step_multiplier;
407 if (params.volt_support)
409 total_steps += steps_v * step_multiplier;
410 if (params.ind_up_down_volt)
411 total_steps += steps_v * step_multiplier;
417 for (int i = 0; i < ports_n; i++)
419 if (checks_status_ports[i])
420 results[i] = margin_test_link(&links[i], &args[i], &results_n[i]);
424 if (results[i]->test_status == MARGIN_TEST_PREREQS)
427 margin_log_bdfs(down_ports[i], up_ports[i]);
428 printf(" is not ready for margining.\n"
429 "Link data rate must be 16 GT/s or 32 GT/s.\n"
430 "Downstream Component must be at D0 PM state.\n");
432 else if (results[i]->test_status == MARGIN_TEST_ARGS_RECVS)
434 margin_log_link(&links[i]);
435 printf("\nInvalid RecNums specified.\n");
437 else if (results[i]->test_status == MARGIN_TEST_ARGS_LANES)
439 margin_log_link(&links[i]);
440 printf("\nInvalid lanes specified.\n");
443 printf("\n----\n\n");
448 printf("Results:\n");
449 printf("\nPass/fail criteria:\nTiming:\n");
450 printf("Minimum Offset (spec): %d %% UI\nRecommended Offset: %d %% UI\n", MARGIN_TIM_MIN,
451 MARGIN_TIM_RECOMMEND);
452 printf("\nVoltage:\nMinimum Offset (spec): %d mV\n\n", MARGIN_VOLT_MIN);
454 "Margining statuses:\nLIM -\tErrorCount exceeded Error Count Limit (found device limit)\n");
455 printf("NAK -\tDevice didn't execute last command, \n\tso result may be less reliable\n");
456 printf("THR -\tThe set (using the utility options) \n\tstep threshold has been reached\n\n");
457 printf("Notations:\nst - steps\n\n");
459 for (int i = 0; i < ports_n; i++)
462 margin_log_bdfs(down_ports[i], up_ports[i]);
464 margin_results_print_brief(results[i], results_n[i]);
466 margin_results_save_csv(results[i], results_n[i], dir_for_csv, up_ports[i]);
471 for (int i = 0; i < ports_n; i++)
472 margin_free_results(results[i], results_n[i]);
478 free(checks_status_ports);