/* * The PCI Utilities -- Margining utility main header * * 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 */ #ifndef _LMR_H #define _LMR_H #include #include "pciutils.h" 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 neg_width; u8 max_width; u8 retimers_n; u8 link_speed; enum margin_hw hw; /* Saved Device settings to restore after margining */ u8 aspm; bool hasd; // Hardware Autonomous Speed Disable bool hawd; // Hardware Autonomous Width Disable }; /* Specification Revision 5.0 Table 8-11 */ struct margin_params { bool ind_error_sampler; bool sample_report_method; bool ind_left_right_tim; bool ind_up_down_volt; bool volt_support; u8 max_lanes; u8 timing_steps; u8 timing_offset; u8 volt_steps; u8 volt_offset; u8 sample_rate_v; u8 sample_rate_t; }; /* Step Margin Execution Status - Step command response */ enum margin_step_exec_sts { MARGIN_NAK = 0, // NAK/Set up for margin MARGIN_LIM, // Too many errors (device limit) MARGIN_THR // Test threshold has been reached }; enum margin_dir { VOLT_UP = 0, VOLT_DOWN, TIM_LEFT, TIM_RIGHT }; /* Margining results of one lane of the receiver */ struct margin_res_lane { u8 lane; u8 steps[4]; enum margin_step_exec_sts statuses[4]; }; /* Reason not to run margining test on the Link/Receiver */ enum margin_test_status { MARGIN_TEST_OK = 0, MARGIN_TEST_READY_BIT, MARGIN_TEST_CAPS, // Couldn't run test MARGIN_TEST_PREREQS, MARGIN_TEST_ARGS_LANES, MARGIN_TEST_ARGS_RECVS, MARGIN_TEST_ASPM }; /* All lanes Receiver results */ struct margin_results { u8 recvn; // Receiver Number; from 1 to 6 struct margin_params params; bool lane_reversal; u8 link_speed; enum margin_test_status test_status; /* Used to convert steps to physical quantity. Calculated from MaxOffset and NumSteps */ double tim_coef; // from steps to % UI double volt_coef; bool tim_off_reported; bool volt_off_reported; u8 lanes_n; struct margin_res_lane *lanes; }; /* pcilmr arguments */ // 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 recvs[6]; // Receivers Numbers u8 recvs_n; // 0 == margin all available receivers struct margin_recv_args recv_args[6]; u8 lanes[32]; // Lanes to Margin u8 lanes_n; // 0 == margin all available lanes }; 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; from 1 to 6 bool lane_reversal; struct margin_params *params; u8 parallel_lanes; u8 error_limit; u8 dwell_time; }; struct margin_lanes_data { struct margin_recv *recv; struct margin_res_lane *results; u8 *lanes_numbers; u8 lanes_n; bool ind; enum margin_dir dir; u8 steps_lane_done; u8 steps_lane_total; u64 *steps_utility; 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); /* Check Margining Ready bit from Margining Port Status Register */ bool margin_check_ready_bit(struct pci_dev *dev); /* Verify link and fill wrappers */ bool margin_fill_link(struct pci_dev *down_port, struct pci_dev *up_port, struct margin_link *wrappers); /* Disable ASPM, set Hardware Autonomous Speed/Width Disable bits */ bool margin_prep_link(struct margin_link *link); /* Restore ASPM, Hardware Autonomous Speed/Width settings */ void margin_restore_link(struct margin_link *link); /* margin */ /* Fill margin_params without calling other functions */ 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_link *link); /* 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, u8 *recvs_n); void margin_free_results(struct margin_results *results, u8 results_n); /* margin_log */ extern bool margin_global_logging; extern bool margin_print_domain; 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, neg_width, speed) */ void margin_log_link(struct margin_link *link); void margin_log_params(struct margin_params *params); /* Print receiver number */ void margin_log_recvn(struct margin_recv *recv); /* Print full info from Receiver struct */ void margin_log_receiver(struct margin_recv *recv); /* Margining in progress log */ void margin_log_margining(struct margin_lanes_data arg); void margin_log_hw_quirks(struct margin_recv *recv); /* margin_results */ // 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, struct margin_link *link); #endif