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