xref: /illumos-gate/usr/src/uts/common/io/pci_cap.c (revision d288ba74)
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
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
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 
166 /*
167  * pci_xcap_locate: Helper function locates a base in extended config space.
168  */
169 int
170 pci_xcap_locate(ddi_acc_handle_t h, uint16_t id, uint16_t *base_p)
171 {
172 	uint16_t status, base;
173 	uint32_t xcaps_hdr;
174 
175 	status = pci_config_get16(h, PCI_CONF_STAT);
176 
177 	if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
178 		return (DDI_FAILURE);
179 
180 	for (base = PCIE_EXT_CAP; base; base = (xcaps_hdr >>
181 		PCIE_EXT_CAP_NEXT_PTR_SHIFT) & PCIE_EXT_CAP_NEXT_PTR_MASK) {
182 
183 		if ((xcaps_hdr = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
184 			break;
185 
186 		if (((xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT) &
187 			PCIE_EXT_CAP_ID_MASK) == id) {
188 			*base_p = base;
189 			return (DDI_SUCCESS);
190 		}
191 	}
192 
193 	*base_p = PCI_CAP_NEXT_PTR_NULL;
194 	return (DDI_FAILURE);
195 }
196 
197 /*
198  * pci_cap_get: This function uses the base or capid to get a byte, word,
199  * or dword. If access by capid is requested, the function uses the capid to
200  * locate the base. Access by a base results in better performance
201  * because no cap list traversal is required.
202  */
203 uint32_t
204 pci_cap_get(ddi_acc_handle_t h, pci_cap_config_size_t size,
205 	uint32_t id, uint16_t base, uint16_t offset)
206 {
207 	uint32_t data;
208 
209 	if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
210 		return (PCI_CAP_EINVAL32);
211 
212 	/*
213 	 * Each access to a PCI Configuration Space should be checked
214 	 * by the calling function. A returned value of the 2's complement
215 	 * of -1 indicates that either the device is offlined or it does not
216 	 * exist.
217 	 */
218 	offset += base;
219 
220 	switch (size) {
221 	case PCI_CAP_CFGSZ_8:
222 		data = pci_config_get8(h, offset);
223 		break;
224 	case PCI_CAP_CFGSZ_16:
225 		data = pci_config_get16(h, offset);
226 		break;
227 	case PCI_CAP_CFGSZ_32:
228 		data = pci_config_get32(h, offset);
229 		break;
230 	default:
231 		data = PCI_CAP_EINVAL32;
232 	}
233 
234 	PCI_CAP_DBG("pci_cap_get: %p[x%x]=x%x\n", (void *)h, offset, data);
235 	return (data);
236 }
237 
238 /*
239  * pci_cap_put: This function uses the caps ptr or capid to put a byte, word,
240  * or dword. If access by capid is requested, the function uses the capid to
241  * locate the base. Access by base results in better performance
242  * because no cap list traversal is required.
243  */
244 int
245 pci_cap_put(ddi_acc_handle_t h, pci_cap_config_size_t size,
246 	uint32_t id, uint16_t base, uint16_t offset,
247 	uint32_t data)
248 {
249 
250 	/*
251 	 * use the pci_config_size_t to switch for the appropriate read
252 	 */
253 	if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
254 		return (DDI_FAILURE);
255 
256 	offset += base;
257 
258 	switch (size) {
259 	case PCI_CAP_CFGSZ_8:
260 		pci_config_put8(h, offset, data);
261 		break;
262 	case PCI_CAP_CFGSZ_16:
263 		pci_config_put16(h, offset, data);
264 		break;
265 	case PCI_CAP_CFGSZ_32:
266 		pci_config_put32(h, offset, data);
267 		break;
268 	default:
269 		return (DDI_FAILURE);
270 	}
271 
272 	PCI_CAP_DBG("pci_cap_put: data=%x\n", data);
273 	return (DDI_SUCCESS);
274 }
275 
276 /*
277  * Cache the entire Cap Structure.  The caller is required to allocate and free
278  * buffer.
279  */
280 int
281 pci_cap_read(ddi_acc_handle_t h, uint32_t id, uint16_t base,
282 	uint32_t *buf_p, uint32_t nwords)
283 {
284 
285 	int i;
286 	uint32_t *ptr;
287 
288 	ASSERT(nwords < 1024);
289 
290 	if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
291 		return (DDI_FAILURE);
292 
293 	for (ptr = buf_p, i = 0; i < nwords; i++, base += 4) {
294 		if ((*ptr++ = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
295 			return (DDI_FAILURE);
296 	}
297 
298 	return (DDI_SUCCESS);
299 }
300