xref: /illumos-gate/usr/src/uts/common/io/pci_cap.c (revision cb7ea99d)
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