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 2006 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 px_p->px_thermal_interrupt = 112 ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 113 "thermal-interrupt", -1); 114 DBG(DBG_ATTACH, dip, "get_px_properties: thermal_interrupt=%d\n", 115 px_p->px_thermal_interrupt); 116 return (DDI_SUCCESS); 117 } 118 119 /* 120 * px_free_props: 121 * 122 * This routine frees the memory used to cache the "interrupts" 123 * and "ranges" properties of the pci bus device node. 124 * 125 * used by: px_detach() 126 * 127 * return value: none 128 */ 129 void 130 px_free_props(px_t *px_p) 131 { 132 kmem_free(px_p->px_inos, px_p->px_inos_len); 133 kmem_free(px_p->px_ranges_p, px_p->px_ranges_length); 134 } 135 136 /* 137 * px_reloc_reg 138 * 139 * If the "reg" entry (*px_rp) is relocatable, lookup "assigned-addresses" 140 * property to fetch corresponding relocated address. 141 * 142 * used by: px_map() 143 * 144 * return value: 145 * 146 * DDI_SUCCESS - on success 147 * DDI_ME_INVAL - regspec is invalid 148 */ 149 int 150 px_reloc_reg(dev_info_t *dip, dev_info_t *rdip, px_t *px_p, 151 pci_regspec_t *rp) 152 { 153 int assign_len, assign_entries, i; 154 pci_regspec_t *assign_p; 155 uint32_t phys_hi = rp->pci_phys_hi; 156 uint32_t space_type = phys_hi & PCI_REG_ADDR_M; /* 28-bit */ 157 158 DBG(DBG_MAP | DBG_CONT, dip, "\tpx_reloc_reg fr: %x.%x.%x %x.%x\n", 159 rp->pci_phys_hi, rp->pci_phys_mid, rp->pci_phys_low, 160 rp->pci_size_hi, rp->pci_size_low); 161 162 if (space_type == PCI_ADDR_CONFIG || phys_hi & PCI_RELOCAT_B) 163 return (DDI_SUCCESS); 164 165 /* 166 * Hot plug will be taken care of later 167 * if (px_p->hotplug_capable == B_FALSE) 168 */ 169 { 170 uint32_t bus = PCI_REG_BUS_G(phys_hi); 171 if (bus < px_p->px_bus_range.lo || 172 bus > px_p->px_bus_range.hi) { 173 DBG(DBG_MAP | DBG_CONT, dip, "bad bus# (%x)\n", bus); 174 return (DDI_ME_INVAL); 175 } 176 } 177 178 i = ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS, 179 "assigned-addresses", (caddr_t)&assign_p, &assign_len); 180 if (i) { 181 DBG(DBG_MAP | DBG_CONT, dip, "%s%d: assigned-addresses %d\n", 182 ddi_driver_name(rdip), ddi_get_instance(rdip), i); 183 return (DDI_ME_INVAL); 184 } 185 186 assign_entries = assign_len / sizeof (pci_regspec_t); 187 for (i = 0; i < assign_entries; i++, assign_p++) { 188 uint32_t assign_type = assign_p->pci_phys_hi & PCI_REG_ADDR_M; 189 uint32_t assign_addr = PCI_REG_BDFR_G(assign_p->pci_phys_hi); 190 191 if (PCI_REG_BDFR_G(phys_hi) != assign_addr) 192 continue; 193 if (space_type == assign_type) { /* exact match */ 194 rp->pci_phys_low += assign_p->pci_phys_low; 195 break; 196 } 197 if (space_type == PCI_ADDR_MEM64 && 198 assign_type == PCI_ADDR_MEM32) { 199 rp->pci_phys_low += assign_p->pci_phys_low; 200 rp->pci_phys_hi ^= PCI_ADDR_MEM64 ^ PCI_ADDR_MEM32; 201 break; 202 } 203 } 204 kmem_free(assign_p - i, assign_len); 205 DBG(DBG_MAP | DBG_CONT, dip, "\tpx_reloc_reg to: %x.%x.%x %x.%x <%d>\n", 206 rp->pci_phys_hi, rp->pci_phys_mid, rp->pci_phys_low, 207 rp->pci_size_hi, rp->pci_size_low, i); 208 return (i < assign_entries ? DDI_SUCCESS : DDI_ME_INVAL); 209 } 210 211 int 212 px_search_ranges(px_t *px_p, uint32_t space_type, uint32_t reg_begin, 213 uint32_t reg_end, px_ranges_t **sel_rng_p, uint_t *base_offset_p) 214 { 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 int n; 218 uint32_t rng_begin, rng_end; 219 220 for (n = 0; n < rng_n; n++, rng_p++) { 221 if (space_type != PCI_REG_ADDR_G(rng_p->child_high)) 222 continue; /* not the same space type */ 223 224 rng_begin = rng_p->child_low; 225 if (space_type == PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) 226 rng_begin += rng_p->child_high; 227 228 rng_end = rng_begin + rng_p->size_low - 1; 229 if (reg_begin >= rng_begin && reg_end <= rng_end) 230 break; 231 } 232 if (n >= rng_n) 233 return (DDI_ME_REGSPEC_RANGE); 234 235 *base_offset_p = reg_begin - rng_begin + rng_p->parent_low; 236 *sel_rng_p = rng_p; 237 238 return (DDI_SUCCESS); 239 } 240 241 /* 242 * use "ranges" to translate relocated pci regspec into parent space 243 */ 244 int 245 px_xlate_reg(px_t *px_p, pci_regspec_t *px_rp, struct regspec *new_rp) 246 { 247 uint32_t space_type = PCI_REG_ADDR_G(px_rp->pci_phys_hi); 248 uint32_t reg_end, reg_begin = px_rp->pci_phys_low; 249 uint32_t sz = px_rp->pci_size_low; 250 px_ranges_t *sel_rng_p; 251 int rval; 252 253 if (space_type == PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) { 254 if (reg_begin > PCI_CONF_HDR_SIZE) 255 return (DDI_ME_INVAL); 256 sz = sz ? MIN(sz, PCI_CONF_HDR_SIZE) : PCI_CONF_HDR_SIZE; 257 reg_begin += px_rp->pci_phys_hi << 4; 258 } 259 reg_end = reg_begin + sz - 1; 260 261 if ((rval = px_search_ranges(px_p, space_type, reg_begin, reg_end, 262 &sel_rng_p, &new_rp->regspec_addr)) == DDI_SUCCESS) { 263 264 new_rp->regspec_bustype = sel_rng_p->parent_high; 265 new_rp->regspec_size = sz; 266 267 DBG(DBG_MAP | DBG_CONT, px_p->px_dip, 268 "\tpx_xlate_reg: new_rp %x.%x %x\n", 269 new_rp->regspec_bustype, new_rp->regspec_addr, sz); 270 } 271 272 return (rval); 273 } 274 275 /* 276 * px_report_dev 277 * 278 * This function is called from our control ops routine on a 279 * DDI_CTLOPS_REPORTDEV request. 280 * 281 * The display format is 282 * 283 * <name><inst> at <pname><pinst> device <dev> function <func> 284 * 285 * where 286 * 287 * <name> this device's name property 288 * <inst> this device's instance number 289 * <name> parent device's name property 290 * <inst> parent device's instance number 291 * <dev> this device's device number 292 * <func> this device's function number 293 */ 294 int 295 px_report_dev(dev_info_t *dip) 296 { 297 if (dip == (dev_info_t *)0) 298 return (DDI_FAILURE); 299 cmn_err(CE_CONT, "?PCI Express-device: %s@%s, %s%d\n", 300 ddi_node_name(dip), ddi_get_name_addr(dip), 301 ddi_driver_name(dip), 302 ddi_get_instance(dip)); 303 return (DDI_SUCCESS); 304 } 305 306 307 /* 308 * reg property for pcimem nodes that covers the entire address 309 * space for the node: config, io, or memory. 310 */ 311 pci_regspec_t pci_pcimem_reg[3] = 312 { 313 {PCI_ADDR_CONFIG, 0, 0, 0, 0x800000 }, 314 {(uint_t)(PCI_ADDR_IO|PCI_RELOCAT_B), 0, 0, 0, PX_IO_SIZE }, 315 {(uint_t)(PCI_ADDR_MEM32|PCI_RELOCAT_B), 0, 0, 0, PX_MEM_SIZE } 316 }; 317 318 /* 319 * px_name_child 320 * 321 * This function is called from init_child to name a node. It is 322 * also passed as a callback for node merging functions. 323 * 324 * return value: DDI_SUCCESS, DDI_FAILURE 325 */ 326 static int 327 px_name_child(dev_info_t *child, char *name, int namelen) 328 { 329 pci_regspec_t *pci_rp; 330 int reglen; 331 uint_t func; 332 char **unit_addr; 333 uint_t n; 334 335 /* 336 * Set the address portion of the node name based on 337 * unit-address property, if it exists. 338 * The interpretation of the unit-address is DD[,F] 339 * where DD is the device id and F is the function. 340 */ 341 if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child, 342 DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) == 343 DDI_PROP_SUCCESS) { 344 if (n != 1 || *unit_addr == NULL || **unit_addr == 0) { 345 cmn_err(CE_WARN, "unit-address property in %s.conf" 346 " not well-formed", ddi_driver_name(child)); 347 ddi_prop_free(unit_addr); 348 return (DDI_FAILURE); 349 } 350 (void) snprintf(name, namelen, "%s", *unit_addr); 351 ddi_prop_free(unit_addr); 352 return (DDI_SUCCESS); 353 } 354 355 /* 356 * The unit-address property is does not exist. Set the address 357 * portion of the node name based on the function and device number. 358 */ 359 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 360 "reg", (int **)&pci_rp, (uint_t *)®len) == DDI_SUCCESS) { 361 if (((reglen * sizeof (int)) % sizeof (pci_regspec_t)) != 0) { 362 cmn_err(CE_WARN, "reg property not well-formed"); 363 return (DDI_FAILURE); 364 } 365 366 func = PCI_REG_FUNC_G(pci_rp[0].pci_phys_hi); 367 if (func != 0) 368 (void) snprintf(name, namelen, "%x,%x", 369 PCI_REG_DEV_G(pci_rp[0].pci_phys_hi), func); 370 else 371 (void) snprintf(name, namelen, "%x", 372 PCI_REG_DEV_G(pci_rp[0].pci_phys_hi)); 373 ddi_prop_free(pci_rp); 374 return (DDI_SUCCESS); 375 } 376 377 cmn_err(CE_WARN, "cannot name pci child '%s'", ddi_node_name(child)); 378 return (DDI_FAILURE); 379 } 380 381 int 382 px_uninit_child(px_t *px_p, dev_info_t *child) 383 { 384 DBG(DBG_INIT_CLD, px_p->px_dip, 385 "DDI_CTLOPS_UNINITCHILD: arg=%s%d\n", 386 ddi_driver_name(child), ddi_get_instance(child)); 387 388 ddi_set_name_addr(child, NULL); 389 ddi_remove_minor_node(child, NULL); 390 impl_rem_dev_props(child); 391 392 DBG(DBG_PWR, ddi_get_parent(child), "\n\n"); 393 394 pcie_uninitchild(child); 395 396 return (DDI_SUCCESS); 397 } 398 399 /* 400 * px_init_child 401 * 402 * This function is called from our control ops routine on a 403 * DDI_CTLOPS_INITCHILD request. It builds and sets the device's 404 * parent private data area. 405 * 406 * used by: pci_ctlops() 407 * 408 * return value: none 409 */ 410 int 411 px_init_child(px_t *px_p, dev_info_t *child) 412 { 413 dev_info_t *parent_dip = px_p->px_dip; 414 pci_regspec_t *pci_rp; 415 char name[10]; 416 int i, no_config; 417 418 /* 419 * The following is a special case for pcimem nodes. 420 * For these nodes we create a reg property with a 421 * single entry that covers the entire address space 422 * for the node (config, io or memory). 423 */ 424 if (strcmp(ddi_driver_name(child), "pcimem") == 0) { 425 (void) ddi_prop_create(DDI_DEV_T_NONE, child, 426 DDI_PROP_CANSLEEP, "reg", (caddr_t)pci_pcimem_reg, 427 sizeof (pci_pcimem_reg)); 428 ddi_set_name_addr(child, "0"); 429 ddi_set_parent_data(child, NULL); 430 return (DDI_SUCCESS); 431 } 432 433 /* 434 * Check whether the node has config space or is a hard decode 435 * node (possibly created by a driver.conf file). 436 */ 437 no_config = ddi_prop_get_int(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 438 "no-config", 0); 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, NULL); 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 pcie_initchild(child); 526 527 /* 528 * Handle chip specific init-child tasks. 529 */ 530 pcie_pm_release(parent_dip); 531 532 return (DDI_SUCCESS); 533 } 534 535 /* 536 * px_get_reg_set_size 537 * 538 * Given a dev info pointer to a pci child and a register number, this 539 * routine returns the size element of that reg set property. 540 * 541 * used by: pci_ctlops() - DDI_CTLOPS_REGSIZE 542 * 543 * return value: size of reg set on success, 0 on error 544 */ 545 off_t 546 px_get_reg_set_size(dev_info_t *child, int rnumber) 547 { 548 pci_regspec_t *pci_rp; 549 off_t size = 0; 550 int i; 551 552 if (rnumber < 0) 553 return (0); 554 555 /* 556 * Get the reg property for the device. 557 */ 558 if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg", 559 (caddr_t)&pci_rp, &i) != DDI_SUCCESS) 560 return (0); 561 562 if (rnumber >= (i / (int)sizeof (pci_regspec_t))) 563 goto done; 564 565 size = pci_rp[rnumber].pci_size_low | 566 ((uint64_t)pci_rp[rnumber].pci_size_hi << 32); 567 done: 568 kmem_free(pci_rp, i); 569 return (size); 570 } 571 572 573 /* 574 * px_get_nreg_set 575 * 576 * Given a dev info pointer to a pci child, this routine returns the 577 * number of sets in its "reg" property. 578 * 579 * used by: pci_ctlops() - DDI_CTLOPS_NREGS 580 * 581 * return value: # of reg sets on success, zero on error 582 */ 583 uint_t 584 px_get_nreg_set(dev_info_t *child) 585 { 586 pci_regspec_t *pci_rp; 587 int i, n; 588 589 /* 590 * Get the reg property for the device. 591 */ 592 if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg", 593 (caddr_t)&pci_rp, &i) != DDI_SUCCESS) 594 return (0); 595 596 n = i / (int)sizeof (pci_regspec_t); 597 kmem_free(pci_rp, i); 598 return (n); 599 } 600 601 602 /* 603 * px_get_nintr 604 * 605 * Given a dev info pointer to a pci child, this routine returns the 606 * number of items in its "interrupts" property. 607 * 608 * used by: pci_ctlops() - DDI_CTLOPS_NREGS 609 * 610 * return value: # of interrupts on success, zero on error 611 */ 612 uint_t 613 px_get_nintr(dev_info_t *child) 614 { 615 int *pci_ip; 616 int i, n; 617 618 if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 619 "interrupts", (caddr_t)&pci_ip, &i) != DDI_SUCCESS) 620 return (0); 621 622 n = i / (int)sizeof (uint_t); 623 kmem_free(pci_ip, i); 624 return (n); 625 } 626 627 uint64_t 628 px_get_cfg_pabase(px_t *px_p) 629 { 630 int i; 631 px_ranges_t *rangep = px_p->px_ranges_p; 632 int nrange = px_p->px_ranges_length / sizeof (px_ranges_t); 633 uint32_t cfg_space_type = PCI_REG_ADDR_G(PCI_ADDR_CONFIG); 634 635 ASSERT(cfg_space_type == 0); 636 637 for (i = 0; i < nrange; i++, rangep++) { 638 if (PCI_REG_ADDR_G(rangep->child_high) == cfg_space_type) 639 break; 640 } 641 642 if (i >= nrange) 643 cmn_err(CE_PANIC, "no cfg space in px(%p) ranges prop.\n", 644 px_p); 645 646 return (((uint64_t)rangep->parent_high << 32) | rangep->parent_low); 647 } 648 649 /* 650 * decodes standard PCI config space 16bit error status reg 651 */ 652 int 653 px_log_cfg_err(dev_info_t *dip, ushort_t status_reg, char *err_msg) 654 { 655 int nerr = ddi_get_instance(dip); /* temp for instance */ 656 uint64_t perr_fatal = px_perr_fatal & (1 << nerr); 657 uint64_t serr_fatal = px_serr_fatal & (1 << nerr); 658 nerr = 0; 659 660 if ((status_reg & PCI_STAT_PERROR) && perr_fatal) 661 nerr++; 662 if ((status_reg & PCI_STAT_S_SYSERR) && serr_fatal) 663 nerr++; 664 if (status_reg & PCI_STAT_R_MAST_AB) 665 nerr++; 666 if ((status_reg & PCI_STAT_S_PERROR) && perr_fatal) 667 nerr++; 668 669 cmn_err(CE_WARN, "%s%d: %sPCI Express config space CSR=0x%b", 670 ddi_driver_name(dip), ddi_get_instance(dip), err_msg, 671 (uint32_t)status_reg, PX_STATUS_BITS); 672 673 return (nerr); 674 } 675 676 /* 677 * This is a software workaround to fix the Broadcom PCIe-PCI bridge 678 * prefetch bug. Existence of a "cross-page-prefetch" property in the 679 * child or parent node means the px nexus driver has to allocate an 680 * extra page and make it valid one, for any DVMA request that comes 681 * from any of the Broadcom child device. 682 */ 683 boolean_t 684 px_child_prefetch(dev_info_t *child) 685 { 686 if (ddi_prop_exists(DDI_DEV_T_ANY, child, DDI_PROP_NOTPROM, 687 "cross-page-prefetch")) 688 return (B_TRUE); 689 690 return (B_FALSE); 691 } 692