* SPDX-License-Identifier: GPL-2.0-or-later
*/
-#include <getopt.h>
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include "lmr/lmr.h"
const char program_name[] = "pcilmr";
-enum mode { MARGIN, FULL };
+enum mode { MARGIN, FULL, SCAN };
static const char usage_msg[]
- = "Usage:\n"
+ = "! Utility requires preliminary preparation of the system. Refer to the pcilmr man page !\n\n"
+ "Usage:\n"
"pcilmr [--margin] [<margining options>] <downstream component> ...\n"
"pcilmr --full [<margining options>]\n"
+ "pcilmr --scan\n\n"
"Device 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:\n\n"
"Margining Test settings:\n"
"-c\t\t\tPrint Device Lane Margining Capabilities only. Do not run margining.\n"
"-v <steps>\t\tSpecify maximum number of steps for Voltage Margining.\n"
"Use only one of -T/-t options at the same time (same for -V/-v).\n"
"Without these options utility will use MaxSteps from Device\n"
- "capabilities as test limit.\n\n";
+ "capabilities as test limit.\n\n"
+ "Margining Log settings:\n"
+ "-o <directory>\t\tSave margining results in csv form into the\n"
+ "\t\t\tspecified directory. Utility will generate file with the\n"
+ "\t\t\tname in form of 'lmr_<downstream component>_Rx#_<timestamp>.csv'\n"
+ "\t\t\tfor each successfully tested receiver.\n";
static struct pci_dev *
dev_for_filter(struct pci_access *pacc, char *filter)
{
struct pci_filter pci_filter;
- char dev[17] = { 0 };
- strncpy(dev, filter, sizeof(dev) - 1);
pci_filter_init(pacc, &pci_filter);
- if (pci_filter_parse_slot(&pci_filter, dev))
+ 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)
return cnt;
}
+static void
+scan_links(struct pci_access *pacc, bool only_ready)
+{
+ if (only_ready)
+ printf("Links ready for margining:\n");
+ else
+ printf("Links with Lane Margining at the Receiver capabilities:\n");
+ bool flag = true;
+ for (struct pci_dev *up = pacc->devices; up; up = up->next)
+ {
+ if (pci_find_cap(up, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED))
+ {
+ struct pci_dev *down = find_down_port_for_up(pacc, up);
+
+ if (down && margin_verify_link(down, up))
+ {
+ margin_log_bdfs(down, up);
+ if (!only_ready && (margin_check_ready_bit(down) || margin_check_ready_bit(up)))
+ printf(" - Ready");
+ printf("\n");
+ flag = false;
+ }
+ }
+ }
+ if (flag)
+ printf("Links not found or you don't have enough privileges.\n");
+ pci_cleanup(pacc);
+ exit(0);
+}
+
static u8
find_ready_links(struct pci_access *pacc, struct pci_dev **down_ports, struct pci_dev **up_ports,
bool cnt_only)
bool run_margin = true;
+ char *dir_for_csv = NULL;
+ bool save_csv = false;
+
u64 total_steps = 0;
pacc = pci_alloc();
struct option long_options[]
= { { .name = "margin", .has_arg = no_argument, .flag = NULL, .val = 0 },
- { .name = "full", .has_arg = no_argument, .flag = NULL, .val = 1 },
+ { .name = "scan", .has_arg = no_argument, .flag = NULL, .val = 1 },
+ { .name = "full", .has_arg = no_argument, .flag = NULL, .val = 2 },
{ 0, 0, 0, 0 } };
int c;
mode = MARGIN;
break;
case 1:
+ mode = SCAN;
+ if (optind == argc)
+ scan_links(pacc, false);
+ optind--;
+ break;
+ case 2:
mode = FULL;
break;
default: /* unknown option symbol */
break;
}
- while ((c = getopt(argc, argv, ":r:e:l:cp:t:v:VT")) != -1)
+ while ((c = getopt(argc, argv, ":r:e:l:cp:t:v:VTo:")) != -1)
{
switch (c)
{
case 'r':
recvs_n = parse_csv_arg(optarg, recvs_arg);
break;
+ case 'o':
+ dir_for_csv = optarg;
+ save_csv = true;
+ break;
default:
die("Invalid arguments\n\n%s", usage_msg);
}
margin_log_bdfs(down_ports[i], up_ports[i]);
printf(":\n\n");
margin_results_print_brief(results[i], results_n[i]);
+ if (save_csv)
+ margin_results_save_csv(results[i], results_n[i], dir_for_csv, up_ports[i]);
printf("\n");
}
}