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"
19 "Brief usage (see man for all options):\n"
20 "pcilmr [--margin] [<common options>] <link port> [<link options>] [<link port> [<link "
22 "pcilmr --full [<common options>]\n"
24 "You can specify Downstream or Upstream Port of the Link.\nPort Specifier:\n"
25 "<device/component>:\t[<domain>:]<bus>:<dev>.<func>\n\n"
27 "--margin\t\tMargin selected Links\n"
28 "--full\t\t\tMargin all ready for testing Links in the system (one by one)\n"
29 "--scan\t\t\tScan for Links available for margining\n\n"
30 "Margining options (see man for all options):\n\n"
31 "Common (for all specified links) options:\n"
32 "-c\t\t\tPrint Device Lane Margining Capabilities only. Do not run margining.\n\n"
33 "Link specific options:\n"
34 "-r <recvn>[,<recvn>...]\tSpecify Receivers to select margining targets.\n"
35 "\t\t\tDefault: all available Receivers (including Retimers).\n"
36 "-t <steps>\t\tSpecify maximum number of steps for Time Margining.\n"
37 "-v <steps>\t\tSpecify maximum number of steps for Voltage Margining.\n";
39 static struct pci_dev *
40 dev_for_filter(struct pci_access *pacc, char *filter)
42 struct pci_filter pci_filter;
43 pci_filter_init(pacc, &pci_filter);
44 if (pci_filter_parse_slot(&pci_filter, filter))
45 die("Invalid device ID: %s\n", filter);
47 if (pci_filter.bus == -1 || pci_filter.slot == -1 || pci_filter.func == -1)
48 die("Invalid device ID: %s\n", filter);
50 if (pci_filter.domain == -1)
51 pci_filter.domain = 0;
53 for (struct pci_dev *p = pacc->devices; p; p = p->next)
55 if (pci_filter_match(&pci_filter, p))
59 die("No such PCI device: %s or you don't have enough privileges.\n", filter);
63 parse_csv_arg(char *arg, u8 *vals)
66 char *token = strtok(arg, ",");
69 vals[cnt] = atoi(token);
71 token = strtok(NULL, ",");
77 find_ready_links(struct pci_access *pacc, struct margin_link *links, bool cnt_only)
80 for (struct pci_dev *p = pacc->devices; p; p = p->next)
82 if (pci_find_cap(p, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED) && margin_port_is_down(p))
84 struct pci_dev *down = NULL;
85 struct pci_dev *up = NULL;
86 margin_find_pair(pacc, p, &down, &up);
88 if (down && margin_verify_link(down, up)
89 && (margin_check_ready_bit(down) || margin_check_ready_bit(up)))
92 margin_fill_link(down, up, &(links[cnt]));
101 init_link_args(struct margin_link_args *link_args, struct margin_com_args *com_args)
103 memset(link_args, 0, sizeof(*link_args));
104 link_args->common = com_args;
105 link_args->parallel_lanes = 1;
109 parse_dev_args(int argc, char **argv, struct margin_link_args *args, u8 link_speed)
114 while ((c = getopt(argc, argv, "+r:l:p:t:v:VTg:")) != -1)
119 args->steps_t = atoi(optarg);
125 args->steps_v = atoi(optarg);
131 args->parallel_lanes = atoi(optarg);
134 args->lanes_n = parse_csv_arg(optarg, args->lanes);
137 args->recvs_n = parse_csv_arg(optarg, args->recvs);
140 char recv[2] = { 0 };
142 char unit[4] = { 0 };
143 float criteria = 0.0;
147 int ret = sscanf(optarg, "%1[1-6]%1[tv]=%f%n%3[%,ps]%n%1[f]%n", recv, dir, &criteria,
148 &cons[0], unit, &cons[1], eye, &cons[2]);
151 ret = sscanf(optarg, "%1[1-6]%1[tv]=%1[f]%n,%f%n%2[ps%]%n", recv, dir, eye,
152 &cons[0], &criteria, &cons[1], unit, &cons[2]);
154 die("Invalid arguments\n\n%s", usage);
158 for (int i = 0; i < 3; i++)
159 if (cons[i] > consumed)
161 if ((size_t)consumed != strlen(optarg))
162 die("Invalid arguments\n\n%s", usage);
164 die("Invalid arguments\n\n%s", usage);
165 if (strstr(unit, ",") && eye[0] == 0)
166 die("Invalid arguments\n\n%s", usage);
168 u8 recv_n = recv[0] - '0' - 1;
171 if (unit[0] != ',' && unit[0] != 0)
172 die("Invalid arguments\n\n%s", usage);
173 args->recv_args[recv_n].v.valid = true;
174 args->recv_args[recv_n].v.criteria = criteria;
176 args->recv_args[recv_n].v.one_side_is_whole = true;
181 criteria = criteria / 100.0 * margin_ui[link_speed];
182 else if (unit[0] != 0 && (unit[0] != 'p' || unit[1] != 's'))
183 die("Invalid arguments\n\n%s", usage);
184 else if (unit[0] == 0 && criteria != 0)
185 die("Invalid arguments\n\n%s", usage);
186 args->recv_args[recv_n].t.valid = true;
187 args->recv_args[recv_n].t.criteria = criteria;
189 args->recv_args[recv_n].t.one_side_is_whole = true;
194 die("Invalid arguments\n\n%s", usage);
203 margin_parse_util_args(struct pci_access *pacc, int argc, char **argv, enum margin_mode mode,
206 struct margin_com_args *com_args = xmalloc(sizeof(*com_args));
207 com_args->error_limit = 4;
208 com_args->run_margin = true;
209 com_args->verbosity = 1;
210 com_args->steps_utility = 0;
211 com_args->dir_for_csv = NULL;
212 com_args->save_csv = false;
213 com_args->dwell_time = 1;
216 while ((c = getopt(argc, argv, "+e:co:d:")) != -1)
221 com_args->run_margin = false;
224 com_args->error_limit = atoi(optarg);
227 com_args->dir_for_csv = optarg;
228 com_args->save_csv = true;
231 com_args->dwell_time = atoi(optarg);
234 die("Invalid arguments\n\n%s", usage);
239 if (mode == FULL && optind != argc)
241 if (mode == MARGIN && optind == argc)
243 if (!status && argc > 1)
244 die("Invalid arguments\n\n%s", usage);
252 struct margin_link *links = NULL;
257 ports_n = find_ready_links(pacc, NULL, true);
259 die("Links not found or you don't have enough privileges.\n");
262 links = xmalloc(ports_n * sizeof(*links));
263 find_ready_links(pacc, links, false);
264 for (int i = 0; i < ports_n; i++)
265 init_link_args(&(links[i].args), com_args);
268 else if (mode == MARGIN)
270 while (optind != argc)
272 struct pci_dev *dev = dev_for_filter(pacc, argv[optind]);
274 links = xrealloc(links, (ports_n + 1) * sizeof(*links));
275 struct pci_dev *down;
277 if (!margin_find_pair(pacc, dev, &down, &up))
278 die("Cannot find pair for the specified device: %s\n", argv[optind - 1]);
279 struct pci_cap *cap = pci_find_cap(down, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
281 die("Looks like you don't have enough privileges to access "
282 "Device Configuration Space.\nTry to run utility as root.\n");
283 if (!margin_fill_link(down, up, &(links[ports_n])))
285 margin_gen_bdfs(down, up, err, sizeof(err));
286 die("Link %s is not ready for margining.\n"
287 "Link data rate must be 16 GT/s or 32 GT/s.\n"
288 "Downstream Component must be at D0 PM state.\n",
291 init_link_args(&(links[ports_n].args), com_args);
292 parse_dev_args(argc, argv, &(links[ports_n].args),
293 links[ports_n].down_port.link_speed - 4);
298 die("Bug in the args parsing!\n");