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 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 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 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 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 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 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 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