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 static const char usage_msg[]
22 "pcilmr [<margining options>] <downstream component>\n\n"
24 "<device/component>:\t[<domain>:]<bus>:<dev>.<func>\n\n"
25 "Margining options:\n\n"
26 "Margining Test settings:\n"
27 "-c\t\t\tPrint Device Lane Margining Capabilities only. Do not run margining.\n"
28 "-l <lane>[,<lane>...]\tSpecify lanes for margining. Default: all link lanes.\n"
29 "\t\t\tRemember that Device may use Lane Reversal for Lane numbering.\n"
30 "\t\t\tHowever, utility uses logical lane numbers in arguments and for logging.\n"
31 "\t\t\tUtility will automatically determine Lane Reversal and tune its calls.\n"
32 "-e <errors>\t\tSpecify Error Count Limit for margining. Default: 4.\n"
33 "-r <recvn>[,<recvn>...]\tSpecify Receivers to select margining targets.\n"
34 "\t\t\tDefault: all available Receivers (including Retimers).\n"
35 "-p <parallel_lanes>\tSpecify number of lanes to margin simultaneously.\n"
37 "\t\t\tAccording to spec it's possible for Receiver to margin up\n"
38 "\t\t\tto MaxLanes + 1 lanes simultaneously, but usually this works\n"
39 "\t\t\tbad, so this option is for experiments mostly.\n"
40 "-T\t\t\tTime Margining will continue until the Error Count is no more\n"
41 "\t\t\tthan an Error Count Limit. Use this option to find Link limit.\n"
42 "-V\t\t\tSame as -T option, but for Voltage.\n"
43 "-t <steps>\t\tSpecify maximum number of steps for Time Margining.\n"
44 "-v <steps>\t\tSpecify maximum number of steps for Voltage Margining.\n"
45 "Use only one of -T/-t options at the same time (same for -V/-v).\n"
46 "Without these options utility will use MaxSteps from Device\n"
47 "capabilities as test limit.\n\n";
49 static struct pci_dev *
50 dev_for_filter(struct pci_access *pacc, char *filter)
52 struct pci_filter pci_filter;
54 strncpy(dev, filter, sizeof(dev) - 1);
55 pci_filter_init(pacc, &pci_filter);
56 if (pci_filter_parse_slot(&pci_filter, dev))
57 die("Invalid device ID: %s\n", filter);
59 if (pci_filter.bus == -1 || pci_filter.slot == -1 || pci_filter.func == -1)
60 die("Invalid device ID: %s\n", filter);
62 if (pci_filter.domain == -1)
63 pci_filter.domain = 0;
65 for (struct pci_dev *p = pacc->devices; p; p = p->next)
67 if (pci_filter_match(&pci_filter, p))
71 die("No such PCI device: %s or you don't have enough privileges.\n", filter);
74 static struct pci_dev *
75 find_down_port_for_up(struct pci_access *pacc, struct pci_dev *up)
77 struct pci_dev *down = NULL;
78 for (struct pci_dev *p = pacc->devices; p; p = p->next)
80 if (pci_read_byte(p, PCI_SECONDARY_BUS) == up->bus && up->domain == p->domain)
90 parse_csv_arg(char *arg, u8 *vals)
93 char *token = strtok(arg, ",");
96 vals[cnt] = atoi(token);
98 token = strtok(NULL, ",");
104 main(int argc, char **argv)
106 struct pci_access *pacc;
108 struct pci_dev *up_port;
109 struct pci_dev *down_port;
111 struct margin_link link;
115 struct margin_results *results;
118 struct margin_args args;
122 u8 parallel_lanes_arg = 1;
128 bool run_margin = true;
136 margin_print_domain = false;
137 for (struct pci_dev *dev = pacc->devices; dev; dev = dev->next)
139 if (dev->domain != 0)
141 margin_print_domain = true;
146 margin_global_logging = true;
150 while ((c = getopt(argc, argv, ":r:e:l:cp:t:v:VT")) != -1)
155 steps_t_arg = atoi(optarg);
161 steps_v_arg = atoi(optarg);
167 parallel_lanes_arg = atoi(optarg);
173 lanes_n = parse_csv_arg(optarg, args.lanes);
176 error_limit = atoi(optarg);
179 recvs_n = parse_csv_arg(optarg, args.recvs);
182 die("Invalid arguments\n\n%s", usage_msg);
186 if (optind != argc - 1)
188 if (!status && argc > 1)
189 die("Invalid arguments\n\n%s", usage_msg);
192 printf("%s", usage_msg);
196 up_port = dev_for_filter(pacc, argv[argc - 1]);
198 down_port = find_down_port_for_up(pacc, up_port);
200 die("Cannot find Upstream Component for the specified device: %s\n", argv[argc - 1]);
202 if (!pci_find_cap(up_port, PCI_CAP_ID_EXP, PCI_CAP_NORMAL))
203 die("Looks like you don't have enough privileges to access "
204 "Device Configuration Space.\nTry to run utility as root.\n");
206 if (!margin_fill_link(down_port, up_port, &link))
209 margin_log_bdfs(down_port, up_port);
210 printf(" is not ready for margining.\n"
211 "Link data rate must be 16 GT/s or 32 GT/s.\n"
212 "Downstream Component must be at D0 PM state.\n");
218 args.error_limit = error_limit;
219 args.lanes_n = lanes_n;
220 args.recvs_n = recvs_n;
221 args.steps_t = steps_t_arg;
222 args.steps_v = steps_v_arg;
223 args.parallel_lanes = parallel_lanes_arg;
224 args.run_margin = run_margin;
226 args.steps_utility = &total_steps;
228 enum margin_test_status args_status;
230 if ((args_status = margin_process_args(&link.down_port, &args)) != MARGIN_TEST_OK)
233 margin_log_link(&link);
234 if (args_status == MARGIN_TEST_ARGS_RECVS)
235 margin_log("\nInvalid RecNums specified.\n");
236 else if (args_status == MARGIN_TEST_ARGS_LANES)
237 margin_log("\nInvalid lanes specified.\n");
243 struct margin_params params;
245 for (int i = 0; i < args.recvs_n; i++)
247 if (margin_read_params(pacc, args.recvs[i] == 6 ? up_port : down_port, args.recvs[i],
250 u8 steps_t = steps_t_arg ? steps_t_arg : params.timing_steps;
251 u8 steps_v = steps_v_arg ? steps_v_arg : params.volt_steps;
252 u8 parallel_recv = parallel_lanes_arg > params.max_lanes + 1 ? params.max_lanes + 1 :
256 = args.lanes_n / parallel_recv + ((args.lanes_n % parallel_recv) > 0);
258 total_steps += steps_t * step_multiplier;
259 if (params.ind_left_right_tim)
260 total_steps += steps_t * step_multiplier;
261 if (params.volt_support)
263 total_steps += steps_v * step_multiplier;
264 if (params.ind_up_down_volt)
265 total_steps += steps_v * step_multiplier;
270 results = margin_test_link(&link, &args, &results_n);
273 if (status && run_margin)
275 printf("\nResults:\n");
276 printf("\nPass/fail criteria:\nTiming:\n");
277 printf("Minimum Offset (spec): %d %% UI\nRecommended Offset: %d %% UI\n", MARGIN_TIM_MIN,
278 MARGIN_TIM_RECOMMEND);
279 printf("\nVoltage:\nMinimum Offset (spec): %d mV\n\n", MARGIN_VOLT_MIN);
281 "Margining statuses:\nLIM -\tErrorCount exceeded Error Count Limit (found device limit)\n");
282 printf("NAK -\tDevice didn't execute last command, \n\tso result may be less reliable\n");
283 printf("THR -\tThe set (using the utility options) \n\tstep threshold has been reached\n\n");
284 printf("Notations:\nst - steps\n\n");
286 margin_results_print_brief(results, results_n);
290 margin_free_results(results, results_n);