2 * The PCI Utilities -- Margining utility main function
4 * Copyright (c) 2023-2024 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;
67 pci_filter_init(pacc, &pci_filter);
68 if (pci_filter_parse_slot(&pci_filter, filter))
69 die("Invalid device ID: %s\n", filter);
71 if (pci_filter.bus == -1 || pci_filter.slot == -1 || pci_filter.func == -1)
72 die("Invalid device ID: %s\n", filter);
74 if (pci_filter.domain == -1)
75 pci_filter.domain = 0;
77 for (struct pci_dev *p = pacc->devices; p; p = p->next)
79 if (pci_filter_match(&pci_filter, p))
83 die("No such PCI device: %s or you don't have enough privileges.\n", filter);
87 parse_csv_arg(char *arg, u8 *vals)
90 char *token = strtok(arg, ",");
93 vals[cnt] = atoi(token);
95 token = strtok(NULL, ",");
101 scan_links(struct pci_access *pacc, bool only_ready)
104 printf("Links ready for margining:\n");
106 printf("Links with Lane Margining at the Receiver capabilities:\n");
108 for (struct pci_dev *p = pacc->devices; p; p = p->next)
110 if (pci_find_cap(p, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED) && margin_port_is_down(p))
112 struct pci_dev *down = NULL;
113 struct pci_dev *up = NULL;
114 margin_find_pair(pacc, p, &down, &up);
116 if (down && margin_verify_link(down, up))
118 margin_log_bdfs(down, up);
119 if (!only_ready && (margin_check_ready_bit(down) || margin_check_ready_bit(up)))
127 printf("Links not found or you don't have enough privileges.\n");
133 find_ready_links(struct pci_access *pacc, struct pci_dev **down_ports, struct pci_dev **up_ports,
137 for (struct pci_dev *p = pacc->devices; p; p = p->next)
139 if (pci_find_cap(p, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED) && margin_port_is_down(p))
141 struct pci_dev *down = NULL;
142 struct pci_dev *up = NULL;
143 margin_find_pair(pacc, p, &down, &up);
145 if (down && margin_verify_link(down, up)
146 && (margin_check_ready_bit(down) || margin_check_ready_bit(up)))
151 down_ports[cnt] = down;
161 main(int argc, char **argv)
163 struct pci_access *pacc;
165 struct pci_dev **up_ports;
166 struct pci_dev **down_ports;
169 struct margin_link *links;
170 bool *checks_status_ports;
175 /* each link has several receivers -> several results */
176 struct margin_results **results;
179 struct margin_args *args;
183 u8 parallel_lanes_arg = 1;
191 bool run_margin = true;
193 char *dir_for_csv = NULL;
194 bool save_csv = false;
202 margin_print_domain = false;
203 for (struct pci_dev *dev = pacc->devices; dev; dev = dev->next)
205 if (dev->domain != 0)
207 margin_print_domain = true;
212 margin_global_logging = true;
214 struct option long_options[]
215 = { { .name = "margin", .has_arg = no_argument, .flag = NULL, .val = 0 },
216 { .name = "scan", .has_arg = no_argument, .flag = NULL, .val = 1 },
217 { .name = "full", .has_arg = no_argument, .flag = NULL, .val = 2 },
221 c = getopt_long(argc, argv, ":", long_options, NULL);
225 case -1: /* no options (strings like component are possible) */
233 scan_links(pacc, false);
239 default: /* unknown option symbol */
245 while ((c = getopt(argc, argv, ":r:e:l:cp:t:v:VTo:")) != -1)
250 steps_t_arg = atoi(optarg);
256 steps_v_arg = atoi(optarg);
262 parallel_lanes_arg = atoi(optarg);
268 lanes_n = parse_csv_arg(optarg, lanes_arg);
271 error_limit = atoi(optarg);
274 recvs_n = parse_csv_arg(optarg, recvs_arg);
277 dir_for_csv = optarg;
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 struct pci_dev *dev = dev_for_filter(pacc, argv[optind]);
321 if (!margin_find_pair(pacc, dev, &(down_ports[cnt]), &(up_ports[cnt])))
322 die("Cannot find pair for the specified device: %s\n", argv[optind]);
328 die("Bug in the args parsing!\n");
330 if (!pci_find_cap(up_ports[0], PCI_CAP_ID_EXP, PCI_CAP_NORMAL))
331 die("Looks like you don't have enough privileges to access "
332 "Device Configuration Space.\nTry to run utility as root.\n");
334 results = xmalloc(ports_n * sizeof(*results));
335 results_n = xmalloc(ports_n * sizeof(*results_n));
336 links = xmalloc(ports_n * sizeof(*links));
337 checks_status_ports = xmalloc(ports_n * sizeof(*checks_status_ports));
338 args = xmalloc(ports_n * sizeof(*args));
340 for (int i = 0; i < ports_n; i++)
342 args[i].error_limit = error_limit;
343 args[i].parallel_lanes = parallel_lanes_arg;
344 args[i].run_margin = run_margin;
345 args[i].verbosity = 1;
346 args[i].steps_t = steps_t_arg;
347 args[i].steps_v = steps_v_arg;
348 for (int j = 0; j < recvs_n; j++)
349 args[i].recvs[j] = recvs_arg[j];
350 args[i].recvs_n = recvs_n;
351 for (int j = 0; j < lanes_n; j++)
352 args[i].lanes[j] = lanes_arg[j];
353 args[i].lanes_n = lanes_n;
354 args[i].steps_utility = &total_steps;
356 enum margin_test_status args_status;
358 if (!margin_fill_link(down_ports[i], up_ports[i], &links[i]))
360 checks_status_ports[i] = false;
361 results[i] = xmalloc(sizeof(*results[i]));
362 results[i]->test_status = MARGIN_TEST_PREREQS;
366 if ((args_status = margin_process_args(&links[i].down_port, &args[i])) != MARGIN_TEST_OK)
368 checks_status_ports[i] = false;
369 results[i] = xmalloc(sizeof(*results[i]));
370 results[i]->test_status = args_status;
374 checks_status_ports[i] = true;
375 struct margin_params params;
377 for (int j = 0; j < args[i].recvs_n; j++)
379 if (margin_read_params(pacc, args[i].recvs[j] == 6 ? up_ports[i] : down_ports[i],
380 args[i].recvs[j], ¶ms))
382 u8 steps_t = steps_t_arg ? steps_t_arg : params.timing_steps;
383 u8 steps_v = steps_v_arg ? steps_v_arg : params.volt_steps;
384 u8 parallel_recv = parallel_lanes_arg > params.max_lanes + 1 ? params.max_lanes + 1 :
388 = args[i].lanes_n / parallel_recv + ((args[i].lanes_n % parallel_recv) > 0);
390 total_steps += steps_t * step_multiplier;
391 if (params.ind_left_right_tim)
392 total_steps += steps_t * step_multiplier;
393 if (params.volt_support)
395 total_steps += steps_v * step_multiplier;
396 if (params.ind_up_down_volt)
397 total_steps += steps_v * step_multiplier;
403 for (int i = 0; i < ports_n; i++)
405 if (checks_status_ports[i])
406 results[i] = margin_test_link(&links[i], &args[i], &results_n[i]);
410 if (results[i]->test_status == MARGIN_TEST_PREREQS)
413 margin_log_bdfs(down_ports[i], up_ports[i]);
414 printf(" is not ready for margining.\n"
415 "Link data rate must be 16 GT/s or 32 GT/s.\n"
416 "Downstream Component must be at D0 PM state.\n");
418 else if (results[i]->test_status == MARGIN_TEST_ARGS_RECVS)
420 margin_log_link(&links[i]);
421 printf("\nInvalid RecNums specified.\n");
423 else if (results[i]->test_status == MARGIN_TEST_ARGS_LANES)
425 margin_log_link(&links[i]);
426 printf("\nInvalid lanes specified.\n");
429 printf("\n----\n\n");
434 printf("Results:\n");
435 printf("\nPass/fail criteria:\nTiming:\n");
436 printf("Minimum Offset (spec): %d %% UI\nRecommended Offset: %d %% UI\n", MARGIN_TIM_MIN,
437 MARGIN_TIM_RECOMMEND);
438 printf("\nVoltage:\nMinimum Offset (spec): %d mV\n\n", MARGIN_VOLT_MIN);
440 "Margining statuses:\nLIM -\tErrorCount exceeded Error Count Limit (found device limit)\n");
441 printf("NAK -\tDevice didn't execute last command, \n\tso result may be less reliable\n");
442 printf("THR -\tThe set (using the utility options) \n\tstep threshold has been reached\n\n");
443 printf("Notations:\nst - steps\n\n");
445 for (int i = 0; i < ports_n; i++)
448 margin_log_bdfs(down_ports[i], up_ports[i]);
450 margin_results_print_brief(results[i], results_n[i]);
452 margin_results_save_csv(results[i], results_n[i], dir_for_csv, up_ports[i]);
457 for (int i = 0; i < ports_n; i++)
458 margin_free_results(results[i], results_n[i]);
464 free(checks_status_ports);