]> mj.ucw.cz Git - pciutils.git/blob - lmr/margin_hw.c
43fa1bdcc7069dcd0aa71ce0325dffe31407794a
[pciutils.git] / lmr / margin_hw.c
1 /*
2  *      The PCI Utilities -- Verify and prepare devices before margining
3  *
4  *      Copyright (c) 2023-2024 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_port_is_down(struct pci_dev *dev)
36 {
37   struct pci_cap *cap = pci_find_cap(dev, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
38   if (!cap)
39     return false;
40   u8 type = pci_read_byte(dev, PCI_HEADER_TYPE) & 0x7F;
41   u8 dir = GET_REG_MASK(pci_read_word(dev, cap->addr + PCI_EXP_FLAGS), PCI_EXP_FLAGS_TYPE);
42
43   if (type == PCI_HEADER_TYPE_BRIDGE
44       && (dir == PCI_EXP_TYPE_ROOT_PORT || dir == PCI_EXP_TYPE_DOWNSTREAM))
45     return true;
46   else
47     return false;
48 }
49
50 bool
51 margin_find_pair(struct pci_access *pacc, struct pci_dev *dev, struct pci_dev **down_port,
52                  struct pci_dev **up_port)
53 {
54   struct pci_cap *cap = pci_find_cap(dev, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
55   if (!cap)
56     return false;
57   bool given_down = margin_port_is_down(dev);
58
59   for (struct pci_dev *p = pacc->devices; p; p = p->next)
60     {
61       if (given_down && pci_read_byte(dev, PCI_SECONDARY_BUS) == p->bus && dev->domain == p->domain
62           && p->func == 0)
63         {
64           *down_port = dev;
65           *up_port = p;
66           return true;
67         }
68       else if (!given_down && pci_read_byte(p, PCI_SECONDARY_BUS) == dev->bus
69                && dev->domain == p->domain)
70         {
71           *down_port = p;
72           *up_port = dev;
73           return true;
74         }
75     }
76   return false;
77 }
78
79 bool
80 margin_verify_link(struct pci_dev *down_port, struct pci_dev *up_port)
81 {
82   struct pci_cap *cap = pci_find_cap(down_port, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
83   if (!cap)
84     return false;
85   if ((pci_read_word(down_port, cap->addr + PCI_EXP_LNKSTA) & PCI_EXP_LNKSTA_SPEED) < 4)
86     return false;
87   if ((pci_read_word(down_port, cap->addr + PCI_EXP_LNKSTA) & PCI_EXP_LNKSTA_SPEED) > 5)
88     return false;
89
90   u8 down_sec = pci_read_byte(down_port, PCI_SECONDARY_BUS);
91
92   // Verify that devices are linked, down_port is Root Port or Downstream Port of Switch,
93   // up_port is Function 0 of a Device
94   if (!(down_sec == up_port->bus && margin_port_is_down(down_port) && up_port->func == 0))
95     return false;
96
97   struct pci_cap *pm = pci_find_cap(up_port, PCI_CAP_ID_PM, PCI_CAP_NORMAL);
98   return pm && !(pci_read_word(up_port, pm->addr + PCI_PM_CTRL) & PCI_PM_CTRL_STATE_MASK); // D0
99 }
100
101 bool
102 margin_check_ready_bit(struct pci_dev *dev)
103 {
104   struct pci_cap *lmr = pci_find_cap(dev, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED);
105   return lmr && (pci_read_word(dev, lmr->addr + PCI_LMR_PORT_STS) & PCI_LMR_PORT_STS_READY);
106 }
107
108 /* Awaits device at 16 GT/s or higher */
109 static struct margin_dev
110 fill_dev_wrapper(struct pci_dev *dev)
111 {
112   struct pci_cap *cap = pci_find_cap(dev, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
113   struct margin_dev res
114     = { .dev = dev,
115         .lmr_cap_addr = pci_find_cap(dev, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED)->addr,
116         .width = GET_REG_MASK(pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA), PCI_EXP_LNKSTA_WIDTH),
117         .retimers_n
118         = (!!(pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA2) & PCI_EXP_LINKSTA2_RETIMER))
119           + (!!(pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA2) & PCI_EXP_LINKSTA2_2RETIMERS)),
120         .link_speed = (pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA) & PCI_EXP_LNKSTA_SPEED),
121         .hw = detect_unique_hw(dev) };
122   return res;
123 }
124
125 bool
126 margin_fill_link(struct pci_dev *down_port, struct pci_dev *up_port, struct margin_link *wrappers)
127 {
128   if (!margin_verify_link(down_port, up_port))
129     return false;
130   wrappers->down_port = fill_dev_wrapper(down_port);
131   wrappers->up_port = fill_dev_wrapper(up_port);
132   return true;
133 }
134
135 /* Disable ASPM, set Hardware Autonomous Speed/Width Disable bits */
136 static bool
137 margin_prep_dev(struct margin_dev *dev)
138 {
139   struct pci_cap *pcie = pci_find_cap(dev->dev, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
140   if (!pcie)
141     return false;
142
143   u16 lnk_ctl = pci_read_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL);
144   dev->aspm = lnk_ctl & PCI_EXP_LNKCTL_ASPM;
145   dev->hawd = !!(lnk_ctl & PCI_EXP_LNKCTL_HWAUTWD);
146   lnk_ctl &= ~PCI_EXP_LNKCTL_ASPM;
147   pci_write_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL, lnk_ctl);
148   if (pci_read_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL) & PCI_EXP_LNKCTL_ASPM)
149     return false;
150
151   lnk_ctl |= PCI_EXP_LNKCTL_HWAUTWD;
152   pci_write_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL, lnk_ctl);
153
154   u16 lnk_ctl2 = pci_read_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL2);
155   dev->hasd = !!(lnk_ctl2 & PCI_EXP_LNKCTL2_SPEED_DIS);
156   lnk_ctl2 |= PCI_EXP_LNKCTL2_SPEED_DIS;
157   pci_write_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL2, lnk_ctl2);
158
159   return true;
160 }
161
162 /* Restore Device ASPM, Hardware Autonomous Speed/Width settings */
163 static void
164 margin_restore_dev(struct margin_dev *dev)
165 {
166   struct pci_cap *pcie = pci_find_cap(dev->dev, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
167   if (!pcie)
168     return;
169
170   u16 lnk_ctl = pci_read_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL);
171   lnk_ctl = SET_REG_MASK(lnk_ctl, PCI_EXP_LNKCAP_ASPM, dev->aspm);
172   lnk_ctl = SET_REG_MASK(lnk_ctl, PCI_EXP_LNKCTL_HWAUTWD, dev->hawd);
173   pci_write_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL, lnk_ctl);
174
175   u16 lnk_ctl2 = pci_read_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL2);
176   lnk_ctl2 = SET_REG_MASK(lnk_ctl2, PCI_EXP_LNKCTL2_SPEED_DIS, dev->hasd);
177   pci_write_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL2, lnk_ctl2);
178 }
179
180 bool
181 margin_prep_link(struct margin_link *link)
182 {
183   if (!link)
184     return false;
185   if (!margin_prep_dev(&link->down_port))
186     return false;
187   if (!margin_prep_dev(&link->up_port))
188     {
189       margin_restore_dev(&link->down_port);
190       return false;
191     }
192   return true;
193 }
194
195 void
196 margin_restore_link(struct margin_link *link)
197 {
198   margin_restore_dev(&link->down_port);
199   margin_restore_dev(&link->up_port);
200 }