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 the common hotplug code that is used by Standard
29*26947304SEvan Yan  * PCIe and PCI HotPlug Controller code.
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/conf.h>
36*26947304SEvan Yan #include <sys/kmem.h>
37*26947304SEvan Yan #include <sys/debug.h>
38*26947304SEvan Yan #include <sys/vtrace.h>
39*26947304SEvan Yan #include <sys/autoconf.h>
40*26947304SEvan Yan #include <sys/varargs.h>
41*26947304SEvan Yan #include <sys/ddi_impldefs.h>
42*26947304SEvan Yan #include <sys/time.h>
43*26947304SEvan Yan #include <sys/note.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.h>
49*26947304SEvan Yan #include <sys/sysevent/eventdefs.h>
50*26947304SEvan Yan #include <sys/sysevent/dr.h>
51*26947304SEvan Yan #include <sys/pci_impl.h>
52*26947304SEvan Yan #include <sys/pci_cap.h>
53*26947304SEvan Yan #include <sys/hotplug/pci/pcicfg.h>
54*26947304SEvan Yan #include <sys/hotplug/pci/pcie_hp.h>
55*26947304SEvan Yan #include <sys/hotplug/pci/pciehpc.h>
56*26947304SEvan Yan #include <sys/hotplug/pci/pcishpc.h>
57*26947304SEvan Yan #include <io/pciex/pcieb.h>
58*26947304SEvan Yan 
59*26947304SEvan Yan /* Local functions prototype */
60*26947304SEvan Yan static int pcie_hp_list_occupants(dev_info_t *dip, void *arg);
61*26947304SEvan Yan static int pcie_hp_register_port(dev_info_t *dip, dev_info_t *pdip,
62*26947304SEvan Yan     char *cn_name);
63*26947304SEvan Yan static int pcie_hp_register_ports_for_dev(dev_info_t *dip, int device_num);
64*26947304SEvan Yan static int pcie_hp_unregister_ports_cb(ddi_hp_cn_info_t *info, void *arg);
65*26947304SEvan Yan static int pcie_hp_get_port_state(ddi_hp_cn_info_t *info, void *arg);
66*26947304SEvan Yan static int pcie_hp_match_dev_func(dev_info_t *dip, void *hdl);
67*26947304SEvan Yan static boolean_t pcie_hp_match_dev(dev_info_t *dip, int dev_num);
68*26947304SEvan Yan static int pcie_hp_get_df_from_port_name(char *cn_name, int *dev_num,
69*26947304SEvan Yan     int *func_num);
70*26947304SEvan Yan static int pcie_hp_create_port_name_num(dev_info_t *dip,
71*26947304SEvan Yan     ddi_hp_cn_info_t *cn_info);
72*26947304SEvan Yan static int pcie_hp_check_hardware_existence(dev_info_t *dip, int dev_num,
73*26947304SEvan Yan     int func_num);
74*26947304SEvan Yan 
75*26947304SEvan Yan /*
76*26947304SEvan Yan  * Global functions (called by other drivers/modules)
77*26947304SEvan Yan  */
78*26947304SEvan Yan 
79*26947304SEvan Yan /*
80*26947304SEvan Yan  * return description text for led state
81*26947304SEvan Yan  */
82*26947304SEvan Yan char *
83*26947304SEvan Yan pcie_led_state_text(pcie_hp_led_state_t state)
84*26947304SEvan Yan {
85*26947304SEvan Yan 	switch (state) {
86*26947304SEvan Yan 	case PCIE_HP_LED_ON:
87*26947304SEvan Yan 		return (PCIEHPC_PROP_VALUE_ON);
88*26947304SEvan Yan 	case PCIE_HP_LED_OFF:
89*26947304SEvan Yan 		return (PCIEHPC_PROP_VALUE_OFF);
90*26947304SEvan Yan 	case PCIE_HP_LED_BLINK:
91*26947304SEvan Yan 	default:
92*26947304SEvan Yan 		return (PCIEHPC_PROP_VALUE_BLINK);
93*26947304SEvan Yan 	}
94*26947304SEvan Yan }
95*26947304SEvan Yan 
96*26947304SEvan Yan /*
97*26947304SEvan Yan  * return description text for slot condition
98*26947304SEvan Yan  */
99*26947304SEvan Yan char *
100*26947304SEvan Yan pcie_slot_condition_text(ap_condition_t condition)
101*26947304SEvan Yan {
102*26947304SEvan Yan 	switch (condition) {
103*26947304SEvan Yan 	case AP_COND_UNKNOWN:
104*26947304SEvan Yan 		return (PCIEHPC_PROP_VALUE_UNKNOWN);
105*26947304SEvan Yan 	case AP_COND_OK:
106*26947304SEvan Yan 		return (PCIEHPC_PROP_VALUE_OK);
107*26947304SEvan Yan 	case AP_COND_FAILING:
108*26947304SEvan Yan 		return (PCIEHPC_PROP_VALUE_FAILING);
109*26947304SEvan Yan 	case AP_COND_FAILED:
110*26947304SEvan Yan 		return (PCIEHPC_PROP_VALUE_FAILED);
111*26947304SEvan Yan 	case AP_COND_UNUSABLE:
112*26947304SEvan Yan 		return (PCIEHPC_PROP_VALUE_UNUSABLE);
113*26947304SEvan Yan 	default:
114*26947304SEvan Yan 		return (PCIEHPC_PROP_VALUE_UNKNOWN);
115*26947304SEvan Yan 	}
116*26947304SEvan Yan }
117*26947304SEvan Yan 
118*26947304SEvan Yan /*
119*26947304SEvan Yan  * routine to copy in a nvlist from userland
120*26947304SEvan Yan  */
121*26947304SEvan Yan int
122*26947304SEvan Yan pcie_copyin_nvlist(char *packed_buf, size_t packed_sz, nvlist_t **nvlp)
123*26947304SEvan Yan {
124*26947304SEvan Yan 	int		ret = DDI_SUCCESS;
125*26947304SEvan Yan 	char		*packed;
126*26947304SEvan Yan 	nvlist_t	*dest = NULL;
127*26947304SEvan Yan 
128*26947304SEvan Yan 	if (packed_buf == NULL || packed_sz == 0)
129*26947304SEvan Yan 		return (DDI_EINVAL);
130*26947304SEvan Yan 
131*26947304SEvan Yan 	/* copyin packed nvlist */
132*26947304SEvan Yan 	if ((packed = kmem_alloc(packed_sz, KM_SLEEP)) == NULL)
133*26947304SEvan Yan 		return (DDI_ENOMEM);
134*26947304SEvan Yan 
135*26947304SEvan Yan 	if (copyin(packed_buf, packed, packed_sz) != 0) {
136*26947304SEvan Yan 		cmn_err(CE_WARN, "pcie_copyin_nvlist: copyin failed.\n");
137*26947304SEvan Yan 		ret = DDI_FAILURE;
138*26947304SEvan Yan 		goto copyin_cleanup;
139*26947304SEvan Yan 	}
140*26947304SEvan Yan 
141*26947304SEvan Yan 	/* unpack packed nvlist */
142*26947304SEvan Yan 	if ((ret = nvlist_unpack(packed, packed_sz, &dest, KM_SLEEP)) != 0) {
143*26947304SEvan Yan 		cmn_err(CE_WARN, "pcie_copyin_nvlist: nvlist_unpack "
144*26947304SEvan Yan 		    "failed with err %d\n", ret);
145*26947304SEvan Yan 		switch (ret) {
146*26947304SEvan Yan 		case EINVAL:
147*26947304SEvan Yan 		case ENOTSUP:
148*26947304SEvan Yan 			ret = DDI_EINVAL;
149*26947304SEvan Yan 			goto copyin_cleanup;
150*26947304SEvan Yan 		case ENOMEM:
151*26947304SEvan Yan 			ret = DDI_ENOMEM;
152*26947304SEvan Yan 			goto copyin_cleanup;
153*26947304SEvan Yan 		default:
154*26947304SEvan Yan 			ret = DDI_FAILURE;
155*26947304SEvan Yan 			goto copyin_cleanup;
156*26947304SEvan Yan 		}
157*26947304SEvan Yan 	}
158*26947304SEvan Yan 	*nvlp = dest;
159*26947304SEvan Yan copyin_cleanup:
160*26947304SEvan Yan 	kmem_free(packed, packed_sz);
161*26947304SEvan Yan 	return (ret);
162*26947304SEvan Yan }
163*26947304SEvan Yan 
164*26947304SEvan Yan /*
165*26947304SEvan Yan  * routine to copy out a nvlist to userland
166*26947304SEvan Yan  */
167*26947304SEvan Yan int
168*26947304SEvan Yan pcie_copyout_nvlist(nvlist_t *nvl, char *packed_buf, size_t *buf_sz)
169*26947304SEvan Yan {
170*26947304SEvan Yan 	int	err = 0;
171*26947304SEvan Yan 	char	*buf = NULL;
172*26947304SEvan Yan 	size_t	packed_sz;
173*26947304SEvan Yan 
174*26947304SEvan Yan 	if (nvl == NULL || packed_buf == NULL || buf_sz == NULL)
175*26947304SEvan Yan 		return (DDI_EINVAL);
176*26947304SEvan Yan 
177*26947304SEvan Yan 	/* pack nvlist, the library will allocate memory */
178*26947304SEvan Yan 	if ((err = nvlist_pack(nvl, &buf, &packed_sz, NV_ENCODE_NATIVE, 0))
179*26947304SEvan Yan 	    != 0) {
180*26947304SEvan Yan 		cmn_err(CE_WARN, "pcie_copyout_nvlist: nvlist_pack "
181*26947304SEvan Yan 		    "failed with err %d\n", err);
182*26947304SEvan Yan 		switch (err) {
183*26947304SEvan Yan 		case EINVAL:
184*26947304SEvan Yan 		case ENOTSUP:
185*26947304SEvan Yan 			return (DDI_EINVAL);
186*26947304SEvan Yan 		case ENOMEM:
187*26947304SEvan Yan 			return (DDI_ENOMEM);
188*26947304SEvan Yan 		default:
189*26947304SEvan Yan 			return (DDI_FAILURE);
190*26947304SEvan Yan 		}
191*26947304SEvan Yan 	}
192*26947304SEvan Yan 	if (packed_sz > *buf_sz) {
193*26947304SEvan Yan 		return (DDI_EINVAL);
194*26947304SEvan Yan 	}
195*26947304SEvan Yan 
196*26947304SEvan Yan 	/* copyout packed nvlist */
197*26947304SEvan Yan 	if (copyout(buf, packed_buf, packed_sz) != 0) {
198*26947304SEvan Yan 		cmn_err(CE_WARN, "pcie_copyout_nvlist: copyout " "failed.\n");
199*26947304SEvan Yan 		kmem_free(buf, packed_sz);
200*26947304SEvan Yan 		return (DDI_FAILURE);
201*26947304SEvan Yan 	}
202*26947304SEvan Yan 
203*26947304SEvan Yan 	*buf_sz = packed_sz;
204*26947304SEvan Yan 	kmem_free(buf, packed_sz);
205*26947304SEvan Yan 	return (DDI_SUCCESS);
206*26947304SEvan Yan }
207*26947304SEvan Yan 
208*26947304SEvan Yan /*
209*26947304SEvan Yan  * init bus_hp_op entry and init hotpluggable slots & virtual ports
210*26947304SEvan Yan  */
211*26947304SEvan Yan int
212*26947304SEvan Yan pcie_hp_init(dev_info_t *dip, caddr_t arg)
213*26947304SEvan Yan {
214*26947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
215*26947304SEvan Yan 	int		ret = DDI_SUCCESS, count;
216*26947304SEvan Yan 	dev_info_t	*cdip;
217*26947304SEvan Yan 
218*26947304SEvan Yan 	if (PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p)) {
219*26947304SEvan Yan 		/* Init hotplug controller */
220*26947304SEvan Yan 		ret = pciehpc_init(dip, arg);
221*26947304SEvan Yan 	} else if (PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p)) {
222*26947304SEvan Yan 		ret = pcishpc_init(dip);
223*26947304SEvan Yan 	}
224*26947304SEvan Yan 
225*26947304SEvan Yan 	if (ret != DDI_SUCCESS) {
226*26947304SEvan Yan 		cmn_err(CE_WARN, "pcie_hp_init: initialize hotplug "
227*26947304SEvan Yan 		    "controller failed with %d\n", ret);
228*26947304SEvan Yan 		return (ret);
229*26947304SEvan Yan 	}
230*26947304SEvan Yan 
231*26947304SEvan Yan 	ndi_devi_enter(dip, &count);
232*26947304SEvan Yan 
233*26947304SEvan Yan 	/* Create port for the first level children */
234*26947304SEvan Yan 	cdip = ddi_get_child(dip);
235*26947304SEvan Yan 	while (cdip != NULL) {
236*26947304SEvan Yan 		if ((ret = pcie_hp_register_port(cdip, dip, NULL))
237*26947304SEvan Yan 		    != DDI_SUCCESS) {
238*26947304SEvan Yan 			/* stop and cleanup */
239*26947304SEvan Yan 			break;
240*26947304SEvan Yan 		}
241*26947304SEvan Yan 		cdip = ddi_get_next_sibling(cdip);
242*26947304SEvan Yan 	}
243*26947304SEvan Yan 	ndi_devi_exit(dip, count);
244*26947304SEvan Yan 	if (ret != DDI_SUCCESS) {
245*26947304SEvan Yan 		cmn_err(CE_WARN, "pcie_hp_init: initialize virtual "
246*26947304SEvan Yan 		    "hotplug port failed with %d\n", ret);
247*26947304SEvan Yan 		(void) pcie_hp_uninit(dip);
248*26947304SEvan Yan 
249*26947304SEvan Yan 		return (ret);
250*26947304SEvan Yan 	}
251*26947304SEvan Yan 
252*26947304SEvan Yan 	return (DDI_SUCCESS);
253*26947304SEvan Yan }
254*26947304SEvan Yan 
255*26947304SEvan Yan /*
256*26947304SEvan Yan  * uninit the hotpluggable slots and virtual ports
257*26947304SEvan Yan  */
258*26947304SEvan Yan int
259*26947304SEvan Yan pcie_hp_uninit(dev_info_t *dip)
260*26947304SEvan Yan {
261*26947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
262*26947304SEvan Yan 	pcie_hp_unreg_port_t arg;
263*26947304SEvan Yan 
264*26947304SEvan Yan 	/*
265*26947304SEvan Yan 	 * Must set arg.rv to NDI_SUCCESS so that if there's no port
266*26947304SEvan Yan 	 * under this dip, we still return success thus the bridge
267*26947304SEvan Yan 	 * driver can be successfully detached.
268*26947304SEvan Yan 	 *
269*26947304SEvan Yan 	 * Note that during the probe PCI configurator calls
270*26947304SEvan Yan 	 * ndi_devi_offline() to detach driver for a new probed bridge,
271*26947304SEvan Yan 	 * so that it can reprogram the resources for the bridge,
272*26947304SEvan Yan 	 * ndi_devi_offline() calls into pcieb_detach() which in turn
273*26947304SEvan Yan 	 * calls into this function. In this case there are no ports
274*26947304SEvan Yan 	 * created under a new probe bridge dip, as ports are only
275*26947304SEvan Yan 	 * created after the configurator finishing probing, thus the
276*26947304SEvan Yan 	 * ndi_hp_walk_cn() will see no ports when this is called
277*26947304SEvan Yan 	 * from the PCI configurtor.
278*26947304SEvan Yan 	 */
279*26947304SEvan Yan 	arg.nexus_dip = dip;
280*26947304SEvan Yan 	arg.connector_num = DDI_HP_CN_NUM_NONE;
281*26947304SEvan Yan 	arg.rv = NDI_SUCCESS;
282*26947304SEvan Yan 
283*26947304SEvan Yan 	/* tear down all virtual hotplug handles */
284*26947304SEvan Yan 	ndi_hp_walk_cn(dip, pcie_hp_unregister_ports_cb, &arg);
285*26947304SEvan Yan 
286*26947304SEvan Yan 	if (arg.rv != NDI_SUCCESS)
287*26947304SEvan Yan 		return (DDI_FAILURE);
288*26947304SEvan Yan 
289*26947304SEvan Yan 	if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p))
290*26947304SEvan Yan 		(void) pciehpc_uninit(dip);
291*26947304SEvan Yan 	else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p))
292*26947304SEvan Yan 		(void) pcishpc_uninit(dip);
293*26947304SEvan Yan 
294*26947304SEvan Yan 	return (DDI_SUCCESS);
295*26947304SEvan Yan }
296*26947304SEvan Yan 
297*26947304SEvan Yan /*
298*26947304SEvan Yan  * interrupt handler
299*26947304SEvan Yan  */
300*26947304SEvan Yan int
301*26947304SEvan Yan pcie_hp_intr(dev_info_t *dip)
302*26947304SEvan Yan {
303*26947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
304*26947304SEvan Yan 	int		ret = DDI_INTR_UNCLAIMED;
305*26947304SEvan Yan 
306*26947304SEvan Yan 	if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p))
307*26947304SEvan Yan 		ret = pciehpc_intr(dip);
308*26947304SEvan Yan 	else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p))
309*26947304SEvan Yan 		ret = pcishpc_intr(dip);
310*26947304SEvan Yan 
311*26947304SEvan Yan 	return (ret);
312*26947304SEvan Yan }
313*26947304SEvan Yan 
314*26947304SEvan Yan /*
315*26947304SEvan Yan  * Probe the given PCIe/PCI Hotplug Connection (CN).
316*26947304SEvan Yan  */
317*26947304SEvan Yan /*ARGSUSED*/
318*26947304SEvan Yan int
319*26947304SEvan Yan pcie_hp_probe(pcie_hp_slot_t *slot_p)
320*26947304SEvan Yan {
321*26947304SEvan Yan 	pcie_hp_ctrl_t	*ctrl_p = slot_p->hs_ctrl;
322*26947304SEvan Yan 	dev_info_t	*dip = ctrl_p->hc_dip;
323*26947304SEvan Yan 
324*26947304SEvan Yan 	/*
325*26947304SEvan Yan 	 * Call the configurator to probe a given PCI hotplug
326*26947304SEvan Yan 	 * Hotplug Connection (CN).
327*26947304SEvan Yan 	 */
328*26947304SEvan Yan 	if (pcicfg_configure(dip, slot_p->hs_device_num, PCICFG_ALL_FUNC, 0)
329*26947304SEvan Yan 	    != PCICFG_SUCCESS) {
330*26947304SEvan Yan 		PCIE_DBG("pcie_hp_probe() failed\n");
331*26947304SEvan Yan 		return (DDI_FAILURE);
332*26947304SEvan Yan 	}
333*26947304SEvan Yan 	slot_p->hs_condition = AP_COND_OK;
334*26947304SEvan Yan 	pcie_hp_create_occupant_props(dip, makedevice(ddi_driver_major(dip),
335*26947304SEvan Yan 	    slot_p->hs_minor), slot_p->hs_device_num);
336*26947304SEvan Yan 
337*26947304SEvan Yan 	/*
338*26947304SEvan Yan 	 * Create ports for the newly probed devices.
339*26947304SEvan Yan 	 * Note, this is only for the first level children because the
340*26947304SEvan Yan 	 * descendants' ports will be created during bridge driver attach.
341*26947304SEvan Yan 	 */
342*26947304SEvan Yan 	return (pcie_hp_register_ports_for_dev(dip, slot_p->hs_device_num));
343*26947304SEvan Yan }
344*26947304SEvan Yan 
345*26947304SEvan Yan /*
346*26947304SEvan Yan  * Unprobe the given PCIe/PCI Hotplug Connection (CN):
347*26947304SEvan Yan  *	1. remove all child device nodes
348*26947304SEvan Yan  *	2. unregister all dependent ports
349*26947304SEvan Yan  */
350*26947304SEvan Yan /*ARGSUSED*/
351*26947304SEvan Yan int
352*26947304SEvan Yan pcie_hp_unprobe(pcie_hp_slot_t *slot_p)
353*26947304SEvan Yan {
354*26947304SEvan Yan 	pcie_hp_ctrl_t	*ctrl_p = slot_p->hs_ctrl;
355*26947304SEvan Yan 	dev_info_t	*dip = ctrl_p->hc_dip;
356*26947304SEvan Yan 	pcie_hp_unreg_port_t arg;
357*26947304SEvan Yan 
358*26947304SEvan Yan 	/*
359*26947304SEvan Yan 	 * Call the configurator to unprobe a given PCI hotplug
360*26947304SEvan Yan 	 * Hotplug Connection (CN).
361*26947304SEvan Yan 	 */
362*26947304SEvan Yan 	if (pcicfg_unconfigure(dip, slot_p->hs_device_num, PCICFG_ALL_FUNC, 0)
363*26947304SEvan Yan 	    != PCICFG_SUCCESS) {
364*26947304SEvan Yan 		PCIE_DBG("pcie_hp_unprobe() failed\n");
365*26947304SEvan Yan 		return (DDI_FAILURE);
366*26947304SEvan Yan 	}
367*26947304SEvan Yan 	slot_p->hs_condition = AP_COND_UNKNOWN;
368*26947304SEvan Yan 	pcie_hp_delete_occupant_props(dip, makedevice(ddi_driver_major(dip),
369*26947304SEvan Yan 	    slot_p->hs_minor));
370*26947304SEvan Yan 
371*26947304SEvan Yan 	/*
372*26947304SEvan Yan 	 * Remove ports for the unprobed devices.
373*26947304SEvan Yan 	 * Note, this is only for the first level children because the
374*26947304SEvan Yan 	 * descendants' ports were already removed during bridge driver dettach.
375*26947304SEvan Yan 	 */
376*26947304SEvan Yan 	arg.nexus_dip = dip;
377*26947304SEvan Yan 	arg.connector_num = slot_p->hs_info.cn_num;
378*26947304SEvan Yan 	arg.rv = NDI_SUCCESS;
379*26947304SEvan Yan 	ndi_hp_walk_cn(dip, pcie_hp_unregister_ports_cb, &arg);
380*26947304SEvan Yan 
381*26947304SEvan Yan 	return (arg.rv == NDI_SUCCESS) ? (DDI_SUCCESS) : (DDI_FAILURE);
382*26947304SEvan Yan }
383*26947304SEvan Yan 
384*26947304SEvan Yan /* Read-only probe: no hardware register programming. */
385*26947304SEvan Yan int
386*26947304SEvan Yan pcie_read_only_probe(dev_info_t *dip, char *cn_name, dev_info_t **pcdip)
387*26947304SEvan Yan {
388*26947304SEvan Yan 	long dev, func;
389*26947304SEvan Yan 	int ret;
390*26947304SEvan Yan 	char *sp;
391*26947304SEvan Yan 	dev_info_t *cdip;
392*26947304SEvan Yan 
393*26947304SEvan Yan 	*pcdip = NULL;
394*26947304SEvan Yan 	/*
395*26947304SEvan Yan 	 * Parse the string of a pci Port name and get the device number
396*26947304SEvan Yan 	 * and function number.
397*26947304SEvan Yan 	 */
398*26947304SEvan Yan 	if (ddi_strtol(cn_name + 4, &sp, 10, &dev) != 0)
399*26947304SEvan Yan 		return (DDI_EINVAL);
400*26947304SEvan Yan 	if (ddi_strtol(sp + 1, NULL, 10, &func) != 0)
401*26947304SEvan Yan 		return (DDI_EINVAL);
402*26947304SEvan Yan 
403*26947304SEvan Yan 	ret = pcicfg_configure(dip, (int)dev, (int)func,
404*26947304SEvan Yan 	    PCICFG_FLAG_READ_ONLY);
405*26947304SEvan Yan 	if (ret == PCICFG_SUCCESS) {
406*26947304SEvan Yan 		cdip = pcie_hp_devi_find(dip, (int)dev, (int)func);
407*26947304SEvan Yan 		*pcdip = cdip;
408*26947304SEvan Yan 	}
409*26947304SEvan Yan 	return (ret);
410*26947304SEvan Yan }
411*26947304SEvan Yan 
412*26947304SEvan Yan /* Read-only unprobe: no hardware register programming. */
413*26947304SEvan Yan int
414*26947304SEvan Yan pcie_read_only_unprobe(dev_info_t *dip, char *cn_name)
415*26947304SEvan Yan {
416*26947304SEvan Yan 	long dev, func;
417*26947304SEvan Yan 	int ret;
418*26947304SEvan Yan 	char *sp;
419*26947304SEvan Yan 
420*26947304SEvan Yan 	/*
421*26947304SEvan Yan 	 * Parse the string of a pci Port name and get the device number
422*26947304SEvan Yan 	 * and function number.
423*26947304SEvan Yan 	 */
424*26947304SEvan Yan 	if (ddi_strtol(cn_name + 4, &sp, 10, &dev) != 0)
425*26947304SEvan Yan 		return (DDI_EINVAL);
426*26947304SEvan Yan 	if (ddi_strtol(sp + 1, NULL, 10, &func) != 0)
427*26947304SEvan Yan 		return (DDI_EINVAL);
428*26947304SEvan Yan 
429*26947304SEvan Yan 	ret = pcicfg_unconfigure(dip, (int)dev, (int)func,
430*26947304SEvan Yan 	    PCICFG_FLAG_READ_ONLY);
431*26947304SEvan Yan 
432*26947304SEvan Yan 	return (ret);
433*26947304SEvan Yan }
434*26947304SEvan Yan 
435*26947304SEvan Yan /* Control structure used to find a device in the devinfo tree */
436*26947304SEvan Yan struct pcie_hp_find_ctrl {
437*26947304SEvan Yan 	uint_t		device;
438*26947304SEvan Yan 	uint_t		function;
439*26947304SEvan Yan 	dev_info_t	*dip;
440*26947304SEvan Yan };
441*26947304SEvan Yan 
442*26947304SEvan Yan /*
443*26947304SEvan Yan  * find a devinfo node with specified device and function number
444*26947304SEvan Yan  * in the device tree under 'dip'
445*26947304SEvan Yan  */
446*26947304SEvan Yan dev_info_t *
447*26947304SEvan Yan pcie_hp_devi_find(dev_info_t *dip, uint_t device, uint_t function)
448*26947304SEvan Yan {
449*26947304SEvan Yan 	struct pcie_hp_find_ctrl	ctrl;
450*26947304SEvan Yan 	int				count;
451*26947304SEvan Yan 
452*26947304SEvan Yan 	ctrl.device = device;
453*26947304SEvan Yan 	ctrl.function = function;
454*26947304SEvan Yan 	ctrl.dip = NULL;
455*26947304SEvan Yan 
456*26947304SEvan Yan 	ndi_devi_enter(dip, &count);
457*26947304SEvan Yan 	ddi_walk_devs(ddi_get_child(dip), pcie_hp_match_dev_func,
458*26947304SEvan Yan 	    (void *)&ctrl);
459*26947304SEvan Yan 	ndi_devi_exit(dip, count);
460*26947304SEvan Yan 
461*26947304SEvan Yan 	return (ctrl.dip);
462*26947304SEvan Yan }
463*26947304SEvan Yan 
464*26947304SEvan Yan /*
465*26947304SEvan Yan  * routine to create 'pci-occupant' property for a hotplug slot
466*26947304SEvan Yan  */
467*26947304SEvan Yan void
468*26947304SEvan Yan pcie_hp_create_occupant_props(dev_info_t *dip, dev_t dev, int pci_dev)
469*26947304SEvan Yan {
470*26947304SEvan Yan 	pcie_bus_t		*bus_p = PCIE_DIP2BUS(dip);
471*26947304SEvan Yan 	pcie_hp_ctrl_t		*ctrl_p = (pcie_hp_ctrl_t *)bus_p->bus_hp_ctrl;
472*26947304SEvan Yan 	pcie_hp_slot_t		*slotp;
473*26947304SEvan Yan 	pcie_hp_cn_cfg_t	cn_cfg;
474*26947304SEvan Yan 	pcie_hp_occupant_info_t	*occupant;
475*26947304SEvan Yan 	int			circular, i;
476*26947304SEvan Yan 
477*26947304SEvan Yan 	ndi_devi_enter(dip, &circular);
478*26947304SEvan Yan 
479*26947304SEvan Yan 	if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p)) {
480*26947304SEvan Yan 		slotp = (ctrl_p && (pci_dev == 0)) ?
481*26947304SEvan Yan 		    ctrl_p->hc_slots[pci_dev] : NULL;
482*26947304SEvan Yan 	} else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p)) {
483*26947304SEvan Yan 		if (ctrl_p) {
484*26947304SEvan Yan 			int	slot_num;
485*26947304SEvan Yan 
486*26947304SEvan Yan 			slot_num = (ctrl_p->hc_device_increases) ?
487*26947304SEvan Yan 			    (pci_dev - ctrl_p->hc_device_start) :
488*26947304SEvan Yan 			    (pci_dev + ctrl_p->hc_device_start);
489*26947304SEvan Yan 
490*26947304SEvan Yan 			slotp = ctrl_p->hc_slots[slot_num];
491*26947304SEvan Yan 		} else {
492*26947304SEvan Yan 			slotp = NULL;
493*26947304SEvan Yan 		}
494*26947304SEvan Yan 	}
495*26947304SEvan Yan 
496*26947304SEvan Yan 	if (slotp == NULL)
497*26947304SEvan Yan 		return;
498*26947304SEvan Yan 
499*26947304SEvan Yan 	occupant = kmem_alloc(sizeof (pcie_hp_occupant_info_t), KM_SLEEP);
500*26947304SEvan Yan 	occupant->i = 0;
501*26947304SEvan Yan 
502*26947304SEvan Yan 	cn_cfg.flag = B_FALSE;
503*26947304SEvan Yan 	cn_cfg.rv = NDI_SUCCESS;
504*26947304SEvan Yan 	cn_cfg.dip = NULL;
505*26947304SEvan Yan 	cn_cfg.slotp = (void *)slotp;
506*26947304SEvan Yan 	cn_cfg.cn_private = (void *)occupant;
507*26947304SEvan Yan 
508*26947304SEvan Yan 	ddi_walk_devs(ddi_get_child(dip), pcie_hp_list_occupants,
509*26947304SEvan Yan 	    (void *)&cn_cfg);
510*26947304SEvan Yan 
511*26947304SEvan Yan 	if (occupant->i == 0) {
512*26947304SEvan Yan 		/* no occupants right now, need to create stub property */
513*26947304SEvan Yan 		char *c[] = { "" };
514*26947304SEvan Yan 		(void) ddi_prop_update_string_array(dev, dip, "pci-occupant",
515*26947304SEvan Yan 		    c, 1);
516*26947304SEvan Yan 	} else {
517*26947304SEvan Yan 		(void) ddi_prop_update_string_array(dev, dip, "pci-occupant",
518*26947304SEvan Yan 		    occupant->id, occupant->i);
519*26947304SEvan Yan 	}
520*26947304SEvan Yan 
521*26947304SEvan Yan 	for (i = 0; i < occupant->i; i++)
522*26947304SEvan Yan 		kmem_free(occupant->id[i], sizeof (char[MAXPATHLEN]));
523*26947304SEvan Yan 
524*26947304SEvan Yan 	kmem_free(occupant, sizeof (pcie_hp_occupant_info_t));
525*26947304SEvan Yan 
526*26947304SEvan Yan 	ndi_devi_exit(dip, circular);
527*26947304SEvan Yan }
528*26947304SEvan Yan 
529*26947304SEvan Yan /*
530*26947304SEvan Yan  * routine to remove 'pci-occupant' property for a hotplug slot
531*26947304SEvan Yan  */
532*26947304SEvan Yan void
533*26947304SEvan Yan pcie_hp_delete_occupant_props(dev_info_t *dip, dev_t dev)
534*26947304SEvan Yan {
535*26947304SEvan Yan 	(void) ddi_prop_remove(dev, dip, "pci-occupant");
536*26947304SEvan Yan }
537*26947304SEvan Yan 
538*26947304SEvan Yan /*
539*26947304SEvan Yan  * general code to create a minor node, called from hotplug controller
540*26947304SEvan Yan  * drivers.
541*26947304SEvan Yan  */
542*26947304SEvan Yan int
543*26947304SEvan Yan pcie_create_minor_node(pcie_hp_ctrl_t *ctrl_p, int slot)
544*26947304SEvan Yan {
545*26947304SEvan Yan 	dev_info_t		*dip = ctrl_p->hc_dip;
546*26947304SEvan Yan 	pcie_hp_slot_t		*slot_p = ctrl_p->hc_slots[slot];
547*26947304SEvan Yan 	ddi_hp_cn_info_t	*info_p = &slot_p->hs_info;
548*26947304SEvan Yan 
549*26947304SEvan Yan 	if (ddi_create_minor_node(dip, info_p->cn_name,
550*26947304SEvan Yan 	    S_IFCHR, slot_p->hs_minor,
551*26947304SEvan Yan 	    DDI_NT_PCI_ATTACHMENT_POINT, 0) != DDI_SUCCESS) {
552*26947304SEvan Yan 		return (DDI_FAILURE);
553*26947304SEvan Yan 	}
554*26947304SEvan Yan 
555*26947304SEvan Yan 	(void) ddi_prop_update_int(DDI_DEV_T_NONE,
556*26947304SEvan Yan 	    dip, "ap-names", 1 << slot_p->hs_device_num);
557*26947304SEvan Yan 
558*26947304SEvan Yan 	return (DDI_SUCCESS);
559*26947304SEvan Yan }
560*26947304SEvan Yan 
561*26947304SEvan Yan /*
562*26947304SEvan Yan  * general code to remove a minor node, called from hotplug controller
563*26947304SEvan Yan  * drivers.
564*26947304SEvan Yan  */
565*26947304SEvan Yan void
566*26947304SEvan Yan pcie_remove_minor_node(pcie_hp_ctrl_t *ctrl_p, int slot)
567*26947304SEvan Yan {
568*26947304SEvan Yan 	ddi_remove_minor_node(ctrl_p->hc_dip,
569*26947304SEvan Yan 	    ctrl_p->hc_slots[slot]->hs_info.cn_name);
570*26947304SEvan Yan }
571*26947304SEvan Yan 
572*26947304SEvan Yan /*
573*26947304SEvan Yan  * Local functions (called within this file)
574*26947304SEvan Yan  */
575*26947304SEvan Yan 
576*26947304SEvan Yan /*
577*26947304SEvan Yan  * Register ports for all the children with device number device_num
578*26947304SEvan Yan  */
579*26947304SEvan Yan static int
580*26947304SEvan Yan pcie_hp_register_ports_for_dev(dev_info_t *dip, int device_num)
581*26947304SEvan Yan {
582*26947304SEvan Yan 	dev_info_t	*cdip;
583*26947304SEvan Yan 	int		rv;
584*26947304SEvan Yan 
585*26947304SEvan Yan 	for (cdip = ddi_get_child(dip); cdip;
586*26947304SEvan Yan 	    cdip = ddi_get_next_sibling(cdip)) {
587*26947304SEvan Yan 		if (pcie_hp_match_dev(cdip, device_num)) {
588*26947304SEvan Yan 			/*
589*26947304SEvan Yan 			 * Found the newly probed device under the
590*26947304SEvan Yan 			 * current slot. Register a port for it.
591*26947304SEvan Yan 			 */
592*26947304SEvan Yan 			if ((rv = pcie_hp_register_port(cdip, dip, NULL))
593*26947304SEvan Yan 			    != DDI_SUCCESS)
594*26947304SEvan Yan 				return (rv);
595*26947304SEvan Yan 		} else {
596*26947304SEvan Yan 			continue;
597*26947304SEvan Yan 		}
598*26947304SEvan Yan 	}
599*26947304SEvan Yan 
600*26947304SEvan Yan 	return (DDI_SUCCESS);
601*26947304SEvan Yan }
602*26947304SEvan Yan 
603*26947304SEvan Yan /*
604*26947304SEvan Yan  * Unregister ports of a pci bridge dip, get called from ndi_hp_walk_cn()
605*26947304SEvan Yan  *
606*26947304SEvan Yan  * If connector_num is specified, then unregister the slot's dependent ports
607*26947304SEvan Yan  * only; Otherwise, unregister all ports of a pci bridge dip.
608*26947304SEvan Yan  */
609*26947304SEvan Yan static int
610*26947304SEvan Yan pcie_hp_unregister_ports_cb(ddi_hp_cn_info_t *info, void *arg)
611*26947304SEvan Yan {
612*26947304SEvan Yan 	pcie_hp_unreg_port_t *unreg_arg = (pcie_hp_unreg_port_t *)arg;
613*26947304SEvan Yan 	dev_info_t *dip = unreg_arg->nexus_dip;
614*26947304SEvan Yan 	int rv = NDI_SUCCESS;
615*26947304SEvan Yan 
616*26947304SEvan Yan 	if (info->cn_type != DDI_HP_CN_TYPE_VIRTUAL_PORT) {
617*26947304SEvan Yan 		unreg_arg->rv = rv;
618*26947304SEvan Yan 		return (DDI_WALK_CONTINUE);
619*26947304SEvan Yan 	}
620*26947304SEvan Yan 
621*26947304SEvan Yan 	if (unreg_arg->connector_num != DDI_HP_CN_NUM_NONE) {
622*26947304SEvan Yan 		/* Unregister ports for all unprobed devices under a slot. */
623*26947304SEvan Yan 		if (unreg_arg->connector_num == info->cn_num_dpd_on) {
624*26947304SEvan Yan 
625*26947304SEvan Yan 			rv = ndi_hp_unregister(dip, info->cn_name);
626*26947304SEvan Yan 		}
627*26947304SEvan Yan 	} else {
628*26947304SEvan Yan 
629*26947304SEvan Yan 		/* Unregister all ports of a pci bridge dip. */
630*26947304SEvan Yan 		rv = ndi_hp_unregister(dip, info->cn_name);
631*26947304SEvan Yan 	}
632*26947304SEvan Yan 
633*26947304SEvan Yan 	unreg_arg->rv = rv;
634*26947304SEvan Yan 	if (rv == NDI_SUCCESS)
635*26947304SEvan Yan 		return (DDI_WALK_CONTINUE);
636*26947304SEvan Yan 	else
637*26947304SEvan Yan 		return (DDI_WALK_TERMINATE);
638*26947304SEvan Yan }
639*26947304SEvan Yan 
640*26947304SEvan Yan /*
641*26947304SEvan Yan  * Find a port according to cn_name and get the port's state.
642*26947304SEvan Yan  */
643*26947304SEvan Yan static int
644*26947304SEvan Yan pcie_hp_get_port_state(ddi_hp_cn_info_t *info, void *arg)
645*26947304SEvan Yan {
646*26947304SEvan Yan 	pcie_hp_port_state_t *port = (pcie_hp_port_state_t *)arg;
647*26947304SEvan Yan 
648*26947304SEvan Yan 	if (info->cn_type != DDI_HP_CN_TYPE_VIRTUAL_PORT)
649*26947304SEvan Yan 		return (DDI_WALK_CONTINUE);
650*26947304SEvan Yan 
651*26947304SEvan Yan 	if (strcmp(info->cn_name, port->cn_name) == 0) {
652*26947304SEvan Yan 		/* Matched. */
653*26947304SEvan Yan 		port->cn_state = info->cn_state;
654*26947304SEvan Yan 		port->rv = DDI_SUCCESS;
655*26947304SEvan Yan 
656*26947304SEvan Yan 		return (DDI_WALK_TERMINATE);
657*26947304SEvan Yan 	}
658*26947304SEvan Yan 
659*26947304SEvan Yan 	return (DDI_WALK_CONTINUE);
660*26947304SEvan Yan }
661*26947304SEvan Yan 
662*26947304SEvan Yan /*
663*26947304SEvan Yan  * Find the physical slot with the given device number;
664*26947304SEvan Yan  * return the slot if found.
665*26947304SEvan Yan  */
666*26947304SEvan Yan static pcie_hp_slot_t *
667*26947304SEvan Yan pcie_find_physical_slot(dev_info_t *dip, int dev_num)
668*26947304SEvan Yan {
669*26947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
670*26947304SEvan Yan 	pcie_hp_ctrl_t	*ctrl = PCIE_GET_HP_CTRL(dip);
671*26947304SEvan Yan 
672*26947304SEvan Yan 	if (PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p)) {
673*26947304SEvan Yan 		/* PCIe has only one slot */
674*26947304SEvan Yan 		return (dev_num == 0) ? (ctrl->hc_slots[0]) : (NULL);
675*26947304SEvan Yan 	} else if (PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p)) {
676*26947304SEvan Yan 		for (int slot = 0; slot < ctrl->hc_num_slots_impl; slot++) {
677*26947304SEvan Yan 			if (ctrl->hc_slots[slot]->hs_device_num == dev_num) {
678*26947304SEvan Yan 				/* found */
679*26947304SEvan Yan 				return (ctrl->hc_slots[slot]);
680*26947304SEvan Yan 			}
681*26947304SEvan Yan 		}
682*26947304SEvan Yan 	}
683*26947304SEvan Yan 
684*26947304SEvan Yan 	return (NULL);
685*26947304SEvan Yan }
686*26947304SEvan Yan 
687*26947304SEvan Yan /*
688*26947304SEvan Yan  * setup slot name/slot-number info for the port which is being registered.
689*26947304SEvan Yan  */
690*26947304SEvan Yan static int
691*26947304SEvan Yan pcie_hp_create_port_name_num(dev_info_t *dip, ddi_hp_cn_info_t *cn_info)
692*26947304SEvan Yan {
693*26947304SEvan Yan 	int		ret, dev_num, func_num, name_len;
694*26947304SEvan Yan 	dev_info_t	*pdip = ddi_get_parent(dip);
695*26947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(pdip);
696*26947304SEvan Yan 	pcie_hp_slot_t	*slot;
697*26947304SEvan Yan 	pcie_req_id_t	bdf;
698*26947304SEvan Yan 	char		tmp[PCIE_HP_DEV_FUNC_NUM_STRING_LEN];
699*26947304SEvan Yan 
700*26947304SEvan Yan 	ret = pcie_get_bdf_from_dip(dip, &bdf);
701*26947304SEvan Yan 	if (ret != DDI_SUCCESS) {
702*26947304SEvan Yan 		return (ret);
703*26947304SEvan Yan 	}
704*26947304SEvan Yan 	if (PCIE_IS_RP(bus_p) || PCIE_IS_SWD(bus_p) ||
705*26947304SEvan Yan 	    PCIE_IS_PCI2PCIE(bus_p)) {
706*26947304SEvan Yan 		/*
707*26947304SEvan Yan 		 * It is under a PCIe device, devcie number is always 0;
708*26947304SEvan Yan 		 * function number might > 8 in ARI supported case.
709*26947304SEvan Yan 		 */
710*26947304SEvan Yan 		dev_num = 0;
711*26947304SEvan Yan 		func_num = (bdf & ((~PCI_REG_BUS_M) >> 8));
712*26947304SEvan Yan 	} else {
713*26947304SEvan Yan 		dev_num = (bdf & (PCI_REG_DEV_M >> 8)) >> 3;
714*26947304SEvan Yan 		func_num = bdf & (PCI_REG_FUNC_M >> 8);
715*26947304SEvan Yan 	}
716*26947304SEvan Yan 	/*
717*26947304SEvan Yan 	 * The string length of dev_num and func_num must be no longer than 4
718*26947304SEvan Yan 	 * including the string end mark. (With ARI case considered, e.g.,
719*26947304SEvan Yan 	 * dev_num=0x0, func_num=0xff.)
720*26947304SEvan Yan 	 */
721*26947304SEvan Yan 	(void) snprintf(tmp, PCIE_HP_DEV_FUNC_NUM_STRING_LEN, "%x%x",
722*26947304SEvan Yan 	    dev_num, func_num);
723*26947304SEvan Yan 	/*
724*26947304SEvan Yan 	 * Calculate the length of cn_name.
725*26947304SEvan Yan 	 * The format of pci port name is: pci.d,f
726*26947304SEvan Yan 	 * d stands for dev_num, f stands for func_num. So the length of the
727*26947304SEvan Yan 	 * name string can be calculated as following.
728*26947304SEvan Yan 	 */
729*26947304SEvan Yan 	name_len = strlen(tmp) + PCIE_HP_PORT_NAME_STRING_LEN + 1;
730*26947304SEvan Yan 
731*26947304SEvan Yan 	cn_info->cn_name = (char *)kmem_zalloc(name_len, KM_SLEEP);
732*26947304SEvan Yan 	(void) snprintf(cn_info->cn_name, name_len, "pci.%x,%x",
733*26947304SEvan Yan 	    dev_num, func_num);
734*26947304SEvan Yan 	cn_info->cn_num = (dev_num << 8) | func_num;
735*26947304SEvan Yan 	slot = pcie_find_physical_slot(pdip, dev_num);
736*26947304SEvan Yan 
737*26947304SEvan Yan 	cn_info->cn_num_dpd_on = slot ?
738*26947304SEvan Yan 	    slot->hs_info.cn_num : DDI_HP_CN_NUM_NONE;
739*26947304SEvan Yan 
740*26947304SEvan Yan 	return (DDI_SUCCESS);
741*26947304SEvan Yan }
742*26947304SEvan Yan 
743*26947304SEvan Yan /*
744*26947304SEvan Yan  * Extract device and function number from port name, whose format is
745*26947304SEvan Yan  * something like 'pci.1,0'
746*26947304SEvan Yan  */
747*26947304SEvan Yan static int
748*26947304SEvan Yan pcie_hp_get_df_from_port_name(char *cn_name, int *dev_num, int *func_num)
749*26947304SEvan Yan {
750*26947304SEvan Yan 	int name_len, ret;
751*26947304SEvan Yan 	long d, f;
752*26947304SEvan Yan 	char *sp;
753*26947304SEvan Yan 
754*26947304SEvan Yan 	/* some checks for the input name */
755*26947304SEvan Yan 	name_len = strlen(cn_name);
756*26947304SEvan Yan 	if ((name_len <= PCIE_HP_PORT_NAME_STRING_LEN) ||
757*26947304SEvan Yan 	    (name_len > (PCIE_HP_PORT_NAME_STRING_LEN +
758*26947304SEvan Yan 	    PCIE_HP_DEV_FUNC_NUM_STRING_LEN - 1)) ||
759*26947304SEvan Yan 	    (strncmp("pci.", cn_name, 4) != 0)) {
760*26947304SEvan Yan 		return (DDI_EINVAL);
761*26947304SEvan Yan 	}
762*26947304SEvan Yan 	ret = ddi_strtol(cn_name + 4, &sp, 10, &d);
763*26947304SEvan Yan 	if (ret != DDI_SUCCESS)
764*26947304SEvan Yan 		return (ret);
765*26947304SEvan Yan 
766*26947304SEvan Yan 	if (strncmp(",", sp, 1) != 0)
767*26947304SEvan Yan 		return (DDI_EINVAL);
768*26947304SEvan Yan 
769*26947304SEvan Yan 	ret = ddi_strtol(sp + 1, NULL, 10, &f);
770*26947304SEvan Yan 	if (ret != DDI_SUCCESS)
771*26947304SEvan Yan 		return (ret);
772*26947304SEvan Yan 	*dev_num = (int)d;
773*26947304SEvan Yan 	*func_num = (int)f;
774*26947304SEvan Yan 
775*26947304SEvan Yan 	return (ret);
776*26947304SEvan Yan }
777*26947304SEvan Yan 
778*26947304SEvan Yan /*
779*26947304SEvan Yan  * Check/copy cn_name and set connection numbers.
780*26947304SEvan Yan  * If it is a valid name, then setup cn_info for the newly created port.
781*26947304SEvan Yan  */
782*26947304SEvan Yan static int
783*26947304SEvan Yan pcie_hp_setup_port_name_num(dev_info_t *pdip, char *cn_name,
784*26947304SEvan Yan     ddi_hp_cn_info_t *cn_info)
785*26947304SEvan Yan {
786*26947304SEvan Yan 	int dev_num, func_num, ret;
787*26947304SEvan Yan 	pcie_hp_slot_t *slot;
788*26947304SEvan Yan 
789*26947304SEvan Yan 	if ((ret = pcie_hp_get_df_from_port_name(cn_name, &dev_num, &func_num))
790*26947304SEvan Yan 	    != DDI_SUCCESS)
791*26947304SEvan Yan 		return (ret);
792*26947304SEvan Yan 
793*26947304SEvan Yan 	if (pcie_hp_check_hardware_existence(pdip, dev_num, func_num) ==
794*26947304SEvan Yan 	    DDI_SUCCESS) {
795*26947304SEvan Yan 		cn_info->cn_state = DDI_HP_CN_STATE_PRESENT;
796*26947304SEvan Yan 	} else {
797*26947304SEvan Yan 		cn_info->cn_state = DDI_HP_CN_STATE_EMPTY;
798*26947304SEvan Yan 	}
799*26947304SEvan Yan 
800*26947304SEvan Yan 	cn_info->cn_name = ddi_strdup(cn_name, KM_SLEEP);
801*26947304SEvan Yan 	cn_info->cn_num = (dev_num << 8) | func_num;
802*26947304SEvan Yan 
803*26947304SEvan Yan 	slot = pcie_find_physical_slot(pdip, dev_num);
804*26947304SEvan Yan 	if (slot) {
805*26947304SEvan Yan 		cn_info->cn_num_dpd_on = slot->hs_info.cn_num;
806*26947304SEvan Yan 	} else {
807*26947304SEvan Yan 		cn_info->cn_num_dpd_on = DDI_HP_CN_NUM_NONE;
808*26947304SEvan Yan 	}
809*26947304SEvan Yan 	return (DDI_SUCCESS);
810*26947304SEvan Yan }
811*26947304SEvan Yan 
812*26947304SEvan Yan static int
813*26947304SEvan Yan ndi2ddi(int n)
814*26947304SEvan Yan {
815*26947304SEvan Yan 	int ret;
816*26947304SEvan Yan 
817*26947304SEvan Yan 	switch (n) {
818*26947304SEvan Yan 	case NDI_SUCCESS:
819*26947304SEvan Yan 		ret = DDI_SUCCESS;
820*26947304SEvan Yan 		break;
821*26947304SEvan Yan 	case NDI_NOMEM:
822*26947304SEvan Yan 		ret = DDI_ENOMEM;
823*26947304SEvan Yan 		break;
824*26947304SEvan Yan 	case NDI_BUSY:
825*26947304SEvan Yan 		ret = DDI_EBUSY;
826*26947304SEvan Yan 		break;
827*26947304SEvan Yan 	case NDI_EINVAL:
828*26947304SEvan Yan 		ret = DDI_EINVAL;
829*26947304SEvan Yan 		break;
830*26947304SEvan Yan 	case NDI_ENOTSUP:
831*26947304SEvan Yan 		ret = DDI_ENOTSUP;
832*26947304SEvan Yan 		break;
833*26947304SEvan Yan 	case NDI_FAILURE:
834*26947304SEvan Yan 	default:
835*26947304SEvan Yan 		ret = DDI_FAILURE;
836*26947304SEvan Yan 		break;
837*26947304SEvan Yan 	}
838*26947304SEvan Yan 	return (ret);
839*26947304SEvan Yan }
840*26947304SEvan Yan 
841*26947304SEvan Yan /*
842*26947304SEvan Yan  * Common routine to create and register a new port
843*26947304SEvan Yan  *
844*26947304SEvan Yan  * Create an empty port if dip is NULL, and cn_name needs to be specified in
845*26947304SEvan Yan  * this case. Otherwise, create a port mapping to the specified dip, and cn_name
846*26947304SEvan Yan  * is not needed in this case.
847*26947304SEvan Yan  */
848*26947304SEvan Yan static int
849*26947304SEvan Yan pcie_hp_register_port(dev_info_t *dip, dev_info_t *pdip, char *cn_name)
850*26947304SEvan Yan {
851*26947304SEvan Yan 	ddi_hp_cn_info_t	*cn_info;
852*26947304SEvan Yan 	int			ret;
853*26947304SEvan Yan 
854*26947304SEvan Yan 	ASSERT((dip == NULL) != (cn_name == NULL));
855*26947304SEvan Yan 	cn_info = kmem_zalloc(sizeof (ddi_hp_cn_info_t), KM_SLEEP);
856*26947304SEvan Yan 	if (dip != NULL)
857*26947304SEvan Yan 		ret = pcie_hp_create_port_name_num(dip, cn_info);
858*26947304SEvan Yan 	else
859*26947304SEvan Yan 		ret = pcie_hp_setup_port_name_num(pdip, cn_name, cn_info);
860*26947304SEvan Yan 
861*26947304SEvan Yan 	if (ret != DDI_SUCCESS) {
862*26947304SEvan Yan 		kmem_free(cn_info, sizeof (ddi_hp_cn_info_t));
863*26947304SEvan Yan 		return (ret);
864*26947304SEvan Yan 	}
865*26947304SEvan Yan 
866*26947304SEvan Yan 	cn_info->cn_child = dip;
867*26947304SEvan Yan 	cn_info->cn_type = DDI_HP_CN_TYPE_VIRTUAL_PORT;
868*26947304SEvan Yan 	cn_info->cn_type_str = DDI_HP_CN_TYPE_STR_PORT;
869*26947304SEvan Yan 
870*26947304SEvan Yan 	ret = ndi_hp_register(pdip, cn_info);
871*26947304SEvan Yan 
872*26947304SEvan Yan 	kmem_free(cn_info->cn_name, strlen(cn_info->cn_name) + 1);
873*26947304SEvan Yan 	kmem_free(cn_info, sizeof (ddi_hp_cn_info_t));
874*26947304SEvan Yan 
875*26947304SEvan Yan 	return (ndi2ddi(ret));
876*26947304SEvan Yan }
877*26947304SEvan Yan 
878*26947304SEvan Yan /* Check if there is a piece of hardware exist corresponding to the cn_name */
879*26947304SEvan Yan static int
880*26947304SEvan Yan pcie_hp_check_hardware_existence(dev_info_t *dip, int dev_num, int func_num)
881*26947304SEvan Yan {
882*26947304SEvan Yan 
883*26947304SEvan Yan 	/*
884*26947304SEvan Yan 	 * VHPTODO:
885*26947304SEvan Yan 	 * According to device and function number, check if there is a hardware
886*26947304SEvan Yan 	 * device exists. Currently, this function can not be reached before
887*26947304SEvan Yan 	 * we enable state transition to or from "Port-Empty" or "Port-Present"
888*26947304SEvan Yan 	 * states. When the pci device type project is integrated, we are going
889*26947304SEvan Yan 	 * to call the pci config space access interfaces introduced by it.
890*26947304SEvan Yan 	 */
891*26947304SEvan Yan 	_NOTE(ARGUNUSED(dip, dev_num, func_num));
892*26947304SEvan Yan 
893*26947304SEvan Yan 	return (DDI_SUCCESS);
894*26947304SEvan Yan }
895*26947304SEvan Yan 
896*26947304SEvan Yan /*
897*26947304SEvan Yan  * Dispatch hotplug commands to different hotplug controller drivers, including
898*26947304SEvan Yan  * physical and virtual hotplug operations.
899*26947304SEvan Yan  */
900*26947304SEvan Yan /* ARGSUSED */
901*26947304SEvan Yan int
902*26947304SEvan Yan pcie_hp_common_ops(dev_info_t *dip, char *cn_name, ddi_hp_op_t op,
903*26947304SEvan Yan     void *arg, void *result)
904*26947304SEvan Yan {
905*26947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
906*26947304SEvan Yan 	int		ret = DDI_SUCCESS;
907*26947304SEvan Yan 
908*26947304SEvan Yan 	PCIE_DBG("pcie_hp_common_ops: dip=%p cn_name=%s op=%x arg=%p\n",
909*26947304SEvan Yan 	    dip, cn_name, op, arg);
910*26947304SEvan Yan 
911*26947304SEvan Yan 	switch (op) {
912*26947304SEvan Yan 	case DDI_HPOP_CN_CREATE_PORT:
913*26947304SEvan Yan 	{
914*26947304SEvan Yan 		/* create an empty port */
915*26947304SEvan Yan 		return (pcie_hp_register_port(NULL, dip, cn_name));
916*26947304SEvan Yan 	}
917*26947304SEvan Yan 	case DDI_HPOP_CN_CHANGE_STATE:
918*26947304SEvan Yan 	{
919*26947304SEvan Yan 		ddi_hp_cn_state_t curr_state;
920*26947304SEvan Yan 		ddi_hp_cn_state_t target_state = *(ddi_hp_cn_state_t *)arg;
921*26947304SEvan Yan 		pcie_hp_port_state_t state_arg;
922*26947304SEvan Yan 
923*26947304SEvan Yan 		if (target_state < DDI_HP_CN_STATE_PORT_EMPTY) {
924*26947304SEvan Yan 			/* this is for physical slot state change */
925*26947304SEvan Yan 			break;
926*26947304SEvan Yan 		}
927*26947304SEvan Yan 		PCIE_DBG("pcie_hp_common_ops: change port state"
928*26947304SEvan Yan 		    " dip=%p cn_name=%s"
929*26947304SEvan Yan 		    " op=%x arg=%p\n", (void *)dip, cn_name, op, arg);
930*26947304SEvan Yan 
931*26947304SEvan Yan 		state_arg.rv = DDI_FAILURE;
932*26947304SEvan Yan 		state_arg.cn_name = cn_name;
933*26947304SEvan Yan 		ndi_hp_walk_cn(dip, pcie_hp_get_port_state, &state_arg);
934*26947304SEvan Yan 		if (state_arg.rv != DDI_SUCCESS) {
935*26947304SEvan Yan 			/* can not find the port */
936*26947304SEvan Yan 			return (DDI_EINVAL);
937*26947304SEvan Yan 		}
938*26947304SEvan Yan 		curr_state = state_arg.cn_state;
939*26947304SEvan Yan 		/*
940*26947304SEvan Yan 		 * Check if this is for changing port's state: change to/from
941*26947304SEvan Yan 		 * PORT_EMPTY/PRESENT states.
942*26947304SEvan Yan 		 */
943*26947304SEvan Yan 		if (curr_state < target_state) {
944*26947304SEvan Yan 			/* Upgrade state */
945*26947304SEvan Yan 			switch (curr_state) {
946*26947304SEvan Yan 			case DDI_HP_CN_STATE_PORT_EMPTY:
947*26947304SEvan Yan 				if (target_state ==
948*26947304SEvan Yan 				    DDI_HP_CN_STATE_PORT_PRESENT) {
949*26947304SEvan Yan 					int dev_num, func_num;
950*26947304SEvan Yan 
951*26947304SEvan Yan 					ret = pcie_hp_get_df_from_port_name(
952*26947304SEvan Yan 					    cn_name, &dev_num, &func_num);
953*26947304SEvan Yan 					if (ret != DDI_SUCCESS)
954*26947304SEvan Yan 						goto port_state_done;
955*26947304SEvan Yan 
956*26947304SEvan Yan 					ret = pcie_hp_check_hardware_existence(
957*26947304SEvan Yan 					    dip, dev_num, func_num);
958*26947304SEvan Yan 				} else if (target_state ==
959*26947304SEvan Yan 				    DDI_HP_CN_STATE_OFFLINE) {
960*26947304SEvan Yan 					ret = pcie_read_only_probe(dip,
961*26947304SEvan Yan 					    cn_name, (dev_info_t **)result);
962*26947304SEvan Yan 				} else
963*26947304SEvan Yan 					ret = DDI_EINVAL;
964*26947304SEvan Yan 
965*26947304SEvan Yan 				goto port_state_done;
966*26947304SEvan Yan 			case DDI_HP_CN_STATE_PORT_PRESENT:
967*26947304SEvan Yan 				if (target_state ==
968*26947304SEvan Yan 				    DDI_HP_CN_STATE_OFFLINE)
969*26947304SEvan Yan 					ret = pcie_read_only_probe(dip,
970*26947304SEvan Yan 					    cn_name, (dev_info_t **)result);
971*26947304SEvan Yan 				else
972*26947304SEvan Yan 					ret = DDI_EINVAL;
973*26947304SEvan Yan 
974*26947304SEvan Yan 				goto port_state_done;
975*26947304SEvan Yan 			default:
976*26947304SEvan Yan 				ASSERT("unexpected state");
977*26947304SEvan Yan 			}
978*26947304SEvan Yan 		} else {
979*26947304SEvan Yan 			/* Downgrade state */
980*26947304SEvan Yan 			switch (curr_state) {
981*26947304SEvan Yan 			case DDI_HP_CN_STATE_PORT_PRESENT:
982*26947304SEvan Yan 			{
983*26947304SEvan Yan 				int dev_num, func_num;
984*26947304SEvan Yan 
985*26947304SEvan Yan 				ret = pcie_hp_get_df_from_port_name(cn_name,
986*26947304SEvan Yan 				    &dev_num, &func_num);
987*26947304SEvan Yan 				if (ret != DDI_SUCCESS)
988*26947304SEvan Yan 					goto port_state_done;
989*26947304SEvan Yan 
990*26947304SEvan Yan 				ret = pcie_hp_check_hardware_existence(dip,
991*26947304SEvan Yan 				    dev_num, func_num);
992*26947304SEvan Yan 
993*26947304SEvan Yan 				goto port_state_done;
994*26947304SEvan Yan 			}
995*26947304SEvan Yan 			case DDI_HP_CN_STATE_OFFLINE:
996*26947304SEvan Yan 				ret = pcie_read_only_unprobe(dip, cn_name);
997*26947304SEvan Yan 
998*26947304SEvan Yan 				goto port_state_done;
999*26947304SEvan Yan 			default:
1000*26947304SEvan Yan 				ASSERT("unexpected state");
1001*26947304SEvan Yan 			}
1002*26947304SEvan Yan 		}
1003*26947304SEvan Yan port_state_done:
1004*26947304SEvan Yan 		*(ddi_hp_cn_state_t *)result = curr_state;
1005*26947304SEvan Yan 		return (ret);
1006*26947304SEvan Yan 	}
1007*26947304SEvan Yan 	default:
1008*26947304SEvan Yan 		break;
1009*26947304SEvan Yan 	}
1010*26947304SEvan Yan 
1011*26947304SEvan Yan 	if (PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p)) {
1012*26947304SEvan Yan 		/* PCIe hotplug */
1013*26947304SEvan Yan 		ret = pciehpc_hp_ops(dip, cn_name, op, arg, result);
1014*26947304SEvan Yan 	} else if (PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p)) {
1015*26947304SEvan Yan 		/* PCI SHPC hotplug */
1016*26947304SEvan Yan 		ret = pcishpc_hp_ops(dip, cn_name, op, arg, result);
1017*26947304SEvan Yan 	} else {
1018*26947304SEvan Yan 		cmn_err(CE_WARN, "pcie_hp_common_ops: op is not supported."
1019*26947304SEvan Yan 		    " dip=%p cn_name=%s"
1020*26947304SEvan Yan 		    " op=%x arg=%p\n", (void *)dip, cn_name, op, arg);
1021*26947304SEvan Yan 		ret = DDI_ENOTSUP;
1022*26947304SEvan Yan 	}
1023*26947304SEvan Yan 
1024*26947304SEvan Yan #if defined(__i386) || defined(__amd64)
1025*26947304SEvan Yan 	/*
1026*26947304SEvan Yan 	 * like in attach, since hotplugging can change error registers,
1027*26947304SEvan Yan 	 * we need to ensure that the proper bits are set on this port
1028*26947304SEvan Yan 	 * after a configure operation
1029*26947304SEvan Yan 	 */
1030*26947304SEvan Yan 	if ((ret == DDI_SUCCESS) && (op == DDI_HPOP_CN_CHANGE_STATE) &&
1031*26947304SEvan Yan 	    (*(ddi_hp_cn_state_t *)arg == DDI_HP_CN_STATE_ENABLED))
1032*26947304SEvan Yan 		pcieb_intel_error_workaround(dip);
1033*26947304SEvan Yan #endif
1034*26947304SEvan Yan 
1035*26947304SEvan Yan 	return (ret);
1036*26947304SEvan Yan }
1037*26947304SEvan Yan 
1038*26947304SEvan Yan /*
1039*26947304SEvan Yan  * pcie_hp_match_dev_func:
1040*26947304SEvan Yan  * Match dip's PCI device number and function number with input ones.
1041*26947304SEvan Yan  */
1042*26947304SEvan Yan static int
1043*26947304SEvan Yan pcie_hp_match_dev_func(dev_info_t *dip, void *hdl)
1044*26947304SEvan Yan {
1045*26947304SEvan Yan 	struct pcie_hp_find_ctrl	*ctrl = (struct pcie_hp_find_ctrl *)hdl;
1046*26947304SEvan Yan 	pci_regspec_t			*pci_rp;
1047*26947304SEvan Yan 	int				length;
1048*26947304SEvan Yan 	int				pci_dev, pci_func;
1049*26947304SEvan Yan 
1050*26947304SEvan Yan 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1051*26947304SEvan Yan 	    "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) {
1052*26947304SEvan Yan 		ctrl->dip = NULL;
1053*26947304SEvan Yan 		return (DDI_WALK_TERMINATE);
1054*26947304SEvan Yan 	}
1055*26947304SEvan Yan 
1056*26947304SEvan Yan 	/* get the PCI device address info */
1057*26947304SEvan Yan 	pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
1058*26947304SEvan Yan 	pci_func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
1059*26947304SEvan Yan 
1060*26947304SEvan Yan 	/*
1061*26947304SEvan Yan 	 * free the memory allocated by ddi_prop_lookup_int_array
1062*26947304SEvan Yan 	 */
1063*26947304SEvan Yan 	ddi_prop_free(pci_rp);
1064*26947304SEvan Yan 
1065*26947304SEvan Yan 	if ((pci_dev == ctrl->device) && (pci_func == ctrl->function)) {
1066*26947304SEvan Yan 		/* found the match for the specified device address */
1067*26947304SEvan Yan 		ctrl->dip = dip;
1068*26947304SEvan Yan 		return (DDI_WALK_TERMINATE);
1069*26947304SEvan Yan 	}
1070*26947304SEvan Yan 
1071*26947304SEvan Yan 	/*
1072*26947304SEvan Yan 	 * continue the walk to the next sibling to look for a match.
1073*26947304SEvan Yan 	 */
1074*26947304SEvan Yan 	return (DDI_WALK_PRUNECHILD);
1075*26947304SEvan Yan }
1076*26947304SEvan Yan 
1077*26947304SEvan Yan /*
1078*26947304SEvan Yan  * pcie_hp_match_dev:
1079*26947304SEvan Yan  * Match the dip's pci device number with the input dev_num
1080*26947304SEvan Yan  */
1081*26947304SEvan Yan static boolean_t
1082*26947304SEvan Yan pcie_hp_match_dev(dev_info_t *dip, int dev_num)
1083*26947304SEvan Yan {
1084*26947304SEvan Yan 	pci_regspec_t			*pci_rp;
1085*26947304SEvan Yan 	int				length;
1086*26947304SEvan Yan 	int				pci_dev;
1087*26947304SEvan Yan 
1088*26947304SEvan Yan 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1089*26947304SEvan Yan 	    "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) {
1090*26947304SEvan Yan 		return (B_FALSE);
1091*26947304SEvan Yan 	}
1092*26947304SEvan Yan 
1093*26947304SEvan Yan 	/* get the PCI device address info */
1094*26947304SEvan Yan 	pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
1095*26947304SEvan Yan 
1096*26947304SEvan Yan 	/*
1097*26947304SEvan Yan 	 * free the memory allocated by ddi_prop_lookup_int_array
1098*26947304SEvan Yan 	 */
1099*26947304SEvan Yan 	ddi_prop_free(pci_rp);
1100*26947304SEvan Yan 
1101*26947304SEvan Yan 	if (pci_dev == dev_num) {
1102*26947304SEvan Yan 		/* found the match for the specified device address */
1103*26947304SEvan Yan 		return (B_TRUE);
1104*26947304SEvan Yan 	}
1105*26947304SEvan Yan 
1106*26947304SEvan Yan 	return (B_FALSE);
1107*26947304SEvan Yan }
1108*26947304SEvan Yan 
1109*26947304SEvan Yan /*
1110*26947304SEvan Yan  * Callback function to match with device number in order to list
1111*26947304SEvan Yan  * occupants under a specific slot
1112*26947304SEvan Yan  */
1113*26947304SEvan Yan static int
1114*26947304SEvan Yan pcie_hp_list_occupants(dev_info_t *dip, void *arg)
1115*26947304SEvan Yan {
1116*26947304SEvan Yan 	pcie_hp_cn_cfg_t	*cn_cfg_p = (pcie_hp_cn_cfg_t *)arg;
1117*26947304SEvan Yan 	pcie_hp_occupant_info_t	*occupant =
1118*26947304SEvan Yan 	    (pcie_hp_occupant_info_t *)cn_cfg_p->cn_private;
1119*26947304SEvan Yan 	pcie_hp_slot_t		*slot_p =
1120*26947304SEvan Yan 	    (pcie_hp_slot_t *)cn_cfg_p->slotp;
1121*26947304SEvan Yan 	int			pci_dev;
1122*26947304SEvan Yan 	pci_regspec_t		*pci_rp;
1123*26947304SEvan Yan 	int			length;
1124*26947304SEvan Yan 	major_t			major;
1125*26947304SEvan Yan 
1126*26947304SEvan Yan 	/*
1127*26947304SEvan Yan 	 * Get the PCI device number information from the devinfo
1128*26947304SEvan Yan 	 * node. Since the node may not have the address field
1129*26947304SEvan Yan 	 * setup (this is done in the DDI_INITCHILD of the parent)
1130*26947304SEvan Yan 	 * we look up the 'reg' property to decode that information.
1131*26947304SEvan Yan 	 */
1132*26947304SEvan Yan 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
1133*26947304SEvan Yan 	    DDI_PROP_DONTPASS, "reg", (int **)&pci_rp,
1134*26947304SEvan Yan 	    (uint_t *)&length) != DDI_PROP_SUCCESS) {
1135*26947304SEvan Yan 		cn_cfg_p->rv = DDI_FAILURE;
1136*26947304SEvan Yan 		cn_cfg_p->dip = dip;
1137*26947304SEvan Yan 		return (DDI_WALK_TERMINATE);
1138*26947304SEvan Yan 	}
1139*26947304SEvan Yan 
1140*26947304SEvan Yan 	/* get the pci device id information */
1141*26947304SEvan Yan 	pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
1142*26947304SEvan Yan 
1143*26947304SEvan Yan 	/*
1144*26947304SEvan Yan 	 * free the memory allocated by ddi_prop_lookup_int_array
1145*26947304SEvan Yan 	 */
1146*26947304SEvan Yan 	ddi_prop_free(pci_rp);
1147*26947304SEvan Yan 
1148*26947304SEvan Yan 	/*
1149*26947304SEvan Yan 	 * Match the node for the device number of the slot.
1150*26947304SEvan Yan 	 */
1151*26947304SEvan Yan 	if (pci_dev == slot_p->hs_device_num) {
1152*26947304SEvan Yan 
1153*26947304SEvan Yan 		major = ddi_driver_major(dip);
1154*26947304SEvan Yan 
1155*26947304SEvan Yan 		/*
1156*26947304SEvan Yan 		 * If the node is not yet attached, then don't list it
1157*26947304SEvan Yan 		 * as an occupant. This is valid, since nothing can be
1158*26947304SEvan Yan 		 * consuming it until it is attached, and cfgadm will
1159*26947304SEvan Yan 		 * ask for the property explicitly which will cause it
1160*26947304SEvan Yan 		 * to be re-freshed right before checking with rcm.
1161*26947304SEvan Yan 		 */
1162*26947304SEvan Yan 		if ((major == DDI_MAJOR_T_NONE) || !i_ddi_devi_attached(dip))
1163*26947304SEvan Yan 			return (DDI_WALK_PRUNECHILD);
1164*26947304SEvan Yan 
1165*26947304SEvan Yan 		/*
1166*26947304SEvan Yan 		 * If we have used all our occupants then print mesage
1167*26947304SEvan Yan 		 * and terminate walk.
1168*26947304SEvan Yan 		 */
1169*26947304SEvan Yan 		if (occupant->i >= PCIE_HP_MAX_OCCUPANTS) {
1170*26947304SEvan Yan 			cmn_err(CE_WARN,
1171*26947304SEvan Yan 			    "pcie (%s%d): unable to list all occupants",
1172*26947304SEvan Yan 			    ddi_driver_name(ddi_get_parent(dip)),
1173*26947304SEvan Yan 			    ddi_get_instance(ddi_get_parent(dip)));
1174*26947304SEvan Yan 			return (DDI_WALK_TERMINATE);
1175*26947304SEvan Yan 		}
1176*26947304SEvan Yan 
1177*26947304SEvan Yan 		/*
1178*26947304SEvan Yan 		 * No need to hold the dip as ddi_walk_devs
1179*26947304SEvan Yan 		 * has already arranged that for us.
1180*26947304SEvan Yan 		 */
1181*26947304SEvan Yan 		occupant->id[occupant->i] =
1182*26947304SEvan Yan 		    kmem_alloc(sizeof (char[MAXPATHLEN]), KM_SLEEP);
1183*26947304SEvan Yan 		(void) ddi_pathname(dip, (char *)occupant->id[occupant->i]);
1184*26947304SEvan Yan 		occupant->i++;
1185*26947304SEvan Yan 	}
1186*26947304SEvan Yan 
1187*26947304SEvan Yan 	/*
1188*26947304SEvan Yan 	 * continue the walk to the next sibling to look for a match
1189*26947304SEvan Yan 	 * or to find other nodes if this card is a multi-function card.
1190*26947304SEvan Yan 	 */
1191*26947304SEvan Yan 	return (DDI_WALK_PRUNECHILD);
1192*26947304SEvan Yan }
1193*26947304SEvan Yan 
1194*26947304SEvan Yan /*
1195*26947304SEvan Yan  * Generate the System Event for ESC_DR_REQ.
1196*26947304SEvan Yan  * One of the consumers is pcidr, it calls to libcfgadm to perform a
1197*26947304SEvan Yan  * configure or unconfigure operation to the AP.
1198*26947304SEvan Yan  */
1199*26947304SEvan Yan void
1200*26947304SEvan Yan pcie_hp_gen_sysevent_req(char *slot_name, int hint,
1201*26947304SEvan Yan     dev_info_t *self, int kmflag)
1202*26947304SEvan Yan {
1203*26947304SEvan Yan 	sysevent_id_t	eid;
1204*26947304SEvan Yan 	nvlist_t	*ev_attr_list = NULL;
1205*26947304SEvan Yan 	char		cn_path[MAXPATHLEN];
1206*26947304SEvan Yan 	char		*ap_id;
1207*26947304SEvan Yan 	int		err, ap_id_len;
1208*26947304SEvan Yan 
1209*26947304SEvan Yan 	/*
1210*26947304SEvan Yan 	 * Minor device name (AP) will be bus path
1211*26947304SEvan Yan 	 * concatenated with slot name
1212*26947304SEvan Yan 	 */
1213*26947304SEvan Yan 	(void) strcpy(cn_path, "/devices");
1214*26947304SEvan Yan 	(void) ddi_pathname(self, cn_path + strlen("/devices"));
1215*26947304SEvan Yan 
1216*26947304SEvan Yan 	ap_id_len = strlen(cn_path) + strlen(":") +
1217*26947304SEvan Yan 	    strlen(slot_name) + 1;
1218*26947304SEvan Yan 	ap_id = kmem_zalloc(ap_id_len, kmflag);
1219*26947304SEvan Yan 	if (ap_id == NULL) {
1220*26947304SEvan Yan 		cmn_err(CE_WARN,
1221*26947304SEvan Yan 		    "%s%d: Failed to allocate memory for AP ID: %s:%s",
1222*26947304SEvan Yan 		    ddi_driver_name(self), ddi_get_instance(self),
1223*26947304SEvan Yan 		    cn_path, slot_name);
1224*26947304SEvan Yan 
1225*26947304SEvan Yan 		return;
1226*26947304SEvan Yan 	}
1227*26947304SEvan Yan 
1228*26947304SEvan Yan 	(void) strcpy(ap_id, cn_path);
1229*26947304SEvan Yan 	(void) strcat(ap_id, ":");
1230*26947304SEvan Yan 	(void) strcat(ap_id, slot_name);
1231*26947304SEvan Yan 
1232*26947304SEvan Yan 	err = nvlist_alloc(&ev_attr_list, NV_UNIQUE_NAME_TYPE, kmflag);
1233*26947304SEvan Yan 	if (err != 0) {
1234*26947304SEvan Yan 		cmn_err(CE_WARN,
1235*26947304SEvan Yan 		    "%s%d: Failed to allocate memory "
1236*26947304SEvan Yan 		    "for event attributes%s", ddi_driver_name(self),
1237*26947304SEvan Yan 		    ddi_get_instance(self), ESC_DR_REQ);
1238*26947304SEvan Yan 
1239*26947304SEvan Yan 		kmem_free(ap_id, ap_id_len);
1240*26947304SEvan Yan 		return;
1241*26947304SEvan Yan 	}
1242*26947304SEvan Yan 
1243*26947304SEvan Yan 	switch (hint) {
1244*26947304SEvan Yan 
1245*26947304SEvan Yan 	case SE_INVESTIGATE_RES:	/* fall through */
1246*26947304SEvan Yan 	case SE_INCOMING_RES:		/* fall through */
1247*26947304SEvan Yan 	case SE_OUTGOING_RES:		/* fall through */
1248*26947304SEvan Yan 
1249*26947304SEvan Yan 		err = nvlist_add_string(ev_attr_list, DR_REQ_TYPE,
1250*26947304SEvan Yan 		    SE_REQ2STR(hint));
1251*26947304SEvan Yan 
1252*26947304SEvan Yan 		if (err != 0) {
1253*26947304SEvan Yan 			cmn_err(CE_WARN,
1254*26947304SEvan Yan 			    "%s%d: Failed to add attr [%s] "
1255*26947304SEvan Yan 			    "for %s event", ddi_driver_name(self),
1256*26947304SEvan Yan 			    ddi_get_instance(self),
1257*26947304SEvan Yan 			    DR_REQ_TYPE, ESC_DR_REQ);
1258*26947304SEvan Yan 
1259*26947304SEvan Yan 			goto done;
1260*26947304SEvan Yan 		}
1261*26947304SEvan Yan 		break;
1262*26947304SEvan Yan 
1263*26947304SEvan Yan 	default:
1264*26947304SEvan Yan 		cmn_err(CE_WARN, "%s%d:  Unknown hint on sysevent",
1265*26947304SEvan Yan 		    ddi_driver_name(self), ddi_get_instance(self));
1266*26947304SEvan Yan 
1267*26947304SEvan Yan 		goto done;
1268*26947304SEvan Yan 	}
1269*26947304SEvan Yan 
1270*26947304SEvan Yan 	/*
1271*26947304SEvan Yan 	 * Add attachment point as attribute (common attribute)
1272*26947304SEvan Yan 	 */
1273*26947304SEvan Yan 
1274*26947304SEvan Yan 	err = nvlist_add_string(ev_attr_list, DR_AP_ID, ap_id);
1275*26947304SEvan Yan 
1276*26947304SEvan Yan 	if (err != 0) {
1277*26947304SEvan Yan 		cmn_err(CE_WARN, "%s%d: Failed to add attr [%s] for %s event",
1278*26947304SEvan Yan 		    ddi_driver_name(self), ddi_get_instance(self),
1279*26947304SEvan Yan 		    DR_AP_ID, EC_DR);
1280*26947304SEvan Yan 
1281*26947304SEvan Yan 		goto done;
1282*26947304SEvan Yan 	}
1283*26947304SEvan Yan 
1284*26947304SEvan Yan 
1285*26947304SEvan Yan 	/*
1286*26947304SEvan Yan 	 * Log this event with sysevent framework.
1287*26947304SEvan Yan 	 */
1288*26947304SEvan Yan 
1289*26947304SEvan Yan 	err = ddi_log_sysevent(self, DDI_VENDOR_SUNW, EC_DR,
1290*26947304SEvan Yan 	    ESC_DR_REQ, ev_attr_list, &eid,
1291*26947304SEvan Yan 	    ((kmflag == KM_SLEEP) ? DDI_SLEEP : DDI_NOSLEEP));
1292*26947304SEvan Yan 	if (err != 0) {
1293*26947304SEvan Yan 		cmn_err(CE_WARN, "%s%d: Failed to log %s event",
1294*26947304SEvan Yan 		    ddi_driver_name(self), ddi_get_instance(self), EC_DR);
1295*26947304SEvan Yan 	}
1296*26947304SEvan Yan 
1297*26947304SEvan Yan done:
1298*26947304SEvan Yan 	nvlist_free(ev_attr_list);
1299*26947304SEvan Yan 	kmem_free(ap_id, ap_id_len);
1300*26947304SEvan Yan }
1301