1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 #include <sys/note.h> 23 #include <sys/conf.h> 24 #include <sys/debug.h> 25 #include <sys/sunddi.h> 26 #include <sys/pci.h> 27 #include <sys/pcie.h> 28 #include <sys/bitmap.h> 29 #include <sys/autoconf.h> 30 #include <sys/sysmacros.h> 31 #include <sys/pci_cap.h> 32 33 /* 34 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 35 * Use is subject to license terms. 36 */ 37 38 #pragma ident "%Z%%M% %I% %E% SMI" 39 40 /* 41 * Generic PCI Capabilites Interface for all pci platforms 42 */ 43 44 #ifdef DEBUG 45 uint_t pci_cap_debug = 0; 46 #endif 47 48 /* Cap Base Macro */ 49 #define PCI_CAP_BASE(h, id, base_p) (*base_p ? DDI_SUCCESS : \ 50 (id ? PCI_CAP_LOCATE(h, id, base_p) : DDI_FAILURE)) 51 52 /* 53 * pci_cap_probe: returns the capid and base based upon a given index 54 */ 55 int 56 pci_cap_probe(ddi_acc_handle_t h, uint16_t index, 57 uint32_t *id_p, uint16_t *base_p) 58 { 59 int i, search_ext = 0; 60 uint16_t base, pcix_cmd, status; 61 uint32_t id, xcaps_hdr; /* Extended Caps Header Word */ 62 63 status = pci_config_get16(h, PCI_CONF_STAT); 64 65 if (status == 0xffff || !(status & PCI_STAT_CAP)) 66 return (DDI_FAILURE); 67 68 /* PCIE and PCIX Version 2 contain Extended Config Space */ 69 for (i = 0, base = pci_config_get8(h, PCI_CONF_CAP_PTR); 70 base && i < index; base = pci_config_get8(h, base 71 + PCI_CAP_NEXT_PTR), i++) { 72 73 if ((id = pci_config_get8(h, base)) == 0xff) 74 break; 75 76 if (id == PCI_CAP_ID_PCI_E) 77 search_ext = 1; 78 else if (id == PCI_CAP_ID_PCIX) { 79 if ((pcix_cmd = pci_config_get16(h, base + 80 PCI_PCIX_COMMAND)) != 0xffff) 81 continue; 82 if ((pcix_cmd & PCI_PCIX_VER_MASK) == PCI_PCIX_VER_2) 83 search_ext = 1; 84 } 85 } 86 87 if (base && i == index) { 88 if ((id = pci_config_get8(h, base)) != 0xff) 89 goto found; 90 } 91 92 if (!search_ext) 93 return (DDI_FAILURE); 94 95 for (base = PCIE_EXT_CAP; base && i < index; i++) { 96 if ((xcaps_hdr = pci_config_get32(h, base)) == 0xffffffff) 97 break; 98 99 id = (xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT) 100 & PCIE_EXT_CAP_ID_MASK; 101 base = (xcaps_hdr >> PCIE_EXT_CAP_NEXT_PTR_SHIFT) 102 & PCIE_EXT_CAP_NEXT_PTR_MASK; 103 } 104 105 if (!base || i < index) 106 return (DDI_FAILURE); 107 108 if ((xcaps_hdr = pci_config_get32(h, base)) == 0xffffffff) 109 return (DDI_FAILURE); 110 111 id = ((xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT) & PCIE_EXT_CAP_ID_MASK) | 112 PCI_CAP_XCFG_FLAG; 113 found: 114 PCI_CAP_DBG("pci_cap_probe: index=%x, id=%x, base=%x\n", 115 index, id, base); 116 117 *id_p = id; 118 *base_p = base; 119 return (DDI_SUCCESS); 120 121 } 122 123 /* 124 * pci_lcap_locate: Helper function locates a base in conventional config space. 125 */ 126 int 127 pci_lcap_locate(ddi_acc_handle_t h, uint8_t id, uint16_t *base_p) 128 { 129 uint16_t status, base; 130 131 status = pci_config_get16(h, PCI_CONF_STAT); 132 133 if (status == 0xffff || !(status & PCI_STAT_CAP)) 134 return (DDI_FAILURE); 135 136 for (base = pci_config_get8(h, PCI_CONF_CAP_PTR); base; 137 base = pci_config_get8(h, base + PCI_CAP_NEXT_PTR)) { 138 if (pci_config_get8(h, base) == id) { 139 *base_p = base; 140 return (DDI_SUCCESS); 141 } 142 } 143 144 *base_p = PCI_CAP_NEXT_PTR_NULL; 145 return (DDI_FAILURE); 146 147 148 } 149 150 /* 151 * pci_xcap_locate: Helper function locates a base in extended config space. 152 */ 153 int 154 pci_xcap_locate(ddi_acc_handle_t h, uint16_t id, uint16_t *base_p) 155 { 156 uint16_t status, base; 157 uint32_t xcaps_hdr; 158 159 status = pci_config_get16(h, PCI_CONF_STAT); 160 161 if (status == 0xffff || !(status & PCI_STAT_CAP)) 162 return (DDI_FAILURE); 163 164 for (base = PCIE_EXT_CAP; base; base = (xcaps_hdr >> 165 PCIE_EXT_CAP_NEXT_PTR_SHIFT) & PCIE_EXT_CAP_NEXT_PTR_MASK) { 166 167 if ((xcaps_hdr = pci_config_get32(h, base)) == 0xffffffff) 168 break; 169 170 if (((xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT) & 171 PCIE_EXT_CAP_ID_MASK) == id) { 172 *base_p = base; 173 return (DDI_SUCCESS); 174 } 175 } 176 177 *base_p = PCI_CAP_NEXT_PTR_NULL; 178 return (DDI_FAILURE); 179 } 180 181 /* 182 * pci_cap_get: This function uses the base or capid to get a byte, word, 183 * or dword. If access by capid is requested, the function uses the capid to 184 * locate the base. Access by a base results in better performance 185 * because no cap list traversal is required. 186 */ 187 uint32_t 188 pci_cap_get(ddi_acc_handle_t h, pci_config_size_t size, 189 uint32_t id, uint16_t base, uint16_t offset) 190 { 191 uint32_t data; 192 193 if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS) 194 return (DDI_FAILURE); 195 196 /* 197 * Each access to a PCI Configuration Space is checked for 198 * returned value of -1, in case the a device is offlined 199 * or if it does not exist. 200 */ 201 offset += base; 202 203 switch (size) { 204 case PCI_CAP_CFGSZ_8: 205 if ((data = pci_config_get8(h, offset)) == 0xff) 206 return (DDI_FAILURE); 207 break; 208 case PCI_CAP_CFGSZ_16: 209 if ((data = pci_config_get16(h, offset)) == 0xffff) 210 return (DDI_FAILURE); 211 break; 212 case PCI_CAP_CFGSZ_32: 213 if ((data = pci_config_get32(h, offset)) == 0xffffffff) 214 return (DDI_FAILURE); 215 break; 216 default: 217 return (DDI_FAILURE); 218 } 219 220 PCI_CAP_DBG("pci_cap_get: %p[x%x]=x%x\n", (void *)h, offset, data); 221 return (data); 222 } 223 224 /* 225 * pci_cap_put: This function uses the caps ptr or capid to put a byte, word, 226 * or dword. If access by capid is requested, the function uses the capid to 227 * locate the base. Access by base results in better performance 228 * because no cap list traversal is required. 229 */ 230 int 231 pci_cap_put(ddi_acc_handle_t h, pci_config_size_t size, 232 uint32_t id, uint16_t base, uint16_t offset, 233 uint32_t data) 234 { 235 236 /* 237 * use the pci_config_size_t to switch for the appropriate read 238 */ 239 if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS) 240 return (DDI_FAILURE); 241 242 offset += base; 243 244 switch (size) { 245 case PCI_CAP_CFGSZ_8: 246 pci_config_put8(h, offset, data); 247 break; 248 case PCI_CAP_CFGSZ_16: 249 pci_config_put16(h, offset, data); 250 break; 251 case PCI_CAP_CFGSZ_32: 252 pci_config_put32(h, offset, data); 253 break; 254 default: 255 return (DDI_FAILURE); 256 } 257 258 PCI_CAP_DBG("pci_cap_put: data=%x\n", data); 259 return (DDI_SUCCESS); 260 } 261 262 /* 263 * Cache the entire Cap Structure. The caller is required to allocate and free 264 * buffer. 265 */ 266 int 267 pci_cap_read(ddi_acc_handle_t h, uint32_t id, uint16_t base, 268 uint32_t *buf_p, uint32_t nwords) 269 { 270 271 int i; 272 uint32_t *ptr; 273 274 ASSERT(nwords < 1024); 275 276 if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS) 277 return (DDI_FAILURE); 278 279 for (ptr = buf_p, i = 0; i < nwords; i++, base += 4) { 280 if ((*ptr++ = pci_config_get32(h, base)) == 0xffffffff) 281 return (DDI_FAILURE); 282 } 283 284 return (DDI_SUCCESS); 285 } 286