xref: /illumos-gate/usr/src/uts/common/io/pci_cap.c (revision a38ddfee9c8c6b6c5a2947ff52fd2338362a4444)
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 == PCI_CAP_EINVAL16 || !(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)) != PCI_CAP_EINVAL16)
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)) == PCI_CAP_EINVAL32)
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)) == PCI_CAP_EINVAL32)
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 == PCI_CAP_EINVAL16 || !(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 == PCI_CAP_EINVAL16 || !(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)) == PCI_CAP_EINVAL32)
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_cap_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 (PCI_CAP_EINVAL32);
195 
196 	/*
197 	 * Each access to a PCI Configuration Space should be checked
198 	 * by the calling function. A returned value of the 2's complement
199 	 * of -1 indicates that either the device is offlined or it does not
200 	 * exist.
201 	 */
202 	offset += base;
203 
204 	switch (size) {
205 	case PCI_CAP_CFGSZ_8:
206 		data = pci_config_get8(h, offset);
207 		break;
208 	case PCI_CAP_CFGSZ_16:
209 		data = pci_config_get16(h, offset);
210 		break;
211 	case PCI_CAP_CFGSZ_32:
212 		data = pci_config_get32(h, offset);
213 		break;
214 	default:
215 		data = PCI_CAP_EINVAL32;
216 	}
217 
218 	PCI_CAP_DBG("pci_cap_get: %p[x%x]=x%x\n", (void *)h, offset, data);
219 	return (data);
220 }
221 
222 /*
223  * pci_cap_put: This function uses the caps ptr or capid to put a byte, word,
224  * or dword. If access by capid is requested, the function uses the capid to
225  * locate the base. Access by base results in better performance
226  * because no cap list traversal is required.
227  */
228 int
229 pci_cap_put(ddi_acc_handle_t h, pci_cap_config_size_t size,
230 	uint32_t id, uint16_t base, uint16_t offset,
231 	uint32_t data)
232 {
233 
234 	/*
235 	 * use the pci_config_size_t to switch for the appropriate read
236 	 */
237 	if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
238 		return (DDI_FAILURE);
239 
240 	offset += base;
241 
242 	switch (size) {
243 	case PCI_CAP_CFGSZ_8:
244 		pci_config_put8(h, offset, data);
245 		break;
246 	case PCI_CAP_CFGSZ_16:
247 		pci_config_put16(h, offset, data);
248 		break;
249 	case PCI_CAP_CFGSZ_32:
250 		pci_config_put32(h, offset, data);
251 		break;
252 	default:
253 		return (DDI_FAILURE);
254 	}
255 
256 	PCI_CAP_DBG("pci_cap_put: data=%x\n", data);
257 	return (DDI_SUCCESS);
258 }
259 
260 /*
261  * Cache the entire Cap Structure.  The caller is required to allocate and free
262  * buffer.
263  */
264 int
265 pci_cap_read(ddi_acc_handle_t h, uint32_t id, uint16_t base,
266 	uint32_t *buf_p, uint32_t nwords)
267 {
268 
269 	int i;
270 	uint32_t *ptr;
271 
272 	ASSERT(nwords < 1024);
273 
274 	if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
275 		return (DDI_FAILURE);
276 
277 	for (ptr = buf_p, i = 0; i < nwords; i++, base += 4) {
278 		if ((*ptr++ = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
279 			return (DDI_FAILURE);
280 	}
281 
282 	return (DDI_SUCCESS);
283 }
284