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 /* get current slot state from the hw */ 269 pciehpc_get_slot_state(slot_p); 270 if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_ENABLED) 271 slot_p->hs_condition = AP_COND_OK; 272 273 mutex_exit(&ctrl_p->hc_mutex); 274 275 /* setup Notify() handler for hot plug events from ACPI BIOS */ 276 if (pciehpc_acpi_install_event_handler(ctrl_p) != AE_OK) 277 return (DDI_FAILURE); 278 279 PCIE_DBG("ACPI hot plug is enabled for slot #%d\n", 280 slot_p->hs_phy_slot_num); 281 282 return (DDI_SUCCESS); 283 } 284 285 /* 286 * This function is similar to pciehcp_slotinfo_uninit() but has ACPI 287 * specific cleanup. 288 */ 289 static int 290 pciehpc_acpi_slotinfo_uninit(pcie_hp_ctrl_t *ctrl_p) 291 { 292 pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0]; 293 294 /* uninstall Notify() event handler */ 295 pciehpc_acpi_uninstall_event_handler(ctrl_p); 296 if (slot_p->hs_info.cn_name) 297 kmem_free(slot_p->hs_info.cn_name, 298 strlen(slot_p->hs_info.cn_name) + 1); 299 300 return (DDI_SUCCESS); 301 } 302 303 /* 304 * This function is same as pciehpc_slot_poweron() except that it 305 * uses ACPI method PS0 to enable power to the slot. If no PS0 method 306 * is present then it returns DDI_FAILURE. 307 */ 308 /*ARGSUSED*/ 309 static int 310 pciehpc_acpi_slot_poweron(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result) 311 { 312 pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl; 313 pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip); 314 uint16_t status, control; 315 316 ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex)); 317 318 /* get the current state of the slot */ 319 pciehpc_get_slot_state(slot_p); 320 321 /* check if the slot is already in the 'ENABLED' state */ 322 if (slot_p->hs_info.cn_state == DDI_HP_CN_STATE_ENABLED) { 323 /* slot is already in the 'connected' state */ 324 PCIE_DBG("slot %d already connected\n", 325 slot_p->hs_phy_slot_num); 326 327 *result = slot_p->hs_info.cn_state; 328 return (DDI_SUCCESS); 329 } 330 331 /* read the Slot Status Register */ 332 status = pciehpc_reg_get16(ctrl_p, 333 bus_p->bus_pcie_off + PCIE_SLOTSTS); 334 335 /* make sure the MRL switch is closed if present */ 336 if ((ctrl_p->hc_has_mrl) && (status & PCIE_SLOTSTS_MRL_SENSOR_OPEN)) { 337 /* MRL switch is open */ 338 cmn_err(CE_WARN, "MRL switch is open on slot %d", 339 slot_p->hs_phy_slot_num); 340 goto cleanup; 341 } 342 343 /* make sure the slot has a device present */ 344 if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) { 345 /* slot is empty */ 346 PCIE_DBG("slot %d is empty\n", slot_p->hs_phy_slot_num); 347 goto cleanup; 348 } 349 350 /* get the current state of Slot Control Register */ 351 control = pciehpc_reg_get16(ctrl_p, 352 bus_p->bus_pcie_off + PCIE_SLOTCTL); 353 354 /* check if the slot's power state is ON */ 355 if (!(control & PCIE_SLOTCTL_PWR_CONTROL)) { 356 /* slot is already powered up */ 357 PCIE_DBG("slot %d already connected\n", 358 slot_p->hs_phy_slot_num); 359 360 *result = slot_p->hs_info.cn_state; 361 return (DDI_SUCCESS); 362 } 363 364 /* turn on power to the slot using ACPI method (PS0) */ 365 if (pciehpc_acpi_power_on_slot(ctrl_p) != AE_OK) 366 goto cleanup; 367 368 *result = slot_p->hs_info.cn_state = DDI_HP_CN_STATE_POWERED; 369 return (DDI_SUCCESS); 370 371 cleanup: 372 return (DDI_FAILURE); 373 } 374 375 /* 376 * This function is same as pciehpc_slot_poweroff() except that it 377 * uses ACPI method EJ0 to disable power to the slot. If no EJ0 method 378 * is present then it returns DDI_FAILURE. 379 */ 380 /*ARGSUSED*/ 381 static int 382 pciehpc_acpi_slot_poweroff(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result) 383 { 384 pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl; 385 pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip); 386 uint16_t status; 387 388 ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex)); 389 390 /* get the current state of the slot */ 391 pciehpc_get_slot_state(slot_p); 392 393 /* check if the slot is already in the state less than 'powered' */ 394 if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED) { 395 /* slot is in the 'disconnected' state */ 396 PCIE_DBG("slot %d already disconnected\n", 397 slot_p->hs_phy_slot_num); 398 ASSERT(slot_p->hs_power_led_state == PCIE_HP_LED_OFF); 399 400 *result = slot_p->hs_info.cn_state; 401 return (DDI_SUCCESS); 402 } 403 404 /* read the Slot Status Register */ 405 status = pciehpc_reg_get16(ctrl_p, 406 bus_p->bus_pcie_off + PCIE_SLOTSTS); 407 408 /* make sure the slot has a device present */ 409 if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) { 410 /* slot is empty */ 411 PCIE_DBG("slot %d is empty", slot_p->hs_phy_slot_num); 412 goto cleanup; 413 } 414 415 /* turn off power to the slot using ACPI method (EJ0) */ 416 if (pciehpc_acpi_power_off_slot(ctrl_p) != AE_OK) 417 goto cleanup; 418 419 /* get the current state of the slot */ 420 pciehpc_get_slot_state(slot_p); 421 422 *result = slot_p->hs_info.cn_state; 423 424 return (DDI_SUCCESS); 425 426 cleanup: 427 return (DDI_FAILURE); 428 } 429 430 /* 431 * Install event handler for the hot plug events on the bus node as well 432 * as device function (dev=0,func=0). 433 */ 434 static ACPI_STATUS 435 pciehpc_acpi_install_event_handler(pcie_hp_ctrl_t *ctrl_p) 436 { 437 pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0]; 438 int status = AE_OK; 439 pciehpc_acpi_t *acpi_p; 440 441 PCIE_DBG("install event handler for slot %d\n", 442 slot_p->hs_phy_slot_num); 443 acpi_p = ctrl_p->hc_misc_data; 444 if (acpi_p->slot_dev_obj == NULL) 445 return (AE_NOT_FOUND); 446 447 /* 448 * Install event hanlder for events on the bus object. 449 * (Note: Insert event (hot-insert) is delivered on this object) 450 */ 451 status = AcpiInstallNotifyHandler(acpi_p->slot_dev_obj, 452 ACPI_SYSTEM_NOTIFY, pciehpc_acpi_notify_handler, (void *)ctrl_p); 453 if (status != AE_OK) 454 goto cleanup; 455 456 /* 457 * Install event hanlder for events on the device function object. 458 * (Note: Eject device event (hot-remove) is delivered on this object) 459 * 460 * NOTE: Here the assumption is that Notify events are delivered 461 * on all of the 8 possible device functions so, subscribing to 462 * one of them is sufficient. 463 */ 464 status = AcpiInstallNotifyHandler(acpi_p->bus_obj, 465 ACPI_SYSTEM_NOTIFY, pciehpc_acpi_notify_handler, (void *)ctrl_p); 466 return (status); 467 468 cleanup: 469 (void) AcpiRemoveNotifyHandler(acpi_p->slot_dev_obj, 470 ACPI_SYSTEM_NOTIFY, pciehpc_acpi_notify_handler); 471 return (status); 472 } 473 474 /*ARGSUSED*/ 475 static void 476 pciehpc_acpi_notify_handler(ACPI_HANDLE device, uint32_t val, void *context) 477 { 478 pcie_hp_ctrl_t *ctrl_p = context; 479 pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0]; 480 pciehpc_acpi_t *acpi_p; 481 ddi_hp_cn_state_t curr_state; 482 int dev_state = 0; 483 484 PCIE_DBG("received Notify(%d) event on slot #%d\n", 485 val, slot_p->hs_phy_slot_num); 486 487 mutex_enter(&ctrl_p->hc_mutex); 488 489 /* 490 * get the state of the device (from _STA method) 491 */ 492 acpi_p = ctrl_p->hc_misc_data; 493 if (pciehpc_acpi_get_dev_state(acpi_p->slot_dev_obj, 494 &dev_state) != AE_OK) { 495 cmn_err(CE_WARN, "failed to get device status on slot %d", 496 slot_p->hs_phy_slot_num); 497 } 498 PCIE_DBG("(1)device state on slot #%d: 0x%x\n", 499 slot_p->hs_phy_slot_num, dev_state); 500 501 curr_state = slot_p->hs_info.cn_state; 502 pciehpc_get_slot_state(slot_p); 503 504 switch (val) { 505 case 0: /* (re)enumerate the device */ 506 case 3: /* Request Eject */ 507 { 508 ddi_hp_cn_state_t target_state; 509 510 /* 511 * Ignore the event if ATTN button is not present (ACPI BIOS 512 * problem). 513 * 514 * NOTE: This situation has been observed on some platforms 515 * where the ACPI BIOS is generating the event for some other 516 * (non hot-plug) operations (bug). 517 */ 518 if (ctrl_p->hc_has_attn == B_FALSE) { 519 PCIE_DBG("Ignore the unexpected event " 520 "on slot #%d (state 0x%x)", 521 slot_p->hs_phy_slot_num, dev_state); 522 break; 523 } 524 525 /* send the event to DDI Hotplug framework */ 526 if (curr_state < DDI_HP_CN_STATE_POWERED) { 527 /* Insertion. Upgrade state to ENABLED */ 528 target_state = DDI_HP_CN_STATE_ENABLED; 529 530 /* 531 * When pressing ATTN button to enable a card, the slot 532 * could be powered. Keep the slot state on PWOERED 533 * other than ENABLED. 534 */ 535 if (slot_p->hs_info.cn_state == DDI_HP_CN_STATE_ENABLED) 536 slot_p->hs_info.cn_state = 537 DDI_HP_CN_STATE_POWERED; 538 } else { 539 /* Want to remove; Power off Connection */ 540 target_state = DDI_HP_CN_STATE_EMPTY; 541 } 542 543 (void) ndi_hp_state_change_req(slot_p->hs_ctrl->hc_dip, 544 slot_p->hs_info.cn_name, 545 target_state, DDI_HP_REQ_ASYNC); 546 547 break; 548 } 549 default: 550 cmn_err(CE_NOTE, "Unknown Notify() event %d on slot #%d\n", 551 val, slot_p->hs_phy_slot_num); 552 break; 553 } 554 mutex_exit(&ctrl_p->hc_mutex); 555 } 556 557 static void 558 pciehpc_acpi_uninstall_event_handler(pcie_hp_ctrl_t *ctrl_p) 559 { 560 pciehpc_acpi_t *acpi_p = ctrl_p->hc_misc_data; 561 pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0]; 562 563 PCIE_DBG("Uninstall event handler for slot #%d\n", 564 slot_p->hs_phy_slot_num); 565 (void) AcpiRemoveNotifyHandler(acpi_p->slot_dev_obj, 566 ACPI_SYSTEM_NOTIFY, pciehpc_acpi_notify_handler); 567 (void) AcpiRemoveNotifyHandler(acpi_p->bus_obj, 568 ACPI_SYSTEM_NOTIFY, pciehpc_acpi_notify_handler); 569 } 570 571 /* 572 * Run _PS0 method to turn on power to the slot. 573 */ 574 static ACPI_STATUS 575 pciehpc_acpi_power_on_slot(pcie_hp_ctrl_t *ctrl_p) 576 { 577 int status = AE_OK; 578 pciehpc_acpi_t *acpi_p = ctrl_p->hc_misc_data; 579 int dev_state = 0; 580 pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0]; 581 582 PCIE_DBG("turn ON power to the slot #%d\n", slot_p->hs_phy_slot_num); 583 584 status = AcpiEvaluateObject(acpi_p->slot_dev_obj, "_PS0", NULL, NULL); 585 586 /* get the state of the device (from _STA method) */ 587 if (status == AE_OK) { 588 if (pciehpc_acpi_get_dev_state(acpi_p->slot_dev_obj, 589 &dev_state) != AE_OK) 590 cmn_err(CE_WARN, "failed to get device status " 591 "on slot #%d", slot_p->hs_phy_slot_num); 592 } 593 594 PCIE_DBG("(3)device state on slot #%d: 0x%x\n", 595 slot_p->hs_phy_slot_num, dev_state); 596 597 pciehpc_get_slot_state(slot_p); 598 599 if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED) { 600 cmn_err(CE_WARN, "failed to power on the slot #%d" 601 "(dev_state 0x%x, ACPI_STATUS 0x%x)", 602 slot_p->hs_phy_slot_num, dev_state, status); 603 return (AE_ERROR); 604 } 605 606 return (status); 607 } 608 609 /* 610 * Run _EJ0 method to turn off power to the slot. 611 */ 612 static ACPI_STATUS 613 pciehpc_acpi_power_off_slot(pcie_hp_ctrl_t *ctrl_p) 614 { 615 int status = AE_OK; 616 pciehpc_acpi_t *acpi_p = ctrl_p->hc_misc_data; 617 int dev_state = 0; 618 pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0]; 619 620 PCIE_DBG("turn OFF power to the slot #%d\n", slot_p->hs_phy_slot_num); 621 622 status = AcpiEvaluateObject(acpi_p->slot_dev_obj, "_EJ0", NULL, NULL); 623 624 /* get the state of the device (from _STA method) */ 625 if (status == AE_OK) { 626 if (pciehpc_acpi_get_dev_state(acpi_p->slot_dev_obj, 627 &dev_state) != AE_OK) 628 cmn_err(CE_WARN, "failed to get device status " 629 "on slot #%d", slot_p->hs_phy_slot_num); 630 } 631 632 PCIE_DBG("(2)device state on slot #%d: 0x%x\n", 633 slot_p->hs_phy_slot_num, dev_state); 634 635 pciehpc_get_slot_state(slot_p); 636 637 if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED) { 638 cmn_err(CE_WARN, "failed to power OFF the slot #%d" 639 "(dev_state 0x%x, ACPI_STATUS 0x%x)", 640 slot_p->hs_phy_slot_num, dev_state, status); 641 return (AE_ERROR); 642 } 643 644 return (status); 645 } 646 647 /* 648 * Get the status info (as returned by _STA method) for the device. 649 */ 650 static ACPI_STATUS 651 pciehpc_acpi_get_dev_state(ACPI_HANDLE obj, int *statusp) 652 { 653 int status; 654 ACPI_STATUS ret; 655 656 ret = acpica_get_object_status(obj, &status); 657 if (ACPI_SUCCESS(ret)) { 658 *statusp = status; 659 } 660 661 return (ret); 662 } 663