xref: /illumos-gate/usr/src/uts/intel/io/pciex/hotplug/pciehpc_acpi.c (revision 5328fc53d11d7151861fa272e4fb0248b8f0e145)
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