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