@@ -57,6 +57,26 @@ union margin_cmd margin_make_cmd(uint8_t payload, uint8_t type, uint8_t recvn)
.type = type, .reserved = 0}};
}
+void margin_apply_hw_quirks(struct margin_recv *recv)
+{
+ switch (recv->dev->hw)
+ {
+ case MARGIN_ICE_LAKE:
+ {
+ struct pci_cap *cap = pci_find_cap(recv->dev->dev, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
+ uint8_t dev_type = (pci_read_word(recv->dev->dev, cap->addr + PCI_EXP_FLAGS) & PCI_EXP_FLAGS_TYPE) >> 4;
+ /* Downstream Port - Root Port */
+ if (recv->recvn == 1 && dev_type == 4)
+ {
+ recv->volt_offset = 12;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
bool margin_read_params(struct margin_recv *recv)
{
union margin_payload resp;
@@ -249,6 +269,7 @@ bool margin_receiver(struct margin_recv *recv, struct margin_args *args, struct
{
if (recv->parallel_lanes > recv->max_lanes + 1)
recv->parallel_lanes = recv->max_lanes + 1;
+ margin_apply_hw_quirks(recv);
results->tim_coef = (double)recv->timing_offset / (double)recv->timing_steps;
results->volt_coef = (double)recv->volt_offset / (double)recv->volt_steps * 10.0;
@@ -378,6 +399,7 @@ struct margin_results *margin_link(struct margin_dev *down_port, struct margin_d
uint8_t *receivers = args->recvs;
margin_log_link(down_port, up_port);
+ margin_log_hw_quirks(down_port);
struct margin_results *results = malloc(sizeof(*results) * receivers_n);
struct margin_recv receiver;
@@ -168,6 +168,8 @@ struct margin_lanes_data {
union margin_cmd margin_make_cmd(uint8_t payload, uint8_t type, uint8_t recvn);
+void margin_apply_hw_quirks(struct margin_recv *recv);
+
/*Read Receiver Lane Margining capabilities.
dev, recvn and lane_reversal fields must be initialized*/
bool margin_read_params(struct margin_recv *recv);
@@ -4,6 +4,83 @@
#include "margin_hw.h"
+union cpuinfo {
+ uint8_t buf[2];
+
+ struct cpu_fields {
+ uint8_t x86_family;
+ uint8_t x86_model;
+ } fields;
+};
+
+enum cpuinfo_field {
+ x86_FAMILY = 0,
+ x86_MODEL = 1
+};
+
+static char *const cpu_field_names[2] = {"cpu family", "model"};
+
+static bool read_cpuinfo(union cpuinfo *cpuinfo)
+{
+ FILE *cpu_file = fopen("/proc/cpuinfo", "r");
+ if (!cpu_file)
+ return false;
+
+ size_t n = 0;
+ char *line = NULL;
+ uint32_t read_fields = 0;
+ uint32_t req_fields = 0;
+ char *line_colon;
+
+ while (getline(&line, &n, cpu_file) > 0 && req_fields == 0)
+ {
+ if (strstr(line, "vendor_id") && strstr(line, "GenuineIntel"))
+ {
+ req_fields = 1 << x86_FAMILY | 1 << x86_MODEL;
+ }
+ }
+
+ rewind(cpu_file);
+ while (getline(&line, &n, cpu_file) > 0 && req_fields != read_fields)
+ {
+ for (uint8_t i = 0; i < sizeof(cpu_field_names) / sizeof(cpu_field_names[0]); i++)
+ {
+ if ((req_fields & (1 << i)) && !(read_fields & (1 << i)) && strstr(line, cpu_field_names[i]))
+ {
+ read_fields |= 1 << i;
+ line_colon = strchr(line, ':');
+ cpuinfo->buf[i] = atoi(line_colon + 1);
+ }
+ }
+ }
+
+ free(line);
+ fclose(cpu_file);
+ if (!req_fields)
+ return false;
+ return req_fields == read_fields;
+}
+
+static enum margin_hw detect_unique_hw(void)
+{
+ union cpuinfo cpuinfo;
+ memset(cpuinfo.buf, 0, sizeof(cpuinfo.buf));
+ if (!read_cpuinfo(&cpuinfo))
+ return MARGIN_HW_DEFAULT;
+
+ uint8_t x86_family = cpuinfo.fields.x86_family;
+ uint8_t x86_model = cpuinfo.fields.x86_model;
+
+ /* Intel Ice Lake */
+ if (x86_family == 6 &&
+ (x86_model == 126 || x86_model == 108 || x86_model == 106))
+ {
+ return MARGIN_ICE_LAKE;
+ }
+
+ return MARGIN_HW_DEFAULT;
+}
+
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);
@@ -43,7 +120,8 @@ struct margin_dev margin_fill_wrapper(struct pci_dev *dev)
.width = (pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA) & PCI_EXP_LNKSTA_WIDTH) >> 4,
.retimers_n = ((pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA2) & PCI_EXP_LINKSTA2_RETIMER) > 0) +
((pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA2) & PCI_EXP_LINKSTA2_2RETIMERS) > 0),
- .link_speed = (pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA) & PCI_EXP_LNKSTA_SPEED)};
+ .link_speed = (pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA) & PCI_EXP_LNKSTA_SPEED),
+ .hw = detect_unique_hw()};
return res;
}
@@ -6,6 +6,11 @@
#include "../lib/pci.h"
+enum margin_hw {
+ MARGIN_HW_DEFAULT,
+ MARGIN_ICE_LAKE
+};
+
/*PCI Device wrapper for margining functions*/
struct margin_dev
{
@@ -15,6 +20,8 @@ struct margin_dev
uint8_t retimers_n;
uint8_t link_speed;
+ enum margin_hw hw;
+
/*Saved Device settings to restore after margining*/
uint16_t aspm;
uint16_t hasd; /*Hardware Autonomous Speed Disable*/
@@ -113,3 +113,18 @@ void margin_log_margining(struct margin_lanes_data arg)
fflush(stdout);
}
}
+
+void margin_log_hw_quirks(struct margin_dev *dev)
+{
+ switch (dev->hw)
+ {
+ case MARGIN_ICE_LAKE:
+ margin_log("\nRunning on Intel Ice Lake CPU.\n"
+ "Applying next quirks for margining process:\n"
+ " - Set MaxVoltageOffset of Rx(A) to 12 (120 mV).\n");
+ break;
+
+ default:
+ break;
+ }
+}
@@ -18,4 +18,6 @@ void margin_log_receiver(struct margin_recv *recv);
void margin_log_margining(struct margin_lanes_data arg);
+void margin_log_hw_quirks(struct margin_dev *dev);
+
#endif