11624Spjha /*
21624Spjha * CDDL HEADER START
31624Spjha *
41624Spjha * The contents of this file are subject to the terms of the
51624Spjha * Common Development and Distribution License (the "License").
61624Spjha * You may not use this file except in compliance with the License.
71624Spjha *
81624Spjha * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91624Spjha * or http://www.opensolaris.org/os/licensing.
101624Spjha * See the License for the specific language governing permissions
111624Spjha * and limitations under the License.
121624Spjha *
131624Spjha * When distributing Covered Code, include this CDDL HEADER in each
141624Spjha * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151624Spjha * If applicable, add the following below this CDDL HEADER, with the
161624Spjha * fields enclosed by brackets "[]" replaced with your own identifying
171624Spjha * information: Portions Copyright [yyyy] [name of copyright owner]
181624Spjha *
191624Spjha * CDDL HEADER END
201624Spjha */
211624Spjha
221624Spjha #include <sys/note.h>
231624Spjha #include <sys/conf.h>
241624Spjha #include <sys/debug.h>
251624Spjha #include <sys/sunddi.h>
261624Spjha #include <sys/pci.h>
271624Spjha #include <sys/pcie.h>
281624Spjha #include <sys/bitmap.h>
291624Spjha #include <sys/autoconf.h>
301624Spjha #include <sys/sysmacros.h>
311624Spjha #include <sys/pci_cap.h>
321624Spjha
331624Spjha /*
349284SCasper.Dik@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
351624Spjha * Use is subject to license terms.
361624Spjha */
371624Spjha
381624Spjha /*
391624Spjha * Generic PCI Capabilites Interface for all pci platforms
401624Spjha */
411624Spjha
421624Spjha #ifdef DEBUG
431624Spjha uint_t pci_cap_debug = 0;
441624Spjha #endif
451624Spjha
461624Spjha /* Cap Base Macro */
471624Spjha #define PCI_CAP_BASE(h, id, base_p) (*base_p ? DDI_SUCCESS : \
481624Spjha (id ? PCI_CAP_LOCATE(h, id, base_p) : DDI_FAILURE))
491624Spjha
501624Spjha /*
511624Spjha * pci_cap_probe: returns the capid and base based upon a given index
521624Spjha */
531624Spjha int
pci_cap_probe(ddi_acc_handle_t h,uint16_t index,uint32_t * id_p,uint16_t * base_p)541624Spjha pci_cap_probe(ddi_acc_handle_t h, uint16_t index,
551624Spjha uint32_t *id_p, uint16_t *base_p)
561624Spjha {
571624Spjha int i, search_ext = 0;
581624Spjha uint16_t base, pcix_cmd, status;
591624Spjha uint32_t id, xcaps_hdr; /* Extended Caps Header Word */
601624Spjha
611624Spjha status = pci_config_get16(h, PCI_CONF_STAT);
621624Spjha
631774Spjha if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
641624Spjha return (DDI_FAILURE);
651624Spjha
661624Spjha /* PCIE and PCIX Version 2 contain Extended Config Space */
671624Spjha for (i = 0, base = pci_config_get8(h, PCI_CONF_CAP_PTR);
68*9970SJimmy.Vetayases@Sun.COM base && i < index; base = pci_config_get8(h, base
69*9970SJimmy.Vetayases@Sun.COM + PCI_CAP_NEXT_PTR), i++) {
701624Spjha
711624Spjha if ((id = pci_config_get8(h, base)) == 0xff)
721624Spjha break;
731624Spjha
741624Spjha if (id == PCI_CAP_ID_PCI_E)
751624Spjha search_ext = 1;
761624Spjha else if (id == PCI_CAP_ID_PCIX) {
771624Spjha if ((pcix_cmd = pci_config_get16(h, base +
78*9970SJimmy.Vetayases@Sun.COM PCI_PCIX_COMMAND)) != PCI_CAP_EINVAL16)
791624Spjha continue;
801624Spjha if ((pcix_cmd & PCI_PCIX_VER_MASK) == PCI_PCIX_VER_2)
811624Spjha search_ext = 1;
821624Spjha }
831624Spjha }
841624Spjha
851624Spjha if (base && i == index) {
861624Spjha if ((id = pci_config_get8(h, base)) != 0xff)
871624Spjha goto found;
881624Spjha }
891624Spjha
901624Spjha if (!search_ext)
911624Spjha return (DDI_FAILURE);
921624Spjha
931624Spjha for (base = PCIE_EXT_CAP; base && i < index; i++) {
941774Spjha if ((xcaps_hdr = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
951624Spjha break;
961624Spjha
971624Spjha id = (xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT)
98*9970SJimmy.Vetayases@Sun.COM & PCIE_EXT_CAP_ID_MASK;
991624Spjha base = (xcaps_hdr >> PCIE_EXT_CAP_NEXT_PTR_SHIFT)
100*9970SJimmy.Vetayases@Sun.COM & PCIE_EXT_CAP_NEXT_PTR_MASK;
1011624Spjha }
1021624Spjha
1031624Spjha if (!base || i < index)
1041624Spjha return (DDI_FAILURE);
1051624Spjha
1061774Spjha if ((xcaps_hdr = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
1071624Spjha return (DDI_FAILURE);
1081624Spjha
1091624Spjha id = ((xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT) & PCIE_EXT_CAP_ID_MASK) |
110*9970SJimmy.Vetayases@Sun.COM PCI_CAP_XCFG_FLAG;
1111624Spjha found:
1121624Spjha PCI_CAP_DBG("pci_cap_probe: index=%x, id=%x, base=%x\n",
113*9970SJimmy.Vetayases@Sun.COM index, id, base);
1141624Spjha
1151624Spjha *id_p = id;
1161624Spjha *base_p = base;
1171624Spjha return (DDI_SUCCESS);
1181624Spjha
1191624Spjha }
1201624Spjha
1211624Spjha /*
1221624Spjha * pci_lcap_locate: Helper function locates a base in conventional config space.
1231624Spjha */
1241624Spjha int
pci_lcap_locate(ddi_acc_handle_t h,uint8_t id,uint16_t * base_p)1251624Spjha pci_lcap_locate(ddi_acc_handle_t h, uint8_t id, uint16_t *base_p)
1261624Spjha {
1279284SCasper.Dik@Sun.COM uint8_t header;
1281624Spjha uint16_t status, base;
1291624Spjha
1301624Spjha status = pci_config_get16(h, PCI_CONF_STAT);
1311624Spjha
1321774Spjha if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
1331624Spjha return (DDI_FAILURE);
1341624Spjha
1359284SCasper.Dik@Sun.COM header = pci_config_get8(h, PCI_CONF_HEADER);
1369284SCasper.Dik@Sun.COM switch (header & PCI_HEADER_TYPE_M) {
1379284SCasper.Dik@Sun.COM case PCI_HEADER_ZERO:
1389284SCasper.Dik@Sun.COM base = PCI_CONF_CAP_PTR;
1399284SCasper.Dik@Sun.COM break;
1409284SCasper.Dik@Sun.COM case PCI_HEADER_PPB:
1419284SCasper.Dik@Sun.COM base = PCI_BCNF_CAP_PTR;
1429284SCasper.Dik@Sun.COM break;
1439284SCasper.Dik@Sun.COM case PCI_HEADER_CARDBUS:
1449284SCasper.Dik@Sun.COM base = PCI_CBUS_CAP_PTR;
1459284SCasper.Dik@Sun.COM break;
1469284SCasper.Dik@Sun.COM default:
1479284SCasper.Dik@Sun.COM cmn_err(CE_WARN, "%s: unexpected pci header type:%x",
148*9970SJimmy.Vetayases@Sun.COM __func__, header);
1499284SCasper.Dik@Sun.COM return (DDI_FAILURE);
1509284SCasper.Dik@Sun.COM }
1519284SCasper.Dik@Sun.COM
1529284SCasper.Dik@Sun.COM for (base = pci_config_get8(h, base); base;
153*9970SJimmy.Vetayases@Sun.COM base = pci_config_get8(h, base + PCI_CAP_NEXT_PTR)) {
1541624Spjha if (pci_config_get8(h, base) == id) {
1551624Spjha *base_p = base;
1561624Spjha return (DDI_SUCCESS);
1571624Spjha }
1581624Spjha }
1591624Spjha
1601624Spjha *base_p = PCI_CAP_NEXT_PTR_NULL;
1611624Spjha return (DDI_FAILURE);
1621624Spjha }
1631624Spjha
1641624Spjha /*
1651624Spjha * pci_xcap_locate: Helper function locates a base in extended config space.
1661624Spjha */
1671624Spjha int
pci_xcap_locate(ddi_acc_handle_t h,uint16_t id,uint16_t * base_p)1681624Spjha pci_xcap_locate(ddi_acc_handle_t h, uint16_t id, uint16_t *base_p)
1691624Spjha {
1701624Spjha uint16_t status, base;
1711624Spjha uint32_t xcaps_hdr;
1721624Spjha
1731624Spjha status = pci_config_get16(h, PCI_CONF_STAT);
1741624Spjha
1751774Spjha if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
1761624Spjha return (DDI_FAILURE);
1771624Spjha
1781624Spjha for (base = PCIE_EXT_CAP; base; base = (xcaps_hdr >>
179*9970SJimmy.Vetayases@Sun.COM PCIE_EXT_CAP_NEXT_PTR_SHIFT) & PCIE_EXT_CAP_NEXT_PTR_MASK) {
1801624Spjha
1811774Spjha if ((xcaps_hdr = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
1821624Spjha break;
1831624Spjha
1841624Spjha if (((xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT) &
185*9970SJimmy.Vetayases@Sun.COM PCIE_EXT_CAP_ID_MASK) == id) {
186*9970SJimmy.Vetayases@Sun.COM *base_p = base;
187*9970SJimmy.Vetayases@Sun.COM return (DDI_SUCCESS);
188*9970SJimmy.Vetayases@Sun.COM }
189*9970SJimmy.Vetayases@Sun.COM }
190*9970SJimmy.Vetayases@Sun.COM
191*9970SJimmy.Vetayases@Sun.COM *base_p = PCI_CAP_NEXT_PTR_NULL;
192*9970SJimmy.Vetayases@Sun.COM return (DDI_FAILURE);
193*9970SJimmy.Vetayases@Sun.COM }
194*9970SJimmy.Vetayases@Sun.COM
195*9970SJimmy.Vetayases@Sun.COM /*
196*9970SJimmy.Vetayases@Sun.COM * There can be multiple pci caps with a Hypertransport technology cap ID
197*9970SJimmy.Vetayases@Sun.COM * Each is distiguished by a type register in the upper half of the cap
198*9970SJimmy.Vetayases@Sun.COM * header (the "command" register part).
199*9970SJimmy.Vetayases@Sun.COM *
200*9970SJimmy.Vetayases@Sun.COM * This returns the location of a hypertransport capability whose upper
201*9970SJimmy.Vetayases@Sun.COM * 16-bits of the cap header matches <reg_val> after masking the value
202*9970SJimmy.Vetayases@Sun.COM * with <reg_mask>; if both <reg_mask> and <reg_val> are 0, it will return
203*9970SJimmy.Vetayases@Sun.COM * the first HT cap found
204*9970SJimmy.Vetayases@Sun.COM */
205*9970SJimmy.Vetayases@Sun.COM int
pci_htcap_locate(ddi_acc_handle_t h,uint16_t reg_mask,uint16_t reg_val,uint16_t * base_p)206*9970SJimmy.Vetayases@Sun.COM pci_htcap_locate(ddi_acc_handle_t h, uint16_t reg_mask, uint16_t reg_val,
207*9970SJimmy.Vetayases@Sun.COM uint16_t *base_p)
208*9970SJimmy.Vetayases@Sun.COM {
209*9970SJimmy.Vetayases@Sun.COM uint8_t header;
210*9970SJimmy.Vetayases@Sun.COM uint16_t status, base;
211*9970SJimmy.Vetayases@Sun.COM
212*9970SJimmy.Vetayases@Sun.COM status = pci_config_get16(h, PCI_CONF_STAT);
213*9970SJimmy.Vetayases@Sun.COM
214*9970SJimmy.Vetayases@Sun.COM if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
215*9970SJimmy.Vetayases@Sun.COM return (DDI_FAILURE);
216*9970SJimmy.Vetayases@Sun.COM
217*9970SJimmy.Vetayases@Sun.COM header = pci_config_get8(h, PCI_CONF_HEADER);
218*9970SJimmy.Vetayases@Sun.COM switch (header & PCI_HEADER_TYPE_M) {
219*9970SJimmy.Vetayases@Sun.COM case PCI_HEADER_ZERO:
220*9970SJimmy.Vetayases@Sun.COM base = PCI_CONF_CAP_PTR;
221*9970SJimmy.Vetayases@Sun.COM break;
222*9970SJimmy.Vetayases@Sun.COM case PCI_HEADER_PPB:
223*9970SJimmy.Vetayases@Sun.COM base = PCI_BCNF_CAP_PTR;
224*9970SJimmy.Vetayases@Sun.COM break;
225*9970SJimmy.Vetayases@Sun.COM default:
226*9970SJimmy.Vetayases@Sun.COM cmn_err(CE_WARN, "%s: unexpected pci header type:%x",
227*9970SJimmy.Vetayases@Sun.COM __func__, header);
228*9970SJimmy.Vetayases@Sun.COM return (DDI_FAILURE);
229*9970SJimmy.Vetayases@Sun.COM }
230*9970SJimmy.Vetayases@Sun.COM
231*9970SJimmy.Vetayases@Sun.COM for (base = pci_config_get8(h, base); base;
232*9970SJimmy.Vetayases@Sun.COM base = pci_config_get8(h, base + PCI_CAP_NEXT_PTR)) {
233*9970SJimmy.Vetayases@Sun.COM if (pci_config_get8(h, base) == PCI_CAP_ID_HT &&
234*9970SJimmy.Vetayases@Sun.COM (pci_config_get16(h, base + PCI_CAP_ID_REGS_OFF) &
235*9970SJimmy.Vetayases@Sun.COM reg_mask) == reg_val) {
2361624Spjha *base_p = base;
2371624Spjha return (DDI_SUCCESS);
2381624Spjha }
2391624Spjha }
2401624Spjha
2411624Spjha *base_p = PCI_CAP_NEXT_PTR_NULL;
2421624Spjha return (DDI_FAILURE);
2431624Spjha }
2441624Spjha
2451624Spjha /*
2461624Spjha * pci_cap_get: This function uses the base or capid to get a byte, word,
2471624Spjha * or dword. If access by capid is requested, the function uses the capid to
2481624Spjha * locate the base. Access by a base results in better performance
2491624Spjha * because no cap list traversal is required.
2501624Spjha */
2511624Spjha 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)2522587Spjha pci_cap_get(ddi_acc_handle_t h, pci_cap_config_size_t size,
2531624Spjha uint32_t id, uint16_t base, uint16_t offset)
2541624Spjha {
2551624Spjha uint32_t data;
2561624Spjha
2571624Spjha if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
2581774Spjha return (PCI_CAP_EINVAL32);
2591624Spjha
2601624Spjha /*
2611644Spjha * Each access to a PCI Configuration Space should be checked
2621644Spjha * by the calling function. A returned value of the 2's complement
2631644Spjha * of -1 indicates that either the device is offlined or it does not
2641644Spjha * exist.
2651624Spjha */
2661624Spjha offset += base;
2671624Spjha
2681624Spjha switch (size) {
2691624Spjha case PCI_CAP_CFGSZ_8:
2701644Spjha data = pci_config_get8(h, offset);
2711624Spjha break;
2721624Spjha case PCI_CAP_CFGSZ_16:
2731644Spjha data = pci_config_get16(h, offset);
2741624Spjha break;
2751624Spjha case PCI_CAP_CFGSZ_32:
2761644Spjha data = pci_config_get32(h, offset);
2771624Spjha break;
2781624Spjha default:
2791774Spjha data = PCI_CAP_EINVAL32;
2801624Spjha }
2811624Spjha
2821624Spjha PCI_CAP_DBG("pci_cap_get: %p[x%x]=x%x\n", (void *)h, offset, data);
2831624Spjha return (data);
2841624Spjha }
2851624Spjha
2861624Spjha /*
2871624Spjha * pci_cap_put: This function uses the caps ptr or capid to put a byte, word,
2881624Spjha * or dword. If access by capid is requested, the function uses the capid to
2891624Spjha * locate the base. Access by base results in better performance
2901624Spjha * because no cap list traversal is required.
2911624Spjha */
2921624Spjha 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)2932587Spjha pci_cap_put(ddi_acc_handle_t h, pci_cap_config_size_t size,
2941624Spjha uint32_t id, uint16_t base, uint16_t offset,
2951624Spjha uint32_t data)
2961624Spjha {
2971624Spjha
2981624Spjha /*
2991624Spjha * use the pci_config_size_t to switch for the appropriate read
3001624Spjha */
3011624Spjha if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
3021624Spjha return (DDI_FAILURE);
3031624Spjha
3041624Spjha offset += base;
3051624Spjha
3061624Spjha switch (size) {
3071624Spjha case PCI_CAP_CFGSZ_8:
3081624Spjha pci_config_put8(h, offset, data);
3091624Spjha break;
3101624Spjha case PCI_CAP_CFGSZ_16:
3111624Spjha pci_config_put16(h, offset, data);
3121624Spjha break;
3131624Spjha case PCI_CAP_CFGSZ_32:
3141624Spjha pci_config_put32(h, offset, data);
3151624Spjha break;
3161624Spjha default:
3171624Spjha return (DDI_FAILURE);
3181624Spjha }
3191624Spjha
3201624Spjha PCI_CAP_DBG("pci_cap_put: data=%x\n", data);
3211624Spjha return (DDI_SUCCESS);
3221624Spjha }
3231624Spjha
3241624Spjha /*
3251624Spjha * Cache the entire Cap Structure. The caller is required to allocate and free
3261624Spjha * buffer.
3271624Spjha */
3281624Spjha int
pci_cap_read(ddi_acc_handle_t h,uint32_t id,uint16_t base,uint32_t * buf_p,uint32_t nwords)3291624Spjha pci_cap_read(ddi_acc_handle_t h, uint32_t id, uint16_t base,
3301624Spjha uint32_t *buf_p, uint32_t nwords)
3311624Spjha {
3321624Spjha
3331624Spjha int i;
3341624Spjha uint32_t *ptr;
3351624Spjha
3361624Spjha ASSERT(nwords < 1024);
3371624Spjha
3381624Spjha if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
3391624Spjha return (DDI_FAILURE);
3401624Spjha
3411624Spjha for (ptr = buf_p, i = 0; i < nwords; i++, base += 4) {
3421774Spjha if ((*ptr++ = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
3431624Spjha return (DDI_FAILURE);
3441624Spjha }
3451624Spjha
3461624Spjha return (DDI_SUCCESS);
3471624Spjha }
348