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
pciehpc_update_ops(pcie_hp_ctrl_t * ctrl_p)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
pciehpc_acpi_setup_ops(pcie_hp_ctrl_t * ctrl_p)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
pciehpc_acpi_hpc_init(pcie_hp_ctrl_t * ctrl_p)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
pciehpc_acpi_hpc_uninit(pcie_hp_ctrl_t * ctrl_p)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
pciehpc_acpi_enable_intr(pcie_hp_ctrl_t * ctrl_p)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
pciehpc_acpi_disable_intr(pcie_hp_ctrl_t * ctrl_p)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
pciehpc_acpi_slotinfo_init(pcie_hp_ctrl_t * ctrl_p)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
pciehpc_acpi_slotinfo_uninit(pcie_hp_ctrl_t * ctrl_p)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
pciehpc_acpi_slot_poweron(pcie_hp_slot_t * slot_p,ddi_hp_cn_state_t * result)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
pciehpc_acpi_slot_poweroff(pcie_hp_slot_t * slot_p,ddi_hp_cn_state_t * result)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
pciehpc_acpi_install_event_handler(pcie_hp_ctrl_t * ctrl_p)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
pciehpc_acpi_notify_handler(ACPI_HANDLE device,uint32_t val,void * context)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
pciehpc_acpi_uninstall_event_handler(pcie_hp_ctrl_t * ctrl_p)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
pciehpc_acpi_power_on_slot(pcie_hp_ctrl_t * ctrl_p)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
pciehpc_acpi_power_off_slot(pcie_hp_ctrl_t * ctrl_p)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
pciehpc_acpi_get_dev_state(ACPI_HANDLE obj,int * statusp)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