126947304SEvan Yan /*
226947304SEvan Yan  * CDDL HEADER START
326947304SEvan Yan  *
426947304SEvan Yan  * The contents of this file are subject to the terms of the
526947304SEvan Yan  * Common Development and Distribution License (the "License").
626947304SEvan Yan  * You may not use this file except in compliance with the License.
726947304SEvan Yan  *
826947304SEvan Yan  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
926947304SEvan Yan  * or http://www.opensolaris.org/os/licensing.
1026947304SEvan Yan  * See the License for the specific language governing permissions
1126947304SEvan Yan  * and limitations under the License.
1226947304SEvan Yan  *
1326947304SEvan Yan  * When distributing Covered Code, include this CDDL HEADER in each
1426947304SEvan Yan  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1526947304SEvan Yan  * If applicable, add the following below this CDDL HEADER, with the
1626947304SEvan Yan  * fields enclosed by brackets "[]" replaced with your own identifying
1726947304SEvan Yan  * information: Portions Copyright [yyyy] [name of copyright owner]
1826947304SEvan Yan  *
1926947304SEvan Yan  * CDDL HEADER END
2026947304SEvan Yan  */
2126947304SEvan Yan 
2226947304SEvan Yan /*
2326947304SEvan Yan  *  Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2426947304SEvan Yan  *  Use is subject to license terms.
25*b3d69c05SRobert Mustacchi  * Copyright 2019 Joyent, Inc.
2626947304SEvan Yan  */
2726947304SEvan Yan 
2826947304SEvan Yan /*
2926947304SEvan Yan  * This file contains the common hotplug code that is used by Standard
3026947304SEvan Yan  * PCIe and PCI HotPlug Controller code.
3126947304SEvan Yan  *
3226947304SEvan Yan  * NOTE: This file is compiled and delivered through misc/pcie module.
3326947304SEvan Yan  */
3426947304SEvan Yan 
3526947304SEvan Yan #include <sys/types.h>
3626947304SEvan Yan #include <sys/conf.h>
3726947304SEvan Yan #include <sys/kmem.h>
3826947304SEvan Yan #include <sys/debug.h>
3926947304SEvan Yan #include <sys/vtrace.h>
4026947304SEvan Yan #include <sys/autoconf.h>
4126947304SEvan Yan #include <sys/varargs.h>
4226947304SEvan Yan #include <sys/ddi_impldefs.h>
4326947304SEvan Yan #include <sys/time.h>
4426947304SEvan Yan #include <sys/note.h>
4526947304SEvan Yan #include <sys/callb.h>
4626947304SEvan Yan #include <sys/ddi.h>
4726947304SEvan Yan #include <sys/sunddi.h>
4826947304SEvan Yan #include <sys/sunndi.h>
4926947304SEvan Yan #include <sys/sysevent.h>
5026947304SEvan Yan #include <sys/sysevent/eventdefs.h>
5126947304SEvan Yan #include <sys/sysevent/dr.h>
5226947304SEvan Yan #include <sys/pci_impl.h>
5326947304SEvan Yan #include <sys/pci_cap.h>
5426947304SEvan Yan #include <sys/hotplug/pci/pcicfg.h>
5526947304SEvan Yan #include <sys/hotplug/pci/pcie_hp.h>
5626947304SEvan Yan #include <sys/hotplug/pci/pciehpc.h>
5726947304SEvan Yan #include <sys/hotplug/pci/pcishpc.h>
5826947304SEvan Yan #include <io/pciex/pcieb.h>
5926947304SEvan Yan 
6026947304SEvan Yan /* Local functions prototype */
6126947304SEvan Yan static int pcie_hp_list_occupants(dev_info_t *dip, void *arg);
6226947304SEvan Yan static int pcie_hp_register_port(dev_info_t *dip, dev_info_t *pdip,
6326947304SEvan Yan     char *cn_name);
6426947304SEvan Yan static int pcie_hp_register_ports_for_dev(dev_info_t *dip, int device_num);
6526947304SEvan Yan static int pcie_hp_unregister_ports_cb(ddi_hp_cn_info_t *info, void *arg);
6626947304SEvan Yan static int pcie_hp_get_port_state(ddi_hp_cn_info_t *info, void *arg);
6726947304SEvan Yan static int pcie_hp_match_dev_func(dev_info_t *dip, void *hdl);
6826947304SEvan Yan static boolean_t pcie_hp_match_dev(dev_info_t *dip, int dev_num);
6926947304SEvan Yan static int pcie_hp_get_df_from_port_name(char *cn_name, int *dev_num,
7026947304SEvan Yan     int *func_num);
7126947304SEvan Yan static int pcie_hp_create_port_name_num(dev_info_t *dip,
7226947304SEvan Yan     ddi_hp_cn_info_t *cn_info);
7326947304SEvan Yan static int pcie_hp_check_hardware_existence(dev_info_t *dip, int dev_num,
7426947304SEvan Yan     int func_num);
7526947304SEvan Yan 
7626947304SEvan Yan /*
7726947304SEvan Yan  * Global functions (called by other drivers/modules)
7826947304SEvan Yan  */
7926947304SEvan Yan 
8026947304SEvan Yan /*
8126947304SEvan Yan  * return description text for led state
8226947304SEvan Yan  */
8326947304SEvan Yan char *
8426947304SEvan Yan pcie_led_state_text(pcie_hp_led_state_t state)
8526947304SEvan Yan {
8626947304SEvan Yan 	switch (state) {
8726947304SEvan Yan 	case PCIE_HP_LED_ON:
8826947304SEvan Yan 		return (PCIEHPC_PROP_VALUE_ON);
8926947304SEvan Yan 	case PCIE_HP_LED_OFF:
9026947304SEvan Yan 		return (PCIEHPC_PROP_VALUE_OFF);
9126947304SEvan Yan 	case PCIE_HP_LED_BLINK:
9226947304SEvan Yan 	default:
9326947304SEvan Yan 		return (PCIEHPC_PROP_VALUE_BLINK);
9426947304SEvan Yan 	}
9526947304SEvan Yan }
9626947304SEvan Yan 
9726947304SEvan Yan /*
9826947304SEvan Yan  * return description text for slot condition
9926947304SEvan Yan  */
10026947304SEvan Yan char *
10126947304SEvan Yan pcie_slot_condition_text(ap_condition_t condition)
10226947304SEvan Yan {
10326947304SEvan Yan 	switch (condition) {
10426947304SEvan Yan 	case AP_COND_UNKNOWN:
10526947304SEvan Yan 		return (PCIEHPC_PROP_VALUE_UNKNOWN);
10626947304SEvan Yan 	case AP_COND_OK:
10726947304SEvan Yan 		return (PCIEHPC_PROP_VALUE_OK);
10826947304SEvan Yan 	case AP_COND_FAILING:
10926947304SEvan Yan 		return (PCIEHPC_PROP_VALUE_FAILING);
11026947304SEvan Yan 	case AP_COND_FAILED:
11126947304SEvan Yan 		return (PCIEHPC_PROP_VALUE_FAILED);
11226947304SEvan Yan 	case AP_COND_UNUSABLE:
11326947304SEvan Yan 		return (PCIEHPC_PROP_VALUE_UNUSABLE);
11426947304SEvan Yan 	default:
11526947304SEvan Yan 		return (PCIEHPC_PROP_VALUE_UNKNOWN);
11626947304SEvan Yan 	}
11726947304SEvan Yan }
11826947304SEvan Yan 
11926947304SEvan Yan /*
12026947304SEvan Yan  * routine to copy in a nvlist from userland
12126947304SEvan Yan  */
12226947304SEvan Yan int
12326947304SEvan Yan pcie_copyin_nvlist(char *packed_buf, size_t packed_sz, nvlist_t **nvlp)
12426947304SEvan Yan {
12526947304SEvan Yan 	int		ret = DDI_SUCCESS;
12626947304SEvan Yan 	char		*packed;
12726947304SEvan Yan 	nvlist_t	*dest = NULL;
12826947304SEvan Yan 
12926947304SEvan Yan 	if (packed_buf == NULL || packed_sz == 0)
13026947304SEvan Yan 		return (DDI_EINVAL);
13126947304SEvan Yan 
13226947304SEvan Yan 	/* copyin packed nvlist */
13326947304SEvan Yan 	if ((packed = kmem_alloc(packed_sz, KM_SLEEP)) == NULL)
13426947304SEvan Yan 		return (DDI_ENOMEM);
13526947304SEvan Yan 
13626947304SEvan Yan 	if (copyin(packed_buf, packed, packed_sz) != 0) {
13726947304SEvan Yan 		cmn_err(CE_WARN, "pcie_copyin_nvlist: copyin failed.\n");
13826947304SEvan Yan 		ret = DDI_FAILURE;
13926947304SEvan Yan 		goto copyin_cleanup;
14026947304SEvan Yan 	}
14126947304SEvan Yan 
14226947304SEvan Yan 	/* unpack packed nvlist */
14326947304SEvan Yan 	if ((ret = nvlist_unpack(packed, packed_sz, &dest, KM_SLEEP)) != 0) {
14426947304SEvan Yan 		cmn_err(CE_WARN, "pcie_copyin_nvlist: nvlist_unpack "
14526947304SEvan Yan 		    "failed with err %d\n", ret);
14626947304SEvan Yan 		switch (ret) {
14726947304SEvan Yan 		case EINVAL:
14826947304SEvan Yan 		case ENOTSUP:
14926947304SEvan Yan 			ret = DDI_EINVAL;
15026947304SEvan Yan 			goto copyin_cleanup;
15126947304SEvan Yan 		case ENOMEM:
15226947304SEvan Yan 			ret = DDI_ENOMEM;
15326947304SEvan Yan 			goto copyin_cleanup;
15426947304SEvan Yan 		default:
15526947304SEvan Yan 			ret = DDI_FAILURE;
15626947304SEvan Yan 			goto copyin_cleanup;
15726947304SEvan Yan 		}
15826947304SEvan Yan 	}
15926947304SEvan Yan 	*nvlp = dest;
16026947304SEvan Yan copyin_cleanup:
16126947304SEvan Yan 	kmem_free(packed, packed_sz);
16226947304SEvan Yan 	return (ret);
16326947304SEvan Yan }
16426947304SEvan Yan 
16526947304SEvan Yan /*
16626947304SEvan Yan  * routine to copy out a nvlist to userland
16726947304SEvan Yan  */
16826947304SEvan Yan int
16926947304SEvan Yan pcie_copyout_nvlist(nvlist_t *nvl, char *packed_buf, size_t *buf_sz)
17026947304SEvan Yan {
17126947304SEvan Yan 	int	err = 0;
17226947304SEvan Yan 	char	*buf = NULL;
17326947304SEvan Yan 	size_t	packed_sz;
17426947304SEvan Yan 
17526947304SEvan Yan 	if (nvl == NULL || packed_buf == NULL || buf_sz == NULL)
17626947304SEvan Yan 		return (DDI_EINVAL);
17726947304SEvan Yan 
17826947304SEvan Yan 	/* pack nvlist, the library will allocate memory */
17926947304SEvan Yan 	if ((err = nvlist_pack(nvl, &buf, &packed_sz, NV_ENCODE_NATIVE, 0))
18026947304SEvan Yan 	    != 0) {
18126947304SEvan Yan 		cmn_err(CE_WARN, "pcie_copyout_nvlist: nvlist_pack "
18226947304SEvan Yan 		    "failed with err %d\n", err);
18326947304SEvan Yan 		switch (err) {
18426947304SEvan Yan 		case EINVAL:
18526947304SEvan Yan 		case ENOTSUP:
18626947304SEvan Yan 			return (DDI_EINVAL);
18726947304SEvan Yan 		case ENOMEM:
18826947304SEvan Yan 			return (DDI_ENOMEM);
18926947304SEvan Yan 		default:
19026947304SEvan Yan 			return (DDI_FAILURE);
19126947304SEvan Yan 		}
19226947304SEvan Yan 	}
19326947304SEvan Yan 	if (packed_sz > *buf_sz) {
19426947304SEvan Yan 		return (DDI_EINVAL);
19526947304SEvan Yan 	}
19626947304SEvan Yan 
19726947304SEvan Yan 	/* copyout packed nvlist */
19826947304SEvan Yan 	if (copyout(buf, packed_buf, packed_sz) != 0) {
19926947304SEvan Yan 		cmn_err(CE_WARN, "pcie_copyout_nvlist: copyout " "failed.\n");
20026947304SEvan Yan 		kmem_free(buf, packed_sz);
20126947304SEvan Yan 		return (DDI_FAILURE);
20226947304SEvan Yan 	}
20326947304SEvan Yan 
20426947304SEvan Yan 	*buf_sz = packed_sz;
20526947304SEvan Yan 	kmem_free(buf, packed_sz);
20626947304SEvan Yan 	return (DDI_SUCCESS);
20726947304SEvan Yan }
20826947304SEvan Yan 
20926947304SEvan Yan /*
21026947304SEvan Yan  * init bus_hp_op entry and init hotpluggable slots & virtual ports
21126947304SEvan Yan  */
21226947304SEvan Yan int
21326947304SEvan Yan pcie_hp_init(dev_info_t *dip, caddr_t arg)
21426947304SEvan Yan {
21526947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
21626947304SEvan Yan 	int		ret = DDI_SUCCESS, count;
21726947304SEvan Yan 	dev_info_t	*cdip;
21826947304SEvan Yan 
21926947304SEvan Yan 	if (PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p)) {
22026947304SEvan Yan 		/* Init hotplug controller */
22126947304SEvan Yan 		ret = pciehpc_init(dip, arg);
22226947304SEvan Yan 	} else if (PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p)) {
22326947304SEvan Yan 		ret = pcishpc_init(dip);
22426947304SEvan Yan 	}
22526947304SEvan Yan 
22626947304SEvan Yan 	if (ret != DDI_SUCCESS) {
227ed11b501SColin Zou - Sun Microsystems - Beijing China 		PCIE_DBG("pcie_hp_init: initialize hotplug "
22826947304SEvan Yan 		    "controller failed with %d\n", ret);
22926947304SEvan Yan 		return (ret);
23026947304SEvan Yan 	}
23126947304SEvan Yan 
23226947304SEvan Yan 	ndi_devi_enter(dip, &count);
23326947304SEvan Yan 
23426947304SEvan Yan 	/* Create port for the first level children */
23526947304SEvan Yan 	cdip = ddi_get_child(dip);
23626947304SEvan Yan 	while (cdip != NULL) {
23726947304SEvan Yan 		if ((ret = pcie_hp_register_port(cdip, dip, NULL))
23826947304SEvan Yan 		    != DDI_SUCCESS) {
23926947304SEvan Yan 			/* stop and cleanup */
24026947304SEvan Yan 			break;
24126947304SEvan Yan 		}
24226947304SEvan Yan 		cdip = ddi_get_next_sibling(cdip);
24326947304SEvan Yan 	}
24426947304SEvan Yan 	ndi_devi_exit(dip, count);
24526947304SEvan Yan 	if (ret != DDI_SUCCESS) {
24626947304SEvan Yan 		cmn_err(CE_WARN, "pcie_hp_init: initialize virtual "
24726947304SEvan Yan 		    "hotplug port failed with %d\n", ret);
24826947304SEvan Yan 		(void) pcie_hp_uninit(dip);
24926947304SEvan Yan 
25026947304SEvan Yan 		return (ret);
25126947304SEvan Yan 	}
25226947304SEvan Yan 
25326947304SEvan Yan 	return (DDI_SUCCESS);
25426947304SEvan Yan }
25526947304SEvan Yan 
25626947304SEvan Yan /*
25726947304SEvan Yan  * uninit the hotpluggable slots and virtual ports
25826947304SEvan Yan  */
25926947304SEvan Yan int
26026947304SEvan Yan pcie_hp_uninit(dev_info_t *dip)
26126947304SEvan Yan {
26226947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
26326947304SEvan Yan 	pcie_hp_unreg_port_t arg;
26426947304SEvan Yan 
26526947304SEvan Yan 	/*
26626947304SEvan Yan 	 * Must set arg.rv to NDI_SUCCESS so that if there's no port
26726947304SEvan Yan 	 * under this dip, we still return success thus the bridge
26826947304SEvan Yan 	 * driver can be successfully detached.
26926947304SEvan Yan 	 *
27026947304SEvan Yan 	 * Note that during the probe PCI configurator calls
27126947304SEvan Yan 	 * ndi_devi_offline() to detach driver for a new probed bridge,
27226947304SEvan Yan 	 * so that it can reprogram the resources for the bridge,
27326947304SEvan Yan 	 * ndi_devi_offline() calls into pcieb_detach() which in turn
27426947304SEvan Yan 	 * calls into this function. In this case there are no ports
27526947304SEvan Yan 	 * created under a new probe bridge dip, as ports are only
27626947304SEvan Yan 	 * created after the configurator finishing probing, thus the
27726947304SEvan Yan 	 * ndi_hp_walk_cn() will see no ports when this is called
27826947304SEvan Yan 	 * from the PCI configurtor.
27926947304SEvan Yan 	 */
28026947304SEvan Yan 	arg.nexus_dip = dip;
28126947304SEvan Yan 	arg.connector_num = DDI_HP_CN_NUM_NONE;
28226947304SEvan Yan 	arg.rv = NDI_SUCCESS;
28326947304SEvan Yan 
28426947304SEvan Yan 	/* tear down all virtual hotplug handles */
28526947304SEvan Yan 	ndi_hp_walk_cn(dip, pcie_hp_unregister_ports_cb, &arg);
28626947304SEvan Yan 
28726947304SEvan Yan 	if (arg.rv != NDI_SUCCESS)
28826947304SEvan Yan 		return (DDI_FAILURE);
28926947304SEvan Yan 
29026947304SEvan Yan 	if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p))
29126947304SEvan Yan 		(void) pciehpc_uninit(dip);
29226947304SEvan Yan 	else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p))
29326947304SEvan Yan 		(void) pcishpc_uninit(dip);
29426947304SEvan Yan 
29526947304SEvan Yan 	return (DDI_SUCCESS);
29626947304SEvan Yan }
29726947304SEvan Yan 
29826947304SEvan Yan /*
29926947304SEvan Yan  * interrupt handler
30026947304SEvan Yan  */
30126947304SEvan Yan int
30226947304SEvan Yan pcie_hp_intr(dev_info_t *dip)
30326947304SEvan Yan {
30426947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
30526947304SEvan Yan 	int		ret = DDI_INTR_UNCLAIMED;
30626947304SEvan Yan 
30726947304SEvan Yan 	if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p))
30826947304SEvan Yan 		ret = pciehpc_intr(dip);
30926947304SEvan Yan 	else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p))
31026947304SEvan Yan 		ret = pcishpc_intr(dip);
31126947304SEvan Yan 
31226947304SEvan Yan 	return (ret);
31326947304SEvan Yan }
31426947304SEvan Yan 
31526947304SEvan Yan /*
31626947304SEvan Yan  * Probe the given PCIe/PCI Hotplug Connection (CN).
31726947304SEvan Yan  */
31826947304SEvan Yan /*ARGSUSED*/
31926947304SEvan Yan int
32026947304SEvan Yan pcie_hp_probe(pcie_hp_slot_t *slot_p)
32126947304SEvan Yan {
32226947304SEvan Yan 	pcie_hp_ctrl_t	*ctrl_p = slot_p->hs_ctrl;
32326947304SEvan Yan 	dev_info_t	*dip = ctrl_p->hc_dip;
32426947304SEvan Yan 
32526947304SEvan Yan 	/*
32626947304SEvan Yan 	 * Call the configurator to probe a given PCI hotplug
32726947304SEvan Yan 	 * Hotplug Connection (CN).
32826947304SEvan Yan 	 */
32926947304SEvan Yan 	if (pcicfg_configure(dip, slot_p->hs_device_num, PCICFG_ALL_FUNC, 0)
33026947304SEvan Yan 	    != PCICFG_SUCCESS) {
33126947304SEvan Yan 		PCIE_DBG("pcie_hp_probe() failed\n");
33226947304SEvan Yan 		return (DDI_FAILURE);
33326947304SEvan Yan 	}
33426947304SEvan Yan 	slot_p->hs_condition = AP_COND_OK;
33526947304SEvan Yan 	pcie_hp_create_occupant_props(dip, makedevice(ddi_driver_major(dip),
33626947304SEvan Yan 	    slot_p->hs_minor), slot_p->hs_device_num);
33726947304SEvan Yan 
33826947304SEvan Yan 	/*
33926947304SEvan Yan 	 * Create ports for the newly probed devices.
34026947304SEvan Yan 	 * Note, this is only for the first level children because the
34126947304SEvan Yan 	 * descendants' ports will be created during bridge driver attach.
34226947304SEvan Yan 	 */
34326947304SEvan Yan 	return (pcie_hp_register_ports_for_dev(dip, slot_p->hs_device_num));
34426947304SEvan Yan }
34526947304SEvan Yan 
34626947304SEvan Yan /*
34726947304SEvan Yan  * Unprobe the given PCIe/PCI Hotplug Connection (CN):
34826947304SEvan Yan  *	1. remove all child device nodes
34926947304SEvan Yan  *	2. unregister all dependent ports
35026947304SEvan Yan  */
35126947304SEvan Yan /*ARGSUSED*/
35226947304SEvan Yan int
35326947304SEvan Yan pcie_hp_unprobe(pcie_hp_slot_t *slot_p)
35426947304SEvan Yan {
35526947304SEvan Yan 	pcie_hp_ctrl_t	*ctrl_p = slot_p->hs_ctrl;
35626947304SEvan Yan 	dev_info_t	*dip = ctrl_p->hc_dip;
35726947304SEvan Yan 	pcie_hp_unreg_port_t arg;
35826947304SEvan Yan 
35926947304SEvan Yan 	/*
36026947304SEvan Yan 	 * Call the configurator to unprobe a given PCI hotplug
36126947304SEvan Yan 	 * Hotplug Connection (CN).
36226947304SEvan Yan 	 */
36326947304SEvan Yan 	if (pcicfg_unconfigure(dip, slot_p->hs_device_num, PCICFG_ALL_FUNC, 0)
36426947304SEvan Yan 	    != PCICFG_SUCCESS) {
36526947304SEvan Yan 		PCIE_DBG("pcie_hp_unprobe() failed\n");
36626947304SEvan Yan 		return (DDI_FAILURE);
36726947304SEvan Yan 	}
36826947304SEvan Yan 	slot_p->hs_condition = AP_COND_UNKNOWN;
36926947304SEvan Yan 	pcie_hp_delete_occupant_props(dip, makedevice(ddi_driver_major(dip),
37026947304SEvan Yan 	    slot_p->hs_minor));
37126947304SEvan Yan 
37226947304SEvan Yan 	/*
37326947304SEvan Yan 	 * Remove ports for the unprobed devices.
37426947304SEvan Yan 	 * Note, this is only for the first level children because the
37526947304SEvan Yan 	 * descendants' ports were already removed during bridge driver dettach.
37626947304SEvan Yan 	 */
37726947304SEvan Yan 	arg.nexus_dip = dip;
37826947304SEvan Yan 	arg.connector_num = slot_p->hs_info.cn_num;
37926947304SEvan Yan 	arg.rv = NDI_SUCCESS;
38026947304SEvan Yan 	ndi_hp_walk_cn(dip, pcie_hp_unregister_ports_cb, &arg);
38126947304SEvan Yan 
38226947304SEvan Yan 	return (arg.rv == NDI_SUCCESS) ? (DDI_SUCCESS) : (DDI_FAILURE);
38326947304SEvan Yan }
38426947304SEvan Yan 
38526947304SEvan Yan /* Read-only probe: no hardware register programming. */
38626947304SEvan Yan int
38726947304SEvan Yan pcie_read_only_probe(dev_info_t *dip, char *cn_name, dev_info_t **pcdip)
38826947304SEvan Yan {
38926947304SEvan Yan 	long dev, func;
39026947304SEvan Yan 	int ret;
39126947304SEvan Yan 	char *sp;
39226947304SEvan Yan 	dev_info_t *cdip;
39326947304SEvan Yan 
39426947304SEvan Yan 	*pcdip = NULL;
39526947304SEvan Yan 	/*
39626947304SEvan Yan 	 * Parse the string of a pci Port name and get the device number
39726947304SEvan Yan 	 * and function number.
39826947304SEvan Yan 	 */
39926947304SEvan Yan 	if (ddi_strtol(cn_name + 4, &sp, 10, &dev) != 0)
40026947304SEvan Yan 		return (DDI_EINVAL);
40126947304SEvan Yan 	if (ddi_strtol(sp + 1, NULL, 10, &func) != 0)
40226947304SEvan Yan 		return (DDI_EINVAL);
40326947304SEvan Yan 
40426947304SEvan Yan 	ret = pcicfg_configure(dip, (int)dev, (int)func,
40526947304SEvan Yan 	    PCICFG_FLAG_READ_ONLY);
40626947304SEvan Yan 	if (ret == PCICFG_SUCCESS) {
40726947304SEvan Yan 		cdip = pcie_hp_devi_find(dip, (int)dev, (int)func);
40826947304SEvan Yan 		*pcdip = cdip;
40926947304SEvan Yan 	}
41026947304SEvan Yan 	return (ret);
41126947304SEvan Yan }
41226947304SEvan Yan 
41326947304SEvan Yan /* Read-only unprobe: no hardware register programming. */
41426947304SEvan Yan int
41526947304SEvan Yan pcie_read_only_unprobe(dev_info_t *dip, char *cn_name)
41626947304SEvan Yan {
41726947304SEvan Yan 	long dev, func;
41826947304SEvan Yan 	int ret;
41926947304SEvan Yan 	char *sp;
42026947304SEvan Yan 
42126947304SEvan Yan 	/*
42226947304SEvan Yan 	 * Parse the string of a pci Port name and get the device number
42326947304SEvan Yan 	 * and function number.
42426947304SEvan Yan 	 */
42526947304SEvan Yan 	if (ddi_strtol(cn_name + 4, &sp, 10, &dev) != 0)
42626947304SEvan Yan 		return (DDI_EINVAL);
42726947304SEvan Yan 	if (ddi_strtol(sp + 1, NULL, 10, &func) != 0)
42826947304SEvan Yan 		return (DDI_EINVAL);
42926947304SEvan Yan 
43026947304SEvan Yan 	ret = pcicfg_unconfigure(dip, (int)dev, (int)func,
43126947304SEvan Yan 	    PCICFG_FLAG_READ_ONLY);
43226947304SEvan Yan 
43326947304SEvan Yan 	return (ret);
43426947304SEvan Yan }
43526947304SEvan Yan 
43626947304SEvan Yan /* Control structure used to find a device in the devinfo tree */
43726947304SEvan Yan struct pcie_hp_find_ctrl {
43826947304SEvan Yan 	uint_t		device;
43926947304SEvan Yan 	uint_t		function;
44026947304SEvan Yan 	dev_info_t	*dip;
44126947304SEvan Yan };
44226947304SEvan Yan 
44326947304SEvan Yan /*
44426947304SEvan Yan  * find a devinfo node with specified device and function number
44526947304SEvan Yan  * in the device tree under 'dip'
44626947304SEvan Yan  */
44726947304SEvan Yan dev_info_t *
44826947304SEvan Yan pcie_hp_devi_find(dev_info_t *dip, uint_t device, uint_t function)
44926947304SEvan Yan {
45026947304SEvan Yan 	struct pcie_hp_find_ctrl	ctrl;
45126947304SEvan Yan 	int				count;
45226947304SEvan Yan 
45326947304SEvan Yan 	ctrl.device = device;
45426947304SEvan Yan 	ctrl.function = function;
45526947304SEvan Yan 	ctrl.dip = NULL;
45626947304SEvan Yan 
45726947304SEvan Yan 	ndi_devi_enter(dip, &count);
45826947304SEvan Yan 	ddi_walk_devs(ddi_get_child(dip), pcie_hp_match_dev_func,
45926947304SEvan Yan 	    (void *)&ctrl);
46026947304SEvan Yan 	ndi_devi_exit(dip, count);
46126947304SEvan Yan 
46226947304SEvan Yan 	return (ctrl.dip);
46326947304SEvan Yan }
46426947304SEvan Yan 
46526947304SEvan Yan /*
46626947304SEvan Yan  * routine to create 'pci-occupant' property for a hotplug slot
46726947304SEvan Yan  */
46826947304SEvan Yan void
46926947304SEvan Yan pcie_hp_create_occupant_props(dev_info_t *dip, dev_t dev, int pci_dev)
47026947304SEvan Yan {
47126947304SEvan Yan 	pcie_bus_t		*bus_p = PCIE_DIP2BUS(dip);
47226947304SEvan Yan 	pcie_hp_ctrl_t		*ctrl_p = (pcie_hp_ctrl_t *)bus_p->bus_hp_ctrl;
473*b3d69c05SRobert Mustacchi 	pcie_hp_slot_t		*slotp = NULL;
47426947304SEvan Yan 	pcie_hp_cn_cfg_t	cn_cfg;
47526947304SEvan Yan 	pcie_hp_occupant_info_t	*occupant;
47626947304SEvan Yan 	int			circular, i;
47726947304SEvan Yan 
47826947304SEvan Yan 	ndi_devi_enter(dip, &circular);
47926947304SEvan Yan 
48026947304SEvan Yan 	if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p)) {
48126947304SEvan Yan 		slotp = (ctrl_p && (pci_dev == 0)) ?
48226947304SEvan Yan 		    ctrl_p->hc_slots[pci_dev] : NULL;
48326947304SEvan Yan 	} else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p)) {
48426947304SEvan Yan 		if (ctrl_p) {
48526947304SEvan Yan 			int	slot_num;
48626947304SEvan Yan 
48726947304SEvan Yan 			slot_num = (ctrl_p->hc_device_increases) ?
48826947304SEvan Yan 			    (pci_dev - ctrl_p->hc_device_start) :
48926947304SEvan Yan 			    (pci_dev + ctrl_p->hc_device_start);
49026947304SEvan Yan 
49126947304SEvan Yan 			slotp = ctrl_p->hc_slots[slot_num];
49226947304SEvan Yan 		} else {
49326947304SEvan Yan 			slotp = NULL;
49426947304SEvan Yan 		}
49526947304SEvan Yan 	}
49626947304SEvan Yan 
49726947304SEvan Yan 	if (slotp == NULL)
49826947304SEvan Yan 		return;
49926947304SEvan Yan 
50026947304SEvan Yan 	occupant = kmem_alloc(sizeof (pcie_hp_occupant_info_t), KM_SLEEP);
50126947304SEvan Yan 	occupant->i = 0;
50226947304SEvan Yan 
50326947304SEvan Yan 	cn_cfg.flag = B_FALSE;
50426947304SEvan Yan 	cn_cfg.rv = NDI_SUCCESS;
50526947304SEvan Yan 	cn_cfg.dip = NULL;
50626947304SEvan Yan 	cn_cfg.slotp = (void *)slotp;
50726947304SEvan Yan 	cn_cfg.cn_private = (void *)occupant;
50826947304SEvan Yan 
50926947304SEvan Yan 	ddi_walk_devs(ddi_get_child(dip), pcie_hp_list_occupants,
51026947304SEvan Yan 	    (void *)&cn_cfg);
51126947304SEvan Yan 
51226947304SEvan Yan 	if (occupant->i == 0) {
51326947304SEvan Yan 		/* no occupants right now, need to create stub property */
51426947304SEvan Yan 		char *c[] = { "" };
51526947304SEvan Yan 		(void) ddi_prop_update_string_array(dev, dip, "pci-occupant",
51626947304SEvan Yan 		    c, 1);
51726947304SEvan Yan 	} else {
51826947304SEvan Yan 		(void) ddi_prop_update_string_array(dev, dip, "pci-occupant",
51926947304SEvan Yan 		    occupant->id, occupant->i);
52026947304SEvan Yan 	}
52126947304SEvan Yan 
52226947304SEvan Yan 	for (i = 0; i < occupant->i; i++)
52326947304SEvan Yan 		kmem_free(occupant->id[i], sizeof (char[MAXPATHLEN]));
52426947304SEvan Yan 
52526947304SEvan Yan 	kmem_free(occupant, sizeof (pcie_hp_occupant_info_t));
52626947304SEvan Yan 
52726947304SEvan Yan 	ndi_devi_exit(dip, circular);
52826947304SEvan Yan }
52926947304SEvan Yan 
53026947304SEvan Yan /*
53126947304SEvan Yan  * routine to remove 'pci-occupant' property for a hotplug slot
53226947304SEvan Yan  */
53326947304SEvan Yan void
53426947304SEvan Yan pcie_hp_delete_occupant_props(dev_info_t *dip, dev_t dev)
53526947304SEvan Yan {
53626947304SEvan Yan 	(void) ddi_prop_remove(dev, dip, "pci-occupant");
53726947304SEvan Yan }
53826947304SEvan Yan 
53926947304SEvan Yan /*
54026947304SEvan Yan  * general code to create a minor node, called from hotplug controller
54126947304SEvan Yan  * drivers.
54226947304SEvan Yan  */
54326947304SEvan Yan int
54426947304SEvan Yan pcie_create_minor_node(pcie_hp_ctrl_t *ctrl_p, int slot)
54526947304SEvan Yan {
54626947304SEvan Yan 	dev_info_t		*dip = ctrl_p->hc_dip;
54726947304SEvan Yan 	pcie_hp_slot_t		*slot_p = ctrl_p->hc_slots[slot];
54826947304SEvan Yan 	ddi_hp_cn_info_t	*info_p = &slot_p->hs_info;
54926947304SEvan Yan 
55026947304SEvan Yan 	if (ddi_create_minor_node(dip, info_p->cn_name,
55126947304SEvan Yan 	    S_IFCHR, slot_p->hs_minor,
55226947304SEvan Yan 	    DDI_NT_PCI_ATTACHMENT_POINT, 0) != DDI_SUCCESS) {
55326947304SEvan Yan 		return (DDI_FAILURE);
55426947304SEvan Yan 	}
55526947304SEvan Yan 
55626947304SEvan Yan 	(void) ddi_prop_update_int(DDI_DEV_T_NONE,
55726947304SEvan Yan 	    dip, "ap-names", 1 << slot_p->hs_device_num);
55826947304SEvan Yan 
55926947304SEvan Yan 	return (DDI_SUCCESS);
56026947304SEvan Yan }
56126947304SEvan Yan 
56226947304SEvan Yan /*
56326947304SEvan Yan  * general code to remove a minor node, called from hotplug controller
56426947304SEvan Yan  * drivers.
56526947304SEvan Yan  */
56626947304SEvan Yan void
56726947304SEvan Yan pcie_remove_minor_node(pcie_hp_ctrl_t *ctrl_p, int slot)
56826947304SEvan Yan {
56926947304SEvan Yan 	ddi_remove_minor_node(ctrl_p->hc_dip,
57026947304SEvan Yan 	    ctrl_p->hc_slots[slot]->hs_info.cn_name);
57126947304SEvan Yan }
57226947304SEvan Yan 
57326947304SEvan Yan /*
57426947304SEvan Yan  * Local functions (called within this file)
57526947304SEvan Yan  */
57626947304SEvan Yan 
57726947304SEvan Yan /*
57826947304SEvan Yan  * Register ports for all the children with device number device_num
57926947304SEvan Yan  */
58026947304SEvan Yan static int
58126947304SEvan Yan pcie_hp_register_ports_for_dev(dev_info_t *dip, int device_num)
58226947304SEvan Yan {
58326947304SEvan Yan 	dev_info_t	*cdip;
58426947304SEvan Yan 	int		rv;
58526947304SEvan Yan 
58626947304SEvan Yan 	for (cdip = ddi_get_child(dip); cdip;
58726947304SEvan Yan 	    cdip = ddi_get_next_sibling(cdip)) {
58826947304SEvan Yan 		if (pcie_hp_match_dev(cdip, device_num)) {
58926947304SEvan Yan 			/*
59026947304SEvan Yan 			 * Found the newly probed device under the
59126947304SEvan Yan 			 * current slot. Register a port for it.
59226947304SEvan Yan 			 */
59326947304SEvan Yan 			if ((rv = pcie_hp_register_port(cdip, dip, NULL))
59426947304SEvan Yan 			    != DDI_SUCCESS)
59526947304SEvan Yan 				return (rv);
59626947304SEvan Yan 		} else {
59726947304SEvan Yan 			continue;
59826947304SEvan Yan 		}
59926947304SEvan Yan 	}
60026947304SEvan Yan 
60126947304SEvan Yan 	return (DDI_SUCCESS);
60226947304SEvan Yan }
60326947304SEvan Yan 
60426947304SEvan Yan /*
60526947304SEvan Yan  * Unregister ports of a pci bridge dip, get called from ndi_hp_walk_cn()
60626947304SEvan Yan  *
60726947304SEvan Yan  * If connector_num is specified, then unregister the slot's dependent ports
60826947304SEvan Yan  * only; Otherwise, unregister all ports of a pci bridge dip.
60926947304SEvan Yan  */
61026947304SEvan Yan static int
61126947304SEvan Yan pcie_hp_unregister_ports_cb(ddi_hp_cn_info_t *info, void *arg)
61226947304SEvan Yan {
61326947304SEvan Yan 	pcie_hp_unreg_port_t *unreg_arg = (pcie_hp_unreg_port_t *)arg;
61426947304SEvan Yan 	dev_info_t *dip = unreg_arg->nexus_dip;
61526947304SEvan Yan 	int rv = NDI_SUCCESS;
61626947304SEvan Yan 
61726947304SEvan Yan 	if (info->cn_type != DDI_HP_CN_TYPE_VIRTUAL_PORT) {
61826947304SEvan Yan 		unreg_arg->rv = rv;
61926947304SEvan Yan 		return (DDI_WALK_CONTINUE);
62026947304SEvan Yan 	}
62126947304SEvan Yan 
62226947304SEvan Yan 	if (unreg_arg->connector_num != DDI_HP_CN_NUM_NONE) {
62326947304SEvan Yan 		/* Unregister ports for all unprobed devices under a slot. */
62426947304SEvan Yan 		if (unreg_arg->connector_num == info->cn_num_dpd_on) {
62526947304SEvan Yan 
62626947304SEvan Yan 			rv = ndi_hp_unregister(dip, info->cn_name);
62726947304SEvan Yan 		}
62826947304SEvan Yan 	} else {
62926947304SEvan Yan 
63026947304SEvan Yan 		/* Unregister all ports of a pci bridge dip. */
63126947304SEvan Yan 		rv = ndi_hp_unregister(dip, info->cn_name);
63226947304SEvan Yan 	}
63326947304SEvan Yan 
63426947304SEvan Yan 	unreg_arg->rv = rv;
63526947304SEvan Yan 	if (rv == NDI_SUCCESS)
63626947304SEvan Yan 		return (DDI_WALK_CONTINUE);
63726947304SEvan Yan 	else
63826947304SEvan Yan 		return (DDI_WALK_TERMINATE);
63926947304SEvan Yan }
64026947304SEvan Yan 
64126947304SEvan Yan /*
64226947304SEvan Yan  * Find a port according to cn_name and get the port's state.
64326947304SEvan Yan  */
64426947304SEvan Yan static int
64526947304SEvan Yan pcie_hp_get_port_state(ddi_hp_cn_info_t *info, void *arg)
64626947304SEvan Yan {
64726947304SEvan Yan 	pcie_hp_port_state_t *port = (pcie_hp_port_state_t *)arg;
64826947304SEvan Yan 
64926947304SEvan Yan 	if (info->cn_type != DDI_HP_CN_TYPE_VIRTUAL_PORT)
65026947304SEvan Yan 		return (DDI_WALK_CONTINUE);
65126947304SEvan Yan 
65226947304SEvan Yan 	if (strcmp(info->cn_name, port->cn_name) == 0) {
65326947304SEvan Yan 		/* Matched. */
65426947304SEvan Yan 		port->cn_state = info->cn_state;
65526947304SEvan Yan 		port->rv = DDI_SUCCESS;
65626947304SEvan Yan 
65726947304SEvan Yan 		return (DDI_WALK_TERMINATE);
65826947304SEvan Yan 	}
65926947304SEvan Yan 
66026947304SEvan Yan 	return (DDI_WALK_CONTINUE);
66126947304SEvan Yan }
66226947304SEvan Yan 
66326947304SEvan Yan /*
66426947304SEvan Yan  * Find the physical slot with the given device number;
66526947304SEvan Yan  * return the slot if found.
66626947304SEvan Yan  */
66726947304SEvan Yan static pcie_hp_slot_t *
66826947304SEvan Yan pcie_find_physical_slot(dev_info_t *dip, int dev_num)
66926947304SEvan Yan {
67026947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
67126947304SEvan Yan 	pcie_hp_ctrl_t	*ctrl = PCIE_GET_HP_CTRL(dip);
67226947304SEvan Yan 
67326947304SEvan Yan 	if (PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p)) {
67426947304SEvan Yan 		/* PCIe has only one slot */
67526947304SEvan Yan 		return (dev_num == 0) ? (ctrl->hc_slots[0]) : (NULL);
67626947304SEvan Yan 	} else if (PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p)) {
67726947304SEvan Yan 		for (int slot = 0; slot < ctrl->hc_num_slots_impl; slot++) {
67826947304SEvan Yan 			if (ctrl->hc_slots[slot]->hs_device_num == dev_num) {
67926947304SEvan Yan 				/* found */
68026947304SEvan Yan 				return (ctrl->hc_slots[slot]);
68126947304SEvan Yan 			}
68226947304SEvan Yan 		}
68326947304SEvan Yan 	}
68426947304SEvan Yan 
68526947304SEvan Yan 	return (NULL);
68626947304SEvan Yan }
68726947304SEvan Yan 
68826947304SEvan Yan /*
68926947304SEvan Yan  * setup slot name/slot-number info for the port which is being registered.
69026947304SEvan Yan  */
69126947304SEvan Yan static int
69226947304SEvan Yan pcie_hp_create_port_name_num(dev_info_t *dip, ddi_hp_cn_info_t *cn_info)
69326947304SEvan Yan {
69426947304SEvan Yan 	int		ret, dev_num, func_num, name_len;
69526947304SEvan Yan 	dev_info_t	*pdip = ddi_get_parent(dip);
69626947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(pdip);
69726947304SEvan Yan 	pcie_hp_slot_t	*slot;
69826947304SEvan Yan 	pcie_req_id_t	bdf;
69926947304SEvan Yan 	char		tmp[PCIE_HP_DEV_FUNC_NUM_STRING_LEN];
70026947304SEvan Yan 
70126947304SEvan Yan 	ret = pcie_get_bdf_from_dip(dip, &bdf);
70226947304SEvan Yan 	if (ret != DDI_SUCCESS) {
70326947304SEvan Yan 		return (ret);
70426947304SEvan Yan 	}
70526947304SEvan Yan 	if (PCIE_IS_RP(bus_p) || PCIE_IS_SWD(bus_p) ||
70626947304SEvan Yan 	    PCIE_IS_PCI2PCIE(bus_p)) {
70726947304SEvan Yan 		/*
70826947304SEvan Yan 		 * It is under a PCIe device, devcie number is always 0;
70926947304SEvan Yan 		 * function number might > 8 in ARI supported case.
71026947304SEvan Yan 		 */
71126947304SEvan Yan 		dev_num = 0;
71226947304SEvan Yan 		func_num = (bdf & ((~PCI_REG_BUS_M) >> 8));
71326947304SEvan Yan 	} else {
71426947304SEvan Yan 		dev_num = (bdf & (PCI_REG_DEV_M >> 8)) >> 3;
71526947304SEvan Yan 		func_num = bdf & (PCI_REG_FUNC_M >> 8);
71626947304SEvan Yan 	}
71726947304SEvan Yan 	/*
71826947304SEvan Yan 	 * The string length of dev_num and func_num must be no longer than 4
71926947304SEvan Yan 	 * including the string end mark. (With ARI case considered, e.g.,
72026947304SEvan Yan 	 * dev_num=0x0, func_num=0xff.)
72126947304SEvan Yan 	 */
72226947304SEvan Yan 	(void) snprintf(tmp, PCIE_HP_DEV_FUNC_NUM_STRING_LEN, "%x%x",
72326947304SEvan Yan 	    dev_num, func_num);
72426947304SEvan Yan 	/*
72526947304SEvan Yan 	 * Calculate the length of cn_name.
72626947304SEvan Yan 	 * The format of pci port name is: pci.d,f
72726947304SEvan Yan 	 * d stands for dev_num, f stands for func_num. So the length of the
72826947304SEvan Yan 	 * name string can be calculated as following.
72926947304SEvan Yan 	 */
73026947304SEvan Yan 	name_len = strlen(tmp) + PCIE_HP_PORT_NAME_STRING_LEN + 1;
73126947304SEvan Yan 
73226947304SEvan Yan 	cn_info->cn_name = (char *)kmem_zalloc(name_len, KM_SLEEP);
73326947304SEvan Yan 	(void) snprintf(cn_info->cn_name, name_len, "pci.%x,%x",
73426947304SEvan Yan 	    dev_num, func_num);
73526947304SEvan Yan 	cn_info->cn_num = (dev_num << 8) | func_num;
73626947304SEvan Yan 	slot = pcie_find_physical_slot(pdip, dev_num);
73726947304SEvan Yan 
73826947304SEvan Yan 	cn_info->cn_num_dpd_on = slot ?
73926947304SEvan Yan 	    slot->hs_info.cn_num : DDI_HP_CN_NUM_NONE;
74026947304SEvan Yan 
74126947304SEvan Yan 	return (DDI_SUCCESS);
74226947304SEvan Yan }
74326947304SEvan Yan 
74426947304SEvan Yan /*
74526947304SEvan Yan  * Extract device and function number from port name, whose format is
74626947304SEvan Yan  * something like 'pci.1,0'
74726947304SEvan Yan  */
74826947304SEvan Yan static int
74926947304SEvan Yan pcie_hp_get_df_from_port_name(char *cn_name, int *dev_num, int *func_num)
75026947304SEvan Yan {
75126947304SEvan Yan 	int name_len, ret;
75226947304SEvan Yan 	long d, f;
75326947304SEvan Yan 	char *sp;
75426947304SEvan Yan 
75526947304SEvan Yan 	/* some checks for the input name */
75626947304SEvan Yan 	name_len = strlen(cn_name);
75726947304SEvan Yan 	if ((name_len <= PCIE_HP_PORT_NAME_STRING_LEN) ||
75826947304SEvan Yan 	    (name_len > (PCIE_HP_PORT_NAME_STRING_LEN +
75926947304SEvan Yan 	    PCIE_HP_DEV_FUNC_NUM_STRING_LEN - 1)) ||
76026947304SEvan Yan 	    (strncmp("pci.", cn_name, 4) != 0)) {
76126947304SEvan Yan 		return (DDI_EINVAL);
76226947304SEvan Yan 	}
76326947304SEvan Yan 	ret = ddi_strtol(cn_name + 4, &sp, 10, &d);
76426947304SEvan Yan 	if (ret != DDI_SUCCESS)
76526947304SEvan Yan 		return (ret);
76626947304SEvan Yan 
76726947304SEvan Yan 	if (strncmp(",", sp, 1) != 0)
76826947304SEvan Yan 		return (DDI_EINVAL);
76926947304SEvan Yan 
77026947304SEvan Yan 	ret = ddi_strtol(sp + 1, NULL, 10, &f);
77126947304SEvan Yan 	if (ret != DDI_SUCCESS)
77226947304SEvan Yan 		return (ret);
77326947304SEvan Yan 	*dev_num = (int)d;
77426947304SEvan Yan 	*func_num = (int)f;
77526947304SEvan Yan 
77626947304SEvan Yan 	return (ret);
77726947304SEvan Yan }
77826947304SEvan Yan 
77926947304SEvan Yan /*
78026947304SEvan Yan  * Check/copy cn_name and set connection numbers.
78126947304SEvan Yan  * If it is a valid name, then setup cn_info for the newly created port.
78226947304SEvan Yan  */
78326947304SEvan Yan static int
78426947304SEvan Yan pcie_hp_setup_port_name_num(dev_info_t *pdip, char *cn_name,
78526947304SEvan Yan     ddi_hp_cn_info_t *cn_info)
78626947304SEvan Yan {
78726947304SEvan Yan 	int dev_num, func_num, ret;
78826947304SEvan Yan 	pcie_hp_slot_t *slot;
78926947304SEvan Yan 
79026947304SEvan Yan 	if ((ret = pcie_hp_get_df_from_port_name(cn_name, &dev_num, &func_num))
79126947304SEvan Yan 	    != DDI_SUCCESS)
79226947304SEvan Yan 		return (ret);
79326947304SEvan Yan 
79426947304SEvan Yan 	if (pcie_hp_check_hardware_existence(pdip, dev_num, func_num) ==
79526947304SEvan Yan 	    DDI_SUCCESS) {
79626947304SEvan Yan 		cn_info->cn_state = DDI_HP_CN_STATE_PRESENT;
79726947304SEvan Yan 	} else {
79826947304SEvan Yan 		cn_info->cn_state = DDI_HP_CN_STATE_EMPTY;
79926947304SEvan Yan 	}
80026947304SEvan Yan 
80126947304SEvan Yan 	cn_info->cn_name = ddi_strdup(cn_name, KM_SLEEP);
80226947304SEvan Yan 	cn_info->cn_num = (dev_num << 8) | func_num;
80326947304SEvan Yan 
80426947304SEvan Yan 	slot = pcie_find_physical_slot(pdip, dev_num);
80526947304SEvan Yan 	if (slot) {
80626947304SEvan Yan 		cn_info->cn_num_dpd_on = slot->hs_info.cn_num;
80726947304SEvan Yan 	} else {
80826947304SEvan Yan 		cn_info->cn_num_dpd_on = DDI_HP_CN_NUM_NONE;
80926947304SEvan Yan 	}
81026947304SEvan Yan 	return (DDI_SUCCESS);
81126947304SEvan Yan }
81226947304SEvan Yan 
81326947304SEvan Yan static int
81426947304SEvan Yan ndi2ddi(int n)
81526947304SEvan Yan {
81626947304SEvan Yan 	int ret;
81726947304SEvan Yan 
81826947304SEvan Yan 	switch (n) {
81926947304SEvan Yan 	case NDI_SUCCESS:
82026947304SEvan Yan 		ret = DDI_SUCCESS;
82126947304SEvan Yan 		break;
82226947304SEvan Yan 	case NDI_NOMEM:
82326947304SEvan Yan 		ret = DDI_ENOMEM;
82426947304SEvan Yan 		break;
82526947304SEvan Yan 	case NDI_BUSY:
82626947304SEvan Yan 		ret = DDI_EBUSY;
82726947304SEvan Yan 		break;
82826947304SEvan Yan 	case NDI_EINVAL:
82926947304SEvan Yan 		ret = DDI_EINVAL;
83026947304SEvan Yan 		break;
83126947304SEvan Yan 	case NDI_ENOTSUP:
83226947304SEvan Yan 		ret = DDI_ENOTSUP;
83326947304SEvan Yan 		break;
83426947304SEvan Yan 	case NDI_FAILURE:
83526947304SEvan Yan 	default:
83626947304SEvan Yan 		ret = DDI_FAILURE;
83726947304SEvan Yan 		break;
83826947304SEvan Yan 	}
83926947304SEvan Yan 	return (ret);
84026947304SEvan Yan }
84126947304SEvan Yan 
84226947304SEvan Yan /*
84326947304SEvan Yan  * Common routine to create and register a new port
84426947304SEvan Yan  *
84526947304SEvan Yan  * Create an empty port if dip is NULL, and cn_name needs to be specified in
84626947304SEvan Yan  * this case. Otherwise, create a port mapping to the specified dip, and cn_name
84726947304SEvan Yan  * is not needed in this case.
84826947304SEvan Yan  */
84926947304SEvan Yan static int
85026947304SEvan Yan pcie_hp_register_port(dev_info_t *dip, dev_info_t *pdip, char *cn_name)
85126947304SEvan Yan {
85226947304SEvan Yan 	ddi_hp_cn_info_t	*cn_info;
85326947304SEvan Yan 	int			ret;
85426947304SEvan Yan 
85526947304SEvan Yan 	ASSERT((dip == NULL) != (cn_name == NULL));
85626947304SEvan Yan 	cn_info = kmem_zalloc(sizeof (ddi_hp_cn_info_t), KM_SLEEP);
85726947304SEvan Yan 	if (dip != NULL)
85826947304SEvan Yan 		ret = pcie_hp_create_port_name_num(dip, cn_info);
85926947304SEvan Yan 	else
86026947304SEvan Yan 		ret = pcie_hp_setup_port_name_num(pdip, cn_name, cn_info);
86126947304SEvan Yan 
86226947304SEvan Yan 	if (ret != DDI_SUCCESS) {
86326947304SEvan Yan 		kmem_free(cn_info, sizeof (ddi_hp_cn_info_t));
86426947304SEvan Yan 		return (ret);
86526947304SEvan Yan 	}
86626947304SEvan Yan 
86726947304SEvan Yan 	cn_info->cn_child = dip;
86826947304SEvan Yan 	cn_info->cn_type = DDI_HP_CN_TYPE_VIRTUAL_PORT;
86926947304SEvan Yan 	cn_info->cn_type_str = DDI_HP_CN_TYPE_STR_PORT;
87026947304SEvan Yan 
87126947304SEvan Yan 	ret = ndi_hp_register(pdip, cn_info);
87226947304SEvan Yan 
87326947304SEvan Yan 	kmem_free(cn_info->cn_name, strlen(cn_info->cn_name) + 1);
87426947304SEvan Yan 	kmem_free(cn_info, sizeof (ddi_hp_cn_info_t));
87526947304SEvan Yan 
87626947304SEvan Yan 	return (ndi2ddi(ret));
87726947304SEvan Yan }
87826947304SEvan Yan 
87926947304SEvan Yan /* Check if there is a piece of hardware exist corresponding to the cn_name */
88026947304SEvan Yan static int
88126947304SEvan Yan pcie_hp_check_hardware_existence(dev_info_t *dip, int dev_num, int func_num)
88226947304SEvan Yan {
88326947304SEvan Yan 
88426947304SEvan Yan 	/*
88526947304SEvan Yan 	 * VHPTODO:
88626947304SEvan Yan 	 * According to device and function number, check if there is a hardware
88726947304SEvan Yan 	 * device exists. Currently, this function can not be reached before
88826947304SEvan Yan 	 * we enable state transition to or from "Port-Empty" or "Port-Present"
88926947304SEvan Yan 	 * states. When the pci device type project is integrated, we are going
89026947304SEvan Yan 	 * to call the pci config space access interfaces introduced by it.
89126947304SEvan Yan 	 */
89226947304SEvan Yan 	_NOTE(ARGUNUSED(dip, dev_num, func_num));
89326947304SEvan Yan 
89426947304SEvan Yan 	return (DDI_SUCCESS);
89526947304SEvan Yan }
89626947304SEvan Yan 
89726947304SEvan Yan /*
89826947304SEvan Yan  * Dispatch hotplug commands to different hotplug controller drivers, including
89926947304SEvan Yan  * physical and virtual hotplug operations.
90026947304SEvan Yan  */
90126947304SEvan Yan /* ARGSUSED */
90226947304SEvan Yan int
90326947304SEvan Yan pcie_hp_common_ops(dev_info_t *dip, char *cn_name, ddi_hp_op_t op,
90426947304SEvan Yan     void *arg, void *result)
90526947304SEvan Yan {
90626947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
90726947304SEvan Yan 	int		ret = DDI_SUCCESS;
90826947304SEvan Yan 
90926947304SEvan Yan 	PCIE_DBG("pcie_hp_common_ops: dip=%p cn_name=%s op=%x arg=%p\n",
91026947304SEvan Yan 	    dip, cn_name, op, arg);
91126947304SEvan Yan 
91226947304SEvan Yan 	switch (op) {
91326947304SEvan Yan 	case DDI_HPOP_CN_CREATE_PORT:
91426947304SEvan Yan 	{
91526947304SEvan Yan 		/* create an empty port */
91626947304SEvan Yan 		return (pcie_hp_register_port(NULL, dip, cn_name));
91726947304SEvan Yan 	}
91826947304SEvan Yan 	case DDI_HPOP_CN_CHANGE_STATE:
91926947304SEvan Yan 	{
92026947304SEvan Yan 		ddi_hp_cn_state_t curr_state;
92126947304SEvan Yan 		ddi_hp_cn_state_t target_state = *(ddi_hp_cn_state_t *)arg;
92226947304SEvan Yan 		pcie_hp_port_state_t state_arg;
92326947304SEvan Yan 
92426947304SEvan Yan 		if (target_state < DDI_HP_CN_STATE_PORT_EMPTY) {
92526947304SEvan Yan 			/* this is for physical slot state change */
92626947304SEvan Yan 			break;
92726947304SEvan Yan 		}
92826947304SEvan Yan 		PCIE_DBG("pcie_hp_common_ops: change port state"
92926947304SEvan Yan 		    " dip=%p cn_name=%s"
93026947304SEvan Yan 		    " op=%x arg=%p\n", (void *)dip, cn_name, op, arg);
93126947304SEvan Yan 
93226947304SEvan Yan 		state_arg.rv = DDI_FAILURE;
93326947304SEvan Yan 		state_arg.cn_name = cn_name;
93426947304SEvan Yan 		ndi_hp_walk_cn(dip, pcie_hp_get_port_state, &state_arg);
93526947304SEvan Yan 		if (state_arg.rv != DDI_SUCCESS) {
93626947304SEvan Yan 			/* can not find the port */
93726947304SEvan Yan 			return (DDI_EINVAL);
93826947304SEvan Yan 		}
93926947304SEvan Yan 		curr_state = state_arg.cn_state;
94026947304SEvan Yan 		/*
94126947304SEvan Yan 		 * Check if this is for changing port's state: change to/from
94226947304SEvan Yan 		 * PORT_EMPTY/PRESENT states.
94326947304SEvan Yan 		 */
94426947304SEvan Yan 		if (curr_state < target_state) {
94526947304SEvan Yan 			/* Upgrade state */
94626947304SEvan Yan 			switch (curr_state) {
94726947304SEvan Yan 			case DDI_HP_CN_STATE_PORT_EMPTY:
94826947304SEvan Yan 				if (target_state ==
94926947304SEvan Yan 				    DDI_HP_CN_STATE_PORT_PRESENT) {
95026947304SEvan Yan 					int dev_num, func_num;
95126947304SEvan Yan 
95226947304SEvan Yan 					ret = pcie_hp_get_df_from_port_name(
95326947304SEvan Yan 					    cn_name, &dev_num, &func_num);
95426947304SEvan Yan 					if (ret != DDI_SUCCESS)
95526947304SEvan Yan 						goto port_state_done;
95626947304SEvan Yan 
95726947304SEvan Yan 					ret = pcie_hp_check_hardware_existence(
95826947304SEvan Yan 					    dip, dev_num, func_num);
95926947304SEvan Yan 				} else if (target_state ==
96026947304SEvan Yan 				    DDI_HP_CN_STATE_OFFLINE) {
96126947304SEvan Yan 					ret = pcie_read_only_probe(dip,
96226947304SEvan Yan 					    cn_name, (dev_info_t **)result);
96326947304SEvan Yan 				} else
96426947304SEvan Yan 					ret = DDI_EINVAL;
96526947304SEvan Yan 
96626947304SEvan Yan 				goto port_state_done;
96726947304SEvan Yan 			case DDI_HP_CN_STATE_PORT_PRESENT:
96826947304SEvan Yan 				if (target_state ==
96926947304SEvan Yan 				    DDI_HP_CN_STATE_OFFLINE)
97026947304SEvan Yan 					ret = pcie_read_only_probe(dip,
97126947304SEvan Yan 					    cn_name, (dev_info_t **)result);
97226947304SEvan Yan 				else
97326947304SEvan Yan 					ret = DDI_EINVAL;
97426947304SEvan Yan 
97526947304SEvan Yan 				goto port_state_done;
97626947304SEvan Yan 			default:
97726947304SEvan Yan 				ASSERT("unexpected state");
97826947304SEvan Yan 			}
97926947304SEvan Yan 		} else {
98026947304SEvan Yan 			/* Downgrade state */
98126947304SEvan Yan 			switch (curr_state) {
98226947304SEvan Yan 			case DDI_HP_CN_STATE_PORT_PRESENT:
98326947304SEvan Yan 			{
98426947304SEvan Yan 				int dev_num, func_num;
98526947304SEvan Yan 
98626947304SEvan Yan 				ret = pcie_hp_get_df_from_port_name(cn_name,
98726947304SEvan Yan 				    &dev_num, &func_num);
98826947304SEvan Yan 				if (ret != DDI_SUCCESS)
98926947304SEvan Yan 					goto port_state_done;
99026947304SEvan Yan 
99126947304SEvan Yan 				ret = pcie_hp_check_hardware_existence(dip,
99226947304SEvan Yan 				    dev_num, func_num);
99326947304SEvan Yan 
99426947304SEvan Yan 				goto port_state_done;
99526947304SEvan Yan 			}
99626947304SEvan Yan 			case DDI_HP_CN_STATE_OFFLINE:
99726947304SEvan Yan 				ret = pcie_read_only_unprobe(dip, cn_name);
99826947304SEvan Yan 
99926947304SEvan Yan 				goto port_state_done;
100026947304SEvan Yan 			default:
100126947304SEvan Yan 				ASSERT("unexpected state");
100226947304SEvan Yan 			}
100326947304SEvan Yan 		}
100426947304SEvan Yan port_state_done:
100526947304SEvan Yan 		*(ddi_hp_cn_state_t *)result = curr_state;
100626947304SEvan Yan 		return (ret);
100726947304SEvan Yan 	}
100826947304SEvan Yan 	default:
100926947304SEvan Yan 		break;
101026947304SEvan Yan 	}
101126947304SEvan Yan 
101226947304SEvan Yan 	if (PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p)) {
101326947304SEvan Yan 		/* PCIe hotplug */
101426947304SEvan Yan 		ret = pciehpc_hp_ops(dip, cn_name, op, arg, result);
101526947304SEvan Yan 	} else if (PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p)) {
101626947304SEvan Yan 		/* PCI SHPC hotplug */
101726947304SEvan Yan 		ret = pcishpc_hp_ops(dip, cn_name, op, arg, result);
101826947304SEvan Yan 	} else {
101926947304SEvan Yan 		cmn_err(CE_WARN, "pcie_hp_common_ops: op is not supported."
102026947304SEvan Yan 		    " dip=%p cn_name=%s"
102126947304SEvan Yan 		    " op=%x arg=%p\n", (void *)dip, cn_name, op, arg);
102226947304SEvan Yan 		ret = DDI_ENOTSUP;
102326947304SEvan Yan 	}
102426947304SEvan Yan 
102526947304SEvan Yan #if defined(__i386) || defined(__amd64)
102626947304SEvan Yan 	/*
102726947304SEvan Yan 	 * like in attach, since hotplugging can change error registers,
102826947304SEvan Yan 	 * we need to ensure that the proper bits are set on this port
102926947304SEvan Yan 	 * after a configure operation
103026947304SEvan Yan 	 */
103126947304SEvan Yan 	if ((ret == DDI_SUCCESS) && (op == DDI_HPOP_CN_CHANGE_STATE) &&
103226947304SEvan Yan 	    (*(ddi_hp_cn_state_t *)arg == DDI_HP_CN_STATE_ENABLED))
103326947304SEvan Yan 		pcieb_intel_error_workaround(dip);
103426947304SEvan Yan #endif
103526947304SEvan Yan 
103626947304SEvan Yan 	return (ret);
103726947304SEvan Yan }
103826947304SEvan Yan 
103926947304SEvan Yan /*
104026947304SEvan Yan  * pcie_hp_match_dev_func:
104126947304SEvan Yan  * Match dip's PCI device number and function number with input ones.
104226947304SEvan Yan  */
104326947304SEvan Yan static int
104426947304SEvan Yan pcie_hp_match_dev_func(dev_info_t *dip, void *hdl)
104526947304SEvan Yan {
104626947304SEvan Yan 	struct pcie_hp_find_ctrl	*ctrl = (struct pcie_hp_find_ctrl *)hdl;
104726947304SEvan Yan 	pci_regspec_t			*pci_rp;
104826947304SEvan Yan 	int				length;
104926947304SEvan Yan 	int				pci_dev, pci_func;
105026947304SEvan Yan 
105126947304SEvan Yan 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
105226947304SEvan Yan 	    "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) {
105326947304SEvan Yan 		ctrl->dip = NULL;
105426947304SEvan Yan 		return (DDI_WALK_TERMINATE);
105526947304SEvan Yan 	}
105626947304SEvan Yan 
105726947304SEvan Yan 	/* get the PCI device address info */
105826947304SEvan Yan 	pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
105926947304SEvan Yan 	pci_func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
106026947304SEvan Yan 
106126947304SEvan Yan 	/*
106226947304SEvan Yan 	 * free the memory allocated by ddi_prop_lookup_int_array
106326947304SEvan Yan 	 */
106426947304SEvan Yan 	ddi_prop_free(pci_rp);
106526947304SEvan Yan 
106626947304SEvan Yan 	if ((pci_dev == ctrl->device) && (pci_func == ctrl->function)) {
106726947304SEvan Yan 		/* found the match for the specified device address */
106826947304SEvan Yan 		ctrl->dip = dip;
106926947304SEvan Yan 		return (DDI_WALK_TERMINATE);
107026947304SEvan Yan 	}
107126947304SEvan Yan 
107226947304SEvan Yan 	/*
107326947304SEvan Yan 	 * continue the walk to the next sibling to look for a match.
107426947304SEvan Yan 	 */
107526947304SEvan Yan 	return (DDI_WALK_PRUNECHILD);
107626947304SEvan Yan }
107726947304SEvan Yan 
107826947304SEvan Yan /*
107926947304SEvan Yan  * pcie_hp_match_dev:
108026947304SEvan Yan  * Match the dip's pci device number with the input dev_num
108126947304SEvan Yan  */
108226947304SEvan Yan static boolean_t
108326947304SEvan Yan pcie_hp_match_dev(dev_info_t *dip, int dev_num)
108426947304SEvan Yan {
108526947304SEvan Yan 	pci_regspec_t			*pci_rp;
108626947304SEvan Yan 	int				length;
108726947304SEvan Yan 	int				pci_dev;
108826947304SEvan Yan 
108926947304SEvan Yan 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
109026947304SEvan Yan 	    "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) {
109126947304SEvan Yan 		return (B_FALSE);
109226947304SEvan Yan 	}
109326947304SEvan Yan 
109426947304SEvan Yan 	/* get the PCI device address info */
109526947304SEvan Yan 	pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
109626947304SEvan Yan 
109726947304SEvan Yan 	/*
109826947304SEvan Yan 	 * free the memory allocated by ddi_prop_lookup_int_array
109926947304SEvan Yan 	 */
110026947304SEvan Yan 	ddi_prop_free(pci_rp);
110126947304SEvan Yan 
110226947304SEvan Yan 	if (pci_dev == dev_num) {
110326947304SEvan Yan 		/* found the match for the specified device address */
110426947304SEvan Yan 		return (B_TRUE);
110526947304SEvan Yan 	}
110626947304SEvan Yan 
110726947304SEvan Yan 	return (B_FALSE);
110826947304SEvan Yan }
110926947304SEvan Yan 
111026947304SEvan Yan /*
111126947304SEvan Yan  * Callback function to match with device number in order to list
111226947304SEvan Yan  * occupants under a specific slot
111326947304SEvan Yan  */
111426947304SEvan Yan static int
111526947304SEvan Yan pcie_hp_list_occupants(dev_info_t *dip, void *arg)
111626947304SEvan Yan {
111726947304SEvan Yan 	pcie_hp_cn_cfg_t	*cn_cfg_p = (pcie_hp_cn_cfg_t *)arg;
111826947304SEvan Yan 	pcie_hp_occupant_info_t	*occupant =
111926947304SEvan Yan 	    (pcie_hp_occupant_info_t *)cn_cfg_p->cn_private;
112026947304SEvan Yan 	pcie_hp_slot_t		*slot_p =
112126947304SEvan Yan 	    (pcie_hp_slot_t *)cn_cfg_p->slotp;
112226947304SEvan Yan 	int			pci_dev;
112326947304SEvan Yan 	pci_regspec_t		*pci_rp;
112426947304SEvan Yan 	int			length;
112526947304SEvan Yan 	major_t			major;
112626947304SEvan Yan 
112726947304SEvan Yan 	/*
112826947304SEvan Yan 	 * Get the PCI device number information from the devinfo
112926947304SEvan Yan 	 * node. Since the node may not have the address field
113026947304SEvan Yan 	 * setup (this is done in the DDI_INITCHILD of the parent)
113126947304SEvan Yan 	 * we look up the 'reg' property to decode that information.
113226947304SEvan Yan 	 */
113326947304SEvan Yan 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
113426947304SEvan Yan 	    DDI_PROP_DONTPASS, "reg", (int **)&pci_rp,
113526947304SEvan Yan 	    (uint_t *)&length) != DDI_PROP_SUCCESS) {
113626947304SEvan Yan 		cn_cfg_p->rv = DDI_FAILURE;
113726947304SEvan Yan 		cn_cfg_p->dip = dip;
113826947304SEvan Yan 		return (DDI_WALK_TERMINATE);
113926947304SEvan Yan 	}
114026947304SEvan Yan 
114126947304SEvan Yan 	/* get the pci device id information */
114226947304SEvan Yan 	pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
114326947304SEvan Yan 
114426947304SEvan Yan 	/*
114526947304SEvan Yan 	 * free the memory allocated by ddi_prop_lookup_int_array
114626947304SEvan Yan 	 */
114726947304SEvan Yan 	ddi_prop_free(pci_rp);
114826947304SEvan Yan 
114926947304SEvan Yan 	/*
115026947304SEvan Yan 	 * Match the node for the device number of the slot.
115126947304SEvan Yan 	 */
115226947304SEvan Yan 	if (pci_dev == slot_p->hs_device_num) {
115326947304SEvan Yan 
115426947304SEvan Yan 		major = ddi_driver_major(dip);
115526947304SEvan Yan 
115626947304SEvan Yan 		/*
115726947304SEvan Yan 		 * If the node is not yet attached, then don't list it
115826947304SEvan Yan 		 * as an occupant. This is valid, since nothing can be
115926947304SEvan Yan 		 * consuming it until it is attached, and cfgadm will
116026947304SEvan Yan 		 * ask for the property explicitly which will cause it
116126947304SEvan Yan 		 * to be re-freshed right before checking with rcm.
116226947304SEvan Yan 		 */
116326947304SEvan Yan 		if ((major == DDI_MAJOR_T_NONE) || !i_ddi_devi_attached(dip))
116426947304SEvan Yan 			return (DDI_WALK_PRUNECHILD);
116526947304SEvan Yan 
116626947304SEvan Yan 		/*
116726947304SEvan Yan 		 * If we have used all our occupants then print mesage
116826947304SEvan Yan 		 * and terminate walk.
116926947304SEvan Yan 		 */
117026947304SEvan Yan 		if (occupant->i >= PCIE_HP_MAX_OCCUPANTS) {
117126947304SEvan Yan 			cmn_err(CE_WARN,
117226947304SEvan Yan 			    "pcie (%s%d): unable to list all occupants",
117326947304SEvan Yan 			    ddi_driver_name(ddi_get_parent(dip)),
117426947304SEvan Yan 			    ddi_get_instance(ddi_get_parent(dip)));
117526947304SEvan Yan 			return (DDI_WALK_TERMINATE);
117626947304SEvan Yan 		}
117726947304SEvan Yan 
117826947304SEvan Yan 		/*
117926947304SEvan Yan 		 * No need to hold the dip as ddi_walk_devs
118026947304SEvan Yan 		 * has already arranged that for us.
118126947304SEvan Yan 		 */
118226947304SEvan Yan 		occupant->id[occupant->i] =
118326947304SEvan Yan 		    kmem_alloc(sizeof (char[MAXPATHLEN]), KM_SLEEP);
118426947304SEvan Yan 		(void) ddi_pathname(dip, (char *)occupant->id[occupant->i]);
118526947304SEvan Yan 		occupant->i++;
118626947304SEvan Yan 	}
118726947304SEvan Yan 
118826947304SEvan Yan 	/*
118926947304SEvan Yan 	 * continue the walk to the next sibling to look for a match
119026947304SEvan Yan 	 * or to find other nodes if this card is a multi-function card.
119126947304SEvan Yan 	 */
119226947304SEvan Yan 	return (DDI_WALK_PRUNECHILD);
119326947304SEvan Yan }
119426947304SEvan Yan 
119526947304SEvan Yan /*
119626947304SEvan Yan  * Generate the System Event for ESC_DR_REQ.
119726947304SEvan Yan  * One of the consumers is pcidr, it calls to libcfgadm to perform a
119826947304SEvan Yan  * configure or unconfigure operation to the AP.
119926947304SEvan Yan  */
120026947304SEvan Yan void
120126947304SEvan Yan pcie_hp_gen_sysevent_req(char *slot_name, int hint,
120226947304SEvan Yan     dev_info_t *self, int kmflag)
120326947304SEvan Yan {
120426947304SEvan Yan 	sysevent_id_t	eid;
120526947304SEvan Yan 	nvlist_t	*ev_attr_list = NULL;
120626947304SEvan Yan 	char		cn_path[MAXPATHLEN];
120726947304SEvan Yan 	char		*ap_id;
120826947304SEvan Yan 	int		err, ap_id_len;
120926947304SEvan Yan 
121026947304SEvan Yan 	/*
121126947304SEvan Yan 	 * Minor device name (AP) will be bus path
121226947304SEvan Yan 	 * concatenated with slot name
121326947304SEvan Yan 	 */
121426947304SEvan Yan 	(void) strcpy(cn_path, "/devices");
121526947304SEvan Yan 	(void) ddi_pathname(self, cn_path + strlen("/devices"));
121626947304SEvan Yan 
121726947304SEvan Yan 	ap_id_len = strlen(cn_path) + strlen(":") +
121826947304SEvan Yan 	    strlen(slot_name) + 1;
121926947304SEvan Yan 	ap_id = kmem_zalloc(ap_id_len, kmflag);
122026947304SEvan Yan 	if (ap_id == NULL) {
122126947304SEvan Yan 		cmn_err(CE_WARN,
122226947304SEvan Yan 		    "%s%d: Failed to allocate memory for AP ID: %s:%s",
122326947304SEvan Yan 		    ddi_driver_name(self), ddi_get_instance(self),
122426947304SEvan Yan 		    cn_path, slot_name);
122526947304SEvan Yan 
122626947304SEvan Yan 		return;
122726947304SEvan Yan 	}
122826947304SEvan Yan 
122926947304SEvan Yan 	(void) strcpy(ap_id, cn_path);
123026947304SEvan Yan 	(void) strcat(ap_id, ":");
123126947304SEvan Yan 	(void) strcat(ap_id, slot_name);
123226947304SEvan Yan 
123326947304SEvan Yan 	err = nvlist_alloc(&ev_attr_list, NV_UNIQUE_NAME_TYPE, kmflag);
123426947304SEvan Yan 	if (err != 0) {
123526947304SEvan Yan 		cmn_err(CE_WARN,
123626947304SEvan Yan 		    "%s%d: Failed to allocate memory "
123726947304SEvan Yan 		    "for event attributes%s", ddi_driver_name(self),
123826947304SEvan Yan 		    ddi_get_instance(self), ESC_DR_REQ);
123926947304SEvan Yan 
124026947304SEvan Yan 		kmem_free(ap_id, ap_id_len);
124126947304SEvan Yan 		return;
124226947304SEvan Yan 	}
124326947304SEvan Yan 
124426947304SEvan Yan 	switch (hint) {
124526947304SEvan Yan 
124626947304SEvan Yan 	case SE_INVESTIGATE_RES:	/* fall through */
124726947304SEvan Yan 	case SE_INCOMING_RES:		/* fall through */
124826947304SEvan Yan 	case SE_OUTGOING_RES:		/* fall through */
124926947304SEvan Yan 
125026947304SEvan Yan 		err = nvlist_add_string(ev_attr_list, DR_REQ_TYPE,
125126947304SEvan Yan 		    SE_REQ2STR(hint));
125226947304SEvan Yan 
125326947304SEvan Yan 		if (err != 0) {
125426947304SEvan Yan 			cmn_err(CE_WARN,
125526947304SEvan Yan 			    "%s%d: Failed to add attr [%s] "
125626947304SEvan Yan 			    "for %s event", ddi_driver_name(self),
125726947304SEvan Yan 			    ddi_get_instance(self),
125826947304SEvan Yan 			    DR_REQ_TYPE, ESC_DR_REQ);
125926947304SEvan Yan 
126026947304SEvan Yan 			goto done;
126126947304SEvan Yan 		}
126226947304SEvan Yan 		break;
126326947304SEvan Yan 
126426947304SEvan Yan 	default:
126526947304SEvan Yan 		cmn_err(CE_WARN, "%s%d:  Unknown hint on sysevent",
126626947304SEvan Yan 		    ddi_driver_name(self), ddi_get_instance(self));
126726947304SEvan Yan 
126826947304SEvan Yan 		goto done;
126926947304SEvan Yan 	}
127026947304SEvan Yan 
127126947304SEvan Yan 	/*
127226947304SEvan Yan 	 * Add attachment point as attribute (common attribute)
127326947304SEvan Yan 	 */
127426947304SEvan Yan 
127526947304SEvan Yan 	err = nvlist_add_string(ev_attr_list, DR_AP_ID, ap_id);
127626947304SEvan Yan 
127726947304SEvan Yan 	if (err != 0) {
127826947304SEvan Yan 		cmn_err(CE_WARN, "%s%d: Failed to add attr [%s] for %s event",
127926947304SEvan Yan 		    ddi_driver_name(self), ddi_get_instance(self),
128026947304SEvan Yan 		    DR_AP_ID, EC_DR);
128126947304SEvan Yan 
128226947304SEvan Yan 		goto done;
128326947304SEvan Yan 	}
128426947304SEvan Yan 
128526947304SEvan Yan 
128626947304SEvan Yan 	/*
128726947304SEvan Yan 	 * Log this event with sysevent framework.
128826947304SEvan Yan 	 */
128926947304SEvan Yan 
129026947304SEvan Yan 	err = ddi_log_sysevent(self, DDI_VENDOR_SUNW, EC_DR,
129126947304SEvan Yan 	    ESC_DR_REQ, ev_attr_list, &eid,
129226947304SEvan Yan 	    ((kmflag == KM_SLEEP) ? DDI_SLEEP : DDI_NOSLEEP));
129326947304SEvan Yan 	if (err != 0) {
129426947304SEvan Yan 		cmn_err(CE_WARN, "%s%d: Failed to log %s event",
129526947304SEvan Yan 		    ddi_driver_name(self), ddi_get_instance(self), EC_DR);
129626947304SEvan Yan 	}
129726947304SEvan Yan 
129826947304SEvan Yan done:
129926947304SEvan Yan 	nvlist_free(ev_attr_list);
130026947304SEvan Yan 	kmem_free(ap_id, ap_id_len);
130126947304SEvan Yan }
1302