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