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