diff options
Diffstat (limited to '')
-rw-r--r-- | lmr/margin_hw.c | 77 |
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); |