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 2009 Sun Microsystems, Inc. All rights reserved. 35 * Use is subject to license terms. 36 */ 37 38 /* 39 * Generic PCI Capabilites Interface for all pci platforms 40 */ 41 42 #ifdef DEBUG 43 uint_t pci_cap_debug = 0; 44 #endif 45 46 /* Cap Base Macro */ 47 #define PCI_CAP_BASE(h, id, base_p) (*base_p ? DDI_SUCCESS : \ 48 (id ? PCI_CAP_LOCATE(h, id, base_p) : DDI_FAILURE)) 49 50 /* 51 * pci_cap_probe: returns the capid and base based upon a given index 52 */ 53 int 54 pci_cap_probe(ddi_acc_handle_t h, uint16_t index, 55 uint32_t *id_p, uint16_t *base_p) 56 { 57 int i, search_ext = 0; 58 uint16_t base, pcix_cmd, status; 59 uint32_t id, xcaps_hdr; /* Extended Caps Header Word */ 60 61 status = pci_config_get16(h, PCI_CONF_STAT); 62 63 if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP)) 64 return (DDI_FAILURE); 65 66 /* PCIE and PCIX Version 2 contain Extended Config Space */ 67 for (i = 0, base = pci_config_get8(h, PCI_CONF_CAP_PTR); 68 base && i < index; base = pci_config_get8(h, base 69 + PCI_CAP_NEXT_PTR), i++) { 70 71 if ((id = pci_config_get8(h, base)) == 0xff) 72 break; 73 74 if (id == PCI_CAP_ID_PCI_E) 75 search_ext = 1; 76 else if (id == PCI_CAP_ID_PCIX) { 77 if ((pcix_cmd = pci_config_get16(h, base + 78 PCI_PCIX_COMMAND)) != PCI_CAP_EINVAL16) 79 continue; 80 if ((pcix_cmd & PCI_PCIX_VER_MASK) == PCI_PCIX_VER_2) 81 search_ext = 1; 82 } 83 } 84 85 if (base && i == index) { 86 if ((id = pci_config_get8(h, base)) != 0xff) 87 goto found; 88 } 89 90 if (!search_ext) 91 return (DDI_FAILURE); 92 93 for (base = PCIE_EXT_CAP; base && i < index; i++) { 94 if ((xcaps_hdr = pci_config_get32(h, base)) == PCI_CAP_EINVAL32) 95 break; 96 97 id = (xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT) 98 & PCIE_EXT_CAP_ID_MASK; 99 base = (xcaps_hdr >> PCIE_EXT_CAP_NEXT_PTR_SHIFT) 100 & PCIE_EXT_CAP_NEXT_PTR_MASK; 101 } 102 103 if (!base || i < index) 104 return (DDI_FAILURE); 105 106 if ((xcaps_hdr = pci_config_get32(h, base)) == PCI_CAP_EINVAL32) 107 return (DDI_FAILURE); 108 109 id = ((xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT) & PCIE_EXT_CAP_ID_MASK) | 110 PCI_CAP_XCFG_FLAG; 111 found: 112 PCI_CAP_DBG("pci_cap_probe: index=%x, id=%x, base=%x\n", 113 index, id, base); 114 115 *id_p = id; 116 *base_p = base; 117 return (DDI_SUCCESS); 118 119 } 120 121 /* 122 * pci_lcap_locate: Helper function locates a base in conventional config space. 123 */ 124 int 125 pci_lcap_locate(ddi_acc_handle_t h, uint8_t id, uint16_t *base_p) 126 { 127 uint8_t header; 128 uint16_t status, base; 129 130 status = pci_config_get16(h, PCI_CONF_STAT); 131 132 if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP)) 133 return (DDI_FAILURE); 134 135 header = pci_config_get8(h, PCI_CONF_HEADER); 136 switch (header & PCI_HEADER_TYPE_M) { 137 case PCI_HEADER_ZERO: 138 base = PCI_CONF_CAP_PTR; 139 break; 140 case PCI_HEADER_PPB: 141 base = PCI_BCNF_CAP_PTR; 142 break; 143 case PCI_HEADER_CARDBUS: 144 base = PCI_CBUS_CAP_PTR; 145 break; 146 default: 147 cmn_err(CE_WARN, "%s: unexpected pci header type:%x", 148 __func__, header); 149 return (DDI_FAILURE); 150 } 151 152 for (base = pci_config_get8(h, base); base; 153 base = pci_config_get8(h, base + PCI_CAP_NEXT_PTR)) { 154 if (pci_config_get8(h, base) == id) { 155 *base_p = base; 156 return (DDI_SUCCESS); 157 } 158 } 159 160 *base_p = PCI_CAP_NEXT_PTR_NULL; 161 return (DDI_FAILURE); 162 } 163 164 /* 165 * pci_xcap_locate: Helper function locates a base in extended config space. 166 */ 167 int 168 pci_xcap_locate(ddi_acc_handle_t h, uint16_t id, uint16_t *base_p) 169 { 170 uint16_t status, base; 171 uint32_t xcaps_hdr; 172 173 status = pci_config_get16(h, PCI_CONF_STAT); 174 175 if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP)) 176 return (DDI_FAILURE); 177 178 for (base = PCIE_EXT_CAP; base; base = (xcaps_hdr >> 179 PCIE_EXT_CAP_NEXT_PTR_SHIFT) & PCIE_EXT_CAP_NEXT_PTR_MASK) { 180 181 if ((xcaps_hdr = pci_config_get32(h, base)) == PCI_CAP_EINVAL32) 182 break; 183 184 if (((xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT) & 185 PCIE_EXT_CAP_ID_MASK) == id) { 186 *base_p = base; 187 return (DDI_SUCCESS); 188 } 189 } 190 191 *base_p = PCI_CAP_NEXT_PTR_NULL; 192 return (DDI_FAILURE); 193 } 194 195 /* 196 * There can be multiple pci caps with a Hypertransport technology cap ID 197 * Each is distiguished by a type register in the upper half of the cap 198 * header (the "command" register part). 199 * 200 * This returns the location of a hypertransport capability whose upper 201 * 16-bits of the cap header matches <reg_val> after masking the value 202 * with <reg_mask>; if both <reg_mask> and <reg_val> are 0, it will return 203 * the first HT cap found 204 */ 205 int 206 pci_htcap_locate(ddi_acc_handle_t h, uint16_t reg_mask, uint16_t reg_val, 207 uint16_t *base_p) 208 { 209 uint8_t header; 210 uint16_t status, base; 211 212 status = pci_config_get16(h, PCI_CONF_STAT); 213 214 if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP)) 215 return (DDI_FAILURE); 216 217 header = pci_config_get8(h, PCI_CONF_HEADER); 218 switch (header & PCI_HEADER_TYPE_M) { 219 case PCI_HEADER_ZERO: 220 base = PCI_CONF_CAP_PTR; 221 break; 222 case PCI_HEADER_PPB: 223 base = PCI_BCNF_CAP_PTR; 224 break; 225 default: 226 cmn_err(CE_WARN, "%s: unexpected pci header type:%x", 227 __func__, header); 228 return (DDI_FAILURE); 229 } 230 231 for (base = pci_config_get8(h, base); base; 232 base = pci_config_get8(h, base + PCI_CAP_NEXT_PTR)) { 233 if (pci_config_get8(h, base) == PCI_CAP_ID_HT && 234 (pci_config_get16(h, base + PCI_CAP_ID_REGS_OFF) & 235 reg_mask) == reg_val) { 236 *base_p = base; 237 return (DDI_SUCCESS); 238 } 239 } 240 241 *base_p = PCI_CAP_NEXT_PTR_NULL; 242 return (DDI_FAILURE); 243 } 244 245 /* 246 * pci_cap_get: This function uses the base or capid to get a byte, word, 247 * or dword. If access by capid is requested, the function uses the capid to 248 * locate the base. Access by a base results in better performance 249 * because no cap list traversal is required. 250 */ 251 uint32_t 252 pci_cap_get(ddi_acc_handle_t h, pci_cap_config_size_t size, 253 uint32_t id, uint16_t base, uint16_t offset) 254 { 255 uint32_t data; 256 257 if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS) 258 return (PCI_CAP_EINVAL32); 259 260 /* 261 * Each access to a PCI Configuration Space should be checked 262 * by the calling function. A returned value of the 2's complement 263 * of -1 indicates that either the device is offlined or it does not 264 * exist. 265 */ 266 offset += base; 267 268 switch (size) { 269 case PCI_CAP_CFGSZ_8: 270 data = pci_config_get8(h, offset); 271 break; 272 case PCI_CAP_CFGSZ_16: 273 data = pci_config_get16(h, offset); 274 break; 275 case PCI_CAP_CFGSZ_32: 276 data = pci_config_get32(h, offset); 277 break; 278 default: 279 data = PCI_CAP_EINVAL32; 280 } 281 282 PCI_CAP_DBG("pci_cap_get: %p[x%x]=x%x\n", (void *)h, offset, data); 283 return (data); 284 } 285 286 /* 287 * pci_cap_put: This function uses the caps ptr or capid to put a byte, word, 288 * or dword. If access by capid is requested, the function uses the capid to 289 * locate the base. Access by base results in better performance 290 * because no cap list traversal is required. 291 */ 292 int 293 pci_cap_put(ddi_acc_handle_t h, pci_cap_config_size_t size, 294 uint32_t id, uint16_t base, uint16_t offset, 295 uint32_t data) 296 { 297 298 /* 299 * use the pci_config_size_t to switch for the appropriate read 300 */ 301 if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS) 302 return (DDI_FAILURE); 303 304 offset += base; 305 306 switch (size) { 307 case PCI_CAP_CFGSZ_8: 308 pci_config_put8(h, offset, data); 309 break; 310 case PCI_CAP_CFGSZ_16: 311 pci_config_put16(h, offset, data); 312 break; 313 case PCI_CAP_CFGSZ_32: 314 pci_config_put32(h, offset, data); 315 break; 316 default: 317 return (DDI_FAILURE); 318 } 319 320 PCI_CAP_DBG("pci_cap_put: data=%x\n", data); 321 return (DDI_SUCCESS); 322 } 323 324 /* 325 * Cache the entire Cap Structure. The caller is required to allocate and free 326 * buffer. 327 */ 328 int 329 pci_cap_read(ddi_acc_handle_t h, uint32_t id, uint16_t base, 330 uint32_t *buf_p, uint32_t nwords) 331 { 332 333 int i; 334 uint32_t *ptr; 335 336 ASSERT(nwords < 1024); 337 338 if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS) 339 return (DDI_FAILURE); 340 341 for (ptr = buf_p, i = 0; i < nwords; i++, base += 4) { 342 if ((*ptr++ = pci_config_get32(h, base)) == PCI_CAP_EINVAL32) 343 return (DDI_FAILURE); 344 } 345 346 return (DDI_SUCCESS); 347 } 348