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.
36*c1bbf920SRobert Mustacchi * Copyright 2022 Oxide Computer Company
3727255037Spjha */
3827255037Spjha
3927255037Spjha /*
4027255037Spjha * Generic PCI Capabilites Interface for all pci platforms
4127255037Spjha */
4227255037Spjha
4327255037Spjha #ifdef DEBUG
4427255037Spjha uint_t pci_cap_debug = 0;
4527255037Spjha #endif
4627255037Spjha
4727255037Spjha /* Cap Base Macro */
4827255037Spjha #define PCI_CAP_BASE(h, id, base_p) (*base_p ? DDI_SUCCESS : \
4927255037Spjha (id ? PCI_CAP_LOCATE(h, id, base_p) : DDI_FAILURE))
5027255037Spjha
5127255037Spjha /*
5227255037Spjha * pci_cap_probe: returns the capid and base based upon a given index
5327255037Spjha */
5427255037Spjha int
pci_cap_probe(ddi_acc_handle_t h,uint16_t index,uint32_t * id_p,uint16_t * base_p)55*c1bbf920SRobert Mustacchi pci_cap_probe(ddi_acc_handle_t h, uint16_t index, uint32_t *id_p,
56*c1bbf920SRobert Mustacchi uint16_t *base_p)
5727255037Spjha {
5827255037Spjha int i, search_ext = 0;
5927255037Spjha uint16_t base, pcix_cmd, status;
6027255037Spjha uint32_t id, xcaps_hdr; /* Extended Caps Header Word */
6127255037Spjha
6227255037Spjha status = pci_config_get16(h, PCI_CONF_STAT);
6327255037Spjha
64dc5d169bSpjha if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
6527255037Spjha return (DDI_FAILURE);
6627255037Spjha
6727255037Spjha /* PCIE and PCIX Version 2 contain Extended Config Space */
6827255037Spjha for (i = 0, base = pci_config_get8(h, PCI_CONF_CAP_PTR);
6927255037Spjha base && i < index; base = pci_config_get8(h, base
7027255037Spjha + PCI_CAP_NEXT_PTR), i++) {
7127255037Spjha
7227255037Spjha if ((id = pci_config_get8(h, base)) == 0xff)
7327255037Spjha break;
7427255037Spjha
7527255037Spjha if (id == PCI_CAP_ID_PCI_E)
7627255037Spjha search_ext = 1;
7727255037Spjha else if (id == PCI_CAP_ID_PCIX) {
7827255037Spjha if ((pcix_cmd = pci_config_get16(h, base +
79dc5d169bSpjha PCI_PCIX_COMMAND)) != PCI_CAP_EINVAL16)
8027255037Spjha continue;
8127255037Spjha if ((pcix_cmd & PCI_PCIX_VER_MASK) == PCI_PCIX_VER_2)
8227255037Spjha search_ext = 1;
8327255037Spjha }
8427255037Spjha }
8527255037Spjha
8627255037Spjha if (base && i == index) {
8727255037Spjha if ((id = pci_config_get8(h, base)) != 0xff)
8827255037Spjha goto found;
8927255037Spjha }
9027255037Spjha
9127255037Spjha if (!search_ext)
9227255037Spjha return (DDI_FAILURE);
9327255037Spjha
9427255037Spjha for (base = PCIE_EXT_CAP; base && i < index; i++) {
95dc5d169bSpjha if ((xcaps_hdr = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
9627255037Spjha break;
9727255037Spjha
9827255037Spjha id = (xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT)
9927255037Spjha & PCIE_EXT_CAP_ID_MASK;
10027255037Spjha base = (xcaps_hdr >> PCIE_EXT_CAP_NEXT_PTR_SHIFT)
10127255037Spjha & PCIE_EXT_CAP_NEXT_PTR_MASK;
10227255037Spjha }
10327255037Spjha
10427255037Spjha if (!base || i < index)
10527255037Spjha return (DDI_FAILURE);
10627255037Spjha
107dc5d169bSpjha if ((xcaps_hdr = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
10827255037Spjha return (DDI_FAILURE);
10927255037Spjha
11027255037Spjha id = ((xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT) & PCIE_EXT_CAP_ID_MASK) |
11127255037Spjha PCI_CAP_XCFG_FLAG;
11227255037Spjha found:
11327255037Spjha PCI_CAP_DBG("pci_cap_probe: index=%x, id=%x, base=%x\n",
11427255037Spjha index, id, base);
11527255037Spjha
11627255037Spjha *id_p = id;
11727255037Spjha *base_p = base;
11827255037Spjha return (DDI_SUCCESS);
11927255037Spjha
12027255037Spjha }
12127255037Spjha
12227255037Spjha /*
12327255037Spjha * pci_lcap_locate: Helper function locates a base in conventional config space.
12427255037Spjha */
12527255037Spjha int
pci_lcap_locate(ddi_acc_handle_t h,uint8_t id,uint16_t * base_p)12627255037Spjha pci_lcap_locate(ddi_acc_handle_t h, uint8_t id, uint16_t *base_p)
12727255037Spjha {
128fb66942fSCasper H.S. Dik uint8_t header;
129*c1bbf920SRobert Mustacchi uint16_t status, base, ncaps;
13027255037Spjha
13127255037Spjha status = pci_config_get16(h, PCI_CONF_STAT);
13227255037Spjha
133dc5d169bSpjha if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
13427255037Spjha return (DDI_FAILURE);
13527255037Spjha
136fb66942fSCasper H.S. Dik header = pci_config_get8(h, PCI_CONF_HEADER);
137fb66942fSCasper H.S. Dik switch (header & PCI_HEADER_TYPE_M) {
138fb66942fSCasper H.S. Dik case PCI_HEADER_ZERO:
139fb66942fSCasper H.S. Dik base = PCI_CONF_CAP_PTR;
140fb66942fSCasper H.S. Dik break;
141fb66942fSCasper H.S. Dik case PCI_HEADER_PPB:
142fb66942fSCasper H.S. Dik base = PCI_BCNF_CAP_PTR;
143fb66942fSCasper H.S. Dik break;
144fb66942fSCasper H.S. Dik case PCI_HEADER_CARDBUS:
145fb66942fSCasper H.S. Dik base = PCI_CBUS_CAP_PTR;
146fb66942fSCasper H.S. Dik break;
147fb66942fSCasper H.S. Dik default:
148fb66942fSCasper H.S. Dik cmn_err(CE_WARN, "%s: unexpected pci header type:%x",
149fb66942fSCasper H.S. Dik __func__, header);
150fb66942fSCasper H.S. Dik return (DDI_FAILURE);
151fb66942fSCasper H.S. Dik }
152fb66942fSCasper H.S. Dik
153*c1bbf920SRobert Mustacchi ncaps = 0;
154fb66942fSCasper H.S. Dik for (base = pci_config_get8(h, base); base;
15527255037Spjha base = pci_config_get8(h, base + PCI_CAP_NEXT_PTR)) {
15627255037Spjha if (pci_config_get8(h, base) == id) {
15727255037Spjha *base_p = base;
15827255037Spjha return (DDI_SUCCESS);
15927255037Spjha }
160*c1bbf920SRobert Mustacchi
161*c1bbf920SRobert Mustacchi ncaps++;
162*c1bbf920SRobert Mustacchi if (ncaps >= PCI_CAP_MAX_PTR)
163*c1bbf920SRobert Mustacchi break;
16427255037Spjha }
16527255037Spjha
16627255037Spjha *base_p = PCI_CAP_NEXT_PTR_NULL;
16727255037Spjha return (DDI_FAILURE);
16827255037Spjha }
16927255037Spjha
17027255037Spjha /*
17127255037Spjha * pci_xcap_locate: Helper function locates a base in extended config space.
17227255037Spjha */
17327255037Spjha int
pci_xcap_locate(ddi_acc_handle_t h,uint16_t id,uint16_t * base_p)17427255037Spjha pci_xcap_locate(ddi_acc_handle_t h, uint16_t id, uint16_t *base_p)
17527255037Spjha {
17627255037Spjha uint16_t status, base;
177*c1bbf920SRobert Mustacchi uint32_t xcaps_hdr, ncaps;
17827255037Spjha
17927255037Spjha status = pci_config_get16(h, PCI_CONF_STAT);
18027255037Spjha
181dc5d169bSpjha if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
18227255037Spjha return (DDI_FAILURE);
18327255037Spjha
184*c1bbf920SRobert Mustacchi ncaps = 0;
18527255037Spjha for (base = PCIE_EXT_CAP; base; base = (xcaps_hdr >>
18627255037Spjha PCIE_EXT_CAP_NEXT_PTR_SHIFT) & PCIE_EXT_CAP_NEXT_PTR_MASK) {
18727255037Spjha
188dc5d169bSpjha if ((xcaps_hdr = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
18927255037Spjha break;
19027255037Spjha
19127255037Spjha if (((xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT) &
19227255037Spjha PCIE_EXT_CAP_ID_MASK) == id) {
19327255037Spjha *base_p = base;
19427255037Spjha return (DDI_SUCCESS);
19527255037Spjha }
196*c1bbf920SRobert Mustacchi
197*c1bbf920SRobert Mustacchi ncaps++;
198*c1bbf920SRobert Mustacchi if (ncaps >= PCIE_EXT_CAP_MAX_PTR)
199*c1bbf920SRobert Mustacchi break;
20027255037Spjha }
20127255037Spjha
20227255037Spjha *base_p = PCI_CAP_NEXT_PTR_NULL;
20327255037Spjha return (DDI_FAILURE);
20427255037Spjha }
20527255037Spjha
20627255037Spjha /*
207cb7ea99dSJimmy Vetayases * There can be multiple pci caps with a Hypertransport technology cap ID
208cb7ea99dSJimmy Vetayases * Each is distiguished by a type register in the upper half of the cap
209cb7ea99dSJimmy Vetayases * header (the "command" register part).
210cb7ea99dSJimmy Vetayases *
211cb7ea99dSJimmy Vetayases * This returns the location of a hypertransport capability whose upper
212cb7ea99dSJimmy Vetayases * 16-bits of the cap header matches <reg_val> after masking the value
213cb7ea99dSJimmy Vetayases * with <reg_mask>; if both <reg_mask> and <reg_val> are 0, it will return
214cb7ea99dSJimmy Vetayases * the first HT cap found
215cb7ea99dSJimmy Vetayases */
216cb7ea99dSJimmy Vetayases int
pci_htcap_locate(ddi_acc_handle_t h,uint16_t reg_mask,uint16_t reg_val,uint16_t * base_p)217cb7ea99dSJimmy Vetayases pci_htcap_locate(ddi_acc_handle_t h, uint16_t reg_mask, uint16_t reg_val,
218cb7ea99dSJimmy Vetayases uint16_t *base_p)
219cb7ea99dSJimmy Vetayases {
220cb7ea99dSJimmy Vetayases uint8_t header;
221cb7ea99dSJimmy Vetayases uint16_t status, base;
222cb7ea99dSJimmy Vetayases
223cb7ea99dSJimmy Vetayases status = pci_config_get16(h, PCI_CONF_STAT);
224cb7ea99dSJimmy Vetayases
225cb7ea99dSJimmy Vetayases if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
226cb7ea99dSJimmy Vetayases return (DDI_FAILURE);
227cb7ea99dSJimmy Vetayases
228cb7ea99dSJimmy Vetayases header = pci_config_get8(h, PCI_CONF_HEADER);
229cb7ea99dSJimmy Vetayases switch (header & PCI_HEADER_TYPE_M) {
230cb7ea99dSJimmy Vetayases case PCI_HEADER_ZERO:
231cb7ea99dSJimmy Vetayases base = PCI_CONF_CAP_PTR;
232cb7ea99dSJimmy Vetayases break;
233cb7ea99dSJimmy Vetayases case PCI_HEADER_PPB:
234cb7ea99dSJimmy Vetayases base = PCI_BCNF_CAP_PTR;
235cb7ea99dSJimmy Vetayases break;
236cb7ea99dSJimmy Vetayases default:
237cb7ea99dSJimmy Vetayases cmn_err(CE_WARN, "%s: unexpected pci header type:%x",
238cb7ea99dSJimmy Vetayases __func__, header);
239cb7ea99dSJimmy Vetayases return (DDI_FAILURE);
240cb7ea99dSJimmy Vetayases }
241cb7ea99dSJimmy Vetayases
242cb7ea99dSJimmy Vetayases for (base = pci_config_get8(h, base); base;
243cb7ea99dSJimmy Vetayases base = pci_config_get8(h, base + PCI_CAP_NEXT_PTR)) {
244cb7ea99dSJimmy Vetayases if (pci_config_get8(h, base) == PCI_CAP_ID_HT &&
245cb7ea99dSJimmy Vetayases (pci_config_get16(h, base + PCI_CAP_ID_REGS_OFF) &
246cb7ea99dSJimmy Vetayases reg_mask) == reg_val) {
247cb7ea99dSJimmy Vetayases *base_p = base;
248cb7ea99dSJimmy Vetayases return (DDI_SUCCESS);
249cb7ea99dSJimmy Vetayases }
250cb7ea99dSJimmy Vetayases }
251cb7ea99dSJimmy Vetayases
252cb7ea99dSJimmy Vetayases *base_p = PCI_CAP_NEXT_PTR_NULL;
253cb7ea99dSJimmy Vetayases return (DDI_FAILURE);
254cb7ea99dSJimmy Vetayases }
255cb7ea99dSJimmy Vetayases
256cb7ea99dSJimmy Vetayases /*
25727255037Spjha * pci_cap_get: This function uses the base or capid to get a byte, word,
25827255037Spjha * or dword. If access by capid is requested, the function uses the capid to
25927255037Spjha * locate the base. Access by a base results in better performance
26027255037Spjha * because no cap list traversal is required.
26127255037Spjha */
26227255037Spjha 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)263*c1bbf920SRobert Mustacchi pci_cap_get(ddi_acc_handle_t h, pci_cap_config_size_t size, uint32_t id,
264*c1bbf920SRobert Mustacchi uint16_t base, uint16_t offset)
26527255037Spjha {
26627255037Spjha uint32_t data;
26727255037Spjha
26827255037Spjha if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
269dc5d169bSpjha return (PCI_CAP_EINVAL32);
27027255037Spjha
27127255037Spjha /*
272911fc2e5Spjha * Each access to a PCI Configuration Space should be checked
273911fc2e5Spjha * by the calling function. A returned value of the 2's complement
274911fc2e5Spjha * of -1 indicates that either the device is offlined or it does not
275911fc2e5Spjha * exist.
27627255037Spjha */
27727255037Spjha offset += base;
27827255037Spjha
27927255037Spjha switch (size) {
28027255037Spjha case PCI_CAP_CFGSZ_8:
281911fc2e5Spjha data = pci_config_get8(h, offset);
28227255037Spjha break;
28327255037Spjha case PCI_CAP_CFGSZ_16:
284911fc2e5Spjha data = pci_config_get16(h, offset);
28527255037Spjha break;
28627255037Spjha case PCI_CAP_CFGSZ_32:
287911fc2e5Spjha data = pci_config_get32(h, offset);
28827255037Spjha break;
28927255037Spjha default:
290dc5d169bSpjha data = PCI_CAP_EINVAL32;
29127255037Spjha }
29227255037Spjha
29327255037Spjha PCI_CAP_DBG("pci_cap_get: %p[x%x]=x%x\n", (void *)h, offset, data);
29427255037Spjha return (data);
29527255037Spjha }
29627255037Spjha
29727255037Spjha /*
29827255037Spjha * pci_cap_put: This function uses the caps ptr or capid to put a byte, word,
29927255037Spjha * or dword. If access by capid is requested, the function uses the capid to
30027255037Spjha * locate the base. Access by base results in better performance
30127255037Spjha * because no cap list traversal is required.
30227255037Spjha */
30327255037Spjha 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)3043c4226f9Spjha pci_cap_put(ddi_acc_handle_t h, pci_cap_config_size_t size,
305*c1bbf920SRobert Mustacchi uint32_t id, uint16_t base, uint16_t offset, uint32_t data)
30627255037Spjha {
30727255037Spjha
30827255037Spjha /*
30927255037Spjha * use the pci_config_size_t to switch for the appropriate read
31027255037Spjha */
31127255037Spjha if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
31227255037Spjha return (DDI_FAILURE);
31327255037Spjha
31427255037Spjha offset += base;
31527255037Spjha
31627255037Spjha switch (size) {
31727255037Spjha case PCI_CAP_CFGSZ_8:
31827255037Spjha pci_config_put8(h, offset, data);
31927255037Spjha break;
32027255037Spjha case PCI_CAP_CFGSZ_16:
32127255037Spjha pci_config_put16(h, offset, data);
32227255037Spjha break;
32327255037Spjha case PCI_CAP_CFGSZ_32:
32427255037Spjha pci_config_put32(h, offset, data);
32527255037Spjha break;
32627255037Spjha default:
32727255037Spjha return (DDI_FAILURE);
32827255037Spjha }
32927255037Spjha
33027255037Spjha PCI_CAP_DBG("pci_cap_put: data=%x\n", data);
33127255037Spjha return (DDI_SUCCESS);
33227255037Spjha }
33327255037Spjha
33427255037Spjha /*
33527255037Spjha * Cache the entire Cap Structure. The caller is required to allocate and free
33627255037Spjha * buffer.
33727255037Spjha */
33827255037Spjha int
pci_cap_read(ddi_acc_handle_t h,uint32_t id,uint16_t base,uint32_t * buf_p,uint32_t nwords)33927255037Spjha pci_cap_read(ddi_acc_handle_t h, uint32_t id, uint16_t base,
34027255037Spjha uint32_t *buf_p, uint32_t nwords)
34127255037Spjha {
34227255037Spjha
34327255037Spjha int i;
34427255037Spjha uint32_t *ptr;
34527255037Spjha
34627255037Spjha ASSERT(nwords < 1024);
34727255037Spjha
34827255037Spjha if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
34927255037Spjha return (DDI_FAILURE);
35027255037Spjha
35127255037Spjha for (ptr = buf_p, i = 0; i < nwords; i++, base += 4) {
352dc5d169bSpjha if ((*ptr++ = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
35327255037Spjha return (DDI_FAILURE);
35427255037Spjha }
35527255037Spjha
35627255037Spjha return (DDI_SUCCESS);
35727255037Spjha }
358