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.
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.
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]
24 #include <sys/debug.h>
25 #include <sys/sunddi.h>
28 #include <sys/bitmap.h>
29 #include <sys/autoconf.h>
30 #include <sys/sysmacros.h>
31 #include <sys/pci_cap.h>
34 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
35 * Use is subject to license terms.
39 * Generic PCI Capabilites Interface for all pci platforms
43 uint_t pci_cap_debug
= 0;
47 #define PCI_CAP_BASE(h, id, base_p) (*base_p ? DDI_SUCCESS : \
48 (id ? PCI_CAP_LOCATE(h, id, base_p) : DDI_FAILURE))
51 * pci_cap_probe: returns the capid and base based upon a given index
54 pci_cap_probe(ddi_acc_handle_t h
, uint16_t index
,
55 uint32_t *id_p
, uint16_t *base_p
)
57 int i
, search_ext
= 0;
58 uint16_t base
, pcix_cmd
, status
;
59 uint32_t id
, xcaps_hdr
; /* Extended Caps Header Word */
61 status
= pci_config_get16(h
, PCI_CONF_STAT
);
63 if (status
== PCI_CAP_EINVAL16
|| !(status
& PCI_STAT_CAP
))
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
++) {
71 if ((id
= pci_config_get8(h
, base
)) == 0xff)
74 if (id
== PCI_CAP_ID_PCI_E
)
76 else if (id
== PCI_CAP_ID_PCIX
) {
77 if ((pcix_cmd
= pci_config_get16(h
, base
+
78 PCI_PCIX_COMMAND
)) != PCI_CAP_EINVAL16
)
80 if ((pcix_cmd
& PCI_PCIX_VER_MASK
) == PCI_PCIX_VER_2
)
85 if (base
&& i
== index
) {
86 if ((id
= pci_config_get8(h
, base
)) != 0xff)
93 for (base
= PCIE_EXT_CAP
; base
&& i
< index
; i
++) {
94 if ((xcaps_hdr
= pci_config_get32(h
, base
)) == PCI_CAP_EINVAL32
)
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
;
103 if (!base
|| i
< index
)
104 return (DDI_FAILURE
);
106 if ((xcaps_hdr
= pci_config_get32(h
, base
)) == PCI_CAP_EINVAL32
)
107 return (DDI_FAILURE
);
109 id
= ((xcaps_hdr
>> PCIE_EXT_CAP_ID_SHIFT
) & PCIE_EXT_CAP_ID_MASK
) |
112 PCI_CAP_DBG("pci_cap_probe: index=%x, id=%x, base=%x\n",
117 return (DDI_SUCCESS
);
122 * pci_lcap_locate: Helper function locates a base in conventional config space.
125 pci_lcap_locate(ddi_acc_handle_t h
, uint8_t id
, uint16_t *base_p
)
128 uint16_t status
, base
;
130 status
= pci_config_get16(h
, PCI_CONF_STAT
);
132 if (status
== PCI_CAP_EINVAL16
|| !(status
& PCI_STAT_CAP
))
133 return (DDI_FAILURE
);
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
;
141 base
= PCI_BCNF_CAP_PTR
;
143 case PCI_HEADER_CARDBUS
:
144 base
= PCI_CBUS_CAP_PTR
;
147 cmn_err(CE_WARN
, "%s: unexpected pci header type:%x",
149 return (DDI_FAILURE
);
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
) {
156 return (DDI_SUCCESS
);
160 *base_p
= PCI_CAP_NEXT_PTR_NULL
;
161 return (DDI_FAILURE
);
165 * pci_xcap_locate: Helper function locates a base in extended config space.
168 pci_xcap_locate(ddi_acc_handle_t h
, uint16_t id
, uint16_t *base_p
)
170 uint16_t status
, base
;
173 status
= pci_config_get16(h
, PCI_CONF_STAT
);
175 if (status
== PCI_CAP_EINVAL16
|| !(status
& PCI_STAT_CAP
))
176 return (DDI_FAILURE
);
178 for (base
= PCIE_EXT_CAP
; base
; base
= (xcaps_hdr
>>
179 PCIE_EXT_CAP_NEXT_PTR_SHIFT
) & PCIE_EXT_CAP_NEXT_PTR_MASK
) {
181 if ((xcaps_hdr
= pci_config_get32(h
, base
)) == PCI_CAP_EINVAL32
)
184 if (((xcaps_hdr
>> PCIE_EXT_CAP_ID_SHIFT
) &
185 PCIE_EXT_CAP_ID_MASK
) == id
) {
187 return (DDI_SUCCESS
);
191 *base_p
= PCI_CAP_NEXT_PTR_NULL
;
192 return (DDI_FAILURE
);
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).
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
206 pci_htcap_locate(ddi_acc_handle_t h
, uint16_t reg_mask
, uint16_t reg_val
,
210 uint16_t status
, base
;
212 status
= pci_config_get16(h
, PCI_CONF_STAT
);
214 if (status
== PCI_CAP_EINVAL16
|| !(status
& PCI_STAT_CAP
))
215 return (DDI_FAILURE
);
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
;
223 base
= PCI_BCNF_CAP_PTR
;
226 cmn_err(CE_WARN
, "%s: unexpected pci header type:%x",
228 return (DDI_FAILURE
);
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
) {
237 return (DDI_SUCCESS
);
241 *base_p
= PCI_CAP_NEXT_PTR_NULL
;
242 return (DDI_FAILURE
);
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.
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
)
257 if (PCI_CAP_BASE(h
, id
, &base
) != DDI_SUCCESS
)
258 return (PCI_CAP_EINVAL32
);
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
269 case PCI_CAP_CFGSZ_8
:
270 data
= pci_config_get8(h
, offset
);
272 case PCI_CAP_CFGSZ_16
:
273 data
= pci_config_get16(h
, offset
);
275 case PCI_CAP_CFGSZ_32
:
276 data
= pci_config_get32(h
, offset
);
279 data
= PCI_CAP_EINVAL32
;
282 PCI_CAP_DBG("pci_cap_get: %p[x%x]=x%x\n", (void *)h
, offset
, data
);
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.
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
,
299 * use the pci_config_size_t to switch for the appropriate read
301 if (PCI_CAP_BASE(h
, id
, &base
) != DDI_SUCCESS
)
302 return (DDI_FAILURE
);
307 case PCI_CAP_CFGSZ_8
:
308 pci_config_put8(h
, offset
, data
);
310 case PCI_CAP_CFGSZ_16
:
311 pci_config_put16(h
, offset
, data
);
313 case PCI_CAP_CFGSZ_32
:
314 pci_config_put32(h
, offset
, data
);
317 return (DDI_FAILURE
);
320 PCI_CAP_DBG("pci_cap_put: data=%x\n", data
);
321 return (DDI_SUCCESS
);
325 * Cache the entire Cap Structure. The caller is required to allocate and free
329 pci_cap_read(ddi_acc_handle_t h
, uint32_t id
, uint16_t base
,
330 uint32_t *buf_p
, uint32_t nwords
)
336 ASSERT(nwords
< 1024);
338 if (PCI_CAP_BASE(h
, id
, &base
) != DDI_SUCCESS
)
339 return (DDI_FAILURE
);
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
);
346 return (DDI_SUCCESS
);