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