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