2 * The PCI Utilities -- Parse pcilmr utility arguments
4 * Copyright (c) 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 = "! Utility requires preliminary preparation of the system. Refer to the pcilmr man page !\n\n"
20 "pcilmr [--margin] [<margining options>] <downstream component> ...\n"
21 "pcilmr --full [<margining options>]\n"
24 "<device/component>:\t[<domain>:]<bus>:<dev>.<func>\n\n"
26 "--margin\t\tMargin selected Links\n"
27 "--full\t\t\tMargin all ready for testing Links in the system (one by one)\n"
28 "--scan\t\t\tScan for Links available for margining\n\n"
29 "Margining options:\n\n"
30 "Margining Test settings:\n"
31 "-c\t\t\tPrint Device Lane Margining Capabilities only. Do not run margining.\n"
32 "-l <lane>[,<lane>...]\tSpecify lanes for margining. Default: all link lanes.\n"
33 "\t\t\tRemember that Device may use Lane Reversal for Lane numbering.\n"
34 "\t\t\tHowever, utility uses logical lane numbers in arguments and for logging.\n"
35 "\t\t\tUtility will automatically determine Lane Reversal and tune its calls.\n"
36 "-e <errors>\t\tSpecify Error Count Limit for margining. Default: 4.\n"
37 "-r <recvn>[,<recvn>...]\tSpecify Receivers to select margining targets.\n"
38 "\t\t\tDefault: all available Receivers (including Retimers).\n"
39 "-p <parallel_lanes>\tSpecify number of lanes to margin simultaneously.\n"
41 "\t\t\tAccording to spec it's possible for Receiver to margin up\n"
42 "\t\t\tto MaxLanes + 1 lanes simultaneously, but usually this works\n"
43 "\t\t\tbad, so this option is for experiments mostly.\n"
44 "-T\t\t\tTime Margining will continue until the Error Count is no more\n"
45 "\t\t\tthan an Error Count Limit. Use this option to find Link limit.\n"
46 "-V\t\t\tSame as -T option, but for Voltage.\n"
47 "-t <steps>\t\tSpecify maximum number of steps for Time Margining.\n"
48 "-v <steps>\t\tSpecify maximum number of steps for Voltage Margining.\n"
49 "Use only one of -T/-t options at the same time (same for -V/-v).\n"
50 "Without these options utility will use MaxSteps from Device\n"
51 "capabilities as test limit.\n\n"
52 "Margining Log settings:\n"
53 "-o <directory>\t\tSave margining results in csv form into the\n"
54 "\t\t\tspecified directory. Utility will generate file with the\n"
55 "\t\t\tname in form of 'lmr_<downstream component>_Rx#_<timestamp>.csv'\n"
56 "\t\t\tfor each successfully tested receiver.\n";
58 static struct pci_dev *
59 dev_for_filter(struct pci_access *pacc, char *filter)
61 struct pci_filter pci_filter;
62 pci_filter_init(pacc, &pci_filter);
63 if (pci_filter_parse_slot(&pci_filter, filter))
64 die("Invalid device ID: %s\n", filter);
66 if (pci_filter.bus == -1 || pci_filter.slot == -1 || pci_filter.func == -1)
67 die("Invalid device ID: %s\n", filter);
69 if (pci_filter.domain == -1)
70 pci_filter.domain = 0;
72 for (struct pci_dev *p = pacc->devices; p; p = p->next)
74 if (pci_filter_match(&pci_filter, p))
78 die("No such PCI device: %s or you don't have enough privileges.\n", filter);
82 parse_csv_arg(char *arg, u8 *vals)
85 char *token = strtok(arg, ",");
88 vals[cnt] = atoi(token);
90 token = strtok(NULL, ",");
96 find_ready_links(struct pci_access *pacc, struct margin_link *links, bool cnt_only)
99 for (struct pci_dev *p = pacc->devices; p; p = p->next)
101 if (pci_find_cap(p, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED) && margin_port_is_down(p))
103 struct pci_dev *down = NULL;
104 struct pci_dev *up = NULL;
105 margin_find_pair(pacc, p, &down, &up);
107 if (down && margin_verify_link(down, up)
108 && (margin_check_ready_bit(down) || margin_check_ready_bit(up)))
111 margin_fill_link(down, up, &(links[cnt]));
120 init_link_args(struct margin_link_args *link_args, struct margin_com_args *com_args)
122 memset(link_args, 0, sizeof(*link_args));
123 link_args->common = com_args;
124 link_args->parallel_lanes = 1;
128 parse_dev_args(int argc, char **argv, struct margin_link_args *args, u8 link_speed)
133 while ((c = getopt(argc, argv, "+r:l:p:t:v:VTg:")) != -1)
138 args->steps_t = atoi(optarg);
144 args->steps_v = atoi(optarg);
150 args->parallel_lanes = atoi(optarg);
153 args->lanes_n = parse_csv_arg(optarg, args->lanes);
156 args->recvs_n = parse_csv_arg(optarg, args->recvs);
159 char recv[2] = { 0 };
161 char unit[4] = { 0 };
162 float criteria = 0.0;
166 int ret = sscanf(optarg, "%1[1-6]%1[tv]=%f%n%3[%,ps]%n%1[f]%n", recv, dir, &criteria,
167 &cons[0], unit, &cons[1], eye, &cons[2]);
170 ret = sscanf(optarg, "%1[1-6]%1[tv]=%1[f]%n,%f%n%2[ps%]%n", recv, dir, eye,
171 &cons[0], &criteria, &cons[1], unit, &cons[2]);
173 die("Invalid arguments\n\n%s", usage);
177 for (int i = 0; i < 3; i++)
178 if (cons[i] > consumed)
180 if ((size_t)consumed != strlen(optarg))
181 die("Invalid arguments\n\n%s", usage);
183 die("Invalid arguments\n\n%s", usage);
184 if (strstr(unit, ",") && eye[0] == 0)
185 die("Invalid arguments\n\n%s", usage);
187 u8 recv_n = recv[0] - '0' - 1;
190 if (unit[0] != ',' && unit[0] != 0)
191 die("Invalid arguments\n\n%s", usage);
192 args->recv_args[recv_n].v.valid = true;
193 args->recv_args[recv_n].v.criteria = criteria;
195 args->recv_args[recv_n].v.one_side_is_whole = true;
200 criteria = criteria / 100.0 * margin_ui[link_speed];
201 else if (unit[0] != 0 && (unit[0] != 'p' || unit[1] != 's'))
202 die("Invalid arguments\n\n%s", usage);
203 else if (unit[0] == 0 && criteria != 0)
204 die("Invalid arguments\n\n%s", usage);
205 args->recv_args[recv_n].t.valid = true;
206 args->recv_args[recv_n].t.criteria = criteria;
208 args->recv_args[recv_n].t.one_side_is_whole = true;
213 die("Invalid arguments\n\n%s", usage);
222 margin_parse_util_args(struct pci_access *pacc, int argc, char **argv, enum margin_mode mode,
225 struct margin_com_args *com_args = xmalloc(sizeof(*com_args));
226 com_args->error_limit = 4;
227 com_args->run_margin = true;
228 com_args->verbosity = 1;
229 com_args->steps_utility = 0;
230 com_args->dir_for_csv = NULL;
231 com_args->save_csv = false;
234 while ((c = getopt(argc, argv, "+e:co:")) != -1)
239 com_args->run_margin = false;
242 com_args->error_limit = atoi(optarg);
245 com_args->dir_for_csv = optarg;
246 com_args->save_csv = true;
249 die("Invalid arguments\n\n%s", usage);
254 if (mode == FULL && optind != argc)
256 if (mode == MARGIN && optind == argc)
258 if (!status && argc > 1)
259 die("Invalid arguments\n\n%s", usage);
267 struct margin_link *links = NULL;
272 ports_n = find_ready_links(pacc, NULL, true);
274 die("Links not found or you don't have enough privileges.\n");
277 links = xmalloc(ports_n * sizeof(*links));
278 find_ready_links(pacc, links, false);
279 for (int i = 0; i < ports_n; i++)
280 init_link_args(&(links[i].args), com_args);
283 else if (mode == MARGIN)
285 while (optind != argc)
287 struct pci_dev *dev = dev_for_filter(pacc, argv[optind]);
289 links = xrealloc(links, (ports_n + 1) * sizeof(*links));
290 struct pci_dev *down;
292 if (!margin_find_pair(pacc, dev, &down, &up))
293 die("Cannot find pair for the specified device: %s\n", argv[optind - 1]);
294 struct pci_cap *cap = pci_find_cap(down, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
296 die("Looks like you don't have enough privileges to access "
297 "Device Configuration Space.\nTry to run utility as root.\n");
298 if (!margin_fill_link(down, up, &(links[ports_n])))
300 margin_gen_bdfs(down, up, err, sizeof(err));
301 die("Link %s is not ready for margining.\n"
302 "Link data rate must be 16 GT/s or 32 GT/s.\n"
303 "Downstream Component must be at D0 PM state.\n",
306 init_link_args(&(links[ports_n].args), com_args);
307 parse_dev_args(argc, argv, &(links[ports_n].args),
308 links[ports_n].down_port.link_speed - 4);
313 die("Bug in the args parsing!\n");