127255037Spjha /*
227255037Spjha * CDDL HEADER START
327255037Spjha *
427255037Spjha * The contents of this file are subject to the terms of the
527255037Spjha * Common Development and Distribution License (the "License").
627255037Spjha * You may not use this file except in compliance with the License.
727255037Spjha *
827255037Spjha * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
927255037Spjha * or http://www.opensolaris.org/os/licensing.
1027255037Spjha * See the License for the specific language governing permissions
1127255037Spjha * and limitations under the License.
1227255037Spjha *
1327255037Spjha * When distributing Covered Code, include this CDDL HEADER in each
1427255037Spjha * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1527255037Spjha * If applicable, add the following below this CDDL HEADER, with the
1627255037Spjha * fields enclosed by brackets "[]" replaced with your own identifying
1727255037Spjha * information: Portions Copyright [yyyy] [name of copyright owner]
1827255037Spjha *
1927255037Spjha * CDDL HEADER END
2027255037Spjha */
2127255037Spjha
2227255037Spjha #include <sys/note.h>
2327255037Spjha #include <sys/conf.h>
2427255037Spjha #include <sys/debug.h>
2527255037Spjha #include <sys/sunddi.h>
2627255037Spjha #include <sys/pci.h>
2727255037Spjha #include <sys/pcie.h>
2827255037Spjha #include <sys/bitmap.h>
2927255037Spjha #include <sys/autoconf.h>
3027255037Spjha #include <sys/sysmacros.h>
3127255037Spjha #include <sys/pci_cap.h>
3227255037Spjha
3327255037Spjha /*
34fb66942fSCasper H.S. Dik * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
3527255037Spjha * Use is subject to license terms.
3627255037Spjha */
3727255037Spjha
3827255037Spjha /*
3927255037Spjha * Generic PCI Capabilites Interface for all pci platforms
4027255037Spjha */
4127255037Spjha
4227255037Spjha #ifdef DEBUG
4327255037Spjha uint_t pci_cap_debug = 0;
4427255037Spjha #endif
4527255037Spjha
4627255037Spjha /* Cap Base Macro */
4727255037Spjha #define PCI_CAP_BASE(h, id, base_p) (*base_p ? DDI_SUCCESS : \
4827255037Spjha (id ? PCI_CAP_LOCATE(h, id, base_p) : DDI_FAILURE))
4927255037Spjha
5027255037Spjha /*
5127255037Spjha * pci_cap_probe: returns the capid and base based upon a given index
5227255037Spjha */
5327255037Spjha int
pci_cap_probe(ddi_acc_handle_t h,uint16_t index,uint32_t * id_p,uint16_t * base_p)5427255037Spjha pci_cap_probe(ddi_acc_handle_t h, uint16_t index,
5527255037Spjha uint32_t *id_p, uint16_t *base_p)
5627255037Spjha {
5727255037Spjha int i, search_ext = 0;
5827255037Spjha uint16_t base, pcix_cmd, status;
5927255037Spjha uint32_t id, xcaps_hdr; /* Extended Caps Header Word */
6027255037Spjha
6127255037Spjha status = pci_config_get16(h, PCI_CONF_STAT);
6227255037Spjha
63dc5d169bSpjha if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
6427255037Spjha return (DDI_FAILURE);
6527255037Spjha
6627255037Spjha /* PCIE and PCIX Version 2 contain Extended Config Space */
6727255037Spjha for (i = 0, base = pci_config_get8(h, PCI_CONF_CAP_PTR);
6827255037Spjha base && i < index; base = pci_config_get8(h, base
6927255037Spjha + PCI_CAP_NEXT_PTR), i++) {
7027255037Spjha
7127255037Spjha if ((id = pci_config_get8(h, base)) == 0xff)
7227255037Spjha break;
7327255037Spjha
7427255037Spjha if (id == PCI_CAP_ID_PCI_E)
7527255037Spjha search_ext = 1;
7627255037Spjha else if (id == PCI_CAP_ID_PCIX) {
7727255037Spjha if ((pcix_cmd = pci_config_get16(h, base +
78dc5d169bSpjha PCI_PCIX_COMMAND)) != PCI_CAP_EINVAL16)
7927255037Spjha continue;
8027255037Spjha if ((pcix_cmd & PCI_PCIX_VER_MASK) == PCI_PCIX_VER_2)
8127255037Spjha search_ext = 1;
8227255037Spjha }
8327255037Spjha }
8427255037Spjha
8527255037Spjha if (base && i == index) {
8627255037Spjha if ((id = pci_config_get8(h, base)) != 0xff)
8727255037Spjha goto found;
8827255037Spjha }
8927255037Spjha
9027255037Spjha if (!search_ext)
9127255037Spjha return (DDI_FAILURE);
9227255037Spjha
9327255037Spjha for (base = PCIE_EXT_CAP; base && i < index; i++) {
94dc5d169bSpjha if ((xcaps_hdr = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
9527255037Spjha break;
9627255037Spjha
9727255037Spjha id = (xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT)
9827255037Spjha & PCIE_EXT_CAP_ID_MASK;
9927255037Spjha base = (xcaps_hdr >> PCIE_EXT_CAP_NEXT_PTR_SHIFT)
10027255037Spjha & PCIE_EXT_CAP_NEXT_PTR_MASK;
10127255037Spjha }
10227255037Spjha
10327255037Spjha if (!base || i < index)
10427255037Spjha return (DDI_FAILURE);
10527255037Spjha
106dc5d169bSpjha if ((xcaps_hdr = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
10727255037Spjha return (DDI_FAILURE);
10827255037Spjha
10927255037Spjha id = ((xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT) & PCIE_EXT_CAP_ID_MASK) |
11027255037Spjha PCI_CAP_XCFG_FLAG;
11127255037Spjha found:
11227255037Spjha PCI_CAP_DBG("pci_cap_probe: index=%x, id=%x, base=%x\n",
11327255037Spjha index, id, base);
11427255037Spjha
11527255037Spjha *id_p = id;
11627255037Spjha *base_p = base;
11727255037Spjha return (DDI_SUCCESS);
11827255037Spjha
11927255037Spjha }
12027255037Spjha
12127255037Spjha /*
12227255037Spjha * pci_lcap_locate: Helper function locates a base in conventional config space.
12327255037Spjha */
12427255037Spjha int
pci_lcap_locate(ddi_acc_handle_t h,uint8_t id,uint16_t * base_p)12527255037Spjha pci_lcap_locate(ddi_acc_handle_t h, uint8_t id, uint16_t *base_p)
12627255037Spjha {
127fb66942fSCasper H.S. Dik uint8_t header;
12827255037Spjha uint16_t status, base;
12927255037Spjha
13027255037Spjha status = pci_config_get16(h, PCI_CONF_STAT);
13127255037Spjha
132dc5d169bSpjha if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
13327255037Spjha return (DDI_FAILURE);
13427255037Spjha
135fb66942fSCasper H.S. Dik header = pci_config_get8(h, PCI_CONF_HEADER);
136fb66942fSCasper H.S. Dik switch (header & PCI_HEADER_TYPE_M) {
137fb66942fSCasper H.S. Dik case PCI_HEADER_ZERO:
138fb66942fSCasper H.S. Dik base = PCI_CONF_CAP_PTR;
139fb66942fSCasper H.S. Dik break;
140fb66942fSCasper H.S. Dik case PCI_HEADER_PPB:
141fb66942fSCasper H.S. Dik base = PCI_BCNF_CAP_PTR;
142fb66942fSCasper H.S. Dik break;
143fb66942fSCasper H.S. Dik case PCI_HEADER_CARDBUS:
144fb66942fSCasper H.S. Dik base = PCI_CBUS_CAP_PTR;
145fb66942fSCasper H.S. Dik break;
146fb66942fSCasper H.S. Dik default:
147fb66942fSCasper H.S. Dik cmn_err(CE_WARN, "%s: unexpected pci header type:%x",
148fb66942fSCasper H.S. Dik __func__, header);
149fb66942fSCasper H.S. Dik return (DDI_FAILURE);
150fb66942fSCasper H.S. Dik }
151fb66942fSCasper H.S. Dik
152fb66942fSCasper H.S. Dik for (base = pci_config_get8(h, base); base;
15327255037Spjha base = pci_config_get8(h, base + PCI_CAP_NEXT_PTR)) {
15427255037Spjha if (pci_config_get8(h, base) == id) {
15527255037Spjha *base_p = base;
15627255037Spjha return (DDI_SUCCESS);
15727255037Spjha }
15827255037Spjha }
15927255037Spjha
16027255037Spjha *base_p = PCI_CAP_NEXT_PTR_NULL;
16127255037Spjha return (DDI_FAILURE);
16227255037Spjha }
16327255037Spjha
16427255037Spjha /*
16527255037Spjha * pci_xcap_locate: Helper function locates a base in extended config space.
16627255037Spjha */
16727255037Spjha int
pci_xcap_locate(ddi_acc_handle_t h,uint16_t id,uint16_t * base_p)16827255037Spjha pci_xcap_locate(ddi_acc_handle_t h, uint16_t id, uint16_t *base_p)
16927255037Spjha {
17027255037Spjha uint16_t status, base;
17127255037Spjha uint32_t xcaps_hdr;
17227255037Spjha
17327255037Spjha status = pci_config_get16(h, PCI_CONF_STAT);
17427255037Spjha
175dc5d169bSpjha if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
17627255037Spjha return (DDI_FAILURE);
17727255037Spjha
17827255037Spjha for (base = PCIE_EXT_CAP; base; base = (xcaps_hdr >>
17927255037Spjha PCIE_EXT_CAP_NEXT_PTR_SHIFT) & PCIE_EXT_CAP_NEXT_PTR_MASK) {
18027255037Spjha
181dc5d169bSpjha if ((xcaps_hdr = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
18227255037Spjha break;
18327255037Spjha
18427255037Spjha if (((xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT) &
18527255037Spjha PCIE_EXT_CAP_ID_MASK) == id) {
18627255037Spjha *base_p = base;
18727255037Spjha return (DDI_SUCCESS);
18827255037Spjha }
18927255037Spjha }
19027255037Spjha
19127255037Spjha *base_p = PCI_CAP_NEXT_PTR_NULL;
19227255037Spjha return (DDI_FAILURE);
19327255037Spjha }
19427255037Spjha
19527255037Spjha /*
196*cb7ea99dSJimmy Vetayases * There can be multiple pci caps with a Hypertransport technology cap ID
197*cb7ea99dSJimmy Vetayases * Each is distiguished by a type register in the upper half of the cap
198*cb7ea99dSJimmy Vetayases * header (the "command" register part).
199*cb7ea99dSJimmy Vetayases *
200*cb7ea99dSJimmy Vetayases * This returns the location of a hypertransport capability whose upper
201*cb7ea99dSJimmy Vetayases * 16-bits of the cap header matches <reg_val> after masking the value
202*cb7ea99dSJimmy Vetayases * with <reg_mask>; if both <reg_mask> and <reg_val> are 0, it will return
203*cb7ea99dSJimmy Vetayases * the first HT cap found
204*cb7ea99dSJimmy Vetayases */
205*cb7ea99dSJimmy Vetayases int
pci_htcap_locate(ddi_acc_handle_t h,uint16_t reg_mask,uint16_t reg_val,uint16_t * base_p)206*cb7ea99dSJimmy Vetayases pci_htcap_locate(ddi_acc_handle_t h, uint16_t reg_mask, uint16_t reg_val,
207*cb7ea99dSJimmy Vetayases uint16_t *base_p)
208*cb7ea99dSJimmy Vetayases {
209*cb7ea99dSJimmy Vetayases uint8_t header;
210*cb7ea99dSJimmy Vetayases uint16_t status, base;
211*cb7ea99dSJimmy Vetayases
212*cb7ea99dSJimmy Vetayases status = pci_config_get16(h, PCI_CONF_STAT);
213*cb7ea99dSJimmy Vetayases
214*cb7ea99dSJimmy Vetayases if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
215*cb7ea99dSJimmy Vetayases return (DDI_FAILURE);
216*cb7ea99dSJimmy Vetayases
217*cb7ea99dSJimmy Vetayases header = pci_config_get8(h, PCI_CONF_HEADER);
218*cb7ea99dSJimmy Vetayases switch (header & PCI_HEADER_TYPE_M) {
219*cb7ea99dSJimmy Vetayases case PCI_HEADER_ZERO:
220*cb7ea99dSJimmy Vetayases base = PCI_CONF_CAP_PTR;
221*cb7ea99dSJimmy Vetayases break;
222*cb7ea99dSJimmy Vetayases case PCI_HEADER_PPB:
223*cb7ea99dSJimmy Vetayases base = PCI_BCNF_CAP_PTR;
224*cb7ea99dSJimmy Vetayases break;
225*cb7ea99dSJimmy Vetayases default:
226*cb7ea99dSJimmy Vetayases cmn_err(CE_WARN, "%s: unexpected pci header type:%x",
227*cb7ea99dSJimmy Vetayases __func__, header);
228*cb7ea99dSJimmy Vetayases return (DDI_FAILURE);
229*cb7ea99dSJimmy Vetayases }
230*cb7ea99dSJimmy Vetayases
231*cb7ea99dSJimmy Vetayases for (base = pci_config_get8(h, base); base;
232*cb7ea99dSJimmy Vetayases base = pci_config_get8(h, base + PCI_CAP_NEXT_PTR)) {
233*cb7ea99dSJimmy Vetayases if (pci_config_get8(h, base) == PCI_CAP_ID_HT &&
234*cb7ea99dSJimmy Vetayases (pci_config_get16(h, base + PCI_CAP_ID_REGS_OFF) &
235*cb7ea99dSJimmy Vetayases reg_mask) == reg_val) {
236*cb7ea99dSJimmy Vetayases *base_p = base;
237*cb7ea99dSJimmy Vetayases return (DDI_SUCCESS);
238*cb7ea99dSJimmy Vetayases }
239*cb7ea99dSJimmy Vetayases }
240*cb7ea99dSJimmy Vetayases
241*cb7ea99dSJimmy Vetayases *base_p = PCI_CAP_NEXT_PTR_NULL;
242*cb7ea99dSJimmy Vetayases return (DDI_FAILURE);
243*cb7ea99dSJimmy Vetayases }
244*cb7ea99dSJimmy Vetayases
245*cb7ea99dSJimmy Vetayases /*
24627255037Spjha * pci_cap_get: This function uses the base or capid to get a byte, word,
24727255037Spjha * or dword. If access by capid is requested, the function uses the capid to
24827255037Spjha * locate the base. Access by a base results in better performance
24927255037Spjha * because no cap list traversal is required.
25027255037Spjha */
25127255037Spjha 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)2523c4226f9Spjha pci_cap_get(ddi_acc_handle_t h, pci_cap_config_size_t size,
25327255037Spjha uint32_t id, uint16_t base, uint16_t offset)
25427255037Spjha {
25527255037Spjha uint32_t data;
25627255037Spjha
25727255037Spjha if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
258dc5d169bSpjha return (PCI_CAP_EINVAL32);
25927255037Spjha
26027255037Spjha /*
261911fc2e5Spjha * Each access to a PCI Configuration Space should be checked
262911fc2e5Spjha * by the calling function. A returned value of the 2's complement
263911fc2e5Spjha * of -1 indicates that either the device is offlined or it does not
264911fc2e5Spjha * exist.
26527255037Spjha */
26627255037Spjha offset += base;
26727255037Spjha
26827255037Spjha switch (size) {
26927255037Spjha case PCI_CAP_CFGSZ_8:
270911fc2e5Spjha data = pci_config_get8(h, offset);
27127255037Spjha break;
27227255037Spjha case PCI_CAP_CFGSZ_16:
273911fc2e5Spjha data = pci_config_get16(h, offset);
27427255037Spjha break;
27527255037Spjha case PCI_CAP_CFGSZ_32:
276911fc2e5Spjha data = pci_config_get32(h, offset);
27727255037Spjha break;
27827255037Spjha default:
279dc5d169bSpjha data = PCI_CAP_EINVAL32;
28027255037Spjha }
28127255037Spjha
28227255037Spjha PCI_CAP_DBG("pci_cap_get: %p[x%x]=x%x\n", (void *)h, offset, data);
28327255037Spjha return (data);
28427255037Spjha }
28527255037Spjha
28627255037Spjha /*
28727255037Spjha * pci_cap_put: This function uses the caps ptr or capid to put a byte, word,
28827255037Spjha * or dword. If access by capid is requested, the function uses the capid to
28927255037Spjha * locate the base. Access by base results in better performance
29027255037Spjha * because no cap list traversal is required.
29127255037Spjha */
29227255037Spjha 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)2933c4226f9Spjha pci_cap_put(ddi_acc_handle_t h, pci_cap_config_size_t size,
29427255037Spjha uint32_t id, uint16_t base, uint16_t offset,
29527255037Spjha uint32_t data)
29627255037Spjha {
29727255037Spjha
29827255037Spjha /*
29927255037Spjha * use the pci_config_size_t to switch for the appropriate read
30027255037Spjha */
30127255037Spjha if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
30227255037Spjha return (DDI_FAILURE);
30327255037Spjha
30427255037Spjha offset += base;
30527255037Spjha
30627255037Spjha switch (size) {
30727255037Spjha case PCI_CAP_CFGSZ_8:
30827255037Spjha pci_config_put8(h, offset, data);
30927255037Spjha break;
31027255037Spjha case PCI_CAP_CFGSZ_16:
31127255037Spjha pci_config_put16(h, offset, data);
31227255037Spjha break;
31327255037Spjha case PCI_CAP_CFGSZ_32:
31427255037Spjha pci_config_put32(h, offset, data);
31527255037Spjha break;
31627255037Spjha default:
31727255037Spjha return (DDI_FAILURE);
31827255037Spjha }
31927255037Spjha
32027255037Spjha PCI_CAP_DBG("pci_cap_put: data=%x\n", data);
32127255037Spjha return (DDI_SUCCESS);
32227255037Spjha }
32327255037Spjha
32427255037Spjha /*
32527255037Spjha * Cache the entire Cap Structure. The caller is required to allocate and free
32627255037Spjha * buffer.
32727255037Spjha */
32827255037Spjha int
pci_cap_read(ddi_acc_handle_t h,uint32_t id,uint16_t base,uint32_t * buf_p,uint32_t nwords)32927255037Spjha pci_cap_read(ddi_acc_handle_t h, uint32_t id, uint16_t base,
33027255037Spjha uint32_t *buf_p, uint32_t nwords)
33127255037Spjha {
33227255037Spjha
33327255037Spjha int i;
33427255037Spjha uint32_t *ptr;
33527255037Spjha
33627255037Spjha ASSERT(nwords < 1024);
33727255037Spjha
33827255037Spjha if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
33927255037Spjha return (DDI_FAILURE);
34027255037Spjha
34127255037Spjha for (ptr = buf_p, i = 0; i < nwords; i++, base += 4) {
342dc5d169bSpjha if ((*ptr++ = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
34327255037Spjha return (DDI_FAILURE);
34427255037Spjha }
34527255037Spjha
34627255037Spjha return (DDI_SUCCESS);
34727255037Spjha }
348