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 #include <px_regs.h> 47 #include <px_csr.h> 48 49 /*LINTLIBRARY*/ 50 51 /* 52 * px_get_props 53 * 54 * This function is called from the attach routine to get the key 55 * properties of the pci nodes. 56 * 57 * used by: px_attach() 58 * 59 * return value: DDI_FAILURE on failure 60 */ 61 int 62 px_get_props(px_t *px_p, dev_info_t *dip) 63 { 64 int i, no_of_intrs; 65 66 /* 67 * Get the bus-ranges property. 68 */ 69 i = sizeof (px_p->px_bus_range); 70 if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 71 "bus-range", (caddr_t)&px_p->px_bus_range, &i) != DDI_SUCCESS) { 72 cmn_err(CE_WARN, "%s%d: no bus-range property\n", 73 ddi_driver_name(dip), ddi_get_instance(dip)); 74 return (DDI_FAILURE); 75 } 76 DBG(DBG_ATTACH, dip, "get_px_properties: bus-range (%x,%x)\n", 77 px_p->px_bus_range.lo, px_p->px_bus_range.hi); 78 79 /* 80 * Get the interrupts property. 81 */ 82 if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 83 "interrupts", (caddr_t)&px_p->px_inos, 84 &px_p->px_inos_len) != DDI_SUCCESS) { 85 86 cmn_err(CE_WARN, "%s%d: no interrupts property\n", 87 ddi_driver_name(dip), ddi_get_instance(dip)); 88 return (DDI_FAILURE); 89 } 90 91 /* 92 * figure out number of interrupts in the "interrupts" property 93 * and convert them all into ino. 94 */ 95 i = ddi_getprop(DDI_DEV_T_ANY, dip, 0, "#interrupt-cells", 1); 96 i = CELLS_1275_TO_BYTES(i); 97 no_of_intrs = px_p->px_inos_len / i; 98 for (i = 0; i < no_of_intrs; i++) 99 px_p->px_inos[i] = px_p->px_inos[i] & 0x3F; 100 101 /* 102 * Get the ranges property. 103 */ 104 if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "ranges", 105 (caddr_t)&px_p->px_ranges_p, &px_p->px_ranges_length) != 106 DDI_SUCCESS) { 107 108 cmn_err(CE_WARN, "%s%d: no ranges property\n", 109 ddi_driver_name(dip), ddi_get_instance(dip)); 110 kmem_free(px_p->px_inos, px_p->px_inos_len); 111 return (DDI_FAILURE); 112 } 113 114 px_p->px_thermal_interrupt = 115 ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 116 "thermal-interrupt", -1); 117 DBG(DBG_ATTACH, dip, "get_px_properties: thermal_interrupt=%d\n", 118 px_p->px_thermal_interrupt); 119 return (DDI_SUCCESS); 120 } 121 122 /* 123 * px_free_props: 124 * 125 * This routine frees the memory used to cache the "interrupts" 126 * and "ranges" properties of the pci bus device node. 127 * 128 * used by: px_detach() 129 * 130 * return value: none 131 */ 132 void 133 px_free_props(px_t *px_p) 134 { 135 kmem_free(px_p->px_inos, px_p->px_inos_len); 136 kmem_free(px_p->px_ranges_p, px_p->px_ranges_length); 137 } 138 139 /* 140 * px_reloc_reg 141 * 142 * If the "reg" entry (*px_rp) is relocatable, lookup "assigned-addresses" 143 * property to fetch corresponding relocated address. 144 * 145 * used by: px_map() 146 * 147 * return value: 148 * 149 * DDI_SUCCESS - on success 150 * DDI_ME_INVAL - regspec is invalid 151 */ 152 int 153 px_reloc_reg(dev_info_t *dip, dev_info_t *rdip, px_t *px_p, 154 pci_regspec_t *rp) 155 { 156 int assign_len, assign_entries, i; 157 pci_regspec_t *assign_p; 158 uint32_t phys_hi = rp->pci_phys_hi; 159 uint32_t space_type = phys_hi & PCI_REG_ADDR_M; /* 28-bit */ 160 161 DBG(DBG_MAP | DBG_CONT, dip, "\tpx_reloc_reg fr: %x.%x.%x %x.%x\n", 162 rp->pci_phys_hi, rp->pci_phys_mid, rp->pci_phys_low, 163 rp->pci_size_hi, rp->pci_size_low); 164 165 if (space_type == PCI_ADDR_CONFIG || phys_hi & PCI_RELOCAT_B) 166 return (DDI_SUCCESS); 167 168 /* 169 * Hot plug will be taken care of later 170 * if (px_p->hotplug_capable == B_FALSE) 171 */ 172 { 173 uint32_t bus = PCI_REG_BUS_G(phys_hi); 174 if (bus < px_p->px_bus_range.lo || 175 bus > px_p->px_bus_range.hi) { 176 DBG(DBG_MAP | DBG_CONT, dip, "bad bus# (%x)\n", bus); 177 return (DDI_ME_INVAL); 178 } 179 } 180 181 i = ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS, 182 "assigned-addresses", (caddr_t)&assign_p, &assign_len); 183 if (i) { 184 DBG(DBG_MAP | DBG_CONT, dip, "%s%d: assigned-addresses %d\n", 185 ddi_driver_name(rdip), ddi_get_instance(rdip), i); 186 return (DDI_ME_INVAL); 187 } 188 189 assign_entries = assign_len / sizeof (pci_regspec_t); 190 for (i = 0; i < assign_entries; i++, assign_p++) { 191 uint32_t assign_type = assign_p->pci_phys_hi & PCI_REG_ADDR_M; 192 uint32_t assign_addr = PCI_REG_BDFR_G(assign_p->pci_phys_hi); 193 194 if (PCI_REG_BDFR_G(phys_hi) != assign_addr) 195 continue; 196 if (space_type == assign_type) { /* exact match */ 197 rp->pci_phys_low += assign_p->pci_phys_low; 198 break; 199 } 200 if (space_type == PCI_ADDR_MEM64 && 201 assign_type == PCI_ADDR_MEM32) { 202 rp->pci_phys_low += assign_p->pci_phys_low; 203 rp->pci_phys_hi ^= PCI_ADDR_MEM64 ^ PCI_ADDR_MEM32; 204 break; 205 } 206 } 207 kmem_free(assign_p - i, assign_len); 208 DBG(DBG_MAP | DBG_CONT, dip, "\tpx_reloc_reg to: %x.%x.%x %x.%x <%d>\n", 209 rp->pci_phys_hi, rp->pci_phys_mid, rp->pci_phys_low, 210 rp->pci_size_hi, rp->pci_size_low, i); 211 return (i < assign_entries ? DDI_SUCCESS : DDI_ME_INVAL); 212 } 213 214 /* 215 * use "ranges" to translate relocated pci regspec into parent space 216 */ 217 int 218 px_xlate_reg(px_t *px_p, pci_regspec_t *px_rp, struct regspec *new_rp) 219 { 220 int n; 221 px_ranges_t *rng_p = px_p->px_ranges_p; 222 int rng_n = px_p->px_ranges_length / sizeof (px_ranges_t); 223 224 uint32_t space_type = PCI_REG_ADDR_G(px_rp->pci_phys_hi); 225 uint32_t reg_end, reg_begin = px_rp->pci_phys_low; 226 uint32_t sz = px_rp->pci_size_low; 227 228 uint32_t rng_begin, rng_end; 229 230 if (space_type == PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) { 231 if (reg_begin > PCI_CONF_HDR_SIZE) 232 return (DDI_ME_INVAL); 233 sz = sz ? MIN(sz, PCI_CONF_HDR_SIZE) : PCI_CONF_HDR_SIZE; 234 reg_begin += px_rp->pci_phys_hi << 4; 235 } 236 reg_end = reg_begin + sz - 1; 237 238 for (n = 0; n < rng_n; n++, rng_p++) { 239 if (space_type != PCI_REG_ADDR_G(rng_p->child_high)) 240 continue; /* not the same space type */ 241 242 rng_begin = rng_p->child_low; 243 if (space_type == PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) 244 rng_begin += rng_p->child_high; 245 246 rng_end = rng_begin + rng_p->size_low - 1; 247 if (reg_begin >= rng_begin && reg_end <= rng_end) 248 break; 249 } 250 if (n >= rng_n) 251 return (DDI_ME_REGSPEC_RANGE); 252 253 new_rp->regspec_addr = reg_begin - rng_begin + rng_p->parent_low; 254 new_rp->regspec_bustype = rng_p->parent_high; 255 new_rp->regspec_size = sz; 256 DBG(DBG_MAP | DBG_CONT, px_p->px_dip, 257 "\tpx_xlate_reg: entry %d new_rp %x.%x %x\n", 258 n, new_rp->regspec_bustype, new_rp->regspec_addr, sz); 259 260 return (DDI_SUCCESS); 261 } 262 263 /* 264 * px_map_registers 265 * 266 * This function is called from the attach routine to map the registers 267 * accessed by this driver. 268 * 269 * used by: px_attach() 270 * 271 * return value: DDI_FAILURE on failure 272 * 273 * Remove px_map_regs() from here and move them to SUN4U library code, 274 * after complete virtualization (after porting MSI and Error handling code). 275 */ 276 int 277 px_map_regs(px_t *px_p, dev_info_t *dip) 278 { 279 ddi_device_acc_attr_t attr; 280 px_reg_bank_t reg_bank = PX_REG_CSR; 281 282 attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 283 attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 284 attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC; 285 286 /* 287 * PCI CSR Base 288 */ 289 if (ddi_regs_map_setup(dip, reg_bank, &px_p->px_address[reg_bank], 290 0, 0, &attr, &px_p->px_ac[reg_bank]) != DDI_SUCCESS) { 291 goto fail; 292 } 293 294 reg_bank++; 295 296 /* 297 * XBUS CSR Base 298 */ 299 if (ddi_regs_map_setup(dip, reg_bank, &px_p->px_address[reg_bank], 300 0, 0, &attr, &px_p->px_ac[reg_bank]) != DDI_SUCCESS) { 301 goto fail; 302 } 303 304 px_p->px_address[reg_bank] -= FIRE_CONTROL_STATUS; 305 306 done: 307 for (; reg_bank >= PX_REG_CSR; reg_bank--) { 308 DBG(DBG_ATTACH, dip, "reg_bank 0x%x address 0x%p\n", 309 reg_bank, px_p->px_address[reg_bank]); 310 } 311 312 return (DDI_SUCCESS); 313 314 fail: 315 cmn_err(CE_WARN, "%s%d: unable to map reg entry %d\n", 316 ddi_driver_name(dip), ddi_get_instance(dip), reg_bank); 317 318 for (reg_bank--; reg_bank >= PX_REG_CSR; reg_bank--) { 319 px_p->px_address[reg_bank] = NULL; 320 ddi_regs_map_free(&px_p->px_ac[reg_bank]); 321 } 322 323 return (DDI_FAILURE); 324 } 325 326 /* 327 * px_unmap_regs: 328 * 329 * This routine unmap the registers mapped by map_px_registers. 330 * 331 * used by: px_detach(), and error conditions in px_attach() 332 * 333 * return value: none 334 */ 335 void 336 px_unmap_regs(px_t *px_p) 337 { 338 int i; 339 340 for (i = 0; i < 4; i++) { 341 if (px_p->px_ac[i]) 342 ddi_regs_map_free(&px_p->px_ac[i]); 343 } 344 } 345 346 /* 347 * px_report_dev 348 * 349 * This function is called from our control ops routine on a 350 * DDI_CTLOPS_REPORTDEV request. 351 * 352 * The display format is 353 * 354 * <name><inst> at <pname><pinst> device <dev> function <func> 355 * 356 * where 357 * 358 * <name> this device's name property 359 * <inst> this device's instance number 360 * <name> parent device's name property 361 * <inst> parent device's instance number 362 * <dev> this device's device number 363 * <func> this device's function number 364 */ 365 int 366 px_report_dev(dev_info_t *dip) 367 { 368 if (dip == (dev_info_t *)0) 369 return (DDI_FAILURE); 370 cmn_err(CE_CONT, "?PCI Express-device: %s@%s, %s%d\n", 371 ddi_node_name(dip), ddi_get_name_addr(dip), 372 ddi_driver_name(dip), 373 ddi_get_instance(dip)); 374 return (DDI_SUCCESS); 375 } 376 377 378 /* 379 * reg property for pcimem nodes that covers the entire address 380 * space for the node: config, io, or memory. 381 */ 382 pci_regspec_t pci_pcimem_reg[3] = 383 { 384 {PCI_ADDR_CONFIG, 0, 0, 0, 0x800000 }, 385 {(uint_t)(PCI_ADDR_IO|PCI_RELOCAT_B), 0, 0, 0, PX_IO_SIZE }, 386 {(uint_t)(PCI_ADDR_MEM32|PCI_RELOCAT_B), 0, 0, 0, PX_MEM_SIZE } 387 }; 388 389 /* 390 * px_name_child 391 * 392 * This function is called from init_child to name a node. It is 393 * also passed as a callback for node merging functions. 394 * 395 * return value: DDI_SUCCESS, DDI_FAILURE 396 */ 397 static int 398 px_name_child(dev_info_t *child, char *name, int namelen) 399 { 400 pci_regspec_t *pci_rp; 401 int reglen; 402 uint_t func; 403 char **unit_addr; 404 uint_t n; 405 406 /* 407 * Set the address portion of the node name based on 408 * unit-address property, if it exists. 409 * The interpretation of the unit-address is DD[,F] 410 * where DD is the device id and F is the function. 411 */ 412 if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child, 413 DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) == 414 DDI_PROP_SUCCESS) { 415 if (n != 1 || *unit_addr == NULL || **unit_addr == 0) { 416 cmn_err(CE_WARN, "unit-address property in %s.conf" 417 " not well-formed", ddi_driver_name(child)); 418 ddi_prop_free(unit_addr); 419 return (DDI_FAILURE); 420 } 421 (void) snprintf(name, namelen, "%s", *unit_addr); 422 ddi_prop_free(unit_addr); 423 return (DDI_SUCCESS); 424 } 425 426 /* 427 * The unit-address property is does not exist. Set the address 428 * portion of the node name based on the function and device number. 429 */ 430 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 431 "reg", (int **)&pci_rp, (uint_t *)®len) == DDI_SUCCESS) { 432 if (((reglen * sizeof (int)) % sizeof (pci_regspec_t)) != 0) { 433 cmn_err(CE_WARN, "reg property not well-formed"); 434 return (DDI_FAILURE); 435 } 436 437 func = PCI_REG_FUNC_G(pci_rp[0].pci_phys_hi); 438 if (func != 0) 439 (void) snprintf(name, namelen, "%x,%x", 440 PCI_REG_DEV_G(pci_rp[0].pci_phys_hi), func); 441 else 442 (void) snprintf(name, namelen, "%x", 443 PCI_REG_DEV_G(pci_rp[0].pci_phys_hi)); 444 ddi_prop_free(pci_rp); 445 return (DDI_SUCCESS); 446 } 447 448 cmn_err(CE_WARN, "cannot name pci child '%s'", ddi_node_name(child)); 449 return (DDI_FAILURE); 450 } 451 452 int 453 px_uninit_child(px_t *px_p, dev_info_t *child) 454 { 455 DBG(DBG_INIT_CLD, px_p->px_dip, 456 "DDI_CTLOPS_UNINITCHILD: arg=%s%d\n", 457 ddi_driver_name(child), ddi_get_instance(child)); 458 459 ddi_set_name_addr(child, NULL); 460 ddi_remove_minor_node(child, NULL); 461 impl_rem_dev_props(child); 462 return (DDI_SUCCESS); 463 } 464 465 /*ARGSUSED*/ 466 void 467 px_post_init_child(px_t *px_p, dev_info_t *child) 468 { 469 /* Add px specific PEC code */ 470 } 471 472 /* 473 * px_init_child 474 * 475 * This function is called from our control ops routine on a 476 * DDI_CTLOPS_INITCHILD request. It builds and sets the device's 477 * parent private data area. 478 * 479 * used by: pci_ctlops() 480 * 481 * return value: none 482 */ 483 int 484 px_init_child(px_t *px_p, dev_info_t *child) 485 { 486 pci_regspec_t *pci_rp; 487 char name[10]; 488 ddi_acc_handle_t config_handle; 489 uint16_t command_preserve, command; 490 uint8_t bcr; 491 uint8_t header_type, min_gnt; 492 uint16_t latency_timer; 493 uint_t n; 494 int i, no_config; 495 496 /* 497 * The following is a special case for pcimem nodes. 498 * For these nodes we create a reg property with a 499 * single entry that covers the entire address space 500 * for the node (config, io or memory). 501 */ 502 if (strcmp(ddi_driver_name(child), "pcimem") == 0) { 503 (void) ddi_prop_create(DDI_DEV_T_NONE, child, 504 DDI_PROP_CANSLEEP, "reg", (caddr_t)pci_pcimem_reg, 505 sizeof (pci_pcimem_reg)); 506 ddi_set_name_addr(child, "0"); 507 ddi_set_parent_data(child, NULL); 508 return (DDI_SUCCESS); 509 } 510 511 /* 512 * Check whether the node has config space or is a hard decode 513 * node (possibly created by a driver.conf file). 514 */ 515 no_config = ddi_prop_get_int(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 516 "no-config", 0); 517 518 /* 519 * Pseudo nodes indicate a prototype node with per-instance 520 * properties to be merged into the real h/w device node. 521 * However, do not merge if the no-config property is set 522 * (see PSARC 2000/088). 523 */ 524 if ((ndi_dev_is_persistent_node(child) == 0) && (no_config == 0)) { 525 extern int pci_allow_pseudo_children; 526 527 if (ddi_getlongprop(DDI_DEV_T_ANY, child, 528 DDI_PROP_DONTPASS, "reg", (caddr_t)&pci_rp, &i) == 529 DDI_SUCCESS) { 530 cmn_err(CE_WARN, "cannot merge prototype from %s.conf", 531 ddi_driver_name(child)); 532 kmem_free(pci_rp, i); 533 return (DDI_NOT_WELL_FORMED); 534 } 535 /* 536 * Name the child 537 */ 538 if (px_name_child(child, name, 10) != DDI_SUCCESS) 539 return (DDI_FAILURE); 540 541 ddi_set_name_addr(child, name); 542 ddi_set_parent_data(child, NULL); 543 544 /* 545 * Try to merge the properties from this prototype 546 * node into real h/w nodes. 547 */ 548 if (ndi_merge_node(child, px_name_child) == DDI_SUCCESS) { 549 /* 550 * Merged ok - return failure to remove the node. 551 */ 552 ddi_set_name_addr(child, NULL); 553 return (DDI_FAILURE); 554 } 555 556 /* workaround for ddivs to run under PCI */ 557 if (pci_allow_pseudo_children) 558 return (DDI_SUCCESS); 559 560 cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged", 561 ddi_driver_name(child), ddi_get_name_addr(child), 562 ddi_driver_name(child)); 563 ddi_set_name_addr(child, NULL); 564 return (DDI_NOT_WELL_FORMED); 565 } 566 567 if (px_name_child(child, name, 10) != DDI_SUCCESS) 568 return (DDI_FAILURE); 569 ddi_set_name_addr(child, name); 570 571 if (no_config != 0) { 572 /* 573 * There is no config space so there's nothing more to do. 574 */ 575 return (DDI_SUCCESS); 576 } 577 578 if (pcie_pm_hold(px_p->px_dip) != DDI_SUCCESS) { 579 DBG(DBG_PWR, px_p->px_dip, 580 "INITCHILD: px_pm_hold failed\n"); 581 return (DDI_FAILURE); 582 } 583 /* Any return of DDI_FAILURE after this must call px_pm_release */ 584 585 /* 586 * If configuration registers were previously saved by 587 * child (before it went to D3), then let the child do the 588 * restore to set up the config regs as it'll first need to 589 * power the device out of D3. 590 */ 591 if (ddi_prop_exists(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 592 "config-regs-saved-by-child") == 1) { 593 DBG(DBG_PWR, child, 594 "INITCHILD: config regs to be restored by child\n"); 595 596 return (DDI_SUCCESS); 597 } 598 599 DBG(DBG_PWR, ddi_get_parent(child), 600 "INITCHILD: config regs setup for %s@%s\n", 601 ddi_node_name(child), ddi_get_name_addr(child)); 602 603 /* 604 * Map the child configuration space to for initialization. 605 * We assume the obp will do the following in the devices 606 * config space: 607 * 608 * Set the latency-timer register to values appropriate 609 * for the devices on the bus (based on other devices 610 * MIN_GNT and MAX_LAT registers. 611 * 612 * Set the fast back-to-back enable bit in the command 613 * register if it's supported and all devices on the bus 614 * have the capability. 615 * 616 */ 617 if (pci_config_setup(child, &config_handle) != DDI_SUCCESS) { 618 ddi_set_name_addr(child, NULL); 619 pcie_pm_release(px_p->px_dip); 620 return (DDI_FAILURE); 621 } 622 623 /* 624 * Determine the configuration header type. 625 */ 626 header_type = pci_config_get8(config_handle, PCI_CONF_HEADER); 627 DBG(DBG_INIT_CLD, px_p->px_dip, "%s: header_type=%x\n", 628 ddi_driver_name(child), header_type); 629 630 /* 631 * Support for "command-preserve" property. Note that we 632 * add PCI_COMM_BACK2BACK_ENAB to the bits to be preserved 633 * since the obp will set this if the device supports and 634 * all targets on the same bus support it. Since psycho 635 * doesn't support PCI_COMM_BACK2BACK_ENAB, it will never 636 * be set. This is just here in case future revs do support 637 * PCI_COMM_BACK2BACK_ENAB. 638 */ 639 command_preserve = 640 ddi_prop_get_int(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 641 "command-preserve", 0); 642 DBG(DBG_INIT_CLD, px_p->px_dip, "%s: command-preserve=%x\n", 643 ddi_driver_name(child), command_preserve); 644 command = pci_config_get16(config_handle, PCI_CONF_COMM); 645 command &= (command_preserve | PCI_COMM_BACK2BACK_ENAB); 646 command |= (px_command_default & ~command_preserve); 647 pci_config_put16(config_handle, PCI_CONF_COMM, command); 648 command = pci_config_get16(config_handle, PCI_CONF_COMM); 649 DBG(DBG_INIT_CLD, px_p->px_dip, "%s: command=%x\n", 650 ddi_driver_name(child), 651 pci_config_get16(config_handle, PCI_CONF_COMM)); 652 653 /* 654 * If the device has a bus control register then program it 655 * based on the settings in the command register. 656 */ 657 if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_ONE) { 658 bcr = pci_config_get8(config_handle, PCI_BCNF_BCNTRL); 659 if (px_command_default & PCI_COMM_PARITY_DETECT) 660 bcr |= PCI_BCNF_BCNTRL_PARITY_ENABLE; 661 if (px_command_default & PCI_COMM_SERR_ENABLE) 662 bcr |= PCI_BCNF_BCNTRL_SERR_ENABLE; 663 bcr |= PCI_BCNF_BCNTRL_MAST_AB_MODE; 664 pci_config_put8(config_handle, PCI_BCNF_BCNTRL, bcr); 665 } 666 667 /* 668 * Initialize latency timer registers if needed. 669 */ 670 if (px_set_latency_timer_register && 671 ddi_getprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 672 "latency-timer", 0) == 0) { 673 674 latency_timer = px_latency_timer; 675 if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_ONE) { 676 pci_config_put8(config_handle, PCI_BCNF_LATENCY_TIMER, 677 latency_timer); 678 } else { 679 min_gnt = pci_config_get8(config_handle, 680 PCI_CONF_MIN_G); 681 DBG(DBG_INIT_CLD, px_p->px_dip, "%s: min_gnt=%x\n", 682 ddi_driver_name(child), min_gnt); 683 } 684 latency_timer = MIN(latency_timer, 0xff); 685 pci_config_put8(config_handle, PCI_CONF_LATENCY_TIMER, 686 latency_timer); 687 n = pci_config_get8(config_handle, PCI_CONF_LATENCY_TIMER); 688 if (n != 0) 689 (void) ndi_prop_update_int(DDI_DEV_T_NONE, child, 690 "latency-timer", n); 691 } 692 693 pci_config_teardown(&config_handle); 694 695 /* 696 * Handle chip specific init-child tasks. 697 */ 698 px_post_init_child(px_p, child); 699 pcie_pm_release(px_p->px_dip); 700 701 return (DDI_SUCCESS); 702 } 703 704 /* 705 * px_get_reg_set_size 706 * 707 * Given a dev info pointer to a pci child and a register number, this 708 * routine returns the size element of that reg set property. 709 * 710 * used by: pci_ctlops() - DDI_CTLOPS_REGSIZE 711 * 712 * return value: size of reg set on success, -1 on error 713 */ 714 off_t 715 px_get_reg_set_size(dev_info_t *child, int rnumber) 716 { 717 pci_regspec_t *pci_rp; 718 off_t size = -1; 719 int i; 720 721 if (rnumber < 0) 722 return (-1); 723 724 /* 725 * Get the reg property for the device. 726 */ 727 if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg", 728 (caddr_t)&pci_rp, &i) != DDI_SUCCESS) 729 return (-1); 730 731 if (rnumber >= (i / (int)sizeof (pci_regspec_t))) 732 goto done; 733 734 size = pci_rp[rnumber].pci_size_low | 735 ((uint64_t)pci_rp[rnumber].pci_size_hi << 32); 736 done: 737 kmem_free(pci_rp, i); 738 return (size); 739 } 740 741 742 /* 743 * px_get_nreg_set 744 * 745 * Given a dev info pointer to a pci child, this routine returns the 746 * number of sets in its "reg" property. 747 * 748 * used by: pci_ctlops() - DDI_CTLOPS_NREGS 749 * 750 * return value: # of reg sets on success, zero on error 751 */ 752 uint_t 753 px_get_nreg_set(dev_info_t *child) 754 { 755 pci_regspec_t *pci_rp; 756 int i, n; 757 758 /* 759 * Get the reg property for the device. 760 */ 761 if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg", 762 (caddr_t)&pci_rp, &i) != DDI_SUCCESS) 763 return (0); 764 765 n = i / (int)sizeof (pci_regspec_t); 766 kmem_free(pci_rp, i); 767 return (n); 768 } 769 770 771 /* 772 * px_get_nintr 773 * 774 * Given a dev info pointer to a pci child, this routine returns the 775 * number of items in its "interrupts" property. 776 * 777 * used by: pci_ctlops() - DDI_CTLOPS_NREGS 778 * 779 * return value: # of interrupts on success, zero on error 780 */ 781 uint_t 782 px_get_nintr(dev_info_t *child) 783 { 784 int *pci_ip; 785 int i, n; 786 787 if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 788 "interrupts", (caddr_t)&pci_ip, &i) != DDI_SUCCESS) 789 return (0); 790 791 n = i / (int)sizeof (uint_t); 792 kmem_free(pci_ip, i); 793 return (n); 794 } 795 796 uint64_t 797 px_get_cfg_pabase(px_t *px_p) 798 { 799 int i; 800 px_ranges_t *rangep = px_p->px_ranges_p; 801 int nrange = px_p->px_ranges_length / sizeof (px_ranges_t); 802 uint32_t cfg_space_type = PCI_REG_ADDR_G(PCI_ADDR_CONFIG); 803 804 ASSERT(cfg_space_type == 0); 805 806 for (i = 0; i < nrange; i++, rangep++) { 807 if (PCI_REG_ADDR_G(rangep->child_high) == cfg_space_type) 808 break; 809 } 810 811 if (i >= nrange) 812 cmn_err(CE_PANIC, "no cfg space in px(%x) ranges prop.\n", 813 (void *)px_p); 814 815 return (((uint64_t)rangep->parent_high << 32) | rangep->parent_low); 816 } 817 818 /* 819 * decodes standard PCI config space 16bit error status reg 820 */ 821 int 822 px_log_cfg_err(dev_info_t *dip, ushort_t status_reg, char *err_msg) 823 { 824 int nerr = ddi_get_instance(dip); /* temp for instance */ 825 uint64_t perr_fatal = px_perr_fatal & (1 << nerr); 826 uint64_t serr_fatal = px_serr_fatal & (1 << nerr); 827 nerr = 0; 828 829 if ((status_reg & PCI_STAT_PERROR) && perr_fatal) 830 nerr++; 831 if ((status_reg & PCI_STAT_S_SYSERR) && serr_fatal) 832 nerr++; 833 if (status_reg & PCI_STAT_R_MAST_AB) 834 nerr++; 835 if ((status_reg & PCI_STAT_S_PERROR) && perr_fatal) 836 nerr++; 837 838 cmn_err(CE_WARN, "%s%d: %sPCI Express config space CSR=0x%b", 839 ddi_driver_name(dip), ddi_get_instance(dip), err_msg, 840 (uint32_t)status_reg, PX_STATUS_BITS); 841 842 return (nerr); 843 } 844 845 /* 846 * remove the following functions once we port error handling and other 847 * misc functionalities based on new VPCI interfaces. 848 */ 849 uint64_t 850 px_get_err_reg(caddr_t csr, uint32_t off) 851 { 852 return (CSR_XR(csr, off)); 853 } 854 855 void 856 px_set_err_reg(caddr_t csr, uint32_t off, uint64_t val) 857 { 858 CSR_XS(csr, off, val); 859 } 860