summaryrefslogtreecommitdiffstats
path: root/lmr/margin_hw.c
diff options
context:
space:
mode:
Diffstat (limited to 'lmr/margin_hw.c')
-rw-r--r--lmr/margin_hw.c77
1 files changed, 61 insertions, 16 deletions
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);