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;
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);
86 static struct pci_dev *
87 find_down_port_for_up(struct pci_access *pacc, struct pci_dev *up)
89 struct pci_dev *down = NULL;
90 for (struct pci_dev *p = pacc->devices; p; p = p->next)
92 if (pci_read_byte(p, PCI_SECONDARY_BUS) == up->bus && up->domain == p->domain)
102 parse_csv_arg(char *arg, u8 *vals)
105 char *token = strtok(arg, ",");
108 vals[cnt] = atoi(token);
110 token = strtok(NULL, ",");
116 scan_links(struct pci_access *pacc, bool only_ready)
119 printf("Links ready for margining:\n");
121 printf("Links with Lane Margining at the Receiver capabilities:\n");
123 for (struct pci_dev *up = pacc->devices; up; up = up->next)
125 if (pci_find_cap(up, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED))
127 struct pci_dev *down = find_down_port_for_up(pacc, up);
129 if (down && margin_verify_link(down, up))
131 margin_log_bdfs(down, up);
132 if (!only_ready && (margin_check_ready_bit(down) || margin_check_ready_bit(up)))
140 printf("Links not found or you don't have enough privileges.\n");
146 find_ready_links(struct pci_access *pacc, struct pci_dev **down_ports, struct pci_dev **up_ports,
150 for (struct pci_dev *up = pacc->devices; up; up = up->next)
152 if (pci_find_cap(up, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED))
154 struct pci_dev *down = find_down_port_for_up(pacc, up);
156 if (down && margin_verify_link(down, up)
157 && (margin_check_ready_bit(down) || margin_check_ready_bit(up)))
162 down_ports[cnt] = down;
172 main(int argc, char **argv)
174 struct pci_access *pacc;
176 struct pci_dev **up_ports;
177 struct pci_dev **down_ports;
180 struct margin_link *links;
181 bool *checks_status_ports;
186 /* each link has several receivers -> several results */
187 struct margin_results **results;
190 struct margin_args *args;
194 u8 parallel_lanes_arg = 1;
202 bool run_margin = true;
204 char *dir_for_csv = NULL;
205 bool save_csv = false;
213 margin_print_domain = false;
214 for (struct pci_dev *dev = pacc->devices; dev; dev = dev->next)
216 if (dev->domain != 0)
218 margin_print_domain = true;
223 margin_global_logging = true;
225 struct option long_options[]
226 = { { .name = "margin", .has_arg = no_argument, .flag = NULL, .val = 0 },
227 { .name = "scan", .has_arg = no_argument, .flag = NULL, .val = 1 },
228 { .name = "full", .has_arg = no_argument, .flag = NULL, .val = 2 },
232 c = getopt_long(argc, argv, ":", long_options, NULL);
236 case -1: /* no options (strings like component are possible) */
244 scan_links(pacc, false);
250 default: /* unknown option symbol */
256 while ((c = getopt(argc, argv, ":r:e:l:cp:t:v:VTo:")) != -1)
261 steps_t_arg = atoi(optarg);
267 steps_v_arg = atoi(optarg);
273 parallel_lanes_arg = atoi(optarg);
279 lanes_n = parse_csv_arg(optarg, lanes_arg);
282 error_limit = atoi(optarg);
285 recvs_n = parse_csv_arg(optarg, recvs_arg);
288 dir_for_csv = optarg;
292 die("Invalid arguments\n\n%s", usage_msg);
296 if (mode == FULL && optind != argc)
298 if (mode == MARGIN && optind == argc)
300 if (!status && argc > 1)
301 die("Invalid arguments\n\n%s", usage_msg);
304 printf("%s", usage_msg);
310 ports_n = find_ready_links(pacc, NULL, NULL, true);
313 die("Links not found or you don't have enough privileges.\n");
317 up_ports = xmalloc(ports_n * sizeof(*up_ports));
318 down_ports = xmalloc(ports_n * sizeof(*down_ports));
319 find_ready_links(pacc, down_ports, up_ports, false);
322 else if (mode == MARGIN)
324 ports_n = argc - optind;
325 up_ports = xmalloc(ports_n * sizeof(*up_ports));
326 down_ports = xmalloc(ports_n * sizeof(*down_ports));
329 while (optind != argc)
331 up_ports[cnt] = dev_for_filter(pacc, argv[optind]);
332 down_ports[cnt] = find_down_port_for_up(pacc, up_ports[cnt]);
333 if (!down_ports[cnt])
334 die("Cannot find Upstream Component for the specified device: %s\n", argv[optind]);
340 die("Bug in the args parsing!\n");
342 if (!pci_find_cap(up_ports[0], PCI_CAP_ID_EXP, PCI_CAP_NORMAL))
343 die("Looks like you don't have enough privileges to access "
344 "Device Configuration Space.\nTry to run utility as root.\n");
346 results = xmalloc(ports_n * sizeof(*results));
347 results_n = xmalloc(ports_n * sizeof(*results_n));
348 links = xmalloc(ports_n * sizeof(*links));
349 checks_status_ports = xmalloc(ports_n * sizeof(*checks_status_ports));
350 args = xmalloc(ports_n * sizeof(*args));
352 for (int i = 0; i < ports_n; i++)
354 args[i].error_limit = error_limit;
355 args[i].parallel_lanes = parallel_lanes_arg;
356 args[i].run_margin = run_margin;
357 args[i].verbosity = 1;
358 args[i].steps_t = steps_t_arg;
359 args[i].steps_v = steps_v_arg;
360 for (int j = 0; j < recvs_n; j++)
361 args[i].recvs[j] = recvs_arg[j];
362 args[i].recvs_n = recvs_n;
363 for (int j = 0; j < lanes_n; j++)
364 args[i].lanes[j] = lanes_arg[j];
365 args[i].lanes_n = lanes_n;
366 args[i].steps_utility = &total_steps;
368 enum margin_test_status args_status;
370 if (!margin_fill_link(down_ports[i], up_ports[i], &links[i]))
372 checks_status_ports[i] = false;
373 results[i] = xmalloc(sizeof(*results[i]));
374 results[i]->test_status = MARGIN_TEST_PREREQS;
378 if ((args_status = margin_process_args(&links[i].down_port, &args[i])) != MARGIN_TEST_OK)
380 checks_status_ports[i] = false;
381 results[i] = xmalloc(sizeof(*results[i]));
382 results[i]->test_status = args_status;
386 checks_status_ports[i] = true;
387 struct margin_params params;
389 for (int j = 0; j < args[i].recvs_n; j++)
391 if (margin_read_params(pacc, args[i].recvs[j] == 6 ? up_ports[i] : down_ports[i],
392 args[i].recvs[j], ¶ms))
394 u8 steps_t = steps_t_arg ? steps_t_arg : params.timing_steps;
395 u8 steps_v = steps_v_arg ? steps_v_arg : params.volt_steps;
396 u8 parallel_recv = parallel_lanes_arg > params.max_lanes + 1 ? params.max_lanes + 1 :
400 = args[i].lanes_n / parallel_recv + ((args[i].lanes_n % parallel_recv) > 0);
402 total_steps += steps_t * step_multiplier;
403 if (params.ind_left_right_tim)
404 total_steps += steps_t * step_multiplier;
405 if (params.volt_support)
407 total_steps += steps_v * step_multiplier;
408 if (params.ind_up_down_volt)
409 total_steps += steps_v * step_multiplier;
415 for (int i = 0; i < ports_n; i++)
417 if (checks_status_ports[i])
418 results[i] = margin_test_link(&links[i], &args[i], &results_n[i]);
422 if (results[i]->test_status == MARGIN_TEST_PREREQS)
425 margin_log_bdfs(down_ports[i], up_ports[i]);
426 printf(" is not ready for margining.\n"
427 "Link data rate must be 16 GT/s or 32 GT/s.\n"
428 "Downstream Component must be at D0 PM state.\n");
430 else if (results[i]->test_status == MARGIN_TEST_ARGS_RECVS)
432 margin_log_link(&links[i]);
433 printf("\nInvalid RecNums specified.\n");
435 else if (results[i]->test_status == MARGIN_TEST_ARGS_LANES)
437 margin_log_link(&links[i]);
438 printf("\nInvalid lanes specified.\n");
441 printf("\n----\n\n");
446 printf("Results:\n");
447 printf("\nPass/fail criteria:\nTiming:\n");
448 printf("Minimum Offset (spec): %d %% UI\nRecommended Offset: %d %% UI\n", MARGIN_TIM_MIN,
449 MARGIN_TIM_RECOMMEND);
450 printf("\nVoltage:\nMinimum Offset (spec): %d mV\n\n", MARGIN_VOLT_MIN);
452 "Margining statuses:\nLIM -\tErrorCount exceeded Error Count Limit (found device limit)\n");
453 printf("NAK -\tDevice didn't execute last command, \n\tso result may be less reliable\n");
454 printf("THR -\tThe set (using the utility options) \n\tstep threshold has been reached\n\n");
455 printf("Notations:\nst - steps\n\n");
457 for (int i = 0; i < ports_n; i++)
460 margin_log_bdfs(down_ports[i], up_ports[i]);
462 margin_results_print_brief(results[i], results_n[i]);
464 margin_results_save_csv(results[i], results_n[i], dir_for_csv, up_ports[i]);
469 for (int i = 0; i < ports_n; i++)
470 margin_free_results(results[i], results_n[i]);
476 free(checks_status_ports);