1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 #include <sys/note.h>
23 #include <sys/conf.h>
24 #include <sys/debug.h>
25 #include <sys/sunddi.h>
26 #include <sys/pci.h>
27 #include <sys/pcie.h>
28 #include <sys/bitmap.h>
29 #include <sys/autoconf.h>
30 #include <sys/sysmacros.h>
31 #include <sys/pci_cap.h>
32
33 /*
34 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
35 * Use is subject to license terms.
36 * Copyright 2022 Oxide Computer Company
37 */
38
39 /*
40 * Generic PCI Capabilites Interface for all pci platforms
41 */
42
43 #ifdef DEBUG
44 uint_t pci_cap_debug = 0;
45 #endif
46
47 /* Cap Base Macro */
48 #define PCI_CAP_BASE(h, id, base_p) (*base_p ? DDI_SUCCESS : \
49 (id ? PCI_CAP_LOCATE(h, id, base_p) : DDI_FAILURE))
50
51 /*
52 * pci_cap_probe: returns the capid and base based upon a given index
53 */
54 int
pci_cap_probe(ddi_acc_handle_t h,uint16_t index,uint32_t * id_p,uint16_t * base_p)55 pci_cap_probe(ddi_acc_handle_t h, uint16_t index, uint32_t *id_p,
56 uint16_t *base_p)
57 {
58 int i, search_ext = 0;
59 uint16_t base, pcix_cmd, status;
60 uint32_t id, xcaps_hdr; /* Extended Caps Header Word */
61
62 status = pci_config_get16(h, PCI_CONF_STAT);
63
64 if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
65 return (DDI_FAILURE);
66
67 /* PCIE and PCIX Version 2 contain Extended Config Space */
68 for (i = 0, base = pci_config_get8(h, PCI_CONF_CAP_PTR);
69 base && i < index; base = pci_config_get8(h, base
70 + PCI_CAP_NEXT_PTR), i++) {
71
72 if ((id = pci_config_get8(h, base)) == 0xff)
73 break;
74
75 if (id == PCI_CAP_ID_PCI_E)
76 search_ext = 1;
77 else if (id == PCI_CAP_ID_PCIX) {
78 if ((pcix_cmd = pci_config_get16(h, base +
79 PCI_PCIX_COMMAND)) != PCI_CAP_EINVAL16)
80 continue;
81 if ((pcix_cmd & PCI_PCIX_VER_MASK) == PCI_PCIX_VER_2)
82 search_ext = 1;
83 }
84 }
85
86 if (base && i == index) {
87 if ((id = pci_config_get8(h, base)) != 0xff)
88 goto found;
89 }
90
91 if (!search_ext)
92 return (DDI_FAILURE);
93
94 for (base = PCIE_EXT_CAP; base && i < index; i++) {
95 if ((xcaps_hdr = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
96 break;
97
98 id = (xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT)
99 & PCIE_EXT_CAP_ID_MASK;
100 base = (xcaps_hdr >> PCIE_EXT_CAP_NEXT_PTR_SHIFT)
101 & PCIE_EXT_CAP_NEXT_PTR_MASK;
102 }
103
104 if (!base || i < index)
105 return (DDI_FAILURE);
106
107 if ((xcaps_hdr = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
108 return (DDI_FAILURE);
109
110 id = ((xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT) & PCIE_EXT_CAP_ID_MASK) |
111 PCI_CAP_XCFG_FLAG;
112 found:
113 PCI_CAP_DBG("pci_cap_probe: index=%x, id=%x, base=%x\n",
114 index, id, base);
115
116 *id_p = id;
117 *base_p = base;
118 return (DDI_SUCCESS);
119
120 }
121
122 /*
123 * pci_lcap_locate: Helper function locates a base in conventional config space.
124 */
125 int
pci_lcap_locate(ddi_acc_handle_t h,uint8_t id,uint16_t * base_p)126 pci_lcap_locate(ddi_acc_handle_t h, uint8_t id, uint16_t *base_p)
127 {
128 uint8_t header;
129 uint16_t status, base, ncaps;
130
131 status = pci_config_get16(h, PCI_CONF_STAT);
132
133 if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
134 return (DDI_FAILURE);
135
136 header = pci_config_get8(h, PCI_CONF_HEADER);
137 switch (header & PCI_HEADER_TYPE_M) {
138 case PCI_HEADER_ZERO:
139 base = PCI_CONF_CAP_PTR;
140 break;
141 case PCI_HEADER_PPB:
142 base = PCI_BCNF_CAP_PTR;
143 break;
144 case PCI_HEADER_CARDBUS:
145 base = PCI_CBUS_CAP_PTR;
146 break;
147 default:
148 cmn_err(CE_WARN, "%s: unexpected pci header type:%x",
149 __func__, header);
150 return (DDI_FAILURE);
151 }
152
153 ncaps = 0;
154 for (base = pci_config_get8(h, base); base;
155 base = pci_config_get8(h, base + PCI_CAP_NEXT_PTR)) {
156 if (pci_config_get8(h, base) == id) {
157 *base_p = base;
158 return (DDI_SUCCESS);
159 }
160
161 ncaps++;
162 if (ncaps >= PCI_CAP_MAX_PTR)
163 break;
164 }
165
166 *base_p = PCI_CAP_NEXT_PTR_NULL;
167 return (DDI_FAILURE);
168 }
169
170 /*
171 * pci_xcap_locate: Helper function locates a base in extended config space.
172 */
173 int
pci_xcap_locate(ddi_acc_handle_t h,uint16_t id,uint16_t * base_p)174 pci_xcap_locate(ddi_acc_handle_t h, uint16_t id, uint16_t *base_p)
175 {
176 uint16_t status, base;
177 uint32_t xcaps_hdr, ncaps;
178
179 status = pci_config_get16(h, PCI_CONF_STAT);
180
181 if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
182 return (DDI_FAILURE);
183
184 ncaps = 0;
185 for (base = PCIE_EXT_CAP; base; base = (xcaps_hdr >>
186 PCIE_EXT_CAP_NEXT_PTR_SHIFT) & PCIE_EXT_CAP_NEXT_PTR_MASK) {
187
188 if ((xcaps_hdr = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
189 break;
190
191 if (((xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT) &
192 PCIE_EXT_CAP_ID_MASK) == id) {
193 *base_p = base;
194 return (DDI_SUCCESS);
195 }
196
197 ncaps++;
198 if (ncaps >= PCIE_EXT_CAP_MAX_PTR)
199 break;
200 }
201
202 *base_p = PCI_CAP_NEXT_PTR_NULL;
203 return (DDI_FAILURE);
204 }
205
206 /*
207 * There can be multiple pci caps with a Hypertransport technology cap ID
208 * Each is distiguished by a type register in the upper half of the cap
209 * header (the "command" register part).
210 *
211 * This returns the location of a hypertransport capability whose upper
212 * 16-bits of the cap header matches <reg_val> after masking the value
213 * with <reg_mask>; if both <reg_mask> and <reg_val> are 0, it will return
214 * the first HT cap found
215 */
216 int
pci_htcap_locate(ddi_acc_handle_t h,uint16_t reg_mask,uint16_t reg_val,uint16_t * base_p)217 pci_htcap_locate(ddi_acc_handle_t h, uint16_t reg_mask, uint16_t reg_val,
218 uint16_t *base_p)
219 {
220 uint8_t header;
221 uint16_t status, base;
222
223 status = pci_config_get16(h, PCI_CONF_STAT);
224
225 if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
226 return (DDI_FAILURE);
227
228 header = pci_config_get8(h, PCI_CONF_HEADER);
229 switch (header & PCI_HEADER_TYPE_M) {
230 case PCI_HEADER_ZERO:
231 base = PCI_CONF_CAP_PTR;
232 break;
233 case PCI_HEADER_PPB:
234 base = PCI_BCNF_CAP_PTR;
235 break;
236 default:
237 cmn_err(CE_WARN, "%s: unexpected pci header type:%x",
238 __func__, header);
239 return (DDI_FAILURE);
240 }
241
242 for (base = pci_config_get8(h, base); base;
243 base = pci_config_get8(h, base + PCI_CAP_NEXT_PTR)) {
244 if (pci_config_get8(h, base) == PCI_CAP_ID_HT &&
245 (pci_config_get16(h, base + PCI_CAP_ID_REGS_OFF) &
246 reg_mask) == reg_val) {
247 *base_p = base;
248 return (DDI_SUCCESS);
249 }
250 }
251
252 *base_p = PCI_CAP_NEXT_PTR_NULL;
253 return (DDI_FAILURE);
254 }
255
256 /*
257 * pci_cap_get: This function uses the base or capid to get a byte, word,
258 * or dword. If access by capid is requested, the function uses the capid to
259 * locate the base. Access by a base results in better performance
260 * because no cap list traversal is required.
261 */
262 uint32_t
pci_cap_get(ddi_acc_handle_t h,pci_cap_config_size_t size,uint32_t id,uint16_t base,uint16_t offset)263 pci_cap_get(ddi_acc_handle_t h, pci_cap_config_size_t size, uint32_t id,
264 uint16_t base, uint16_t offset)
265 {
266 uint32_t data;
267
268 if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
269 return (PCI_CAP_EINVAL32);
270
271 /*
272 * Each access to a PCI Configuration Space should be checked
273 * by the calling function. A returned value of the 2's complement
274 * of -1 indicates that either the device is offlined or it does not
275 * exist.
276 */
277 offset += base;
278
279 switch (size) {
280 case PCI_CAP_CFGSZ_8:
281 data = pci_config_get8(h, offset);
282 break;
283 case PCI_CAP_CFGSZ_16:
284 data = pci_config_get16(h, offset);
285 break;
286 case PCI_CAP_CFGSZ_32:
287 data = pci_config_get32(h, offset);
288 break;
289 default:
290 data = PCI_CAP_EINVAL32;
291 }
292
293 PCI_CAP_DBG("pci_cap_get: %p[x%x]=x%x\n", (void *)h, offset, data);
294 return (data);
295 }
296
297 /*
298 * pci_cap_put: This function uses the caps ptr or capid to put a byte, word,
299 * or dword. If access by capid is requested, the function uses the capid to
300 * locate the base. Access by base results in better performance
301 * because no cap list traversal is required.
302 */
303 int
pci_cap_put(ddi_acc_handle_t h,pci_cap_config_size_t size,uint32_t id,uint16_t base,uint16_t offset,uint32_t data)304 pci_cap_put(ddi_acc_handle_t h, pci_cap_config_size_t size,
305 uint32_t id, uint16_t base, uint16_t offset, uint32_t data)
306 {
307
308 /*
309 * use the pci_config_size_t to switch for the appropriate read
310 */
311 if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
312 return (DDI_FAILURE);
313
314 offset += base;
315
316 switch (size) {
317 case PCI_CAP_CFGSZ_8:
318 pci_config_put8(h, offset, data);
319 break;
320 case PCI_CAP_CFGSZ_16:
321 pci_config_put16(h, offset, data);
322 break;
323 case PCI_CAP_CFGSZ_32:
324 pci_config_put32(h, offset, data);
325 break;
326 default:
327 return (DDI_FAILURE);
328 }
329
330 PCI_CAP_DBG("pci_cap_put: data=%x\n", data);
331 return (DDI_SUCCESS);
332 }
333
334 /*
335 * Cache the entire Cap Structure. The caller is required to allocate and free
336 * buffer.
337 */
338 int
pci_cap_read(ddi_acc_handle_t h,uint32_t id,uint16_t base,uint32_t * buf_p,uint32_t nwords)339 pci_cap_read(ddi_acc_handle_t h, uint32_t id, uint16_t base,
340 uint32_t *buf_p, uint32_t nwords)
341 {
342
343 int i;
344 uint32_t *ptr;
345
346 ASSERT(nwords < 1024);
347
348 if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
349 return (DDI_FAILURE);
350
351 for (ptr = buf_p, i = 0; i < nwords; i++, base += 4) {
352 if ((*ptr++ = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
353 return (DDI_FAILURE);
354 }
355
356 return (DDI_SUCCESS);
357 }
358