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