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