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 /* 34*9284SCasper.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 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); 681624Spjha base && i < index; base = pci_config_get8(h, base 691624Spjha + 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 + 781774Spjha 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) 981624Spjha & PCIE_EXT_CAP_ID_MASK; 991624Spjha base = (xcaps_hdr >> PCIE_EXT_CAP_NEXT_PTR_SHIFT) 1001624Spjha & 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) | 1101624Spjha PCI_CAP_XCFG_FLAG; 1111624Spjha found: 1121624Spjha PCI_CAP_DBG("pci_cap_probe: index=%x, id=%x, base=%x\n", 1131624Spjha 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 1251624Spjha pci_lcap_locate(ddi_acc_handle_t h, uint8_t id, uint16_t *base_p) 1261624Spjha { 127*9284SCasper.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 135*9284SCasper.Dik@Sun.COM header = pci_config_get8(h, PCI_CONF_HEADER); 136*9284SCasper.Dik@Sun.COM switch (header & PCI_HEADER_TYPE_M) { 137*9284SCasper.Dik@Sun.COM case PCI_HEADER_ZERO: 138*9284SCasper.Dik@Sun.COM base = PCI_CONF_CAP_PTR; 139*9284SCasper.Dik@Sun.COM break; 140*9284SCasper.Dik@Sun.COM case PCI_HEADER_PPB: 141*9284SCasper.Dik@Sun.COM base = PCI_BCNF_CAP_PTR; 142*9284SCasper.Dik@Sun.COM break; 143*9284SCasper.Dik@Sun.COM case PCI_HEADER_CARDBUS: 144*9284SCasper.Dik@Sun.COM base = PCI_CBUS_CAP_PTR; 145*9284SCasper.Dik@Sun.COM break; 146*9284SCasper.Dik@Sun.COM default: 147*9284SCasper.Dik@Sun.COM cmn_err(CE_WARN, "%s: unexpected pci header type:%x", 148*9284SCasper.Dik@Sun.COM __func__, header); 149*9284SCasper.Dik@Sun.COM return (DDI_FAILURE); 150*9284SCasper.Dik@Sun.COM } 151*9284SCasper.Dik@Sun.COM 152*9284SCasper.Dik@Sun.COM for (base = pci_config_get8(h, base); base; 1531624Spjha 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 1661624Spjha /* 1671624Spjha * pci_xcap_locate: Helper function locates a base in extended config space. 1681624Spjha */ 1691624Spjha int 1701624Spjha pci_xcap_locate(ddi_acc_handle_t h, uint16_t id, uint16_t *base_p) 1711624Spjha { 1721624Spjha uint16_t status, base; 1731624Spjha uint32_t xcaps_hdr; 1741624Spjha 1751624Spjha status = pci_config_get16(h, PCI_CONF_STAT); 1761624Spjha 1771774Spjha if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP)) 1781624Spjha return (DDI_FAILURE); 1791624Spjha 1801624Spjha for (base = PCIE_EXT_CAP; base; base = (xcaps_hdr >> 1811624Spjha PCIE_EXT_CAP_NEXT_PTR_SHIFT) & PCIE_EXT_CAP_NEXT_PTR_MASK) { 1821624Spjha 1831774Spjha if ((xcaps_hdr = pci_config_get32(h, base)) == PCI_CAP_EINVAL32) 1841624Spjha break; 1851624Spjha 1861624Spjha if (((xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT) & 1871624Spjha PCIE_EXT_CAP_ID_MASK) == id) { 1881624Spjha *base_p = base; 1891624Spjha return (DDI_SUCCESS); 1901624Spjha } 1911624Spjha } 1921624Spjha 1931624Spjha *base_p = PCI_CAP_NEXT_PTR_NULL; 1941624Spjha return (DDI_FAILURE); 1951624Spjha } 1961624Spjha 1971624Spjha /* 1981624Spjha * pci_cap_get: This function uses the base or capid to get a byte, word, 1991624Spjha * or dword. If access by capid is requested, the function uses the capid to 2001624Spjha * locate the base. Access by a base results in better performance 2011624Spjha * because no cap list traversal is required. 2021624Spjha */ 2031624Spjha uint32_t 2042587Spjha pci_cap_get(ddi_acc_handle_t h, pci_cap_config_size_t size, 2051624Spjha uint32_t id, uint16_t base, uint16_t offset) 2061624Spjha { 2071624Spjha uint32_t data; 2081624Spjha 2091624Spjha if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS) 2101774Spjha return (PCI_CAP_EINVAL32); 2111624Spjha 2121624Spjha /* 2131644Spjha * Each access to a PCI Configuration Space should be checked 2141644Spjha * by the calling function. A returned value of the 2's complement 2151644Spjha * of -1 indicates that either the device is offlined or it does not 2161644Spjha * exist. 2171624Spjha */ 2181624Spjha offset += base; 2191624Spjha 2201624Spjha switch (size) { 2211624Spjha case PCI_CAP_CFGSZ_8: 2221644Spjha data = pci_config_get8(h, offset); 2231624Spjha break; 2241624Spjha case PCI_CAP_CFGSZ_16: 2251644Spjha data = pci_config_get16(h, offset); 2261624Spjha break; 2271624Spjha case PCI_CAP_CFGSZ_32: 2281644Spjha data = pci_config_get32(h, offset); 2291624Spjha break; 2301624Spjha default: 2311774Spjha data = PCI_CAP_EINVAL32; 2321624Spjha } 2331624Spjha 2341624Spjha PCI_CAP_DBG("pci_cap_get: %p[x%x]=x%x\n", (void *)h, offset, data); 2351624Spjha return (data); 2361624Spjha } 2371624Spjha 2381624Spjha /* 2391624Spjha * pci_cap_put: This function uses the caps ptr or capid to put a byte, word, 2401624Spjha * or dword. If access by capid is requested, the function uses the capid to 2411624Spjha * locate the base. Access by base results in better performance 2421624Spjha * because no cap list traversal is required. 2431624Spjha */ 2441624Spjha int 2452587Spjha pci_cap_put(ddi_acc_handle_t h, pci_cap_config_size_t size, 2461624Spjha uint32_t id, uint16_t base, uint16_t offset, 2471624Spjha uint32_t data) 2481624Spjha { 2491624Spjha 2501624Spjha /* 2511624Spjha * use the pci_config_size_t to switch for the appropriate read 2521624Spjha */ 2531624Spjha if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS) 2541624Spjha return (DDI_FAILURE); 2551624Spjha 2561624Spjha offset += base; 2571624Spjha 2581624Spjha switch (size) { 2591624Spjha case PCI_CAP_CFGSZ_8: 2601624Spjha pci_config_put8(h, offset, data); 2611624Spjha break; 2621624Spjha case PCI_CAP_CFGSZ_16: 2631624Spjha pci_config_put16(h, offset, data); 2641624Spjha break; 2651624Spjha case PCI_CAP_CFGSZ_32: 2661624Spjha pci_config_put32(h, offset, data); 2671624Spjha break; 2681624Spjha default: 2691624Spjha return (DDI_FAILURE); 2701624Spjha } 2711624Spjha 2721624Spjha PCI_CAP_DBG("pci_cap_put: data=%x\n", data); 2731624Spjha return (DDI_SUCCESS); 2741624Spjha } 2751624Spjha 2761624Spjha /* 2771624Spjha * Cache the entire Cap Structure. The caller is required to allocate and free 2781624Spjha * buffer. 2791624Spjha */ 2801624Spjha int 2811624Spjha pci_cap_read(ddi_acc_handle_t h, uint32_t id, uint16_t base, 2821624Spjha uint32_t *buf_p, uint32_t nwords) 2831624Spjha { 2841624Spjha 2851624Spjha int i; 2861624Spjha uint32_t *ptr; 2871624Spjha 2881624Spjha ASSERT(nwords < 1024); 2891624Spjha 2901624Spjha if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS) 2911624Spjha return (DDI_FAILURE); 2921624Spjha 2931624Spjha for (ptr = buf_p, i = 0; i < nwords; i++, base += 4) { 2941774Spjha if ((*ptr++ = pci_config_get32(h, base)) == PCI_CAP_EINVAL32) 2951624Spjha return (DDI_FAILURE); 2961624Spjha } 2971624Spjha 2981624Spjha return (DDI_SUCCESS); 2991624Spjha } 300