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 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * This file contains the common hotplug code that is used by Standard 29 * PCIe and PCI HotPlug Controller code. 30 * 31 * NOTE: This file is compiled and delivered through misc/pcie module. 32 */ 33 34 #include <sys/types.h> 35 #include <sys/conf.h> 36 #include <sys/kmem.h> 37 #include <sys/debug.h> 38 #include <sys/vtrace.h> 39 #include <sys/autoconf.h> 40 #include <sys/varargs.h> 41 #include <sys/ddi_impldefs.h> 42 #include <sys/time.h> 43 #include <sys/note.h> 44 #include <sys/callb.h> 45 #include <sys/ddi.h> 46 #include <sys/sunddi.h> 47 #include <sys/sunndi.h> 48 #include <sys/sysevent.h> 49 #include <sys/sysevent/eventdefs.h> 50 #include <sys/sysevent/dr.h> 51 #include <sys/pci_impl.h> 52 #include <sys/pci_cap.h> 53 #include <sys/hotplug/pci/pcicfg.h> 54 #include <sys/hotplug/pci/pcie_hp.h> 55 #include <sys/hotplug/pci/pciehpc.h> 56 #include <sys/hotplug/pci/pcishpc.h> 57 #include <io/pciex/pcieb.h> 58 59 /* Local functions prototype */ 60 static int pcie_hp_list_occupants(dev_info_t *dip, void *arg); 61 static int pcie_hp_register_port(dev_info_t *dip, dev_info_t *pdip, 62 char *cn_name); 63 static int pcie_hp_register_ports_for_dev(dev_info_t *dip, int device_num); 64 static int pcie_hp_unregister_ports_cb(ddi_hp_cn_info_t *info, void *arg); 65 static int pcie_hp_get_port_state(ddi_hp_cn_info_t *info, void *arg); 66 static int pcie_hp_match_dev_func(dev_info_t *dip, void *hdl); 67 static boolean_t pcie_hp_match_dev(dev_info_t *dip, int dev_num); 68 static int pcie_hp_get_df_from_port_name(char *cn_name, int *dev_num, 69 int *func_num); 70 static int pcie_hp_create_port_name_num(dev_info_t *dip, 71 ddi_hp_cn_info_t *cn_info); 72 static int pcie_hp_check_hardware_existence(dev_info_t *dip, int dev_num, 73 int func_num); 74 75 /* 76 * Global functions (called by other drivers/modules) 77 */ 78 79 /* 80 * return description text for led state 81 */ 82 char * 83 pcie_led_state_text(pcie_hp_led_state_t state) 84 { 85 switch (state) { 86 case PCIE_HP_LED_ON: 87 return (PCIEHPC_PROP_VALUE_ON); 88 case PCIE_HP_LED_OFF: 89 return (PCIEHPC_PROP_VALUE_OFF); 90 case PCIE_HP_LED_BLINK: 91 default: 92 return (PCIEHPC_PROP_VALUE_BLINK); 93 } 94 } 95 96 /* 97 * return description text for slot condition 98 */ 99 char * 100 pcie_slot_condition_text(ap_condition_t condition) 101 { 102 switch (condition) { 103 case AP_COND_UNKNOWN: 104 return (PCIEHPC_PROP_VALUE_UNKNOWN); 105 case AP_COND_OK: 106 return (PCIEHPC_PROP_VALUE_OK); 107 case AP_COND_FAILING: 108 return (PCIEHPC_PROP_VALUE_FAILING); 109 case AP_COND_FAILED: 110 return (PCIEHPC_PROP_VALUE_FAILED); 111 case AP_COND_UNUSABLE: 112 return (PCIEHPC_PROP_VALUE_UNUSABLE); 113 default: 114 return (PCIEHPC_PROP_VALUE_UNKNOWN); 115 } 116 } 117 118 /* 119 * routine to copy in a nvlist from userland 120 */ 121 int 122 pcie_copyin_nvlist(char *packed_buf, size_t packed_sz, nvlist_t **nvlp) 123 { 124 int ret = DDI_SUCCESS; 125 char *packed; 126 nvlist_t *dest = NULL; 127 128 if (packed_buf == NULL || packed_sz == 0) 129 return (DDI_EINVAL); 130 131 /* copyin packed nvlist */ 132 if ((packed = kmem_alloc(packed_sz, KM_SLEEP)) == NULL) 133 return (DDI_ENOMEM); 134 135 if (copyin(packed_buf, packed, packed_sz) != 0) { 136 cmn_err(CE_WARN, "pcie_copyin_nvlist: copyin failed.\n"); 137 ret = DDI_FAILURE; 138 goto copyin_cleanup; 139 } 140 141 /* unpack packed nvlist */ 142 if ((ret = nvlist_unpack(packed, packed_sz, &dest, KM_SLEEP)) != 0) { 143 cmn_err(CE_WARN, "pcie_copyin_nvlist: nvlist_unpack " 144 "failed with err %d\n", ret); 145 switch (ret) { 146 case EINVAL: 147 case ENOTSUP: 148 ret = DDI_EINVAL; 149 goto copyin_cleanup; 150 case ENOMEM: 151 ret = DDI_ENOMEM; 152 goto copyin_cleanup; 153 default: 154 ret = DDI_FAILURE; 155 goto copyin_cleanup; 156 } 157 } 158 *nvlp = dest; 159 copyin_cleanup: 160 kmem_free(packed, packed_sz); 161 return (ret); 162 } 163 164 /* 165 * routine to copy out a nvlist to userland 166 */ 167 int 168 pcie_copyout_nvlist(nvlist_t *nvl, char *packed_buf, size_t *buf_sz) 169 { 170 int err = 0; 171 char *buf = NULL; 172 size_t packed_sz; 173 174 if (nvl == NULL || packed_buf == NULL || buf_sz == NULL) 175 return (DDI_EINVAL); 176 177 /* pack nvlist, the library will allocate memory */ 178 if ((err = nvlist_pack(nvl, &buf, &packed_sz, NV_ENCODE_NATIVE, 0)) 179 != 0) { 180 cmn_err(CE_WARN, "pcie_copyout_nvlist: nvlist_pack " 181 "failed with err %d\n", err); 182 switch (err) { 183 case EINVAL: 184 case ENOTSUP: 185 return (DDI_EINVAL); 186 case ENOMEM: 187 return (DDI_ENOMEM); 188 default: 189 return (DDI_FAILURE); 190 } 191 } 192 if (packed_sz > *buf_sz) { 193 return (DDI_EINVAL); 194 } 195 196 /* copyout packed nvlist */ 197 if (copyout(buf, packed_buf, packed_sz) != 0) { 198 cmn_err(CE_WARN, "pcie_copyout_nvlist: copyout " "failed.\n"); 199 kmem_free(buf, packed_sz); 200 return (DDI_FAILURE); 201 } 202 203 *buf_sz = packed_sz; 204 kmem_free(buf, packed_sz); 205 return (DDI_SUCCESS); 206 } 207 208 /* 209 * init bus_hp_op entry and init hotpluggable slots & virtual ports 210 */ 211 int 212 pcie_hp_init(dev_info_t *dip, caddr_t arg) 213 { 214 pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 215 int ret = DDI_SUCCESS, count; 216 dev_info_t *cdip; 217 218 if (PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p)) { 219 /* Init hotplug controller */ 220 ret = pciehpc_init(dip, arg); 221 } else if (PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p)) { 222 ret = pcishpc_init(dip); 223 } 224 225 if (ret != DDI_SUCCESS) { 226 cmn_err(CE_WARN, "pcie_hp_init: initialize hotplug " 227 "controller failed with %d\n", ret); 228 return (ret); 229 } 230 231 ndi_devi_enter(dip, &count); 232 233 /* Create port for the first level children */ 234 cdip = ddi_get_child(dip); 235 while (cdip != NULL) { 236 if ((ret = pcie_hp_register_port(cdip, dip, NULL)) 237 != DDI_SUCCESS) { 238 /* stop and cleanup */ 239 break; 240 } 241 cdip = ddi_get_next_sibling(cdip); 242 } 243 ndi_devi_exit(dip, count); 244 if (ret != DDI_SUCCESS) { 245 cmn_err(CE_WARN, "pcie_hp_init: initialize virtual " 246 "hotplug port failed with %d\n", ret); 247 (void) pcie_hp_uninit(dip); 248 249 return (ret); 250 } 251 252 return (DDI_SUCCESS); 253 } 254 255 /* 256 * uninit the hotpluggable slots and virtual ports 257 */ 258 int 259 pcie_hp_uninit(dev_info_t *dip) 260 { 261 pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 262 pcie_hp_unreg_port_t arg; 263 264 /* 265 * Must set arg.rv to NDI_SUCCESS so that if there's no port 266 * under this dip, we still return success thus the bridge 267 * driver can be successfully detached. 268 * 269 * Note that during the probe PCI configurator calls 270 * ndi_devi_offline() to detach driver for a new probed bridge, 271 * so that it can reprogram the resources for the bridge, 272 * ndi_devi_offline() calls into pcieb_detach() which in turn 273 * calls into this function. In this case there are no ports 274 * created under a new probe bridge dip, as ports are only 275 * created after the configurator finishing probing, thus the 276 * ndi_hp_walk_cn() will see no ports when this is called 277 * from the PCI configurtor. 278 */ 279 arg.nexus_dip = dip; 280 arg.connector_num = DDI_HP_CN_NUM_NONE; 281 arg.rv = NDI_SUCCESS; 282 283 /* tear down all virtual hotplug handles */ 284 ndi_hp_walk_cn(dip, pcie_hp_unregister_ports_cb, &arg); 285 286 if (arg.rv != NDI_SUCCESS) 287 return (DDI_FAILURE); 288 289 if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p)) 290 (void) pciehpc_uninit(dip); 291 else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p)) 292 (void) pcishpc_uninit(dip); 293 294 return (DDI_SUCCESS); 295 } 296 297 /* 298 * interrupt handler 299 */ 300 int 301 pcie_hp_intr(dev_info_t *dip) 302 { 303 pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 304 int ret = DDI_INTR_UNCLAIMED; 305 306 if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p)) 307 ret = pciehpc_intr(dip); 308 else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p)) 309 ret = pcishpc_intr(dip); 310 311 return (ret); 312 } 313 314 /* 315 * Probe the given PCIe/PCI Hotplug Connection (CN). 316 */ 317 /*ARGSUSED*/ 318 int 319 pcie_hp_probe(pcie_hp_slot_t *slot_p) 320 { 321 pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl; 322 dev_info_t *dip = ctrl_p->hc_dip; 323 324 /* 325 * Call the configurator to probe a given PCI hotplug 326 * Hotplug Connection (CN). 327 */ 328 if (pcicfg_configure(dip, slot_p->hs_device_num, PCICFG_ALL_FUNC, 0) 329 != PCICFG_SUCCESS) { 330 PCIE_DBG("pcie_hp_probe() failed\n"); 331 return (DDI_FAILURE); 332 } 333 slot_p->hs_condition = AP_COND_OK; 334 pcie_hp_create_occupant_props(dip, makedevice(ddi_driver_major(dip), 335 slot_p->hs_minor), slot_p->hs_device_num); 336 337 /* 338 * Create ports for the newly probed devices. 339 * Note, this is only for the first level children because the 340 * descendants' ports will be created during bridge driver attach. 341 */ 342 return (pcie_hp_register_ports_for_dev(dip, slot_p->hs_device_num)); 343 } 344 345 /* 346 * Unprobe the given PCIe/PCI Hotplug Connection (CN): 347 * 1. remove all child device nodes 348 * 2. unregister all dependent ports 349 */ 350 /*ARGSUSED*/ 351 int 352 pcie_hp_unprobe(pcie_hp_slot_t *slot_p) 353 { 354 pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl; 355 dev_info_t *dip = ctrl_p->hc_dip; 356 pcie_hp_unreg_port_t arg; 357 358 /* 359 * Call the configurator to unprobe a given PCI hotplug 360 * Hotplug Connection (CN). 361 */ 362 if (pcicfg_unconfigure(dip, slot_p->hs_device_num, PCICFG_ALL_FUNC, 0) 363 != PCICFG_SUCCESS) { 364 PCIE_DBG("pcie_hp_unprobe() failed\n"); 365 return (DDI_FAILURE); 366 } 367 slot_p->hs_condition = AP_COND_UNKNOWN; 368 pcie_hp_delete_occupant_props(dip, makedevice(ddi_driver_major(dip), 369 slot_p->hs_minor)); 370 371 /* 372 * Remove ports for the unprobed devices. 373 * Note, this is only for the first level children because the 374 * descendants' ports were already removed during bridge driver dettach. 375 */ 376 arg.nexus_dip = dip; 377 arg.connector_num = slot_p->hs_info.cn_num; 378 arg.rv = NDI_SUCCESS; 379 ndi_hp_walk_cn(dip, pcie_hp_unregister_ports_cb, &arg); 380 381 return (arg.rv == NDI_SUCCESS) ? (DDI_SUCCESS) : (DDI_FAILURE); 382 } 383 384 /* Read-only probe: no hardware register programming. */ 385 int 386 pcie_read_only_probe(dev_info_t *dip, char *cn_name, dev_info_t **pcdip) 387 { 388 long dev, func; 389 int ret; 390 char *sp; 391 dev_info_t *cdip; 392 393 *pcdip = NULL; 394 /* 395 * Parse the string of a pci Port name and get the device number 396 * and function number. 397 */ 398 if (ddi_strtol(cn_name + 4, &sp, 10, &dev) != 0) 399 return (DDI_EINVAL); 400 if (ddi_strtol(sp + 1, NULL, 10, &func) != 0) 401 return (DDI_EINVAL); 402 403 ret = pcicfg_configure(dip, (int)dev, (int)func, 404 PCICFG_FLAG_READ_ONLY); 405 if (ret == PCICFG_SUCCESS) { 406 cdip = pcie_hp_devi_find(dip, (int)dev, (int)func); 407 *pcdip = cdip; 408 } 409 return (ret); 410 } 411 412 /* Read-only unprobe: no hardware register programming. */ 413 int 414 pcie_read_only_unprobe(dev_info_t *dip, char *cn_name) 415 { 416 long dev, func; 417 int ret; 418 char *sp; 419 420 /* 421 * Parse the string of a pci Port name and get the device number 422 * and function number. 423 */ 424 if (ddi_strtol(cn_name + 4, &sp, 10, &dev) != 0) 425 return (DDI_EINVAL); 426 if (ddi_strtol(sp + 1, NULL, 10, &func) != 0) 427 return (DDI_EINVAL); 428 429 ret = pcicfg_unconfigure(dip, (int)dev, (int)func, 430 PCICFG_FLAG_READ_ONLY); 431 432 return (ret); 433 } 434 435 /* Control structure used to find a device in the devinfo tree */ 436 struct pcie_hp_find_ctrl { 437 uint_t device; 438 uint_t function; 439 dev_info_t *dip; 440 }; 441 442 /* 443 * find a devinfo node with specified device and function number 444 * in the device tree under 'dip' 445 */ 446 dev_info_t * 447 pcie_hp_devi_find(dev_info_t *dip, uint_t device, uint_t function) 448 { 449 struct pcie_hp_find_ctrl ctrl; 450 int count; 451 452 ctrl.device = device; 453 ctrl.function = function; 454 ctrl.dip = NULL; 455 456 ndi_devi_enter(dip, &count); 457 ddi_walk_devs(ddi_get_child(dip), pcie_hp_match_dev_func, 458 (void *)&ctrl); 459 ndi_devi_exit(dip, count); 460 461 return (ctrl.dip); 462 } 463 464 /* 465 * routine to create 'pci-occupant' property for a hotplug slot 466 */ 467 void 468 pcie_hp_create_occupant_props(dev_info_t *dip, dev_t dev, int pci_dev) 469 { 470 pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 471 pcie_hp_ctrl_t *ctrl_p = (pcie_hp_ctrl_t *)bus_p->bus_hp_ctrl; 472 pcie_hp_slot_t *slotp; 473 pcie_hp_cn_cfg_t cn_cfg; 474 pcie_hp_occupant_info_t *occupant; 475 int circular, i; 476 477 ndi_devi_enter(dip, &circular); 478 479 if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p)) { 480 slotp = (ctrl_p && (pci_dev == 0)) ? 481 ctrl_p->hc_slots[pci_dev] : NULL; 482 } else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p)) { 483 if (ctrl_p) { 484 int slot_num; 485 486 slot_num = (ctrl_p->hc_device_increases) ? 487 (pci_dev - ctrl_p->hc_device_start) : 488 (pci_dev + ctrl_p->hc_device_start); 489 490 slotp = ctrl_p->hc_slots[slot_num]; 491 } else { 492 slotp = NULL; 493 } 494 } 495 496 if (slotp == NULL) 497 return; 498 499 occupant = kmem_alloc(sizeof (pcie_hp_occupant_info_t), KM_SLEEP); 500 occupant->i = 0; 501 502 cn_cfg.flag = B_FALSE; 503 cn_cfg.rv = NDI_SUCCESS; 504 cn_cfg.dip = NULL; 505 cn_cfg.slotp = (void *)slotp; 506 cn_cfg.cn_private = (void *)occupant; 507 508 ddi_walk_devs(ddi_get_child(dip), pcie_hp_list_occupants, 509 (void *)&cn_cfg); 510 511 if (occupant->i == 0) { 512 /* no occupants right now, need to create stub property */ 513 char *c[] = { "" }; 514 (void) ddi_prop_update_string_array(dev, dip, "pci-occupant", 515 c, 1); 516 } else { 517 (void) ddi_prop_update_string_array(dev, dip, "pci-occupant", 518 occupant->id, occupant->i); 519 } 520 521 for (i = 0; i < occupant->i; i++) 522 kmem_free(occupant->id[i], sizeof (char[MAXPATHLEN])); 523 524 kmem_free(occupant, sizeof (pcie_hp_occupant_info_t)); 525 526 ndi_devi_exit(dip, circular); 527 } 528 529 /* 530 * routine to remove 'pci-occupant' property for a hotplug slot 531 */ 532 void 533 pcie_hp_delete_occupant_props(dev_info_t *dip, dev_t dev) 534 { 535 (void) ddi_prop_remove(dev, dip, "pci-occupant"); 536 } 537 538 /* 539 * general code to create a minor node, called from hotplug controller 540 * drivers. 541 */ 542 int 543 pcie_create_minor_node(pcie_hp_ctrl_t *ctrl_p, int slot) 544 { 545 dev_info_t *dip = ctrl_p->hc_dip; 546 pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[slot]; 547 ddi_hp_cn_info_t *info_p = &slot_p->hs_info; 548 549 if (ddi_create_minor_node(dip, info_p->cn_name, 550 S_IFCHR, slot_p->hs_minor, 551 DDI_NT_PCI_ATTACHMENT_POINT, 0) != DDI_SUCCESS) { 552 return (DDI_FAILURE); 553 } 554 555 (void) ddi_prop_update_int(DDI_DEV_T_NONE, 556 dip, "ap-names", 1 << slot_p->hs_device_num); 557 558 return (DDI_SUCCESS); 559 } 560 561 /* 562 * general code to remove a minor node, called from hotplug controller 563 * drivers. 564 */ 565 void 566 pcie_remove_minor_node(pcie_hp_ctrl_t *ctrl_p, int slot) 567 { 568 ddi_remove_minor_node(ctrl_p->hc_dip, 569 ctrl_p->hc_slots[slot]->hs_info.cn_name); 570 } 571 572 /* 573 * Local functions (called within this file) 574 */ 575 576 /* 577 * Register ports for all the children with device number device_num 578 */ 579 static int 580 pcie_hp_register_ports_for_dev(dev_info_t *dip, int device_num) 581 { 582 dev_info_t *cdip; 583 int rv; 584 585 for (cdip = ddi_get_child(dip); cdip; 586 cdip = ddi_get_next_sibling(cdip)) { 587 if (pcie_hp_match_dev(cdip, device_num)) { 588 /* 589 * Found the newly probed device under the 590 * current slot. Register a port for it. 591 */ 592 if ((rv = pcie_hp_register_port(cdip, dip, NULL)) 593 != DDI_SUCCESS) 594 return (rv); 595 } else { 596 continue; 597 } 598 } 599 600 return (DDI_SUCCESS); 601 } 602 603 /* 604 * Unregister ports of a pci bridge dip, get called from ndi_hp_walk_cn() 605 * 606 * If connector_num is specified, then unregister the slot's dependent ports 607 * only; Otherwise, unregister all ports of a pci bridge dip. 608 */ 609 static int 610 pcie_hp_unregister_ports_cb(ddi_hp_cn_info_t *info, void *arg) 611 { 612 pcie_hp_unreg_port_t *unreg_arg = (pcie_hp_unreg_port_t *)arg; 613 dev_info_t *dip = unreg_arg->nexus_dip; 614 int rv = NDI_SUCCESS; 615 616 if (info->cn_type != DDI_HP_CN_TYPE_VIRTUAL_PORT) { 617 unreg_arg->rv = rv; 618 return (DDI_WALK_CONTINUE); 619 } 620 621 if (unreg_arg->connector_num != DDI_HP_CN_NUM_NONE) { 622 /* Unregister ports for all unprobed devices under a slot. */ 623 if (unreg_arg->connector_num == info->cn_num_dpd_on) { 624 625 rv = ndi_hp_unregister(dip, info->cn_name); 626 } 627 } else { 628 629 /* Unregister all ports of a pci bridge dip. */ 630 rv = ndi_hp_unregister(dip, info->cn_name); 631 } 632 633 unreg_arg->rv = rv; 634 if (rv == NDI_SUCCESS) 635 return (DDI_WALK_CONTINUE); 636 else 637 return (DDI_WALK_TERMINATE); 638 } 639 640 /* 641 * Find a port according to cn_name and get the port's state. 642 */ 643 static int 644 pcie_hp_get_port_state(ddi_hp_cn_info_t *info, void *arg) 645 { 646 pcie_hp_port_state_t *port = (pcie_hp_port_state_t *)arg; 647 648 if (info->cn_type != DDI_HP_CN_TYPE_VIRTUAL_PORT) 649 return (DDI_WALK_CONTINUE); 650 651 if (strcmp(info->cn_name, port->cn_name) == 0) { 652 /* Matched. */ 653 port->cn_state = info->cn_state; 654 port->rv = DDI_SUCCESS; 655 656 return (DDI_WALK_TERMINATE); 657 } 658 659 return (DDI_WALK_CONTINUE); 660 } 661 662 /* 663 * Find the physical slot with the given device number; 664 * return the slot if found. 665 */ 666 static pcie_hp_slot_t * 667 pcie_find_physical_slot(dev_info_t *dip, int dev_num) 668 { 669 pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 670 pcie_hp_ctrl_t *ctrl = PCIE_GET_HP_CTRL(dip); 671 672 if (PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p)) { 673 /* PCIe has only one slot */ 674 return (dev_num == 0) ? (ctrl->hc_slots[0]) : (NULL); 675 } else if (PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p)) { 676 for (int slot = 0; slot < ctrl->hc_num_slots_impl; slot++) { 677 if (ctrl->hc_slots[slot]->hs_device_num == dev_num) { 678 /* found */ 679 return (ctrl->hc_slots[slot]); 680 } 681 } 682 } 683 684 return (NULL); 685 } 686 687 /* 688 * setup slot name/slot-number info for the port which is being registered. 689 */ 690 static int 691 pcie_hp_create_port_name_num(dev_info_t *dip, ddi_hp_cn_info_t *cn_info) 692 { 693 int ret, dev_num, func_num, name_len; 694 dev_info_t *pdip = ddi_get_parent(dip); 695 pcie_bus_t *bus_p = PCIE_DIP2BUS(pdip); 696 pcie_hp_slot_t *slot; 697 pcie_req_id_t bdf; 698 char tmp[PCIE_HP_DEV_FUNC_NUM_STRING_LEN]; 699 700 ret = pcie_get_bdf_from_dip(dip, &bdf); 701 if (ret != DDI_SUCCESS) { 702 return (ret); 703 } 704 if (PCIE_IS_RP(bus_p) || PCIE_IS_SWD(bus_p) || 705 PCIE_IS_PCI2PCIE(bus_p)) { 706 /* 707 * It is under a PCIe device, devcie number is always 0; 708 * function number might > 8 in ARI supported case. 709 */ 710 dev_num = 0; 711 func_num = (bdf & ((~PCI_REG_BUS_M) >> 8)); 712 } else { 713 dev_num = (bdf & (PCI_REG_DEV_M >> 8)) >> 3; 714 func_num = bdf & (PCI_REG_FUNC_M >> 8); 715 } 716 /* 717 * The string length of dev_num and func_num must be no longer than 4 718 * including the string end mark. (With ARI case considered, e.g., 719 * dev_num=0x0, func_num=0xff.) 720 */ 721 (void) snprintf(tmp, PCIE_HP_DEV_FUNC_NUM_STRING_LEN, "%x%x", 722 dev_num, func_num); 723 /* 724 * Calculate the length of cn_name. 725 * The format of pci port name is: pci.d,f 726 * d stands for dev_num, f stands for func_num. So the length of the 727 * name string can be calculated as following. 728 */ 729 name_len = strlen(tmp) + PCIE_HP_PORT_NAME_STRING_LEN + 1; 730 731 cn_info->cn_name = (char *)kmem_zalloc(name_len, KM_SLEEP); 732 (void) snprintf(cn_info->cn_name, name_len, "pci.%x,%x", 733 dev_num, func_num); 734 cn_info->cn_num = (dev_num << 8) | func_num; 735 slot = pcie_find_physical_slot(pdip, dev_num); 736 737 cn_info->cn_num_dpd_on = slot ? 738 slot->hs_info.cn_num : DDI_HP_CN_NUM_NONE; 739 740 return (DDI_SUCCESS); 741 } 742 743 /* 744 * Extract device and function number from port name, whose format is 745 * something like 'pci.1,0' 746 */ 747 static int 748 pcie_hp_get_df_from_port_name(char *cn_name, int *dev_num, int *func_num) 749 { 750 int name_len, ret; 751 long d, f; 752 char *sp; 753 754 /* some checks for the input name */ 755 name_len = strlen(cn_name); 756 if ((name_len <= PCIE_HP_PORT_NAME_STRING_LEN) || 757 (name_len > (PCIE_HP_PORT_NAME_STRING_LEN + 758 PCIE_HP_DEV_FUNC_NUM_STRING_LEN - 1)) || 759 (strncmp("pci.", cn_name, 4) != 0)) { 760 return (DDI_EINVAL); 761 } 762 ret = ddi_strtol(cn_name + 4, &sp, 10, &d); 763 if (ret != DDI_SUCCESS) 764 return (ret); 765 766 if (strncmp(",", sp, 1) != 0) 767 return (DDI_EINVAL); 768 769 ret = ddi_strtol(sp + 1, NULL, 10, &f); 770 if (ret != DDI_SUCCESS) 771 return (ret); 772 *dev_num = (int)d; 773 *func_num = (int)f; 774 775 return (ret); 776 } 777 778 /* 779 * Check/copy cn_name and set connection numbers. 780 * If it is a valid name, then setup cn_info for the newly created port. 781 */ 782 static int 783 pcie_hp_setup_port_name_num(dev_info_t *pdip, char *cn_name, 784 ddi_hp_cn_info_t *cn_info) 785 { 786 int dev_num, func_num, ret; 787 pcie_hp_slot_t *slot; 788 789 if ((ret = pcie_hp_get_df_from_port_name(cn_name, &dev_num, &func_num)) 790 != DDI_SUCCESS) 791 return (ret); 792 793 if (pcie_hp_check_hardware_existence(pdip, dev_num, func_num) == 794 DDI_SUCCESS) { 795 cn_info->cn_state = DDI_HP_CN_STATE_PRESENT; 796 } else { 797 cn_info->cn_state = DDI_HP_CN_STATE_EMPTY; 798 } 799 800 cn_info->cn_name = ddi_strdup(cn_name, KM_SLEEP); 801 cn_info->cn_num = (dev_num << 8) | func_num; 802 803 slot = pcie_find_physical_slot(pdip, dev_num); 804 if (slot) { 805 cn_info->cn_num_dpd_on = slot->hs_info.cn_num; 806 } else { 807 cn_info->cn_num_dpd_on = DDI_HP_CN_NUM_NONE; 808 } 809 return (DDI_SUCCESS); 810 } 811 812 static int 813 ndi2ddi(int n) 814 { 815 int ret; 816 817 switch (n) { 818 case NDI_SUCCESS: 819 ret = DDI_SUCCESS; 820 break; 821 case NDI_NOMEM: 822 ret = DDI_ENOMEM; 823 break; 824 case NDI_BUSY: 825 ret = DDI_EBUSY; 826 break; 827 case NDI_EINVAL: 828 ret = DDI_EINVAL; 829 break; 830 case NDI_ENOTSUP: 831 ret = DDI_ENOTSUP; 832 break; 833 case NDI_FAILURE: 834 default: 835 ret = DDI_FAILURE; 836 break; 837 } 838 return (ret); 839 } 840 841 /* 842 * Common routine to create and register a new port 843 * 844 * Create an empty port if dip is NULL, and cn_name needs to be specified in 845 * this case. Otherwise, create a port mapping to the specified dip, and cn_name 846 * is not needed in this case. 847 */ 848 static int 849 pcie_hp_register_port(dev_info_t *dip, dev_info_t *pdip, char *cn_name) 850 { 851 ddi_hp_cn_info_t *cn_info; 852 int ret; 853 854 ASSERT((dip == NULL) != (cn_name == NULL)); 855 cn_info = kmem_zalloc(sizeof (ddi_hp_cn_info_t), KM_SLEEP); 856 if (dip != NULL) 857 ret = pcie_hp_create_port_name_num(dip, cn_info); 858 else 859 ret = pcie_hp_setup_port_name_num(pdip, cn_name, cn_info); 860 861 if (ret != DDI_SUCCESS) { 862 kmem_free(cn_info, sizeof (ddi_hp_cn_info_t)); 863 return (ret); 864 } 865 866 cn_info->cn_child = dip; 867 cn_info->cn_type = DDI_HP_CN_TYPE_VIRTUAL_PORT; 868 cn_info->cn_type_str = DDI_HP_CN_TYPE_STR_PORT; 869 870 ret = ndi_hp_register(pdip, cn_info); 871 872 kmem_free(cn_info->cn_name, strlen(cn_info->cn_name) + 1); 873 kmem_free(cn_info, sizeof (ddi_hp_cn_info_t)); 874 875 return (ndi2ddi(ret)); 876 } 877 878 /* Check if there is a piece of hardware exist corresponding to the cn_name */ 879 static int 880 pcie_hp_check_hardware_existence(dev_info_t *dip, int dev_num, int func_num) 881 { 882 883 /* 884 * VHPTODO: 885 * According to device and function number, check if there is a hardware 886 * device exists. Currently, this function can not be reached before 887 * we enable state transition to or from "Port-Empty" or "Port-Present" 888 * states. When the pci device type project is integrated, we are going 889 * to call the pci config space access interfaces introduced by it. 890 */ 891 _NOTE(ARGUNUSED(dip, dev_num, func_num)); 892 893 return (DDI_SUCCESS); 894 } 895 896 /* 897 * Dispatch hotplug commands to different hotplug controller drivers, including 898 * physical and virtual hotplug operations. 899 */ 900 /* ARGSUSED */ 901 int 902 pcie_hp_common_ops(dev_info_t *dip, char *cn_name, ddi_hp_op_t op, 903 void *arg, void *result) 904 { 905 pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 906 int ret = DDI_SUCCESS; 907 908 PCIE_DBG("pcie_hp_common_ops: dip=%p cn_name=%s op=%x arg=%p\n", 909 dip, cn_name, op, arg); 910 911 switch (op) { 912 case DDI_HPOP_CN_CREATE_PORT: 913 { 914 /* create an empty port */ 915 return (pcie_hp_register_port(NULL, dip, cn_name)); 916 } 917 case DDI_HPOP_CN_CHANGE_STATE: 918 { 919 ddi_hp_cn_state_t curr_state; 920 ddi_hp_cn_state_t target_state = *(ddi_hp_cn_state_t *)arg; 921 pcie_hp_port_state_t state_arg; 922 923 if (target_state < DDI_HP_CN_STATE_PORT_EMPTY) { 924 /* this is for physical slot state change */ 925 break; 926 } 927 PCIE_DBG("pcie_hp_common_ops: change port state" 928 " dip=%p cn_name=%s" 929 " op=%x arg=%p\n", (void *)dip, cn_name, op, arg); 930 931 state_arg.rv = DDI_FAILURE; 932 state_arg.cn_name = cn_name; 933 ndi_hp_walk_cn(dip, pcie_hp_get_port_state, &state_arg); 934 if (state_arg.rv != DDI_SUCCESS) { 935 /* can not find the port */ 936 return (DDI_EINVAL); 937 } 938 curr_state = state_arg.cn_state; 939 /* 940 * Check if this is for changing port's state: change to/from 941 * PORT_EMPTY/PRESENT states. 942 */ 943 if (curr_state < target_state) { 944 /* Upgrade state */ 945 switch (curr_state) { 946 case DDI_HP_CN_STATE_PORT_EMPTY: 947 if (target_state == 948 DDI_HP_CN_STATE_PORT_PRESENT) { 949 int dev_num, func_num; 950 951 ret = pcie_hp_get_df_from_port_name( 952 cn_name, &dev_num, &func_num); 953 if (ret != DDI_SUCCESS) 954 goto port_state_done; 955 956 ret = pcie_hp_check_hardware_existence( 957 dip, dev_num, func_num); 958 } else if (target_state == 959 DDI_HP_CN_STATE_OFFLINE) { 960 ret = pcie_read_only_probe(dip, 961 cn_name, (dev_info_t **)result); 962 } else 963 ret = DDI_EINVAL; 964 965 goto port_state_done; 966 case DDI_HP_CN_STATE_PORT_PRESENT: 967 if (target_state == 968 DDI_HP_CN_STATE_OFFLINE) 969 ret = pcie_read_only_probe(dip, 970 cn_name, (dev_info_t **)result); 971 else 972 ret = DDI_EINVAL; 973 974 goto port_state_done; 975 default: 976 ASSERT("unexpected state"); 977 } 978 } else { 979 /* Downgrade state */ 980 switch (curr_state) { 981 case DDI_HP_CN_STATE_PORT_PRESENT: 982 { 983 int dev_num, func_num; 984 985 ret = pcie_hp_get_df_from_port_name(cn_name, 986 &dev_num, &func_num); 987 if (ret != DDI_SUCCESS) 988 goto port_state_done; 989 990 ret = pcie_hp_check_hardware_existence(dip, 991 dev_num, func_num); 992 993 goto port_state_done; 994 } 995 case DDI_HP_CN_STATE_OFFLINE: 996 ret = pcie_read_only_unprobe(dip, cn_name); 997 998 goto port_state_done; 999 default: 1000 ASSERT("unexpected state"); 1001 } 1002 } 1003 port_state_done: 1004 *(ddi_hp_cn_state_t *)result = curr_state; 1005 return (ret); 1006 } 1007 default: 1008 break; 1009 } 1010 1011 if (PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p)) { 1012 /* PCIe hotplug */ 1013 ret = pciehpc_hp_ops(dip, cn_name, op, arg, result); 1014 } else if (PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p)) { 1015 /* PCI SHPC hotplug */ 1016 ret = pcishpc_hp_ops(dip, cn_name, op, arg, result); 1017 } else { 1018 cmn_err(CE_WARN, "pcie_hp_common_ops: op is not supported." 1019 " dip=%p cn_name=%s" 1020 " op=%x arg=%p\n", (void *)dip, cn_name, op, arg); 1021 ret = DDI_ENOTSUP; 1022 } 1023 1024 #if defined(__i386) || defined(__amd64) 1025 /* 1026 * like in attach, since hotplugging can change error registers, 1027 * we need to ensure that the proper bits are set on this port 1028 * after a configure operation 1029 */ 1030 if ((ret == DDI_SUCCESS) && (op == DDI_HPOP_CN_CHANGE_STATE) && 1031 (*(ddi_hp_cn_state_t *)arg == DDI_HP_CN_STATE_ENABLED)) 1032 pcieb_intel_error_workaround(dip); 1033 #endif 1034 1035 return (ret); 1036 } 1037 1038 /* 1039 * pcie_hp_match_dev_func: 1040 * Match dip's PCI device number and function number with input ones. 1041 */ 1042 static int 1043 pcie_hp_match_dev_func(dev_info_t *dip, void *hdl) 1044 { 1045 struct pcie_hp_find_ctrl *ctrl = (struct pcie_hp_find_ctrl *)hdl; 1046 pci_regspec_t *pci_rp; 1047 int length; 1048 int pci_dev, pci_func; 1049 1050 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1051 "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) { 1052 ctrl->dip = NULL; 1053 return (DDI_WALK_TERMINATE); 1054 } 1055 1056 /* get the PCI device address info */ 1057 pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi); 1058 pci_func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi); 1059 1060 /* 1061 * free the memory allocated by ddi_prop_lookup_int_array 1062 */ 1063 ddi_prop_free(pci_rp); 1064 1065 if ((pci_dev == ctrl->device) && (pci_func == ctrl->function)) { 1066 /* found the match for the specified device address */ 1067 ctrl->dip = dip; 1068 return (DDI_WALK_TERMINATE); 1069 } 1070 1071 /* 1072 * continue the walk to the next sibling to look for a match. 1073 */ 1074 return (DDI_WALK_PRUNECHILD); 1075 } 1076 1077 /* 1078 * pcie_hp_match_dev: 1079 * Match the dip's pci device number with the input dev_num 1080 */ 1081 static boolean_t 1082 pcie_hp_match_dev(dev_info_t *dip, int dev_num) 1083 { 1084 pci_regspec_t *pci_rp; 1085 int length; 1086 int pci_dev; 1087 1088 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1089 "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) { 1090 return (B_FALSE); 1091 } 1092 1093 /* get the PCI device address info */ 1094 pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi); 1095 1096 /* 1097 * free the memory allocated by ddi_prop_lookup_int_array 1098 */ 1099 ddi_prop_free(pci_rp); 1100 1101 if (pci_dev == dev_num) { 1102 /* found the match for the specified device address */ 1103 return (B_TRUE); 1104 } 1105 1106 return (B_FALSE); 1107 } 1108 1109 /* 1110 * Callback function to match with device number in order to list 1111 * occupants under a specific slot 1112 */ 1113 static int 1114 pcie_hp_list_occupants(dev_info_t *dip, void *arg) 1115 { 1116 pcie_hp_cn_cfg_t *cn_cfg_p = (pcie_hp_cn_cfg_t *)arg; 1117 pcie_hp_occupant_info_t *occupant = 1118 (pcie_hp_occupant_info_t *)cn_cfg_p->cn_private; 1119 pcie_hp_slot_t *slot_p = 1120 (pcie_hp_slot_t *)cn_cfg_p->slotp; 1121 int pci_dev; 1122 pci_regspec_t *pci_rp; 1123 int length; 1124 major_t major; 1125 1126 /* 1127 * Get the PCI device number information from the devinfo 1128 * node. Since the node may not have the address field 1129 * setup (this is done in the DDI_INITCHILD of the parent) 1130 * we look up the 'reg' property to decode that information. 1131 */ 1132 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, 1133 DDI_PROP_DONTPASS, "reg", (int **)&pci_rp, 1134 (uint_t *)&length) != DDI_PROP_SUCCESS) { 1135 cn_cfg_p->rv = DDI_FAILURE; 1136 cn_cfg_p->dip = dip; 1137 return (DDI_WALK_TERMINATE); 1138 } 1139 1140 /* get the pci device id information */ 1141 pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi); 1142 1143 /* 1144 * free the memory allocated by ddi_prop_lookup_int_array 1145 */ 1146 ddi_prop_free(pci_rp); 1147 1148 /* 1149 * Match the node for the device number of the slot. 1150 */ 1151 if (pci_dev == slot_p->hs_device_num) { 1152 1153 major = ddi_driver_major(dip); 1154 1155 /* 1156 * If the node is not yet attached, then don't list it 1157 * as an occupant. This is valid, since nothing can be 1158 * consuming it until it is attached, and cfgadm will 1159 * ask for the property explicitly which will cause it 1160 * to be re-freshed right before checking with rcm. 1161 */ 1162 if ((major == DDI_MAJOR_T_NONE) || !i_ddi_devi_attached(dip)) 1163 return (DDI_WALK_PRUNECHILD); 1164 1165 /* 1166 * If we have used all our occupants then print mesage 1167 * and terminate walk. 1168 */ 1169 if (occupant->i >= PCIE_HP_MAX_OCCUPANTS) { 1170 cmn_err(CE_WARN, 1171 "pcie (%s%d): unable to list all occupants", 1172 ddi_driver_name(ddi_get_parent(dip)), 1173 ddi_get_instance(ddi_get_parent(dip))); 1174 return (DDI_WALK_TERMINATE); 1175 } 1176 1177 /* 1178 * No need to hold the dip as ddi_walk_devs 1179 * has already arranged that for us. 1180 */ 1181 occupant->id[occupant->i] = 1182 kmem_alloc(sizeof (char[MAXPATHLEN]), KM_SLEEP); 1183 (void) ddi_pathname(dip, (char *)occupant->id[occupant->i]); 1184 occupant->i++; 1185 } 1186 1187 /* 1188 * continue the walk to the next sibling to look for a match 1189 * or to find other nodes if this card is a multi-function card. 1190 */ 1191 return (DDI_WALK_PRUNECHILD); 1192 } 1193 1194 /* 1195 * Generate the System Event for ESC_DR_REQ. 1196 * One of the consumers is pcidr, it calls to libcfgadm to perform a 1197 * configure or unconfigure operation to the AP. 1198 */ 1199 void 1200 pcie_hp_gen_sysevent_req(char *slot_name, int hint, 1201 dev_info_t *self, int kmflag) 1202 { 1203 sysevent_id_t eid; 1204 nvlist_t *ev_attr_list = NULL; 1205 char cn_path[MAXPATHLEN]; 1206 char *ap_id; 1207 int err, ap_id_len; 1208 1209 /* 1210 * Minor device name (AP) will be bus path 1211 * concatenated with slot name 1212 */ 1213 (void) strcpy(cn_path, "/devices"); 1214 (void) ddi_pathname(self, cn_path + strlen("/devices")); 1215 1216 ap_id_len = strlen(cn_path) + strlen(":") + 1217 strlen(slot_name) + 1; 1218 ap_id = kmem_zalloc(ap_id_len, kmflag); 1219 if (ap_id == NULL) { 1220 cmn_err(CE_WARN, 1221 "%s%d: Failed to allocate memory for AP ID: %s:%s", 1222 ddi_driver_name(self), ddi_get_instance(self), 1223 cn_path, slot_name); 1224 1225 return; 1226 } 1227 1228 (void) strcpy(ap_id, cn_path); 1229 (void) strcat(ap_id, ":"); 1230 (void) strcat(ap_id, slot_name); 1231 1232 err = nvlist_alloc(&ev_attr_list, NV_UNIQUE_NAME_TYPE, kmflag); 1233 if (err != 0) { 1234 cmn_err(CE_WARN, 1235 "%s%d: Failed to allocate memory " 1236 "for event attributes%s", ddi_driver_name(self), 1237 ddi_get_instance(self), ESC_DR_REQ); 1238 1239 kmem_free(ap_id, ap_id_len); 1240 return; 1241 } 1242 1243 switch (hint) { 1244 1245 case SE_INVESTIGATE_RES: /* fall through */ 1246 case SE_INCOMING_RES: /* fall through */ 1247 case SE_OUTGOING_RES: /* fall through */ 1248 1249 err = nvlist_add_string(ev_attr_list, DR_REQ_TYPE, 1250 SE_REQ2STR(hint)); 1251 1252 if (err != 0) { 1253 cmn_err(CE_WARN, 1254 "%s%d: Failed to add attr [%s] " 1255 "for %s event", ddi_driver_name(self), 1256 ddi_get_instance(self), 1257 DR_REQ_TYPE, ESC_DR_REQ); 1258 1259 goto done; 1260 } 1261 break; 1262 1263 default: 1264 cmn_err(CE_WARN, "%s%d: Unknown hint on sysevent", 1265 ddi_driver_name(self), ddi_get_instance(self)); 1266 1267 goto done; 1268 } 1269 1270 /* 1271 * Add attachment point as attribute (common attribute) 1272 */ 1273 1274 err = nvlist_add_string(ev_attr_list, DR_AP_ID, ap_id); 1275 1276 if (err != 0) { 1277 cmn_err(CE_WARN, "%s%d: Failed to add attr [%s] for %s event", 1278 ddi_driver_name(self), ddi_get_instance(self), 1279 DR_AP_ID, EC_DR); 1280 1281 goto done; 1282 } 1283 1284 1285 /* 1286 * Log this event with sysevent framework. 1287 */ 1288 1289 err = ddi_log_sysevent(self, DDI_VENDOR_SUNW, EC_DR, 1290 ESC_DR_REQ, ev_attr_list, &eid, 1291 ((kmflag == KM_SLEEP) ? DDI_SLEEP : DDI_NOSLEEP)); 1292 if (err != 0) { 1293 cmn_err(CE_WARN, "%s%d: Failed to log %s event", 1294 ddi_driver_name(self), ddi_get_instance(self), EC_DR); 1295 } 1296 1297 done: 1298 nvlist_free(ev_attr_list); 1299 kmem_free(ap_id, ap_id_len); 1300 } 1301