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