xref: /aosp_15_r20/external/pciutils/lmr/margin_hw.c (revision c2e0c6b56a71da9abe8df5c8348fb3eb5c2c9251)
1*c2e0c6b5SAndroid Build Coastguard Worker /*
2*c2e0c6b5SAndroid Build Coastguard Worker  *	The PCI Utilities -- Verify and prepare devices before margining
3*c2e0c6b5SAndroid Build Coastguard Worker  *
4*c2e0c6b5SAndroid Build Coastguard Worker  *	Copyright (c) 2023-2024 KNS Group LLC (YADRO)
5*c2e0c6b5SAndroid Build Coastguard Worker  *
6*c2e0c6b5SAndroid Build Coastguard Worker  *	Can be freely distributed and used under the terms of the GNU GPL v2+.
7*c2e0c6b5SAndroid Build Coastguard Worker  *
8*c2e0c6b5SAndroid Build Coastguard Worker  *	SPDX-License-Identifier: GPL-2.0-or-later
9*c2e0c6b5SAndroid Build Coastguard Worker  */
10*c2e0c6b5SAndroid Build Coastguard Worker 
11*c2e0c6b5SAndroid Build Coastguard Worker #include <memory.h>
12*c2e0c6b5SAndroid Build Coastguard Worker 
13*c2e0c6b5SAndroid Build Coastguard Worker #include "lmr.h"
14*c2e0c6b5SAndroid Build Coastguard Worker 
15*c2e0c6b5SAndroid Build Coastguard Worker static u16 special_hw[][4] =
16*c2e0c6b5SAndroid Build Coastguard Worker   // Vendor ID, Device ID, Revision ID, margin_hw
17*c2e0c6b5SAndroid Build Coastguard Worker   { { 0x8086, 0x347A, 0x4, MARGIN_ICE_LAKE_RC },
18*c2e0c6b5SAndroid Build Coastguard Worker     { 0xFFFF, 0, 0, MARGIN_HW_DEFAULT }
19*c2e0c6b5SAndroid Build Coastguard Worker   };
20*c2e0c6b5SAndroid Build Coastguard Worker 
21*c2e0c6b5SAndroid Build Coastguard Worker static enum margin_hw
detect_unique_hw(struct pci_dev * dev)22*c2e0c6b5SAndroid Build Coastguard Worker detect_unique_hw(struct pci_dev *dev)
23*c2e0c6b5SAndroid Build Coastguard Worker {
24*c2e0c6b5SAndroid Build Coastguard Worker   u16 vendor = pci_read_word(dev, PCI_VENDOR_ID);
25*c2e0c6b5SAndroid Build Coastguard Worker   u16 device = pci_read_word(dev, PCI_DEVICE_ID);
26*c2e0c6b5SAndroid Build Coastguard Worker   u8 revision = pci_read_byte(dev, PCI_REVISION_ID);
27*c2e0c6b5SAndroid Build Coastguard Worker 
28*c2e0c6b5SAndroid Build Coastguard Worker   for (int i = 0; special_hw[i][0] != 0xFFFF; i++)
29*c2e0c6b5SAndroid Build Coastguard Worker     {
30*c2e0c6b5SAndroid Build Coastguard Worker       if (vendor == special_hw[i][0] && device == special_hw[i][1] && revision == special_hw[i][2])
31*c2e0c6b5SAndroid Build Coastguard Worker         return special_hw[i][3];
32*c2e0c6b5SAndroid Build Coastguard Worker     }
33*c2e0c6b5SAndroid Build Coastguard Worker   return MARGIN_HW_DEFAULT;
34*c2e0c6b5SAndroid Build Coastguard Worker }
35*c2e0c6b5SAndroid Build Coastguard Worker 
36*c2e0c6b5SAndroid Build Coastguard Worker bool
margin_port_is_down(struct pci_dev * dev)37*c2e0c6b5SAndroid Build Coastguard Worker margin_port_is_down(struct pci_dev *dev)
38*c2e0c6b5SAndroid Build Coastguard Worker {
39*c2e0c6b5SAndroid Build Coastguard Worker   struct pci_cap *cap = pci_find_cap(dev, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
40*c2e0c6b5SAndroid Build Coastguard Worker   if (!cap)
41*c2e0c6b5SAndroid Build Coastguard Worker     return false;
42*c2e0c6b5SAndroid Build Coastguard Worker   u8 type = pci_read_byte(dev, PCI_HEADER_TYPE) & 0x7F;
43*c2e0c6b5SAndroid Build Coastguard Worker   u8 dir = GET_REG_MASK(pci_read_word(dev, cap->addr + PCI_EXP_FLAGS), PCI_EXP_FLAGS_TYPE);
44*c2e0c6b5SAndroid Build Coastguard Worker 
45*c2e0c6b5SAndroid Build Coastguard Worker   if (type == PCI_HEADER_TYPE_BRIDGE
46*c2e0c6b5SAndroid Build Coastguard Worker       && (dir == PCI_EXP_TYPE_ROOT_PORT || dir == PCI_EXP_TYPE_DOWNSTREAM))
47*c2e0c6b5SAndroid Build Coastguard Worker     return true;
48*c2e0c6b5SAndroid Build Coastguard Worker   else
49*c2e0c6b5SAndroid Build Coastguard Worker     return false;
50*c2e0c6b5SAndroid Build Coastguard Worker }
51*c2e0c6b5SAndroid Build Coastguard Worker 
52*c2e0c6b5SAndroid Build Coastguard Worker bool
margin_find_pair(struct pci_access * pacc,struct pci_dev * dev,struct pci_dev ** down_port,struct pci_dev ** up_port)53*c2e0c6b5SAndroid Build Coastguard Worker margin_find_pair(struct pci_access *pacc, struct pci_dev *dev, struct pci_dev **down_port,
54*c2e0c6b5SAndroid Build Coastguard Worker                  struct pci_dev **up_port)
55*c2e0c6b5SAndroid Build Coastguard Worker {
56*c2e0c6b5SAndroid Build Coastguard Worker   struct pci_cap *cap = pci_find_cap(dev, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
57*c2e0c6b5SAndroid Build Coastguard Worker   if (!cap)
58*c2e0c6b5SAndroid Build Coastguard Worker     return false;
59*c2e0c6b5SAndroid Build Coastguard Worker   bool given_down = margin_port_is_down(dev);
60*c2e0c6b5SAndroid Build Coastguard Worker 
61*c2e0c6b5SAndroid Build Coastguard Worker   for (struct pci_dev *p = pacc->devices; p; p = p->next)
62*c2e0c6b5SAndroid Build Coastguard Worker     {
63*c2e0c6b5SAndroid Build Coastguard Worker       if (given_down && pci_read_byte(dev, PCI_SECONDARY_BUS) == p->bus && dev->domain == p->domain
64*c2e0c6b5SAndroid Build Coastguard Worker           && p->func == 0)
65*c2e0c6b5SAndroid Build Coastguard Worker         {
66*c2e0c6b5SAndroid Build Coastguard Worker           *down_port = dev;
67*c2e0c6b5SAndroid Build Coastguard Worker           *up_port = p;
68*c2e0c6b5SAndroid Build Coastguard Worker           return true;
69*c2e0c6b5SAndroid Build Coastguard Worker         }
70*c2e0c6b5SAndroid Build Coastguard Worker       else if (!given_down && pci_read_byte(p, PCI_SECONDARY_BUS) == dev->bus
71*c2e0c6b5SAndroid Build Coastguard Worker                && dev->domain == p->domain)
72*c2e0c6b5SAndroid Build Coastguard Worker         {
73*c2e0c6b5SAndroid Build Coastguard Worker           *down_port = p;
74*c2e0c6b5SAndroid Build Coastguard Worker           *up_port = dev;
75*c2e0c6b5SAndroid Build Coastguard Worker           return true;
76*c2e0c6b5SAndroid Build Coastguard Worker         }
77*c2e0c6b5SAndroid Build Coastguard Worker     }
78*c2e0c6b5SAndroid Build Coastguard Worker   return false;
79*c2e0c6b5SAndroid Build Coastguard Worker }
80*c2e0c6b5SAndroid Build Coastguard Worker 
81*c2e0c6b5SAndroid Build Coastguard Worker bool
margin_verify_link(struct pci_dev * down_port,struct pci_dev * up_port)82*c2e0c6b5SAndroid Build Coastguard Worker margin_verify_link(struct pci_dev *down_port, struct pci_dev *up_port)
83*c2e0c6b5SAndroid Build Coastguard Worker {
84*c2e0c6b5SAndroid Build Coastguard Worker   struct pci_cap *cap = pci_find_cap(down_port, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
85*c2e0c6b5SAndroid Build Coastguard Worker   if (!cap)
86*c2e0c6b5SAndroid Build Coastguard Worker     return false;
87*c2e0c6b5SAndroid Build Coastguard Worker   if ((pci_read_word(down_port, cap->addr + PCI_EXP_LNKSTA) & PCI_EXP_LNKSTA_SPEED) < 4)
88*c2e0c6b5SAndroid Build Coastguard Worker     return false;
89*c2e0c6b5SAndroid Build Coastguard Worker   if ((pci_read_word(down_port, cap->addr + PCI_EXP_LNKSTA) & PCI_EXP_LNKSTA_SPEED) > 5)
90*c2e0c6b5SAndroid Build Coastguard Worker     return false;
91*c2e0c6b5SAndroid Build Coastguard Worker 
92*c2e0c6b5SAndroid Build Coastguard Worker   u8 down_sec = pci_read_byte(down_port, PCI_SECONDARY_BUS);
93*c2e0c6b5SAndroid Build Coastguard Worker 
94*c2e0c6b5SAndroid Build Coastguard Worker   // Verify that devices are linked, down_port is Root Port or Downstream Port of Switch,
95*c2e0c6b5SAndroid Build Coastguard Worker   // up_port is Function 0 of a Device
96*c2e0c6b5SAndroid Build Coastguard Worker   if (!(down_sec == up_port->bus && margin_port_is_down(down_port) && up_port->func == 0))
97*c2e0c6b5SAndroid Build Coastguard Worker     return false;
98*c2e0c6b5SAndroid Build Coastguard Worker 
99*c2e0c6b5SAndroid Build Coastguard Worker   struct pci_cap *pm = pci_find_cap(up_port, PCI_CAP_ID_PM, PCI_CAP_NORMAL);
100*c2e0c6b5SAndroid Build Coastguard Worker   return pm && !(pci_read_word(up_port, pm->addr + PCI_PM_CTRL) & PCI_PM_CTRL_STATE_MASK); // D0
101*c2e0c6b5SAndroid Build Coastguard Worker }
102*c2e0c6b5SAndroid Build Coastguard Worker 
103*c2e0c6b5SAndroid Build Coastguard Worker bool
margin_check_ready_bit(struct pci_dev * dev)104*c2e0c6b5SAndroid Build Coastguard Worker margin_check_ready_bit(struct pci_dev *dev)
105*c2e0c6b5SAndroid Build Coastguard Worker {
106*c2e0c6b5SAndroid Build Coastguard Worker   struct pci_cap *lmr = pci_find_cap(dev, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED);
107*c2e0c6b5SAndroid Build Coastguard Worker   return lmr && (pci_read_word(dev, lmr->addr + PCI_LMR_PORT_STS) & PCI_LMR_PORT_STS_READY);
108*c2e0c6b5SAndroid Build Coastguard Worker }
109*c2e0c6b5SAndroid Build Coastguard Worker 
110*c2e0c6b5SAndroid Build Coastguard Worker /* Awaits device at 16 GT/s or higher */
111*c2e0c6b5SAndroid Build Coastguard Worker static struct margin_dev
fill_dev_wrapper(struct pci_dev * dev)112*c2e0c6b5SAndroid Build Coastguard Worker fill_dev_wrapper(struct pci_dev *dev)
113*c2e0c6b5SAndroid Build Coastguard Worker {
114*c2e0c6b5SAndroid Build Coastguard Worker   struct pci_cap *cap = pci_find_cap(dev, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
115*c2e0c6b5SAndroid Build Coastguard Worker   struct margin_dev res = {
116*c2e0c6b5SAndroid Build Coastguard Worker     .dev = dev,
117*c2e0c6b5SAndroid Build Coastguard Worker     .lmr_cap_addr = pci_find_cap(dev, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED)->addr,
118*c2e0c6b5SAndroid Build Coastguard Worker     .neg_width = GET_REG_MASK(pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA), PCI_EXP_LNKSTA_WIDTH),
119*c2e0c6b5SAndroid Build Coastguard Worker     .max_width = GET_REG_MASK(pci_read_long(dev, cap->addr + PCI_EXP_LNKCAP), PCI_EXP_LNKCAP_WIDTH),
120*c2e0c6b5SAndroid Build Coastguard Worker     .retimers_n
121*c2e0c6b5SAndroid Build Coastguard Worker     = (!!(pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA2) & PCI_EXP_LINKSTA2_RETIMER))
122*c2e0c6b5SAndroid Build Coastguard Worker       + (!!(pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA2) & PCI_EXP_LINKSTA2_2RETIMERS)),
123*c2e0c6b5SAndroid Build Coastguard Worker     .link_speed = (pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA) & PCI_EXP_LNKSTA_SPEED),
124*c2e0c6b5SAndroid Build Coastguard Worker     .hw = detect_unique_hw(dev)
125*c2e0c6b5SAndroid Build Coastguard Worker   };
126*c2e0c6b5SAndroid Build Coastguard Worker   return res;
127*c2e0c6b5SAndroid Build Coastguard Worker }
128*c2e0c6b5SAndroid Build Coastguard Worker 
129*c2e0c6b5SAndroid Build Coastguard Worker bool
margin_fill_link(struct pci_dev * down_port,struct pci_dev * up_port,struct margin_link * wrappers)130*c2e0c6b5SAndroid Build Coastguard Worker margin_fill_link(struct pci_dev *down_port, struct pci_dev *up_port, struct margin_link *wrappers)
131*c2e0c6b5SAndroid Build Coastguard Worker {
132*c2e0c6b5SAndroid Build Coastguard Worker   memset(wrappers, 0, sizeof(*wrappers));
133*c2e0c6b5SAndroid Build Coastguard Worker   if (!margin_verify_link(down_port, up_port))
134*c2e0c6b5SAndroid Build Coastguard Worker     return false;
135*c2e0c6b5SAndroid Build Coastguard Worker   wrappers->down_port = fill_dev_wrapper(down_port);
136*c2e0c6b5SAndroid Build Coastguard Worker   wrappers->up_port = fill_dev_wrapper(up_port);
137*c2e0c6b5SAndroid Build Coastguard Worker   return true;
138*c2e0c6b5SAndroid Build Coastguard Worker }
139*c2e0c6b5SAndroid Build Coastguard Worker 
140*c2e0c6b5SAndroid Build Coastguard Worker /* Disable ASPM, set Hardware Autonomous Speed/Width Disable bits */
141*c2e0c6b5SAndroid Build Coastguard Worker static bool
margin_prep_dev(struct margin_dev * dev)142*c2e0c6b5SAndroid Build Coastguard Worker margin_prep_dev(struct margin_dev *dev)
143*c2e0c6b5SAndroid Build Coastguard Worker {
144*c2e0c6b5SAndroid Build Coastguard Worker   struct pci_cap *pcie = pci_find_cap(dev->dev, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
145*c2e0c6b5SAndroid Build Coastguard Worker   if (!pcie)
146*c2e0c6b5SAndroid Build Coastguard Worker     return false;
147*c2e0c6b5SAndroid Build Coastguard Worker 
148*c2e0c6b5SAndroid Build Coastguard Worker   u16 lnk_ctl = pci_read_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL);
149*c2e0c6b5SAndroid Build Coastguard Worker   dev->aspm = lnk_ctl & PCI_EXP_LNKCTL_ASPM;
150*c2e0c6b5SAndroid Build Coastguard Worker   dev->hawd = !!(lnk_ctl & PCI_EXP_LNKCTL_HWAUTWD);
151*c2e0c6b5SAndroid Build Coastguard Worker   lnk_ctl &= ~PCI_EXP_LNKCTL_ASPM;
152*c2e0c6b5SAndroid Build Coastguard Worker   pci_write_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL, lnk_ctl);
153*c2e0c6b5SAndroid Build Coastguard Worker   if (pci_read_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL) & PCI_EXP_LNKCTL_ASPM)
154*c2e0c6b5SAndroid Build Coastguard Worker     return false;
155*c2e0c6b5SAndroid Build Coastguard Worker 
156*c2e0c6b5SAndroid Build Coastguard Worker   lnk_ctl |= PCI_EXP_LNKCTL_HWAUTWD;
157*c2e0c6b5SAndroid Build Coastguard Worker   pci_write_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL, lnk_ctl);
158*c2e0c6b5SAndroid Build Coastguard Worker 
159*c2e0c6b5SAndroid Build Coastguard Worker   u16 lnk_ctl2 = pci_read_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL2);
160*c2e0c6b5SAndroid Build Coastguard Worker   dev->hasd = !!(lnk_ctl2 & PCI_EXP_LNKCTL2_SPEED_DIS);
161*c2e0c6b5SAndroid Build Coastguard Worker   lnk_ctl2 |= PCI_EXP_LNKCTL2_SPEED_DIS;
162*c2e0c6b5SAndroid Build Coastguard Worker   pci_write_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL2, lnk_ctl2);
163*c2e0c6b5SAndroid Build Coastguard Worker 
164*c2e0c6b5SAndroid Build Coastguard Worker   return true;
165*c2e0c6b5SAndroid Build Coastguard Worker }
166*c2e0c6b5SAndroid Build Coastguard Worker 
167*c2e0c6b5SAndroid Build Coastguard Worker /* Restore Device ASPM, Hardware Autonomous Speed/Width settings */
168*c2e0c6b5SAndroid Build Coastguard Worker static void
margin_restore_dev(struct margin_dev * dev)169*c2e0c6b5SAndroid Build Coastguard Worker margin_restore_dev(struct margin_dev *dev)
170*c2e0c6b5SAndroid Build Coastguard Worker {
171*c2e0c6b5SAndroid Build Coastguard Worker   struct pci_cap *pcie = pci_find_cap(dev->dev, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
172*c2e0c6b5SAndroid Build Coastguard Worker   if (!pcie)
173*c2e0c6b5SAndroid Build Coastguard Worker     return;
174*c2e0c6b5SAndroid Build Coastguard Worker 
175*c2e0c6b5SAndroid Build Coastguard Worker   u16 lnk_ctl = pci_read_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL);
176*c2e0c6b5SAndroid Build Coastguard Worker   lnk_ctl = SET_REG_MASK(lnk_ctl, PCI_EXP_LNKCAP_ASPM, dev->aspm);
177*c2e0c6b5SAndroid Build Coastguard Worker   lnk_ctl = SET_REG_MASK(lnk_ctl, PCI_EXP_LNKCTL_HWAUTWD, dev->hawd);
178*c2e0c6b5SAndroid Build Coastguard Worker   pci_write_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL, lnk_ctl);
179*c2e0c6b5SAndroid Build Coastguard Worker 
180*c2e0c6b5SAndroid Build Coastguard Worker   u16 lnk_ctl2 = pci_read_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL2);
181*c2e0c6b5SAndroid Build Coastguard Worker   lnk_ctl2 = SET_REG_MASK(lnk_ctl2, PCI_EXP_LNKCTL2_SPEED_DIS, dev->hasd);
182*c2e0c6b5SAndroid Build Coastguard Worker   pci_write_word(dev->dev, pcie->addr + PCI_EXP_LNKCTL2, lnk_ctl2);
183*c2e0c6b5SAndroid Build Coastguard Worker }
184*c2e0c6b5SAndroid Build Coastguard Worker 
185*c2e0c6b5SAndroid Build Coastguard Worker bool
margin_prep_link(struct margin_link * link)186*c2e0c6b5SAndroid Build Coastguard Worker margin_prep_link(struct margin_link *link)
187*c2e0c6b5SAndroid Build Coastguard Worker {
188*c2e0c6b5SAndroid Build Coastguard Worker   if (!link)
189*c2e0c6b5SAndroid Build Coastguard Worker     return false;
190*c2e0c6b5SAndroid Build Coastguard Worker   if (!margin_prep_dev(&link->down_port))
191*c2e0c6b5SAndroid Build Coastguard Worker     return false;
192*c2e0c6b5SAndroid Build Coastguard Worker   if (!margin_prep_dev(&link->up_port))
193*c2e0c6b5SAndroid Build Coastguard Worker     {
194*c2e0c6b5SAndroid Build Coastguard Worker       margin_restore_dev(&link->down_port);
195*c2e0c6b5SAndroid Build Coastguard Worker       return false;
196*c2e0c6b5SAndroid Build Coastguard Worker     }
197*c2e0c6b5SAndroid Build Coastguard Worker   return true;
198*c2e0c6b5SAndroid Build Coastguard Worker }
199*c2e0c6b5SAndroid Build Coastguard Worker 
200*c2e0c6b5SAndroid Build Coastguard Worker void
margin_restore_link(struct margin_link * link)201*c2e0c6b5SAndroid Build Coastguard Worker margin_restore_link(struct margin_link *link)
202*c2e0c6b5SAndroid Build Coastguard Worker {
203*c2e0c6b5SAndroid Build Coastguard Worker   margin_restore_dev(&link->down_port);
204*c2e0c6b5SAndroid Build Coastguard Worker   margin_restore_dev(&link->up_port);
205*c2e0c6b5SAndroid Build Coastguard Worker }
206