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 * ACPI interface related functions used in PCIEHPC driver module. 29 * 30 * NOTE: This file is compiled and delivered through misc/pcie module. 31 */ 32 33 #include <sys/note.h> 34 #include <sys/conf.h> 35 #include <sys/kmem.h> 36 #include <sys/debug.h> 37 #include <sys/vtrace.h> 38 #include <sys/varargs.h> 39 #include <sys/ddi_impldefs.h> 40 #include <sys/pci.h> 41 #include <sys/ddi.h> 42 #include <sys/sunddi.h> 43 #include <sys/sunndi.h> 44 #include <sys/pci_impl.h> 45 #include <sys/pcie_acpi.h> 46 #include <sys/hotplug/pci/pcie_hp.h> 47 #include <sys/hotplug/pci/pciehpc_acpi.h> 48 49 /* local static functions */ 50 static int pciehpc_acpi_hpc_init(pcie_hp_ctrl_t *ctrl_p); 51 static int pciehpc_acpi_hpc_uninit(pcie_hp_ctrl_t *ctrl_p); 52 static int pciehpc_acpi_slotinfo_init(pcie_hp_ctrl_t *ctrl_p); 53 static int pciehpc_acpi_slotinfo_uninit(pcie_hp_ctrl_t *ctrl_p); 54 static int pciehpc_acpi_enable_intr(pcie_hp_ctrl_t *ctrl_p); 55 static int pciehpc_acpi_disable_intr(pcie_hp_ctrl_t *ctrl_p); 56 static int pciehpc_acpi_slot_poweron(pcie_hp_slot_t *slot_p, 57 ddi_hp_cn_state_t *result); 58 static int pciehpc_acpi_slot_poweroff(pcie_hp_slot_t *slot_p, 59 ddi_hp_cn_state_t *result); 60 static void pciehpc_acpi_setup_ops(pcie_hp_ctrl_t *ctrl_p); 61 62 static ACPI_STATUS pciehpc_acpi_install_event_handler(pcie_hp_ctrl_t *ctrl_p); 63 static void pciehpc_acpi_uninstall_event_handler(pcie_hp_ctrl_t *ctrl_p); 64 static ACPI_STATUS pciehpc_acpi_power_on_slot(pcie_hp_ctrl_t *ctrl_p); 65 static ACPI_STATUS pciehpc_acpi_power_off_slot(pcie_hp_ctrl_t *ctrl_p); 66 static void pciehpc_acpi_notify_handler(ACPI_HANDLE device, uint32_t val, 67 void *context); 68 static ACPI_STATUS pciehpc_acpi_get_dev_state(ACPI_HANDLE obj, int *statusp); 69 70 /* 71 * Update ops vector with platform specific (ACPI, CK8-04,...) functions. 72 */ 73 void 74 pciehpc_update_ops(pcie_hp_ctrl_t *ctrl_p) 75 { 76 boolean_t hp_native_mode = B_FALSE; 77 uint32_t osc_flags = OSC_CONTROL_PCIE_NAT_HP; 78 79 /* 80 * Call _OSC method to determine if hotplug mode is native or ACPI. 81 * If _OSC method succeeds hp_native_mode below will be set according to 82 * if native hotplug control was granted or not by BIOS. 83 * 84 * If _OSC method fails for any reason or if native hotplug control was 85 * not granted assume it's ACPI mode and update platform specific 86 * (ACPI, CK8-04,...) impl. ops 87 */ 88 89 if (pcie_acpi_osc(ctrl_p->hc_dip, &osc_flags) == DDI_SUCCESS) { 90 hp_native_mode = (osc_flags & OSC_CONTROL_PCIE_NAT_HP) ? 91 B_TRUE : B_FALSE; 92 } 93 94 if (!hp_native_mode) { 95 pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip); 96 97 /* update ops vector for ACPI mode */ 98 pciehpc_acpi_setup_ops(ctrl_p); 99 bus_p->bus_hp_sup_modes |= PCIE_ACPI_HP_MODE; 100 bus_p->bus_hp_curr_mode = PCIE_ACPI_HP_MODE; 101 } 102 } 103 104 void 105 pciehpc_acpi_setup_ops(pcie_hp_ctrl_t *ctrl_p) 106 { 107 ctrl_p->hc_ops.init_hpc_hw = pciehpc_acpi_hpc_init; 108 ctrl_p->hc_ops.uninit_hpc_hw = pciehpc_acpi_hpc_uninit; 109 ctrl_p->hc_ops.init_hpc_slotinfo = pciehpc_acpi_slotinfo_init; 110 ctrl_p->hc_ops.uninit_hpc_slotinfo = pciehpc_acpi_slotinfo_uninit; 111 ctrl_p->hc_ops.poweron_hpc_slot = pciehpc_acpi_slot_poweron; 112 ctrl_p->hc_ops.poweroff_hpc_slot = pciehpc_acpi_slot_poweroff; 113 ctrl_p->hc_ops.disable_hpc_intr = pciehpc_acpi_disable_intr; 114 ctrl_p->hc_ops.enable_hpc_intr = pciehpc_acpi_enable_intr; 115 } 116 117 /* 118 * Intialize hot plug control for ACPI mode. 119 */ 120 static int 121 pciehpc_acpi_hpc_init(pcie_hp_ctrl_t *ctrl_p) 122 { 123 ACPI_HANDLE pcibus_obj; 124 int status = AE_ERROR; 125 ACPI_HANDLE slot_dev_obj; 126 ACPI_HANDLE hdl; 127 pciehpc_acpi_t *acpi_p; 128 uint16_t bus_methods = 0; 129 uint16_t slot_methods = 0; 130 131 /* get the ACPI object for the bus node */ 132 status = acpica_get_handle(ctrl_p->hc_dip, &pcibus_obj); 133 if (status != AE_OK) 134 return (DDI_FAILURE); 135 136 /* get the ACPI object handle for the child node */ 137 status = AcpiGetNextObject(ACPI_TYPE_DEVICE, pcibus_obj, 138 NULL, &slot_dev_obj); 139 if (status != AE_OK) 140 return (DDI_FAILURE); 141 142 /* 143 * gather the info about the ACPI methods present on the bus node 144 * and the child nodes. 145 */ 146 if (AcpiGetHandle(pcibus_obj, "_OSC", &hdl) == AE_OK) 147 bus_methods |= PCIEHPC_ACPI_OSC_PRESENT; 148 if (AcpiGetHandle(pcibus_obj, "_OSHP", &hdl) == AE_OK) 149 bus_methods |= PCIEHPC_ACPI_OSHP_PRESENT; 150 if (AcpiGetHandle(pcibus_obj, "_HPX", &hdl) == AE_OK) 151 bus_methods |= PCIEHPC_ACPI_HPX_PRESENT; 152 if (AcpiGetHandle(pcibus_obj, "_HPP", &hdl) == AE_OK) 153 bus_methods |= PCIEHPC_ACPI_HPP_PRESENT; 154 if (AcpiGetHandle(pcibus_obj, "_DSM", &hdl) == AE_OK) 155 bus_methods |= PCIEHPC_ACPI_DSM_PRESENT; 156 if (AcpiGetHandle(slot_dev_obj, "_SUN", &hdl) == AE_OK) 157 slot_methods |= PCIEHPC_ACPI_SUN_PRESENT; 158 if (AcpiGetHandle(slot_dev_obj, "_PS0", &hdl) == AE_OK) 159 slot_methods |= PCIEHPC_ACPI_PS0_PRESENT; 160 if (AcpiGetHandle(slot_dev_obj, "_EJ0", &hdl) == AE_OK) 161 slot_methods |= PCIEHPC_ACPI_EJ0_PRESENT; 162 if (AcpiGetHandle(slot_dev_obj, "_STA", &hdl) == AE_OK) 163 slot_methods |= PCIEHPC_ACPI_STA_PRESENT; 164 165 /* save ACPI object handles, etc. */ 166 acpi_p = kmem_zalloc(sizeof (pciehpc_acpi_t), KM_SLEEP); 167 acpi_p->bus_obj = pcibus_obj; 168 acpi_p->slot_dev_obj = slot_dev_obj; 169 acpi_p->bus_methods = bus_methods; 170 acpi_p->slot_methods = slot_methods; 171 ctrl_p->hc_misc_data = acpi_p; 172 173 return (DDI_SUCCESS); 174 } 175 176 /* 177 * Uninitialize HPC. 178 */ 179 static int 180 pciehpc_acpi_hpc_uninit(pcie_hp_ctrl_t *ctrl_p) 181 { 182 /* free up buffer used for misc_data */ 183 if (ctrl_p->hc_misc_data) { 184 kmem_free(ctrl_p->hc_misc_data, sizeof (pciehpc_acpi_t)); 185 ctrl_p->hc_misc_data = NULL; 186 } 187 188 return (DDI_SUCCESS); 189 } 190 191 /* 192 * Enable interrupts. For ACPI hot plug this is a NOP. 193 * Just return DDI_SUCCESS. 194 */ 195 /*ARGSUSED*/ 196 static int 197 pciehpc_acpi_enable_intr(pcie_hp_ctrl_t *ctrl_p) 198 { 199 return (DDI_SUCCESS); 200 } 201 202 /* 203 * Disable interrupts. For ACPI hot plug this is a NOP. 204 * Just return DDI_SUCCESS. 205 */ 206 /*ARGSUSED*/ 207 static int 208 pciehpc_acpi_disable_intr(pcie_hp_ctrl_t *ctrl_p) 209 { 210 return (DDI_SUCCESS); 211 } 212 213 /* 214 * This function is similar to pciehpc_slotinfo_init() with some 215 * changes: 216 * - no need for kernel thread to handle ATTN button events 217 * - function ops for connect/disconnect are different 218 * 219 * ASSUMPTION: No conflict in doing reads to HP registers directly. 220 * Otherwise, there are no ACPI interfaces to do LED control or to get 221 * the hot plug capabilities (ATTN button, MRL, etc.). 222 */ 223 static int 224 pciehpc_acpi_slotinfo_init(pcie_hp_ctrl_t *ctrl_p) 225 { 226 uint32_t slot_capabilities; 227 pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0]; 228 pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip); 229 230 mutex_enter(&ctrl_p->hc_mutex); 231 /* 232 * setup DDI HP framework slot information structure 233 */ 234 slot_p->hs_device_num = 0; 235 slot_p->hs_info.cn_type = DDI_HP_CN_TYPE_PCIE; 236 slot_p->hs_info.cn_type_str = PCIE_ACPI_HP_TYPE; 237 slot_p->hs_info.cn_child = NULL; 238 239 slot_p->hs_minor = 240 PCI_MINOR_NUM(ddi_get_instance(ctrl_p->hc_dip), 241 slot_p->hs_device_num); 242 243 /* read Slot Capabilities Register */ 244 slot_capabilities = pciehpc_reg_get32(ctrl_p, 245 bus_p->bus_pcie_off + PCIE_SLOTCAP); 246 247 /* setup slot number/name */ 248 pciehpc_set_slot_name(ctrl_p); 249 250 /* check if Attn Button present */ 251 ctrl_p->hc_has_attn = (slot_capabilities & 252 PCIE_SLOTCAP_ATTN_BUTTON) ? B_TRUE : B_FALSE; 253 254 /* check if Manual Retention Latch sensor present */ 255 ctrl_p->hc_has_mrl = (slot_capabilities & PCIE_SLOTCAP_MRL_SENSOR) ? 256 B_TRUE : B_FALSE; 257 258 /* 259 * PCI-E (draft) version 1.1 defines EMI Lock Present bit 260 * in Slot Capabilities register. Check for it. 261 */ 262 ctrl_p->hc_has_emi_lock = (slot_capabilities & 263 PCIE_SLOTCAP_EMI_LOCK_PRESENT) ? B_TRUE : B_FALSE; 264 265 /* get current slot state from the hw */ 266 pciehpc_get_slot_state(slot_p); 267 if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_ENABLED) 268 slot_p->hs_condition = AP_COND_OK; 269 270 mutex_exit(&ctrl_p->hc_mutex); 271 272 /* setup Notify() handler for hot plug events from ACPI BIOS */ 273 if (pciehpc_acpi_install_event_handler(ctrl_p) != AE_OK) 274 return (DDI_FAILURE); 275 276 PCIE_DBG("ACPI hot plug is enabled for slot #%d\n", 277 slot_p->hs_phy_slot_num); 278 279 return (DDI_SUCCESS); 280 } 281 282 /* 283 * This function is similar to pciehcp_slotinfo_uninit() but has ACPI 284 * specific cleanup. 285 */ 286 static int 287 pciehpc_acpi_slotinfo_uninit(pcie_hp_ctrl_t *ctrl_p) 288 { 289 pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0]; 290 291 /* uninstall Notify() event handler */ 292 pciehpc_acpi_uninstall_event_handler(ctrl_p); 293 if (slot_p->hs_info.cn_name) 294 kmem_free(slot_p->hs_info.cn_name, 295 strlen(slot_p->hs_info.cn_name) + 1); 296 297 return (DDI_SUCCESS); 298 } 299 300 /* 301 * This function is same as pciehpc_slot_poweron() except that it 302 * uses ACPI method PS0 to enable power to the slot. If no PS0 method 303 * is present then it returns DDI_FAILURE. 304 */ 305 /*ARGSUSED*/ 306 static int 307 pciehpc_acpi_slot_poweron(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result) 308 { 309 pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl; 310 pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip); 311 uint16_t status, control; 312 313 ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex)); 314 315 /* get the current state of the slot */ 316 pciehpc_get_slot_state(slot_p); 317 318 /* check if the slot is already in the 'ENABLED' state */ 319 if (slot_p->hs_info.cn_state == DDI_HP_CN_STATE_ENABLED) { 320 /* slot is already in the 'connected' state */ 321 PCIE_DBG("slot %d already connected\n", 322 slot_p->hs_phy_slot_num); 323 324 *result = slot_p->hs_info.cn_state; 325 return (DDI_SUCCESS); 326 } 327 328 /* read the Slot Status Register */ 329 status = pciehpc_reg_get16(ctrl_p, 330 bus_p->bus_pcie_off + PCIE_SLOTSTS); 331 332 /* make sure the MRL switch is closed if present */ 333 if ((ctrl_p->hc_has_mrl) && (status & PCIE_SLOTSTS_MRL_SENSOR_OPEN)) { 334 /* MRL switch is open */ 335 cmn_err(CE_WARN, "MRL switch is open on slot %d", 336 slot_p->hs_phy_slot_num); 337 goto cleanup; 338 } 339 340 /* make sure the slot has a device present */ 341 if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) { 342 /* slot is empty */ 343 PCIE_DBG("slot %d is empty\n", slot_p->hs_phy_slot_num); 344 goto cleanup; 345 } 346 347 /* get the current state of Slot Control Register */ 348 control = pciehpc_reg_get16(ctrl_p, 349 bus_p->bus_pcie_off + PCIE_SLOTCTL); 350 351 /* check if the slot's power state is ON */ 352 if (!(control & PCIE_SLOTCTL_PWR_CONTROL)) { 353 /* slot is already powered up */ 354 PCIE_DBG("slot %d already connected\n", 355 slot_p->hs_phy_slot_num); 356 357 *result = slot_p->hs_info.cn_state; 358 return (DDI_SUCCESS); 359 } 360 361 /* turn on power to the slot using ACPI method (PS0) */ 362 if (pciehpc_acpi_power_on_slot(ctrl_p) != AE_OK) 363 goto cleanup; 364 365 *result = slot_p->hs_info.cn_state = DDI_HP_CN_STATE_POWERED; 366 return (DDI_SUCCESS); 367 368 cleanup: 369 return (DDI_FAILURE); 370 } 371 372 /* 373 * This function is same as pciehpc_slot_poweroff() except that it 374 * uses ACPI method EJ0 to disable power to the slot. If no EJ0 method 375 * is present then it returns DDI_FAILURE. 376 */ 377 /*ARGSUSED*/ 378 static int 379 pciehpc_acpi_slot_poweroff(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result) 380 { 381 pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl; 382 pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip); 383 uint16_t status; 384 385 ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex)); 386 387 /* get the current state of the slot */ 388 pciehpc_get_slot_state(slot_p); 389 390 /* check if the slot is already in the state less than 'powered' */ 391 if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED) { 392 /* slot is in the 'disconnected' state */ 393 PCIE_DBG("slot %d already disconnected\n", 394 slot_p->hs_phy_slot_num); 395 ASSERT(slot_p->hs_power_led_state == PCIE_HP_LED_OFF); 396 397 *result = slot_p->hs_info.cn_state; 398 return (DDI_SUCCESS); 399 } 400 401 /* read the Slot Status Register */ 402 status = pciehpc_reg_get16(ctrl_p, 403 bus_p->bus_pcie_off + PCIE_SLOTSTS); 404 405 /* make sure the slot has a device present */ 406 if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) { 407 /* slot is empty */ 408 PCIE_DBG("slot %d is empty", slot_p->hs_phy_slot_num); 409 goto cleanup; 410 } 411 412 /* turn off power to the slot using ACPI method (EJ0) */ 413 if (pciehpc_acpi_power_off_slot(ctrl_p) != AE_OK) 414 goto cleanup; 415 416 /* get the current state of the slot */ 417 pciehpc_get_slot_state(slot_p); 418 419 *result = slot_p->hs_info.cn_state; 420 421 return (DDI_SUCCESS); 422 423 cleanup: 424 return (DDI_FAILURE); 425 } 426 427 /* 428 * Install event handler for the hot plug events on the bus node as well 429 * as device function (dev=0,func=0). 430 */ 431 static ACPI_STATUS 432 pciehpc_acpi_install_event_handler(pcie_hp_ctrl_t *ctrl_p) 433 { 434 pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0]; 435 int status = AE_OK; 436 pciehpc_acpi_t *acpi_p; 437 438 PCIE_DBG("install event handler for slot %d\n", 439 slot_p->hs_phy_slot_num); 440 acpi_p = ctrl_p->hc_misc_data; 441 if (acpi_p->slot_dev_obj == NULL) 442 return (AE_NOT_FOUND); 443 444 /* 445 * Install event hanlder for events on the bus object. 446 * (Note: Insert event (hot-insert) is delivered on this object) 447 */ 448 status = AcpiInstallNotifyHandler(acpi_p->slot_dev_obj, 449 ACPI_SYSTEM_NOTIFY, pciehpc_acpi_notify_handler, (void *)ctrl_p); 450 if (status != AE_OK) 451 goto cleanup; 452 453 /* 454 * Install event hanlder for events on the device function object. 455 * (Note: Eject device event (hot-remove) is delivered on this object) 456 * 457 * NOTE: Here the assumption is that Notify events are delivered 458 * on all of the 8 possible device functions so, subscribing to 459 * one of them is sufficient. 460 */ 461 status = AcpiInstallNotifyHandler(acpi_p->bus_obj, 462 ACPI_SYSTEM_NOTIFY, pciehpc_acpi_notify_handler, (void *)ctrl_p); 463 return (status); 464 465 cleanup: 466 (void) AcpiRemoveNotifyHandler(acpi_p->slot_dev_obj, 467 ACPI_SYSTEM_NOTIFY, pciehpc_acpi_notify_handler); 468 return (status); 469 } 470 471 /*ARGSUSED*/ 472 static void 473 pciehpc_acpi_notify_handler(ACPI_HANDLE device, uint32_t val, void *context) 474 { 475 pcie_hp_ctrl_t *ctrl_p = context; 476 pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0]; 477 pciehpc_acpi_t *acpi_p; 478 ddi_hp_cn_state_t curr_state; 479 int dev_state = 0; 480 481 PCIE_DBG("received Notify(%d) event on slot #%d\n", 482 val, slot_p->hs_phy_slot_num); 483 484 mutex_enter(&ctrl_p->hc_mutex); 485 486 /* 487 * get the state of the device (from _STA method) 488 */ 489 acpi_p = ctrl_p->hc_misc_data; 490 if (pciehpc_acpi_get_dev_state(acpi_p->slot_dev_obj, 491 &dev_state) != AE_OK) { 492 cmn_err(CE_WARN, "failed to get device status on slot %d", 493 slot_p->hs_phy_slot_num); 494 } 495 PCIE_DBG("(1)device state on slot #%d: 0x%x\n", 496 slot_p->hs_phy_slot_num, dev_state); 497 498 curr_state = slot_p->hs_info.cn_state; 499 pciehpc_get_slot_state(slot_p); 500 501 switch (val) { 502 case 0: /* (re)enumerate the device */ 503 case 3: /* Request Eject */ 504 { 505 ddi_hp_cn_state_t target_state; 506 507 /* 508 * Ignore the event if ATTN button is not present (ACPI BIOS 509 * problem). 510 * 511 * NOTE: This situation has been observed on some platforms 512 * where the ACPI BIOS is generating the event for some other 513 * (non hot-plug) operations (bug). 514 */ 515 if (ctrl_p->hc_has_attn == B_FALSE) { 516 PCIE_DBG("Ignore the unexpected event " 517 "on slot #%d (state 0x%x)", 518 slot_p->hs_phy_slot_num, dev_state); 519 break; 520 } 521 522 /* send the event to DDI Hotplug framework */ 523 if (curr_state < DDI_HP_CN_STATE_POWERED) { 524 /* Insertion. Upgrade state to ENABLED */ 525 target_state = DDI_HP_CN_STATE_ENABLED; 526 527 /* 528 * When pressing ATTN button to enable a card, the slot 529 * could be powered. Keep the slot state on PWOERED 530 * other than ENABLED. 531 */ 532 if (slot_p->hs_info.cn_state == DDI_HP_CN_STATE_ENABLED) 533 slot_p->hs_info.cn_state = 534 DDI_HP_CN_STATE_POWERED; 535 } else { 536 /* Want to remove; Power off Connection */ 537 target_state = DDI_HP_CN_STATE_EMPTY; 538 } 539 540 (void) ndi_hp_state_change_req(slot_p->hs_ctrl->hc_dip, 541 slot_p->hs_info.cn_name, 542 target_state, DDI_HP_REQ_ASYNC); 543 544 break; 545 } 546 default: 547 cmn_err(CE_NOTE, "Unknown Notify() event %d on slot #%d\n", 548 val, slot_p->hs_phy_slot_num); 549 break; 550 } 551 mutex_exit(&ctrl_p->hc_mutex); 552 } 553 554 static void 555 pciehpc_acpi_uninstall_event_handler(pcie_hp_ctrl_t *ctrl_p) 556 { 557 pciehpc_acpi_t *acpi_p = ctrl_p->hc_misc_data; 558 pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0]; 559 560 PCIE_DBG("Uninstall event handler for slot #%d\n", 561 slot_p->hs_phy_slot_num); 562 (void) AcpiRemoveNotifyHandler(acpi_p->slot_dev_obj, 563 ACPI_SYSTEM_NOTIFY, pciehpc_acpi_notify_handler); 564 (void) AcpiRemoveNotifyHandler(acpi_p->bus_obj, 565 ACPI_SYSTEM_NOTIFY, pciehpc_acpi_notify_handler); 566 } 567 568 /* 569 * Run _PS0 method to turn on power to the slot. 570 */ 571 static ACPI_STATUS 572 pciehpc_acpi_power_on_slot(pcie_hp_ctrl_t *ctrl_p) 573 { 574 int status = AE_OK; 575 pciehpc_acpi_t *acpi_p = ctrl_p->hc_misc_data; 576 int dev_state = 0; 577 pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0]; 578 579 PCIE_DBG("turn ON power to the slot #%d\n", slot_p->hs_phy_slot_num); 580 581 status = AcpiEvaluateObject(acpi_p->slot_dev_obj, "_PS0", NULL, NULL); 582 583 /* get the state of the device (from _STA method) */ 584 if (status == AE_OK) { 585 if (pciehpc_acpi_get_dev_state(acpi_p->slot_dev_obj, 586 &dev_state) != AE_OK) 587 cmn_err(CE_WARN, "failed to get device status " 588 "on slot #%d", slot_p->hs_phy_slot_num); 589 } 590 591 PCIE_DBG("(3)device state on slot #%d: 0x%x\n", 592 slot_p->hs_phy_slot_num, dev_state); 593 594 pciehpc_get_slot_state(slot_p); 595 596 if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED) { 597 cmn_err(CE_WARN, "failed to power on the slot #%d" 598 "(dev_state 0x%x, ACPI_STATUS 0x%x)", 599 slot_p->hs_phy_slot_num, dev_state, status); 600 return (AE_ERROR); 601 } 602 603 return (status); 604 } 605 606 /* 607 * Run _EJ0 method to turn off power to the slot. 608 */ 609 static ACPI_STATUS 610 pciehpc_acpi_power_off_slot(pcie_hp_ctrl_t *ctrl_p) 611 { 612 int status = AE_OK; 613 pciehpc_acpi_t *acpi_p = ctrl_p->hc_misc_data; 614 int dev_state = 0; 615 pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0]; 616 617 PCIE_DBG("turn OFF power to the slot #%d\n", slot_p->hs_phy_slot_num); 618 619 status = AcpiEvaluateObject(acpi_p->slot_dev_obj, "_EJ0", NULL, NULL); 620 621 /* get the state of the device (from _STA method) */ 622 if (status == AE_OK) { 623 if (pciehpc_acpi_get_dev_state(acpi_p->slot_dev_obj, 624 &dev_state) != AE_OK) 625 cmn_err(CE_WARN, "failed to get device status " 626 "on slot #%d", slot_p->hs_phy_slot_num); 627 } 628 629 PCIE_DBG("(2)device state on slot #%d: 0x%x\n", 630 slot_p->hs_phy_slot_num, dev_state); 631 632 pciehpc_get_slot_state(slot_p); 633 634 if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED) { 635 cmn_err(CE_WARN, "failed to power OFF the slot #%d" 636 "(dev_state 0x%x, ACPI_STATUS 0x%x)", 637 slot_p->hs_phy_slot_num, dev_state, status); 638 return (AE_ERROR); 639 } 640 641 return (status); 642 } 643 644 /* 645 * Get the status info (as returned by _STA method) for the device. 646 */ 647 static ACPI_STATUS 648 pciehpc_acpi_get_dev_state(ACPI_HANDLE obj, int *statusp) 649 { 650 ACPI_BUFFER rb; 651 ACPI_DEVICE_INFO *info = NULL; 652 int ret = AE_OK; 653 654 /* 655 * Get device info object 656 */ 657 rb.Length = ACPI_ALLOCATE_BUFFER; 658 rb.Pointer = NULL; 659 if ((ret = AcpiGetObjectInfo(obj, &rb)) != AE_OK) 660 return (ret); 661 info = (ACPI_DEVICE_INFO *)rb.Pointer; 662 663 if (info->Valid & ACPI_VALID_STA) { 664 *statusp = info->CurrentStatus; 665 } else { 666 /* 667 * no _STA present; assume the device status is normal 668 * (i.e present, enabled, shown in UI and functioning). 669 * See section 6.3.7 of ACPI 3.0 spec. 670 */ 671 *statusp = STATUS_NORMAL; 672 } 673 674 AcpiOsFree(info); 675 676 return (ret); 677 } 678