2 * The PCI Utilities -- Verify and prepare devices before margining
4 * Copyright (c) 2023 KNS Group LLC (YADRO)
6 * Can be freely distributed and used under the terms of the GNU GPL v2+.
8 * SPDX-License-Identifier: GPL-2.0-or-later
14 margin_verify_link(struct pci_dev *down_port, struct pci_dev *up_port)
16 struct pci_cap *cap = pci_find_cap(down_port, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
19 if ((pci_read_word(down_port, cap->addr + PCI_EXP_LNKSTA) & PCI_EXP_LNKSTA_SPEED) < 4)
21 if ((pci_read_word(down_port, cap->addr + PCI_EXP_LNKSTA) & PCI_EXP_LNKSTA_SPEED) > 5)
24 u8 down_type = pci_read_byte(down_port, PCI_HEADER_TYPE) & 0x7F;
25 u8 down_sec = pci_read_byte(down_port, PCI_SECONDARY_BUS);
27 = GET_REG_MASK(pci_read_word(down_port, cap->addr + PCI_EXP_FLAGS), PCI_EXP_FLAGS_TYPE);
29 // Verify that devices are linked, down_port is Root Port or Downstream Port of Switch,
30 // up_port is Function 0 of a Device
31 if (!(down_sec == up_port->bus && down_type == PCI_HEADER_TYPE_BRIDGE
32 && (down_dir == PCI_EXP_TYPE_ROOT_PORT || down_dir == PCI_EXP_TYPE_DOWNSTREAM)
33 && up_port->func == 0))
36 struct pci_cap *pm = pci_find_cap(up_port, PCI_CAP_ID_PM, PCI_CAP_NORMAL);
37 return pm && !(pci_read_word(up_port, pm->addr + PCI_PM_CTRL) & PCI_PM_CTRL_STATE_MASK); // D0
41 margin_check_ready_bit(struct pci_dev *dev)
43 struct pci_cap *lmr = pci_find_cap(dev, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED);
44 return lmr && (pci_read_word(dev, lmr->addr + PCI_LMR_PORT_STS) & PCI_LMR_PORT_STS_READY);
47 /* Awaits device at 16 GT/s or higher */
48 static struct margin_dev
49 fill_dev_wrapper(struct pci_dev *dev)
51 struct pci_cap *cap = pci_find_cap(dev, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
54 .lmr_cap_addr = pci_find_cap(dev, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED)->addr,
55 .width = GET_REG_MASK(pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA), PCI_EXP_LNKSTA_WIDTH),
57 = (!!(pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA2) & PCI_EXP_LINKSTA2_RETIMER))
58 + (!!(pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA2) & PCI_EXP_LINKSTA2_2RETIMERS)),
59 .link_speed = (pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA) & PCI_EXP_LNKSTA_SPEED) };
64 margin_fill_link(struct pci_dev *down_port, struct pci_dev *up_port, struct margin_link *wrappers)
66 if (!margin_verify_link(down_port, up_port))
68 wrappers->down_port = fill_dev_wrapper(down_port);
69 wrappers->up_port = fill_dev_wrapper(up_port);
73 /* Disable ASPM, set Hardware Autonomous Speed/Width Disable bits */
75 margin_prep_dev(struct margin_dev *dev)
77 struct pci_cap *pcie = pci_find_cap(dev->dev, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
81 u16 lnk_ctl = pci_read_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL);
82 dev->aspm = lnk_ctl & PCI_EXP_LNKCTL_ASPM;
83 dev->hawd = !!(lnk_ctl & PCI_EXP_LNKCTL_HWAUTWD);
84 lnk_ctl &= ~PCI_EXP_LNKCTL_ASPM;
85 pci_write_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL, lnk_ctl);
86 if (pci_read_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL) & PCI_EXP_LNKCTL_ASPM)
89 lnk_ctl |= PCI_EXP_LNKCTL_HWAUTWD;
90 pci_write_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL, lnk_ctl);
92 u16 lnk_ctl2 = pci_read_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL2);
93 dev->hasd = !!(lnk_ctl2 & PCI_EXP_LNKCTL2_SPEED_DIS);
94 lnk_ctl2 |= PCI_EXP_LNKCTL2_SPEED_DIS;
95 pci_write_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL2, lnk_ctl2);
100 /* Restore Device ASPM, Hardware Autonomous Speed/Width settings */
102 margin_restore_dev(struct margin_dev *dev)
104 struct pci_cap *pcie = pci_find_cap(dev->dev, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
108 u16 lnk_ctl = pci_read_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL);
109 lnk_ctl = SET_REG_MASK(lnk_ctl, PCI_EXP_LNKCAP_ASPM, dev->aspm);
110 lnk_ctl = SET_REG_MASK(lnk_ctl, PCI_EXP_LNKCTL_HWAUTWD, dev->hawd);
111 pci_write_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL, lnk_ctl);
113 u16 lnk_ctl2 = pci_read_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL2);
114 lnk_ctl2 = SET_REG_MASK(lnk_ctl2, PCI_EXP_LNKCTL2_SPEED_DIS, dev->hasd);
115 pci_write_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL2, lnk_ctl2);
119 margin_prep_link(struct margin_link *link)
123 if (!margin_prep_dev(&link->down_port))
125 if (!margin_prep_dev(&link->up_port))
127 margin_restore_dev(&link->down_port);
134 margin_restore_link(struct margin_link *link)
136 margin_restore_dev(&link->down_port);
137 margin_restore_dev(&link->up_port);