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