xref: /onnv-gate/usr/src/uts/sun4u/os/mach_ddi_impl.c (revision 789:b348f31ed315)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
220Sstevel@tonic-gate /*
230Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
280Sstevel@tonic-gate 
290Sstevel@tonic-gate /*
300Sstevel@tonic-gate  * sun4u specific DDI implementation
310Sstevel@tonic-gate  */
320Sstevel@tonic-gate #include <sys/bootconf.h>
330Sstevel@tonic-gate #include <sys/conf.h>
340Sstevel@tonic-gate #include <sys/ddi_subrdefs.h>
350Sstevel@tonic-gate #include <sys/ethernet.h>
360Sstevel@tonic-gate #include <sys/idprom.h>
370Sstevel@tonic-gate #include <sys/machsystm.h>
380Sstevel@tonic-gate #include <sys/modhash.h>
390Sstevel@tonic-gate #include <sys/promif.h>
400Sstevel@tonic-gate #include <sys/prom_plat.h>
410Sstevel@tonic-gate #include <sys/sunndi.h>
420Sstevel@tonic-gate #include <sys/systeminfo.h>
430Sstevel@tonic-gate #include <sys/fpu/fpusystm.h>
440Sstevel@tonic-gate #include <sys/vm.h>
450Sstevel@tonic-gate #include <sys/fs/dv_node.h>
460Sstevel@tonic-gate #include <sys/fs/snode.h>
470Sstevel@tonic-gate 
480Sstevel@tonic-gate /*
490Sstevel@tonic-gate  * Favored drivers of this implementation
500Sstevel@tonic-gate  * architecture.  These drivers MUST be present for
510Sstevel@tonic-gate  * the system to boot at all.
520Sstevel@tonic-gate  */
530Sstevel@tonic-gate char *impl_module_list[] = {
540Sstevel@tonic-gate 	"rootnex",
550Sstevel@tonic-gate 	"options",
560Sstevel@tonic-gate 	"sad",		/* Referenced via init_tbl[] */
570Sstevel@tonic-gate 	"pseudo",
580Sstevel@tonic-gate 	"clone",
590Sstevel@tonic-gate 	"scsi_vhci",
600Sstevel@tonic-gate 	(char *)0
610Sstevel@tonic-gate };
620Sstevel@tonic-gate 
630Sstevel@tonic-gate /*
640Sstevel@tonic-gate  * These strings passed to not_serviced in locore.s
650Sstevel@tonic-gate  */
660Sstevel@tonic-gate const char busname_ovec[] = "onboard ";
670Sstevel@tonic-gate const char busname_svec[] = "SBus ";
680Sstevel@tonic-gate const char busname_vec[] = "";
690Sstevel@tonic-gate 
700Sstevel@tonic-gate 
710Sstevel@tonic-gate static uint64_t *intr_map_reg[32];
720Sstevel@tonic-gate 
730Sstevel@tonic-gate /*
740Sstevel@tonic-gate  * Forward declarations
750Sstevel@tonic-gate  */
760Sstevel@tonic-gate static int getlongprop_buf();
770Sstevel@tonic-gate static int get_boardnum(int nid, dev_info_t *par);
780Sstevel@tonic-gate 
790Sstevel@tonic-gate /*
800Sstevel@tonic-gate  * Check the status of the device node passed as an argument.
810Sstevel@tonic-gate  *
820Sstevel@tonic-gate  *	if ((status is OKAY) || (status is DISABLED))
830Sstevel@tonic-gate  *		return DDI_SUCCESS
840Sstevel@tonic-gate  *	else
850Sstevel@tonic-gate  *		print a warning and return DDI_FAILURE
860Sstevel@tonic-gate  */
870Sstevel@tonic-gate /*ARGSUSED*/
880Sstevel@tonic-gate int
890Sstevel@tonic-gate check_status(int id, char *buf, dev_info_t *parent)
900Sstevel@tonic-gate {
910Sstevel@tonic-gate 	char status_buf[64];
920Sstevel@tonic-gate 	char devtype_buf[OBP_MAXPROPNAME];
930Sstevel@tonic-gate 	char board_buf[32];
940Sstevel@tonic-gate 	char path[OBP_MAXPATHLEN];
950Sstevel@tonic-gate 	int boardnum;
960Sstevel@tonic-gate 	int retval = DDI_FAILURE;
970Sstevel@tonic-gate 	extern int status_okay(int, char *, int);
980Sstevel@tonic-gate 
990Sstevel@tonic-gate 	/*
1000Sstevel@tonic-gate 	 * is the status okay?
1010Sstevel@tonic-gate 	 */
1020Sstevel@tonic-gate 	if (status_okay(id, status_buf, sizeof (status_buf)))
1030Sstevel@tonic-gate 		return (DDI_SUCCESS);
1040Sstevel@tonic-gate 
1050Sstevel@tonic-gate 	/*
1060Sstevel@tonic-gate 	 * a status property indicating bad memory will be associated
1070Sstevel@tonic-gate 	 * with a node which has a "device_type" property with a value of
1080Sstevel@tonic-gate 	 * "memory-controller". in this situation, return DDI_SUCCESS
1090Sstevel@tonic-gate 	 */
1100Sstevel@tonic-gate 	if (getlongprop_buf(id, OBP_DEVICETYPE, devtype_buf,
1110Sstevel@tonic-gate 	    sizeof (devtype_buf)) > 0) {
1120Sstevel@tonic-gate 		if (strcmp(devtype_buf, "memory-controller") == 0)
1130Sstevel@tonic-gate 			retval = DDI_SUCCESS;
1140Sstevel@tonic-gate 	}
1150Sstevel@tonic-gate 
1160Sstevel@tonic-gate 	/*
1170Sstevel@tonic-gate 	 * get the full OBP pathname of this node
1180Sstevel@tonic-gate 	 */
1190Sstevel@tonic-gate 	if (prom_phandle_to_path((phandle_t)id, path, sizeof (path)) < 0)
1200Sstevel@tonic-gate 		cmn_err(CE_WARN, "prom_phandle_to_path(%d) failed", id);
1210Sstevel@tonic-gate 
1220Sstevel@tonic-gate 	/*
1230Sstevel@tonic-gate 	 * get the board number, if one exists
1240Sstevel@tonic-gate 	 */
1250Sstevel@tonic-gate 	if ((boardnum = get_boardnum(id, parent)) >= 0)
1260Sstevel@tonic-gate 		(void) sprintf(board_buf, " on board %d", boardnum);
1270Sstevel@tonic-gate 	else
1280Sstevel@tonic-gate 		board_buf[0] = '\0';
1290Sstevel@tonic-gate 
1300Sstevel@tonic-gate 	/*
1310Sstevel@tonic-gate 	 * print the status property information
1320Sstevel@tonic-gate 	 */
1330Sstevel@tonic-gate 	cmn_err(CE_WARN, "status '%s' for '%s'%s",
1340Sstevel@tonic-gate 		status_buf, path, board_buf);
1350Sstevel@tonic-gate 	return (retval);
1360Sstevel@tonic-gate }
1370Sstevel@tonic-gate 
1380Sstevel@tonic-gate /*
1390Sstevel@tonic-gate  * determine the board number associated with this nodeid
1400Sstevel@tonic-gate  */
1410Sstevel@tonic-gate static int
1420Sstevel@tonic-gate get_boardnum(int nid, dev_info_t *par)
1430Sstevel@tonic-gate {
1440Sstevel@tonic-gate 	int board_num;
1450Sstevel@tonic-gate 
146*789Sahrens 	if (prom_getprop((pnode_t)nid, OBP_BOARDNUM,
1470Sstevel@tonic-gate 	    (caddr_t)&board_num) != -1)
1480Sstevel@tonic-gate 		return (board_num);
1490Sstevel@tonic-gate 
1500Sstevel@tonic-gate 	/*
1510Sstevel@tonic-gate 	 * Look at current node and up the parent chain
1520Sstevel@tonic-gate 	 * till we find a node with an OBP_BOARDNUM.
1530Sstevel@tonic-gate 	 */
1540Sstevel@tonic-gate 	while (par) {
1550Sstevel@tonic-gate 		nid = ddi_get_nodeid(par);
1560Sstevel@tonic-gate 
157*789Sahrens 		if (prom_getprop((pnode_t)nid, OBP_BOARDNUM,
1580Sstevel@tonic-gate 		    (caddr_t)&board_num) != -1)
1590Sstevel@tonic-gate 			return (board_num);
1600Sstevel@tonic-gate 
1610Sstevel@tonic-gate 		par = ddi_get_parent(par);
1620Sstevel@tonic-gate 	}
1630Sstevel@tonic-gate 	return (-1);
1640Sstevel@tonic-gate }
1650Sstevel@tonic-gate 
1660Sstevel@tonic-gate /*
1670Sstevel@tonic-gate  * Note that this routine does not take into account the endianness
1680Sstevel@tonic-gate  * of the host or the device (or PROM) when retrieving properties.
1690Sstevel@tonic-gate  */
1700Sstevel@tonic-gate static int
1710Sstevel@tonic-gate getlongprop_buf(int id, char *name, char *buf, int maxlen)
1720Sstevel@tonic-gate {
1730Sstevel@tonic-gate 	int size;
1740Sstevel@tonic-gate 
175*789Sahrens 	size = prom_getproplen((pnode_t)id, name);
1760Sstevel@tonic-gate 	if (size <= 0 || (size > maxlen - 1))
1770Sstevel@tonic-gate 		return (-1);
1780Sstevel@tonic-gate 
179*789Sahrens 	if (-1 == prom_getprop((pnode_t)id, name, buf))
1800Sstevel@tonic-gate 		return (-1);
1810Sstevel@tonic-gate 
1820Sstevel@tonic-gate 	/*
1830Sstevel@tonic-gate 	 * Workaround for bugid 1085575 - OBP may return a "name" property
1840Sstevel@tonic-gate 	 * without null terminating the string with '\0'.  When this occurs,
1850Sstevel@tonic-gate 	 * append a '\0' and return (size + 1).
1860Sstevel@tonic-gate 	 */
1870Sstevel@tonic-gate 	if (strcmp("name", name) == 0) {
1880Sstevel@tonic-gate 		if (buf[size - 1] != '\0') {
1890Sstevel@tonic-gate 			buf[size] = '\0';
1900Sstevel@tonic-gate 			size += 1;
1910Sstevel@tonic-gate 		}
1920Sstevel@tonic-gate 	}
1930Sstevel@tonic-gate 
1940Sstevel@tonic-gate 	return (size);
1950Sstevel@tonic-gate }
1960Sstevel@tonic-gate 
1970Sstevel@tonic-gate /*
1980Sstevel@tonic-gate  * Routines to set/get UPA slave only device interrupt mapping registers.
1990Sstevel@tonic-gate  * set_intr_mapping_reg() is called by the UPA master to register the address
2000Sstevel@tonic-gate  * of an interrupt mapping register. The upa id is that of the master. If
2010Sstevel@tonic-gate  * this routine is called on behalf of a slave device, the framework
2020Sstevel@tonic-gate  * determines the upa id of the slave based on that supplied by the master.
2030Sstevel@tonic-gate  *
2040Sstevel@tonic-gate  * get_intr_mapping_reg() is called by the UPA nexus driver on behalf
2050Sstevel@tonic-gate  * of a child device to get and program the interrupt mapping register of
2060Sstevel@tonic-gate  * one of it's child nodes.  It uses the upa id of the child device to
2070Sstevel@tonic-gate  * index into a table of mapping registers.  If the routine is called on
2080Sstevel@tonic-gate  * behalf of a slave device and the mapping register has not been set,
2090Sstevel@tonic-gate  * the framework determines the devinfo node of the corresponding master
2100Sstevel@tonic-gate  * nexus which owns the mapping register of the slave and installs that
2110Sstevel@tonic-gate  * driver.  The device driver which owns the mapping register must call
2120Sstevel@tonic-gate  * set_intr_mapping_reg() in its attach routine to register the slaves
2130Sstevel@tonic-gate  * mapping register with the system.
2140Sstevel@tonic-gate  */
2150Sstevel@tonic-gate void
2160Sstevel@tonic-gate set_intr_mapping_reg(int upaid, uint64_t *addr, int slave)
2170Sstevel@tonic-gate {
2180Sstevel@tonic-gate 	int affin_upaid;
2190Sstevel@tonic-gate 
2200Sstevel@tonic-gate 	/* For UPA master devices, set the mapping reg addr and we're done */
2210Sstevel@tonic-gate 	if (slave == 0) {
2220Sstevel@tonic-gate 		intr_map_reg[upaid] = addr;
2230Sstevel@tonic-gate 		return;
2240Sstevel@tonic-gate 	}
2250Sstevel@tonic-gate 
2260Sstevel@tonic-gate 	/*
2270Sstevel@tonic-gate 	 * If we get here, we're adding an entry for a UPA slave only device.
2280Sstevel@tonic-gate 	 * The UPA id of the device which has affinity with that requesting,
2290Sstevel@tonic-gate 	 * will be the device with the same UPA id minus the slave number.
2300Sstevel@tonic-gate 	 * If the affin_upaid is negative, silently return to the caller.
2310Sstevel@tonic-gate 	 */
2320Sstevel@tonic-gate 	if ((affin_upaid = upaid - slave) < 0)
2330Sstevel@tonic-gate 		return;
2340Sstevel@tonic-gate 
2350Sstevel@tonic-gate 	/*
2360Sstevel@tonic-gate 	 * Load the address of the mapping register in the correct slot
2370Sstevel@tonic-gate 	 * for the slave device.
2380Sstevel@tonic-gate 	 */
2390Sstevel@tonic-gate 	intr_map_reg[affin_upaid] = addr;
2400Sstevel@tonic-gate }
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate uint64_t *
2430Sstevel@tonic-gate get_intr_mapping_reg(int upaid, int slave)
2440Sstevel@tonic-gate {
2450Sstevel@tonic-gate 	int affin_upaid;
2460Sstevel@tonic-gate 	dev_info_t *affin_dip;
2470Sstevel@tonic-gate 	uint64_t *addr = intr_map_reg[upaid];
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate 	/* If we're a UPA master, or we have a valid mapping register. */
2500Sstevel@tonic-gate 	if (!slave || addr != NULL)
2510Sstevel@tonic-gate 		return (addr);
2520Sstevel@tonic-gate 
2530Sstevel@tonic-gate 	/*
2540Sstevel@tonic-gate 	 * We only get here if we're a UPA slave only device whose interrupt
2550Sstevel@tonic-gate 	 * mapping register has not been set.
2560Sstevel@tonic-gate 	 * We need to try and install the nexus whose physical address
2570Sstevel@tonic-gate 	 * space is where the slaves mapping register resides.  They
2580Sstevel@tonic-gate 	 * should call set_intr_mapping_reg() in their xxattach() to register
2590Sstevel@tonic-gate 	 * the mapping register with the system.
2600Sstevel@tonic-gate 	 */
2610Sstevel@tonic-gate 
2620Sstevel@tonic-gate 	/*
2630Sstevel@tonic-gate 	 * We don't know if a single- or multi-interrupt proxy is fielding
2640Sstevel@tonic-gate 	 * our UPA slave interrupt, we must check both cases.
2650Sstevel@tonic-gate 	 * Start out by assuming the multi-interrupt case.
2660Sstevel@tonic-gate 	 * We assume that single- and multi- interrupters are not
2670Sstevel@tonic-gate 	 * overlapping in UPA portid space.
2680Sstevel@tonic-gate 	 */
2690Sstevel@tonic-gate 
2700Sstevel@tonic-gate 	affin_upaid = upaid | 3;
2710Sstevel@tonic-gate 
2720Sstevel@tonic-gate 	/*
2730Sstevel@tonic-gate 	 * We start looking for the multi-interrupter affinity node.
2740Sstevel@tonic-gate 	 * We know it's ONLY a child of the root node since the root
2750Sstevel@tonic-gate 	 * node defines UPA space.
2760Sstevel@tonic-gate 	 */
2770Sstevel@tonic-gate 	for (affin_dip = ddi_get_child(ddi_root_node()); affin_dip;
2780Sstevel@tonic-gate 	    affin_dip = ddi_get_next_sibling(affin_dip))
2790Sstevel@tonic-gate 		if (ddi_prop_get_int(DDI_DEV_T_ANY, affin_dip,
2800Sstevel@tonic-gate 		    DDI_PROP_DONTPASS, "upa-portid", -1) == affin_upaid)
2810Sstevel@tonic-gate 			break;
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate 	if (affin_dip) {
2840Sstevel@tonic-gate 		if (i_ddi_attach_node_hierarchy(affin_dip) == DDI_SUCCESS) {
2850Sstevel@tonic-gate 			/* try again to get the mapping register. */
2860Sstevel@tonic-gate 			addr = intr_map_reg[upaid];
2870Sstevel@tonic-gate 		}
2880Sstevel@tonic-gate 	}
2890Sstevel@tonic-gate 
2900Sstevel@tonic-gate 	/*
2910Sstevel@tonic-gate 	 * If we still don't have a mapping register try single -interrupter
2920Sstevel@tonic-gate 	 * case.
2930Sstevel@tonic-gate 	 */
2940Sstevel@tonic-gate 	if (addr == NULL) {
2950Sstevel@tonic-gate 
2960Sstevel@tonic-gate 		affin_upaid = upaid | 1;
2970Sstevel@tonic-gate 
2980Sstevel@tonic-gate 		for (affin_dip = ddi_get_child(ddi_root_node()); affin_dip;
2990Sstevel@tonic-gate 		    affin_dip = ddi_get_next_sibling(affin_dip))
3000Sstevel@tonic-gate 			if (ddi_prop_get_int(DDI_DEV_T_ANY, affin_dip,
3010Sstevel@tonic-gate 			    DDI_PROP_DONTPASS, "upa-portid", -1) == affin_upaid)
3020Sstevel@tonic-gate 				break;
3030Sstevel@tonic-gate 
3040Sstevel@tonic-gate 		if (affin_dip) {
3050Sstevel@tonic-gate 			if (i_ddi_attach_node_hierarchy(affin_dip)
3060Sstevel@tonic-gate 			    == DDI_SUCCESS) {
3070Sstevel@tonic-gate 				/* try again to get the mapping register. */
3080Sstevel@tonic-gate 				addr = intr_map_reg[upaid];
3090Sstevel@tonic-gate 			}
3100Sstevel@tonic-gate 		}
3110Sstevel@tonic-gate 	}
3120Sstevel@tonic-gate 	return (addr);
3130Sstevel@tonic-gate }
3140Sstevel@tonic-gate 
3150Sstevel@tonic-gate 
3160Sstevel@tonic-gate static struct upa_dma_pfns {
3170Sstevel@tonic-gate 	pfn_t hipfn;
3180Sstevel@tonic-gate 	pfn_t lopfn;
3190Sstevel@tonic-gate } upa_dma_pfn_array[MAX_UPA];
3200Sstevel@tonic-gate 
3210Sstevel@tonic-gate static int upa_dma_pfn_ndx = 0;
3220Sstevel@tonic-gate 
3230Sstevel@tonic-gate /*
3240Sstevel@tonic-gate  * Certain UPA busses cannot accept dma transactions from any other source
3250Sstevel@tonic-gate  * except for memory due to livelock conditions in their hardware. (e.g. sbus
3260Sstevel@tonic-gate  * and PCI). These routines allow devices or busses on the UPA to register
3270Sstevel@tonic-gate  * a physical address block within it's own register space where DMA can be
3280Sstevel@tonic-gate  * performed.  Currently, the FFB is the only such device which supports
3290Sstevel@tonic-gate  * device DMA on the UPA.
3300Sstevel@tonic-gate  */
3310Sstevel@tonic-gate void
3320Sstevel@tonic-gate pf_set_dmacapable(pfn_t hipfn, pfn_t lopfn)
3330Sstevel@tonic-gate {
3340Sstevel@tonic-gate 	int i = upa_dma_pfn_ndx;
3350Sstevel@tonic-gate 
3360Sstevel@tonic-gate 	upa_dma_pfn_ndx++;
3370Sstevel@tonic-gate 
3380Sstevel@tonic-gate 	upa_dma_pfn_array[i].hipfn = hipfn;
3390Sstevel@tonic-gate 	upa_dma_pfn_array[i].lopfn = lopfn;
3400Sstevel@tonic-gate }
3410Sstevel@tonic-gate 
3420Sstevel@tonic-gate void
3430Sstevel@tonic-gate pf_unset_dmacapable(pfn_t pfn)
3440Sstevel@tonic-gate {
3450Sstevel@tonic-gate 	int i;
3460Sstevel@tonic-gate 
3470Sstevel@tonic-gate 	for (i = 0; i < upa_dma_pfn_ndx; i++) {
3480Sstevel@tonic-gate 		if (pfn <= upa_dma_pfn_array[i].hipfn &&
3490Sstevel@tonic-gate 		    pfn >= upa_dma_pfn_array[i].lopfn) {
3500Sstevel@tonic-gate 			upa_dma_pfn_array[i].hipfn =
3510Sstevel@tonic-gate 			    upa_dma_pfn_array[upa_dma_pfn_ndx - 1].hipfn;
3520Sstevel@tonic-gate 			upa_dma_pfn_array[i].lopfn =
3530Sstevel@tonic-gate 			    upa_dma_pfn_array[upa_dma_pfn_ndx - 1].lopfn;
3540Sstevel@tonic-gate 			upa_dma_pfn_ndx--;
3550Sstevel@tonic-gate 			break;
3560Sstevel@tonic-gate 		}
3570Sstevel@tonic-gate 	}
3580Sstevel@tonic-gate }
3590Sstevel@tonic-gate 
3600Sstevel@tonic-gate /*
3610Sstevel@tonic-gate  * This routine should only be called using a pfn that is known to reside
3620Sstevel@tonic-gate  * in IO space.  The function pf_is_memory() can be used to determine this.
3630Sstevel@tonic-gate  */
3640Sstevel@tonic-gate int
3650Sstevel@tonic-gate pf_is_dmacapable(pfn_t pfn)
3660Sstevel@tonic-gate {
3670Sstevel@tonic-gate 	int i, j;
3680Sstevel@tonic-gate 
3690Sstevel@tonic-gate 	/* If the caller passed in a memory pfn, return true. */
3700Sstevel@tonic-gate 	if (pf_is_memory(pfn))
3710Sstevel@tonic-gate 		return (1);
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate 	for (i = upa_dma_pfn_ndx, j = 0; j < i; j++)
3740Sstevel@tonic-gate 		if (pfn <= upa_dma_pfn_array[j].hipfn &&
3750Sstevel@tonic-gate 		    pfn >= upa_dma_pfn_array[j].lopfn)
3760Sstevel@tonic-gate 			return (1);
3770Sstevel@tonic-gate 
3780Sstevel@tonic-gate 	return (0);
3790Sstevel@tonic-gate }
3800Sstevel@tonic-gate 
3810Sstevel@tonic-gate 
3820Sstevel@tonic-gate /*
3830Sstevel@tonic-gate  * Find cpu_id corresponding to the dip of a CPU device node
3840Sstevel@tonic-gate  */
3850Sstevel@tonic-gate int
3860Sstevel@tonic-gate dip_to_cpu_id(dev_info_t *dip, processorid_t *cpu_id)
3870Sstevel@tonic-gate {
388*789Sahrens 	pnode_t		nodeid;
3890Sstevel@tonic-gate 	int		i;
3900Sstevel@tonic-gate 
391*789Sahrens 	nodeid = (pnode_t)ddi_get_nodeid(dip);
3920Sstevel@tonic-gate 	for (i = 0; i < NCPU; i++) {
3930Sstevel@tonic-gate 		if (cpunodes[i].nodeid == nodeid) {
3940Sstevel@tonic-gate 			*cpu_id = i;
3950Sstevel@tonic-gate 			return (DDI_SUCCESS);
3960Sstevel@tonic-gate 		}
3970Sstevel@tonic-gate 	}
3980Sstevel@tonic-gate 	return (DDI_FAILURE);
3990Sstevel@tonic-gate }
4000Sstevel@tonic-gate 
4010Sstevel@tonic-gate /*
4020Sstevel@tonic-gate  * Platform independent DR routines
4030Sstevel@tonic-gate  */
4040Sstevel@tonic-gate 
4050Sstevel@tonic-gate static int
4060Sstevel@tonic-gate ndi2errno(int n)
4070Sstevel@tonic-gate {
4080Sstevel@tonic-gate 	int err = 0;
4090Sstevel@tonic-gate 
4100Sstevel@tonic-gate 	switch (n) {
4110Sstevel@tonic-gate 		case NDI_NOMEM:
4120Sstevel@tonic-gate 			err = ENOMEM;
4130Sstevel@tonic-gate 			break;
4140Sstevel@tonic-gate 		case NDI_BUSY:
4150Sstevel@tonic-gate 			err = EBUSY;
4160Sstevel@tonic-gate 			break;
4170Sstevel@tonic-gate 		case NDI_FAULT:
4180Sstevel@tonic-gate 			err = EFAULT;
4190Sstevel@tonic-gate 			break;
4200Sstevel@tonic-gate 		case NDI_FAILURE:
4210Sstevel@tonic-gate 			err = EIO;
4220Sstevel@tonic-gate 			break;
4230Sstevel@tonic-gate 		case NDI_SUCCESS:
4240Sstevel@tonic-gate 			break;
4250Sstevel@tonic-gate 		case NDI_BADHANDLE:
4260Sstevel@tonic-gate 		default:
4270Sstevel@tonic-gate 			err = EINVAL;
4280Sstevel@tonic-gate 			break;
4290Sstevel@tonic-gate 	}
4300Sstevel@tonic-gate 	return (err);
4310Sstevel@tonic-gate }
4320Sstevel@tonic-gate 
4330Sstevel@tonic-gate /*
4340Sstevel@tonic-gate  * Prom tree node list
4350Sstevel@tonic-gate  */
4360Sstevel@tonic-gate struct ptnode {
437*789Sahrens 	pnode_t		nodeid;
4380Sstevel@tonic-gate 	struct ptnode	*next;
4390Sstevel@tonic-gate };
4400Sstevel@tonic-gate 
4410Sstevel@tonic-gate /*
4420Sstevel@tonic-gate  * Prom tree walk arg
4430Sstevel@tonic-gate  */
4440Sstevel@tonic-gate struct pta {
4450Sstevel@tonic-gate 	dev_info_t	*pdip;
4460Sstevel@tonic-gate 	devi_branch_t	*bp;
4470Sstevel@tonic-gate 	uint_t		flags;
4480Sstevel@tonic-gate 	dev_info_t	*fdip;
4490Sstevel@tonic-gate 	struct ptnode	*head;
4500Sstevel@tonic-gate };
4510Sstevel@tonic-gate 
4520Sstevel@tonic-gate static void
453*789Sahrens visit_node(pnode_t nodeid, struct pta *ap)
4540Sstevel@tonic-gate {
4550Sstevel@tonic-gate 	struct ptnode	**nextp;
456*789Sahrens 	int		(*select)(pnode_t, void *, uint_t);
4570Sstevel@tonic-gate 
4580Sstevel@tonic-gate 	ASSERT(nodeid != OBP_NONODE && nodeid != OBP_BADNODE);
4590Sstevel@tonic-gate 
4600Sstevel@tonic-gate 	select = ap->bp->create.prom_branch_select;
4610Sstevel@tonic-gate 
4620Sstevel@tonic-gate 	ASSERT(select);
4630Sstevel@tonic-gate 
4640Sstevel@tonic-gate 	if (select(nodeid, ap->bp->arg, 0) == DDI_SUCCESS) {
4650Sstevel@tonic-gate 
4660Sstevel@tonic-gate 		for (nextp = &ap->head; *nextp; nextp = &(*nextp)->next)
4670Sstevel@tonic-gate 			;
4680Sstevel@tonic-gate 
4690Sstevel@tonic-gate 		*nextp = kmem_zalloc(sizeof (struct ptnode), KM_SLEEP);
4700Sstevel@tonic-gate 
4710Sstevel@tonic-gate 		(*nextp)->nodeid = nodeid;
4720Sstevel@tonic-gate 	}
4730Sstevel@tonic-gate 
4740Sstevel@tonic-gate 	if ((ap->flags & DEVI_BRANCH_CHILD) == DEVI_BRANCH_CHILD)
4750Sstevel@tonic-gate 		return;
4760Sstevel@tonic-gate 
4770Sstevel@tonic-gate 	nodeid = prom_childnode(nodeid);
4780Sstevel@tonic-gate 	while (nodeid != OBP_NONODE && nodeid != OBP_BADNODE) {
4790Sstevel@tonic-gate 		visit_node(nodeid, ap);
4800Sstevel@tonic-gate 		nodeid = prom_nextnode(nodeid);
4810Sstevel@tonic-gate 	}
4820Sstevel@tonic-gate }
4830Sstevel@tonic-gate 
4840Sstevel@tonic-gate /*ARGSUSED*/
4850Sstevel@tonic-gate static int
4860Sstevel@tonic-gate set_dip_offline(dev_info_t *dip, void *arg)
4870Sstevel@tonic-gate {
4880Sstevel@tonic-gate 	ASSERT(dip);
4890Sstevel@tonic-gate 
490495Scth 	mutex_enter(&(DEVI(dip)->devi_lock));
4910Sstevel@tonic-gate 	if (!DEVI_IS_DEVICE_OFFLINE(dip))
4920Sstevel@tonic-gate 		DEVI_SET_DEVICE_OFFLINE(dip);
493495Scth 	mutex_exit(&(DEVI(dip)->devi_lock));
4940Sstevel@tonic-gate 
4950Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
4960Sstevel@tonic-gate }
4970Sstevel@tonic-gate 
4980Sstevel@tonic-gate /*ARGSUSED*/
4990Sstevel@tonic-gate static int
5000Sstevel@tonic-gate create_prom_branch(void *arg, int has_changed)
5010Sstevel@tonic-gate {
5020Sstevel@tonic-gate 	int		circ, c;
5030Sstevel@tonic-gate 	int		exists, rv;
504*789Sahrens 	pnode_t		nodeid;
5050Sstevel@tonic-gate 	struct ptnode	*tnp;
5060Sstevel@tonic-gate 	dev_info_t	*dip;
5070Sstevel@tonic-gate 	struct pta	*ap = arg;
5080Sstevel@tonic-gate 	devi_branch_t	*bp;
5090Sstevel@tonic-gate 
5100Sstevel@tonic-gate 	ASSERT(ap);
5110Sstevel@tonic-gate 	ASSERT(ap->fdip == NULL);
5120Sstevel@tonic-gate 	ASSERT(ap->pdip && ndi_dev_is_prom_node(ap->pdip));
5130Sstevel@tonic-gate 
5140Sstevel@tonic-gate 	bp = ap->bp;
5150Sstevel@tonic-gate 
5160Sstevel@tonic-gate 	nodeid = ddi_get_nodeid(ap->pdip);
5170Sstevel@tonic-gate 	if (nodeid == OBP_NONODE || nodeid == OBP_BADNODE) {
5180Sstevel@tonic-gate 		cmn_err(CE_WARN, "create_prom_branch: invalid "
5190Sstevel@tonic-gate 		    "nodeid: 0x%x", nodeid);
5200Sstevel@tonic-gate 		return (EINVAL);
5210Sstevel@tonic-gate 	}
5220Sstevel@tonic-gate 
5230Sstevel@tonic-gate 	ap->head = NULL;
5240Sstevel@tonic-gate 
5250Sstevel@tonic-gate 	nodeid = prom_childnode(nodeid);
5260Sstevel@tonic-gate 	while (nodeid != OBP_NONODE && nodeid != OBP_BADNODE) {
5270Sstevel@tonic-gate 		visit_node(nodeid, ap);
5280Sstevel@tonic-gate 		nodeid = prom_nextnode(nodeid);
5290Sstevel@tonic-gate 	}
5300Sstevel@tonic-gate 
5310Sstevel@tonic-gate 	if (ap->head == NULL)
5320Sstevel@tonic-gate 		return (ENODEV);
5330Sstevel@tonic-gate 
5340Sstevel@tonic-gate 	rv = 0;
5350Sstevel@tonic-gate 	while ((tnp = ap->head) != NULL) {
5360Sstevel@tonic-gate 		ap->head = tnp->next;
5370Sstevel@tonic-gate 
5380Sstevel@tonic-gate 		ndi_devi_enter(ap->pdip, &circ);
5390Sstevel@tonic-gate 
5400Sstevel@tonic-gate 		/*
5410Sstevel@tonic-gate 		 * Check if the branch already exists.
5420Sstevel@tonic-gate 		 */
5430Sstevel@tonic-gate 		exists = 0;
5440Sstevel@tonic-gate 		dip = e_ddi_nodeid_to_dip(tnp->nodeid);
5450Sstevel@tonic-gate 		if (dip != NULL) {
5460Sstevel@tonic-gate 			exists = 1;
5470Sstevel@tonic-gate 
5480Sstevel@tonic-gate 			/* Parent is held busy, so release hold */
5490Sstevel@tonic-gate 			ndi_rele_devi(dip);
5500Sstevel@tonic-gate #ifdef	DEBUG
5510Sstevel@tonic-gate 			cmn_err(CE_WARN, "create_prom_branch: dip(%p) exists"
5520Sstevel@tonic-gate 			    " for nodeid 0x%x", (void *)dip, tnp->nodeid);
5530Sstevel@tonic-gate #endif
5540Sstevel@tonic-gate 		} else {
5550Sstevel@tonic-gate 			dip = i_ddi_create_branch(ap->pdip, tnp->nodeid);
5560Sstevel@tonic-gate 		}
5570Sstevel@tonic-gate 
5580Sstevel@tonic-gate 		kmem_free(tnp, sizeof (struct ptnode));
5590Sstevel@tonic-gate 
5600Sstevel@tonic-gate 		if (dip == NULL) {
5610Sstevel@tonic-gate 			ndi_devi_exit(ap->pdip, circ);
5620Sstevel@tonic-gate 			rv = EIO;
5630Sstevel@tonic-gate 			continue;
5640Sstevel@tonic-gate 		}
5650Sstevel@tonic-gate 
5660Sstevel@tonic-gate 		ASSERT(ddi_get_parent(dip) == ap->pdip);
5670Sstevel@tonic-gate 
5680Sstevel@tonic-gate 		/*
5690Sstevel@tonic-gate 		 * Hold the branch if it is not already held
5700Sstevel@tonic-gate 		 */
5710Sstevel@tonic-gate 		if (!exists)
5720Sstevel@tonic-gate 			e_ddi_branch_hold(dip);
5730Sstevel@tonic-gate 
5740Sstevel@tonic-gate 		ASSERT(e_ddi_branch_held(dip));
5750Sstevel@tonic-gate 
5760Sstevel@tonic-gate 		/*
5770Sstevel@tonic-gate 		 * Set all dips in the branch offline so that
5780Sstevel@tonic-gate 		 * only a "configure" operation can attach
5790Sstevel@tonic-gate 		 * the branch
5800Sstevel@tonic-gate 		 */
5810Sstevel@tonic-gate 		(void) set_dip_offline(dip, NULL);
5820Sstevel@tonic-gate 
5830Sstevel@tonic-gate 		ndi_devi_enter(dip, &c);
5840Sstevel@tonic-gate 		ddi_walk_devs(ddi_get_child(dip), set_dip_offline, NULL);
5850Sstevel@tonic-gate 		ndi_devi_exit(dip, c);
5860Sstevel@tonic-gate 
5870Sstevel@tonic-gate 		ndi_devi_exit(ap->pdip, circ);
5880Sstevel@tonic-gate 
5890Sstevel@tonic-gate 		if (ap->flags & DEVI_BRANCH_CONFIGURE) {
5900Sstevel@tonic-gate 			int error = e_ddi_branch_configure(dip, &ap->fdip, 0);
5910Sstevel@tonic-gate 			if (error && rv == 0)
5920Sstevel@tonic-gate 				rv = error;
5930Sstevel@tonic-gate 		}
5940Sstevel@tonic-gate 
5950Sstevel@tonic-gate 		/*
5960Sstevel@tonic-gate 		 * Invoke devi_branch_callback() (if it exists) only for
5970Sstevel@tonic-gate 		 * newly created branches
5980Sstevel@tonic-gate 		 */
5990Sstevel@tonic-gate 		if (bp->devi_branch_callback && !exists)
6000Sstevel@tonic-gate 			bp->devi_branch_callback(dip, bp->arg, 0);
6010Sstevel@tonic-gate 	}
6020Sstevel@tonic-gate 
6030Sstevel@tonic-gate 	return (rv);
6040Sstevel@tonic-gate }
6050Sstevel@tonic-gate 
6060Sstevel@tonic-gate static int
6070Sstevel@tonic-gate sid_node_create(dev_info_t *pdip, devi_branch_t *bp, dev_info_t **rdipp)
6080Sstevel@tonic-gate {
6090Sstevel@tonic-gate 	int			rv, circ, len;
6100Sstevel@tonic-gate 	int			i, flags;
6110Sstevel@tonic-gate 	dev_info_t		*dip;
6120Sstevel@tonic-gate 	char			*nbuf;
6130Sstevel@tonic-gate 	static const char	*noname = "<none>";
6140Sstevel@tonic-gate 
6150Sstevel@tonic-gate 	ASSERT(pdip);
6160Sstevel@tonic-gate 	ASSERT(DEVI_BUSY_OWNED(pdip));
6170Sstevel@tonic-gate 
6180Sstevel@tonic-gate 	flags = 0;
6190Sstevel@tonic-gate 
6200Sstevel@tonic-gate 	/*
6210Sstevel@tonic-gate 	 * Creating the root of a branch ?
6220Sstevel@tonic-gate 	 */
6230Sstevel@tonic-gate 	if (rdipp) {
6240Sstevel@tonic-gate 		*rdipp = NULL;
6250Sstevel@tonic-gate 		flags = DEVI_BRANCH_ROOT;
6260Sstevel@tonic-gate 	}
6270Sstevel@tonic-gate 
6280Sstevel@tonic-gate 	ndi_devi_alloc_sleep(pdip, (char *)noname, DEVI_SID_NODEID, &dip);
6290Sstevel@tonic-gate 	rv = bp->create.sid_branch_create(dip, bp->arg, flags);
6300Sstevel@tonic-gate 
6310Sstevel@tonic-gate 	nbuf = kmem_alloc(OBP_MAXDRVNAME, KM_SLEEP);
6320Sstevel@tonic-gate 
6330Sstevel@tonic-gate 	if (rv == DDI_WALK_ERROR) {
6340Sstevel@tonic-gate 		cmn_err(CE_WARN, "e_ddi_branch_create: Error setting"
6350Sstevel@tonic-gate 		    " properties on devinfo node %p",  (void *)dip);
6360Sstevel@tonic-gate 		goto fail;
6370Sstevel@tonic-gate 	}
6380Sstevel@tonic-gate 
6390Sstevel@tonic-gate 	len = OBP_MAXDRVNAME;
6400Sstevel@tonic-gate 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
6410Sstevel@tonic-gate 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "name", nbuf, &len)
6420Sstevel@tonic-gate 	    != DDI_PROP_SUCCESS) {
6430Sstevel@tonic-gate 		cmn_err(CE_WARN, "e_ddi_branch_create: devinfo node %p has"
6440Sstevel@tonic-gate 		    "no name property", (void *)dip);
6450Sstevel@tonic-gate 		goto fail;
6460Sstevel@tonic-gate 	}
6470Sstevel@tonic-gate 
6480Sstevel@tonic-gate 	ASSERT(i_ddi_node_state(dip) == DS_PROTO);
6490Sstevel@tonic-gate 	if (ndi_devi_set_nodename(dip, nbuf, 0) != NDI_SUCCESS) {
6500Sstevel@tonic-gate 		cmn_err(CE_WARN, "e_ddi_branch_create: cannot set name (%s)"
6510Sstevel@tonic-gate 		    " for devinfo node %p", nbuf, (void *)dip);
6520Sstevel@tonic-gate 		goto fail;
6530Sstevel@tonic-gate 	}
6540Sstevel@tonic-gate 
6550Sstevel@tonic-gate 	kmem_free(nbuf, OBP_MAXDRVNAME);
6560Sstevel@tonic-gate 
6570Sstevel@tonic-gate 	/*
6580Sstevel@tonic-gate 	 * Ignore bind failures just like boot does
6590Sstevel@tonic-gate 	 */
6600Sstevel@tonic-gate 	(void) ndi_devi_bind_driver(dip, 0);
6610Sstevel@tonic-gate 
6620Sstevel@tonic-gate 	switch (rv) {
6630Sstevel@tonic-gate 	case DDI_WALK_CONTINUE:
6640Sstevel@tonic-gate 	case DDI_WALK_PRUNESIB:
6650Sstevel@tonic-gate 		ndi_devi_enter(dip, &circ);
6660Sstevel@tonic-gate 
6670Sstevel@tonic-gate 		i = DDI_WALK_CONTINUE;
6680Sstevel@tonic-gate 		for (; i == DDI_WALK_CONTINUE; ) {
6690Sstevel@tonic-gate 			i = sid_node_create(dip, bp, NULL);
6700Sstevel@tonic-gate 		}
6710Sstevel@tonic-gate 
6720Sstevel@tonic-gate 		ASSERT(i == DDI_WALK_ERROR || i == DDI_WALK_PRUNESIB);
6730Sstevel@tonic-gate 		if (i == DDI_WALK_ERROR)
6740Sstevel@tonic-gate 			rv = i;
6750Sstevel@tonic-gate 		/*
6760Sstevel@tonic-gate 		 * If PRUNESIB stop creating siblings
6770Sstevel@tonic-gate 		 * of dip's child. Subsequent walk behavior
6780Sstevel@tonic-gate 		 * is determined by rv returned by dip.
6790Sstevel@tonic-gate 		 */
6800Sstevel@tonic-gate 
6810Sstevel@tonic-gate 		ndi_devi_exit(dip, circ);
6820Sstevel@tonic-gate 		break;
6830Sstevel@tonic-gate 	case DDI_WALK_TERMINATE:
6840Sstevel@tonic-gate 		/*
6850Sstevel@tonic-gate 		 * Don't create children and ask our parent
6860Sstevel@tonic-gate 		 * to not create siblings either.
6870Sstevel@tonic-gate 		 */
6880Sstevel@tonic-gate 		rv = DDI_WALK_PRUNESIB;
6890Sstevel@tonic-gate 		break;
6900Sstevel@tonic-gate 	case DDI_WALK_PRUNECHILD:
6910Sstevel@tonic-gate 		/*
6920Sstevel@tonic-gate 		 * Don't create children, but ask parent to continue
6930Sstevel@tonic-gate 		 * with siblings.
6940Sstevel@tonic-gate 		 */
6950Sstevel@tonic-gate 		rv = DDI_WALK_CONTINUE;
6960Sstevel@tonic-gate 		break;
6970Sstevel@tonic-gate 	default:
6980Sstevel@tonic-gate 		ASSERT(0);
6990Sstevel@tonic-gate 		break;
7000Sstevel@tonic-gate 	}
7010Sstevel@tonic-gate 
7020Sstevel@tonic-gate 	if (rdipp)
7030Sstevel@tonic-gate 		*rdipp = dip;
7040Sstevel@tonic-gate 
7050Sstevel@tonic-gate 	/*
7060Sstevel@tonic-gate 	 * Set device offline - only the "configure" op should cause an attach
7070Sstevel@tonic-gate 	 */
7080Sstevel@tonic-gate 	(void) set_dip_offline(dip, NULL);
7090Sstevel@tonic-gate 
7100Sstevel@tonic-gate 	return (rv);
7110Sstevel@tonic-gate fail:
7120Sstevel@tonic-gate 	(void) ndi_devi_free(dip);
7130Sstevel@tonic-gate 	kmem_free(nbuf, OBP_MAXDRVNAME);
7140Sstevel@tonic-gate 	return (DDI_WALK_ERROR);
7150Sstevel@tonic-gate }
7160Sstevel@tonic-gate 
7170Sstevel@tonic-gate static int
7180Sstevel@tonic-gate create_sid_branch(
7190Sstevel@tonic-gate 	dev_info_t	*pdip,
7200Sstevel@tonic-gate 	devi_branch_t	*bp,
7210Sstevel@tonic-gate 	dev_info_t	**dipp,
7220Sstevel@tonic-gate 	uint_t		flags)
7230Sstevel@tonic-gate {
7240Sstevel@tonic-gate 	int		rv = 0, state = DDI_WALK_CONTINUE;
7250Sstevel@tonic-gate 	dev_info_t	*rdip;
7260Sstevel@tonic-gate 
7270Sstevel@tonic-gate 	while (state == DDI_WALK_CONTINUE) {
7280Sstevel@tonic-gate 		int	circ;
7290Sstevel@tonic-gate 
7300Sstevel@tonic-gate 		ndi_devi_enter(pdip, &circ);
7310Sstevel@tonic-gate 
7320Sstevel@tonic-gate 		state = sid_node_create(pdip, bp, &rdip);
7330Sstevel@tonic-gate 		if (rdip == NULL) {
7340Sstevel@tonic-gate 			ndi_devi_exit(pdip, circ);
7350Sstevel@tonic-gate 			ASSERT(state == DDI_WALK_ERROR);
7360Sstevel@tonic-gate 			break;
7370Sstevel@tonic-gate 		}
7380Sstevel@tonic-gate 
7390Sstevel@tonic-gate 		e_ddi_branch_hold(rdip);
7400Sstevel@tonic-gate 
7410Sstevel@tonic-gate 		ndi_devi_exit(pdip, circ);
7420Sstevel@tonic-gate 
7430Sstevel@tonic-gate 		if (flags & DEVI_BRANCH_CONFIGURE) {
7440Sstevel@tonic-gate 			int error = e_ddi_branch_configure(rdip, dipp, 0);
7450Sstevel@tonic-gate 			if (error && rv == 0)
7460Sstevel@tonic-gate 				rv = error;
7470Sstevel@tonic-gate 		}
7480Sstevel@tonic-gate 
7490Sstevel@tonic-gate 		/*
7500Sstevel@tonic-gate 		 * devi_branch_callback() is optional
7510Sstevel@tonic-gate 		 */
7520Sstevel@tonic-gate 		if (bp->devi_branch_callback)
7530Sstevel@tonic-gate 			bp->devi_branch_callback(rdip, bp->arg, 0);
7540Sstevel@tonic-gate 	}
7550Sstevel@tonic-gate 
7560Sstevel@tonic-gate 	ASSERT(state == DDI_WALK_ERROR || state == DDI_WALK_PRUNESIB);
7570Sstevel@tonic-gate 
7580Sstevel@tonic-gate 	return (state == DDI_WALK_ERROR ? EIO : rv);
7590Sstevel@tonic-gate }
7600Sstevel@tonic-gate 
7610Sstevel@tonic-gate int
7620Sstevel@tonic-gate e_ddi_branch_create(
7630Sstevel@tonic-gate 	dev_info_t	*pdip,
7640Sstevel@tonic-gate 	devi_branch_t	*bp,
7650Sstevel@tonic-gate 	dev_info_t	**dipp,
7660Sstevel@tonic-gate 	uint_t		flags)
7670Sstevel@tonic-gate {
7680Sstevel@tonic-gate 	int prom_devi, sid_devi, error;
7690Sstevel@tonic-gate 
7700Sstevel@tonic-gate 	if (pdip == NULL || bp == NULL || bp->type == 0)
7710Sstevel@tonic-gate 		return (EINVAL);
7720Sstevel@tonic-gate 
7730Sstevel@tonic-gate 	prom_devi = (bp->type == DEVI_BRANCH_PROM) ? 1 : 0;
7740Sstevel@tonic-gate 	sid_devi = (bp->type == DEVI_BRANCH_SID) ? 1 : 0;
7750Sstevel@tonic-gate 
7760Sstevel@tonic-gate 	if (prom_devi && bp->create.prom_branch_select == NULL)
7770Sstevel@tonic-gate 		return (EINVAL);
7780Sstevel@tonic-gate 	else if (sid_devi && bp->create.sid_branch_create == NULL)
7790Sstevel@tonic-gate 		return (EINVAL);
7800Sstevel@tonic-gate 	else if (!prom_devi && !sid_devi)
7810Sstevel@tonic-gate 		return (EINVAL);
7820Sstevel@tonic-gate 
7830Sstevel@tonic-gate 	if (flags & DEVI_BRANCH_EVENT)
7840Sstevel@tonic-gate 		return (EINVAL);
7850Sstevel@tonic-gate 
7860Sstevel@tonic-gate 	if (prom_devi) {
7870Sstevel@tonic-gate 		struct pta pta = {0};
7880Sstevel@tonic-gate 
7890Sstevel@tonic-gate 		pta.pdip = pdip;
7900Sstevel@tonic-gate 		pta.bp = bp;
7910Sstevel@tonic-gate 		pta.flags = flags;
7920Sstevel@tonic-gate 
7930Sstevel@tonic-gate 		error = prom_tree_access(create_prom_branch, &pta, NULL);
7940Sstevel@tonic-gate 
7950Sstevel@tonic-gate 		if (dipp)
7960Sstevel@tonic-gate 			*dipp = pta.fdip;
7970Sstevel@tonic-gate 		else if (pta.fdip)
7980Sstevel@tonic-gate 			ndi_rele_devi(pta.fdip);
7990Sstevel@tonic-gate 	} else {
8000Sstevel@tonic-gate 		error = create_sid_branch(pdip, bp, dipp, flags);
8010Sstevel@tonic-gate 	}
8020Sstevel@tonic-gate 
8030Sstevel@tonic-gate 	return (error);
8040Sstevel@tonic-gate }
8050Sstevel@tonic-gate 
8060Sstevel@tonic-gate int
8070Sstevel@tonic-gate e_ddi_branch_configure(dev_info_t *rdip, dev_info_t **dipp, uint_t flags)
8080Sstevel@tonic-gate {
8090Sstevel@tonic-gate 	int		circ, rv;
8100Sstevel@tonic-gate 	char		*devnm;
8110Sstevel@tonic-gate 	dev_info_t	*pdip;
8120Sstevel@tonic-gate 
8130Sstevel@tonic-gate 	if (dipp)
8140Sstevel@tonic-gate 		*dipp = NULL;
8150Sstevel@tonic-gate 
8160Sstevel@tonic-gate 	if (rdip == NULL || flags != 0 || (flags & DEVI_BRANCH_EVENT))
8170Sstevel@tonic-gate 		return (EINVAL);
8180Sstevel@tonic-gate 
8190Sstevel@tonic-gate 	pdip = ddi_get_parent(rdip);
8200Sstevel@tonic-gate 
8210Sstevel@tonic-gate 	ndi_devi_enter(pdip, &circ);
8220Sstevel@tonic-gate 
8230Sstevel@tonic-gate 	if (!e_ddi_branch_held(rdip)) {
8240Sstevel@tonic-gate 		ndi_devi_exit(pdip, circ);
8250Sstevel@tonic-gate 		cmn_err(CE_WARN, "e_ddi_branch_configure: "
8260Sstevel@tonic-gate 		    "dip(%p) not held", (void *)rdip);
8270Sstevel@tonic-gate 		return (EINVAL);
8280Sstevel@tonic-gate 	}
8290Sstevel@tonic-gate 
8300Sstevel@tonic-gate 	if (i_ddi_node_state(rdip) < DS_INITIALIZED) {
8310Sstevel@tonic-gate 		/*
8320Sstevel@tonic-gate 		 * First attempt to bind a driver. If we fail, return
8330Sstevel@tonic-gate 		 * success (On some platforms, dips for some device
8340Sstevel@tonic-gate 		 * types (CPUs) may not have a driver)
8350Sstevel@tonic-gate 		 */
8360Sstevel@tonic-gate 		if (ndi_devi_bind_driver(rdip, 0) != NDI_SUCCESS) {
8370Sstevel@tonic-gate 			ndi_devi_exit(pdip, circ);
8380Sstevel@tonic-gate 			return (0);
8390Sstevel@tonic-gate 		}
8400Sstevel@tonic-gate 
8410Sstevel@tonic-gate 		if (ddi_initchild(pdip, rdip) != DDI_SUCCESS) {
8420Sstevel@tonic-gate 			rv = NDI_FAILURE;
8430Sstevel@tonic-gate 			goto out;
8440Sstevel@tonic-gate 		}
8450Sstevel@tonic-gate 	}
8460Sstevel@tonic-gate 
8470Sstevel@tonic-gate 	ASSERT(i_ddi_node_state(rdip) >= DS_INITIALIZED);
8480Sstevel@tonic-gate 
8490Sstevel@tonic-gate 	devnm = kmem_alloc(MAXNAMELEN + 1, KM_SLEEP);
8500Sstevel@tonic-gate 
8510Sstevel@tonic-gate 	(void) ddi_deviname(rdip, devnm);
8520Sstevel@tonic-gate 
8530Sstevel@tonic-gate 	if ((rv = ndi_devi_config_one(pdip, devnm+1, &rdip,
8540Sstevel@tonic-gate 	    NDI_DEVI_ONLINE | NDI_CONFIG)) == NDI_SUCCESS) {
8550Sstevel@tonic-gate 		/* release hold from ndi_devi_config_one() */
8560Sstevel@tonic-gate 		ndi_rele_devi(rdip);
8570Sstevel@tonic-gate 	}
8580Sstevel@tonic-gate 
8590Sstevel@tonic-gate 	kmem_free(devnm, MAXNAMELEN + 1);
8600Sstevel@tonic-gate out:
8610Sstevel@tonic-gate 	if (rv != NDI_SUCCESS && dipp) {
8620Sstevel@tonic-gate 		ndi_hold_devi(rdip);
8630Sstevel@tonic-gate 		*dipp = rdip;
8640Sstevel@tonic-gate 	}
8650Sstevel@tonic-gate 	ndi_devi_exit(pdip, circ);
8660Sstevel@tonic-gate 	return (ndi2errno(rv));
8670Sstevel@tonic-gate }
8680Sstevel@tonic-gate 
8690Sstevel@tonic-gate void
8700Sstevel@tonic-gate e_ddi_branch_hold(dev_info_t *rdip)
8710Sstevel@tonic-gate {
8720Sstevel@tonic-gate 	if (e_ddi_branch_held(rdip)) {
8730Sstevel@tonic-gate 		cmn_err(CE_WARN, "e_ddi_branch_hold: branch already held");
8740Sstevel@tonic-gate 		return;
8750Sstevel@tonic-gate 	}
8760Sstevel@tonic-gate 
8770Sstevel@tonic-gate 	mutex_enter(&DEVI(rdip)->devi_lock);
8780Sstevel@tonic-gate 	if ((DEVI(rdip)->devi_flags & DEVI_BRANCH_HELD) == 0) {
8790Sstevel@tonic-gate 		DEVI(rdip)->devi_flags |= DEVI_BRANCH_HELD;
8800Sstevel@tonic-gate 		DEVI(rdip)->devi_ref++;
8810Sstevel@tonic-gate 	}
8820Sstevel@tonic-gate 	ASSERT(DEVI(rdip)->devi_ref > 0);
8830Sstevel@tonic-gate 	mutex_exit(&DEVI(rdip)->devi_lock);
8840Sstevel@tonic-gate }
8850Sstevel@tonic-gate 
8860Sstevel@tonic-gate int
8870Sstevel@tonic-gate e_ddi_branch_held(dev_info_t *rdip)
8880Sstevel@tonic-gate {
8890Sstevel@tonic-gate 	int rv = 0;
8900Sstevel@tonic-gate 
8910Sstevel@tonic-gate 	mutex_enter(&DEVI(rdip)->devi_lock);
8920Sstevel@tonic-gate 	if ((DEVI(rdip)->devi_flags & DEVI_BRANCH_HELD) &&
8930Sstevel@tonic-gate 	    DEVI(rdip)->devi_ref > 0) {
8940Sstevel@tonic-gate 		rv = 1;
8950Sstevel@tonic-gate 	}
8960Sstevel@tonic-gate 	mutex_exit(&DEVI(rdip)->devi_lock);
8970Sstevel@tonic-gate 
8980Sstevel@tonic-gate 	return (rv);
8990Sstevel@tonic-gate }
9000Sstevel@tonic-gate void
9010Sstevel@tonic-gate e_ddi_branch_rele(dev_info_t *rdip)
9020Sstevel@tonic-gate {
9030Sstevel@tonic-gate 	mutex_enter(&DEVI(rdip)->devi_lock);
9040Sstevel@tonic-gate 	DEVI(rdip)->devi_flags &= ~DEVI_BRANCH_HELD;
9050Sstevel@tonic-gate 	DEVI(rdip)->devi_ref--;
9060Sstevel@tonic-gate 	mutex_exit(&DEVI(rdip)->devi_lock);
9070Sstevel@tonic-gate }
9080Sstevel@tonic-gate 
9090Sstevel@tonic-gate int
9100Sstevel@tonic-gate e_ddi_branch_unconfigure(
9110Sstevel@tonic-gate 	dev_info_t *rdip,
9120Sstevel@tonic-gate 	dev_info_t **dipp,
9130Sstevel@tonic-gate 	uint_t flags)
9140Sstevel@tonic-gate {
9150Sstevel@tonic-gate 	int	circ, rv;
9160Sstevel@tonic-gate 	int	destroy;
9170Sstevel@tonic-gate 	char	*devnm;
9180Sstevel@tonic-gate 	uint_t	nflags;
9190Sstevel@tonic-gate 	dev_info_t *pdip;
9200Sstevel@tonic-gate 
9210Sstevel@tonic-gate 	if (dipp)
9220Sstevel@tonic-gate 		*dipp = NULL;
9230Sstevel@tonic-gate 
9240Sstevel@tonic-gate 	if (rdip == NULL)
9250Sstevel@tonic-gate 		return (EINVAL);
9260Sstevel@tonic-gate 
9270Sstevel@tonic-gate 	pdip = ddi_get_parent(rdip);
9280Sstevel@tonic-gate 
9290Sstevel@tonic-gate 	ASSERT(pdip);
9300Sstevel@tonic-gate 
9310Sstevel@tonic-gate 	/*
9320Sstevel@tonic-gate 	 * Check if caller holds pdip busy - can cause deadlocks during
9330Sstevel@tonic-gate 	 * devfs_clean()
9340Sstevel@tonic-gate 	 */
9350Sstevel@tonic-gate 	if (DEVI_BUSY_OWNED(pdip)) {
9360Sstevel@tonic-gate 		cmn_err(CE_WARN, "e_ddi_branch_unconfigure: failed: parent"
9370Sstevel@tonic-gate 		    " devinfo node(%p) is busy held", (void *)pdip);
9380Sstevel@tonic-gate 		return (EINVAL);
9390Sstevel@tonic-gate 	}
9400Sstevel@tonic-gate 
9410Sstevel@tonic-gate 	destroy = (flags & DEVI_BRANCH_DESTROY) ? 1 : 0;
9420Sstevel@tonic-gate 
9430Sstevel@tonic-gate 	devnm = kmem_alloc(MAXNAMELEN + 1, KM_SLEEP);
9440Sstevel@tonic-gate 
9450Sstevel@tonic-gate 	ndi_devi_enter(pdip, &circ);
9460Sstevel@tonic-gate 	(void) ddi_deviname(rdip, devnm);
9470Sstevel@tonic-gate 	ndi_devi_exit(pdip, circ);
9480Sstevel@tonic-gate 
9490Sstevel@tonic-gate 	/*
9500Sstevel@tonic-gate 	 * ddi_deviname() returns a component name with / prepended.
9510Sstevel@tonic-gate 	 */
9520Sstevel@tonic-gate 	rv = devfs_clean(pdip, devnm + 1, DV_CLEAN_FORCE);
9530Sstevel@tonic-gate 	if (rv) {
9540Sstevel@tonic-gate 		kmem_free(devnm, MAXNAMELEN + 1);
9550Sstevel@tonic-gate 		return (rv);
9560Sstevel@tonic-gate 	}
9570Sstevel@tonic-gate 
9580Sstevel@tonic-gate 	ndi_devi_enter(pdip, &circ);
9590Sstevel@tonic-gate 
9600Sstevel@tonic-gate 	/*
9610Sstevel@tonic-gate 	 * Recreate device name as it may have changed state (init/uninit)
9620Sstevel@tonic-gate 	 * when parent busy lock was dropped for devfs_clean()
9630Sstevel@tonic-gate 	 */
9640Sstevel@tonic-gate 	(void) ddi_deviname(rdip, devnm);
9650Sstevel@tonic-gate 
9660Sstevel@tonic-gate 	if (!e_ddi_branch_held(rdip)) {
9670Sstevel@tonic-gate 		kmem_free(devnm, MAXNAMELEN + 1);
9680Sstevel@tonic-gate 		ndi_devi_exit(pdip, circ);
9690Sstevel@tonic-gate 		cmn_err(CE_WARN, "e_ddi_%s_branch: dip(%p) not held",
9700Sstevel@tonic-gate 		    destroy ? "destroy" : "unconfigure", (void *)rdip);
9710Sstevel@tonic-gate 		return (EINVAL);
9720Sstevel@tonic-gate 	}
9730Sstevel@tonic-gate 
9740Sstevel@tonic-gate 	/*
9750Sstevel@tonic-gate 	 * Release hold on the branch. This is ok since we are holding the
9760Sstevel@tonic-gate 	 * parent busy. If rdip is not removed, we must do a hold on the
9770Sstevel@tonic-gate 	 * branch before returning.
9780Sstevel@tonic-gate 	 */
9790Sstevel@tonic-gate 	e_ddi_branch_rele(rdip);
9800Sstevel@tonic-gate 
9810Sstevel@tonic-gate 	nflags = NDI_DEVI_OFFLINE;
9820Sstevel@tonic-gate 	if (destroy || (flags & DEVI_BRANCH_DESTROY)) {
9830Sstevel@tonic-gate 		nflags |= NDI_DEVI_REMOVE;
9840Sstevel@tonic-gate 		destroy = 1;
9850Sstevel@tonic-gate 	} else {
9860Sstevel@tonic-gate 		nflags |= NDI_UNCONFIG;		/* uninit but don't remove */
9870Sstevel@tonic-gate 	}
9880Sstevel@tonic-gate 
9890Sstevel@tonic-gate 	if (flags & DEVI_BRANCH_EVENT)
9900Sstevel@tonic-gate 		nflags |= NDI_POST_EVENT;
9910Sstevel@tonic-gate 
9920Sstevel@tonic-gate 	if (i_ddi_node_state(pdip) == DS_READY &&
9930Sstevel@tonic-gate 	    i_ddi_node_state(rdip) >= DS_INITIALIZED) {
9940Sstevel@tonic-gate 		rv = ndi_devi_unconfig_one(pdip, devnm+1, dipp, nflags);
9950Sstevel@tonic-gate 	} else {
9960Sstevel@tonic-gate 		rv = e_ddi_devi_unconfig(rdip, dipp, nflags);
9970Sstevel@tonic-gate 		if (rv == NDI_SUCCESS) {
9980Sstevel@tonic-gate 			ASSERT(!destroy || ddi_get_child(rdip) == NULL);
9990Sstevel@tonic-gate 			rv = ndi_devi_offline(rdip, nflags);
10000Sstevel@tonic-gate 		}
10010Sstevel@tonic-gate 	}
10020Sstevel@tonic-gate 
10030Sstevel@tonic-gate 	if (!destroy || rv != NDI_SUCCESS) {
10040Sstevel@tonic-gate 		/* The dip still exists, so do a hold */
10050Sstevel@tonic-gate 		e_ddi_branch_hold(rdip);
10060Sstevel@tonic-gate 	}
10070Sstevel@tonic-gate out:
10080Sstevel@tonic-gate 	kmem_free(devnm, MAXNAMELEN + 1);
10090Sstevel@tonic-gate 	ndi_devi_exit(pdip, circ);
10100Sstevel@tonic-gate 	return (ndi2errno(rv));
10110Sstevel@tonic-gate }
10120Sstevel@tonic-gate 
10130Sstevel@tonic-gate int
10140Sstevel@tonic-gate e_ddi_branch_destroy(dev_info_t *rdip, dev_info_t **dipp, uint_t flag)
10150Sstevel@tonic-gate {
10160Sstevel@tonic-gate 	return (e_ddi_branch_unconfigure(rdip, dipp,
10170Sstevel@tonic-gate 	    flag|DEVI_BRANCH_DESTROY));
10180Sstevel@tonic-gate }
10190Sstevel@tonic-gate 
10200Sstevel@tonic-gate /*
10210Sstevel@tonic-gate  * Number of chains for hash table
10220Sstevel@tonic-gate  */
10230Sstevel@tonic-gate #define	NUMCHAINS	17
10240Sstevel@tonic-gate 
10250Sstevel@tonic-gate /*
10260Sstevel@tonic-gate  * Devinfo busy arg
10270Sstevel@tonic-gate  */
10280Sstevel@tonic-gate struct devi_busy {
10290Sstevel@tonic-gate 	int dv_total;
10300Sstevel@tonic-gate 	int s_total;
10310Sstevel@tonic-gate 	mod_hash_t *dv_hash;
10320Sstevel@tonic-gate 	mod_hash_t *s_hash;
10330Sstevel@tonic-gate 	int (*callback)(dev_info_t *, void *, uint_t);
10340Sstevel@tonic-gate 	void *arg;
10350Sstevel@tonic-gate };
10360Sstevel@tonic-gate 
10370Sstevel@tonic-gate static int
10380Sstevel@tonic-gate visit_dip(dev_info_t *dip, void *arg)
10390Sstevel@tonic-gate {
10400Sstevel@tonic-gate 	uintptr_t sbusy, dvbusy, ref;
10410Sstevel@tonic-gate 	struct devi_busy *bsp = arg;
10420Sstevel@tonic-gate 
10430Sstevel@tonic-gate 	ASSERT(bsp->callback);
10440Sstevel@tonic-gate 
10450Sstevel@tonic-gate 	/*
10460Sstevel@tonic-gate 	 * A dip cannot be busy if its reference count is 0
10470Sstevel@tonic-gate 	 */
10480Sstevel@tonic-gate 	if ((ref = e_ddi_devi_holdcnt(dip)) == 0) {
10490Sstevel@tonic-gate 		return (bsp->callback(dip, bsp->arg, 0));
10500Sstevel@tonic-gate 	}
10510Sstevel@tonic-gate 
10520Sstevel@tonic-gate 	if (mod_hash_find(bsp->dv_hash, dip, (mod_hash_val_t *)&dvbusy))
10530Sstevel@tonic-gate 		dvbusy = 0;
10540Sstevel@tonic-gate 
10550Sstevel@tonic-gate 	/*
10560Sstevel@tonic-gate 	 * To catch device opens currently maintained on specfs common snodes.
10570Sstevel@tonic-gate 	 */
10580Sstevel@tonic-gate 	if (mod_hash_find(bsp->s_hash, dip, (mod_hash_val_t *)&sbusy))
10590Sstevel@tonic-gate 		sbusy = 0;
10600Sstevel@tonic-gate 
10610Sstevel@tonic-gate #ifdef	DEBUG
10620Sstevel@tonic-gate 	if (ref < sbusy || ref < dvbusy) {
10630Sstevel@tonic-gate 		cmn_err(CE_WARN, "dip(%p): sopen = %lu, dvopen = %lu "
10640Sstevel@tonic-gate 		    "dip ref = %lu\n", (void *)dip, sbusy, dvbusy, ref);
10650Sstevel@tonic-gate 	}
10660Sstevel@tonic-gate #endif
10670Sstevel@tonic-gate 
10680Sstevel@tonic-gate 	dvbusy = (sbusy > dvbusy) ? sbusy : dvbusy;
10690Sstevel@tonic-gate 
10700Sstevel@tonic-gate 	return (bsp->callback(dip, bsp->arg, dvbusy));
10710Sstevel@tonic-gate }
10720Sstevel@tonic-gate 
10730Sstevel@tonic-gate static int
10740Sstevel@tonic-gate visit_snode(struct snode *sp, void *arg)
10750Sstevel@tonic-gate {
10760Sstevel@tonic-gate 	uintptr_t sbusy;
10770Sstevel@tonic-gate 	dev_info_t *dip;
10780Sstevel@tonic-gate 	int count;
10790Sstevel@tonic-gate 	struct devi_busy *bsp = arg;
10800Sstevel@tonic-gate 
10810Sstevel@tonic-gate 	ASSERT(sp);
10820Sstevel@tonic-gate 
10830Sstevel@tonic-gate 	/*
10840Sstevel@tonic-gate 	 * The stable lock is held. This prevents
10850Sstevel@tonic-gate 	 * the snode and its associated dip from
10860Sstevel@tonic-gate 	 * going away.
10870Sstevel@tonic-gate 	 */
10880Sstevel@tonic-gate 	dip = NULL;
10890Sstevel@tonic-gate 	count = spec_devi_open_count(sp, &dip);
10900Sstevel@tonic-gate 
10910Sstevel@tonic-gate 	if (count <= 0)
10920Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
10930Sstevel@tonic-gate 
10940Sstevel@tonic-gate 	ASSERT(dip);
10950Sstevel@tonic-gate 
10960Sstevel@tonic-gate 	if (mod_hash_remove(bsp->s_hash, dip, (mod_hash_val_t *)&sbusy))
10970Sstevel@tonic-gate 		sbusy = count;
10980Sstevel@tonic-gate 	else
10990Sstevel@tonic-gate 		sbusy += count;
11000Sstevel@tonic-gate 
11010Sstevel@tonic-gate 	if (mod_hash_insert(bsp->s_hash, dip, (mod_hash_val_t)sbusy)) {
11020Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s: s_hash insert failed: dip=0x%p, "
11030Sstevel@tonic-gate 		    "sbusy = %lu", "e_ddi_branch_referenced",
11040Sstevel@tonic-gate 		    (void *)dip, sbusy);
11050Sstevel@tonic-gate 	}
11060Sstevel@tonic-gate 
11070Sstevel@tonic-gate 	bsp->s_total += count;
11080Sstevel@tonic-gate 
11090Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
11100Sstevel@tonic-gate }
11110Sstevel@tonic-gate 
11120Sstevel@tonic-gate static void
11130Sstevel@tonic-gate visit_dvnode(struct dv_node *dv, void *arg)
11140Sstevel@tonic-gate {
11150Sstevel@tonic-gate 	uintptr_t dvbusy;
11160Sstevel@tonic-gate 	uint_t count;
11170Sstevel@tonic-gate 	struct vnode *vp;
11180Sstevel@tonic-gate 	struct devi_busy *bsp = arg;
11190Sstevel@tonic-gate 
11200Sstevel@tonic-gate 	ASSERT(dv && dv->dv_devi);
11210Sstevel@tonic-gate 
11220Sstevel@tonic-gate 	vp = DVTOV(dv);
11230Sstevel@tonic-gate 
11240Sstevel@tonic-gate 	mutex_enter(&vp->v_lock);
11250Sstevel@tonic-gate 	count = vp->v_count;
11260Sstevel@tonic-gate 	mutex_exit(&vp->v_lock);
11270Sstevel@tonic-gate 
11280Sstevel@tonic-gate 	if (!count)
11290Sstevel@tonic-gate 		return;
11300Sstevel@tonic-gate 
11310Sstevel@tonic-gate 	if (mod_hash_remove(bsp->dv_hash, dv->dv_devi,
11320Sstevel@tonic-gate 	    (mod_hash_val_t *)&dvbusy))
11330Sstevel@tonic-gate 		dvbusy = count;
11340Sstevel@tonic-gate 	else
11350Sstevel@tonic-gate 		dvbusy += count;
11360Sstevel@tonic-gate 
11370Sstevel@tonic-gate 	if (mod_hash_insert(bsp->dv_hash, dv->dv_devi,
11380Sstevel@tonic-gate 	    (mod_hash_val_t)dvbusy)) {
11390Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s: dv_hash insert failed: dip=0x%p, "
11400Sstevel@tonic-gate 		    "dvbusy=%lu", "e_ddi_branch_referenced",
11410Sstevel@tonic-gate 		    (void *)dv->dv_devi, dvbusy);
11420Sstevel@tonic-gate 	}
11430Sstevel@tonic-gate 
11440Sstevel@tonic-gate 	bsp->dv_total += count;
11450Sstevel@tonic-gate }
11460Sstevel@tonic-gate 
11470Sstevel@tonic-gate /*
11480Sstevel@tonic-gate  * Returns reference count on success or -1 on failure.
11490Sstevel@tonic-gate  */
11500Sstevel@tonic-gate int
11510Sstevel@tonic-gate e_ddi_branch_referenced(
11520Sstevel@tonic-gate 	dev_info_t *rdip,
11530Sstevel@tonic-gate 	int (*callback)(dev_info_t *dip, void *arg, uint_t ref),
11540Sstevel@tonic-gate 	void *arg)
11550Sstevel@tonic-gate {
11560Sstevel@tonic-gate 	int circ;
11570Sstevel@tonic-gate 	char *path;
11580Sstevel@tonic-gate 	dev_info_t *pdip;
11590Sstevel@tonic-gate 	struct devi_busy bsa = {0};
11600Sstevel@tonic-gate 
11610Sstevel@tonic-gate 	ASSERT(rdip);
11620Sstevel@tonic-gate 
11630Sstevel@tonic-gate 	path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
11640Sstevel@tonic-gate 
11650Sstevel@tonic-gate 	ndi_hold_devi(rdip);
11660Sstevel@tonic-gate 
11670Sstevel@tonic-gate 	pdip = ddi_get_parent(rdip);
11680Sstevel@tonic-gate 
11690Sstevel@tonic-gate 	ASSERT(pdip);
11700Sstevel@tonic-gate 
11710Sstevel@tonic-gate 	/*
11720Sstevel@tonic-gate 	 * Check if caller holds pdip busy - can cause deadlocks during
11730Sstevel@tonic-gate 	 * devfs_walk()
11740Sstevel@tonic-gate 	 */
11750Sstevel@tonic-gate 	if (!e_ddi_branch_held(rdip) || DEVI_BUSY_OWNED(pdip)) {
11760Sstevel@tonic-gate 		cmn_err(CE_WARN, "e_ddi_branch_referenced: failed: "
11770Sstevel@tonic-gate 		    "devinfo branch(%p) not held or parent busy held",
11780Sstevel@tonic-gate 		    (void *)rdip);
11790Sstevel@tonic-gate 		ndi_rele_devi(rdip);
11800Sstevel@tonic-gate 		kmem_free(path, MAXPATHLEN);
11810Sstevel@tonic-gate 		return (-1);
11820Sstevel@tonic-gate 	}
11830Sstevel@tonic-gate 
11840Sstevel@tonic-gate 	ndi_devi_enter(pdip, &circ);
11850Sstevel@tonic-gate 	(void) ddi_pathname(rdip, path);
11860Sstevel@tonic-gate 	ndi_devi_exit(pdip, circ);
11870Sstevel@tonic-gate 
11880Sstevel@tonic-gate 	bsa.dv_hash = mod_hash_create_ptrhash("dv_node busy hash", NUMCHAINS,
11890Sstevel@tonic-gate 	    mod_hash_null_valdtor, sizeof (struct dev_info));
11900Sstevel@tonic-gate 
11910Sstevel@tonic-gate 	bsa.s_hash = mod_hash_create_ptrhash("snode busy hash", NUMCHAINS,
11920Sstevel@tonic-gate 	    mod_hash_null_valdtor, sizeof (struct snode));
11930Sstevel@tonic-gate 
11940Sstevel@tonic-gate 	if (devfs_walk(path, visit_dvnode, &bsa)) {
11950Sstevel@tonic-gate 		cmn_err(CE_WARN, "e_ddi_branch_referenced: "
11960Sstevel@tonic-gate 		    "devfs walk failed for: %s", path);
11970Sstevel@tonic-gate 		kmem_free(path, MAXPATHLEN);
11980Sstevel@tonic-gate 		bsa.s_total = bsa.dv_total = -1;
11990Sstevel@tonic-gate 		goto out;
12000Sstevel@tonic-gate 	}
12010Sstevel@tonic-gate 
12020Sstevel@tonic-gate 	kmem_free(path, MAXPATHLEN);
12030Sstevel@tonic-gate 
12040Sstevel@tonic-gate 	/*
12050Sstevel@tonic-gate 	 * Walk the snode table to detect device opens, which are currently
12060Sstevel@tonic-gate 	 * maintained on specfs common snodes.
12070Sstevel@tonic-gate 	 */
12080Sstevel@tonic-gate 	spec_snode_walk(visit_snode, &bsa);
12090Sstevel@tonic-gate 
12100Sstevel@tonic-gate 	if (callback == NULL)
12110Sstevel@tonic-gate 		goto out;
12120Sstevel@tonic-gate 
12130Sstevel@tonic-gate 	bsa.callback = callback;
12140Sstevel@tonic-gate 	bsa.arg = arg;
12150Sstevel@tonic-gate 
12160Sstevel@tonic-gate 	if (visit_dip(rdip, &bsa) == DDI_WALK_CONTINUE) {
12170Sstevel@tonic-gate 		ndi_devi_enter(rdip, &circ);
12180Sstevel@tonic-gate 		ddi_walk_devs(ddi_get_child(rdip), visit_dip, &bsa);
12190Sstevel@tonic-gate 		ndi_devi_exit(rdip, circ);
12200Sstevel@tonic-gate 	}
12210Sstevel@tonic-gate 
12220Sstevel@tonic-gate out:
12230Sstevel@tonic-gate 	ndi_rele_devi(rdip);
12240Sstevel@tonic-gate 	mod_hash_destroy_ptrhash(bsa.s_hash);
12250Sstevel@tonic-gate 	mod_hash_destroy_ptrhash(bsa.dv_hash);
12260Sstevel@tonic-gate 	return (bsa.s_total > bsa.dv_total ? bsa.s_total : bsa.dv_total);
12270Sstevel@tonic-gate }
1228