From edb119b465853321fd5f7b60bf08029c5107c607 Mon Sep 17 00:00:00 2001 From: Kurtis Bohlen Date: Wed, 6 May 2026 23:03:28 +0000 Subject: [PATCH] pcilmr: Add PCIe Gen 6 (64 GT/s) support - Extend speed-indexed arrays in lmr.h to support link_speed=6 - Allow Gen 6 link speed in margin_verify_link() - Fix NULL dereference crash in margin_fill_link() when either port lacks LMR capability - Fix scan_links() and find_ready_links() to check up port has LMR capability before reporting link as available - Fix GT/s display formula in margin_log_link() for Gen 6 - Update user-facing strings to include 64 GT/s --- lmr/lmr.h | 12 ++++++------ lmr/margin_args.c | 3 ++- lmr/margin_hw.c | 6 +++++- lmr/margin_log.c | 2 +- lmr/margin_results.c | 2 +- pcilmr.c | 2 +- 6 files changed, 16 insertions(+), 11 deletions(-) diff --git a/lmr/lmr.h b/lmr/lmr.h index 98df17a..d9995e0 100644 --- a/lmr/lmr.h +++ b/lmr/lmr.h @@ -18,7 +18,7 @@ enum margin_hw { MARGIN_HW_DEFAULT, MARGIN_ICE_LAKE_RC }; // in ps -static const double margin_ui[] = { 62.5, 31.25 }; +static const double margin_ui[] = { 62.5, 31.25, 31.25 }; /* PCI Device wrapper for margining functions */ struct margin_dev { @@ -194,7 +194,7 @@ bool margin_port_is_down(struct pci_dev *dev); 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 */ +/* Verify that devices form the link with valid data rate */ bool margin_verify_link(struct pci_dev *down_port, struct pci_dev *up_port); /* Check Margining Ready bit from Margining Port Status Register */ @@ -258,11 +258,11 @@ void margin_log_hw_quirks(struct margin_recv *recv); // (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_ew_min[] = { 18.75, 9.375, 9.375 }; +static const double margin_ew_rec[] = { 23.75, 10.1565, 10.1565 }; -static const double margin_eh_min[] = { 15, 15 }; -static const double margin_eh_rec[] = { 21, 19.75 }; +static const double margin_eh_min[] = { 15, 15, 5 }; +static const double margin_eh_rec[] = { 21, 19.75, 7 }; void margin_results_print_brief(struct margin_results *results, u8 recvs_n, struct margin_link_args *args); diff --git a/lmr/margin_args.c b/lmr/margin_args.c index 57a1d0a..a1adbf0 100644 --- a/lmr/margin_args.c +++ b/lmr/margin_args.c @@ -86,6 +86,7 @@ find_ready_links(struct pci_access *pacc, struct margin_link *links, bool cnt_on margin_find_pair(pacc, p, &down, &up); if (down && margin_verify_link(down, up) + && pci_find_cap(up, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED) && (margin_check_ready_bit(down) || margin_check_ready_bit(up))) { if (!cnt_only) @@ -284,7 +285,7 @@ margin_parse_util_args(struct pci_access *pacc, int argc, char **argv, enum marg { 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" + "Link data rate must be 16 GT/s, 32 GT/s, or 64 GT/s.\n" "Downstream Component must be at D0 PM state.\n", err); } diff --git a/lmr/margin_hw.c b/lmr/margin_hw.c index c6785f1..47102a3 100644 --- a/lmr/margin_hw.c +++ b/lmr/margin_hw.c @@ -87,7 +87,7 @@ margin_verify_link(struct pci_dev *down_port, struct pci_dev *up_port) return false; if ((pci_read_word(down_port, cap->addr + PCI_EXP_LNKSTA) & PCI_EXP_LNKSTA_SPEED) < 4) return false; - if ((pci_read_word(down_port, cap->addr + PCI_EXP_LNKSTA) & PCI_EXP_LNKSTA_SPEED) > 5) + if ((pci_read_word(down_port, cap->addr + PCI_EXP_LNKSTA) & PCI_EXP_LNKSTA_SPEED) > 6) return false; u8 down_sec = pci_read_byte(down_port, PCI_SECONDARY_BUS); @@ -133,6 +133,10 @@ margin_fill_link(struct pci_dev *down_port, struct pci_dev *up_port, struct marg memset(wrappers, 0, sizeof(*wrappers)); if (!margin_verify_link(down_port, up_port)) return false; + if (!pci_find_cap(down_port, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED)) + return false; + if (!pci_find_cap(up_port, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED)) + return false; wrappers->down_port = fill_dev_wrapper(down_port); wrappers->up_port = fill_dev_wrapper(up_port); return true; diff --git a/lmr/margin_log.c b/lmr/margin_log.c index 2cb01c8..ce8381b 100644 --- a/lmr/margin_log.c +++ b/lmr/margin_log.c @@ -54,7 +54,7 @@ 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.neg_width); - margin_log("Link Speed: %d.0 GT/s = Gen %d\n", (link->down_port.link_speed - 3) * 16, + margin_log("Link Speed: %d.0 GT/s = Gen %d\n", 16 << (link->down_port.link_speed - 4), link->down_port.link_speed); margin_log("Available receivers: "); int receivers_n = 2 + 2 * link->down_port.retimers_n; diff --git a/lmr/margin_results.c b/lmr/margin_results.c index b0c5c26..9543100 100644 --- a/lmr/margin_results.c +++ b/lmr/margin_results.c @@ -64,7 +64,7 @@ margin_results_print_brief(struct margin_results *results, u8 recvs_n, char *no_test_msgs[] = { "", "Margining Ready bit is Clear", "Error during caps reading", - "Margining prerequisites are not satisfied (16/32 GT/s, D0)", + "Margining prerequisites are not satisfied (supported rate, D0)", "Invalid lanes specified with arguments", "Invalid receivers specified with arguments", "Couldn't disable ASPM" }; diff --git a/pcilmr.c b/pcilmr.c index accee44..b9844c7 100644 --- a/pcilmr.c +++ b/pcilmr.c @@ -32,7 +32,7 @@ scan_links(struct pci_access *pacc, bool only_ready) struct pci_dev *up = NULL; margin_find_pair(pacc, p, &down, &up); - if (down && margin_verify_link(down, up)) + if (down && margin_verify_link(down, up) && pci_find_cap(up, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED)) { margin_log_bdfs(down, up); if (!only_ready && (margin_check_ready_bit(down) || margin_check_ready_bit(up))) -- 2.47.3