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