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"
56 "Margining Log settings:\n"
57 "-o <directory>\t\tSave margining results in csv form into the\n"
58 "\t\t\tspecified directory. Utility will generate file with the\n"
59 "\t\t\tname in form of 'lmr_<downstream component>_Rx#_<timestamp>.csv'\n"
60 "\t\t\tfor each successfully tested receiver.\n";
62 static struct pci_dev *
63 dev_for_filter(struct pci_access *pacc, char *filter)
65 struct pci_filter pci_filter;
67 strncpy(dev, filter, sizeof(dev) - 1);
68 pci_filter_init(pacc, &pci_filter);
69 if (pci_filter_parse_slot(&pci_filter, dev))
70 die("Invalid device ID: %s\n", filter);
72 if (pci_filter.bus == -1 || pci_filter.slot == -1 || pci_filter.func == -1)
73 die("Invalid device ID: %s\n", filter);
75 if (pci_filter.domain == -1)
76 pci_filter.domain = 0;
78 for (struct pci_dev *p = pacc->devices; p; p = p->next)
80 if (pci_filter_match(&pci_filter, p))
84 die("No such PCI device: %s or you don't have enough privileges.\n", filter);
87 static struct pci_dev *
88 find_down_port_for_up(struct pci_access *pacc, struct pci_dev *up)
90 struct pci_dev *down = NULL;
91 for (struct pci_dev *p = pacc->devices; p; p = p->next)
93 if (pci_read_byte(p, PCI_SECONDARY_BUS) == up->bus && up->domain == p->domain)
103 parse_csv_arg(char *arg, u8 *vals)
106 char *token = strtok(arg, ",");
109 vals[cnt] = atoi(token);
111 token = strtok(NULL, ",");
117 scan_links(struct pci_access *pacc, bool only_ready)
120 printf("Links ready for margining:\n");
122 printf("Links with Lane Margining at the Receiver capabilities:\n");
124 for (struct pci_dev *up = pacc->devices; up; up = up->next)
126 if (pci_find_cap(up, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED))
128 struct pci_dev *down = find_down_port_for_up(pacc, up);
130 if (down && margin_verify_link(down, up))
132 margin_log_bdfs(down, up);
133 if (!only_ready && (margin_check_ready_bit(down) || margin_check_ready_bit(up)))
141 printf("Links not found or you don't have enough privileges.\n");
147 find_ready_links(struct pci_access *pacc, struct pci_dev **down_ports, struct pci_dev **up_ports,
151 for (struct pci_dev *up = pacc->devices; up; up = up->next)
153 if (pci_find_cap(up, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED))
155 struct pci_dev *down = find_down_port_for_up(pacc, up);
157 if (down && margin_verify_link(down, up)
158 && (margin_check_ready_bit(down) || margin_check_ready_bit(up)))
163 down_ports[cnt] = down;
173 main(int argc, char **argv)
175 struct pci_access *pacc;
177 struct pci_dev **up_ports;
178 struct pci_dev **down_ports;
181 struct margin_link *links;
182 bool *checks_status_ports;
187 /* each link has several receivers -> several results */
188 struct margin_results **results;
191 struct margin_args *args;
195 u8 parallel_lanes_arg = 1;
203 bool run_margin = true;
205 char *dir_for_csv = NULL;
206 bool save_csv = false;
214 margin_print_domain = false;
215 for (struct pci_dev *dev = pacc->devices; dev; dev = dev->next)
217 if (dev->domain != 0)
219 margin_print_domain = true;
224 margin_global_logging = true;
226 struct option long_options[]
227 = { { .name = "margin", .has_arg = no_argument, .flag = NULL, .val = 0 },
228 { .name = "scan", .has_arg = no_argument, .flag = NULL, .val = 1 },
229 { .name = "full", .has_arg = no_argument, .flag = NULL, .val = 2 },
233 c = getopt_long(argc, argv, ":", long_options, NULL);
237 case -1: /* no options (strings like component are possible) */
245 scan_links(pacc, false);
251 default: /* unknown option symbol */
257 while ((c = getopt(argc, argv, ":r:e:l:cp:t:v:VTo:")) != -1)
262 steps_t_arg = atoi(optarg);
268 steps_v_arg = atoi(optarg);
274 parallel_lanes_arg = atoi(optarg);
280 lanes_n = parse_csv_arg(optarg, lanes_arg);
283 error_limit = atoi(optarg);
286 recvs_n = parse_csv_arg(optarg, recvs_arg);
289 dir_for_csv = optarg;
293 die("Invalid arguments\n\n%s", usage_msg);
297 if (mode == FULL && optind != argc)
299 if (mode == MARGIN && optind == argc)
301 if (!status && argc > 1)
302 die("Invalid arguments\n\n%s", usage_msg);
305 printf("%s", usage_msg);
311 ports_n = find_ready_links(pacc, NULL, NULL, true);
314 die("Links not found or you don't have enough privileges.\n");
318 up_ports = xmalloc(ports_n * sizeof(*up_ports));
319 down_ports = xmalloc(ports_n * sizeof(*down_ports));
320 find_ready_links(pacc, down_ports, up_ports, false);
323 else if (mode == MARGIN)
325 ports_n = argc - optind;
326 up_ports = xmalloc(ports_n * sizeof(*up_ports));
327 down_ports = xmalloc(ports_n * sizeof(*down_ports));
330 while (optind != argc)
332 up_ports[cnt] = dev_for_filter(pacc, argv[optind]);
333 down_ports[cnt] = find_down_port_for_up(pacc, up_ports[cnt]);
334 if (!down_ports[cnt])
335 die("Cannot find Upstream Component for the specified device: %s\n", argv[optind]);
341 die("Bug in the args parsing!\n");
343 if (!pci_find_cap(up_ports[0], PCI_CAP_ID_EXP, PCI_CAP_NORMAL))
344 die("Looks like you don't have enough privileges to access "
345 "Device Configuration Space.\nTry to run utility as root.\n");
347 results = xmalloc(ports_n * sizeof(*results));
348 results_n = xmalloc(ports_n * sizeof(*results_n));
349 links = xmalloc(ports_n * sizeof(*links));
350 checks_status_ports = xmalloc(ports_n * sizeof(*checks_status_ports));
351 args = xmalloc(ports_n * sizeof(*args));
353 for (int i = 0; i < ports_n; i++)
355 args[i].error_limit = error_limit;
356 args[i].parallel_lanes = parallel_lanes_arg;
357 args[i].run_margin = run_margin;
358 args[i].verbosity = 1;
359 args[i].steps_t = steps_t_arg;
360 args[i].steps_v = steps_v_arg;
361 for (int j = 0; j < recvs_n; j++)
362 args[i].recvs[j] = recvs_arg[j];
363 args[i].recvs_n = recvs_n;
364 for (int j = 0; j < lanes_n; j++)
365 args[i].lanes[j] = lanes_arg[j];
366 args[i].lanes_n = lanes_n;
367 args[i].steps_utility = &total_steps;
369 enum margin_test_status args_status;
371 if (!margin_fill_link(down_ports[i], up_ports[i], &links[i]))
373 checks_status_ports[i] = false;
374 results[i] = xmalloc(sizeof(*results[i]));
375 results[i]->test_status = MARGIN_TEST_PREREQS;
379 if ((args_status = margin_process_args(&links[i].down_port, &args[i])) != MARGIN_TEST_OK)
381 checks_status_ports[i] = false;
382 results[i] = xmalloc(sizeof(*results[i]));
383 results[i]->test_status = args_status;
387 checks_status_ports[i] = true;
388 struct margin_params params;
390 for (int j = 0; j < args[i].recvs_n; j++)
392 if (margin_read_params(pacc, args[i].recvs[j] == 6 ? up_ports[i] : down_ports[i],
393 args[i].recvs[j], ¶ms))
395 u8 steps_t = steps_t_arg ? steps_t_arg : params.timing_steps;
396 u8 steps_v = steps_v_arg ? steps_v_arg : params.volt_steps;
397 u8 parallel_recv = parallel_lanes_arg > params.max_lanes + 1 ? params.max_lanes + 1 :
401 = args[i].lanes_n / parallel_recv + ((args[i].lanes_n % parallel_recv) > 0);
403 total_steps += steps_t * step_multiplier;
404 if (params.ind_left_right_tim)
405 total_steps += steps_t * step_multiplier;
406 if (params.volt_support)
408 total_steps += steps_v * step_multiplier;
409 if (params.ind_up_down_volt)
410 total_steps += steps_v * step_multiplier;
416 for (int i = 0; i < ports_n; i++)
418 if (checks_status_ports[i])
419 results[i] = margin_test_link(&links[i], &args[i], &results_n[i]);
423 if (results[i]->test_status == MARGIN_TEST_PREREQS)
426 margin_log_bdfs(down_ports[i], up_ports[i]);
427 printf(" is not ready for margining.\n"
428 "Link data rate must be 16 GT/s or 32 GT/s.\n"
429 "Downstream Component must be at D0 PM state.\n");
431 else if (results[i]->test_status == MARGIN_TEST_ARGS_RECVS)
433 margin_log_link(&links[i]);
434 printf("\nInvalid RecNums specified.\n");
436 else if (results[i]->test_status == MARGIN_TEST_ARGS_LANES)
438 margin_log_link(&links[i]);
439 printf("\nInvalid lanes specified.\n");
442 printf("\n----\n\n");
447 printf("Results:\n");
448 printf("\nPass/fail criteria:\nTiming:\n");
449 printf("Minimum Offset (spec): %d %% UI\nRecommended Offset: %d %% UI\n", MARGIN_TIM_MIN,
450 MARGIN_TIM_RECOMMEND);
451 printf("\nVoltage:\nMinimum Offset (spec): %d mV\n\n", MARGIN_VOLT_MIN);
453 "Margining statuses:\nLIM -\tErrorCount exceeded Error Count Limit (found device limit)\n");
454 printf("NAK -\tDevice didn't execute last command, \n\tso result may be less reliable\n");
455 printf("THR -\tThe set (using the utility options) \n\tstep threshold has been reached\n\n");
456 printf("Notations:\nst - steps\n\n");
458 for (int i = 0; i < ports_n; i++)
461 margin_log_bdfs(down_ports[i], up_ports[i]);
463 margin_results_print_brief(results[i], results_n[i]);
465 margin_results_save_csv(results[i], results_n[i], dir_for_csv, up_ports[i]);
470 for (int i = 0; i < ports_n; i++)
471 margin_free_results(results[i], results_n[i]);
477 free(checks_status_ports);