summaryrefslogtreecommitdiffstats
path: root/lmr/margin_args.c
diff options
context:
space:
mode:
Diffstat (limited to 'lmr/margin_args.c')
-rw-r--r--lmr/margin_args.c302
1 files changed, 302 insertions, 0 deletions
diff --git a/lmr/margin_args.c b/lmr/margin_args.c
new file mode 100644
index 0000000..57a1d0a
--- /dev/null
+++ b/lmr/margin_args.c
@@ -0,0 +1,302 @@
+/*
+ * The PCI Utilities -- Parse pcilmr utility arguments
+ *
+ * Copyright (c) 2024 KNS Group LLC (YADRO)
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lmr.h"
+
+const char *usage
+ = "! Utility requires preliminary preparation of the system. Refer to the pcilmr man page !\n\n"
+ "Brief usage (see man for all options):\n"
+ "pcilmr [--margin] [<common options>] <link port> [<link options>] [<link port> [<link "
+ "options>] ...]\n"
+ "pcilmr --full [<common options>]\n"
+ "pcilmr --scan\n\n"
+ "You can specify Downstream or Upstream Port of the Link.\nPort Specifier:\n"
+ "<device/component>:\t[<domain>:]<bus>:<dev>.<func>\n\n"
+ "Modes:\n"
+ "--margin\t\tMargin selected Links\n"
+ "--full\t\t\tMargin all ready for testing Links in the system (one by one)\n"
+ "--scan\t\t\tScan for Links available for margining\n\n"
+ "Margining options (see man for all options):\n\n"
+ "Common (for all specified links) options:\n"
+ "-c\t\t\tPrint Device Lane Margining Capabilities only. Do not run margining.\n\n"
+ "Link specific options:\n"
+ "-r <recvn>[,<recvn>...]\tSpecify Receivers to select margining targets.\n"
+ "\t\t\tDefault: all available Receivers (including Retimers).\n"
+ "-t <steps>\t\tSpecify maximum number of steps for Time Margining.\n"
+ "-v <steps>\t\tSpecify maximum number of steps for Voltage Margining.\n";
+
+static struct pci_dev *
+dev_for_filter(struct pci_access *pacc, char *filter)
+{
+ struct pci_filter pci_filter;
+ pci_filter_init(pacc, &pci_filter);
+ if (pci_filter_parse_slot(&pci_filter, filter))
+ die("Invalid device ID: %s\n", filter);
+
+ if (pci_filter.bus == -1 || pci_filter.slot == -1 || pci_filter.func == -1)
+ die("Invalid device ID: %s\n", filter);
+
+ if (pci_filter.domain == -1)
+ pci_filter.domain = 0;
+
+ for (struct pci_dev *p = pacc->devices; p; p = p->next)
+ {
+ if (pci_filter_match(&pci_filter, p))
+ return p;
+ }
+
+ die("No such PCI device: %s or you don't have enough privileges.\n", filter);
+}
+
+static u8
+parse_csv_arg(char *arg, u8 *vals)
+{
+ u8 cnt = 0;
+ char *token = strtok(arg, ",");
+ while (token)
+ {
+ vals[cnt] = atoi(token);
+ cnt++;
+ token = strtok(NULL, ",");
+ }
+ return cnt;
+}
+
+static u8
+find_ready_links(struct pci_access *pacc, struct margin_link *links, bool cnt_only)
+{
+ u8 cnt = 0;
+ for (struct pci_dev *p = pacc->devices; p; p = p->next)
+ {
+ if (pci_find_cap(p, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED) && margin_port_is_down(p))
+ {
+ struct pci_dev *down = NULL;
+ struct pci_dev *up = NULL;
+ margin_find_pair(pacc, p, &down, &up);
+
+ if (down && margin_verify_link(down, up)
+ && (margin_check_ready_bit(down) || margin_check_ready_bit(up)))
+ {
+ if (!cnt_only)
+ margin_fill_link(down, up, &(links[cnt]));
+ cnt++;
+ }
+ }
+ }
+ return cnt;
+}
+
+static void
+init_link_args(struct margin_link_args *link_args, struct margin_com_args *com_args)
+{
+ memset(link_args, 0, sizeof(*link_args));
+ link_args->common = com_args;
+ link_args->parallel_lanes = 1;
+}
+
+static void
+parse_dev_args(int argc, char **argv, struct margin_link_args *args, u8 link_speed)
+{
+ if (argc == optind)
+ return;
+ int c;
+ while ((c = getopt(argc, argv, "+r:l:p:t:v:VTg:")) != -1)
+ {
+ switch (c)
+ {
+ case 't':
+ args->steps_t = atoi(optarg);
+ break;
+ case 'T':
+ args->steps_t = 63;
+ break;
+ case 'v':
+ args->steps_v = atoi(optarg);
+ break;
+ case 'V':
+ args->steps_v = 127;
+ break;
+ case 'p':
+ args->parallel_lanes = atoi(optarg);
+ break;
+ case 'l':
+ args->lanes_n = parse_csv_arg(optarg, args->lanes);
+ break;
+ case 'r':
+ args->recvs_n = parse_csv_arg(optarg, args->recvs);
+ break;
+ case 'g': {
+ char recv[2] = { 0 };
+ char dir[2] = { 0 };
+ char unit[4] = { 0 };
+ float criteria = 0.0;
+ char eye[2] = { 0 };
+ int cons[3] = { 0 };
+
+ int ret = sscanf(optarg, "%1[1-6]%1[tv]=%f%n%3[%,ps]%n%1[f]%n", recv, dir, &criteria,
+ &cons[0], unit, &cons[1], eye, &cons[2]);
+ if (ret < 3)
+ {
+ ret = sscanf(optarg, "%1[1-6]%1[tv]=%1[f]%n,%f%n%2[ps%]%n", recv, dir, eye,
+ &cons[0], &criteria, &cons[1], unit, &cons[2]);
+ if (ret < 3)
+ die("Invalid arguments\n\n%s", usage);
+ }
+
+ int consumed = 0;
+ for (int i = 0; i < 3; i++)
+ if (cons[i] > consumed)
+ consumed = cons[i];
+ if ((size_t)consumed != strlen(optarg))
+ die("Invalid arguments\n\n%s", usage);
+ if (criteria < 0)
+ die("Invalid arguments\n\n%s", usage);
+ if (strstr(unit, ",") && eye[0] == 0)
+ die("Invalid arguments\n\n%s", usage);
+
+ u8 recv_n = recv[0] - '0' - 1;
+ if (dir[0] == 'v')
+ {
+ if (unit[0] != ',' && unit[0] != 0)
+ die("Invalid arguments\n\n%s", usage);
+ args->recv_args[recv_n].v.valid = true;
+ args->recv_args[recv_n].v.criteria = criteria;
+ if (eye[0] != 0)
+ args->recv_args[recv_n].v.one_side_is_whole = true;
+ }
+ else
+ {
+ if (unit[0] == '%')
+ criteria = criteria / 100.0 * margin_ui[link_speed];
+ else if (unit[0] != 0 && (unit[0] != 'p' || unit[1] != 's'))
+ die("Invalid arguments\n\n%s", usage);
+ else if (unit[0] == 0 && criteria != 0)
+ die("Invalid arguments\n\n%s", usage);
+ args->recv_args[recv_n].t.valid = true;
+ args->recv_args[recv_n].t.criteria = criteria;
+ if (eye[0] != 0)
+ args->recv_args[recv_n].t.one_side_is_whole = true;
+ }
+ break;
+ }
+ case '?':
+ die("Invalid arguments\n\n%s", usage);
+ break;
+ default:
+ return;
+ }
+ }
+}
+
+struct margin_link *
+margin_parse_util_args(struct pci_access *pacc, int argc, char **argv, enum margin_mode mode,
+ u8 *links_n)
+{
+ struct margin_com_args *com_args = xmalloc(sizeof(*com_args));
+ com_args->error_limit = 4;
+ com_args->run_margin = true;
+ com_args->verbosity = 1;
+ com_args->steps_utility = 0;
+ com_args->dir_for_csv = NULL;
+ com_args->save_csv = false;
+ com_args->dwell_time = 1;
+
+ int c;
+ while ((c = getopt(argc, argv, "+e:co:d:")) != -1)
+ {
+ switch (c)
+ {
+ case 'c':
+ com_args->run_margin = false;
+ break;
+ case 'e':
+ com_args->error_limit = atoi(optarg);
+ break;
+ case 'o':
+ com_args->dir_for_csv = optarg;
+ com_args->save_csv = true;
+ break;
+ case 'd':
+ com_args->dwell_time = atoi(optarg);
+ break;
+ default:
+ die("Invalid arguments\n\n%s", usage);
+ }
+ }
+
+ bool status = true;
+ if (mode == FULL && optind != argc)
+ status = false;
+ if (mode == MARGIN && optind == argc)
+ status = false;
+ if (!status && argc > 1)
+ die("Invalid arguments\n\n%s", usage);
+ if (!status)
+ {
+ printf("%s", usage);
+ exit(0);
+ }
+
+ u8 ports_n = 0;
+ struct margin_link *links = NULL;
+ char err[128];
+
+ if (mode == FULL)
+ {
+ ports_n = find_ready_links(pacc, NULL, true);
+ if (ports_n == 0)
+ die("Links not found or you don't have enough privileges.\n");
+ else
+ {
+ links = xmalloc(ports_n * sizeof(*links));
+ find_ready_links(pacc, links, false);
+ for (int i = 0; i < ports_n; i++)
+ init_link_args(&(links[i].args), com_args);
+ }
+ }
+ else if (mode == MARGIN)
+ {
+ while (optind != argc)
+ {
+ struct pci_dev *dev = dev_for_filter(pacc, argv[optind]);
+ optind++;
+ links = xrealloc(links, (ports_n + 1) * sizeof(*links));
+ struct pci_dev *down;
+ struct pci_dev *up;
+ if (!margin_find_pair(pacc, dev, &down, &up))
+ die("Cannot find pair for the specified device: %s\n", argv[optind - 1]);
+ struct pci_cap *cap = pci_find_cap(down, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
+ if (!cap)
+ die("Looks like you don't have enough privileges to access "
+ "Device Configuration Space.\nTry to run utility as root.\n");
+ if (!margin_fill_link(down, up, &(links[ports_n])))
+ {
+ margin_gen_bdfs(down, up, err, sizeof(err));
+ die("Link %s is not ready for margining.\n"
+ "Link data rate must be 16 GT/s or 32 GT/s.\n"
+ "Downstream Component must be at D0 PM state.\n",
+ err);
+ }
+ init_link_args(&(links[ports_n].args), com_args);
+ parse_dev_args(argc, argv, &(links[ports_n].args),
+ links[ports_n].down_port.link_speed - 4);
+ ports_n++;
+ }
+ }
+ else
+ die("Bug in the args parsing!\n");
+
+ *links_n = ports_n;
+ return links;
+}