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