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