xref: /illumos-gate/usr/src/uts/common/io/pciex/hotplug/pciehpc.c (revision 7dbbfe7762f9eabac3999ee1a8b38311d428f7a8)
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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2019 Joyent, Inc.
25  * Copyright 2023 Oxide Computer Company
26  */
27 
28 /*
29  * This file contains Standard PCI Express HotPlug functionality that is
30  * compatible with the PCI Express ver 1.1 specification.
31  *
32  * NOTE: This file is compiled and delivered through misc/pcie module.
33  */
34 
35 #include <sys/types.h>
36 #include <sys/note.h>
37 #include <sys/conf.h>
38 #include <sys/kmem.h>
39 #include <sys/debug.h>
40 #include <sys/vtrace.h>
41 #include <sys/autoconf.h>
42 #include <sys/varargs.h>
43 #include <sys/ddi_impldefs.h>
44 #include <sys/time.h>
45 #include <sys/callb.h>
46 #include <sys/ddi.h>
47 #include <sys/sunddi.h>
48 #include <sys/sunndi.h>
49 #include <sys/sysevent/dr.h>
50 #include <sys/pci_impl.h>
51 #include <sys/hotplug/pci/pcie_hp.h>
52 #include <sys/hotplug/pci/pciehpc.h>
53 
54 typedef struct pciehpc_prop {
55 	char	*prop_name;
56 	char	*prop_value;
57 } pciehpc_prop_t;
58 
59 static pciehpc_prop_t	pciehpc_props[] = {
60 	{ PCIEHPC_PROP_LED_FAULT,	PCIEHPC_PROP_VALUE_LED },
61 	{ PCIEHPC_PROP_LED_POWER,	PCIEHPC_PROP_VALUE_LED },
62 	{ PCIEHPC_PROP_LED_ATTN,	PCIEHPC_PROP_VALUE_LED },
63 	{ PCIEHPC_PROP_LED_ACTIVE,	PCIEHPC_PROP_VALUE_LED },
64 	{ PCIEHPC_PROP_CARD_TYPE,	PCIEHPC_PROP_VALUE_TYPE },
65 	{ PCIEHPC_PROP_BOARD_TYPE,	PCIEHPC_PROP_VALUE_TYPE },
66 	{ PCIEHPC_PROP_SLOT_CONDITION,	PCIEHPC_PROP_VALUE_TYPE }
67 };
68 
69 /* Local functions prototype */
70 static int pciehpc_hpc_init(pcie_hp_ctrl_t *ctrl_p);
71 static int pciehpc_hpc_uninit(pcie_hp_ctrl_t *ctrl_p);
72 static int pciehpc_slotinfo_init(pcie_hp_ctrl_t *ctrl_p);
73 static int pciehpc_slotinfo_uninit(pcie_hp_ctrl_t *ctrl_p);
74 static int pciehpc_enable_intr(pcie_hp_ctrl_t *ctrl_p);
75 static int pciehpc_disable_intr(pcie_hp_ctrl_t *ctrl_p);
76 static pcie_hp_ctrl_t *pciehpc_create_controller(dev_info_t *dip);
77 static void pciehpc_destroy_controller(dev_info_t *dip);
78 static int pciehpc_register_slot(pcie_hp_ctrl_t *ctrl_p);
79 static int pciehpc_unregister_slot(pcie_hp_ctrl_t *ctrl_p);
80 static int pciehpc_slot_get_property(pcie_hp_slot_t *slot_p,
81     ddi_hp_property_t *arg, ddi_hp_property_t *rval);
82 static int pciehpc_slot_set_property(pcie_hp_slot_t *slot_p,
83     ddi_hp_property_t *arg, ddi_hp_property_t *rval);
84 static void pciehpc_issue_hpc_command(pcie_hp_ctrl_t *ctrl_p, uint16_t control);
85 static void pciehpc_attn_btn_handler(pcie_hp_ctrl_t *ctrl_p);
86 static pcie_hp_led_state_t pciehpc_led_state_to_hpc(uint16_t state);
87 static pcie_hp_led_state_t pciehpc_get_led_state(pcie_hp_ctrl_t *ctrl_p,
88     pcie_hp_led_t led);
89 static void pciehpc_set_led_state(pcie_hp_ctrl_t *ctrl_p, pcie_hp_led_t led,
90     pcie_hp_led_state_t state);
91 
92 static int pciehpc_upgrade_slot_state(pcie_hp_slot_t *slot_p,
93     ddi_hp_cn_state_t target_state);
94 static int pciehpc_downgrade_slot_state(pcie_hp_slot_t *slot_p,
95     ddi_hp_cn_state_t target_state);
96 static int pciehpc_change_slot_state(pcie_hp_slot_t *slot_p,
97     ddi_hp_cn_state_t target_state);
98 static int
99     pciehpc_slot_poweron(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result);
100 static int
101     pciehpc_slot_poweroff(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result);
102 static int pciehpc_slot_probe(pcie_hp_slot_t *slot_p);
103 static int pciehpc_slot_unprobe(pcie_hp_slot_t *slot_p);
104 static void pciehpc_handle_power_fault(dev_info_t *dip);
105 static void pciehpc_power_fault_handler(void *arg);
106 
107 #ifdef	DEBUG
108 static void pciehpc_dump_hpregs(pcie_hp_ctrl_t *ctrl_p);
109 #endif	/* DEBUG */
110 
111 /*
112  * Global functions (called by other drivers/modules)
113  */
114 
115 /*
116  * Initialize Hot Plug Controller if present. The arguments are:
117  *	dip	- Devinfo node pointer to the hot plug bus node
118  *	regops	- register ops to access HPC registers for non-standard
119  *		  HPC hw implementations (e.g: HPC in host PCI-E brdiges)
120  *		  This is NULL for standard HPC in PCIe bridges.
121  * Returns:
122  *	DDI_SUCCESS for successful HPC initialization
123  *	DDI_FAILURE for errors or if HPC hw not found
124  */
125 int
126 pciehpc_init(dev_info_t *dip, caddr_t arg)
127 {
128 	pcie_hp_regops_t	*regops = (pcie_hp_regops_t *)(void *)arg;
129 	pcie_hp_ctrl_t		*ctrl_p;
130 
131 	PCIE_DBG("pciehpc_init() called (dip=%p)\n", (void *)dip);
132 
133 	/* Make sure that it is not already initialized */
134 	if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) != NULL) {
135 		PCIE_DBG("%s%d: pciehpc instance already initialized!\n",
136 		    ddi_driver_name(dip), ddi_get_instance(dip));
137 		return (DDI_SUCCESS);
138 	}
139 
140 	/* Allocate a new hotplug controller and slot structures */
141 	ctrl_p = pciehpc_create_controller(dip);
142 
143 	/* setup access handle for HPC regs */
144 	if (regops != NULL) {
145 		/* HPC access is non-standard; use the supplied reg ops */
146 		ctrl_p->hc_regops = *regops;
147 	}
148 
149 	/*
150 	 * Setup resource maps for this bus node.
151 	 */
152 	(void) pci_resource_setup(dip);
153 
154 	PCIE_DISABLE_ERRORS(dip);
155 
156 	/*
157 	 * Set the platform specific hot plug mode.
158 	 */
159 	ctrl_p->hc_ops.init_hpc_hw = pciehpc_hpc_init;
160 	ctrl_p->hc_ops.uninit_hpc_hw = pciehpc_hpc_uninit;
161 	ctrl_p->hc_ops.init_hpc_slotinfo = pciehpc_slotinfo_init;
162 	ctrl_p->hc_ops.uninit_hpc_slotinfo = pciehpc_slotinfo_uninit;
163 	ctrl_p->hc_ops.poweron_hpc_slot = pciehpc_slot_poweron;
164 	ctrl_p->hc_ops.poweroff_hpc_slot = pciehpc_slot_poweroff;
165 
166 	ctrl_p->hc_ops.enable_hpc_intr = pciehpc_enable_intr;
167 	ctrl_p->hc_ops.disable_hpc_intr = pciehpc_disable_intr;
168 
169 #if	defined(__x86)
170 	pciehpc_update_ops(ctrl_p);
171 #endif
172 
173 	/* initialize hot plug controller hw */
174 	if ((ctrl_p->hc_ops.init_hpc_hw)(ctrl_p) != DDI_SUCCESS)
175 		goto cleanup1;
176 
177 	/* initialize slot information soft state structure */
178 	if ((ctrl_p->hc_ops.init_hpc_slotinfo)(ctrl_p) != DDI_SUCCESS)
179 		goto cleanup2;
180 
181 	/* register the hot plug slot with DDI HP framework */
182 	if (pciehpc_register_slot(ctrl_p) != DDI_SUCCESS)
183 		goto cleanup3;
184 
185 	/* create minor node for this slot */
186 	if (pcie_create_minor_node(ctrl_p, 0) != DDI_SUCCESS)
187 		goto cleanup4;
188 
189 	/* HPC initialization is complete now */
190 	ctrl_p->hc_flags = PCIE_HP_INITIALIZED_FLAG;
191 
192 #ifdef	DEBUG
193 	/* For debug, dump the HPC registers */
194 	pciehpc_dump_hpregs(ctrl_p);
195 #endif	/* DEBUG */
196 
197 	return (DDI_SUCCESS);
198 cleanup4:
199 	(void) pciehpc_unregister_slot(ctrl_p);
200 cleanup3:
201 	(void) (ctrl_p->hc_ops.uninit_hpc_slotinfo)(ctrl_p);
202 
203 cleanup2:
204 	(void) (ctrl_p->hc_ops.uninit_hpc_hw)(ctrl_p);
205 
206 cleanup1:
207 	PCIE_ENABLE_ERRORS(dip);
208 	(void) pci_resource_destroy(dip);
209 
210 	pciehpc_destroy_controller(dip);
211 	return (DDI_FAILURE);
212 }
213 
214 /*
215  * Uninitialize HPC soft state structure and free up any resources
216  * used for the HPC instance.
217  */
218 int
219 pciehpc_uninit(dev_info_t *dip)
220 {
221 	pcie_hp_ctrl_t *ctrl_p;
222 
223 	PCIE_DBG("pciehpc_uninit() called (dip=%p)\n", (void *)dip);
224 
225 	/* get the soft state structure for this dip */
226 	if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL) {
227 		return (DDI_FAILURE);
228 	}
229 
230 	pcie_remove_minor_node(ctrl_p, 0);
231 
232 	/* unregister the slot */
233 	(void) pciehpc_unregister_slot(ctrl_p);
234 
235 	/* uninit any slot info data structures */
236 	(void) (ctrl_p->hc_ops.uninit_hpc_slotinfo)(ctrl_p);
237 
238 	/* uninitialize hpc, remove interrupt handler, etc. */
239 	(void) (ctrl_p->hc_ops.uninit_hpc_hw)(ctrl_p);
240 
241 	PCIE_ENABLE_ERRORS(dip);
242 
243 	/*
244 	 * Destroy resource maps for this bus node.
245 	 */
246 	(void) pci_resource_destroy(dip);
247 
248 	/* destroy the soft state structure */
249 	pciehpc_destroy_controller(dip);
250 
251 	return (DDI_SUCCESS);
252 }
253 
254 /*
255  * pciehpc_intr()
256  *
257  * Interrupt handler for PCI-E Hot plug controller interrupts.
258  *
259  * Note: This is only for native mode hot plug. This is called
260  * by the nexus driver at interrupt context. Interrupt Service Routine
261  * registration is done by the nexus driver for both hot plug and
262  * non-hot plug interrupts. This function is called from the ISR
263  * of the nexus driver to handle hot-plug interrupts.
264  */
265 int
266 pciehpc_intr(dev_info_t *dip)
267 {
268 	pcie_hp_ctrl_t	*ctrl_p;
269 	pcie_hp_slot_t	*slot_p;
270 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
271 	uint16_t	status, control;
272 
273 	/* get the soft state structure for this dip */
274 	if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL)
275 		return (DDI_INTR_UNCLAIMED);
276 
277 	mutex_enter(&ctrl_p->hc_mutex);
278 
279 	/* make sure the controller soft state is initialized */
280 	if (!(ctrl_p->hc_flags & PCIE_HP_INITIALIZED_FLAG)) {
281 		mutex_exit(&ctrl_p->hc_mutex);
282 		return (DDI_INTR_UNCLAIMED);
283 	}
284 
285 	/* if it is not NATIVE hot plug mode then return */
286 	if (bus_p->bus_hp_curr_mode != PCIE_NATIVE_HP_MODE) {
287 		mutex_exit(&ctrl_p->hc_mutex);
288 		return (DDI_INTR_UNCLAIMED);
289 	}
290 
291 	slot_p = ctrl_p->hc_slots[0];
292 
293 	/* read the current slot status register */
294 	status = pciehpc_reg_get16(ctrl_p,
295 	    bus_p->bus_pcie_off + PCIE_SLOTSTS);
296 
297 	/* check if there are any hot plug interrupts occurred */
298 	if (!(status & PCIE_SLOTSTS_STATUS_EVENTS)) {
299 		/* no hot plug events occurred */
300 		mutex_exit(&ctrl_p->hc_mutex);
301 		return (DDI_INTR_UNCLAIMED);
302 	}
303 
304 	/* clear the interrupt status bits */
305 	pciehpc_reg_put16(ctrl_p,
306 	    bus_p->bus_pcie_off + PCIE_SLOTSTS, status);
307 
308 	/* check for CMD COMPLETE interrupt */
309 	if (status & PCIE_SLOTSTS_COMMAND_COMPLETED) {
310 		PCIE_DBG("pciehpc_intr(): CMD COMPLETED interrupt received\n");
311 		/* wake up any one waiting for Command Completion event */
312 		cv_signal(&ctrl_p->hc_cmd_comp_cv);
313 	}
314 
315 	/* check for ATTN button interrupt */
316 	if (status & PCIE_SLOTSTS_ATTN_BTN_PRESSED) {
317 		PCIE_DBG("pciehpc_intr(): ATTN BUTTON interrupt received\n");
318 
319 		/* if ATTN button event is still pending then cancel it */
320 		if (slot_p->hs_attn_btn_pending == B_TRUE)
321 			slot_p->hs_attn_btn_pending = B_FALSE;
322 		else
323 			slot_p->hs_attn_btn_pending = B_TRUE;
324 
325 		/* wake up the ATTN event handler */
326 		cv_signal(&slot_p->hs_attn_btn_cv);
327 	}
328 
329 	/* check for power fault interrupt */
330 	if (status & PCIE_SLOTSTS_PWR_FAULT_DETECTED) {
331 
332 		PCIE_DBG("pciehpc_intr(): POWER FAULT interrupt received"
333 		    " on slot %d\n", slot_p->hs_phy_slot_num);
334 		control =  pciehpc_reg_get16(ctrl_p,
335 		    bus_p->bus_pcie_off + PCIE_SLOTCTL);
336 
337 		if (control & PCIE_SLOTCTL_PWR_FAULT_EN) {
338 			slot_p->hs_condition = AP_COND_FAILED;
339 
340 			/* disable power fault detction interrupt */
341 			pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off +
342 			    PCIE_SLOTCTL, control & ~PCIE_SLOTCTL_PWR_FAULT_EN);
343 
344 			pciehpc_handle_power_fault(dip);
345 		}
346 	}
347 
348 	/* check for MRL SENSOR CHANGED interrupt */
349 	if (status & PCIE_SLOTSTS_MRL_SENSOR_CHANGED) {
350 		/* For now (phase-I), no action is taken on this event */
351 		PCIE_DBG("pciehpc_intr(): MRL SENSOR CHANGED interrupt received"
352 		    " on slot %d\n", slot_p->hs_phy_slot_num);
353 	}
354 
355 	/* check for PRESENCE CHANGED interrupt */
356 	if (status & PCIE_SLOTSTS_PRESENCE_CHANGED) {
357 
358 		PCIE_DBG("pciehpc_intr(): PRESENCE CHANGED interrupt received"
359 		    " on slot %d\n", slot_p->hs_phy_slot_num);
360 
361 		if (status & PCIE_SLOTSTS_PRESENCE_DETECTED) {
362 			/*
363 			 * card is inserted into the slot, ask DDI Hotplug
364 			 * framework to change state to Present.
365 			 */
366 			cmn_err(CE_NOTE, "pciehpc (%s%d): card is inserted"
367 			    " in the slot %s",
368 			    ddi_driver_name(dip),
369 			    ddi_get_instance(dip),
370 			    slot_p->hs_info.cn_name);
371 
372 			(void) ndi_hp_state_change_req(dip,
373 			    slot_p->hs_info.cn_name,
374 			    DDI_HP_CN_STATE_PRESENT,
375 			    DDI_HP_REQ_ASYNC);
376 		} else { /* card is removed from the slot */
377 			cmn_err(CE_NOTE, "pciehpc (%s%d): card is removed"
378 			    " from the slot %s",
379 			    ddi_driver_name(dip),
380 			    ddi_get_instance(dip),
381 			    slot_p->hs_info.cn_name);
382 
383 			if (slot_p->hs_info.cn_state ==
384 			    DDI_HP_CN_STATE_ENABLED) {
385 				/* Card is removed when slot is enabled */
386 				slot_p->hs_condition = AP_COND_FAILED;
387 			} else {
388 				slot_p->hs_condition = AP_COND_UNKNOWN;
389 			}
390 			/* make sure to disable power fault detction intr */
391 			control =  pciehpc_reg_get16(ctrl_p,
392 			    bus_p->bus_pcie_off + PCIE_SLOTCTL);
393 
394 			if (control & PCIE_SLOTCTL_PWR_FAULT_EN)
395 				pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off +
396 				    PCIE_SLOTCTL,
397 				    control & ~PCIE_SLOTCTL_PWR_FAULT_EN);
398 
399 			/*
400 			 * If supported, notify the child device driver that the
401 			 * device is being removed.
402 			 */
403 			dev_info_t *cdip = ddi_get_child(dip);
404 			if (cdip != NULL) {
405 				ddi_eventcookie_t rm_cookie;
406 				if (ddi_get_eventcookie(cdip,
407 				    DDI_DEVI_REMOVE_EVENT,
408 				    &rm_cookie) == DDI_SUCCESS) {
409 					ndi_post_event(dip, cdip, rm_cookie,
410 					    NULL);
411 				}
412 			}
413 
414 			/*
415 			 * Ask DDI Hotplug framework to change state to Empty
416 			 */
417 			(void) ndi_hp_state_change_req(dip,
418 			    slot_p->hs_info.cn_name,
419 			    DDI_HP_CN_STATE_EMPTY,
420 			    DDI_HP_REQ_ASYNC);
421 		}
422 	}
423 
424 	/* check for DLL state changed interrupt */
425 	if (ctrl_p->hc_dll_active_rep &&
426 	    (status & PCIE_SLOTSTS_DLL_STATE_CHANGED)) {
427 		PCIE_DBG("pciehpc_intr(): DLL STATE CHANGED interrupt received"
428 		    " on slot %d\n", slot_p->hs_phy_slot_num);
429 
430 		cv_signal(&slot_p->hs_dll_active_cv);
431 	}
432 
433 	mutex_exit(&ctrl_p->hc_mutex);
434 
435 	return (DDI_INTR_CLAIMED);
436 }
437 
438 /*
439  * Handle hotplug commands
440  *
441  * Note: This function is called by DDI HP framework at kernel context only
442  */
443 /* ARGSUSED */
444 int
445 pciehpc_hp_ops(dev_info_t *dip, char *cn_name, ddi_hp_op_t op,
446     void *arg, void *result)
447 {
448 	pcie_hp_ctrl_t	*ctrl_p;
449 	pcie_hp_slot_t	*slot_p;
450 	int		ret = DDI_SUCCESS;
451 
452 	PCIE_DBG("pciehpc_hp_ops: dip=%p cn_name=%s op=%x arg=%p\n",
453 	    dip, cn_name, op, arg);
454 
455 	if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL)
456 		return (DDI_FAILURE);
457 
458 	slot_p = ctrl_p->hc_slots[0];
459 
460 	if (strcmp(cn_name, slot_p->hs_info.cn_name) != 0)
461 		return (DDI_EINVAL);
462 
463 	switch (op) {
464 	case DDI_HPOP_CN_GET_STATE:
465 	{
466 		mutex_enter(&slot_p->hs_ctrl->hc_mutex);
467 
468 		/* get the current slot state */
469 		pciehpc_get_slot_state(slot_p);
470 
471 		*((ddi_hp_cn_state_t *)result) = slot_p->hs_info.cn_state;
472 
473 		mutex_exit(&slot_p->hs_ctrl->hc_mutex);
474 		break;
475 	}
476 	case DDI_HPOP_CN_CHANGE_STATE:
477 	{
478 		ddi_hp_cn_state_t target_state = *(ddi_hp_cn_state_t *)arg;
479 
480 		mutex_enter(&slot_p->hs_ctrl->hc_mutex);
481 
482 		ret = pciehpc_change_slot_state(slot_p, target_state);
483 		*(ddi_hp_cn_state_t *)result = slot_p->hs_info.cn_state;
484 
485 		mutex_exit(&slot_p->hs_ctrl->hc_mutex);
486 		break;
487 	}
488 	case DDI_HPOP_CN_PROBE:
489 
490 		ret = pciehpc_slot_probe(slot_p);
491 
492 		break;
493 	case DDI_HPOP_CN_UNPROBE:
494 		ret = pciehpc_slot_unprobe(slot_p);
495 
496 		break;
497 	case DDI_HPOP_CN_GET_PROPERTY:
498 		ret = pciehpc_slot_get_property(slot_p,
499 		    (ddi_hp_property_t *)arg, (ddi_hp_property_t *)result);
500 		break;
501 	case DDI_HPOP_CN_SET_PROPERTY:
502 		ret = pciehpc_slot_set_property(slot_p,
503 		    (ddi_hp_property_t *)arg, (ddi_hp_property_t *)result);
504 		break;
505 	default:
506 		ret = DDI_ENOTSUP;
507 		break;
508 	}
509 
510 	return (ret);
511 }
512 
513 /*
514  * Get the current state of the slot from the hw.
515  *
516  * The slot state should have been initialized before this function gets called.
517  */
518 void
519 pciehpc_get_slot_state(pcie_hp_slot_t *slot_p)
520 {
521 	pcie_hp_ctrl_t	*ctrl_p = slot_p->hs_ctrl;
522 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
523 	uint16_t	control, status;
524 	ddi_hp_cn_state_t curr_state = slot_p->hs_info.cn_state;
525 
526 	/* read the Slot Control Register */
527 	control = pciehpc_reg_get16(ctrl_p,
528 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
529 
530 	slot_p->hs_fault_led_state = PCIE_HP_LED_OFF; /* no fault led */
531 	slot_p->hs_active_led_state = PCIE_HP_LED_OFF; /* no active led */
532 
533 	/* read the current Slot Status Register */
534 	status = pciehpc_reg_get16(ctrl_p,
535 	    bus_p->bus_pcie_off + PCIE_SLOTSTS);
536 
537 	/* get POWER led state */
538 	slot_p->hs_power_led_state =
539 	    pciehpc_led_state_to_hpc(pcie_slotctl_pwr_indicator_get(control));
540 
541 	/* get ATTN led state */
542 	slot_p->hs_attn_led_state =
543 	    pciehpc_led_state_to_hpc(pcie_slotctl_attn_indicator_get(control));
544 
545 	if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
546 		/* no device present; slot is empty */
547 		slot_p->hs_info.cn_state = DDI_HP_CN_STATE_EMPTY;
548 
549 		return;
550 	}
551 
552 	/* device is present */
553 	slot_p->hs_info.cn_state = DDI_HP_CN_STATE_PRESENT;
554 
555 	if (!(control & PCIE_SLOTCTL_PWR_CONTROL)) {
556 		/*
557 		 * Device is powered on. Set to "ENABLED" state (skip
558 		 * POWERED state) because there is not a explicit "enable"
559 		 * action exists for PCIe.
560 		 * If it is already in "POWERED" state, then keep it until
561 		 * user explicitly change it to other states.
562 		 */
563 		if (curr_state == DDI_HP_CN_STATE_POWERED) {
564 			slot_p->hs_info.cn_state = curr_state;
565 		} else {
566 			slot_p->hs_info.cn_state = DDI_HP_CN_STATE_ENABLED;
567 		}
568 	}
569 }
570 
571 /*
572  * setup slot name/slot-number info.
573  */
574 void
575 pciehpc_set_slot_name(pcie_hp_ctrl_t *ctrl_p)
576 {
577 	pcie_hp_slot_t	*slot_p = ctrl_p->hc_slots[0];
578 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
579 	uchar_t		*slotname_data;
580 	int		*slotnum;
581 	uint_t		count;
582 	int		len;
583 	int		invalid_slotnum = 0;
584 	uint32_t	slot_capabilities;
585 
586 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, ctrl_p->hc_dip,
587 	    DDI_PROP_DONTPASS, "physical-slot#", &slotnum, &count) ==
588 	    DDI_PROP_SUCCESS) {
589 		slot_p->hs_phy_slot_num = slotnum[0];
590 		ddi_prop_free(slotnum);
591 	} else {
592 		slot_capabilities = pciehpc_reg_get32(ctrl_p,
593 		    bus_p->bus_pcie_off + PCIE_SLOTCAP);
594 		slot_p->hs_phy_slot_num =
595 		    PCIE_SLOTCAP_PHY_SLOT_NUM(slot_capabilities);
596 	}
597 
598 	/* platform may not have initialized it */
599 	if (!slot_p->hs_phy_slot_num) {
600 		PCIE_DBG("%s#%d: Invalid slot number!\n",
601 		    ddi_driver_name(ctrl_p->hc_dip),
602 		    ddi_get_instance(ctrl_p->hc_dip));
603 		slot_p->hs_phy_slot_num = pciehpc_reg_get8(ctrl_p,
604 		    PCI_BCNF_SECBUS);
605 		invalid_slotnum = 1;
606 	}
607 	slot_p->hs_info.cn_num = slot_p->hs_phy_slot_num;
608 	slot_p->hs_info.cn_num_dpd_on = DDI_HP_CN_NUM_NONE;
609 
610 	/*
611 	 * construct the slot_name:
612 	 *	if "slot-names" property exists then use that name
613 	 *	else if valid slot number exists then it is "pcie<slot-num>".
614 	 *	else it will be "pcie<sec-bus-number>dev0"
615 	 */
616 	if (ddi_getlongprop(DDI_DEV_T_ANY, ctrl_p->hc_dip, DDI_PROP_DONTPASS,
617 	    "slot-names", (caddr_t)&slotname_data, &len) == DDI_PROP_SUCCESS) {
618 		char tmp_name[256];
619 
620 		/*
621 		 * Note: for PCI-E slots, the device number is always 0 so the
622 		 * first (and only) string is the slot name for this slot.
623 		 */
624 		(void) snprintf(tmp_name, sizeof (tmp_name),
625 		    (char *)slotname_data + 4);
626 		slot_p->hs_info.cn_name = ddi_strdup(tmp_name, KM_SLEEP);
627 		kmem_free(slotname_data, len);
628 	} else {
629 		if (invalid_slotnum) {
630 			/* use device number ie. 0 */
631 			slot_p->hs_info.cn_name = ddi_strdup("pcie0",
632 			    KM_SLEEP);
633 		} else {
634 			char tmp_name[256];
635 
636 			(void) snprintf(tmp_name, sizeof (tmp_name), "pcie%d",
637 			    slot_p->hs_phy_slot_num);
638 			slot_p->hs_info.cn_name = ddi_strdup(tmp_name,
639 			    KM_SLEEP);
640 		}
641 	}
642 }
643 
644 /*
645  * Read/Write access to HPC registers. If platform nexus has non-standard
646  * HPC access mechanism then regops functions are used to do reads/writes.
647  */
648 uint8_t
649 pciehpc_reg_get8(pcie_hp_ctrl_t *ctrl_p, uint_t off)
650 {
651 	if (ctrl_p->hc_regops.get != NULL) {
652 		return ((uint8_t)ctrl_p->hc_regops.get(
653 		    ctrl_p->hc_regops.cookie, (off_t)off));
654 	} else {
655 		pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
656 
657 		return (pci_config_get8(bus_p->bus_cfg_hdl, off));
658 	}
659 }
660 
661 uint16_t
662 pciehpc_reg_get16(pcie_hp_ctrl_t *ctrl_p, uint_t off)
663 {
664 	if (ctrl_p->hc_regops.get != NULL) {
665 		return ((uint16_t)ctrl_p->hc_regops.get(
666 		    ctrl_p->hc_regops.cookie, (off_t)off));
667 	} else {
668 		pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
669 
670 		return (pci_config_get16(bus_p->bus_cfg_hdl, off));
671 	}
672 }
673 
674 uint32_t
675 pciehpc_reg_get32(pcie_hp_ctrl_t *ctrl_p, uint_t off)
676 {
677 	if (ctrl_p->hc_regops.get != NULL) {
678 		return ((uint32_t)ctrl_p->hc_regops.get(
679 		    ctrl_p->hc_regops.cookie, (off_t)off));
680 	} else {
681 		pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
682 
683 		return (pci_config_get32(bus_p->bus_cfg_hdl, off));
684 	}
685 }
686 
687 void
688 pciehpc_reg_put8(pcie_hp_ctrl_t *ctrl_p, uint_t off, uint8_t val)
689 {
690 	if (ctrl_p->hc_regops.put != NULL) {
691 		ctrl_p->hc_regops.put(ctrl_p->hc_regops.cookie,
692 		    (off_t)off, (uint_t)val);
693 	} else {
694 		pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
695 
696 		pci_config_put8(bus_p->bus_cfg_hdl, off, val);
697 	}
698 }
699 
700 void
701 pciehpc_reg_put16(pcie_hp_ctrl_t *ctrl_p, uint_t off, uint16_t val)
702 {
703 	if (ctrl_p->hc_regops.put != NULL) {
704 		ctrl_p->hc_regops.put(ctrl_p->hc_regops.cookie,
705 		    (off_t)off, (uint_t)val);
706 	} else {
707 		pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
708 
709 		pci_config_put16(bus_p->bus_cfg_hdl, off, val);
710 	}
711 }
712 
713 void
714 pciehpc_reg_put32(pcie_hp_ctrl_t *ctrl_p, uint_t off, uint32_t val)
715 {
716 	if (ctrl_p->hc_regops.put != NULL) {
717 		ctrl_p->hc_regops.put(ctrl_p->hc_regops.cookie,
718 		    (off_t)off, (uint_t)val);
719 	} else {
720 		pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
721 
722 		pci_config_put32(bus_p->bus_cfg_hdl, off, val);
723 	}
724 }
725 
726 /*
727  * ************************************************************************
728  * ***	Local functions (called within this file)
729  * ***	PCIe Native Hotplug mode specific functions
730  * ************************************************************************
731  */
732 
733 /*
734  * Initialize HPC hardware, install interrupt handler, etc. It doesn't
735  * enable hot plug interrupts.
736  *
737  * (Note: It is called only from pciehpc_init().)
738  */
739 static int
740 pciehpc_hpc_init(pcie_hp_ctrl_t *ctrl_p)
741 {
742 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
743 	uint16_t	reg;
744 
745 	/* read the Slot Control Register */
746 	reg = pciehpc_reg_get16(ctrl_p,
747 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
748 
749 	/* disable all interrupts */
750 	reg &= ~(PCIE_SLOTCTL_INTR_MASK);
751 	pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off +
752 	    PCIE_SLOTCTL, reg);
753 
754 	/* clear any interrupt status bits */
755 	reg = pciehpc_reg_get16(ctrl_p,
756 	    bus_p->bus_pcie_off + PCIE_SLOTSTS);
757 	pciehpc_reg_put16(ctrl_p,
758 	    bus_p->bus_pcie_off + PCIE_SLOTSTS, reg);
759 
760 	return (DDI_SUCCESS);
761 }
762 
763 /*
764  * Uninitialize HPC hardware, uninstall interrupt handler, etc.
765  *
766  * (Note: It is called only from pciehpc_uninit().)
767  */
768 static int
769 pciehpc_hpc_uninit(pcie_hp_ctrl_t *ctrl_p)
770 {
771 	/* disable interrupts */
772 	(void) pciehpc_disable_intr(ctrl_p);
773 
774 	return (DDI_SUCCESS);
775 }
776 
777 /*
778  * Setup slot information for use with DDI HP framework.
779  */
780 static int
781 pciehpc_slotinfo_init(pcie_hp_ctrl_t *ctrl_p)
782 {
783 	uint32_t	slot_capabilities, link_capabilities;
784 	pcie_hp_slot_t	*slot_p = ctrl_p->hc_slots[0];
785 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
786 
787 	mutex_enter(&ctrl_p->hc_mutex);
788 	/*
789 	 * setup DDI HP framework slot information structure
790 	 */
791 	slot_p->hs_device_num = 0;
792 
793 	slot_p->hs_info.cn_type = DDI_HP_CN_TYPE_PCIE;
794 	slot_p->hs_info.cn_type_str = (ctrl_p->hc_regops.get == NULL) ?
795 	    PCIE_NATIVE_HP_TYPE : PCIE_PROP_HP_TYPE;
796 	slot_p->hs_info.cn_child = NULL;
797 
798 	slot_p->hs_minor =
799 	    PCI_MINOR_NUM(ddi_get_instance(ctrl_p->hc_dip),
800 	    slot_p->hs_device_num);
801 	slot_p->hs_condition = AP_COND_UNKNOWN;
802 
803 	/* read Slot Capabilities Register */
804 	slot_capabilities = pciehpc_reg_get32(ctrl_p,
805 	    bus_p->bus_pcie_off + PCIE_SLOTCAP);
806 
807 	/* set slot-name/slot-number info */
808 	pciehpc_set_slot_name(ctrl_p);
809 
810 	/* check if Attn Button present */
811 	ctrl_p->hc_has_attn = (slot_capabilities & PCIE_SLOTCAP_ATTN_BUTTON) ?
812 	    B_TRUE : B_FALSE;
813 
814 	/* check if Manual Retention Latch sensor present */
815 	ctrl_p->hc_has_mrl = (slot_capabilities & PCIE_SLOTCAP_MRL_SENSOR) ?
816 	    B_TRUE : B_FALSE;
817 
818 	/*
819 	 * Contrary to what one might expect, not all systems actually have
820 	 * power control despite having hot-swap capabilities. This is most
821 	 * commonly due to the Enterprise SSD specification which doesn't call
822 	 * for power-control in the PCIe native hotplug implementation.
823 	 */
824 	ctrl_p->hc_has_pwr = (slot_capabilities &
825 	    PCIE_SLOTCAP_POWER_CONTROLLER) ? B_TRUE: B_FALSE;
826 
827 	/*
828 	 * PCI-E version 1.1 defines EMI Lock Present bit
829 	 * in Slot Capabilities register. Check for it.
830 	 */
831 	ctrl_p->hc_has_emi_lock = (slot_capabilities &
832 	    PCIE_SLOTCAP_EMI_LOCK_PRESENT) ? B_TRUE : B_FALSE;
833 
834 	link_capabilities = pciehpc_reg_get32(ctrl_p,
835 	    bus_p->bus_pcie_off + PCIE_LINKCAP);
836 	ctrl_p->hc_dll_active_rep = (link_capabilities &
837 	    PCIE_LINKCAP_DLL_ACTIVE_REP_CAPABLE) ? B_TRUE : B_FALSE;
838 	if (ctrl_p->hc_dll_active_rep)
839 		cv_init(&slot_p->hs_dll_active_cv, NULL, CV_DRIVER, NULL);
840 
841 	/* setup thread for handling ATTN button events */
842 	if (ctrl_p->hc_has_attn) {
843 		PCIE_DBG("pciehpc_slotinfo_init: setting up ATTN button event "
844 		    "handler thread for slot %d\n", slot_p->hs_phy_slot_num);
845 
846 		cv_init(&slot_p->hs_attn_btn_cv, NULL, CV_DRIVER, NULL);
847 		slot_p->hs_attn_btn_pending = B_FALSE;
848 		slot_p->hs_attn_btn_threadp = thread_create(NULL, 0,
849 		    pciehpc_attn_btn_handler,
850 		    (void *)ctrl_p, 0, &p0, TS_RUN, minclsyspri);
851 		slot_p->hs_attn_btn_thread_exit = B_FALSE;
852 	}
853 
854 	/* get current slot state from the hw */
855 	slot_p->hs_info.cn_state = DDI_HP_CN_STATE_EMPTY;
856 	pciehpc_get_slot_state(slot_p);
857 	if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_ENABLED)
858 		slot_p->hs_condition = AP_COND_OK;
859 
860 	mutex_exit(&ctrl_p->hc_mutex);
861 
862 	return (DDI_SUCCESS);
863 }
864 
865 /*ARGSUSED*/
866 static int
867 pciehpc_slotinfo_uninit(pcie_hp_ctrl_t *ctrl_p)
868 {
869 	pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
870 
871 	if (slot_p->hs_attn_btn_threadp != NULL) {
872 		mutex_enter(&ctrl_p->hc_mutex);
873 		slot_p->hs_attn_btn_thread_exit = B_TRUE;
874 		cv_signal(&slot_p->hs_attn_btn_cv);
875 		PCIE_DBG("pciehpc_slotinfo_uninit: "
876 		    "waiting for ATTN thread exit\n");
877 		cv_wait(&slot_p->hs_attn_btn_cv, &ctrl_p->hc_mutex);
878 		PCIE_DBG("pciehpc_slotinfo_uninit: ATTN thread exit\n");
879 		cv_destroy(&slot_p->hs_attn_btn_cv);
880 		slot_p->hs_attn_btn_threadp = NULL;
881 		mutex_exit(&ctrl_p->hc_mutex);
882 	}
883 
884 	if (ctrl_p->hc_dll_active_rep)
885 		cv_destroy(&slot_p->hs_dll_active_cv);
886 	if (slot_p->hs_info.cn_name)
887 		kmem_free(slot_p->hs_info.cn_name,
888 		    strlen(slot_p->hs_info.cn_name) + 1);
889 
890 	return (DDI_SUCCESS);
891 }
892 
893 /*
894  * Enable hot plug interrupts.
895  * Note: this is only for Native hot plug mode.
896  */
897 static int
898 pciehpc_enable_intr(pcie_hp_ctrl_t *ctrl_p)
899 {
900 	pcie_hp_slot_t	*slot_p = ctrl_p->hc_slots[0];
901 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
902 	uint16_t	reg;
903 	uint16_t	intr_mask = PCIE_SLOTCTL_INTR_MASK;
904 
905 	/*
906 	 * power fault detection interrupt is enabled only
907 	 * when the slot is powered ON
908 	 */
909 	if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED)
910 		intr_mask &= ~PCIE_SLOTCTL_PWR_FAULT_EN;
911 
912 	/*
913 	 * enable interrupt sources but leave the top-level
914 	 * interrupt disabled. some sources may generate a
915 	 * spurrious event when they are first enabled.
916 	 * by leaving the top-level interrupt disabled, those
917 	 * can be cleared first.
918 	 */
919 	reg = pciehpc_reg_get16(ctrl_p,
920 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
921 	pciehpc_reg_put16(ctrl_p,
922 	    bus_p->bus_pcie_off + PCIE_SLOTCTL,
923 	    reg | (intr_mask & ~PCIE_SLOTCTL_HP_INTR_EN));
924 
925 	/* clear any interrupt status bits */
926 	reg = pciehpc_reg_get16(ctrl_p,
927 	    bus_p->bus_pcie_off + PCIE_SLOTSTS);
928 	pciehpc_reg_put16(ctrl_p,
929 	    bus_p->bus_pcie_off + PCIE_SLOTSTS, reg);
930 
931 	/* enable top-level interrupt */
932 	reg = pciehpc_reg_get16(ctrl_p,
933 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
934 	pciehpc_reg_put16(ctrl_p,
935 	    bus_p->bus_pcie_off + PCIE_SLOTCTL,
936 	    reg | intr_mask);
937 
938 	return (DDI_SUCCESS);
939 }
940 
941 /*
942  * Disable hot plug interrupts.
943  * Note: this is only for Native hot plug mode.
944  */
945 static int
946 pciehpc_disable_intr(pcie_hp_ctrl_t *ctrl_p)
947 {
948 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
949 	uint16_t	reg;
950 
951 	/* read the Slot Control Register */
952 	reg = pciehpc_reg_get16(ctrl_p,
953 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
954 
955 	/* disable all interrupts */
956 	reg &= ~(PCIE_SLOTCTL_INTR_MASK);
957 	pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off + PCIE_SLOTCTL, reg);
958 
959 	/* clear any interrupt status bits */
960 	reg = pciehpc_reg_get16(ctrl_p,
961 	    bus_p->bus_pcie_off + PCIE_SLOTSTS);
962 	pciehpc_reg_put16(ctrl_p,
963 	    bus_p->bus_pcie_off + PCIE_SLOTSTS, reg);
964 
965 	return (DDI_SUCCESS);
966 }
967 
968 /*
969  * Allocate a new hotplug controller and slot structures for HPC
970  * associated with this dip.
971  */
972 static pcie_hp_ctrl_t *
973 pciehpc_create_controller(dev_info_t *dip)
974 {
975 	pcie_hp_ctrl_t	*ctrl_p;
976 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
977 
978 	ctrl_p = kmem_zalloc(sizeof (pcie_hp_ctrl_t), KM_SLEEP);
979 	ctrl_p->hc_dip = dip;
980 
981 	/* Allocate a new slot structure. */
982 	ctrl_p->hc_slots[0] = kmem_zalloc(sizeof (pcie_hp_slot_t), KM_SLEEP);
983 	ctrl_p->hc_slots[0]->hs_num = 0;
984 	ctrl_p->hc_slots[0]->hs_ctrl = ctrl_p;
985 
986 	/* Initialize the interrupt mutex */
987 	mutex_init(&ctrl_p->hc_mutex, NULL, MUTEX_DRIVER,
988 	    (void *)PCIE_INTR_PRI);
989 
990 	/* Initialize synchronization conditional variable */
991 	cv_init(&ctrl_p->hc_cmd_comp_cv, NULL, CV_DRIVER, NULL);
992 	ctrl_p->hc_cmd_pending = B_FALSE;
993 
994 	bus_p->bus_hp_curr_mode = PCIE_NATIVE_HP_MODE;
995 	PCIE_SET_HP_CTRL(dip, ctrl_p);
996 
997 	return (ctrl_p);
998 }
999 
1000 /*
1001  * Remove the HPC controller and slot structures
1002  */
1003 static void
1004 pciehpc_destroy_controller(dev_info_t *dip)
1005 {
1006 	pcie_hp_ctrl_t	*ctrl_p;
1007 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
1008 
1009 	/* get the soft state structure for this dip */
1010 	if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL)
1011 		return;
1012 
1013 	PCIE_SET_HP_CTRL(dip, NULL);
1014 	bus_p->bus_hp_curr_mode = PCIE_NONE_HP_MODE;
1015 
1016 	mutex_destroy(&ctrl_p->hc_mutex);
1017 	cv_destroy(&ctrl_p->hc_cmd_comp_cv);
1018 	kmem_free(ctrl_p->hc_slots[0], sizeof (pcie_hp_slot_t));
1019 	kmem_free(ctrl_p, sizeof (pcie_hp_ctrl_t));
1020 }
1021 
1022 /*
1023  * Register the PCI-E hot plug slot with DDI HP framework.
1024  */
1025 static int
1026 pciehpc_register_slot(pcie_hp_ctrl_t *ctrl_p)
1027 {
1028 	pcie_hp_slot_t	*slot_p = ctrl_p->hc_slots[0];
1029 	dev_info_t	*dip = ctrl_p->hc_dip;
1030 
1031 	/* register the slot with DDI HP framework */
1032 	if (ndi_hp_register(dip, &slot_p->hs_info) != NDI_SUCCESS) {
1033 		PCIE_DBG("pciehpc_register_slot() failed to register slot %d\n",
1034 		    slot_p->hs_phy_slot_num);
1035 		return (DDI_FAILURE);
1036 	}
1037 
1038 	pcie_hp_create_occupant_props(dip, makedevice(ddi_driver_major(dip),
1039 	    slot_p->hs_minor), slot_p->hs_device_num);
1040 
1041 	PCIE_DBG("pciehpc_register_slot(): registered slot %d\n",
1042 	    slot_p->hs_phy_slot_num);
1043 
1044 	return (DDI_SUCCESS);
1045 }
1046 
1047 /*
1048  * Unregister the PCI-E hot plug slot from DDI HP framework.
1049  */
1050 static int
1051 pciehpc_unregister_slot(pcie_hp_ctrl_t *ctrl_p)
1052 {
1053 	pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
1054 	dev_info_t	*dip = ctrl_p->hc_dip;
1055 
1056 	pcie_hp_delete_occupant_props(dip, makedevice(ddi_driver_major(dip),
1057 	    slot_p->hs_minor));
1058 
1059 	/* unregister the slot with DDI HP framework */
1060 	if (ndi_hp_unregister(dip, slot_p->hs_info.cn_name) != NDI_SUCCESS) {
1061 		PCIE_DBG("pciehpc_unregister_slot() "
1062 		    "failed to unregister slot %d\n", slot_p->hs_phy_slot_num);
1063 		return (DDI_FAILURE);
1064 	}
1065 
1066 	PCIE_DBG("pciehpc_unregister_slot(): unregistered slot %d\n",
1067 	    slot_p->hs_phy_slot_num);
1068 
1069 	return (DDI_SUCCESS);
1070 }
1071 
1072 /*
1073  * pciehpc_slot_poweron()
1074  *
1075  * Poweron/Enable the slot.
1076  *
1077  * Note: This function is called by DDI HP framework at kernel context only
1078  */
1079 /*ARGSUSED*/
1080 static int
1081 pciehpc_slot_poweron(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result)
1082 {
1083 	pcie_hp_ctrl_t	*ctrl_p = slot_p->hs_ctrl;
1084 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
1085 	uint16_t	status, control;
1086 
1087 	ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
1088 
1089 	/* get the current state of the slot */
1090 	pciehpc_get_slot_state(slot_p);
1091 
1092 	/* check if the slot is already in the 'enabled' state */
1093 	if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED) {
1094 		/* slot is already in the 'enabled' state */
1095 		PCIE_DBG("pciehpc_slot_poweron() slot %d already enabled\n",
1096 		    slot_p->hs_phy_slot_num);
1097 
1098 		*result = slot_p->hs_info.cn_state;
1099 		return (DDI_SUCCESS);
1100 	}
1101 
1102 	/* read the Slot Status Register */
1103 	status =  pciehpc_reg_get16(ctrl_p,
1104 	    bus_p->bus_pcie_off + PCIE_SLOTSTS);
1105 
1106 	/* make sure the MRL switch is closed if present */
1107 	if ((ctrl_p->hc_has_mrl) && (status & PCIE_SLOTSTS_MRL_SENSOR_OPEN)) {
1108 		/* MRL switch is open */
1109 		cmn_err(CE_WARN, "MRL switch is open on slot %d\n",
1110 		    slot_p->hs_phy_slot_num);
1111 		goto cleanup;
1112 	}
1113 
1114 	/* make sure the slot has a device present */
1115 	if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
1116 		/* slot is empty */
1117 		PCIE_DBG("slot %d is empty\n", slot_p->hs_phy_slot_num);
1118 		goto cleanup;
1119 	}
1120 
1121 	/*
1122 	 * If the hardware doesn't have support for a power controller, then
1123 	 * that generally means that power is already on or at the least there
1124 	 * isn't very much else we can do and the PCIe spec says it's the
1125 	 * responsibility of the controller to have turned it on if a device is
1126 	 * present. Given that we checked for presence above, in such a case we
1127 	 * just return. Note, we still indicate that is a failure since we can't
1128 	 * change it and instead rely on code executing the actual state machine
1129 	 * to figure out how to handle this.
1130 	 */
1131 	if (!ctrl_p->hc_has_pwr) {
1132 		PCIE_DBG("slot %d has no power, but was asked to power on\n",
1133 		    slot_p->hs_phy_slot_num);
1134 		return (DDI_FAILURE);
1135 	}
1136 
1137 	/* get the current state of Slot Control Register */
1138 	control =  pciehpc_reg_get16(ctrl_p,
1139 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
1140 
1141 	/*
1142 	 * Enable power to the slot involves:
1143 	 *	1. Set power LED to blink and ATTN led to OFF.
1144 	 *	2. Set power control ON in Slot Control Reigster and
1145 	 *	   wait for Command Completed Interrupt or 1 sec timeout.
1146 	 *	3. If Data Link Layer State Changed events are supported
1147 	 *	   then wait for the event to indicate Data Layer Link
1148 	 *	   is active. The time out value for this event is 1 second.
1149 	 *	   This is specified in PCI-E version 1.1.
1150 	 *	4. Set power LED to be ON.
1151 	 */
1152 
1153 	/* 1. set power LED to blink & ATTN led to OFF */
1154 	pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED, PCIE_HP_LED_BLINK);
1155 	pciehpc_set_led_state(ctrl_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_OFF);
1156 
1157 	/* 2. set power control to ON */
1158 	control =  pciehpc_reg_get16(ctrl_p,
1159 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
1160 	control &= ~PCIE_SLOTCTL_PWR_CONTROL;
1161 	pciehpc_issue_hpc_command(ctrl_p, control);
1162 
1163 	/* 3. wait for DLL State Change event, if it's supported */
1164 	if (ctrl_p->hc_dll_active_rep) {
1165 		clock_t deadline;
1166 
1167 		/* wait 1 sec for the DLL State Changed event */
1168 		status = pciehpc_reg_get16(ctrl_p,
1169 		    bus_p->bus_pcie_off + PCIE_LINKSTS);
1170 
1171 		deadline = ddi_get_lbolt() +
1172 		    SEC_TO_TICK(PCIE_HP_DLL_STATE_CHANGE_TIMEOUT);
1173 
1174 		while ((status & PCIE_LINKSTS_DLL_LINK_ACTIVE) == 0 &&
1175 		    ddi_get_lbolt() < deadline) {
1176 			(void) cv_timedwait(&slot_p->hs_dll_active_cv,
1177 			    &ctrl_p->hc_mutex, deadline);
1178 
1179 			/* check Link status */
1180 			status =  pciehpc_reg_get16(ctrl_p,
1181 			    bus_p->bus_pcie_off +
1182 			    PCIE_LINKSTS);
1183 		}
1184 
1185 		if ((status & PCIE_LINKSTS_DLL_LINK_ACTIVE) == 0)
1186 			goto cleanup2;
1187 	} else {
1188 		/* wait 1 sec for link to come up */
1189 		delay(drv_usectohz(1000000));
1190 	}
1191 
1192 	/* check power is really turned ON */
1193 	control =  pciehpc_reg_get16(ctrl_p,
1194 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
1195 
1196 	if (control & PCIE_SLOTCTL_PWR_CONTROL) {
1197 		PCIE_DBG("slot %d fails to turn on power on connect\n",
1198 		    slot_p->hs_phy_slot_num);
1199 
1200 		goto cleanup1;
1201 	}
1202 
1203 	/* clear power fault status */
1204 	status =  pciehpc_reg_get16(ctrl_p,
1205 	    bus_p->bus_pcie_off + PCIE_SLOTSTS);
1206 	status |= PCIE_SLOTSTS_PWR_FAULT_DETECTED;
1207 	pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off + PCIE_SLOTSTS,
1208 	    status);
1209 
1210 	/* enable power fault detection interrupt */
1211 	control |= PCIE_SLOTCTL_PWR_FAULT_EN;
1212 	pciehpc_issue_hpc_command(ctrl_p, control);
1213 
1214 	/* 4. Set power LED to be ON */
1215 	pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED, PCIE_HP_LED_ON);
1216 
1217 	/* if EMI is present, turn it ON */
1218 	if (ctrl_p->hc_has_emi_lock) {
1219 		status =  pciehpc_reg_get16(ctrl_p,
1220 		    bus_p->bus_pcie_off + PCIE_SLOTSTS);
1221 
1222 		if (!(status & PCIE_SLOTSTS_EMI_LOCK_SET)) {
1223 			control =  pciehpc_reg_get16(ctrl_p,
1224 			    bus_p->bus_pcie_off + PCIE_SLOTCTL);
1225 			control |= PCIE_SLOTCTL_EMI_LOCK_CONTROL;
1226 			pciehpc_issue_hpc_command(ctrl_p, control);
1227 
1228 			/* wait 1 sec after toggling the state of EMI lock */
1229 			delay(drv_usectohz(1000000));
1230 		}
1231 	}
1232 
1233 	*result = slot_p->hs_info.cn_state =
1234 	    DDI_HP_CN_STATE_POWERED;
1235 
1236 	return (DDI_SUCCESS);
1237 
1238 cleanup2:
1239 	control =  pciehpc_reg_get16(ctrl_p,
1240 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
1241 
1242 	/* if power is ON, set power control to OFF */
1243 	if (!(control & PCIE_SLOTCTL_PWR_CONTROL)) {
1244 		control |= PCIE_SLOTCTL_PWR_CONTROL;
1245 		pciehpc_issue_hpc_command(ctrl_p, control);
1246 	}
1247 
1248 cleanup1:
1249 	/* set power led to OFF */
1250 	pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED, PCIE_HP_LED_OFF);
1251 
1252 cleanup:
1253 	return (DDI_FAILURE);
1254 }
1255 
1256 /*ARGSUSED*/
1257 static int
1258 pciehpc_slot_poweroff(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result)
1259 {
1260 	pcie_hp_ctrl_t	*ctrl_p = slot_p->hs_ctrl;
1261 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
1262 	uint16_t	status, control;
1263 
1264 	ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
1265 
1266 	/* get the current state of the slot */
1267 	pciehpc_get_slot_state(slot_p);
1268 
1269 	/* check if the slot is not in the "enabled' state */
1270 	if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED) {
1271 		/* slot is in the 'disabled' state */
1272 		PCIE_DBG("pciehpc_slot_poweroff(): "
1273 		    "slot %d already disabled\n", slot_p->hs_phy_slot_num);
1274 		ASSERT(slot_p->hs_power_led_state == PCIE_HP_LED_OFF);
1275 
1276 		*result = slot_p->hs_info.cn_state;
1277 		return (DDI_SUCCESS);
1278 	}
1279 
1280 	/* read the Slot Status Register */
1281 	status =  pciehpc_reg_get16(ctrl_p,
1282 	    bus_p->bus_pcie_off + PCIE_SLOTSTS);
1283 
1284 	/* make sure the slot has a device present */
1285 	if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
1286 		/* slot is empty */
1287 		PCIE_DBG("pciehpc_slot_poweroff(): slot %d is empty\n",
1288 		    slot_p->hs_phy_slot_num);
1289 		goto cleanup;
1290 	}
1291 
1292 	/*
1293 	 * Some devices do not have a power controller. In such cases we need to
1294 	 * fail any request to power it off. If a device is being pulled, the
1295 	 * state will generally have automatically been updated; however, if
1296 	 * someone is asking for us to do something via an explicit request,
1297 	 * then this will fail. Note, this is after the presence check to ensure
1298 	 * that an empty slot is accounted for correctly.
1299 	 */
1300 	if (!ctrl_p->hc_has_pwr) {
1301 		PCIE_DBG("pciehpc_slot_poweroff(): slot %d doesn't have a "
1302 		    "power controller\n",
1303 		    slot_p->hs_phy_slot_num);
1304 		return (DDI_ENOTSUP);
1305 	}
1306 
1307 	/*
1308 	 * Disable power to the slot involves:
1309 	 *	1. Set power LED to blink.
1310 	 *	2. Set power control OFF in Slot Control Reigster and
1311 	 *	   wait for Command Completed Interrupt or 1 sec timeout.
1312 	 *	3. Set POWER led and ATTN led to be OFF.
1313 	 */
1314 
1315 	/* 1. set power LED to blink */
1316 	pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED, PCIE_HP_LED_BLINK);
1317 
1318 	/* disable power fault detection interrupt */
1319 	control = pciehpc_reg_get16(ctrl_p,
1320 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
1321 	control &= ~PCIE_SLOTCTL_PWR_FAULT_EN;
1322 	pciehpc_issue_hpc_command(ctrl_p, control);
1323 
1324 	/* 2. set power control to OFF */
1325 	control =  pciehpc_reg_get16(ctrl_p,
1326 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
1327 	control |= PCIE_SLOTCTL_PWR_CONTROL;
1328 	pciehpc_issue_hpc_command(ctrl_p, control);
1329 
1330 #ifdef DEBUG
1331 	/* check for power control bit to be OFF */
1332 	control =  pciehpc_reg_get16(ctrl_p,
1333 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
1334 	ASSERT(control & PCIE_SLOTCTL_PWR_CONTROL);
1335 #endif
1336 
1337 	/* 3. Set power LED to be OFF */
1338 	pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED, PCIE_HP_LED_OFF);
1339 	pciehpc_set_led_state(ctrl_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_OFF);
1340 
1341 	/* if EMI is present, turn it OFF */
1342 	if (ctrl_p->hc_has_emi_lock) {
1343 		status =  pciehpc_reg_get16(ctrl_p,
1344 		    bus_p->bus_pcie_off + PCIE_SLOTSTS);
1345 
1346 		if (status & PCIE_SLOTSTS_EMI_LOCK_SET) {
1347 			control =  pciehpc_reg_get16(ctrl_p,
1348 			    bus_p->bus_pcie_off + PCIE_SLOTCTL);
1349 			control |= PCIE_SLOTCTL_EMI_LOCK_CONTROL;
1350 			pciehpc_issue_hpc_command(ctrl_p, control);
1351 
1352 			/* wait 1 sec after toggling the state of EMI lock */
1353 			delay(drv_usectohz(1000000));
1354 		}
1355 	}
1356 
1357 	/* get the current state of the slot */
1358 	pciehpc_get_slot_state(slot_p);
1359 
1360 	*result = slot_p->hs_info.cn_state;
1361 
1362 	return (DDI_SUCCESS);
1363 
1364 cleanup:
1365 	return (DDI_FAILURE);
1366 }
1367 
1368 /*
1369  * pciehpc_slot_probe()
1370  *
1371  * Probe the slot.
1372  *
1373  * Note: This function is called by DDI HP framework at kernel context only
1374  */
1375 /*ARGSUSED*/
1376 static int
1377 pciehpc_slot_probe(pcie_hp_slot_t *slot_p)
1378 {
1379 	pcie_hp_ctrl_t	*ctrl_p = slot_p->hs_ctrl;
1380 	int		ret = DDI_SUCCESS;
1381 
1382 	mutex_enter(&ctrl_p->hc_mutex);
1383 
1384 	/* get the current state of the slot */
1385 	pciehpc_get_slot_state(slot_p);
1386 
1387 	/*
1388 	 * Probe a given PCIe Hotplug Connection (CN).
1389 	 */
1390 	PCIE_DISABLE_ERRORS(ctrl_p->hc_dip);
1391 	ret = pcie_hp_probe(slot_p);
1392 
1393 	if (ret != DDI_SUCCESS) {
1394 		PCIE_DBG("pciehpc_slot_probe() failed\n");
1395 
1396 		/* turn the ATTN led ON for configure failure */
1397 		pciehpc_set_led_state(ctrl_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_ON);
1398 
1399 		/* if power to the slot is still on then set Power led to ON */
1400 		if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED)
1401 			pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED,
1402 			    PCIE_HP_LED_ON);
1403 
1404 		mutex_exit(&ctrl_p->hc_mutex);
1405 		return (DDI_FAILURE);
1406 	}
1407 
1408 	PCIE_ENABLE_ERRORS(ctrl_p->hc_dip);
1409 
1410 	/* get the current state of the slot */
1411 	pciehpc_get_slot_state(slot_p);
1412 
1413 	mutex_exit(&ctrl_p->hc_mutex);
1414 	return (DDI_SUCCESS);
1415 }
1416 
1417 /*
1418  * pciehpc_slot_unprobe()
1419  *
1420  * Unprobe the slot.
1421  *
1422  * Note: This function is called by DDI HP framework at kernel context only
1423  */
1424 /*ARGSUSED*/
1425 static int
1426 pciehpc_slot_unprobe(pcie_hp_slot_t *slot_p)
1427 {
1428 	pcie_hp_ctrl_t	*ctrl_p = slot_p->hs_ctrl;
1429 	int		ret;
1430 
1431 	mutex_enter(&ctrl_p->hc_mutex);
1432 
1433 	/* get the current state of the slot */
1434 	pciehpc_get_slot_state(slot_p);
1435 
1436 	/*
1437 	 * Unprobe a given PCIe Hotplug Connection (CN).
1438 	 */
1439 	PCIE_DISABLE_ERRORS(ctrl_p->hc_dip);
1440 	ret = pcie_hp_unprobe(slot_p);
1441 
1442 	if (ret != DDI_SUCCESS) {
1443 		PCIE_DBG("pciehpc_slot_unprobe() failed\n");
1444 
1445 		/* if power to the slot is still on then set Power led to ON */
1446 		if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED)
1447 			pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED,
1448 			    PCIE_HP_LED_ON);
1449 
1450 		PCIE_ENABLE_ERRORS(ctrl_p->hc_dip);
1451 
1452 		mutex_exit(&ctrl_p->hc_mutex);
1453 		return (DDI_FAILURE);
1454 	}
1455 
1456 	/* get the current state of the slot */
1457 	pciehpc_get_slot_state(slot_p);
1458 
1459 	mutex_exit(&ctrl_p->hc_mutex);
1460 	return (DDI_SUCCESS);
1461 }
1462 
1463 static int
1464 pciehpc_upgrade_slot_state(pcie_hp_slot_t *slot_p,
1465     ddi_hp_cn_state_t target_state)
1466 {
1467 	ddi_hp_cn_state_t curr_state;
1468 	int rv = DDI_SUCCESS;
1469 
1470 	if (target_state > DDI_HP_CN_STATE_ENABLED) {
1471 		return (DDI_EINVAL);
1472 	}
1473 
1474 	curr_state = slot_p->hs_info.cn_state;
1475 	while ((curr_state < target_state) && (rv == DDI_SUCCESS)) {
1476 
1477 		switch (curr_state) {
1478 		case DDI_HP_CN_STATE_EMPTY:
1479 			/*
1480 			 * From EMPTY to PRESENT, just check the hardware
1481 			 * slot state.
1482 			 */
1483 			pciehpc_get_slot_state(slot_p);
1484 			curr_state = slot_p->hs_info.cn_state;
1485 			if (curr_state < DDI_HP_CN_STATE_PRESENT)
1486 				rv = DDI_FAILURE;
1487 			break;
1488 		case DDI_HP_CN_STATE_PRESENT:
1489 			if (!slot_p->hs_ctrl->hc_has_pwr) {
1490 				pciehpc_get_slot_state(slot_p);
1491 				curr_state = slot_p->hs_info.cn_state;
1492 				if (curr_state < DDI_HP_CN_STATE_POWERED)
1493 					rv = DDI_FAILURE;
1494 				break;
1495 			}
1496 
1497 			rv = (slot_p->hs_ctrl->hc_ops.poweron_hpc_slot)(slot_p,
1498 			    &curr_state);
1499 
1500 			break;
1501 		case DDI_HP_CN_STATE_POWERED:
1502 			curr_state = slot_p->hs_info.cn_state =
1503 			    DDI_HP_CN_STATE_ENABLED;
1504 			break;
1505 		default:
1506 			/* should never reach here */
1507 			ASSERT("unknown devinfo state");
1508 		}
1509 	}
1510 
1511 	return (rv);
1512 }
1513 
1514 static int
1515 pciehpc_downgrade_slot_state(pcie_hp_slot_t *slot_p,
1516     ddi_hp_cn_state_t target_state)
1517 {
1518 	ddi_hp_cn_state_t curr_state;
1519 	int rv = DDI_SUCCESS;
1520 
1521 
1522 	curr_state = slot_p->hs_info.cn_state;
1523 	while ((curr_state > target_state) && (rv == DDI_SUCCESS)) {
1524 
1525 		switch (curr_state) {
1526 		case DDI_HP_CN_STATE_PRESENT:
1527 			/*
1528 			 * From PRESENT to EMPTY, just check hardware slot
1529 			 * state.
1530 			 */
1531 			pciehpc_get_slot_state(slot_p);
1532 			curr_state = slot_p->hs_info.cn_state;
1533 			if (curr_state >= DDI_HP_CN_STATE_PRESENT)
1534 				rv = DDI_FAILURE;
1535 			break;
1536 		case DDI_HP_CN_STATE_POWERED:
1537 			/*
1538 			 * If the device doesn't have power control then we
1539 			 * cannot ask it to power off the slot. However, a
1540 			 * device may have been removed and therefore we need to
1541 			 * manually check if the device was removed by getting
1542 			 * the state. Otherwise we let power control do
1543 			 * everything.
1544 			 */
1545 			if (!slot_p->hs_ctrl->hc_has_pwr) {
1546 				pciehpc_get_slot_state(slot_p);
1547 				curr_state = slot_p->hs_info.cn_state;
1548 				if (curr_state >= DDI_HP_CN_STATE_POWERED)
1549 					rv = DDI_FAILURE;
1550 				break;
1551 			}
1552 
1553 			rv = (slot_p->hs_ctrl->hc_ops.poweroff_hpc_slot)(
1554 			    slot_p, &curr_state);
1555 
1556 			break;
1557 		case DDI_HP_CN_STATE_ENABLED:
1558 			curr_state = slot_p->hs_info.cn_state =
1559 			    DDI_HP_CN_STATE_POWERED;
1560 
1561 			break;
1562 		default:
1563 			/* should never reach here */
1564 			ASSERT("unknown devinfo state");
1565 		}
1566 	}
1567 
1568 	return (rv);
1569 }
1570 
1571 /* Change slot state to a target state */
1572 static int
1573 pciehpc_change_slot_state(pcie_hp_slot_t *slot_p,
1574     ddi_hp_cn_state_t target_state)
1575 {
1576 	ddi_hp_cn_state_t curr_state;
1577 	int rv;
1578 
1579 	pciehpc_get_slot_state(slot_p);
1580 	curr_state = slot_p->hs_info.cn_state;
1581 
1582 	if (curr_state == target_state) {
1583 		return (DDI_SUCCESS);
1584 	}
1585 	if (curr_state < target_state) {
1586 
1587 		rv = pciehpc_upgrade_slot_state(slot_p, target_state);
1588 	} else {
1589 		rv = pciehpc_downgrade_slot_state(slot_p, target_state);
1590 	}
1591 
1592 	return (rv);
1593 }
1594 
1595 int
1596 pciehpc_slot_get_property(pcie_hp_slot_t *slot_p, ddi_hp_property_t *arg,
1597     ddi_hp_property_t *rval)
1598 {
1599 	ddi_hp_property_t request, result;
1600 #ifdef _SYSCALL32_IMPL
1601 	ddi_hp_property32_t request32, result32;
1602 #endif
1603 	pcie_hp_ctrl_t	*ctrl_p = slot_p->hs_ctrl;
1604 	nvlist_t	*prop_list;
1605 	nvlist_t	*prop_rlist; /* nvlist for return values */
1606 	nvpair_t	*prop_pair;
1607 	char		*name, *value;
1608 	int		ret = DDI_SUCCESS;
1609 	int		i, n;
1610 	boolean_t	get_all_prop = B_FALSE;
1611 
1612 	if (get_udatamodel() == DATAMODEL_NATIVE) {
1613 		if (copyin(arg, &request, sizeof (ddi_hp_property_t)) ||
1614 		    copyin(rval, &result, sizeof (ddi_hp_property_t)))
1615 			return (DDI_FAILURE);
1616 	}
1617 #ifdef _SYSCALL32_IMPL
1618 	else {
1619 		bzero(&request, sizeof (request));
1620 		bzero(&result, sizeof (result));
1621 		if (copyin(arg, &request32, sizeof (ddi_hp_property32_t)) ||
1622 		    copyin(rval, &result32, sizeof (ddi_hp_property32_t)))
1623 			return (DDI_FAILURE);
1624 		request.nvlist_buf = (char *)(uintptr_t)request32.nvlist_buf;
1625 		request.buf_size = request32.buf_size;
1626 		result.nvlist_buf = (char *)(uintptr_t)result32.nvlist_buf;
1627 		result.buf_size = result32.buf_size;
1628 	}
1629 #endif
1630 
1631 	if ((ret = pcie_copyin_nvlist(request.nvlist_buf, request.buf_size,
1632 	    &prop_list)) != DDI_SUCCESS)
1633 		return (ret);
1634 
1635 	if (nvlist_alloc(&prop_rlist, NV_UNIQUE_NAME, 0)) {
1636 		ret = DDI_ENOMEM;
1637 		goto get_prop_cleanup;
1638 	}
1639 
1640 	/* check whether the requested property is "all" or "help" */
1641 	prop_pair = nvlist_next_nvpair(prop_list, NULL);
1642 	if (prop_pair && !nvlist_next_nvpair(prop_list, prop_pair)) {
1643 		name = nvpair_name(prop_pair);
1644 		n = sizeof (pciehpc_props) / sizeof (pciehpc_prop_t);
1645 
1646 		if (strcmp(name, PCIEHPC_PROP_ALL) == 0) {
1647 			(void) nvlist_remove_all(prop_list, PCIEHPC_PROP_ALL);
1648 
1649 			/*
1650 			 * Add all properties into the request list, so that we
1651 			 * will get the values in the following for loop.
1652 			 */
1653 			for (i = 0; i < n; i++) {
1654 				if (nvlist_add_string(prop_list,
1655 				    pciehpc_props[i].prop_name, "") != 0) {
1656 					ret = DDI_FAILURE;
1657 					goto get_prop_cleanup1;
1658 				}
1659 			}
1660 			get_all_prop = B_TRUE;
1661 		} else if (strcmp(name, PCIEHPC_PROP_HELP) == 0) {
1662 			/*
1663 			 * Empty the request list, and add help strings into the
1664 			 * return list. We will pass the following for loop.
1665 			 */
1666 			(void) nvlist_remove_all(prop_list, PCIEHPC_PROP_HELP);
1667 
1668 			for (i = 0; i < n; i++) {
1669 				if (nvlist_add_string(prop_rlist,
1670 				    pciehpc_props[i].prop_name,
1671 				    pciehpc_props[i].prop_value) != 0) {
1672 					ret = DDI_FAILURE;
1673 					goto get_prop_cleanup1;
1674 				}
1675 			}
1676 		}
1677 	}
1678 
1679 	mutex_enter(&ctrl_p->hc_mutex);
1680 
1681 	/* get the current slot state */
1682 	pciehpc_get_slot_state(slot_p);
1683 
1684 	/* for each requested property, get the value and add it to nvlist */
1685 	prop_pair = NULL;
1686 	while ((prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) != NULL) {
1687 		name = nvpair_name(prop_pair);
1688 		value = NULL;
1689 
1690 		if (strcmp(name, PCIEHPC_PROP_LED_FAULT) == 0) {
1691 			value = pcie_led_state_text(
1692 			    slot_p->hs_fault_led_state);
1693 		} else if (strcmp(name, PCIEHPC_PROP_LED_POWER) == 0) {
1694 			value = pcie_led_state_text(
1695 			    slot_p->hs_power_led_state);
1696 		} else if (strcmp(name, PCIEHPC_PROP_LED_ATTN) == 0) {
1697 			value = pcie_led_state_text(
1698 			    slot_p->hs_attn_led_state);
1699 		} else if (strcmp(name, PCIEHPC_PROP_LED_ACTIVE) == 0) {
1700 			value = pcie_led_state_text(
1701 			    slot_p->hs_active_led_state);
1702 		} else if (strcmp(name, PCIEHPC_PROP_CARD_TYPE) == 0) {
1703 			ddi_acc_handle_t handle;
1704 			dev_info_t	*cdip;
1705 			uint8_t		prog_class, base_class, sub_class;
1706 			int		i;
1707 
1708 			mutex_exit(&ctrl_p->hc_mutex);
1709 			cdip = pcie_hp_devi_find(
1710 			    ctrl_p->hc_dip, slot_p->hs_device_num, 0);
1711 			mutex_enter(&ctrl_p->hc_mutex);
1712 
1713 			if ((slot_p->hs_info.cn_state
1714 			    != DDI_HP_CN_STATE_ENABLED) || (cdip == NULL)) {
1715 				/*
1716 				 * When getting all properties, just ignore the
1717 				 * one that's not available under certain state.
1718 				 */
1719 				if (get_all_prop)
1720 					continue;
1721 
1722 				ret = DDI_ENOTSUP;
1723 				goto get_prop_cleanup2;
1724 			}
1725 
1726 			if (pci_config_setup(cdip, &handle) != DDI_SUCCESS) {
1727 				ret = DDI_FAILURE;
1728 				goto get_prop_cleanup2;
1729 			}
1730 
1731 			prog_class = pci_config_get8(handle,
1732 			    PCI_CONF_PROGCLASS);
1733 			base_class = pci_config_get8(handle, PCI_CONF_BASCLASS);
1734 			sub_class = pci_config_get8(handle, PCI_CONF_SUBCLASS);
1735 			pci_config_teardown(&handle);
1736 
1737 			for (i = 0; i < class_pci_items; i++) {
1738 				if ((base_class == class_pci[i].base_class) &&
1739 				    (sub_class == class_pci[i].sub_class) &&
1740 				    (prog_class == class_pci[i].prog_class)) {
1741 					value = class_pci[i].short_desc;
1742 					break;
1743 				}
1744 			}
1745 			if (i == class_pci_items)
1746 				value = PCIEHPC_PROP_VALUE_UNKNOWN;
1747 		} else if (strcmp(name, PCIEHPC_PROP_BOARD_TYPE) == 0) {
1748 			if (slot_p->hs_info.cn_state <= DDI_HP_CN_STATE_EMPTY)
1749 				value = PCIEHPC_PROP_VALUE_UNKNOWN;
1750 			else
1751 				value = PCIEHPC_PROP_VALUE_PCIHOTPLUG;
1752 		} else if (strcmp(name, PCIEHPC_PROP_SLOT_CONDITION) == 0) {
1753 			value = pcie_slot_condition_text(slot_p->hs_condition);
1754 		} else {
1755 			/* unsupported property */
1756 			PCIE_DBG("Unsupported property: %s\n", name);
1757 
1758 			ret = DDI_ENOTSUP;
1759 			goto get_prop_cleanup2;
1760 		}
1761 		if (nvlist_add_string(prop_rlist, name, value) != 0) {
1762 			ret = DDI_FAILURE;
1763 			goto get_prop_cleanup2;
1764 		}
1765 	}
1766 
1767 	/* pack nvlist and copyout */
1768 	if ((ret = pcie_copyout_nvlist(prop_rlist, result.nvlist_buf,
1769 	    &result.buf_size)) != DDI_SUCCESS) {
1770 		goto get_prop_cleanup2;
1771 	}
1772 	if (get_udatamodel() == DATAMODEL_NATIVE) {
1773 		if (copyout(&result, rval, sizeof (ddi_hp_property_t)))
1774 			ret = DDI_FAILURE;
1775 	}
1776 #ifdef _SYSCALL32_IMPL
1777 	else {
1778 		if (result.buf_size > UINT32_MAX) {
1779 			ret = DDI_FAILURE;
1780 		} else {
1781 			result32.buf_size = (uint32_t)result.buf_size;
1782 			if (copyout(&result32, rval,
1783 			    sizeof (ddi_hp_property32_t)))
1784 				ret = DDI_FAILURE;
1785 		}
1786 	}
1787 #endif
1788 
1789 get_prop_cleanup2:
1790 	mutex_exit(&ctrl_p->hc_mutex);
1791 get_prop_cleanup1:
1792 	nvlist_free(prop_rlist);
1793 get_prop_cleanup:
1794 	nvlist_free(prop_list);
1795 	return (ret);
1796 }
1797 
1798 int
1799 pciehpc_slot_set_property(pcie_hp_slot_t *slot_p, ddi_hp_property_t *arg,
1800     ddi_hp_property_t *rval)
1801 {
1802 	ddi_hp_property_t	request, result;
1803 #ifdef _SYSCALL32_IMPL
1804 	ddi_hp_property32_t	request32, result32;
1805 #endif
1806 	pcie_hp_ctrl_t		*ctrl_p = slot_p->hs_ctrl;
1807 	nvlist_t		*prop_list;
1808 	nvlist_t		*prop_rlist;
1809 	nvpair_t		*prop_pair;
1810 	char			*name, *value;
1811 	pcie_hp_led_state_t	led_state;
1812 	int			ret = DDI_SUCCESS;
1813 
1814 	if (get_udatamodel() == DATAMODEL_NATIVE) {
1815 		if (copyin(arg, &request, sizeof (ddi_hp_property_t)))
1816 			return (DDI_FAILURE);
1817 		if (rval &&
1818 		    copyin(rval, &result, sizeof (ddi_hp_property_t)))
1819 			return (DDI_FAILURE);
1820 	}
1821 #ifdef _SYSCALL32_IMPL
1822 	else {
1823 		bzero(&request, sizeof (request));
1824 		bzero(&result, sizeof (result));
1825 		if (copyin(arg, &request32, sizeof (ddi_hp_property32_t)))
1826 			return (DDI_FAILURE);
1827 		if (rval &&
1828 		    copyin(rval, &result32, sizeof (ddi_hp_property32_t)))
1829 			return (DDI_FAILURE);
1830 		request.nvlist_buf = (char *)(uintptr_t)request32.nvlist_buf;
1831 		request.buf_size = request32.buf_size;
1832 		if (rval) {
1833 			result.nvlist_buf =
1834 			    (char *)(uintptr_t)result32.nvlist_buf;
1835 			result.buf_size = result32.buf_size;
1836 		}
1837 	}
1838 #endif
1839 
1840 	if ((ret = pcie_copyin_nvlist(request.nvlist_buf, request.buf_size,
1841 	    &prop_list)) != DDI_SUCCESS)
1842 		return (ret);
1843 
1844 	/* check whether the requested property is "help" */
1845 	prop_pair = nvlist_next_nvpair(prop_list, NULL);
1846 	if (prop_pair && !nvlist_next_nvpair(prop_list, prop_pair) &&
1847 	    (strcmp(nvpair_name(prop_pair), PCIEHPC_PROP_HELP) == 0)) {
1848 		if (!rval) {
1849 			ret = DDI_ENOTSUP;
1850 			goto set_prop_cleanup;
1851 		}
1852 
1853 		if (nvlist_alloc(&prop_rlist, NV_UNIQUE_NAME, 0)) {
1854 			ret = DDI_ENOMEM;
1855 			goto set_prop_cleanup;
1856 		}
1857 		if (nvlist_add_string(prop_rlist, PCIEHPC_PROP_LED_ATTN,
1858 		    PCIEHPC_PROP_VALUE_LED) != 0) {
1859 			ret = DDI_FAILURE;
1860 			goto set_prop_cleanup1;
1861 		}
1862 
1863 		if ((ret = pcie_copyout_nvlist(prop_rlist, result.nvlist_buf,
1864 		    &result.buf_size)) != DDI_SUCCESS) {
1865 			goto set_prop_cleanup1;
1866 		}
1867 		if (get_udatamodel() == DATAMODEL_NATIVE) {
1868 			if (copyout(&result, rval,
1869 			    sizeof (ddi_hp_property_t))) {
1870 				ret =  DDI_FAILURE;
1871 				goto set_prop_cleanup1;
1872 			}
1873 		}
1874 #ifdef _SYSCALL32_IMPL
1875 		else {
1876 			if (result.buf_size > UINT32_MAX) {
1877 				ret =  DDI_FAILURE;
1878 				goto set_prop_cleanup1;
1879 			} else {
1880 				result32.buf_size = (uint32_t)result.buf_size;
1881 				if (copyout(&result32, rval,
1882 				    sizeof (ddi_hp_property32_t))) {
1883 					ret =  DDI_FAILURE;
1884 					goto set_prop_cleanup1;
1885 				}
1886 			}
1887 		}
1888 #endif
1889 set_prop_cleanup1:
1890 		nvlist_free(prop_rlist);
1891 		nvlist_free(prop_list);
1892 		return (ret);
1893 	}
1894 
1895 	/* Validate the request */
1896 	prop_pair = NULL;
1897 	while ((prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) != NULL) {
1898 		name = nvpair_name(prop_pair);
1899 		if (nvpair_type(prop_pair) != DATA_TYPE_STRING) {
1900 			PCIE_DBG("Unexpected data type of setting "
1901 			    "property %s.\n", name);
1902 			ret = DDI_EINVAL;
1903 			goto set_prop_cleanup;
1904 		}
1905 		if (nvpair_value_string(prop_pair, &value)) {
1906 			PCIE_DBG("Get string value failed for property %s.\n",
1907 			    name);
1908 			ret = DDI_FAILURE;
1909 			goto set_prop_cleanup;
1910 		}
1911 
1912 		if (strcmp(name, PCIEHPC_PROP_LED_ATTN) == 0) {
1913 			if ((strcmp(value, PCIEHPC_PROP_VALUE_ON) != 0) &&
1914 			    (strcmp(value, PCIEHPC_PROP_VALUE_OFF) != 0) &&
1915 			    (strcmp(value, PCIEHPC_PROP_VALUE_BLINK) != 0)) {
1916 				PCIE_DBG("Unsupported value of setting "
1917 				    "property %s\n", name);
1918 				ret = DDI_ENOTSUP;
1919 				goto set_prop_cleanup;
1920 			}
1921 		} else {
1922 			PCIE_DBG("Unsupported property: %s\n", name);
1923 			ret = DDI_ENOTSUP;
1924 			goto set_prop_cleanup;
1925 		}
1926 	}
1927 	mutex_enter(&ctrl_p->hc_mutex);
1928 
1929 	/* get the current slot state */
1930 	pciehpc_get_slot_state(slot_p);
1931 
1932 	/* set each property */
1933 	prop_pair = NULL;
1934 	while ((prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) != NULL) {
1935 		name = nvpair_name(prop_pair);
1936 
1937 		/*
1938 		 * The validity of the property was checked above.
1939 		 */
1940 		if (strcmp(name, PCIEHPC_PROP_LED_ATTN) == 0) {
1941 			if (strcmp(value, PCIEHPC_PROP_VALUE_ON) == 0)
1942 				led_state = PCIE_HP_LED_ON;
1943 			else if (strcmp(value, PCIEHPC_PROP_VALUE_OFF) == 0)
1944 				led_state = PCIE_HP_LED_OFF;
1945 			else if (strcmp(value, PCIEHPC_PROP_VALUE_BLINK) == 0)
1946 				led_state = PCIE_HP_LED_BLINK;
1947 			else
1948 				continue;
1949 
1950 			pciehpc_set_led_state(ctrl_p, PCIE_HP_ATTN_LED,
1951 			    led_state);
1952 		}
1953 	}
1954 	if (rval) {
1955 		if (get_udatamodel() == DATAMODEL_NATIVE) {
1956 			result.buf_size = 0;
1957 			if (copyout(&result, rval, sizeof (ddi_hp_property_t)))
1958 				ret =  DDI_FAILURE;
1959 		}
1960 #ifdef _SYSCALL32_IMPL
1961 		else {
1962 			result32.buf_size = 0;
1963 			if (copyout(&result32, rval,
1964 			    sizeof (ddi_hp_property32_t)))
1965 				ret =  DDI_FAILURE;
1966 		}
1967 #endif
1968 	}
1969 
1970 	mutex_exit(&ctrl_p->hc_mutex);
1971 set_prop_cleanup:
1972 	nvlist_free(prop_list);
1973 	return (ret);
1974 }
1975 
1976 /*
1977  * Send a command to the PCI-E Hot Plug Controller.
1978  *
1979  * NOTES: The PCI-E spec defines the following semantics for issuing hot plug
1980  * commands.
1981  * 1) If Command Complete events/interrupts are supported then software
1982  *    waits for Command Complete event after issuing a command (i.e writing
1983  *    to the Slot Control register). The command completion could take as
1984  *    long as 1 second so software should be prepared to wait for 1 second
1985  *    before issuing another command.
1986  *
1987  * 2) If Command Complete events/interrupts are not supported then
1988  *    software could issue multiple Slot Control writes without any delay
1989  *    between writes.
1990  */
1991 static void
1992 pciehpc_issue_hpc_command(pcie_hp_ctrl_t *ctrl_p, uint16_t control)
1993 {
1994 	pcie_hp_slot_t	*slot_p = ctrl_p->hc_slots[0];
1995 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
1996 	uint16_t	status;
1997 	uint32_t	slot_cap;
1998 
1999 	/*
2000 	 * PCI-E version 1.1 spec defines No Command Completed
2001 	 * Support bit (bit#18) in Slot Capabilities register. If this
2002 	 * bit is set then slot doesn't support notification of command
2003 	 * completion events.
2004 	 */
2005 	slot_cap =  pciehpc_reg_get32(ctrl_p,
2006 	    bus_p->bus_pcie_off + PCIE_SLOTCAP);
2007 
2008 	/*
2009 	 * If no Command Completion event is supported or it is ACPI
2010 	 * hot plug mode then just issue the command and return.
2011 	 */
2012 	if ((slot_cap & PCIE_SLOTCAP_NO_CMD_COMP_SUPP) ||
2013 	    (bus_p->bus_hp_curr_mode == PCIE_ACPI_HP_MODE)) {
2014 		pciehpc_reg_put16(ctrl_p,
2015 		    bus_p->bus_pcie_off + PCIE_SLOTCTL, control);
2016 		return;
2017 	}
2018 
2019 	/*
2020 	 * **************************************
2021 	 * Command Complete events are supported.
2022 	 * **************************************
2023 	 */
2024 
2025 	/*
2026 	 * If HPC is not yet initialized then just poll for the Command
2027 	 * Completion interrupt.
2028 	 */
2029 	if (!(ctrl_p->hc_flags & PCIE_HP_INITIALIZED_FLAG)) {
2030 		int retry = PCIE_HP_CMD_WAIT_RETRY;
2031 
2032 		/* write the command to the HPC */
2033 		pciehpc_reg_put16(ctrl_p,
2034 		    bus_p->bus_pcie_off + PCIE_SLOTCTL, control);
2035 
2036 		/* poll for status completion */
2037 		while (retry--) {
2038 			/* wait for 10 msec before checking the status */
2039 			delay(drv_usectohz(PCIE_HP_CMD_WAIT_TIME));
2040 
2041 			status = pciehpc_reg_get16(ctrl_p,
2042 			    bus_p->bus_pcie_off + PCIE_SLOTSTS);
2043 
2044 			if (status & PCIE_SLOTSTS_COMMAND_COMPLETED) {
2045 				/* clear the status bits */
2046 				pciehpc_reg_put16(ctrl_p,
2047 				    bus_p->bus_pcie_off + PCIE_SLOTSTS, status);
2048 				break;
2049 			}
2050 		}
2051 		return;
2052 	}
2053 
2054 	/* HPC is already initialized */
2055 
2056 	ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
2057 
2058 	/*
2059 	 * If previous command is still pending then wait for its
2060 	 * completion. i.e cv_wait()
2061 	 */
2062 
2063 	while (ctrl_p->hc_cmd_pending == B_TRUE)
2064 		cv_wait(&ctrl_p->hc_cmd_comp_cv, &ctrl_p->hc_mutex);
2065 
2066 	/*
2067 	 * Issue the command and wait for Command Completion or
2068 	 * the 1 sec timeout.
2069 	 */
2070 	pciehpc_reg_put16(ctrl_p,
2071 	    bus_p->bus_pcie_off + PCIE_SLOTCTL, control);
2072 
2073 	ctrl_p->hc_cmd_pending = B_TRUE;
2074 
2075 	if (cv_timedwait(&ctrl_p->hc_cmd_comp_cv, &ctrl_p->hc_mutex,
2076 	    ddi_get_lbolt() + SEC_TO_TICK(1)) == -1) {
2077 
2078 		/* it is a timeout */
2079 		PCIE_DBG("pciehpc_issue_hpc_command: Command Complete"
2080 		    " interrupt is not received for slot %d\n",
2081 		    slot_p->hs_phy_slot_num);
2082 
2083 		/* clear the status info in case interrupts are disabled? */
2084 		status = pciehpc_reg_get16(ctrl_p,
2085 		    bus_p->bus_pcie_off + PCIE_SLOTSTS);
2086 
2087 		if (status & PCIE_SLOTSTS_COMMAND_COMPLETED) {
2088 			/* clear the status bits */
2089 			pciehpc_reg_put16(ctrl_p,
2090 			    bus_p->bus_pcie_off + PCIE_SLOTSTS, status);
2091 		}
2092 	}
2093 
2094 	ctrl_p->hc_cmd_pending = B_FALSE;
2095 
2096 	/* wake up any one waiting for issuing another command to HPC */
2097 	cv_signal(&ctrl_p->hc_cmd_comp_cv);
2098 }
2099 
2100 /*
2101  * pciehcp_attn_btn_handler()
2102  *
2103  * This handles ATTN button pressed event as per the PCI-E 1.1 spec.
2104  */
2105 static void
2106 pciehpc_attn_btn_handler(pcie_hp_ctrl_t *ctrl_p)
2107 {
2108 	pcie_hp_slot_t		*slot_p = ctrl_p->hc_slots[0];
2109 	pcie_hp_led_state_t	power_led_state;
2110 	callb_cpr_t		cprinfo;
2111 
2112 	PCIE_DBG("pciehpc_attn_btn_handler: thread started\n");
2113 
2114 	CALLB_CPR_INIT(&cprinfo, &ctrl_p->hc_mutex, callb_generic_cpr,
2115 	    "pciehpc_attn_btn_handler");
2116 
2117 	mutex_enter(&ctrl_p->hc_mutex);
2118 
2119 	/* wait for ATTN button event */
2120 	cv_wait(&slot_p->hs_attn_btn_cv, &ctrl_p->hc_mutex);
2121 
2122 	while (slot_p->hs_attn_btn_thread_exit == B_FALSE) {
2123 		if (slot_p->hs_attn_btn_pending == B_TRUE) {
2124 			/* get the current state of power LED */
2125 			power_led_state = pciehpc_get_led_state(ctrl_p,
2126 			    PCIE_HP_POWER_LED);
2127 
2128 			/* Blink the Power LED while we wait for 5 seconds */
2129 			pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED,
2130 			    PCIE_HP_LED_BLINK);
2131 
2132 			/* wait for 5 seconds before taking any action */
2133 			if (cv_timedwait(&slot_p->hs_attn_btn_cv,
2134 			    &ctrl_p->hc_mutex,
2135 			    ddi_get_lbolt() + SEC_TO_TICK(5)) == -1) {
2136 				/*
2137 				 * It is a time out; make sure the ATTN pending
2138 				 * flag is still ON before sending the event to
2139 				 * DDI HP framework.
2140 				 */
2141 				if (slot_p->hs_attn_btn_pending == B_TRUE) {
2142 					int hint;
2143 
2144 					slot_p->hs_attn_btn_pending = B_FALSE;
2145 					pciehpc_get_slot_state(slot_p);
2146 
2147 					if (slot_p->hs_info.cn_state <=
2148 					    DDI_HP_CN_STATE_PRESENT) {
2149 						/*
2150 						 * Insertion.
2151 						 */
2152 						hint = SE_INCOMING_RES;
2153 					} else {
2154 						/*
2155 						 * Want to remove;
2156 						 */
2157 						hint = SE_OUTGOING_RES;
2158 					}
2159 
2160 					/*
2161 					 * We can't call ddihp_cn_gen_sysevent
2162 					 * here since it's not a DDI interface.
2163 					 */
2164 					pcie_hp_gen_sysevent_req(
2165 					    slot_p->hs_info.cn_name,
2166 					    hint,
2167 					    ctrl_p->hc_dip,
2168 					    KM_SLEEP);
2169 				}
2170 			}
2171 
2172 			/* restore the power LED state */
2173 			pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED,
2174 			    power_led_state);
2175 			continue;
2176 		}
2177 
2178 		/* wait for another ATTN button event */
2179 		cv_wait(&slot_p->hs_attn_btn_cv, &ctrl_p->hc_mutex);
2180 	}
2181 
2182 	PCIE_DBG("pciehpc_attn_btn_handler: thread exit\n");
2183 	cv_signal(&slot_p->hs_attn_btn_cv);
2184 	CALLB_CPR_EXIT(&cprinfo);
2185 	thread_exit();
2186 }
2187 
2188 /*
2189  * convert LED state from PCIE HPC definition to pcie_hp_led_state_t
2190  * definition.
2191  */
2192 static pcie_hp_led_state_t
2193 pciehpc_led_state_to_hpc(uint16_t state)
2194 {
2195 	switch (state) {
2196 	case PCIE_SLOTCTL_INDICATOR_STATE_ON:
2197 		return (PCIE_HP_LED_ON);
2198 	case PCIE_SLOTCTL_INDICATOR_STATE_BLINK:
2199 		return (PCIE_HP_LED_BLINK);
2200 	case PCIE_SLOTCTL_INDICATOR_STATE_OFF:
2201 	default:
2202 		return (PCIE_HP_LED_OFF);
2203 	}
2204 }
2205 
2206 /*
2207  * Get the state of an LED.
2208  */
2209 static pcie_hp_led_state_t
2210 pciehpc_get_led_state(pcie_hp_ctrl_t *ctrl_p, pcie_hp_led_t led)
2211 {
2212 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
2213 	uint16_t	control, state;
2214 
2215 	/* get the current state of Slot Control register */
2216 	control =  pciehpc_reg_get16(ctrl_p,
2217 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
2218 
2219 	switch (led) {
2220 	case PCIE_HP_POWER_LED:
2221 		state = pcie_slotctl_pwr_indicator_get(control);
2222 		break;
2223 	case PCIE_HP_ATTN_LED:
2224 		state = pcie_slotctl_attn_indicator_get(control);
2225 		break;
2226 	default:
2227 		PCIE_DBG("pciehpc_get_led_state() invalid LED %d\n", led);
2228 		return (PCIE_HP_LED_OFF);
2229 	}
2230 
2231 	switch (state) {
2232 	case PCIE_SLOTCTL_INDICATOR_STATE_ON:
2233 		return (PCIE_HP_LED_ON);
2234 
2235 	case PCIE_SLOTCTL_INDICATOR_STATE_BLINK:
2236 		return (PCIE_HP_LED_BLINK);
2237 
2238 	case PCIE_SLOTCTL_INDICATOR_STATE_OFF:
2239 	default:
2240 		return (PCIE_HP_LED_OFF);
2241 	}
2242 }
2243 
2244 /*
2245  * Set the state of an LED. It updates both hw and sw state.
2246  */
2247 static void
2248 pciehpc_set_led_state(pcie_hp_ctrl_t *ctrl_p, pcie_hp_led_t led,
2249     pcie_hp_led_state_t state)
2250 {
2251 	pcie_hp_slot_t	*slot_p = ctrl_p->hc_slots[0];
2252 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
2253 	uint16_t	control;
2254 
2255 	/* get the current state of Slot Control register */
2256 	control =  pciehpc_reg_get16(ctrl_p,
2257 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
2258 
2259 	switch (led) {
2260 	case PCIE_HP_POWER_LED:
2261 		/* clear led mask */
2262 		control &= ~PCIE_SLOTCTL_PWR_INDICATOR_MASK;
2263 		slot_p->hs_power_led_state = state;
2264 		break;
2265 	case PCIE_HP_ATTN_LED:
2266 		/* clear led mask */
2267 		control &= ~PCIE_SLOTCTL_ATTN_INDICATOR_MASK;
2268 		slot_p->hs_attn_led_state = state;
2269 		break;
2270 	default:
2271 		PCIE_DBG("pciehpc_set_led_state() invalid LED %d\n", led);
2272 		return;
2273 	}
2274 
2275 	switch (state) {
2276 	case PCIE_HP_LED_ON:
2277 		if (led == PCIE_HP_POWER_LED)
2278 			control = pcie_slotctl_pwr_indicator_set(control,
2279 			    PCIE_SLOTCTL_INDICATOR_STATE_ON);
2280 		else if (led == PCIE_HP_ATTN_LED)
2281 			control = pcie_slotctl_attn_indicator_set(control,
2282 			    PCIE_SLOTCTL_INDICATOR_STATE_ON);
2283 		break;
2284 	case PCIE_HP_LED_OFF:
2285 		if (led == PCIE_HP_POWER_LED)
2286 			control = pcie_slotctl_pwr_indicator_set(control,
2287 			    PCIE_SLOTCTL_INDICATOR_STATE_OFF);
2288 		else if (led == PCIE_HP_ATTN_LED)
2289 			control = pcie_slotctl_attn_indicator_set(control,
2290 			    PCIE_SLOTCTL_INDICATOR_STATE_OFF);
2291 		break;
2292 	case PCIE_HP_LED_BLINK:
2293 		if (led == PCIE_HP_POWER_LED)
2294 			control = pcie_slotctl_pwr_indicator_set(control,
2295 			    PCIE_SLOTCTL_INDICATOR_STATE_BLINK);
2296 		else if (led == PCIE_HP_ATTN_LED)
2297 			control = pcie_slotctl_attn_indicator_set(control,
2298 			    PCIE_SLOTCTL_INDICATOR_STATE_BLINK);
2299 		break;
2300 
2301 	default:
2302 		PCIE_DBG("pciehpc_set_led_state() invalid LED state %d\n",
2303 		    state);
2304 		return;
2305 	}
2306 
2307 	/* update the Slot Control Register */
2308 	pciehpc_issue_hpc_command(ctrl_p, control);
2309 
2310 #ifdef DEBUG
2311 	/* get the current state of Slot Control register */
2312 	control =  pciehpc_reg_get16(ctrl_p,
2313 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
2314 
2315 	PCIE_DBG("pciehpc_set_led_state: slot %d power-led %s attn-led %s\n",
2316 	    slot_p->hs_phy_slot_num, pcie_led_state_text(
2317 	    pciehpc_led_state_to_hpc(pcie_slotctl_pwr_indicator_get(control))),
2318 	    pcie_led_state_text(pciehpc_led_state_to_hpc(
2319 	    pcie_slotctl_attn_indicator_get(control))));
2320 #endif
2321 }
2322 
2323 static void
2324 pciehpc_handle_power_fault(dev_info_t *dip)
2325 {
2326 	/*
2327 	 * Hold the parent's ref so that it won't disappear when the taskq is
2328 	 * scheduled to run.
2329 	 */
2330 	ndi_hold_devi(dip);
2331 
2332 	if (taskq_dispatch(system_taskq, pciehpc_power_fault_handler, dip,
2333 	    TQ_NOSLEEP) == TASKQID_INVALID) {
2334 		ndi_rele_devi(dip);
2335 		PCIE_DBG("pciehpc_intr(): "
2336 		    "Failed to dispatch power fault handler, dip %p\n", dip);
2337 	}
2338 }
2339 
2340 static void
2341 pciehpc_power_fault_handler(void *arg)
2342 {
2343 	dev_info_t *dip = (dev_info_t *)arg;
2344 	pcie_hp_ctrl_t  *ctrl_p;
2345 	pcie_hp_slot_t  *slot_p;
2346 
2347 	/* get the soft state structure for this dip */
2348 	if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL) {
2349 		ndi_rele_devi(dip);
2350 		return;
2351 	}
2352 	slot_p = ctrl_p->hc_slots[0];
2353 
2354 	/*
2355 	 * Send the event to DDI Hotplug framework, power off
2356 	 * the slot
2357 	 */
2358 	(void) ndi_hp_state_change_req(dip,
2359 	    slot_p->hs_info.cn_name,
2360 	    DDI_HP_CN_STATE_EMPTY, DDI_HP_REQ_SYNC);
2361 
2362 	mutex_enter(&ctrl_p->hc_mutex);
2363 	pciehpc_set_led_state(ctrl_p, PCIE_HP_ATTN_LED,
2364 	    PCIE_HP_LED_ON);
2365 	mutex_exit(&ctrl_p->hc_mutex);
2366 	ndi_rele_devi(dip);
2367 }
2368 
2369 #ifdef DEBUG
2370 /*
2371  * Dump PCI-E Hot Plug registers.
2372  */
2373 static void
2374 pciehpc_dump_hpregs(pcie_hp_ctrl_t *ctrl_p)
2375 {
2376 	pcie_hp_slot_t	*slot_p = ctrl_p->hc_slots[0];
2377 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
2378 	uint16_t	control;
2379 	uint32_t	capabilities;
2380 
2381 	if (!pcie_debug_flags)
2382 		return;
2383 
2384 	capabilities = pciehpc_reg_get32(ctrl_p,
2385 	    bus_p->bus_pcie_off + PCIE_SLOTCAP);
2386 
2387 	control =  pciehpc_reg_get16(ctrl_p,
2388 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
2389 
2390 	PCIE_DBG("pciehpc_dump_hpregs: Found PCI-E hot plug slot %d\n",
2391 	    slot_p->hs_phy_slot_num);
2392 
2393 	PCIE_DBG("Attention Button Present = %s\n",
2394 	    capabilities & PCIE_SLOTCAP_ATTN_BUTTON ? "Yes":"No");
2395 
2396 	PCIE_DBG("Power controller Present = %s\n",
2397 	    capabilities & PCIE_SLOTCAP_POWER_CONTROLLER ? "Yes":"No");
2398 
2399 	PCIE_DBG("MRL Sensor Present	   = %s\n",
2400 	    capabilities & PCIE_SLOTCAP_MRL_SENSOR ? "Yes":"No");
2401 
2402 	PCIE_DBG("Attn Indicator Present   = %s\n",
2403 	    capabilities & PCIE_SLOTCAP_ATTN_INDICATOR ? "Yes":"No");
2404 
2405 	PCIE_DBG("Power Indicator Present  = %s\n",
2406 	    capabilities & PCIE_SLOTCAP_PWR_INDICATOR ? "Yes":"No");
2407 
2408 	PCIE_DBG("HotPlug Surprise	   = %s\n",
2409 	    capabilities & PCIE_SLOTCAP_HP_SURPRISE ? "Yes":"No");
2410 
2411 	PCIE_DBG("HotPlug Capable	   = %s\n",
2412 	    capabilities & PCIE_SLOTCAP_HP_CAPABLE ? "Yes":"No");
2413 
2414 	PCIE_DBG("Physical Slot Number	   = %d\n",
2415 	    PCIE_SLOTCAP_PHY_SLOT_NUM(capabilities));
2416 
2417 	PCIE_DBG("Attn Button interrupt Enabled  = %s\n",
2418 	    control & PCIE_SLOTCTL_ATTN_BTN_EN ? "Yes":"No");
2419 
2420 	PCIE_DBG("Power Fault interrupt Enabled  = %s\n",
2421 	    control & PCIE_SLOTCTL_PWR_FAULT_EN ? "Yes":"No");
2422 
2423 	PCIE_DBG("MRL Sensor INTR Enabled   = %s\n",
2424 	    control & PCIE_SLOTCTL_MRL_SENSOR_EN ? "Yes":"No");
2425 
2426 	PCIE_DBG("Presence interrupt Enabled	 = %s\n",
2427 	    control & PCIE_SLOTCTL_PRESENCE_CHANGE_EN ? "Yes":"No");
2428 
2429 	PCIE_DBG("Cmd Complete interrupt Enabled = %s\n",
2430 	    control & PCIE_SLOTCTL_CMD_INTR_EN ? "Yes":"No");
2431 
2432 	PCIE_DBG("HotPlug interrupt Enabled	 = %s\n",
2433 	    control & PCIE_SLOTCTL_HP_INTR_EN ? "Yes":"No");
2434 
2435 	PCIE_DBG("Power Indicator LED = %s", pcie_led_state_text(
2436 	    pciehpc_led_state_to_hpc(pcie_slotctl_pwr_indicator_get(control))));
2437 
2438 	PCIE_DBG("Attn Indicator LED = %s\n",
2439 	    pcie_led_state_text(pciehpc_led_state_to_hpc(
2440 	    pcie_slotctl_attn_indicator_get(control))));
2441 }
2442 #endif	/* DEBUG */
2443