]> mj.ucw.cz Git - pciutils.git/blob - lmr/margin_hw.c
Merge pull request #177 from OscarL/cache-loc-on-man-pages
[pciutils.git] / lmr / margin_hw.c
1 /*
2  *      The PCI Utilities -- Verify and prepare devices before margining
3  *
4  *      Copyright (c) 2023 KNS Group LLC (YADRO)
5  *
6  *      Can be freely distributed and used under the terms of the GNU GPL v2+.
7  *
8  *      SPDX-License-Identifier: GPL-2.0-or-later
9  */
10
11 #include "lmr.h"
12
13 static u16 special_hw[][4] =
14   // Vendor ID, Device ID, Revision ID, margin_hw
15   { { 0x8086, 0x347A, 0x4, MARGIN_ICE_LAKE_RC }, 
16     { 0xFFFF, 0, 0, MARGIN_HW_DEFAULT } 
17   };
18
19 static enum margin_hw
20 detect_unique_hw(struct pci_dev *dev)
21 {
22   u16 vendor = pci_read_word(dev, PCI_VENDOR_ID);
23   u16 device = pci_read_word(dev, PCI_DEVICE_ID);
24   u8 revision = pci_read_byte(dev, PCI_REVISION_ID);
25
26   for (int i = 0; special_hw[i][0] != 0xFFFF; i++)
27     {
28       if (vendor == special_hw[i][0] && device == special_hw[i][1] && revision == special_hw[i][2])
29         return special_hw[i][3];
30     }
31   return MARGIN_HW_DEFAULT;
32 }
33
34 bool
35 margin_verify_link(struct pci_dev *down_port, struct pci_dev *up_port)
36 {
37   struct pci_cap *cap = pci_find_cap(down_port, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
38   if (!cap)
39     return false;
40   if ((pci_read_word(down_port, cap->addr + PCI_EXP_LNKSTA) & PCI_EXP_LNKSTA_SPEED) < 4)
41     return false;
42   if ((pci_read_word(down_port, cap->addr + PCI_EXP_LNKSTA) & PCI_EXP_LNKSTA_SPEED) > 5)
43     return false;
44
45   u8 down_type = pci_read_byte(down_port, PCI_HEADER_TYPE) & 0x7F;
46   u8 down_sec = pci_read_byte(down_port, PCI_SECONDARY_BUS);
47   u8 down_dir
48     = GET_REG_MASK(pci_read_word(down_port, cap->addr + PCI_EXP_FLAGS), PCI_EXP_FLAGS_TYPE);
49
50   // Verify that devices are linked, down_port is Root Port or Downstream Port of Switch,
51   // up_port is Function 0 of a Device
52   if (!(down_sec == up_port->bus && down_type == PCI_HEADER_TYPE_BRIDGE
53         && (down_dir == PCI_EXP_TYPE_ROOT_PORT || down_dir == PCI_EXP_TYPE_DOWNSTREAM)
54         && up_port->func == 0))
55     return false;
56
57   struct pci_cap *pm = pci_find_cap(up_port, PCI_CAP_ID_PM, PCI_CAP_NORMAL);
58   return pm && !(pci_read_word(up_port, pm->addr + PCI_PM_CTRL) & PCI_PM_CTRL_STATE_MASK); // D0
59 }
60
61 bool
62 margin_check_ready_bit(struct pci_dev *dev)
63 {
64   struct pci_cap *lmr = pci_find_cap(dev, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED);
65   return lmr && (pci_read_word(dev, lmr->addr + PCI_LMR_PORT_STS) & PCI_LMR_PORT_STS_READY);
66 }
67
68 /* Awaits device at 16 GT/s or higher */
69 static struct margin_dev
70 fill_dev_wrapper(struct pci_dev *dev)
71 {
72   struct pci_cap *cap = pci_find_cap(dev, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
73   struct margin_dev res
74     = { .dev = dev,
75         .lmr_cap_addr = pci_find_cap(dev, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED)->addr,
76         .width = GET_REG_MASK(pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA), PCI_EXP_LNKSTA_WIDTH),
77         .retimers_n
78         = (!!(pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA2) & PCI_EXP_LINKSTA2_RETIMER))
79           + (!!(pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA2) & PCI_EXP_LINKSTA2_2RETIMERS)),
80         .link_speed = (pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA) & PCI_EXP_LNKSTA_SPEED),
81         .hw = detect_unique_hw(dev) };
82   return res;
83 }
84
85 bool
86 margin_fill_link(struct pci_dev *down_port, struct pci_dev *up_port, struct margin_link *wrappers)
87 {
88   if (!margin_verify_link(down_port, up_port))
89     return false;
90   wrappers->down_port = fill_dev_wrapper(down_port);
91   wrappers->up_port = fill_dev_wrapper(up_port);
92   return true;
93 }
94
95 /* Disable ASPM, set Hardware Autonomous Speed/Width Disable bits */
96 static bool
97 margin_prep_dev(struct margin_dev *dev)
98 {
99   struct pci_cap *pcie = pci_find_cap(dev->dev, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
100   if (!pcie)
101     return false;
102
103   u16 lnk_ctl = pci_read_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL);
104   dev->aspm = lnk_ctl & PCI_EXP_LNKCTL_ASPM;
105   dev->hawd = !!(lnk_ctl & PCI_EXP_LNKCTL_HWAUTWD);
106   lnk_ctl &= ~PCI_EXP_LNKCTL_ASPM;
107   pci_write_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL, lnk_ctl);
108   if (pci_read_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL) & PCI_EXP_LNKCTL_ASPM)
109     return false;
110
111   lnk_ctl |= PCI_EXP_LNKCTL_HWAUTWD;
112   pci_write_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL, lnk_ctl);
113
114   u16 lnk_ctl2 = pci_read_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL2);
115   dev->hasd = !!(lnk_ctl2 & PCI_EXP_LNKCTL2_SPEED_DIS);
116   lnk_ctl2 |= PCI_EXP_LNKCTL2_SPEED_DIS;
117   pci_write_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL2, lnk_ctl2);
118
119   return true;
120 }
121
122 /* Restore Device ASPM, Hardware Autonomous Speed/Width settings */
123 static void
124 margin_restore_dev(struct margin_dev *dev)
125 {
126   struct pci_cap *pcie = pci_find_cap(dev->dev, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
127   if (!pcie)
128     return;
129
130   u16 lnk_ctl = pci_read_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL);
131   lnk_ctl = SET_REG_MASK(lnk_ctl, PCI_EXP_LNKCAP_ASPM, dev->aspm);
132   lnk_ctl = SET_REG_MASK(lnk_ctl, PCI_EXP_LNKCTL_HWAUTWD, dev->hawd);
133   pci_write_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL, lnk_ctl);
134
135   u16 lnk_ctl2 = pci_read_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL2);
136   lnk_ctl2 = SET_REG_MASK(lnk_ctl2, PCI_EXP_LNKCTL2_SPEED_DIS, dev->hasd);
137   pci_write_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL2, lnk_ctl2);
138 }
139
140 bool
141 margin_prep_link(struct margin_link *link)
142 {
143   if (!link)
144     return false;
145   if (!margin_prep_dev(&link->down_port))
146     return false;
147   if (!margin_prep_dev(&link->up_port))
148     {
149       margin_restore_dev(&link->down_port);
150       return false;
151     }
152   return true;
153 }
154
155 void
156 margin_restore_link(struct margin_link *link)
157 {
158   margin_restore_dev(&link->down_port);
159   margin_restore_dev(&link->up_port);
160 }