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