/*
* 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+.
*
/* 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;
/* 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;
/* 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;
/* 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);
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)
{
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))
/*
* 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+.
*
return MARGIN_HW_DEFAULT;
}
+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)
{
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);
/*
* 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+.
*
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)
{
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))
{
bool cnt_only)
{
u8 cnt = 0;
- 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)
&& (margin_check_ready_bit(down) || margin_check_ready_bit(up)))
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]);
+ struct pci_dev *dev = dev_for_filter(pacc, argv[optind]);
+ if (!margin_find_pair(pacc, dev, &(down_ports[cnt]), &(up_ports[cnt])))
+ die("Cannot find pair for the specified device: %s\n", argv[optind]);
cnt++;
optind++;
}