aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog22
-rw-r--r--Makefile6
-rw-r--r--lib/access.c22
-rw-r--r--lib/caps.c2
-rw-r--r--lib/dump.c3
-rw-r--r--lib/filter.c6
-rw-r--r--lib/header.h12
-rw-r--r--lib/internal.h1
-rw-r--r--lib/libpci.ver5
-rw-r--r--lib/pci.h6
-rw-r--r--lib/sysfs.c14
-rw-r--r--lmr/lmr.h108
-rw-r--r--lmr/margin.c80
-rw-r--r--lmr/margin_args.c302
-rw-r--r--lmr/margin_hw.c77
-rw-r--r--lmr/margin_log.c25
-rw-r--r--lmr/margin_results.c322
-rw-r--r--ls-caps.c66
-rw-r--r--ls-ecaps.c48
-rw-r--r--lspci.c2
-rw-r--r--pci.ids165
-rw-r--r--pcilmr.c353
-rw-r--r--pcilmr.man138
23 files changed, 1179 insertions, 606 deletions
diff --git a/ChangeLog b/ChangeLog
index 6512888..d8d38eb 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,27 @@
+2024-05-30 Martin Mares <mj@ucw.cz>
+
+ * Released as 3.13.0.
+
+ * lspci decodes CXL 1.1 device link status information.
+ This requires a recent kernel which exports rcd_* atributes via
+ sysfs.
+
+ * Further development of the pcilmr (the link margining utility)
+
+ * Dump parsing supports 6-digit domain numbers.
+
+ * Bug fixes in PCIe link state reporting.
+
+ * Decode more fields in PCIe AER capability.
+
+ * Fixed build on Linux systems with musl libc.
+
+ * Updated pci.ids.
+
2024-04-05 Martin Mares <mj@ucw.cz>
+ * Released as 3.12.0.
+
* lspci decodes the IDE (Integrity & Data Encryption) and TEE-IO
extended capabilities.
diff --git a/Makefile b/Makefile
index be23593..51449ba 100644
--- a/Makefile
+++ b/Makefile
@@ -4,8 +4,8 @@
OPT=-O2
CFLAGS=$(OPT) -Wall -W -Wno-parentheses -Wstrict-prototypes -Wmissing-prototypes
-VERSION=3.12.0
-DATE=2024-04-05
+VERSION=3.13.0
+DATE=2024-05-30
# Host OS and release (override if you are cross-compiling)
HOST=
@@ -67,7 +67,7 @@ PCIINC_INS=lib/config.h lib/header.h lib/pci.h lib/types.h
UTILINC=pciutils.h bitops.h $(PCIINC)
-LMR=margin_hw.o margin.o margin_log.o margin_results.o
+LMR=margin_hw.o margin.o margin_log.o margin_results.o margin_args.o
LMROBJS=$(addprefix lmr/,$(LMR))
LMRINC=lmr/lmr.h $(UTILINC)
diff --git a/lib/access.c b/lib/access.c
index 7d66123..baaabcb 100644
--- a/lib/access.c
+++ b/lib/access.c
@@ -191,7 +191,7 @@ pci_reset_properties(struct pci_dev *d)
}
int
-pci_fill_info_v38(struct pci_dev *d, int flags)
+pci_fill_info_v313(struct pci_dev *d, int flags)
{
unsigned int uflags = flags;
if (uflags & PCI_FILL_RESCAN)
@@ -205,21 +205,23 @@ pci_fill_info_v38(struct pci_dev *d, int flags)
}
/* In version 3.1, pci_fill_info got new flags => versioned alias */
-/* In versions 3.2, 3.3, 3.4, 3.5 and 3.8, the same has happened */
-STATIC_ALIAS(int pci_fill_info(struct pci_dev *d, int flags), pci_fill_info_v38(d, flags));
-DEFINE_ALIAS(int pci_fill_info_v30(struct pci_dev *d, int flags), pci_fill_info_v38);
-DEFINE_ALIAS(int pci_fill_info_v31(struct pci_dev *d, int flags), pci_fill_info_v38);
-DEFINE_ALIAS(int pci_fill_info_v32(struct pci_dev *d, int flags), pci_fill_info_v38);
-DEFINE_ALIAS(int pci_fill_info_v33(struct pci_dev *d, int flags), pci_fill_info_v38);
-DEFINE_ALIAS(int pci_fill_info_v34(struct pci_dev *d, int flags), pci_fill_info_v38);
-DEFINE_ALIAS(int pci_fill_info_v35(struct pci_dev *d, int flags), pci_fill_info_v38);
+/* In versions 3.2, 3.3, 3.4, 3.5, 3.8 and 3.12, the same has happened */
+STATIC_ALIAS(int pci_fill_info(struct pci_dev *d, int flags), pci_fill_info_v313(d, flags));
+DEFINE_ALIAS(int pci_fill_info_v30(struct pci_dev *d, int flags), pci_fill_info_v313);
+DEFINE_ALIAS(int pci_fill_info_v31(struct pci_dev *d, int flags), pci_fill_info_v313);
+DEFINE_ALIAS(int pci_fill_info_v32(struct pci_dev *d, int flags), pci_fill_info_v313);
+DEFINE_ALIAS(int pci_fill_info_v33(struct pci_dev *d, int flags), pci_fill_info_v313);
+DEFINE_ALIAS(int pci_fill_info_v34(struct pci_dev *d, int flags), pci_fill_info_v313);
+DEFINE_ALIAS(int pci_fill_info_v35(struct pci_dev *d, int flags), pci_fill_info_v313);
+DEFINE_ALIAS(int pci_fill_info_v38(struct pci_dev *d, int flags), pci_fill_info_v313);
SYMBOL_VERSION(pci_fill_info_v30, pci_fill_info@LIBPCI_3.0);
SYMBOL_VERSION(pci_fill_info_v31, pci_fill_info@LIBPCI_3.1);
SYMBOL_VERSION(pci_fill_info_v32, pci_fill_info@LIBPCI_3.2);
SYMBOL_VERSION(pci_fill_info_v33, pci_fill_info@LIBPCI_3.3);
SYMBOL_VERSION(pci_fill_info_v34, pci_fill_info@LIBPCI_3.4);
SYMBOL_VERSION(pci_fill_info_v35, pci_fill_info@LIBPCI_3.5);
-SYMBOL_VERSION(pci_fill_info_v38, pci_fill_info@@LIBPCI_3.8);
+SYMBOL_VERSION(pci_fill_info_v38, pci_fill_info@LIBPCI_3.8);
+SYMBOL_VERSION(pci_fill_info_v313, pci_fill_info@@LIBPCI_3.13);
void
pci_setup_cache(struct pci_dev *d, byte *cache, int len)
diff --git a/lib/caps.c b/lib/caps.c
index cf1df5d..6b39c10 100644
--- a/lib/caps.c
+++ b/lib/caps.c
@@ -130,7 +130,7 @@ pci_find_cap_nr(struct pci_dev *d, unsigned int id, unsigned int type,
unsigned int target = (cap_number ? *cap_number : 0);
unsigned int index = 0;
- pci_fill_info_v38(d, ((type == PCI_CAP_NORMAL) ? PCI_FILL_CAPS : PCI_FILL_EXT_CAPS));
+ pci_fill_info_v313(d, ((type == PCI_CAP_NORMAL) ? PCI_FILL_CAPS : PCI_FILL_EXT_CAPS));
for (c=d->first_cap; c; c=c->next)
{
diff --git a/lib/dump.c b/lib/dump.c
index 5f4ba2e..00ebc9e 100644
--- a/lib/dump.c
+++ b/lib/dump.c
@@ -83,7 +83,8 @@ dump_init(struct pci_access *a)
mn = 0;
if (dump_validate(buf, "##:##.# ") && sscanf(buf, "%x:%x.%d", &bn, &dn, &fn) == 3 ||
dump_validate(buf, "####:##:##.# ") && sscanf(buf, "%x:%x:%x.%d", &mn, &bn, &dn, &fn) == 4 ||
- dump_validate(buf, "#####:##:##.# ") && sscanf(buf, "%x:%x:%x.%d", &mn, &bn, &dn, &fn) == 4)
+ dump_validate(buf, "#####:##:##.# ") && sscanf(buf, "%x:%x:%x.%d", &mn, &bn, &dn, &fn) == 4 ||
+ dump_validate(buf, "######:##:##.# ") && sscanf(buf, "%x:%x:%x.%d", &mn, &bn, &dn, &fn) == 4)
{
dev = pci_get_dev(a, mn, bn, dn, fn);
dump_alloc_data(dev, 256);
diff --git a/lib/filter.c b/lib/filter.c
index 0301f49..4221f57 100644
--- a/lib/filter.c
+++ b/lib/filter.c
@@ -204,20 +204,20 @@ pci_filter_match_v38(struct pci_filter *f, struct pci_dev *d)
return 0;
if (f->device >= 0 || f->vendor >= 0)
{
- pci_fill_info_v38(d, PCI_FILL_IDENT);
+ pci_fill_info_v313(d, PCI_FILL_IDENT);
if ((f->device >= 0 && f->device != d->device_id) ||
(f->vendor >= 0 && f->vendor != d->vendor_id))
return 0;
}
if (f->device_class >= 0)
{
- pci_fill_info_v38(d, PCI_FILL_CLASS);
+ pci_fill_info_v313(d, PCI_FILL_CLASS);
if ((f->device_class ^ d->device_class) & f->device_class_mask)
return 0;
}
if (f->prog_if >= 0)
{
- pci_fill_info_v38(d, PCI_FILL_CLASS_EXT);
+ pci_fill_info_v313(d, PCI_FILL_CLASS_EXT);
if (f->prog_if != d->prog_if)
return 0;
}
diff --git a/lib/header.h b/lib/header.h
index 031912f..0827ac0 100644
--- a/lib/header.h
+++ b/lib/header.h
@@ -993,6 +993,16 @@
#define PCI_ERR_UNC_ECRC 0x00080000 /* ECRC Error Status */
#define PCI_ERR_UNC_UNSUP 0x00100000 /* Unsupported Request */
#define PCI_ERR_UNC_ACS_VIOL 0x00200000 /* ACS Violation */
+#define PCI_ERR_UNC_INTERNAL 0x00400000 /* Uncorrectable Internal Error */
+#define PCI_ERR_UNC_MC_BLOCKED_TLP 0x00800000 /* MC Blocked TLP */
+#define PCI_ERR_UNC_ATOMICOP_EGRESS_BLOCKED 0x01000000 /* AtomicOp Egress Blocked */
+#define PCI_ERR_UNC_TLP_PREFIX_BLOCKED 0x02000000 /* TLP Prefix Blocked Error */
+#define PCI_ERR_UNC_POISONED_TLP_EGRESS 0x04000000 /* Poisoned TLP Egress Blocked */
+#define PCI_ERR_UNC_DMWR_REQ_EGRESS_BLOCKED 0x08000000 /* DMWr Request Egress Blocked */
+#define PCI_ERR_UNC_IDE_CHECK 0x10000000 /* IDE Check Failed */
+#define PCI_ERR_UNC_MISR_IDE_TLP 0x20000000 /* Misrouted IDE TLP */
+#define PCI_ERR_UNC_PCRC_CHECK 0x40000000 /* PCRC Check Failed */
+#define PCI_ERR_UNC_TLP_XLAT_EGRESS_BLOCKED 0x80000000 /* TLP Translation Egress Blocked */
#define PCI_ERR_UNCOR_MASK 8 /* Uncorrectable Error Mask */
/* Same bits as above */
#define PCI_ERR_UNCOR_SEVER 12 /* Uncorrectable Error Severity */
@@ -1004,6 +1014,8 @@
#define PCI_ERR_COR_REP_ROLL 0x00000100 /* REPLAY_NUM Rollover */
#define PCI_ERR_COR_REP_TIMER 0x00001000 /* Replay Timer Timeout */
#define PCI_ERR_COR_REP_ANFE 0x00002000 /* Advisory Non-Fatal Error */
+#define PCI_ERR_COR_INTERNAL 0x00004000 /* Corrected Internal Error */
+#define PCI_ERR_COR_HDRLOG_OVER 0x00008000 /* Header Log Overflow */
#define PCI_ERR_COR_MASK 20 /* Correctable Error Mask */
/* Same bits as above */
#define PCI_ERR_CAP 24 /* Advanced Error Capabilities */
diff --git a/lib/internal.h b/lib/internal.h
index 68e9fa0..996b80d 100644
--- a/lib/internal.h
+++ b/lib/internal.h
@@ -104,6 +104,7 @@ int pci_fill_info_v33(struct pci_dev *, int flags) VERSIONED_ABI;
int pci_fill_info_v34(struct pci_dev *, int flags) VERSIONED_ABI;
int pci_fill_info_v35(struct pci_dev *, int flags) VERSIONED_ABI;
int pci_fill_info_v38(struct pci_dev *, int flags) VERSIONED_ABI;
+int pci_fill_info_v313(struct pci_dev *, int flags) VERSIONED_ABI;
static inline int want_fill(struct pci_dev *d, unsigned want_fields, unsigned int try_fields)
{
diff --git a/lib/libpci.ver b/lib/libpci.ver
index 33ee024..dbcc876 100644
--- a/lib/libpci.ver
+++ b/lib/libpci.ver
@@ -98,3 +98,8 @@ LIBPCI_3.8 {
pci_filter_parse_id;
pci_filter_parse_slot;
};
+
+LIBPCI_3.13 {
+ global:
+ pci_fill_info;
+};
diff --git a/lib/pci.h b/lib/pci.h
index 4b5f6c1..71dae3a 100644
--- a/lib/pci.h
+++ b/lib/pci.h
@@ -18,7 +18,7 @@
#include "header.h"
#include "types.h"
-#define PCI_LIB_VERSION 0x030c00
+#define PCI_LIB_VERSION 0x030d00
#ifndef PCI_ABI
#define PCI_ABI
@@ -159,6 +159,9 @@ struct pci_dev {
u16 subsys_vendor_id, subsys_id; /* Subsystem vendor id and subsystem id */
struct pci_dev *parent; /* Parent device, does not have to be always accessible */
int no_config_access; /* No access to config space for this device */
+ u32 rcd_link_cap; /* Link Capabilities register for Restricted CXL Devices */
+ u16 rcd_link_status; /* Link Status register for RCD */
+ u16 rcd_link_ctrl; /* Link Control register for RCD */
/* Fields used internally */
struct pci_access *access;
@@ -231,6 +234,7 @@ char *pci_get_string_property(struct pci_dev *d, u32 prop) PCI_ABI;
#define PCI_FILL_SUBSYS 0x00040000 /* subsys_vendor_id and subsys_id */
#define PCI_FILL_PARENT 0x00080000
#define PCI_FILL_DRIVER 0x00100000 /* OS driver currently in use (string property) */
+#define PCI_FILL_RCD_LNK 0x00200000 /* CXL RCD Link status properties (rcd_*) */
void pci_setup_cache(struct pci_dev *, u8 *cache, int len) PCI_ABI;
diff --git a/lib/sysfs.c b/lib/sysfs.c
index 0e763dc..7dedc0b 100644
--- a/lib/sysfs.c
+++ b/lib/sysfs.c
@@ -2,7 +2,7 @@
* The PCI Library -- Configuration Access via /sys/bus/pci
*
* Copyright (c) 2003 Matthew Wilcox <matthew@wil.cx>
- * Copyright (c) 1997--2023 Martin Mares <mj@ucw.cz>
+ * Copyright (c) 1997--2024 Martin Mares <mj@ucw.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL v2+.
*
@@ -19,6 +19,7 @@
#include <errno.h>
#include <dirent.h>
#include <fcntl.h>
+#include <libgen.h>
#include <sys/types.h>
#include "internal.h"
@@ -483,6 +484,17 @@ sysfs_fill_info(struct pci_dev *d, unsigned int flags)
clear_fill(d, PCI_FILL_DRIVER);
}
+ if (want_fill(d, flags, PCI_FILL_RCD_LNK))
+ {
+ char buf[OBJBUFSIZE];
+ if (sysfs_get_string(d, "rcd_link_cap", buf, 0))
+ d->rcd_link_cap = strtoul(buf, NULL, 16);
+ if (sysfs_get_string(d, "rcd_link_ctrl", buf, 0))
+ d->rcd_link_ctrl = strtoul(buf, NULL, 16);
+ if (sysfs_get_string(d, "rcd_link_status", buf, 0))
+ d->rcd_link_status = strtoul(buf, NULL, 16);
+ }
+
pci_generic_fill_info(d, flags);
}
diff --git a/lmr/lmr.h b/lmr/lmr.h
index 7375c33..98df17a 100644
--- a/lmr/lmr.h
+++ b/lmr/lmr.h
@@ -1,7 +1,7 @@
/*
* The PCI Utilities -- Margining utility main header
*
- * Copyright (c) 2023 KNS Group LLC (YADRO)
+ * Copyright (c) 2023-2024 KNS Group LLC (YADRO)
*
* Can be freely distributed and used under the terms of the GNU GPL v2+.
*
@@ -15,19 +15,17 @@
#include "pciutils.h"
-#define MARGIN_STEP_MS 1000
-
-#define MARGIN_TIM_MIN 20
-#define MARGIN_TIM_RECOMMEND 30
-#define MARGIN_VOLT_MIN 50
-
enum margin_hw { MARGIN_HW_DEFAULT, MARGIN_ICE_LAKE_RC };
+// in ps
+static const double margin_ui[] = { 62.5, 31.25 };
+
/* PCI Device wrapper for margining functions */
struct margin_dev {
struct pci_dev *dev;
int lmr_cap_addr;
- u8 width;
+ u8 neg_width;
+ u8 max_width;
u8 retimers_n;
u8 link_speed;
@@ -39,11 +37,6 @@ struct margin_dev {
bool hawd; // Hardware Autonomous Width Disable
};
-struct margin_link {
- struct margin_dev down_port;
- struct margin_dev up_port;
-};
-
/* Specification Revision 5.0 Table 8-11 */
struct margin_params {
bool ind_error_sampler;
@@ -95,7 +88,7 @@ enum margin_test_status {
/* All lanes Receiver results */
struct margin_results {
- u8 recvn; // Receiver Number
+ u8 recvn; // Receiver Number; from 1 to 6
struct margin_params params;
bool lane_reversal;
u8 link_speed;
@@ -104,7 +97,7 @@ struct margin_results {
/* Used to convert steps to physical quantity.
Calculated from MaxOffset and NumSteps */
- double tim_coef;
+ double tim_coef; // from steps to % UI
double volt_coef;
bool tim_off_reported;
@@ -115,31 +108,56 @@ struct margin_results {
};
/* pcilmr arguments */
-struct margin_args {
+
+// Common args
+struct margin_com_args {
+ u8 error_limit; // [0; 63]
+ bool run_margin; // Or print params only
+ u8 verbosity; // 0 - basic;
+ // 1 - add info about remaining time and lanes in progress during margining
+ u64 steps_utility; // For ETA logging
+ bool save_csv;
+ char *dir_for_csv;
+ u8 dwell_time;
+};
+
+struct margin_recv_args {
+ // Grading options
+ struct {
+ bool valid;
+ double criteria; // in ps/mV
+ bool one_side_is_whole;
+ } t, v;
+};
+
+struct margin_link_args {
+ struct margin_com_args *common;
u8 steps_t; // 0 == use NumTimingSteps
u8 steps_v; // 0 == use NumVoltageSteps
u8 parallel_lanes; // [1; MaxLanes + 1]
- u8 error_limit; // [0; 63]
u8 recvs[6]; // Receivers Numbers
u8 recvs_n; // 0 == margin all available receivers
- u8 lanes[32]; // Lanes to Margin
- u8 lanes_n; // 0 == margin all available lanes
- bool run_margin; // Or print params only
- u8 verbosity; // 0 - basic;
- // 1 - add info about remaining time and lanes in progress during margining
+ struct margin_recv_args recv_args[6];
+ u8 lanes[32]; // Lanes to Margin
+ u8 lanes_n; // 0 == margin all available lanes
+};
- u64 *steps_utility; // For ETA logging
+struct margin_link {
+ struct margin_dev down_port;
+ struct margin_dev up_port;
+ struct margin_link_args args;
};
/* Receiver structure */
struct margin_recv {
struct margin_dev *dev;
- u8 recvn; // Receiver Number
+ u8 recvn; // Receiver Number; from 1 to 6
bool lane_reversal;
struct margin_params *params;
u8 parallel_lanes;
u8 error_limit;
+ u8 dwell_time;
};
struct margin_lanes_data {
@@ -159,8 +177,23 @@ struct margin_lanes_data {
u8 verbosity;
};
+/* margin_args */
+
+enum margin_mode { MARGIN, FULL, SCAN };
+
+extern const char *usage;
+
+struct margin_link *margin_parse_util_args(struct pci_access *pacc, int argc, char **argv,
+ enum margin_mode mode, u8 *links_n);
+
/* margin_hw */
+bool margin_port_is_down(struct pci_dev *dev);
+
+/* Results through down/up ports */
+bool margin_find_pair(struct pci_access *pacc, struct pci_dev *dev, struct pci_dev **down_port,
+ struct pci_dev **up_port);
+
/* Verify that devices form the link with 16 GT/s or 32 GT/s data rate */
bool margin_verify_link(struct pci_dev *down_port, struct pci_dev *up_port);
@@ -183,12 +216,11 @@ void margin_restore_link(struct margin_link *link);
bool margin_read_params(struct pci_access *pacc, struct pci_dev *dev, u8 recvn,
struct margin_params *params);
-enum margin_test_status margin_process_args(struct margin_dev *dev, struct margin_args *args);
+enum margin_test_status margin_process_args(struct margin_link *link);
-/* Awaits that args are prepared through process_args.
+/* Awaits that links are prepared through process_args.
Returns number of margined Receivers through recvs_n */
-struct margin_results *margin_test_link(struct margin_link *link, struct margin_args *args,
- u8 *recvs_n);
+struct margin_results *margin_test_link(struct margin_link *link, u8 *recvs_n);
void margin_free_results(struct margin_results *results, u8 results_n);
@@ -201,8 +233,9 @@ void margin_log(char *format, ...);
/* b:d.f -> b:d.f */
void margin_log_bdfs(struct pci_dev *down_port, struct pci_dev *up_port);
+void margin_gen_bdfs(struct pci_dev *down_port, struct pci_dev *up_port, char *dest, size_t maxlen);
-/* Print Link header (bdfs, width, speed) */
+/* Print Link header (bdfs, neg_width, speed) */
void margin_log_link(struct margin_link *link);
void margin_log_params(struct margin_params *params);
@@ -220,9 +253,20 @@ void margin_log_hw_quirks(struct margin_recv *recv);
/* margin_results */
-void margin_results_print_brief(struct margin_results *results, u8 recvs_n);
+// Min values are taken from PCIe Base Spec Rev. 5.0 Section 8.4.2.
+// Rec values are based on PCIe Arch PHY Test Spec Rev 5.0
+// (Transmitter Electrical Compliance)
+
+// values in ps
+static const double margin_ew_min[] = { 18.75, 9.375 };
+static const double margin_ew_rec[] = { 23.75, 10.1565 };
+
+static const double margin_eh_min[] = { 15, 15 };
+static const double margin_eh_rec[] = { 21, 19.75 };
+
+void margin_results_print_brief(struct margin_results *results, u8 recvs_n,
+ struct margin_link_args *args);
-void margin_results_save_csv(struct margin_results *results, u8 recvs_n, char *dir,
- struct pci_dev *up_port);
+void margin_results_save_csv(struct margin_results *results, u8 recvs_n, struct margin_link *link);
#endif
diff --git a/lmr/margin.c b/lmr/margin.c
index a8c6571..4d68031 100644
--- a/lmr/margin.c
+++ b/lmr/margin.c
@@ -1,7 +1,7 @@
/*
* The PCI Utilities -- Obtain the margin information of the Link
*
- * Copyright (c) 2023 KNS Group LLC (YADRO)
+ * Copyright (c) 2023-2024 KNS Group LLC (YADRO)
*
* Can be freely distributed and used under the terms of the GNU GPL v2+.
*
@@ -143,13 +143,17 @@ margin_report_cmd(struct margin_dev *dev, u8 lane, margin_cmd cmd, margin_cmd *r
}
static void
-margin_apply_hw_quirks(struct margin_recv *recv)
+margin_apply_hw_quirks(struct margin_recv *recv, struct margin_link_args *args)
{
switch (recv->dev->hw)
{
case MARGIN_ICE_LAKE_RC:
if (recv->recvn == 1)
- recv->params->volt_offset = 12;
+ {
+ recv->params->volt_offset = 12;
+ args->recv_args[recv->recvn - 1].t.one_side_is_whole = true;
+ args->recv_args[recv->recvn - 1].t.valid = true;
+ }
break;
default:
break;
@@ -161,7 +165,7 @@ read_params_internal(struct margin_dev *dev, u8 recvn, bool lane_reversal,
struct margin_params *params)
{
margin_cmd resp;
- u8 lane = lane_reversal ? dev->width - 1 : 0;
+ u8 lane = lane_reversal ? dev->max_width - 1 : 0;
margin_set_cmd(dev, lane, NO_COMMAND);
bool status = margin_report_cmd(dev, lane, REPORT_CAPS(recvn), &resp);
if (status)
@@ -260,7 +264,7 @@ margin_test_lanes(struct margin_lanes_data arg)
pci_write_word(arg.recv->dev->dev, ctrl_addr, step_cmd);
}
}
- msleep(MARGIN_STEP_MS);
+ msleep(arg.recv->dwell_time * 1000);
for (int i = 0; i < arg.lanes_n; i++)
{
@@ -300,7 +304,7 @@ margin_test_lanes(struct margin_lanes_data arg)
/* Awaits that Receiver is prepared through prep_dev function */
static bool
-margin_test_receiver(struct margin_dev *dev, u8 recvn, struct margin_args *args,
+margin_test_receiver(struct margin_dev *dev, u8 recvn, struct margin_link_args *args,
struct margin_results *results)
{
u8 *lanes_to_margin = args->lanes;
@@ -312,7 +316,8 @@ margin_test_receiver(struct margin_dev *dev, u8 recvn, struct margin_args *args,
.lane_reversal = false,
.params = &params,
.parallel_lanes = args->parallel_lanes ? args->parallel_lanes : 1,
- .error_limit = args->error_limit };
+ .error_limit = args->common->error_limit,
+ .dwell_time = args->common->dwell_time };
results->recvn = recvn;
results->lanes_n = lanes_n;
@@ -340,7 +345,7 @@ margin_test_receiver(struct margin_dev *dev, u8 recvn, struct margin_args *args,
if (recv.parallel_lanes > params.max_lanes + 1)
recv.parallel_lanes = params.max_lanes + 1;
- margin_apply_hw_quirks(&recv);
+ margin_apply_hw_quirks(&recv, args);
margin_log_hw_quirks(&recv);
results->tim_off_reported = params.timing_offset != 0;
@@ -361,15 +366,16 @@ margin_test_receiver(struct margin_dev *dev, u8 recvn, struct margin_args *args,
for (int i = 0; i < lanes_n; i++)
{
results->lanes[i].lane
- = recv.lane_reversal ? dev->width - lanes_to_margin[i] - 1 : lanes_to_margin[i];
+ = recv.lane_reversal ? dev->max_width - lanes_to_margin[i] - 1 : lanes_to_margin[i];
}
- if (args->run_margin)
+ if (args->common->run_margin)
{
- if (args->verbosity > 0)
+ if (args->common->verbosity > 0)
margin_log("\n");
- struct margin_lanes_data lanes_data
- = { .recv = &recv, .verbosity = args->verbosity, .steps_utility = args->steps_utility };
+ struct margin_lanes_data lanes_data = { .recv = &recv,
+ .verbosity = args->common->verbosity,
+ .steps_utility = &args->common->steps_utility };
enum margin_dir dir[] = { TIM_LEFT, TIM_RIGHT, VOLT_UP, VOLT_DOWN };
@@ -399,15 +405,15 @@ margin_test_receiver(struct margin_dev *dev, u8 recvn, struct margin_args *args,
lanes_data.ind = timing ? params.ind_left_right_tim : params.ind_up_down_volt;
lanes_data.dir = dir[i];
lanes_data.steps_lane_total = timing ? steps_t : steps_v;
- if (*args->steps_utility >= lanes_data.steps_lane_total)
- *args->steps_utility -= lanes_data.steps_lane_total;
+ if (args->common->steps_utility >= lanes_data.steps_lane_total)
+ args->common->steps_utility -= lanes_data.steps_lane_total;
else
- *args->steps_utility = 0;
+ args->common->steps_utility = 0;
margin_test_lanes(lanes_data);
}
lanes_done += use_lanes;
}
- if (args->verbosity > 0)
+ if (args->common->verbosity > 0)
margin_log("\n");
if (recv.lane_reversal)
{
@@ -426,13 +432,8 @@ margin_read_params(struct pci_access *pacc, struct pci_dev *dev, u8 recvn,
struct pci_cap *cap = pci_find_cap(dev, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
if (!cap)
return false;
- u8 dev_dir = GET_REG_MASK(pci_read_word(dev, cap->addr + PCI_EXP_FLAGS), PCI_EXP_FLAGS_TYPE);
- bool dev_down;
- if (dev_dir == PCI_EXP_TYPE_ROOT_PORT || dev_dir == PCI_EXP_TYPE_DOWNSTREAM)
- dev_down = true;
- else
- dev_down = false;
+ bool dev_down = margin_port_is_down(dev);
if (recvn == 0)
{
@@ -453,25 +454,7 @@ margin_read_params(struct pci_access *pacc, struct pci_dev *dev, u8 recvn,
struct pci_dev *up = NULL;
struct margin_link link;
- for (struct pci_dev *p = pacc->devices; p; p = p->next)
- {
- if (dev_down && pci_read_byte(dev, PCI_SECONDARY_BUS) == p->bus && dev->domain == p->domain
- && p->func == 0)
- {
- down = dev;
- up = p;
- break;
- }
- else if (!dev_down && pci_read_byte(p, PCI_SECONDARY_BUS) == dev->bus
- && dev->domain == p->domain)
- {
- down = p;
- up = dev;
- break;
- }
- }
-
- if (!down)
+ if (!margin_find_pair(pacc, dev, &down, &up))
return false;
if (!margin_fill_link(down, up, &link))
@@ -499,8 +482,11 @@ margin_read_params(struct pci_access *pacc, struct pci_dev *dev, u8 recvn,
}
enum margin_test_status
-margin_process_args(struct margin_dev *dev, struct margin_args *args)
+margin_process_args(struct margin_link *link)
{
+ struct margin_dev *dev = &link->down_port;
+ struct margin_link_args *args = &link->args;
+
u8 receivers_n = 2 + 2 * dev->retimers_n;
if (!args->recvs_n)
@@ -524,7 +510,7 @@ margin_process_args(struct margin_dev *dev, struct margin_args *args)
if (!args->lanes_n)
{
- args->lanes_n = dev->width;
+ args->lanes_n = dev->neg_width;
for (int i = 0; i < args->lanes_n; i++)
args->lanes[i] = i;
}
@@ -532,7 +518,7 @@ margin_process_args(struct margin_dev *dev, struct margin_args *args)
{
for (int i = 0; i < args->lanes_n; i++)
{
- if (args->lanes[i] >= dev->width)
+ if (args->lanes[i] >= dev->neg_width)
{
return MARGIN_TEST_ARGS_LANES;
}
@@ -543,8 +529,10 @@ margin_process_args(struct margin_dev *dev, struct margin_args *args)
}
struct margin_results *
-margin_test_link(struct margin_link *link, struct margin_args *args, u8 *recvs_n)
+margin_test_link(struct margin_link *link, u8 *recvs_n)
{
+ struct margin_link_args *args = &link->args;
+
bool status = margin_prep_link(link);
u8 receivers_n = status ? args->recvs_n : 1;
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;
+}
diff --git a/lmr/margin_hw.c b/lmr/margin_hw.c
index fc427c8..c376549 100644
--- a/lmr/margin_hw.c
+++ b/lmr/margin_hw.c
@@ -1,13 +1,15 @@
/*
* The PCI Utilities -- Verify and prepare devices before margining
*
- * Copyright (c) 2023 KNS Group LLC (YADRO)
+ * Copyright (c) 2023-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 <memory.h>
+
#include "lmr.h"
static u16 special_hw[][4] =
@@ -32,6 +34,51 @@ detect_unique_hw(struct pci_dev *dev)
}
bool
+margin_port_is_down(struct pci_dev *dev)
+{
+ struct pci_cap *cap = pci_find_cap(dev, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
+ if (!cap)
+ return false;
+ u8 type = pci_read_byte(dev, PCI_HEADER_TYPE) & 0x7F;
+ u8 dir = GET_REG_MASK(pci_read_word(dev, cap->addr + PCI_EXP_FLAGS), PCI_EXP_FLAGS_TYPE);
+
+ if (type == PCI_HEADER_TYPE_BRIDGE
+ && (dir == PCI_EXP_TYPE_ROOT_PORT || dir == PCI_EXP_TYPE_DOWNSTREAM))
+ return true;
+ else
+ return false;
+}
+
+bool
+margin_find_pair(struct pci_access *pacc, struct pci_dev *dev, struct pci_dev **down_port,
+ struct pci_dev **up_port)
+{
+ struct pci_cap *cap = pci_find_cap(dev, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
+ if (!cap)
+ return false;
+ bool given_down = margin_port_is_down(dev);
+
+ for (struct pci_dev *p = pacc->devices; p; p = p->next)
+ {
+ if (given_down && pci_read_byte(dev, PCI_SECONDARY_BUS) == p->bus && dev->domain == p->domain
+ && p->func == 0)
+ {
+ *down_port = dev;
+ *up_port = p;
+ return true;
+ }
+ else if (!given_down && pci_read_byte(p, PCI_SECONDARY_BUS) == dev->bus
+ && dev->domain == p->domain)
+ {
+ *down_port = p;
+ *up_port = dev;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
margin_verify_link(struct pci_dev *down_port, struct pci_dev *up_port)
{
struct pci_cap *cap = pci_find_cap(down_port, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
@@ -42,16 +89,11 @@ margin_verify_link(struct pci_dev *down_port, struct pci_dev *up_port)
if ((pci_read_word(down_port, cap->addr + PCI_EXP_LNKSTA) & PCI_EXP_LNKSTA_SPEED) > 5)
return false;
- u8 down_type = pci_read_byte(down_port, PCI_HEADER_TYPE) & 0x7F;
u8 down_sec = pci_read_byte(down_port, PCI_SECONDARY_BUS);
- u8 down_dir
- = GET_REG_MASK(pci_read_word(down_port, cap->addr + PCI_EXP_FLAGS), PCI_EXP_FLAGS_TYPE);
// Verify that devices are linked, down_port is Root Port or Downstream Port of Switch,
// up_port is Function 0 of a Device
- if (!(down_sec == up_port->bus && down_type == PCI_HEADER_TYPE_BRIDGE
- && (down_dir == PCI_EXP_TYPE_ROOT_PORT || down_dir == PCI_EXP_TYPE_DOWNSTREAM)
- && up_port->func == 0))
+ if (!(down_sec == up_port->bus && margin_port_is_down(down_port) && up_port->func == 0))
return false;
struct pci_cap *pm = pci_find_cap(up_port, PCI_CAP_ID_PM, PCI_CAP_NORMAL);
@@ -70,21 +112,24 @@ static struct margin_dev
fill_dev_wrapper(struct pci_dev *dev)
{
struct pci_cap *cap = pci_find_cap(dev, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
- struct margin_dev res
- = { .dev = dev,
- .lmr_cap_addr = pci_find_cap(dev, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED)->addr,
- .width = GET_REG_MASK(pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA), PCI_EXP_LNKSTA_WIDTH),
- .retimers_n
- = (!!(pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA2) & PCI_EXP_LINKSTA2_RETIMER))
- + (!!(pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA2) & PCI_EXP_LINKSTA2_2RETIMERS)),
- .link_speed = (pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA) & PCI_EXP_LNKSTA_SPEED),
- .hw = detect_unique_hw(dev) };
+ struct margin_dev res = {
+ .dev = dev,
+ .lmr_cap_addr = pci_find_cap(dev, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED)->addr,
+ .neg_width = GET_REG_MASK(pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA), PCI_EXP_LNKSTA_WIDTH),
+ .max_width = GET_REG_MASK(pci_read_long(dev, cap->addr + PCI_EXP_LNKCAP), PCI_EXP_LNKCAP_WIDTH),
+ .retimers_n
+ = (!!(pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA2) & PCI_EXP_LINKSTA2_RETIMER))
+ + (!!(pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA2) & PCI_EXP_LINKSTA2_2RETIMERS)),
+ .link_speed = (pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA) & PCI_EXP_LNKSTA_SPEED),
+ .hw = detect_unique_hw(dev)
+ };
return res;
}
bool
margin_fill_link(struct pci_dev *down_port, struct pci_dev *up_port, struct margin_link *wrappers)
{
+ memset(wrappers, 0, sizeof(*wrappers));
if (!margin_verify_link(down_port, up_port))
return false;
wrappers->down_port = fill_dev_wrapper(down_port);
diff --git a/lmr/margin_log.c b/lmr/margin_log.c
index b3c4bd5..2cb01c8 100644
--- a/lmr/margin_log.c
+++ b/lmr/margin_log.c
@@ -1,7 +1,7 @@
/*
* The PCI Utilities -- Log margining process
*
- * Copyright (c) 2023 KNS Group LLC (YADRO)
+ * Copyright (c) 2023-2024 KNS Group LLC (YADRO)
*
* Can be freely distributed and used under the terms of the GNU GPL v2+.
*
@@ -38,11 +38,22 @@ margin_log_bdfs(struct pci_dev *down, struct pci_dev *up)
}
void
+margin_gen_bdfs(struct pci_dev *down, struct pci_dev *up, char *dest, size_t maxlen)
+{
+ if (margin_print_domain)
+ snprintf(dest, maxlen, "%x:%x:%x.%x -> %x:%x:%x.%x", down->domain, down->bus, down->dev,
+ down->func, up->domain, up->bus, up->dev, up->func);
+ else
+ snprintf(dest, maxlen, "%x:%x.%x -> %x:%x.%x", down->bus, down->dev, down->func, up->bus,
+ up->dev, up->func);
+}
+
+void
margin_log_link(struct margin_link *link)
{
margin_log("Link ");
margin_log_bdfs(link->down_port.dev, link->up_port.dev);
- margin_log("\nNegotiated Link Width: %d\n", link->down_port.width);
+ margin_log("\nNegotiated Link Width: %d\n", link->down_port.neg_width);
margin_log("Link Speed: %d.0 GT/s = Gen %d\n", (link->down_port.link_speed - 3) * 16,
link->down_port.link_speed);
margin_log("Available receivers: ");
@@ -77,7 +88,8 @@ void
margin_log_receiver(struct margin_recv *recv)
{
margin_log("\nError Count Limit = %d\n", recv->error_limit);
- margin_log("Parallel Lanes: %d\n\n", recv->parallel_lanes);
+ margin_log("Parallel Lanes: %d\n", recv->parallel_lanes);
+ margin_log("Margining dwell time: %d s\n\n", recv->dwell_time);
margin_log_params(recv->params);
@@ -132,8 +144,8 @@ margin_log_margining(struct margin_lanes_data arg)
}
margin_log("]");
- u64 lane_eta_s = (arg.steps_lane_total - arg.steps_lane_done) * MARGIN_STEP_MS / 1000;
- u64 total_eta_s = *arg.steps_utility * MARGIN_STEP_MS / 1000 + lane_eta_s;
+ u64 lane_eta_s = (arg.steps_lane_total - arg.steps_lane_done) * arg.recv->dwell_time;
+ u64 total_eta_s = *arg.steps_utility * arg.recv->dwell_time + lane_eta_s;
margin_log(" - ETA: %3ds Steps: %3d Total ETA: %3dm %2ds", lane_eta_s, arg.steps_lane_done,
total_eta_s / 60, total_eta_s % 60);
@@ -150,7 +162,8 @@ margin_log_hw_quirks(struct margin_recv *recv)
if (recv->recvn == 1)
margin_log("\nRx(A) is Intel Ice Lake RC port.\n"
"Applying next quirks for margining process:\n"
- " - Set MaxVoltageOffset to 12 (120 mV).\n");
+ " - Set MaxVoltageOffset to 12 (120 mV);\n"
+ " - Force the use of 'one side is the whole' grading mode.\n");
break;
default:
break;
diff --git a/lmr/margin_results.c b/lmr/margin_results.c
index 4d28f04..b0c5c26 100644
--- a/lmr/margin_results.c
+++ b/lmr/margin_results.c
@@ -1,7 +1,7 @@
/*
* The PCI Utilities -- Display/save margining results
*
- * Copyright (c) 2023 KNS Group LLC (YADRO)
+ * Copyright (c) 2023-2024 KNS Group LLC (YADRO)
*
* Can be freely distributed and used under the terms of the GNU GPL v2+.
*
@@ -16,25 +16,23 @@
#include "lmr.h"
enum lane_rating {
- BAD = 0,
- OKAY,
+ FAIL = 0,
+ PASS,
PERFECT,
- WEIRD,
INIT,
};
-static char *const grades[] = { "Bad", "Okay", "Perfect", "Weird" };
+static char *const grades[] = { "Fail", "Pass", "Perfect" };
static char *const sts_strings[] = { "NAK", "LIM", "THR" };
-static const double ui[] = { 62.5 / 100, 31.25 / 100 };
static enum lane_rating
rate_lane(double value, double min, double recommended, enum lane_rating cur_rate)
{
enum lane_rating res = PERFECT;
if (value < recommended)
- res = OKAY;
+ res = PASS;
if (value < min)
- res = BAD;
+ res = FAIL;
if (cur_rate == INIT)
return res;
if (res < cur_rate)
@@ -43,34 +41,9 @@ rate_lane(double value, double min, double recommended, enum lane_rating cur_rat
return cur_rate;
}
-static bool
-check_recv_weird(struct margin_results *results, double tim_min, double volt_min)
-{
- bool result = true;
-
- struct margin_res_lane *lane;
- for (int i = 0; i < results->lanes_n && result; i++)
- {
- lane = &(results->lanes[i]);
- if (lane->steps[TIM_LEFT] * results->tim_coef != tim_min)
- result = false;
- if (results->params.ind_left_right_tim
- && lane->steps[TIM_RIGHT] * results->tim_coef != tim_min)
- result = false;
- if (results->params.volt_support)
- {
- if (lane->steps[VOLT_UP] * results->volt_coef != volt_min)
- result = false;
- if (results->params.ind_up_down_volt
- && lane->steps[VOLT_DOWN] * results->volt_coef != volt_min)
- result = false;
- }
- }
- return result;
-}
-
void
-margin_results_print_brief(struct margin_results *results, u8 recvs_n)
+margin_results_print_brief(struct margin_results *results, u8 recvs_n,
+ struct margin_link_args *args)
{
struct margin_res_lane *lane;
struct margin_results *res;
@@ -80,6 +53,14 @@ margin_results_print_brief(struct margin_results *results, u8 recvs_n)
u8 link_speed;
+ struct margin_recv_args grade_args;
+ bool spec_ref_only;
+
+ double ew_min;
+ double ew_rec;
+ double eh_min;
+ double eh_rec;
+
char *no_test_msgs[] = { "",
"Margining Ready bit is Clear",
"Error during caps reading",
@@ -102,6 +83,67 @@ margin_results_print_brief(struct margin_results *results, u8 recvs_n)
continue;
}
+ spec_ref_only = true;
+ grade_args = args->recv_args[res->recvn - 1];
+ if (grade_args.t.criteria != 0)
+ {
+ spec_ref_only = false;
+ ew_min = grade_args.t.criteria;
+ ew_rec = grade_args.t.criteria;
+ }
+ else
+ {
+ ew_min = margin_ew_min[link_speed];
+ ew_rec = margin_ew_rec[link_speed];
+ }
+
+ if (grade_args.v.criteria != 0)
+ {
+ spec_ref_only = false;
+ eh_min = grade_args.v.criteria;
+ eh_rec = grade_args.v.criteria;
+ }
+ else
+ {
+ eh_min = margin_eh_min[link_speed];
+ eh_rec = margin_eh_rec[link_speed];
+ }
+
+ printf("Rx(%X) - Grading criteria:\n", 10 + res->recvn - 1);
+ if (spec_ref_only)
+ {
+ printf("\tUsing spec only:\n");
+ printf("\tEW: minimum - %.2f ps; recommended - %.2f ps\n", ew_min, ew_rec);
+ printf("\tEH: minimum - %.2f mV; recommended - %.2f mV\n\n", eh_min, eh_rec);
+ }
+ else
+ {
+ printf("\tEW: pass - %.2f ps\n", ew_min);
+ printf("\tEH: pass - %.2f mV\n\n", eh_min);
+ }
+
+ if (!params.ind_left_right_tim)
+ {
+ printf("Rx(%X) - EW: independent left/right timing margin is not supported:\n",
+ 10 + res->recvn - 1);
+ if (grade_args.t.one_side_is_whole)
+ printf("\tmanual setting - the entire margin across the eye "
+ "is what is reported by one side margining\n\n");
+ else
+ printf("\tdefault - calculating EW as double one side result\n\n");
+ }
+
+ if (params.volt_support && !params.ind_up_down_volt)
+ {
+ printf("Rx(%X) - EH: independent up and down voltage margining is not supported:\n",
+ 10 + res->recvn - 1);
+ if (grade_args.v.one_side_is_whole)
+ printf("\tmanual setting - the entire margin across the eye "
+ "is what is reported by one side margining\n\n");
+ else
+ printf("\tdefault - calculating EH as double one side result\n\n");
+ }
+
if (res->lane_reversal)
printf("Rx(%X) - Lane Reversal\n", 10 + res->recvn - 1);
@@ -118,51 +160,60 @@ margin_results_print_brief(struct margin_results *results, u8 recvs_n)
"reliable.\n\n",
10 + res->recvn - 1);
- if (check_recv_weird(res, MARGIN_TIM_MIN, MARGIN_VOLT_MIN))
- lane_rating = WEIRD;
- else
- lane_rating = INIT;
-
- for (u8 j = 0; j < res->lanes_n; j++)
+ for (int j = 0; j < res->lanes_n; j++)
{
+ if (spec_ref_only)
+ lane_rating = INIT;
+ else
+ lane_rating = PASS;
+
lane = &(res->lanes[j]);
- double left_ui = lane->steps[TIM_LEFT] * res->tim_coef;
- double right_ui = lane->steps[TIM_RIGHT] * res->tim_coef;
+ double left_ps = lane->steps[TIM_LEFT] * res->tim_coef / 100.0 * margin_ui[link_speed];
+ double right_ps = lane->steps[TIM_RIGHT] * res->tim_coef / 100.0 * margin_ui[link_speed];
double up_volt = lane->steps[VOLT_UP] * res->volt_coef;
double down_volt = lane->steps[VOLT_DOWN] * res->volt_coef;
- if (lane_rating != WEIRD)
+ double ew = left_ps;
+ if (params.ind_left_right_tim)
+ ew += right_ps;
+ else if (!grade_args.t.one_side_is_whole)
+ ew *= 2.0;
+
+ double eh = 0.0;
+ if (params.volt_support)
{
- lane_rating = rate_lane(left_ui, MARGIN_TIM_MIN, MARGIN_TIM_RECOMMEND, INIT);
- if (params.ind_left_right_tim)
- lane_rating
- = rate_lane(right_ui, MARGIN_TIM_MIN, MARGIN_TIM_RECOMMEND, lane_rating);
- if (params.volt_support)
- {
- lane_rating = rate_lane(up_volt, MARGIN_VOLT_MIN, MARGIN_VOLT_MIN, lane_rating);
- if (params.ind_up_down_volt)
- lane_rating
- = rate_lane(down_volt, MARGIN_VOLT_MIN, MARGIN_VOLT_MIN, lane_rating);
- }
+ eh += up_volt;
+ if (params.ind_up_down_volt)
+ eh += down_volt;
+ else if (!grade_args.v.one_side_is_whole)
+ eh *= 2.0;
}
- printf("Rx(%X) Lane %2d - %s\t", 10 + res->recvn - 1, lane->lane, grades[lane_rating]);
+ lane_rating = rate_lane(ew, ew_min, ew_rec, lane_rating);
+ if (params.volt_support)
+ lane_rating = rate_lane(eh, eh_min, eh_rec, lane_rating);
+
+ printf("Rx(%X) Lane %2d: %s\t (W %4.1f%% UI - %5.2fps", 10 + res->recvn - 1, lane->lane,
+ grades[lane_rating], ew / margin_ui[link_speed] * 100.0, ew);
+ if (params.volt_support)
+ printf(", H %5.1f mV", eh);
if (params.ind_left_right_tim)
- printf("L %4.1f%% UI - %5.2fps - %2dst %s, R %4.1f%% UI - %5.2fps - %2dst %s", left_ui,
- left_ui * ui[link_speed], lane->steps[TIM_LEFT],
- sts_strings[lane->statuses[TIM_LEFT]], right_ui, right_ui * ui[link_speed],
- lane->steps[TIM_RIGHT], sts_strings[lane->statuses[TIM_RIGHT]]);
+ printf(") (L %4.1f%% UI - %5.2fps - %2dst %s) (R %4.1f%% UI - %5.2fps - %2dst %s)",
+ left_ps / margin_ui[link_speed] * 100.0, left_ps, lane->steps[TIM_LEFT],
+ sts_strings[lane->statuses[TIM_LEFT]], right_ps / margin_ui[link_speed] * 100.0,
+ right_ps, lane->steps[TIM_RIGHT], sts_strings[lane->statuses[TIM_RIGHT]]);
else
- printf("T %4.1f%% UI - %5.2fps - %2dst %s", left_ui, left_ui * ui[link_speed],
- lane->steps[TIM_LEFT], sts_strings[lane->statuses[TIM_LEFT]]);
+ printf(") (T %4.1f%% UI - %5.2fps - %2dst %s)",
+ left_ps / margin_ui[link_speed] * 100.0, left_ps, lane->steps[TIM_LEFT],
+ sts_strings[lane->statuses[TIM_LEFT]]);
if (params.volt_support)
{
if (params.ind_up_down_volt)
- printf(", U %5.1f mV - %3dst %s, D %5.1f mV - %3dst %s", up_volt,
+ printf(" (U %5.1f mV - %3dst %s) (D %5.1f mV - %3dst %s)", up_volt,
lane->steps[VOLT_UP], sts_strings[lane->statuses[VOLT_UP]], down_volt,
lane->steps[VOLT_DOWN], sts_strings[lane->statuses[VOLT_DOWN]]);
else
- printf(", V %5.1f mV - %3dst %s", up_volt, lane->steps[VOLT_UP],
+ printf(" (V %5.1f mV - %3dst %s)", up_volt, lane->steps[VOLT_UP],
sts_strings[lane->statuses[VOLT_UP]]);
}
printf("\n");
@@ -172,13 +223,13 @@ margin_results_print_brief(struct margin_results *results, u8 recvs_n)
}
void
-margin_results_save_csv(struct margin_results *results, u8 recvs_n, char *dir,
- struct pci_dev *up_port)
+margin_results_save_csv(struct margin_results *results, u8 recvs_n, struct margin_link *link)
{
char timestamp[64];
time_t tim = time(NULL);
strftime(timestamp, sizeof(timestamp), "%Y-%m-%dT%H:%M:%S", gmtime(&tim));
+ char *dir = link->args.common->dir_for_csv;
size_t pathlen = strlen(dir) + 128;
char *path = xmalloc(pathlen);
FILE *csv;
@@ -190,6 +241,16 @@ margin_results_save_csv(struct margin_results *results, u8 recvs_n, char *dir,
enum lane_rating lane_rating;
u8 link_speed;
+ struct margin_recv_args grade_args;
+ bool spec_ref_only;
+
+ double ew_min;
+ double ew_rec;
+ double eh_min;
+ double eh_rec;
+
+ struct pci_dev *port;
+
for (int i = 0; i < recvs_n; i++)
{
res = &(results[i]);
@@ -198,81 +259,120 @@ margin_results_save_csv(struct margin_results *results, u8 recvs_n, char *dir,
if (res->test_status != MARGIN_TEST_OK)
continue;
+
+ port = res->recvn == 6 ? link->up_port.dev : link->down_port.dev;
snprintf(path, pathlen, "%s/lmr_%0*x.%02x.%02x.%x_Rx%X_%s.csv", dir,
- up_port->domain_16 == 0xffff ? 8 : 4, up_port->domain, up_port->bus, up_port->dev,
- up_port->func, 10 + res->recvn - 1, timestamp);
+ port->domain_16 == 0xffff ? 8 : 4, port->domain, port->bus, port->dev, port->func,
+ 10 + res->recvn - 1, timestamp);
csv = fopen(path, "w");
if (!csv)
die("Error while saving %s\n", path);
- fprintf(csv, "Lane,Lane Status,Left %% UI,Left ps,Left Steps,Left Status,"
- "Right %% UI,Right ps,Right Steps,Right Status,"
- "Time %% UI,Time ps,Time Steps,Time Status,"
- "Up mV,Up Steps,Up Status,Down mV,Down Steps,Down Status,"
- "Voltage mV,Voltage Steps,Voltage Status\n");
+ fprintf(csv, "Lane,EW Min,EW Rec,EW,EH Min,EH Rec,EH,Lane Status,Left %% UI,Left "
+ "ps,Left Steps,Left Status,Right %% UI,Right ps,Right Steps,Right Status,Up "
+ "mV,Up Steps,Up Status,Down mV,Down Steps,Down Status\n");
- if (check_recv_weird(res, MARGIN_TIM_MIN, MARGIN_VOLT_MIN))
- lane_rating = WEIRD;
+ spec_ref_only = true;
+ grade_args = link->args.recv_args[res->recvn - 1];
+ if (grade_args.t.criteria != 0)
+ {
+ spec_ref_only = false;
+ ew_min = grade_args.t.criteria;
+ ew_rec = grade_args.t.criteria;
+ }
+ else
+ {
+ ew_min = margin_ew_min[link_speed];
+ ew_rec = margin_ew_rec[link_speed];
+ }
+ if (grade_args.v.criteria != 0)
+ {
+ spec_ref_only = false;
+ eh_min = grade_args.v.criteria;
+ eh_rec = grade_args.v.criteria;
+ }
else
- lane_rating = INIT;
+ {
+ eh_min = margin_eh_min[link_speed];
+ eh_rec = margin_eh_rec[link_speed];
+ }
for (int j = 0; j < res->lanes_n; j++)
{
+ if (spec_ref_only)
+ lane_rating = INIT;
+ else
+ lane_rating = PASS;
+
lane = &(res->lanes[j]);
- double left_ui = lane->steps[TIM_LEFT] * res->tim_coef;
- double right_ui = lane->steps[TIM_RIGHT] * res->tim_coef;
+ double left_ps = lane->steps[TIM_LEFT] * res->tim_coef / 100.0 * margin_ui[link_speed];
+ double right_ps = lane->steps[TIM_RIGHT] * res->tim_coef / 100.0 * margin_ui[link_speed];
double up_volt = lane->steps[VOLT_UP] * res->volt_coef;
double down_volt = lane->steps[VOLT_DOWN] * res->volt_coef;
- if (lane_rating != WEIRD)
+ double ew = left_ps;
+ if (params.ind_left_right_tim)
+ ew += right_ps;
+ else if (!grade_args.t.one_side_is_whole)
+ ew *= 2.0;
+
+ double eh = 0.0;
+ if (params.volt_support)
{
- lane_rating = rate_lane(left_ui, MARGIN_TIM_MIN, MARGIN_TIM_RECOMMEND, INIT);
- if (params.ind_left_right_tim)
- lane_rating
- = rate_lane(right_ui, MARGIN_TIM_MIN, MARGIN_TIM_RECOMMEND, lane_rating);
- if (params.volt_support)
- {
- lane_rating = rate_lane(up_volt, MARGIN_VOLT_MIN, MARGIN_VOLT_MIN, lane_rating);
- if (params.ind_up_down_volt)
- lane_rating
- = rate_lane(down_volt, MARGIN_VOLT_MIN, MARGIN_VOLT_MIN, lane_rating);
- }
+ eh += up_volt;
+ if (params.ind_up_down_volt)
+ eh += down_volt;
+ else if (!grade_args.v.one_side_is_whole)
+ eh *= 2.0;
}
- fprintf(csv, "%d,%s,", lane->lane, grades[lane_rating]);
- if (params.ind_left_right_tim)
+ lane_rating = rate_lane(ew, ew_min, ew_rec, lane_rating);
+ if (params.volt_support)
+ lane_rating = rate_lane(eh, eh_min, eh_rec, lane_rating);
+
+ fprintf(csv, "%d,%f,", lane->lane, ew_min);
+ if (spec_ref_only)
+ fprintf(csv, "%f,", ew_rec);
+ else
+ fprintf(csv, "NA,");
+ fprintf(csv, "%f,", ew);
+ if (params.volt_support)
{
- fprintf(csv, "%f,%f,%d,%s,%f,%f,%d,%s,NA,NA,NA,NA,", left_ui,
- left_ui * ui[link_speed], lane->steps[TIM_LEFT],
- sts_strings[lane->statuses[TIM_LEFT]], right_ui, right_ui * ui[link_speed],
- lane->steps[TIM_RIGHT], sts_strings[lane->statuses[TIM_RIGHT]]);
+ fprintf(csv, "%f,", eh_min);
+ if (spec_ref_only)
+ fprintf(csv, "%f,", eh_rec);
+ else
+ fprintf(csv, "NA,");
+ fprintf(csv, "%f,", eh);
}
else
+ fprintf(csv, "NA,NA,NA,");
+ fprintf(csv, "%s,", grades[lane_rating]);
+
+ fprintf(csv, "%f,%f,%d,%s,", left_ps * 100.0 / margin_ui[link_speed], left_ps,
+ lane->steps[TIM_LEFT], sts_strings[lane->statuses[TIM_LEFT]]);
+
+ if (params.ind_left_right_tim)
+ fprintf(csv, "%f,%f,%d,%s,", right_ps * 100.0 / margin_ui[link_speed], right_ps,
+ lane->steps[TIM_RIGHT], sts_strings[lane->statuses[TIM_RIGHT]]);
+ else
{
- for (int k = 0; k < 8; k++)
+ for (int k = 0; k < 4; k++)
fprintf(csv, "NA,");
- fprintf(csv, "%f,%f,%d,%s,", left_ui, left_ui * ui[link_speed], lane->steps[TIM_LEFT],
- sts_strings[lane->statuses[TIM_LEFT]]);
}
if (params.volt_support)
{
+ fprintf(csv, "%f,%d,%s,", up_volt, lane->steps[VOLT_UP],
+ sts_strings[lane->statuses[VOLT_UP]]);
if (params.ind_up_down_volt)
- {
- fprintf(csv, "%f,%d,%s,%f,%d,%s,NA,NA,NA\n", up_volt, lane->steps[VOLT_UP],
- sts_strings[lane->statuses[VOLT_UP]], down_volt, lane->steps[VOLT_DOWN],
- sts_strings[lane->statuses[VOLT_DOWN]]);
- }
+ fprintf(csv, "%f,%d,%s\n", down_volt, lane->steps[VOLT_DOWN],
+ sts_strings[lane->statuses[VOLT_DOWN]]);
else
- {
- for (int k = 0; k < 6; k++)
- fprintf(csv, "NA,");
- fprintf(csv, "%f,%d,%s\n", up_volt, lane->steps[VOLT_UP],
- sts_strings[lane->statuses[VOLT_UP]]);
- }
+ fprintf(csv, "NA,NA,NA\n");
}
else
{
- for (int k = 0; k < 8; k++)
+ for (int k = 0; k < 5; k++)
fprintf(csv, "NA,");
fprintf(csv, "NA\n");
}
diff --git a/ls-caps.c b/ls-caps.c
index 65e92e6..c2aaea5 100644
--- a/ls-caps.c
+++ b/ls-caps.c
@@ -10,6 +10,7 @@
#include <stdio.h>
#include <string.h>
+#include <stdlib.h>
#include "lspci.h"
@@ -1390,6 +1391,68 @@ static void cap_express_slot2(struct device *d UNUSED, int where UNUSED)
/* No capabilities that require this field in PCIe rev2.0 spec. */
}
+static void cap_express_link_rcd(struct device *d)
+{
+ u32 t, aspm, cap_speed, cap_width, sta_speed, sta_width;
+ u16 w;
+ struct pci_dev *pdev = d->dev;
+
+ if (!pdev->rcd_link_cap)
+ return;
+
+ t = pdev->rcd_link_cap;
+ aspm = (t & PCI_EXP_LNKCAP_ASPM) >> 10;
+ cap_speed = t & PCI_EXP_LNKCAP_SPEED;
+ cap_width = (t & PCI_EXP_LNKCAP_WIDTH) >> 4;
+ printf("\t\tLnkCap:\tPort #%d, Speed %s, Width x%d, ASPM %s",
+ t >> 24,
+ link_speed(cap_speed), cap_width,
+ aspm_support(aspm));
+ if (aspm)
+ {
+ printf(", Exit Latency ");
+ if (aspm & 1)
+ printf("L0s %s", latency_l0s((t & PCI_EXP_LNKCAP_L0S) >> 12));
+ if (aspm & 2)
+ printf("%sL1 %s", (aspm & 1) ? ", " : "",
+ latency_l1((t & PCI_EXP_LNKCAP_L1) >> 15));
+ }
+ printf("\n");
+ printf("\t\t\tClockPM%c Surprise%c LLActRep%c BwNot%c ASPMOptComp%c\n",
+ FLAG(t, PCI_EXP_LNKCAP_CLOCKPM),
+ FLAG(t, PCI_EXP_LNKCAP_SURPRISE),
+ FLAG(t, PCI_EXP_LNKCAP_DLLA),
+ FLAG(t, PCI_EXP_LNKCAP_LBNC),
+ FLAG(t, PCI_EXP_LNKCAP_AOC));
+
+ w = pdev->rcd_link_ctrl;
+ printf("\t\tLnkCtl:\tASPM %s;", aspm_enabled(w & PCI_EXP_LNKCTL_ASPM));
+ printf(" Disabled%c CommClk%c\n\t\t\tExtSynch%c ClockPM%c AutWidDis%c BWInt%c AutBWInt%c\n",
+ FLAG(w, PCI_EXP_LNKCTL_DISABLE),
+ FLAG(w, PCI_EXP_LNKCTL_CLOCK),
+ FLAG(w, PCI_EXP_LNKCTL_XSYNCH),
+ FLAG(w, PCI_EXP_LNKCTL_CLOCKPM),
+ FLAG(w, PCI_EXP_LNKCTL_HWAUTWD),
+ FLAG(w, PCI_EXP_LNKCTL_BWMIE),
+ FLAG(w, PCI_EXP_LNKCTL_AUTBWIE));
+
+ w = pdev->rcd_link_status;
+ sta_speed = w & PCI_EXP_LNKSTA_SPEED;
+ sta_width = (w & PCI_EXP_LNKSTA_WIDTH) >> 4;
+ printf("\t\tLnkSta:\tSpeed %s%s, Width x%d%s\n",
+ link_speed(sta_speed),
+ link_compare(PCI_EXP_TYPE_ROOT_INT_EP, sta_speed, cap_speed),
+ sta_width,
+ link_compare(PCI_EXP_TYPE_ROOT_INT_EP, sta_width, cap_width));
+ printf("\t\t\tTrErr%c Train%c SlotClk%c DLActive%c BWMgmt%c ABWMgmt%c\n",
+ FLAG(w, PCI_EXP_LNKSTA_TR_ERR),
+ FLAG(w, PCI_EXP_LNKSTA_TRAIN),
+ FLAG(w, PCI_EXP_LNKSTA_SL_CLK),
+ FLAG(w, PCI_EXP_LNKSTA_DL_ACT),
+ FLAG(w, PCI_EXP_LNKSTA_BWMGMT),
+ FLAG(w, PCI_EXP_LNKSTA_AUTBW));
+}
+
static int
cap_express(struct device *d, int where, int cap)
{
@@ -1454,6 +1517,9 @@ cap_express(struct device *d, int where, int cap)
cap_express_dev(d, where, type);
if (link)
cap_express_link(d, where, type);
+ else if (d->dev->rcd_link_cap)
+ cap_express_link_rcd(d);
+
if (slot)
cap_express_slot(d, where);
if (type == PCI_EXP_TYPE_ROOT_PORT || type == PCI_EXP_TYPE_ROOT_EC)
diff --git a/ls-ecaps.c b/ls-ecaps.c
index 2340084..e817180 100644
--- a/ls-ecaps.c
+++ b/ls-ecaps.c
@@ -136,34 +136,56 @@ cap_aer(struct device *d, int where, int type)
return;
l = get_conf_long(d, where + PCI_ERR_UNCOR_STATUS);
- printf("\t\tUESta:\tDLP%c SDES%c TLP%c FCP%c CmpltTO%c CmpltAbrt%c UnxCmplt%c RxOF%c "
- "MalfTLP%c ECRC%c UnsupReq%c ACSViol%c\n",
+ printf("\t\tUESta:\tDLP%c SDES%c TLP%c FCP%c CmpltTO%c CmpltAbrt%c UnxCmplt%c RxOF%c MalfTLP%c\n"
+ "\t\t\tECRC%c UnsupReq%c ACSViol%c UncorrIntErr%c BlockedTLP%c AtomicOpBlocked%c TLPBlockedErr%c\n"
+ "\t\t\tPoisonTLPBlocked%c DMWrReqBlocked%c IDECheck%c MisIDETLP%c PCRC_CHECK%c TLPXlatBlocked%c\n",
FLAG(l, PCI_ERR_UNC_DLP), FLAG(l, PCI_ERR_UNC_SDES), FLAG(l, PCI_ERR_UNC_POISON_TLP),
FLAG(l, PCI_ERR_UNC_FCP), FLAG(l, PCI_ERR_UNC_COMP_TIME), FLAG(l, PCI_ERR_UNC_COMP_ABORT),
FLAG(l, PCI_ERR_UNC_UNX_COMP), FLAG(l, PCI_ERR_UNC_RX_OVER), FLAG(l, PCI_ERR_UNC_MALF_TLP),
- FLAG(l, PCI_ERR_UNC_ECRC), FLAG(l, PCI_ERR_UNC_UNSUP), FLAG(l, PCI_ERR_UNC_ACS_VIOL));
+ FLAG(l, PCI_ERR_UNC_ECRC), FLAG(l, PCI_ERR_UNC_UNSUP), FLAG(l, PCI_ERR_UNC_ACS_VIOL),
+ FLAG(l, PCI_ERR_UNC_INTERNAL), FLAG(l, PCI_ERR_UNC_MC_BLOCKED_TLP),
+ FLAG(l, PCI_ERR_UNC_ATOMICOP_EGRESS_BLOCKED), FLAG(l, PCI_ERR_UNC_TLP_PREFIX_BLOCKED),
+ FLAG(l, PCI_ERR_UNC_POISONED_TLP_EGRESS), FLAG(l, PCI_ERR_UNC_DMWR_REQ_EGRESS_BLOCKED),
+ FLAG(l, PCI_ERR_UNC_IDE_CHECK), FLAG(l, PCI_ERR_UNC_MISR_IDE_TLP), FLAG(l, PCI_ERR_UNC_PCRC_CHECK),
+ FLAG(l, PCI_ERR_UNC_TLP_XLAT_EGRESS_BLOCKED));
l = get_conf_long(d, where + PCI_ERR_UNCOR_MASK);
- printf("\t\tUEMsk:\tDLP%c SDES%c TLP%c FCP%c CmpltTO%c CmpltAbrt%c UnxCmplt%c RxOF%c "
- "MalfTLP%c ECRC%c UnsupReq%c ACSViol%c\n",
+ printf("\t\tUEMsk:\tDLP%c SDES%c TLP%c FCP%c CmpltTO%c CmpltAbrt%c UnxCmplt%c RxOF%c MalfTLP%c\n"
+ "\t\t\tECRC%c UnsupReq%c ACSViol%c UncorrIntErr%c BlockedTLP%c AtomicOpBlocked%c TLPBlockedErr%c\n"
+ "\t\t\tPoisonTLPBlocked%c DMWrReqBlocked%c IDECheck%c MisIDETLP%c PCRC_CHECK%c TLPXlatBlocked%c\n",
FLAG(l, PCI_ERR_UNC_DLP), FLAG(l, PCI_ERR_UNC_SDES), FLAG(l, PCI_ERR_UNC_POISON_TLP),
FLAG(l, PCI_ERR_UNC_FCP), FLAG(l, PCI_ERR_UNC_COMP_TIME), FLAG(l, PCI_ERR_UNC_COMP_ABORT),
FLAG(l, PCI_ERR_UNC_UNX_COMP), FLAG(l, PCI_ERR_UNC_RX_OVER), FLAG(l, PCI_ERR_UNC_MALF_TLP),
- FLAG(l, PCI_ERR_UNC_ECRC), FLAG(l, PCI_ERR_UNC_UNSUP), FLAG(l, PCI_ERR_UNC_ACS_VIOL));
+ FLAG(l, PCI_ERR_UNC_ECRC), FLAG(l, PCI_ERR_UNC_UNSUP), FLAG(l, PCI_ERR_UNC_ACS_VIOL),
+ FLAG(l, PCI_ERR_UNC_INTERNAL), FLAG(l, PCI_ERR_UNC_MC_BLOCKED_TLP),
+ FLAG(l, PCI_ERR_UNC_ATOMICOP_EGRESS_BLOCKED), FLAG(l, PCI_ERR_UNC_TLP_PREFIX_BLOCKED),
+ FLAG(l, PCI_ERR_UNC_POISONED_TLP_EGRESS), FLAG(l, PCI_ERR_UNC_DMWR_REQ_EGRESS_BLOCKED),
+ FLAG(l, PCI_ERR_UNC_IDE_CHECK), FLAG(l, PCI_ERR_UNC_MISR_IDE_TLP), FLAG(l, PCI_ERR_UNC_PCRC_CHECK),
+ FLAG(l, PCI_ERR_UNC_TLP_XLAT_EGRESS_BLOCKED));
l = get_conf_long(d, where + PCI_ERR_UNCOR_SEVER);
- printf("\t\tUESvrt:\tDLP%c SDES%c TLP%c FCP%c CmpltTO%c CmpltAbrt%c UnxCmplt%c RxOF%c "
- "MalfTLP%c ECRC%c UnsupReq%c ACSViol%c\n",
+ printf("\t\tUESvrt:\tDLP%c SDES%c TLP%c FCP%c CmpltTO%c CmpltAbrt%c UnxCmplt%c RxOF%c MalfTLP%c\n"
+ "\t\t\tECRC%c UnsupReq%c ACSViol%c UncorrIntErr%c BlockedTLP%c AtomicOpBlocked%c TLPBlockedErr%c\n"
+ "\t\t\tPoisonTLPBlocked%c DMWrReqBlocked%c IDECheck%c MisIDETLP%c PCRC_CHECK%c TLPXlatBlocked%c\n",
FLAG(l, PCI_ERR_UNC_DLP), FLAG(l, PCI_ERR_UNC_SDES), FLAG(l, PCI_ERR_UNC_POISON_TLP),
FLAG(l, PCI_ERR_UNC_FCP), FLAG(l, PCI_ERR_UNC_COMP_TIME), FLAG(l, PCI_ERR_UNC_COMP_ABORT),
FLAG(l, PCI_ERR_UNC_UNX_COMP), FLAG(l, PCI_ERR_UNC_RX_OVER), FLAG(l, PCI_ERR_UNC_MALF_TLP),
- FLAG(l, PCI_ERR_UNC_ECRC), FLAG(l, PCI_ERR_UNC_UNSUP), FLAG(l, PCI_ERR_UNC_ACS_VIOL));
+ FLAG(l, PCI_ERR_UNC_ECRC), FLAG(l, PCI_ERR_UNC_UNSUP), FLAG(l, PCI_ERR_UNC_ACS_VIOL),
+ FLAG(l, PCI_ERR_UNC_INTERNAL), FLAG(l, PCI_ERR_UNC_MC_BLOCKED_TLP),
+ FLAG(l, PCI_ERR_UNC_ATOMICOP_EGRESS_BLOCKED), FLAG(l, PCI_ERR_UNC_TLP_PREFIX_BLOCKED),
+ FLAG(l, PCI_ERR_UNC_POISONED_TLP_EGRESS), FLAG(l, PCI_ERR_UNC_DMWR_REQ_EGRESS_BLOCKED),
+ FLAG(l, PCI_ERR_UNC_IDE_CHECK), FLAG(l, PCI_ERR_UNC_MISR_IDE_TLP), FLAG(l, PCI_ERR_UNC_PCRC_CHECK),
+ FLAG(l, PCI_ERR_UNC_TLP_XLAT_EGRESS_BLOCKED));
l = get_conf_long(d, where + PCI_ERR_COR_STATUS);
- printf("\t\tCESta:\tRxErr%c BadTLP%c BadDLLP%c Rollover%c Timeout%c AdvNonFatalErr%c\n",
+ printf("\t\tCESta:\tRxErr%c BadTLP%c BadDLLP%c Rollover%c Timeout%c AdvNonFatalErr%c "
+ "CorrIntErr%c HeaderOF%c\n",
FLAG(l, PCI_ERR_COR_RCVR), FLAG(l, PCI_ERR_COR_BAD_TLP), FLAG(l, PCI_ERR_COR_BAD_DLLP),
- FLAG(l, PCI_ERR_COR_REP_ROLL), FLAG(l, PCI_ERR_COR_REP_TIMER), FLAG(l, PCI_ERR_COR_REP_ANFE));
+ FLAG(l, PCI_ERR_COR_REP_ROLL), FLAG(l, PCI_ERR_COR_REP_TIMER), FLAG(l, PCI_ERR_COR_REP_ANFE),
+ FLAG(l, PCI_ERR_COR_INTERNAL), FLAG(l, PCI_ERR_COR_HDRLOG_OVER));
l = get_conf_long(d, where + PCI_ERR_COR_MASK);
- printf("\t\tCEMsk:\tRxErr%c BadTLP%c BadDLLP%c Rollover%c Timeout%c AdvNonFatalErr%c\n",
+ printf("\t\tCEMsk:\tRxErr%c BadTLP%c BadDLLP%c Rollover%c Timeout%c AdvNonFatalErr%c "
+ "CorrIntErr%c HeaderOF%c\n",
FLAG(l, PCI_ERR_COR_RCVR), FLAG(l, PCI_ERR_COR_BAD_TLP), FLAG(l, PCI_ERR_COR_BAD_DLLP),
- FLAG(l, PCI_ERR_COR_REP_ROLL), FLAG(l, PCI_ERR_COR_REP_TIMER), FLAG(l, PCI_ERR_COR_REP_ANFE));
+ FLAG(l, PCI_ERR_COR_REP_ROLL), FLAG(l, PCI_ERR_COR_REP_TIMER), FLAG(l, PCI_ERR_COR_REP_ANFE),
+ FLAG(l, PCI_ERR_COR_INTERNAL), FLAG(l, PCI_ERR_COR_HDRLOG_OVER));
l = get_conf_long(d, where + PCI_ERR_CAP);
printf("\t\tAERCap:\tFirst Error Pointer: %02x, ECRCGenCap%c ECRCGenEn%c ECRCChkCap%c ECRCChkEn%c\n"
"\t\t\tMultHdrRecCap%c MultHdrRecEn%c TLPPfxPres%c HdrLogCap%c\n",
diff --git a/lspci.c b/lspci.c
index 071cc11..aee1673 100644
--- a/lspci.c
+++ b/lspci.c
@@ -805,7 +805,7 @@ show_verbose(struct device *d)
pci_fill_info(p, PCI_FILL_IRQ | PCI_FILL_BASES | PCI_FILL_ROM_BASE | PCI_FILL_SIZES |
PCI_FILL_PHYS_SLOT | PCI_FILL_NUMA_NODE | PCI_FILL_DT_NODE | PCI_FILL_IOMMU_GROUP |
- PCI_FILL_BRIDGE_BASES | PCI_FILL_CLASS_EXT | PCI_FILL_SUBSYS);
+ PCI_FILL_BRIDGE_BASES | PCI_FILL_CLASS_EXT | PCI_FILL_SUBSYS | PCI_FILL_RCD_LNK);
switch (htype)
{
diff --git a/pci.ids b/pci.ids
index 7a69d6d..df152a4 100644
--- a/pci.ids
+++ b/pci.ids
@@ -1,8 +1,8 @@
#
# List of PCI ID's
#
-# Version: 2024.03.31
-# Date: 2024-03-31 03:15:02
+# Version: 2024.05.14
+# Date: 2024-05-14 03:15:02
#
# Maintained by Albert Pool, Martin Mares, and other volunteers from
# the PCI ID Project at https://pci-ids.ucw.cz/.
@@ -9200,6 +9200,7 @@
87d0 PEX PCI Express Switch DMA interface
9016 PLX 9016 8-port serial controller
9030 PCI9030 32-bit 33MHz PCI <-> IOBus Bridge
+ 10b5 1205 Becker & Hickl MSA-1000
10b5 2695 Hilscher CIF50-PB/DPS Profibus
10b5 2862 Alpermann+Velte PCL PCI LV (3V/5V): Timecode Reader Board
10b5 2906 Alpermann+Velte PCI TS (3V/5V): Time Synchronisation Board
@@ -9235,7 +9236,13 @@
10b5 1067 IXXAT CAN i165
10b5 114e Wasco WITIO PCI168extended
10b5 1169 Wasco OPTOIO32standard 32 digital in, 32 digital out
+ 10b5 1171 Becker & Hickl PMS-400
10b5 1172 IK220 (Heidenhain)
+ 10b5 1201 Becker & Hickl SPC-6x0
+ 10b5 1202 Becker & Hickl SPC-7x0
+ 10b5 1203 Becker & Hickl MSA-300
+ 10b5 1206 Becker & Hickl DCC-100
+ 10b5 120a Becker & Hickl STP-340
10b5 2036 SatPak GPS
10b5 2221 Alpermann+Velte PCL PCI LV: Timecode Reader Board
10b5 2273 SH ARC-PCI SOHARD ARCNET card
@@ -9283,6 +9290,11 @@
d84d 4078 EX-4078 2S(16C552) RS-232+1P
9052 PCI9052 PCI <-> IOBus Bridge
9054 PCI9054 32-bit 33MHz PCI <-> IOBus Bridge
+ 10b5 1171 Becker & Hickl PMS-400A
+ 10b5 1208 Becker & Hickl SPC-830
+ 10b5 120e Becker & Hickl SPC-930
+ 10b5 120f Becker & Hickl SPC-150
+ 10b5 1210 Becker & Hickl DPC-230
10b5 2455 Wessex Techology PHIL-PCI
10b5 2696 Innes Corp AM Radcap card
10b5 2717 Innes Corp Auricon card
@@ -12761,6 +12773,7 @@
1dc1 GV100 [CMP 100-200]
1df0 GV100GL [Tesla PG500-216]
1df2 GV100GL [Tesla PG503-216]
+ 1df4 GV100 [CMP 100-210]
1df5 GV100GL [Tesla V100 SXM2 16GB]
1df6 GV100GL [Tesla V100S PCIe 32GB]
1e02 TU102 [TITAN RTX]
@@ -13045,6 +13058,8 @@
25ac GN20-P0-R-K2 [GeForce RTX 3050 6GB Laptop GPU]
25ad GA107 [GeForce RTX 2050]
25af GA107 [GeForce RTX 3050 Engineering Sample]
+ 25b0 GA107GL [RTX A1000]
+ 25b2 GA107GL [RTX A400]
25b5 GA107GLM [RTX A4 Mobile]
# A16 - 25B6 10DE 14A9 / A2 - 25B6 10DE 157E
25b6 GA107GL [A2 / A16]
@@ -13110,7 +13125,7 @@
2882 AD107 [GeForce RTX 4060]
28a0 AD107M [GeForce RTX 4060 Max-Q / Mobile]
28a1 AD107M [GeForce RTX 4050 Max-Q / Mobile]
- 28b0 AD107GL [RTX 2000 Ada Generation]
+ 28b0 AD107GL [RTX 2000 / 2000E Ada Generation]
28b8 AD107GLM [RTX 2000 Ada Generation Laptop GPU]
28b9 AD107GLM [RTX 1000 Ada Generation Laptop GPU]
28ba AD107GLM [RTX 500 Ada Generation Laptop GPU]
@@ -13369,6 +13384,7 @@
5762 RTS5762 NVMe SSD Controller
5763 RTS5763DL NVMe SSD Controller (DRAM-less)
5765 RTS5765DL NVMe SSD Controller (DRAM-less)
+ 5770 RTS5770DL NVMe SSD Controller (DRAM-less)
5772 RTS5772DL NVMe SSD Controller (DRAM-less)
8029 RTL-8029(AS)
10b8 2011 EZ-Card (SMC1208)
@@ -16464,6 +16480,38 @@
11e2 Samsung Information Systems America
11e3 Quicklogic Corporation
0001 COM-ON-AIR Dosch&Amand DECT
+ 0010 QL5032 (PQFP208) [QuickPCI] 33 MHz/32-bit PCI Master/Target with Embedded Programmable Logic and Dual Port SRAM
+ 0011 QL5032 (PBGA256) [QuickPCI] 33 MHz/32-bit PCI Master/Target with Embedded Programmable Logic and Dual Port SRAM
+ 0012 QL5232 (PQFP208) [QuickPCI] 33 MHz/32-bit PCI Master/Target with Embedded Programmable Logic and Dual Port SRAM
+ 11e3 1204 Becker & Hickl SPC-130
+ 11e3 1207 Becker & Hickl DDG-200
+ 11e3 1209 Becker & Hickl SHM-180
+ 11e3 120c Becker & Hickl PMM-428
+ 0013 QL5232 (PBGA456) [QuickPCI] 33 MHz/32-bit PCI Master/Target with Embedded Programmable Logic and Dual Port SRAM
+ 0014 QL5030 (TQFP144) [QuickPCI] 33 MHz/32-bit PCI Target with Embedded Programmable Logic and Dual Port SRAM
+ 0015 QL5130 (TQFP144) [QuickPCI] 33 MHz/32-bit PCI Target with Embedded Programmable Logic and Dual Port SRAM
+ 0016 QL5130 (PQFP208) [QuickPCI] 33 MHz/32-bit PCI Target with Embedded Programmable Logic and Dual Port SRAM
+ 11e3 120b Becker & Hickl DEL-350
+ 0017 QL5130 (PBGA256) [QuickPCI] 33 MHz/32-bit PCI Target with Embedded Programmable Logic and Dual Port SRAM
+ 0019 QL5332 (PQFP208) [QuickPCI] 33 MHz/32-bit PCI Master/Target with Embedded Programmable Logic and Dual Port SRAM
+ 001a QL5332 (PBGA256) [QuickPCI] 33 MHz/32-bit PCI Master/Target with Embedded Programmable Logic and Dual Port SRAM
+ 001b QL5432 (PQFP208) [QuickPCI] 33 MHz/32-bit PCI Master/Target with Embedded Programmable Logic and Dual Port SRAM
+ 11e3 120d Becker & Hickl SPC-140
+ 11e3 1211 Becker & Hickl GVD-120
+ 11e3 1212 Becker & Hickl DDG-210
+ 001c QL5432 (PBGA456) [QuickPCI] 33 MHz/32-bit PCI Master/Target with Embedded Programmable Logic and Dual Port SRAM
+ 001d QL5632 (PQFP208) [QuickPCI] 33 MHz/32-bit PCI Master/Target with Embedded Programmable Logic and Dual Port SRAM
+ 001e QL5632 (PBGA280) [QuickPCI] 33 MHz/32-bit PCI Master/Target with Embedded Programmable Logic and Dual Port SRAM
+ 001f QL5632 (PBGA484) [QuickPCI] 33 MHz/32-bit PCI Master/Target with Embedded Programmable Logic and Dual Port SRAM
+ 0020 QL5632 (PBGA516) [QuickPCI] 33 MHz/32-bit PCI Master/Target with Embedded Programmable Logic and Dual Port SRAM
+ 0021 QL5732 (PQFP208) [QuickPCI] 33 MHz/32-bit PCI Master/Target with Embedded Programmable Logic and Dual Port SRAM
+ 0022 QL5732 (PBGA280) [QuickPCI] 33 MHz/32-bit PCI Master/Target with Embedded Programmable Logic and Dual Port SRAM
+ 0023 QL5732 (PBGA484) [QuickPCI] 33 MHz/32-bit PCI Master/Target with Embedded Programmable Logic and Dual Port SRAM
+ 0024 QL5732 (PBGA516) [QuickPCI] 33 MHz/32-bit PCI Master/Target with Embedded Programmable Logic and Dual Port SRAM
+ 002d QL5022 (TQFP144) [QuickPCI] 33 MHz/32-bit PCI Master/Target with Embedded Programmable Logic and Dual Port SRAM
+ 002e QL5022 (PQFP208) [QuickPCI] 33 MHz/32-bit PCI Master/Target with Embedded Programmable Logic and Dual Port SRAM
+ 002f QL5020 (TQFP144) [QuickPCI] 33 MHz/32-bit PCI Target with Embedded Programmable Logic and Dual Port SRAM
+ 0030 QL5020 (PQFP208) [QuickPCI] 33 MHz/32-bit PCI Target with Embedded Programmable Logic and Dual Port SRAM
0560 QL5064 Companion Design Demo Board
5030 PC Watchdog
8417 QL5064 [QuickPCI] PCI v2.2 bridge for SMT417 Dual TMS320C6416T PMC Module
@@ -17073,6 +17121,7 @@
0820 SM820 Lynx3D
0910 SM910
2260 SM2260 NVMe SSD Controller
+ 2261 SM2261XT x2 NVMe SSD Controller (DRAM-less)
2262 SM2262/SM2262EN SSD Controller
2263 SM2263EN/SM2263XT (DRAM-less) NVMe SSD Controllers
2269 SM2269XT (DRAM-less) NVMe SSD Controller
@@ -19606,14 +19655,14 @@
6002 T6225-SO-CR Unified Wire Ethernet Controller
6003 T6425-CR Unified Wire Ethernet Controller
6004 T6425-SO-CR Unified Wire Ethernet Controller
- 6005 T6225-OCP-SO Unified Wire Ethernet Controller
- 6006 T62100-OCP-SO Unified Wire Ethernet Controller
+ 6005 T6225-SO-OCP3 Unified Wire Ethernet Controller
+ 6006 T6225-OCP3 Unified Wire Ethernet Controller
6007 T62100-LP-CR Unified Wire Ethernet Controller
6008 T62100-SO-CR Unified Wire Ethernet Controller
6009 T6210-BT Unified Wire Ethernet Controller
600d T62100-CR Unified Wire Ethernet Controller
6011 T6225-LL-CR Unified Wire Ethernet Controller
- 6014 T61100-OCP-SO Unified Wire Ethernet Controller
+ 6014 T62100-SO-OCP3 Unified Wire Ethernet Controller
6015 T6201-BT Unified Wire Ethernet Controller
6080 T6225-6080 Unified Wire Ethernet Controller
6081 T62100-6081 Unified Wire Ethernet Controller
@@ -19632,14 +19681,14 @@
6402 T6225-SO-CR Unified Wire Ethernet Controller
6403 T6425-CR Unified Wire Ethernet Controller
6404 T6425-SO-CR Unified Wire Ethernet Controller
- 6405 T6225-OCP-SO Unified Wire Ethernet Controller
- 6406 T62100-OCP-SO Unified Wire Ethernet Controller
+ 6405 T6225-SO-OCP3 Unified Wire Ethernet Controller
+ 6406 T6225-OCP3 Unified Wire Ethernet Controller
6407 T62100-LP-CR Unified Wire Ethernet Controller
6408 T62100-SO-CR Unified Wire Ethernet Controller
6409 T6210-BT Unified Wire Ethernet Controller
640d T62100-CR Unified Wire Ethernet Controller
6411 T6225-LL-CR Unified Wire Ethernet Controller
- 6414 T61100-OCP-SO Unified Wire Ethernet Controller
+ 6414 T62100-SO-OCP3 Unified Wire Ethernet Controller
6415 T6201-BT Unified Wire Ethernet Controller
6480 T6225-6080 Unified Wire Ethernet Controller
6481 T62100-6081 Unified Wire Ethernet Controller
@@ -19658,14 +19707,14 @@
6502 T6225-SO-CR Unified Wire Storage Controller
6503 T6425-CR Unified Wire Storage Controller
6504 T6425-SO-CR Unified Wire Storage Controller
- 6505 T6225-OCP-SO Unified Wire Storage Controller
- 6506 T62100-OCP-SO Unified Wire Storage Controller
+ 6505 T6225-SO-OCP3 Unified Wire Storage Controller
+ 6506 T6225-OCP3 Unified Wire Storage Controller
6507 T62100-LP-CR Unified Wire Storage Controller
6508 T62100-SO-CR Unified Wire Storage Controller
6509 T6210-BT Unified Wire Storage Controller
650d T62100-CR Unified Wire Storage Controller
6511 T6225-LL-CR Unified Wire Storage Controller
- 6514 T61100-OCP-SO Unified Wire Storage Controller
+ 6514 T62100-SO-OCP3 Unified Wire Storage Controller
6515 T6201-BT Unified Wire Storage Controller
6580 T6225-6080 Unified Wire Storage Controller
6581 T62100-6081 Unified Wire Storage Controller
@@ -19683,14 +19732,14 @@
6602 T6225-SO-CR Unified Wire Storage Controller
6603 T6425-CR Unified Wire Storage Controller
6604 T6425-SO-CR Unified Wire Storage Controller
- 6605 T6225-OCP-SO Unified Wire Storage Controller
- 6606 T62100-OCP-SO Unified Wire Storage Controller
+ 6605 T6225-SO-OCP3 Unified Wire Storage Controller
+ 6606 T6225-OCP3 Unified Wire Storage Controller
6607 T62100-LP-CR Unified Wire Storage Controller
6608 T62100-SO-CR Unified Wire Storage Controller
6609 T6210-BT Unified Wire Storage Controller
660d T62100-CR Unified Wire Storage Controller
6611 T6225-LL-CR Unified Wire Storage Controller
- 6614 T61100-OCP-SO Unified Wire Storage Controller
+ 6614 T62100-SO-OCP3 Unified Wire Storage Controller
6615 T6201-BT Unified Wire Storage Controller
6680 T6225-6080 Unified Wire Storage Controller
6681 T62100-6081 Unified Wire Storage Controller
@@ -19708,14 +19757,14 @@
6802 T6225-SO-CR Unified Wire Ethernet Controller [VF]
6803 T6425-CR Unified Wire Ethernet Controller [VF]
6804 T6425-SO-CR Unified Wire Ethernet Controller [VF]
- 6805 T6225-OCP-SO Unified Wire Ethernet Controller [VF]
- 6806 T62100-OCP-SO Unified Wire Ethernet Controller [VF]
+ 6805 T6225-SO-OCP3 Unified Wire Ethernet Controller [VF]
+ 6806 T6225-OCP3 Unified Wire Ethernet Controller [VF]
6807 T62100-LP-CR Unified Wire Ethernet Controller [VF]
6808 T62100-SO-CR Unified Wire Ethernet Controller [VF]
6809 T6210-BT Unified Wire Ethernet Controller [VF]
680d T62100-CR Unified Wire Ethernet Controller [VF]
6811 T6225-LL-CR Unified Wire Ethernet Controller [VF]
- 6814 T61100-OCP-SO Unified Wire Ethernet Controller [VF]
+ 6814 T62100-SO-OCP3 Unified Wire Ethernet Controller [VF]
6815 T6201-BT Unified Wire Ethernet Controller [VF]
6880 T6225-6080 Unified Wire Ethernet Controller [VF]
6881 T62100-6081 Unified Wire Ethernet Controller [VF]
@@ -21871,6 +21920,9 @@
0285 Sagitta RMA
0286 LibraE Flash Recovery
0287 LibraE RMA
+# Flash recovery
+ 0288 Arcus2
+ 0289 Arcus2 RMA
1002 MT25400 Family [ConnectX-2 Virtual Function]
1003 MT27500 Family [ConnectX-3]
1014 04b5 PCIe3 40GbE RoCE Converged Host Bus Adapter for Power
@@ -21989,6 +22041,7 @@
2023 MT2910 Family [ConnectX-7 Emulated PCIe Bridge]
2024 MT43244 Family [BlueField-3 SoC Emulated PCIe Bridge]
2025 ConnectX/BlueField Family mlx5Gen Emulated PCIe Bridge [Emulated PCIe Bridge]
+ 2100 CX8 Family [CX8 Data Direct Interface]
4117 MT27712A0-FDCF-AE
1bd4 0039 SN10XMP2P25
1bd4 003a 25G SFP28 SP EO251FM9 Adapter
@@ -22054,6 +22107,7 @@
a2df BF4 Family integrated network controller [BlueField-4 integrated network controller]
b200 ArcusE
b201 LibraE
+ b202 Arcus2
c2d1 BlueField DPU Family Auxiliary Communication Channel [BlueField Family]
c2d2 MT416842 BlueField SoC management interfac
c2d3 MT42822 BlueField-2 SoC Management Interface
@@ -23880,7 +23934,7 @@
0100 A104d QUAD T1/E1 AFT card
0300 A101 single-port T1/E1
0400 A104u Quad T1/E1 AFT
-1924 Solarflare Communications
+1924 AMD Solarflare
0703 SFC4000 rev A net [Solarstorm]
10b8 0102 SMC10GPCIe-10BT (A2) [TigerCard]
10b8 0103 SMC10GPCIe-10BT (A3) [TigerCard]
@@ -24197,8 +24251,11 @@
196d Club-3D BV
196e PNY
1971 AGEIA Technologies, Inc.
- 1011 Physics Processing Unit [PhysX]
+ 0000 Physics Processing Unit [PhysX] 100 Series PCI Express Card
+# The PCI and PCIe versions have a different PID
+ 1011 Physics Processing Unit [PhysX] 100 Series PCI Card
1043 0001 PhysX P1
+ 1021 Physics Processing Unit [PhysX] 200 Series PCI Express Card
# nee Eberspaecher Electronics
1974 Star Electronics GmbH & Co. KG
0009 FlexCard PMC-II
@@ -25277,6 +25334,7 @@
2429 PE6011 NVMe Solid State Drive
243b PE6110 NVMe Solid State Drive
1c5c 0100 PE6110 NVMe Solid State Drive
+ 2527 PE4010 NVMe Solid State Drive
2839 PE8000 Series NVMe Solid State Drive
1028 2143 DC NVMe SED PE8010 RI U.2 960GB
1028 2144 DC NVMe PE8010 RI U.2 960GB
@@ -25861,7 +25919,7 @@
2262 NVMe PCIe SSD 220S/MTE662T2
2263 NVMe PCIe SSD 110S/112S/120S/MTE300S/MTE400S/MTE652T2 (DRAM-less)
2264 NVMe PCIe SSD 250H
- 2267 NVMe PCIe SSD 240S/MTE710T
+ 2267 NVMe PCIe SSD 220S/240S/MTE710T
5766 NVMe PCIe SSD 110Q (DRAM-less)
1d7c Aerotech, Inc.
# Fiber-optic HyperWire motion control bus from Aerotech.
@@ -26338,6 +26396,7 @@
1df3 0001 ENA2100RN
1df5 Shenzhen TIGO Semiconductor
1202 kimtigo NVMe SSD (DRAM-less)
+ 2263 kimtigo MG931K NVMe SSD (DRAM-less)
1df7 opencpi.org
0001 ml605
0002 alst4
@@ -26446,6 +26505,7 @@
1028 223c Ent NVMe CM7 U.2 MU 6.4TB
1028 223d Ent NVMe CM7 U.2 MU 3.2TB
1028 223e Ent NVMe CM7 U.2 MU 1.6TB
+ 002a Exceria Plus G3 NVMe SSD (DRAM-less)
002c NVMe SSD Controller CD8P EDSFF
1028 22bf DC NVMe CD8P E3.S 15.36TB
1028 22c0 DC NVMe CD8P E3.S 7.68TB
@@ -26561,6 +26621,17 @@
1e3b 00f3 Enterprise NVMe SSD U.2 3.20TB (X2900)
1e3b 00f5 Enterprise NVMe SSD U.2 0.40TB (X2900P)
1e3b 00f6 Enterprise NVMe SSD U.2 0.80TB (X2900P)
+ 0800 DP800
+ 1e3b 0001 Enterprise NVMe SSD U.2 QDP 3.84TB(R6100)
+ 1e3b 0007 Enterprise NVMe SSD U.2 ODP 15.36TB (R6100)
+ 1e3b 000a Enterprise NVMe SSD U.2 3.20TB (R6300)
+ 1e3b 000d Enterprise NVMe SSD U.2 6.40TB (R6300)
+ 1e3b 0010 Enterprise NVMe SSD U.2 12.80TB (R6300)
+ 1e3b 0018 Enterprise NVMe SSD U.2 QDP 3.84TB (R6100C)
+ 1e3b 0019 Enterprise NVMe SSD U.2 ODP 7.68TB (R6100C)
+ 1e3b 001a Enterprise NVMe SSD U.2 3.20TB (R6300C)
+ 1e3b 001b Enterprise NVMe SSD U.2 6.40TB (R6300C)
+ 1e3b 001c Enterprise NVMe SSD U.2 ODP 7.68TB (R6100)
1098 Haishen3 NVMe SSD
1e3b 0001 Enterprise NVMe SSD U.2 0.8TB (H2100)
1e3b 0002 Enterprise NVMe SSD U.2 0.96TB (H2200)
@@ -26613,6 +26684,8 @@
1011 PC210 NVMe SSD
1013 PC210 NVMe SSD
1031 PC300 NVMe SSD (DRAM-less)
+ 1033 PC300 NVMe SSD (DRAM-less)
+ 1071 PC411 NVMe SSD (DRAM-less)
1e4b MAXIO Technology (Hangzhou) Ltd.
1001 NVMe SSD Controller MAP1001
1002 NVMe SSD Controller MAP1002 (DRAM-less)
@@ -26758,6 +26831,10 @@
1ed0 Hosin Global Electronics
1ed2 FuriosaAI, Inc.
0000 Warboy
+ 1111 RNGD
+ 0000 1111 RNGD-S
+ 0000 2222 RNGD VF
+ 0000 3333 RNGD-S VF
1ed3 Yeston
1ed5 Moore Threads Technology Co.,Ltd
0100 MTT S10
@@ -26777,6 +26854,7 @@
0211 MTT X300
0221 G2S80
0222 MTT S3000
+ 1ed5 0001 C3150
0223 G2S4
0251 G2N10
02ff MTT HDMI/DP Audio
@@ -27069,6 +27147,7 @@
1fe4 0076 Enterprise NVMe SSD U.2 7.68TB(HP610)
1fe4 0077 Enterprise NVMe SSD U.2 6.40TB(HP630)
1fe4 0078 Enterprise NVMe SSD U.2 3.20TB(HP630)
+1fe9 MemryX
1ff4 DEEPX Co., Ltd.
0000 DX_M1
0001 DX_M1A
@@ -27083,9 +27162,12 @@
2003 Smart Link Ltd.
8800 LM-I56N
2004 Smart Link Ltd.
+202c CAEN S.p.A.
+ 5818 A5818
2036 Netforward Microelectronics Co., Ltd.
1618 NF1618 PCI Express Ethernet Controller
-2046 Shenzhen Inovance Technology Co., Ltd.
+ 1619 NF1618 Family Virtual Function
+2046 GXMICRO Technology (Shanghai) Co., Ltd.
2048 Beijing SpaceControl Technology Co.Ltd
20f4 TRENDnet
2116 ZyDAS Technology Corp.
@@ -27126,6 +27208,7 @@
501d NV2 NVMe SSD TC2200 (DRAM-less)
501f FURY Renegade NVMe SSD with heatsink
5021 OM8SEP4 Design-In PCIe 4 NVMe SSD (QLC) (DRAM-less)
+ 5022 OM8PGP4 Design-In PCIe 4 NVMe SSD (QLC) (DRAM-less)
5023 NV2 NVMe SSD SM2269XT (DRAM-less)
5024 DC2000B NVMe SSD E18DC
270b Xantel Corporation
@@ -29657,6 +29740,12 @@
12d2 Ethernet Controller E830-CC for QSFP
12d3 Ethernet Controller E830-CC for SFP
12d4 Ethernet Controller E830-CC for SFP-DD
+ 12d5 Ethernet Controller E830-C for backplane
+ 12d8 Ethernet Controller E830-C for QSFP
+ 12da Ethernet Controller E830-C for SFP
+ 12dc Ethernet Controller E830-XXV for backplane
+ 12dd Ethernet Controller E830-XXV for QSFP
+ 12de Ethernet Controller E830-XXV for SFP
1360 82806AA PCI64 Hub PCI Bridge
1361 82806AA PCI64 Hub Controller (HRes)
8086 1361 82806AA PCI64 Hub Controller (HRes)
@@ -34870,6 +34959,7 @@
46b0 AlderLake-P [Iris Xe Graphics]
46b1 AlderLake-P [Iris Xe Graphics]
46b3 Alder Lake-UP3 GT1 [UHD Graphics]
+ 1025 161d N22C6 [Extensa 15 EX215-55]
46b6 AlderLake-P [Iris Xe Graphics]
46b8 AlderLake-P [Iris Xe Graphics]
46ba AlderLake-P [Iris Xe Graphics]
@@ -35129,7 +35219,8 @@
579e Ethernet Connection E825-C for SFP
57a4 Thunderbolt Bridge [Barlow Ridge Hub 40G 2023]
57a5 Thunderbolt USB Controller [Barlow Ridge Hub 40G 2023]
- 57b1 Ethernet Controller E610 1GBASE T
+ 57b0 Ethernet Controller E610 10GBASE T
+ 57b1 Ethernet Controller E610 2.5GBASE T
8086 0000 Ethernet Converged Network Adapter E610
5845 QEMU NVM Express Controller
1af4 1100 QEMU Virtual Machine
@@ -36438,6 +36529,7 @@
1028 06e4 XPS 15 9550
103c 825b OMEN-17-w001nv
1043 86c7 H110I-PLUS Motherboard
+ 1462 f994 H110M ECO/GAMING
a171 CM238 HD Audio Controller
a182 C620 Series Chipset Family SATA Controller [AHCI mode]
a186 C620 Series Chipset Family SATA Controller [RAID mode]
@@ -36580,6 +36672,7 @@
1028 0869 Vostro 3470
a305 Z390 Chipset LPC/eSPI Controller
a306 Q370 Chipset LPC/eSPI Controller
+ a308 300 Series Chipset Family LPC Controller
a309 Cannon Point-LP LPC Controller
a30c QM370 Chipset LPC/eSPI Controller
a30d HM470 Chipset LPC/eSPI Controller
@@ -36650,10 +36743,12 @@
a3eb Comet Lake PCI Express Root Port #21
a3f0 Comet Lake PCH-V cAVS
a620 6400/6402 Advanced Memory Buffer (AMB)
+ a703 Raptor Lake-S Host Bridge/DRAM Controller
a706 Raptor Lake-P 6p+8e cores Host Bridge/DRAM Controller
1028 0c06 Precision 3580
a707 Raptor Lake-P/U 4p+8e cores Host Bridge/DRAM Controller
a708 Raptor Lake-P/U 2p+8e cores Host Bridge/DRAM Controller
+ a70d Raptor Lake PCI Express 5.0 Graphics Port (PEG010)
a71d Raptor Lake Dynamic Platform and Thermal Framework Processor Participant
1028 0c06 Precision 3580
a71e Raptor Lake-P Thunderbolt 4 USB Controller
@@ -36719,6 +36814,7 @@
abc0 Omni-Path Fabric Switch Silicon 100 Series
ad0b Volume Management Device NVMe RAID Controller Intel Corporation
ad1d Arrow Lake NPU
+ b03e Panther Lake NPU
b152 21152 PCI-to-PCI Bridge
8086 b152 21152 PCI-to-PCI Bridge
# observed, and documented in Intel revision note; new mask of 1011:0026
@@ -36810,9 +36906,10 @@
8088 0420 Qual-Port Ethernet Network Adaptor SF400HT-S
0109 WX1860-LC Gigabit Ethernet Controller
010a WX1860A1 Gigabit Ethernet Controller
- 4c52 2023 LRES2034PT Single-port 1Gb Ethernet Network Adapter
4c52 2026 LRES2026PF Single-port 1Gb Ethernet Network Adapter
+ 4c52 2034 LRES2034PT Single-port 1Gb Ethernet Network Adapter
010b WX1860AL1 Gigabit Ethernet Controller
+ 4c52 2215 LRES2215PT Single-port 1Gb Ethernet Network Adapter
8088 0102 Single-Port Ethernet Network Adaptor SF100HT
8088 4102 Single-Port Ethernet Network Adaptor SF100HT (WOL)
8088 8102 Single-Port Ethernet Network Adaptor SF100HT (NCSI)
@@ -36825,7 +36922,7 @@
011a WX1860A1 Gigabit Ethernet Controller Virtual Function
011b WX1860AL1 Gigabit Ethernet Controller Virtual Function
1000 Ethernet Controller RP1000 Virtual Function for 10GbE SFP+
- 1001 Ethernet Controller RP1000 for 10GbE SFP+
+ 1001 Ethernet Controller SP1000A for 10GbE SFP+
1bd4 0084 Ethernet Controller SP1000A for 10GbE SFP+(lldp)
1bd4 0085 Ethernet Controller SP1000A for 10GBASE-T
4c52 1002 LRES1002PF Dual-port 10Gb Ethernet Server Adapter
@@ -36835,7 +36932,7 @@
8088 0300 Ethernet Network Adaptor RP1000-A03 for 10GbE SFP+
8088 0400 Ethernet Network Adaptor RP1000-A04 for 10GbE SFP+
2000 Ethernet Controller RP2000 Virtual Function for 10GbE SFP+
- 2001 Ethernet Controller RP2000 for 10GbE SFP+
+ 2001 Ethernet Controller WX1820AL for 10GbE SFP+
8088 2000 Ethernet Network Adaptor RP2000 for 10GbE SFP+
8088 2300 Ethernet Network Adaptor RP2000-A03 for 10GbE SFP+
8088 2400 Ethernet Network Adaptor RP2000-A04 for 10GbE SFP+
@@ -36858,6 +36955,7 @@
# Wuxi Micro Innovation Integrated Circuit Design Co.,Ltd.
8848 MUCSE
1000 Ethernet Controller N10 Series for 10GbE or 40GbE (Dual-port)
+ 4c52 3032 LRES3032PF Dual-port 10Gb Ethernet Server Adapter for OCP
8848 8410 Ethernet Network Adapter N10G-X2-DC for 10GbE SFP+ 2-port
1001 Ethernet Controller N400 Series for 1GbE (Dual-port)
1003 Ethernet Controller N400 Series for 10GbE (Single-port)
@@ -36875,12 +36973,18 @@
1081 Ethernet Controller N400 Series Virtual Function
1083 Ethernet Controller N400 Series Virtual Function
8308 Ethernet Controller N500 Series for 1GbE (Quad-port, Copper RJ45)
+# NIC-ETH3M0T-3S-4P Quad-Port RJ45 Adapter for OCP 3.0
+ 193d 1088 NIC-ETH3M0T-3S-4P
4c52 1048 LRES1048PT Quad-port 1Gb Ethernet Network Adapter
+ 4c52 3044 LRES3044PT Quad-port 1Gb Ethernet Server Adapter for OCP
8309 Ethernet Controller N500 Series Virtual Function
8318 Ethernet Controller N500 Series for 1GbE (Dual-port, Copper RJ45)
4c52 1049 LRES1049PT Dual-port 1Gb Ethernet Network Adapter
+ 4c52 3043 LRES3043PT Dual-port 1Gb Ethernet Server Adapter for OCP
8866 T-Square Design Inc.
8888 Silicon Magic
+# 4 port HDMI capture card
+ 8504 AVMatrix VC42
8912 TRX
# 8c4a is not Winbond but there is a board misprogrammed
8c4a Winbond
@@ -37458,6 +37562,9 @@
2001 STAR2000E NVMe SSD
2002 STAR2000C NVMe SSD
2003 STAR2000L NVMe SSD
+ 2004 EAST 2000K SSD
+ 2008 STAR2008 PCIE NVMe SSD Controller
+ 2010 STAR2010 PCIE NVMe Secure SSD Controller
bb5b Asgard AN3+ NVMe SSD
fc22 Asgard AN3+ NVMe SSD
a000 Asix Electronics Corporation (Wrong ID)
@@ -37555,6 +37662,7 @@ c0a9 Micron/Crucial Technology
5412 P5 NVMe PCIe SSD[SlashP5]
5415 T500 NVMe PCIe SSD
5419 T700 NVMe PCIe SSD
+ 5421 P3 Plus NVMe PCIe SSD (DRAM-less)
c0de Motorola
c0fe Motion Engineering, Inc.
ca01 I-TEK OptoElectronics Co., LTD.
@@ -37819,6 +37927,11 @@ edd8 ARK Logic Inc
f043 ASUSTeK Computer Inc. (Wrong ID)
f05b Foxconn International, Inc. (Wrong ID)
f111 Framework Computer Inc.
+f117 Cerio
+ 1000 Emulated PCIe Switch
+ 1010 Placeholder Device
+ 1020 Pseudo-Device
+ 1030 Test Device
f15e SiFive, Inc.
0000 FU740-C000 RISC-V SoC PCI Express x8 to AXI4 Bridge
f1d0 AJA Video
diff --git a/pcilmr.c b/pcilmr.c
index cb8bd77..accee44 100644
--- a/pcilmr.c
+++ b/pcilmr.c
@@ -1,7 +1,7 @@
/*
* The PCI Utilities -- Margining utility main function
*
- * Copyright (c) 2023 KNS Group LLC (YADRO)
+ * Copyright (c) 2023-2024 KNS Group LLC (YADRO)
*
* Can be freely distributed and used under the terms of the GNU GPL v2+.
*
@@ -11,107 +11,11 @@
#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, SCAN };
-
-static const char usage_msg[]
- = "! 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"
- "-l <lane>[,<lane>...]\tSpecify lanes for margining. Default: all link lanes.\n"
- "\t\t\tRemember that Device may use Lane Reversal for Lane numbering.\n"
- "\t\t\tHowever, utility uses logical lane numbers in arguments and for logging.\n"
- "\t\t\tUtility will automatically determine Lane Reversal and tune its calls.\n"
- "-e <errors>\t\tSpecify Error Count Limit for margining. Default: 4.\n"
- "-r <recvn>[,<recvn>...]\tSpecify Receivers to select margining targets.\n"
- "\t\t\tDefault: all available Receivers (including Retimers).\n"
- "-p <parallel_lanes>\tSpecify number of lanes to margin simultaneously.\n"
- "\t\t\tDefault: 1.\n"
- "\t\t\tAccording to spec it's possible for Receiver to margin up\n"
- "\t\t\tto MaxLanes + 1 lanes simultaneously, but usually this works\n"
- "\t\t\tbad, so this option is for experiments mostly.\n"
- "-T\t\t\tTime Margining will continue until the Error Count is no more\n"
- "\t\t\tthan an Error Count Limit. Use this option to find Link limit.\n"
- "-V\t\t\tSame as -T option, but for Voltage.\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"
- "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"
- "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;
- 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 struct pci_dev *
-find_down_port_for_up(struct pci_access *pacc, struct pci_dev *up)
-{
- struct pci_dev *down = NULL;
- for (struct pci_dev *p = pacc->devices; p; p = p->next)
- {
- if (pci_read_byte(p, PCI_SECONDARY_BUS) == up->bus && up->domain == p->domain)
- {
- down = p;
- break;
- }
- }
- return down;
-}
-
-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 void
scan_links(struct pci_access *pacc, bool only_ready)
{
@@ -120,11 +24,13 @@ scan_links(struct pci_access *pacc, bool only_ready)
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)
+ for (struct pci_dev *p = pacc->devices; p; p = p->next)
{
- if (pci_find_cap(up, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED))
+ if (pci_find_cap(p, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED) && margin_port_is_down(p))
{
- struct pci_dev *down = find_down_port_for_up(pacc, up);
+ struct pci_dev *down = NULL;
+ struct pci_dev *up = NULL;
+ margin_find_pair(pacc, p, &down, &up);
if (down && margin_verify_link(down, up))
{
@@ -142,70 +48,21 @@ scan_links(struct pci_access *pacc, bool only_ready)
exit(0);
}
-static u8
-find_ready_links(struct pci_access *pacc, struct pci_dev **down_ports, struct pci_dev **up_ports,
- bool cnt_only)
-{
- u8 cnt = 0;
- 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_check_ready_bit(down) || margin_check_ready_bit(up)))
- {
- if (!cnt_only)
- {
- up_ports[cnt] = up;
- down_ports[cnt] = down;
- }
- cnt++;
- }
- }
- }
- return cnt;
-}
-
int
main(int argc, char **argv)
{
struct pci_access *pacc;
- struct pci_dev **up_ports;
- struct pci_dev **down_ports;
- u8 ports_n = 0;
-
+ u8 links_n = 0;
struct margin_link *links;
bool *checks_status_ports;
- bool status = true;
- enum mode mode;
+ enum margin_mode mode;
/* each link has several receivers -> several results */
struct margin_results **results;
u8 *results_n;
- struct margin_args *args;
-
- u8 steps_t_arg = 0;
- u8 steps_v_arg = 0;
- u8 parallel_lanes_arg = 1;
- u8 error_limit = 4;
- u8 lanes_arg[32];
- u8 recvs_arg[6];
-
- u8 lanes_n = 0;
- u8 recvs_n = 0;
-
- bool run_margin = true;
-
- char *dir_for_csv = NULL;
- bool save_csv = false;
-
- u64 total_steps = 0;
-
pacc = pci_alloc();
pci_init(pacc);
pci_scan_bus(pacc);
@@ -228,8 +85,9 @@ main(int argc, char **argv)
{ .name = "full", .has_arg = no_argument, .flag = NULL, .val = 2 },
{ 0, 0, 0, 0 } };
+ opterr = 0;
int c;
- c = getopt_long(argc, argv, ":", long_options, NULL);
+ c = getopt_long(argc, argv, "+", long_options, NULL);
switch (c)
{
@@ -242,7 +100,8 @@ main(int argc, char **argv)
mode = SCAN;
if (optind == argc)
scan_links(pacc, false);
- optind--;
+ else
+ die("Invalid arguments\n\n%s", usage);
break;
case 2:
mode = FULL;
@@ -253,129 +112,20 @@ main(int argc, char **argv)
break;
}
- while ((c = getopt(argc, argv, ":r:e:l:cp:t:v:VTo:")) != -1)
- {
- switch (c)
- {
- case 't':
- steps_t_arg = atoi(optarg);
- break;
- case 'T':
- steps_t_arg = 63;
- break;
- case 'v':
- steps_v_arg = atoi(optarg);
- break;
- case 'V':
- steps_v_arg = 127;
- break;
- case 'p':
- parallel_lanes_arg = atoi(optarg);
- break;
- case 'c':
- run_margin = false;
- break;
- case 'l':
- lanes_n = parse_csv_arg(optarg, lanes_arg);
- break;
- case 'e':
- error_limit = atoi(optarg);
- break;
- 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);
- }
- }
-
- 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_msg);
- if (!status)
- {
- printf("%s", usage_msg);
- exit(0);
- }
-
- if (mode == FULL)
- {
- ports_n = find_ready_links(pacc, NULL, NULL, true);
- if (ports_n == 0)
- {
- die("Links not found or you don't have enough privileges.\n");
- }
- else
- {
- up_ports = xmalloc(ports_n * sizeof(*up_ports));
- down_ports = xmalloc(ports_n * sizeof(*down_ports));
- find_ready_links(pacc, down_ports, up_ports, false);
- }
- }
- else if (mode == MARGIN)
- {
- ports_n = argc - optind;
- up_ports = xmalloc(ports_n * sizeof(*up_ports));
- down_ports = xmalloc(ports_n * sizeof(*down_ports));
-
- u8 cnt = 0;
- while (optind != argc)
- {
- up_ports[cnt] = dev_for_filter(pacc, argv[optind]);
- down_ports[cnt] = find_down_port_for_up(pacc, up_ports[cnt]);
- if (!down_ports[cnt])
- die("Cannot find Upstream Component for the specified device: %s\n", argv[optind]);
- cnt++;
- optind++;
- }
- }
- else
- die("Bug in the args parsing!\n");
+ opterr = 1;
- if (!pci_find_cap(up_ports[0], PCI_CAP_ID_EXP, PCI_CAP_NORMAL))
- die("Looks like you don't have enough privileges to access "
- "Device Configuration Space.\nTry to run utility as root.\n");
+ links = margin_parse_util_args(pacc, argc, argv, mode, &links_n);
+ struct margin_com_args *com_args = links[0].args.common;
- results = xmalloc(ports_n * sizeof(*results));
- results_n = xmalloc(ports_n * sizeof(*results_n));
- links = xmalloc(ports_n * sizeof(*links));
- checks_status_ports = xmalloc(ports_n * sizeof(*checks_status_ports));
- args = xmalloc(ports_n * sizeof(*args));
+ results = xmalloc(links_n * sizeof(*results));
+ results_n = xmalloc(links_n * sizeof(*results_n));
+ checks_status_ports = xmalloc(links_n * sizeof(*checks_status_ports));
- for (int i = 0; i < ports_n; i++)
+ for (int i = 0; i < links_n; i++)
{
- args[i].error_limit = error_limit;
- args[i].parallel_lanes = parallel_lanes_arg;
- args[i].run_margin = run_margin;
- args[i].verbosity = 1;
- args[i].steps_t = steps_t_arg;
- args[i].steps_v = steps_v_arg;
- for (int j = 0; j < recvs_n; j++)
- args[i].recvs[j] = recvs_arg[j];
- args[i].recvs_n = recvs_n;
- for (int j = 0; j < lanes_n; j++)
- args[i].lanes[j] = lanes_arg[j];
- args[i].lanes_n = lanes_n;
- args[i].steps_utility = &total_steps;
-
enum margin_test_status args_status;
- if (!margin_fill_link(down_ports[i], up_ports[i], &links[i]))
- {
- checks_status_ports[i] = false;
- results[i] = xmalloc(sizeof(*results[i]));
- results[i]->test_status = MARGIN_TEST_PREREQS;
- continue;
- }
-
- if ((args_status = margin_process_args(&links[i].down_port, &args[i])) != MARGIN_TEST_OK)
+ if ((args_status = margin_process_args(&links[i])) != MARGIN_TEST_OK)
{
checks_status_ports[i] = false;
results[i] = xmalloc(sizeof(*results[i]));
@@ -385,49 +135,44 @@ main(int argc, char **argv)
checks_status_ports[i] = true;
struct margin_params params;
+ struct margin_link_args *link_args = &links[i].args;
- for (int j = 0; j < args[i].recvs_n; j++)
+ for (int j = 0; j < link_args->recvs_n; j++)
{
- if (margin_read_params(pacc, args[i].recvs[j] == 6 ? up_ports[i] : down_ports[i],
- args[i].recvs[j], &params))
+ if (margin_read_params(
+ pacc, link_args->recvs[j] == 6 ? links[i].up_port.dev : links[i].down_port.dev,
+ link_args->recvs[j], &params))
{
- u8 steps_t = steps_t_arg ? steps_t_arg : params.timing_steps;
- u8 steps_v = steps_v_arg ? steps_v_arg : params.volt_steps;
- u8 parallel_recv = parallel_lanes_arg > params.max_lanes + 1 ? params.max_lanes + 1 :
- parallel_lanes_arg;
+ u8 steps_t = link_args->steps_t ? link_args->steps_t : params.timing_steps;
+ u8 steps_v = link_args->steps_v ? link_args->steps_v : params.volt_steps;
+ u8 parallel_recv = link_args->parallel_lanes > params.max_lanes + 1 ?
+ params.max_lanes + 1 :
+ link_args->parallel_lanes;
u8 step_multiplier
- = args[i].lanes_n / parallel_recv + ((args[i].lanes_n % parallel_recv) > 0);
+ = link_args->lanes_n / parallel_recv + ((link_args->lanes_n % parallel_recv) > 0);
- total_steps += steps_t * step_multiplier;
+ com_args->steps_utility += steps_t * step_multiplier;
if (params.ind_left_right_tim)
- total_steps += steps_t * step_multiplier;
+ com_args->steps_utility += steps_t * step_multiplier;
if (params.volt_support)
{
- total_steps += steps_v * step_multiplier;
+ com_args->steps_utility += steps_v * step_multiplier;
if (params.ind_up_down_volt)
- total_steps += steps_v * step_multiplier;
+ com_args->steps_utility += steps_v * step_multiplier;
}
}
}
}
- for (int i = 0; i < ports_n; i++)
+ for (int i = 0; i < links_n; i++)
{
if (checks_status_ports[i])
- results[i] = margin_test_link(&links[i], &args[i], &results_n[i]);
+ results[i] = margin_test_link(&links[i], &results_n[i]);
else
{
results_n[i] = 1;
- if (results[i]->test_status == MARGIN_TEST_PREREQS)
- {
- printf("Link ");
- margin_log_bdfs(down_ports[i], up_ports[i]);
- printf(" 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");
- }
- else if (results[i]->test_status == MARGIN_TEST_ARGS_RECVS)
+ if (results[i]->test_status == MARGIN_TEST_ARGS_RECVS)
{
margin_log_link(&links[i]);
printf("\nInvalid RecNums specified.\n");
@@ -441,40 +186,34 @@ main(int argc, char **argv)
printf("\n----\n\n");
}
- if (run_margin)
+ if (com_args->run_margin)
{
printf("Results:\n");
- printf("\nPass/fail criteria:\nTiming:\n");
- printf("Minimum Offset (spec): %d %% UI\nRecommended Offset: %d %% UI\n", MARGIN_TIM_MIN,
- MARGIN_TIM_RECOMMEND);
- printf("\nVoltage:\nMinimum Offset (spec): %d mV\n\n", MARGIN_VOLT_MIN);
printf(
"Margining statuses:\nLIM -\tErrorCount exceeded Error Count Limit (found device limit)\n");
printf("NAK -\tDevice didn't execute last command, \n\tso result may be less reliable\n");
printf("THR -\tThe set (using the utility options) \n\tstep threshold has been reached\n\n");
printf("Notations:\nst - steps\n\n");
- for (int i = 0; i < ports_n; i++)
+ for (int i = 0; i < links_n; i++)
{
printf("Link ");
- margin_log_bdfs(down_ports[i], up_ports[i]);
+ margin_log_bdfs(links[i].down_port.dev, links[i].up_port.dev);
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]);
+ margin_results_print_brief(results[i], results_n[i], &links[i].args);
+ if (com_args->save_csv)
+ margin_results_save_csv(results[i], results_n[i], &links[i]);
printf("\n");
}
}
- for (int i = 0; i < ports_n; i++)
+ for (int i = 0; i < links_n; i++)
margin_free_results(results[i], results_n[i]);
free(results_n);
free(results);
- free(up_ports);
- free(down_ports);
+ free(com_args);
free(links);
free(checks_status_ports);
- free(args);
pci_cleanup(pacc);
return 0;
diff --git a/pcilmr.man b/pcilmr.man
index 673262f..b2f3161 100644
--- a/pcilmr.man
+++ b/pcilmr.man
@@ -4,10 +4,10 @@ pcilmr \- margin PCIe Links
.SH SYNOPSIS
.B pcilmr
.RB [ "--margin" ]
-.RI [ "<margining options>" ] " <downstream component> ..."
+.RI [ "<common options>" ] " <link port> " [ "<link options>" "] [" "<link port> " [ "<link options>" ] " ..." ]
.br
.B pcilmr --full
-.RI [ "<margining options>" ]
+.RI [ "<common options>" ]
.br
.B pcilmr --scan
.SH CONFIGURATION
@@ -64,26 +64,81 @@ Utility allows to get an approximation of the eye margin diagram in the form of
links without a hardware debugger and without the need to stop the target system. Utility
can be useful to debug link issues due to receiver margins.
-However, the utility results may be not particularly accurate and, as it was found out during
-testing, specific devices provide rather dubious capability support and the reliability of
-the information they provide is questionable. The PCIe specification provides reference values
-for the eye diagram, which are also used by the
+.B pcilmr
+requires root privileges (to access Extended Configuration Space), but during our testing
+there were no problems with the devices and they successfully returned to their normal initial
+state after the end of testing.
+
+.SH RESULTS GRADING
+The PCIe specification provides reference values for the eye diagram, which are also used by the
.B pcilmr
to evaluate the results, but it seems that it makes sense to contact the
manufacturer of a particular device for references.
-The PCIe Base Specification Revision 5.0 sets allowed range for Timing Margin from 20%\~UI to 50%\~UI and
-for Voltage Margin from 50\~mV to 500\~mV. Utility uses 30%\~UI as the recommended
-value for Timing - taken from NVIDIA presentation ("PCIe 4.0 Mass Electrical Margins Data
-Collection").
+The utility uses values set in PCIe Base Spec Rev. 5.0 Section 8.4.2 as the default eye width and height
+minimum references. Recommended values were taken from
+the PCIe Architecture PHY Test Spec Rev 5.0 (Transmitter Electrical Compliance).
+
+Reference grading values currently used by the utility are presented in the table below:
+
+.TS
+box tab(:);
+C | Cb S | Cb S
+C | Cb | Cb | Cb | Cb
+Lb | C | C | C | C.
+\&:16 GT/s (Gen 4):32 GT/s (Gen 5)
+\&:EW:EH:EW:EH
+_
+Min:T{
+18.75 ps
+.br
+30% UI
+T}:15 mV:T{
+9.375 ps
+.br
+30% UI
+T}:15 mV
+_
+Rec:T{
+23.75 ps
+.br
+38% UI
+T}:21 mV:T{
+10.157 ps
+.br
+33% UI
+T}:19.75 mV
+.TE
.B pcilmr
-requires root privileges (to access Extended Configuration Space), but during our testing
-there were no problems with the devices and they successfully returned to their normal initial
-state after the end of testing.
+uses full eye width and height values to grade lanes. However, it is possible that
+device supports only one side margining. In such cases by default utility will
+calculate EW or EH as a double one side result.
+
+If info for specific device is available, you can configure grading criteria
+and tweak utility behavior in one-side only cases for your device using
+.I -g
+link specific option (see below).
+
+.SH HARDWARE QUIRKS SUPPORT
+
+Thanks to testing or directly from the manufacturer's documentation, we know that
+some devices require special treatment during the margining.
+Utility detects such devices based on their Vendor ID - Device ID pair.
+Right now the list of special devices is hardcoded in
+.I margin_hw
+file. For such devices utility can automatically adjust port margining parameters
+or grading options.
+
+For example, for Ice Lake CPUs RC ports
+.B pcilmr
+will change device MaxVoltageOffset value and will force the use of
+.RI ' "one side is the whole" "' grading mode."
.SH OPTIONS
.SS Device Specifier
+.B "You can specify Downstream or Upstream Port of the Link."
+.TP
.B "<device/component>" \t
.RI [ "<domain>" :] <bus> : <dev> . <func>
(see
@@ -102,13 +157,29 @@ option) Links in the system (one by one).
Scan for Links with negotiated speed 16 GT/s or higher. Mark "Ready" those of them
in which at least one of the Link sides have Margining Ready bit set meaning that
these Links are ready for testing and you can run utility on them.
-.SS Margining Test options
-.TP
+.SS Margining Common (for all specified links) options
.B -c
Print Device Lane Margining Capabilities only. Do not run margining.
.TP
+.BI -e " <errors>"
+Specify Error Count Limit for margining.
+.br
+Default: 4.
+.TP
+.BI -o " <directory>"
+Save margining results in csv form into the specified directory. Utility
+will generate file with the name in form of
+.RI "\[dq]lmr_" "<port>" "_Rx" # _ <timestamp> ".csv\[dq]"
+for each successfully tested receiver.
+.TP
+.BI -d " <time>"
+Specify dwell time in seconds for the margining step.
+.br
+Default: 1 s
+.SS Margining Link specific options
+.TP
\fB\-l\fI <lane>\fP[\fI,<lane>...\fP]
-Specify lanes for margining.
+.R Specify lanes for margining.
.br
Remember that Device may use Lane Reversal for Lane numbering. However, utility
uses logical lane numbers in arguments and for logging. Utility will automatically
@@ -116,11 +187,6 @@ determine Lane Reversal and tune its calls.
.br
Default: all link lanes.
.TP
-.BI -e " <errors>"
-Specify Error Count Limit for margining.
-.br
-Default: 4.
-.TP
\fB-r\fI <recvn>\fP[\fI,<recvn>...\fP]
Specify Receivers to select margining targets.
.br
@@ -157,18 +223,34 @@ option, but for Voltage.
.TP
.BI -v " <steps>"
Specify maximum number of steps for Voltage Margining.
-.SS Margining Log options
.TP
-.BI -o " <directory>"
-Save margining results in csv form into the specified directory. Utility
-will generate file with the name in form of
-.RI "\[dq]lmr_" "<downstream component>" "_Rx" # _ <timestamp> ".csv\[dq]"
-for each successfully tested receiver.
+\fB-g\fI <recvn>\fPt=\fI<criteria>\fP{%|ps}[,f]
+.TP
+.IB " <recvn>" t=f[, "<criteria>" "{%|ps}]"
+.TP
+.IB " <recvn>" v= "<criteria>" "[,f]"
+.TP
+.IB " <recvn>" v=f[, "<criteria>" ]
+Specify pass/fail grading criteria for eye width (timing - t) or height
+(voltage - v) for one of the link receivers. For EW you must choose one of the
+units (% UI or ps), for EH mV is used.
+.br
+Additional flag
+.I f
+is for situations when port doesn't support two side independent
+margining. In such cases by default utility will calculate EW or EH as a
+double one side result. You can add
+.I f
+flag for
+.I -g
+option to tell the utility that the result in one direction is actually the
+measurement of the full eye and it does not need to be multiplied. This is so called
+.RI ' "one side is the whole" "' grading mode."
.SH EXAMPLES
Utility syntax example:
.RS
-.BI "pcilmr -l" " 0,1 " "-r" " 1,6 " "-TV" " ab:0.0 52:0.0"
+.BI "pcilmr -o " "csv ab:0.0 " "-r " "1,6 " "-g " "1t=20% " "-g " "1v=f,30 52:0.0 " "-l " "0,1,2 " "-TV"
.RE
.UR https://gist.github.com/bombanya/f2b15263712757ffba1a11eea011c419