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