xref: /illumos-gate/usr/src/uts/common/io/pci_cap.c (revision 27255037bba8df933008f7bd2112201bef2e2429)
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 2006 Sun Microsystems, Inc.  All rights reserved.
35  * Use is subject to license terms.
36  */
37 
38 #pragma ident	"%Z%%M%	%I%	%E% SMI"
39 
40 /*
41  * Generic PCI Capabilites Interface for all pci platforms
42  */
43 
44 #ifdef DEBUG
45 uint_t  pci_cap_debug = 0;
46 #endif
47 
48 /* Cap Base Macro */
49 #define	PCI_CAP_BASE(h, id, base_p) (*base_p ? DDI_SUCCESS : \
50 	(id ? PCI_CAP_LOCATE(h, id, base_p) : DDI_FAILURE))
51 
52 /*
53  * pci_cap_probe: returns the capid and base based upon a given index
54  */
55 int
56 pci_cap_probe(ddi_acc_handle_t h, uint16_t index,
57 	uint32_t *id_p, uint16_t *base_p)
58 {
59 	int i, search_ext = 0;
60 	uint16_t base, pcix_cmd, status;
61 	uint32_t id, xcaps_hdr; /* Extended Caps Header Word */
62 
63 	status = pci_config_get16(h, PCI_CONF_STAT);
64 
65 	if (status == 0xffff || !(status & PCI_STAT_CAP))
66 		return (DDI_FAILURE);
67 
68 	/* PCIE and PCIX Version 2 contain Extended Config Space */
69 	for (i = 0, base = pci_config_get8(h, PCI_CONF_CAP_PTR);
70 		base && i < index; base = pci_config_get8(h, base
71 			+ PCI_CAP_NEXT_PTR), i++) {
72 
73 		if ((id = pci_config_get8(h, base)) == 0xff)
74 			break;
75 
76 		if (id == PCI_CAP_ID_PCI_E)
77 			search_ext = 1;
78 		else if (id == PCI_CAP_ID_PCIX) {
79 			if ((pcix_cmd = pci_config_get16(h, base +
80 				PCI_PCIX_COMMAND)) != 0xffff)
81 				continue;
82 			if ((pcix_cmd & PCI_PCIX_VER_MASK) == PCI_PCIX_VER_2)
83 				search_ext = 1;
84 		}
85 	}
86 
87 	if (base && i == index) {
88 		if ((id = pci_config_get8(h, base)) != 0xff)
89 			goto found;
90 	}
91 
92 	if (!search_ext)
93 		return (DDI_FAILURE);
94 
95 	for (base = PCIE_EXT_CAP; base && i < index; i++) {
96 		if ((xcaps_hdr = pci_config_get32(h, base)) == 0xffffffff)
97 			break;
98 
99 		id = (xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT)
100 			& PCIE_EXT_CAP_ID_MASK;
101 		base = (xcaps_hdr >> PCIE_EXT_CAP_NEXT_PTR_SHIFT)
102 			& PCIE_EXT_CAP_NEXT_PTR_MASK;
103 	}
104 
105 	if (!base || i < index)
106 		return (DDI_FAILURE);
107 
108 	if ((xcaps_hdr = pci_config_get32(h, base)) == 0xffffffff)
109 		return (DDI_FAILURE);
110 
111 	id = ((xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT) & PCIE_EXT_CAP_ID_MASK) |
112 			PCI_CAP_XCFG_FLAG;
113 found:
114 	PCI_CAP_DBG("pci_cap_probe: index=%x, id=%x, base=%x\n",
115 		index, id, base);
116 
117 	*id_p = id;
118 	*base_p = base;
119 	return (DDI_SUCCESS);
120 
121 }
122 
123 /*
124  * pci_lcap_locate: Helper function locates a base in conventional config space.
125  */
126 int
127 pci_lcap_locate(ddi_acc_handle_t h, uint8_t id, uint16_t *base_p)
128 {
129 	uint16_t status, base;
130 
131 	status = pci_config_get16(h, PCI_CONF_STAT);
132 
133 	if (status == 0xffff || !(status & PCI_STAT_CAP))
134 		return (DDI_FAILURE);
135 
136 	for (base = pci_config_get8(h, PCI_CONF_CAP_PTR); base;
137 		base = pci_config_get8(h, base + PCI_CAP_NEXT_PTR)) {
138 		if (pci_config_get8(h, base) == id) {
139 			*base_p = base;
140 			return (DDI_SUCCESS);
141 		}
142 	}
143 
144 	*base_p = PCI_CAP_NEXT_PTR_NULL;
145 	return (DDI_FAILURE);
146 
147 
148 }
149 
150 /*
151  * pci_xcap_locate: Helper function locates a base in extended config space.
152  */
153 int
154 pci_xcap_locate(ddi_acc_handle_t h, uint16_t id, uint16_t *base_p)
155 {
156 	uint16_t status, base;
157 	uint32_t xcaps_hdr;
158 
159 	status = pci_config_get16(h, PCI_CONF_STAT);
160 
161 	if (status == 0xffff || !(status & PCI_STAT_CAP))
162 		return (DDI_FAILURE);
163 
164 	for (base = PCIE_EXT_CAP; base; base = (xcaps_hdr >>
165 		PCIE_EXT_CAP_NEXT_PTR_SHIFT) & PCIE_EXT_CAP_NEXT_PTR_MASK) {
166 
167 		if ((xcaps_hdr = pci_config_get32(h, base)) == 0xffffffff)
168 			break;
169 
170 		if (((xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT) &
171 			PCIE_EXT_CAP_ID_MASK) == id) {
172 			*base_p = base;
173 			return (DDI_SUCCESS);
174 		}
175 	}
176 
177 	*base_p = PCI_CAP_NEXT_PTR_NULL;
178 	return (DDI_FAILURE);
179 }
180 
181 /*
182  * pci_cap_get: This function uses the base or capid to get a byte, word,
183  * or dword. If access by capid is requested, the function uses the capid to
184  * locate the base. Access by a base results in better performance
185  * because no cap list traversal is required.
186  */
187 uint32_t
188 pci_cap_get(ddi_acc_handle_t h, pci_config_size_t size,
189 	uint32_t id, uint16_t base, uint16_t offset)
190 {
191 	uint32_t data;
192 
193 	if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
194 		return (DDI_FAILURE);
195 
196 	/*
197 	 * Each access to a PCI Configuration Space is checked for
198 	 * returned value of -1, in case the a device is offlined
199 	 * or if it does not exist.
200 	 */
201 	offset += base;
202 
203 	switch (size) {
204 	case PCI_CAP_CFGSZ_8:
205 		if ((data = pci_config_get8(h, offset)) == 0xff)
206 			return (DDI_FAILURE);
207 		break;
208 	case PCI_CAP_CFGSZ_16:
209 		if ((data = pci_config_get16(h, offset)) == 0xffff)
210 			return (DDI_FAILURE);
211 		break;
212 	case PCI_CAP_CFGSZ_32:
213 		if ((data = pci_config_get32(h, offset)) == 0xffffffff)
214 			return (DDI_FAILURE);
215 		break;
216 	default:
217 		return (DDI_FAILURE);
218 	}
219 
220 	PCI_CAP_DBG("pci_cap_get: %p[x%x]=x%x\n", (void *)h, offset, data);
221 	return (data);
222 }
223 
224 /*
225  * pci_cap_put: This function uses the caps ptr or capid to put a byte, word,
226  * or dword. If access by capid is requested, the function uses the capid to
227  * locate the base. Access by base results in better performance
228  * because no cap list traversal is required.
229  */
230 int
231 pci_cap_put(ddi_acc_handle_t h, pci_config_size_t size,
232 	uint32_t id, uint16_t base, uint16_t offset,
233 	uint32_t data)
234 {
235 
236 	/*
237 	 * use the pci_config_size_t to switch for the appropriate read
238 	 */
239 	if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
240 		return (DDI_FAILURE);
241 
242 	offset += base;
243 
244 	switch (size) {
245 	case PCI_CAP_CFGSZ_8:
246 		pci_config_put8(h, offset, data);
247 		break;
248 	case PCI_CAP_CFGSZ_16:
249 		pci_config_put16(h, offset, data);
250 		break;
251 	case PCI_CAP_CFGSZ_32:
252 		pci_config_put32(h, offset, data);
253 		break;
254 	default:
255 		return (DDI_FAILURE);
256 	}
257 
258 	PCI_CAP_DBG("pci_cap_put: data=%x\n", data);
259 	return (DDI_SUCCESS);
260 }
261 
262 /*
263  * Cache the entire Cap Structure.  The caller is required to allocate and free
264  * buffer.
265  */
266 int
267 pci_cap_read(ddi_acc_handle_t h, uint32_t id, uint16_t base,
268 	uint32_t *buf_p, uint32_t nwords)
269 {
270 
271 	int i;
272 	uint32_t *ptr;
273 
274 	ASSERT(nwords < 1024);
275 
276 	if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
277 		return (DDI_FAILURE);
278 
279 	for (ptr = buf_p, i = 0; i < nwords; i++, base += 4) {
280 		if ((*ptr++ = pci_config_get32(h, base)) == 0xffffffff)
281 			return (DDI_FAILURE);
282 	}
283 
284 	return (DDI_SUCCESS);
285 }
286