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 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * PCI nexus utility routines: 28 * property and config routines for attach() 29 * reg/intr/range/assigned-address property routines for bus_map() 30 * init_child() 31 * fault handling 32 */ 33 34 #include <sys/types.h> 35 #include <sys/kmem.h> 36 #include <sys/async.h> 37 #include <sys/sysmacros.h> 38 #include <sys/sunddi.h> 39 #include <sys/sunndi.h> 40 #include <sys/ddi_impldefs.h> 41 #include "px_obj.h" 42 #include <sys/pcie_pwr.h> 43 44 /*LINTLIBRARY*/ 45 46 /* 47 * px_get_props 48 * 49 * This function is called from the attach routine to get the key 50 * properties of the pci nodes. 51 * 52 * used by: px_attach() 53 * 54 * return value: DDI_FAILURE on failure 55 */ 56 int 57 px_get_props(px_t *px_p, dev_info_t *dip) 58 { 59 int i, no_of_intrs; 60 61 /* 62 * Get the bus-ranges property. 63 */ 64 i = sizeof (px_p->px_bus_range); 65 if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 66 "bus-range", (caddr_t)&px_p->px_bus_range, &i) != DDI_SUCCESS) { 67 cmn_err(CE_WARN, "%s%d: no bus-range property\n", 68 ddi_driver_name(dip), ddi_get_instance(dip)); 69 return (DDI_FAILURE); 70 } 71 DBG(DBG_ATTACH, dip, "get_px_properties: bus-range (%x,%x)\n", 72 px_p->px_bus_range.lo, px_p->px_bus_range.hi); 73 74 /* 75 * Get the interrupts property. 76 */ 77 if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 78 "interrupts", (caddr_t)&px_p->px_inos, 79 &px_p->px_inos_len) != DDI_SUCCESS) { 80 81 cmn_err(CE_WARN, "%s%d: no interrupts property\n", 82 ddi_driver_name(dip), ddi_get_instance(dip)); 83 return (DDI_FAILURE); 84 } 85 86 /* 87 * figure out number of interrupts in the "interrupts" property 88 * and convert them all into ino. 89 */ 90 i = ddi_getprop(DDI_DEV_T_ANY, dip, 0, "#interrupt-cells", 1); 91 i = CELLS_1275_TO_BYTES(i); 92 no_of_intrs = px_p->px_inos_len / i; 93 for (i = 0; i < no_of_intrs; i++) 94 px_p->px_inos[i] = px_p->px_inos[i] & 0x3F; 95 96 /* 97 * Get the ranges property. 98 */ 99 if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "ranges", 100 (caddr_t)&px_p->px_ranges_p, &px_p->px_ranges_length) != 101 DDI_SUCCESS) { 102 103 cmn_err(CE_WARN, "%s%d: no ranges property\n", 104 ddi_driver_name(dip), ddi_get_instance(dip)); 105 kmem_free(px_p->px_inos, px_p->px_inos_len); 106 return (DDI_FAILURE); 107 } 108 109 return (DDI_SUCCESS); 110 } 111 112 /* 113 * px_free_props: 114 * 115 * This routine frees the memory used to cache the "interrupts" 116 * and "ranges" properties of the pci bus device node. 117 * 118 * used by: px_detach() 119 * 120 * return value: none 121 */ 122 void 123 px_free_props(px_t *px_p) 124 { 125 kmem_free(px_p->px_inos, px_p->px_inos_len); 126 kmem_free(px_p->px_ranges_p, px_p->px_ranges_length); 127 } 128 129 /* 130 * px_reloc_reg 131 * 132 * If the "reg" entry (*px_rp) is relocatable, lookup "assigned-addresses" 133 * property to fetch corresponding relocated address. 134 * 135 * used by: px_map() 136 * 137 * return value: 138 * 139 * DDI_SUCCESS - on success 140 * DDI_ME_INVAL - regspec is invalid 141 */ 142 int 143 px_reloc_reg(dev_info_t *dip, dev_info_t *rdip, px_t *px_p, 144 pci_regspec_t *rp) 145 { 146 int assign_len, assign_entries, i; 147 pci_regspec_t *assign_p; 148 uint32_t phys_hi = rp->pci_phys_hi; 149 uint32_t space_type = phys_hi & PCI_REG_ADDR_M; /* 28-bit */ 150 151 DBG(DBG_MAP | DBG_CONT, dip, "\tpx_reloc_reg fr: %x.%x.%x %x.%x\n", 152 rp->pci_phys_hi, rp->pci_phys_mid, rp->pci_phys_low, 153 rp->pci_size_hi, rp->pci_size_low); 154 155 if (space_type == PCI_ADDR_CONFIG || phys_hi & PCI_RELOCAT_B) 156 return (DDI_SUCCESS); 157 158 /* 159 * Hot plug will be taken care of later 160 * if (px_p->hotplug_capable == B_FALSE) 161 */ 162 { 163 uint32_t bus = PCI_REG_BUS_G(phys_hi); 164 if (bus < px_p->px_bus_range.lo || 165 bus > px_p->px_bus_range.hi) { 166 DBG(DBG_MAP | DBG_CONT, dip, "bad bus# (%x)\n", bus); 167 return (DDI_ME_INVAL); 168 } 169 } 170 171 i = ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS, 172 "assigned-addresses", (caddr_t)&assign_p, &assign_len); 173 if (i) { 174 DBG(DBG_MAP | DBG_CONT, dip, "%s%d: assigned-addresses %d\n", 175 ddi_driver_name(rdip), ddi_get_instance(rdip), i); 176 return (DDI_ME_INVAL); 177 } 178 179 assign_entries = assign_len / sizeof (pci_regspec_t); 180 for (i = 0; i < assign_entries; i++, assign_p++) { 181 uint32_t assign_type = assign_p->pci_phys_hi & PCI_REG_ADDR_M; 182 uint32_t assign_addr = PCI_REG_BDFR_G(assign_p->pci_phys_hi); 183 184 if (PCI_REG_BDFR_G(phys_hi) != assign_addr) 185 continue; 186 if (space_type == assign_type) { /* exact match */ 187 rp->pci_phys_low += assign_p->pci_phys_low; 188 if (space_type == PCI_ADDR_MEM64) 189 rp->pci_phys_mid += assign_p->pci_phys_mid; 190 break; 191 } 192 if (space_type == PCI_ADDR_MEM64 && 193 assign_type == PCI_ADDR_MEM32) { 194 rp->pci_phys_low += assign_p->pci_phys_low; 195 rp->pci_phys_hi ^= PCI_ADDR_MEM64 ^ PCI_ADDR_MEM32; 196 break; 197 } 198 } 199 kmem_free(assign_p - i, assign_len); 200 DBG(DBG_MAP | DBG_CONT, dip, "\tpx_reloc_reg to: %x.%x.%x %x.%x <%d>\n", 201 rp->pci_phys_hi, rp->pci_phys_mid, rp->pci_phys_low, 202 rp->pci_size_hi, rp->pci_size_low, i); 203 return (i < assign_entries ? DDI_SUCCESS : DDI_ME_INVAL); 204 } 205 206 /* 207 * use "ranges" to translate relocated pci regspec into parent space 208 */ 209 int 210 px_xlate_reg(px_t *px_p, pci_regspec_t *px_rp, struct regspec *new_rp) 211 { 212 int n; 213 px_ranges_t *rng_p = px_p->px_ranges_p; 214 int rng_n = px_p->px_ranges_length / sizeof (px_ranges_t); 215 uint32_t space_type = PCI_REG_ADDR_G(px_rp->pci_phys_hi); 216 uint64_t reg_begin, reg_end, reg_sz; 217 uint64_t rng_begin, rng_end, rng_sz; 218 uint64_t addr; 219 220 reg_begin = (uint64_t)px_rp->pci_phys_mid << 32 | px_rp->pci_phys_low; 221 reg_sz = (uint64_t)px_rp->pci_size_hi << 32 | px_rp->pci_size_low; 222 if (space_type == PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) { 223 if (reg_begin > PCI_CONF_HDR_SIZE) 224 return (DDI_ME_INVAL); 225 reg_sz = reg_sz ? MIN(reg_sz, PCI_CONF_HDR_SIZE) : 226 PCI_CONF_HDR_SIZE; 227 reg_begin += px_rp->pci_phys_hi << 4; 228 } 229 reg_end = reg_begin + reg_sz - 1; 230 231 for (n = 0; n < rng_n; n++, rng_p++) { 232 if (space_type != PCI_REG_ADDR_G(rng_p->child_high)) 233 continue; /* not the same space type */ 234 235 rng_begin = (uint64_t)rng_p->child_mid << 32 | rng_p->child_low; 236 rng_sz = (uint64_t)rng_p->size_high << 32 | rng_p->size_low; 237 if (space_type == PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) 238 rng_begin += rng_p->child_high; 239 240 rng_end = rng_begin + rng_sz - 1; 241 if (reg_begin >= rng_begin && reg_end <= rng_end) 242 break; 243 } 244 if (n >= rng_n) 245 return (DDI_ME_REGSPEC_RANGE); 246 247 addr = reg_begin - rng_begin + ((uint64_t)rng_p->parent_high << 32 | 248 rng_p->parent_low); 249 new_rp->regspec_addr = (uint32_t)addr; 250 new_rp->regspec_bustype = (uint32_t)(addr >> 32); 251 new_rp->regspec_size = (uint32_t)reg_sz; 252 DBG(DBG_MAP | DBG_CONT, px_p->px_dip, 253 "\tpx_xlate_reg: entry %d new_rp %x.%x %x\n", 254 n, new_rp->regspec_bustype, new_rp->regspec_addr, reg_sz); 255 256 return (DDI_SUCCESS); 257 } 258 259 /* 260 * px_report_dev 261 * 262 * This function is called from our control ops routine on a 263 * DDI_CTLOPS_REPORTDEV request. 264 * 265 * The display format is 266 * 267 * <name><inst> at <pname><pinst> device <dev> function <func> 268 * 269 * where 270 * 271 * <name> this device's name property 272 * <inst> this device's instance number 273 * <name> parent device's name property 274 * <inst> parent device's instance number 275 * <dev> this device's device number 276 * <func> this device's function number 277 */ 278 int 279 px_report_dev(dev_info_t *dip) 280 { 281 if (dip == (dev_info_t *)0) 282 return (DDI_FAILURE); 283 cmn_err(CE_CONT, "?PCI Express-device: %s@%s, %s%d\n", 284 ddi_node_name(dip), ddi_get_name_addr(dip), 285 ddi_driver_name(dip), 286 ddi_get_instance(dip)); 287 return (DDI_SUCCESS); 288 } 289 290 291 /* 292 * reg property for pcimem nodes that covers the entire address 293 * space for the node: config, io, or memory. 294 */ 295 pci_regspec_t pci_pcimem_reg[3] = 296 { 297 {PCI_ADDR_CONFIG, 0, 0, 0, 0x800000 }, 298 {(uint_t)(PCI_ADDR_IO|PCI_RELOCAT_B), 0, 0, 0, PX_IO_SIZE }, 299 {(uint_t)(PCI_ADDR_MEM32|PCI_RELOCAT_B), 0, 0, 0, PX_MEM_SIZE } 300 }; 301 302 /* 303 * px_name_child 304 * 305 * This function is called from init_child to name a node. It is 306 * also passed as a callback for node merging functions. 307 * 308 * return value: DDI_SUCCESS, DDI_FAILURE 309 */ 310 static int 311 px_name_child(dev_info_t *child, char *name, int namelen) 312 { 313 pci_regspec_t *pci_rp; 314 int reglen; 315 uint_t func; 316 char **unit_addr; 317 uint_t n; 318 319 /* 320 * Set the address portion of the node name based on 321 * unit-address property, if it exists. 322 * The interpretation of the unit-address is DD[,F] 323 * where DD is the device id and F is the function. 324 */ 325 if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child, 326 DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) == 327 DDI_PROP_SUCCESS) { 328 if (n != 1 || *unit_addr == NULL || **unit_addr == 0) { 329 cmn_err(CE_WARN, "unit-address property in %s.conf" 330 " not well-formed", ddi_driver_name(child)); 331 ddi_prop_free(unit_addr); 332 return (DDI_FAILURE); 333 } 334 (void) snprintf(name, namelen, "%s", *unit_addr); 335 ddi_prop_free(unit_addr); 336 return (DDI_SUCCESS); 337 } 338 339 /* 340 * The unit-address property is does not exist. Set the address 341 * portion of the node name based on the function and device number. 342 */ 343 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 344 "reg", (int **)&pci_rp, (uint_t *)®len) == DDI_SUCCESS) { 345 if (((reglen * sizeof (int)) % sizeof (pci_regspec_t)) != 0) { 346 cmn_err(CE_WARN, "reg property not well-formed"); 347 return (DDI_FAILURE); 348 } 349 350 func = PCI_REG_FUNC_G(pci_rp[0].pci_phys_hi); 351 if (func != 0) 352 (void) snprintf(name, namelen, "%x,%x", 353 PCI_REG_DEV_G(pci_rp[0].pci_phys_hi), func); 354 else 355 (void) snprintf(name, namelen, "%x", 356 PCI_REG_DEV_G(pci_rp[0].pci_phys_hi)); 357 ddi_prop_free(pci_rp); 358 return (DDI_SUCCESS); 359 } 360 361 cmn_err(CE_WARN, "cannot name pci child '%s'", ddi_node_name(child)); 362 return (DDI_FAILURE); 363 } 364 365 int 366 px_uninit_child(px_t *px_p, dev_info_t *child) 367 { 368 DBG(DBG_INIT_CLD, px_p->px_dip, 369 "DDI_CTLOPS_UNINITCHILD: arg=%s%d\n", 370 ddi_driver_name(child), ddi_get_instance(child)); 371 372 ddi_set_name_addr(child, NULL); 373 ddi_remove_minor_node(child, NULL); 374 375 /* 376 * XXX Clear parent private data used as a flag to disable 377 * iommu BDF protection 378 */ 379 if ((intptr_t)ddi_get_parent_data(child) == 1) 380 ddi_set_parent_data(child, NULL); 381 382 impl_rem_dev_props(child); 383 384 DBG(DBG_PWR, ddi_get_parent(child), "\n\n"); 385 386 pcie_uninitchild(child); 387 388 return (DDI_SUCCESS); 389 } 390 391 /* 392 * px_init_child 393 * 394 * This function is called from our control ops routine on a 395 * DDI_CTLOPS_INITCHILD request. It builds and sets the device's 396 * parent private data area. 397 * 398 * used by: pci_ctlops() 399 * 400 * return value: none 401 */ 402 int 403 px_init_child(px_t *px_p, dev_info_t *child) 404 { 405 dev_info_t *parent_dip = px_p->px_dip; 406 pci_regspec_t *pci_rp; 407 char name[10]; 408 int i, no_config; 409 intptr_t ppd = NULL; 410 411 /* 412 * The following is a special case for pcimem nodes. 413 * For these nodes we create a reg property with a 414 * single entry that covers the entire address space 415 * for the node (config, io or memory). 416 */ 417 if (strcmp(ddi_driver_name(child), "pcimem") == 0) { 418 (void) ddi_prop_create(DDI_DEV_T_NONE, child, 419 DDI_PROP_CANSLEEP, "reg", (caddr_t)pci_pcimem_reg, 420 sizeof (pci_pcimem_reg)); 421 ddi_set_name_addr(child, "0"); 422 ddi_set_parent_data(child, NULL); 423 return (DDI_SUCCESS); 424 } 425 426 /* 427 * Check whether the node has config space or is a hard decode 428 * node (possibly created by a driver.conf file). 429 */ 430 no_config = ddi_prop_get_int(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 431 "no-config", 0); 432 433 /* 434 * XXX set ppd to 1 to disable iommu BDF protection 435 * It relies on unused parent private data for PCI devices. 436 */ 437 if (ddi_prop_exists(DDI_DEV_T_NONE, child, DDI_PROP_DONTPASS, 438 "dvma-share")) 439 ppd = 1; 440 441 /* 442 * Pseudo nodes indicate a prototype node with per-instance 443 * properties to be merged into the real h/w device node. 444 * However, do not merge if the no-config property is set 445 * (see PSARC 2000/088). 446 */ 447 if ((ndi_dev_is_persistent_node(child) == 0) && (no_config == 0)) { 448 extern int pci_allow_pseudo_children; 449 450 if (ddi_getlongprop(DDI_DEV_T_ANY, child, 451 DDI_PROP_DONTPASS, "reg", (caddr_t)&pci_rp, &i) == 452 DDI_SUCCESS) { 453 cmn_err(CE_WARN, "cannot merge prototype from %s.conf", 454 ddi_driver_name(child)); 455 kmem_free(pci_rp, i); 456 return (DDI_NOT_WELL_FORMED); 457 } 458 /* 459 * Name the child 460 */ 461 if (px_name_child(child, name, 10) != DDI_SUCCESS) 462 return (DDI_FAILURE); 463 464 ddi_set_name_addr(child, name); 465 ddi_set_parent_data(child, (void *)ppd); 466 467 /* 468 * Try to merge the properties from this prototype 469 * node into real h/w nodes. 470 */ 471 if (ndi_merge_node(child, px_name_child) == DDI_SUCCESS) { 472 /* 473 * Merged ok - return failure to remove the node. 474 */ 475 ddi_set_name_addr(child, NULL); 476 return (DDI_FAILURE); 477 } 478 479 /* workaround for ddivs to run under PCI */ 480 if (pci_allow_pseudo_children) 481 return (DDI_SUCCESS); 482 483 cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged", 484 ddi_driver_name(child), ddi_get_name_addr(child), 485 ddi_driver_name(child)); 486 ddi_set_name_addr(child, NULL); 487 return (DDI_NOT_WELL_FORMED); 488 } 489 490 if (px_name_child(child, name, 10) != DDI_SUCCESS) 491 return (DDI_FAILURE); 492 ddi_set_name_addr(child, name); 493 494 if (no_config != 0) { 495 /* 496 * There is no config space so there's nothing more to do. 497 */ 498 return (DDI_SUCCESS); 499 } 500 501 if (pcie_pm_hold(parent_dip) != DDI_SUCCESS) { 502 DBG(DBG_PWR, parent_dip, 503 "INITCHILD: px_pm_hold failed\n"); 504 return (DDI_FAILURE); 505 } 506 /* Any return of DDI_FAILURE after this must call px_pm_release */ 507 508 /* 509 * If configuration registers were previously saved by 510 * child (before it went to D3), then let the child do the 511 * restore to set up the config regs as it'll first need to 512 * power the device out of D3. 513 */ 514 if (ddi_prop_exists(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 515 "config-regs-saved-by-child") == 1) { 516 DBG(DBG_PWR, child, 517 "INITCHILD: config regs to be restored by child\n"); 518 519 return (DDI_SUCCESS); 520 } 521 522 DBG(DBG_PWR, parent_dip, 523 "INITCHILD: config regs setup for %s@%s\n", 524 ddi_node_name(child), ddi_get_name_addr(child)); 525 526 ddi_set_parent_data(child, (void *)ppd); 527 if (pcie_init_bus(child)) 528 (void) pcie_initchild(child); 529 530 /* 531 * Handle chip specific init-child tasks. 532 */ 533 pcie_pm_release(parent_dip); 534 535 return (DDI_SUCCESS); 536 } 537 538 /* 539 * px_get_reg_set_size 540 * 541 * Given a dev info pointer to a pci child and a register number, this 542 * routine returns the size element of that reg set property. 543 * 544 * used by: pci_ctlops() - DDI_CTLOPS_REGSIZE 545 * 546 * return value: size of reg set on success, 0 on error 547 */ 548 off_t 549 px_get_reg_set_size(dev_info_t *child, int rnumber) 550 { 551 pci_regspec_t *pci_rp; 552 off_t size = 0; 553 int i; 554 555 if (rnumber < 0) 556 return (0); 557 558 /* 559 * Get the reg property for the device. 560 */ 561 if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg", 562 (caddr_t)&pci_rp, &i) != DDI_SUCCESS) 563 return (0); 564 565 if (rnumber >= (i / (int)sizeof (pci_regspec_t))) 566 goto done; 567 568 size = pci_rp[rnumber].pci_size_low | 569 ((uint64_t)pci_rp[rnumber].pci_size_hi << 32); 570 done: 571 kmem_free(pci_rp, i); 572 return (size); 573 } 574 575 576 /* 577 * px_get_nreg_set 578 * 579 * Given a dev info pointer to a pci child, this routine returns the 580 * number of sets in its "reg" property. 581 * 582 * used by: pci_ctlops() - DDI_CTLOPS_NREGS 583 * 584 * return value: # of reg sets on success, zero on error 585 */ 586 uint_t 587 px_get_nreg_set(dev_info_t *child) 588 { 589 pci_regspec_t *pci_rp; 590 int i, n; 591 592 /* 593 * Get the reg property for the device. 594 */ 595 if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg", 596 (caddr_t)&pci_rp, &i) != DDI_SUCCESS) 597 return (0); 598 599 n = i / (int)sizeof (pci_regspec_t); 600 kmem_free(pci_rp, i); 601 return (n); 602 } 603 604 605 /* 606 * px_get_nintr 607 * 608 * Given a dev info pointer to a pci child, this routine returns the 609 * number of items in its "interrupts" property. 610 * 611 * used by: pci_ctlops() - DDI_CTLOPS_NREGS 612 * 613 * return value: # of interrupts on success, zero on error 614 */ 615 uint_t 616 px_get_nintr(dev_info_t *child) 617 { 618 int *pci_ip; 619 int i, n; 620 621 if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 622 "interrupts", (caddr_t)&pci_ip, &i) != DDI_SUCCESS) 623 return (0); 624 625 n = i / (int)sizeof (uint_t); 626 kmem_free(pci_ip, i); 627 return (n); 628 } 629 630 uint64_t 631 px_get_cfg_pabase(px_t *px_p) 632 { 633 int i; 634 px_ranges_t *rangep = px_p->px_ranges_p; 635 int nrange = px_p->px_ranges_length / sizeof (px_ranges_t); 636 uint32_t cfg_space_type = PCI_REG_ADDR_G(PCI_ADDR_CONFIG); 637 638 ASSERT(cfg_space_type == 0); 639 640 for (i = 0; i < nrange; i++, rangep++) { 641 if (PCI_REG_ADDR_G(rangep->child_high) == cfg_space_type) 642 break; 643 } 644 645 if (i >= nrange) 646 cmn_err(CE_PANIC, "no cfg space in px(%p) ranges prop.\n", 647 px_p); 648 649 return (((uint64_t)rangep->parent_high << 32) | rangep->parent_low); 650 } 651 652 /* 653 * decodes standard PCI config space 16bit error status reg 654 */ 655 int 656 px_log_cfg_err(dev_info_t *dip, ushort_t status_reg, char *err_msg) 657 { 658 int nerr = ddi_get_instance(dip); /* temp for instance */ 659 uint64_t perr_fatal = px_perr_fatal & (1 << nerr); 660 uint64_t serr_fatal = px_serr_fatal & (1 << nerr); 661 nerr = 0; 662 663 if ((status_reg & PCI_STAT_PERROR) && perr_fatal) 664 nerr++; 665 if ((status_reg & PCI_STAT_S_SYSERR) && serr_fatal) 666 nerr++; 667 if (status_reg & PCI_STAT_R_MAST_AB) 668 nerr++; 669 if ((status_reg & PCI_STAT_S_PERROR) && perr_fatal) 670 nerr++; 671 672 cmn_err(CE_WARN, "%s%d: %sPCI Express config space CSR=0x%b", 673 ddi_driver_name(dip), ddi_get_instance(dip), err_msg, 674 (uint32_t)status_reg, PX_STATUS_BITS); 675 676 return (nerr); 677 } 678