xref: /onnv-gate/usr/src/uts/sun4u/os/mach_ddi_impl.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate /*
30*0Sstevel@tonic-gate  * sun4u specific DDI implementation
31*0Sstevel@tonic-gate  */
32*0Sstevel@tonic-gate #include <sys/bootconf.h>
33*0Sstevel@tonic-gate #include <sys/conf.h>
34*0Sstevel@tonic-gate #include <sys/ddi_subrdefs.h>
35*0Sstevel@tonic-gate #include <sys/ethernet.h>
36*0Sstevel@tonic-gate #include <sys/idprom.h>
37*0Sstevel@tonic-gate #include <sys/machsystm.h>
38*0Sstevel@tonic-gate #include <sys/modhash.h>
39*0Sstevel@tonic-gate #include <sys/promif.h>
40*0Sstevel@tonic-gate #include <sys/prom_plat.h>
41*0Sstevel@tonic-gate #include <sys/sunndi.h>
42*0Sstevel@tonic-gate #include <sys/systeminfo.h>
43*0Sstevel@tonic-gate #include <sys/fpu/fpusystm.h>
44*0Sstevel@tonic-gate #include <sys/vm.h>
45*0Sstevel@tonic-gate #include <sys/fs/dv_node.h>
46*0Sstevel@tonic-gate #include <sys/fs/snode.h>
47*0Sstevel@tonic-gate 
48*0Sstevel@tonic-gate /*
49*0Sstevel@tonic-gate  * Favored drivers of this implementation
50*0Sstevel@tonic-gate  * architecture.  These drivers MUST be present for
51*0Sstevel@tonic-gate  * the system to boot at all.
52*0Sstevel@tonic-gate  */
53*0Sstevel@tonic-gate char *impl_module_list[] = {
54*0Sstevel@tonic-gate 	"rootnex",
55*0Sstevel@tonic-gate 	"options",
56*0Sstevel@tonic-gate 	"sad",		/* Referenced via init_tbl[] */
57*0Sstevel@tonic-gate 	"pseudo",
58*0Sstevel@tonic-gate 	"clone",
59*0Sstevel@tonic-gate 	"scsi_vhci",
60*0Sstevel@tonic-gate 	(char *)0
61*0Sstevel@tonic-gate };
62*0Sstevel@tonic-gate 
63*0Sstevel@tonic-gate /*
64*0Sstevel@tonic-gate  * These strings passed to not_serviced in locore.s
65*0Sstevel@tonic-gate  */
66*0Sstevel@tonic-gate const char busname_ovec[] = "onboard ";
67*0Sstevel@tonic-gate const char busname_svec[] = "SBus ";
68*0Sstevel@tonic-gate const char busname_vec[] = "";
69*0Sstevel@tonic-gate 
70*0Sstevel@tonic-gate 
71*0Sstevel@tonic-gate static uint64_t *intr_map_reg[32];
72*0Sstevel@tonic-gate 
73*0Sstevel@tonic-gate /*
74*0Sstevel@tonic-gate  * Forward declarations
75*0Sstevel@tonic-gate  */
76*0Sstevel@tonic-gate static int getlongprop_buf();
77*0Sstevel@tonic-gate static int get_boardnum(int nid, dev_info_t *par);
78*0Sstevel@tonic-gate 
79*0Sstevel@tonic-gate /*
80*0Sstevel@tonic-gate  * Check the status of the device node passed as an argument.
81*0Sstevel@tonic-gate  *
82*0Sstevel@tonic-gate  *	if ((status is OKAY) || (status is DISABLED))
83*0Sstevel@tonic-gate  *		return DDI_SUCCESS
84*0Sstevel@tonic-gate  *	else
85*0Sstevel@tonic-gate  *		print a warning and return DDI_FAILURE
86*0Sstevel@tonic-gate  */
87*0Sstevel@tonic-gate /*ARGSUSED*/
88*0Sstevel@tonic-gate int
89*0Sstevel@tonic-gate check_status(int id, char *buf, dev_info_t *parent)
90*0Sstevel@tonic-gate {
91*0Sstevel@tonic-gate 	char status_buf[64];
92*0Sstevel@tonic-gate 	char devtype_buf[OBP_MAXPROPNAME];
93*0Sstevel@tonic-gate 	char board_buf[32];
94*0Sstevel@tonic-gate 	char path[OBP_MAXPATHLEN];
95*0Sstevel@tonic-gate 	int boardnum;
96*0Sstevel@tonic-gate 	int retval = DDI_FAILURE;
97*0Sstevel@tonic-gate 	extern int status_okay(int, char *, int);
98*0Sstevel@tonic-gate 
99*0Sstevel@tonic-gate 	/*
100*0Sstevel@tonic-gate 	 * is the status okay?
101*0Sstevel@tonic-gate 	 */
102*0Sstevel@tonic-gate 	if (status_okay(id, status_buf, sizeof (status_buf)))
103*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
104*0Sstevel@tonic-gate 
105*0Sstevel@tonic-gate 	/*
106*0Sstevel@tonic-gate 	 * a status property indicating bad memory will be associated
107*0Sstevel@tonic-gate 	 * with a node which has a "device_type" property with a value of
108*0Sstevel@tonic-gate 	 * "memory-controller". in this situation, return DDI_SUCCESS
109*0Sstevel@tonic-gate 	 */
110*0Sstevel@tonic-gate 	if (getlongprop_buf(id, OBP_DEVICETYPE, devtype_buf,
111*0Sstevel@tonic-gate 	    sizeof (devtype_buf)) > 0) {
112*0Sstevel@tonic-gate 		if (strcmp(devtype_buf, "memory-controller") == 0)
113*0Sstevel@tonic-gate 			retval = DDI_SUCCESS;
114*0Sstevel@tonic-gate 	}
115*0Sstevel@tonic-gate 
116*0Sstevel@tonic-gate 	/*
117*0Sstevel@tonic-gate 	 * get the full OBP pathname of this node
118*0Sstevel@tonic-gate 	 */
119*0Sstevel@tonic-gate 	if (prom_phandle_to_path((phandle_t)id, path, sizeof (path)) < 0)
120*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "prom_phandle_to_path(%d) failed", id);
121*0Sstevel@tonic-gate 
122*0Sstevel@tonic-gate 	/*
123*0Sstevel@tonic-gate 	 * get the board number, if one exists
124*0Sstevel@tonic-gate 	 */
125*0Sstevel@tonic-gate 	if ((boardnum = get_boardnum(id, parent)) >= 0)
126*0Sstevel@tonic-gate 		(void) sprintf(board_buf, " on board %d", boardnum);
127*0Sstevel@tonic-gate 	else
128*0Sstevel@tonic-gate 		board_buf[0] = '\0';
129*0Sstevel@tonic-gate 
130*0Sstevel@tonic-gate 	/*
131*0Sstevel@tonic-gate 	 * print the status property information
132*0Sstevel@tonic-gate 	 */
133*0Sstevel@tonic-gate 	cmn_err(CE_WARN, "status '%s' for '%s'%s",
134*0Sstevel@tonic-gate 		status_buf, path, board_buf);
135*0Sstevel@tonic-gate 	return (retval);
136*0Sstevel@tonic-gate }
137*0Sstevel@tonic-gate 
138*0Sstevel@tonic-gate /*
139*0Sstevel@tonic-gate  * determine the board number associated with this nodeid
140*0Sstevel@tonic-gate  */
141*0Sstevel@tonic-gate static int
142*0Sstevel@tonic-gate get_boardnum(int nid, dev_info_t *par)
143*0Sstevel@tonic-gate {
144*0Sstevel@tonic-gate 	int board_num;
145*0Sstevel@tonic-gate 
146*0Sstevel@tonic-gate 	if (prom_getprop((dnode_t)nid, OBP_BOARDNUM,
147*0Sstevel@tonic-gate 	    (caddr_t)&board_num) != -1)
148*0Sstevel@tonic-gate 		return (board_num);
149*0Sstevel@tonic-gate 
150*0Sstevel@tonic-gate 	/*
151*0Sstevel@tonic-gate 	 * Look at current node and up the parent chain
152*0Sstevel@tonic-gate 	 * till we find a node with an OBP_BOARDNUM.
153*0Sstevel@tonic-gate 	 */
154*0Sstevel@tonic-gate 	while (par) {
155*0Sstevel@tonic-gate 		nid = ddi_get_nodeid(par);
156*0Sstevel@tonic-gate 
157*0Sstevel@tonic-gate 		if (prom_getprop((dnode_t)nid, OBP_BOARDNUM,
158*0Sstevel@tonic-gate 		    (caddr_t)&board_num) != -1)
159*0Sstevel@tonic-gate 			return (board_num);
160*0Sstevel@tonic-gate 
161*0Sstevel@tonic-gate 		par = ddi_get_parent(par);
162*0Sstevel@tonic-gate 	}
163*0Sstevel@tonic-gate 	return (-1);
164*0Sstevel@tonic-gate }
165*0Sstevel@tonic-gate 
166*0Sstevel@tonic-gate /*
167*0Sstevel@tonic-gate  * Note that this routine does not take into account the endianness
168*0Sstevel@tonic-gate  * of the host or the device (or PROM) when retrieving properties.
169*0Sstevel@tonic-gate  */
170*0Sstevel@tonic-gate static int
171*0Sstevel@tonic-gate getlongprop_buf(int id, char *name, char *buf, int maxlen)
172*0Sstevel@tonic-gate {
173*0Sstevel@tonic-gate 	int size;
174*0Sstevel@tonic-gate 
175*0Sstevel@tonic-gate 	size = prom_getproplen((dnode_t)id, name);
176*0Sstevel@tonic-gate 	if (size <= 0 || (size > maxlen - 1))
177*0Sstevel@tonic-gate 		return (-1);
178*0Sstevel@tonic-gate 
179*0Sstevel@tonic-gate 	if (-1 == prom_getprop((dnode_t)id, name, buf))
180*0Sstevel@tonic-gate 		return (-1);
181*0Sstevel@tonic-gate 
182*0Sstevel@tonic-gate 	/*
183*0Sstevel@tonic-gate 	 * Workaround for bugid 1085575 - OBP may return a "name" property
184*0Sstevel@tonic-gate 	 * without null terminating the string with '\0'.  When this occurs,
185*0Sstevel@tonic-gate 	 * append a '\0' and return (size + 1).
186*0Sstevel@tonic-gate 	 */
187*0Sstevel@tonic-gate 	if (strcmp("name", name) == 0) {
188*0Sstevel@tonic-gate 		if (buf[size - 1] != '\0') {
189*0Sstevel@tonic-gate 			buf[size] = '\0';
190*0Sstevel@tonic-gate 			size += 1;
191*0Sstevel@tonic-gate 		}
192*0Sstevel@tonic-gate 	}
193*0Sstevel@tonic-gate 
194*0Sstevel@tonic-gate 	return (size);
195*0Sstevel@tonic-gate }
196*0Sstevel@tonic-gate 
197*0Sstevel@tonic-gate /*
198*0Sstevel@tonic-gate  * Routines to set/get UPA slave only device interrupt mapping registers.
199*0Sstevel@tonic-gate  * set_intr_mapping_reg() is called by the UPA master to register the address
200*0Sstevel@tonic-gate  * of an interrupt mapping register. The upa id is that of the master. If
201*0Sstevel@tonic-gate  * this routine is called on behalf of a slave device, the framework
202*0Sstevel@tonic-gate  * determines the upa id of the slave based on that supplied by the master.
203*0Sstevel@tonic-gate  *
204*0Sstevel@tonic-gate  * get_intr_mapping_reg() is called by the UPA nexus driver on behalf
205*0Sstevel@tonic-gate  * of a child device to get and program the interrupt mapping register of
206*0Sstevel@tonic-gate  * one of it's child nodes.  It uses the upa id of the child device to
207*0Sstevel@tonic-gate  * index into a table of mapping registers.  If the routine is called on
208*0Sstevel@tonic-gate  * behalf of a slave device and the mapping register has not been set,
209*0Sstevel@tonic-gate  * the framework determines the devinfo node of the corresponding master
210*0Sstevel@tonic-gate  * nexus which owns the mapping register of the slave and installs that
211*0Sstevel@tonic-gate  * driver.  The device driver which owns the mapping register must call
212*0Sstevel@tonic-gate  * set_intr_mapping_reg() in its attach routine to register the slaves
213*0Sstevel@tonic-gate  * mapping register with the system.
214*0Sstevel@tonic-gate  */
215*0Sstevel@tonic-gate void
216*0Sstevel@tonic-gate set_intr_mapping_reg(int upaid, uint64_t *addr, int slave)
217*0Sstevel@tonic-gate {
218*0Sstevel@tonic-gate 	int affin_upaid;
219*0Sstevel@tonic-gate 
220*0Sstevel@tonic-gate 	/* For UPA master devices, set the mapping reg addr and we're done */
221*0Sstevel@tonic-gate 	if (slave == 0) {
222*0Sstevel@tonic-gate 		intr_map_reg[upaid] = addr;
223*0Sstevel@tonic-gate 		return;
224*0Sstevel@tonic-gate 	}
225*0Sstevel@tonic-gate 
226*0Sstevel@tonic-gate 	/*
227*0Sstevel@tonic-gate 	 * If we get here, we're adding an entry for a UPA slave only device.
228*0Sstevel@tonic-gate 	 * The UPA id of the device which has affinity with that requesting,
229*0Sstevel@tonic-gate 	 * will be the device with the same UPA id minus the slave number.
230*0Sstevel@tonic-gate 	 * If the affin_upaid is negative, silently return to the caller.
231*0Sstevel@tonic-gate 	 */
232*0Sstevel@tonic-gate 	if ((affin_upaid = upaid - slave) < 0)
233*0Sstevel@tonic-gate 		return;
234*0Sstevel@tonic-gate 
235*0Sstevel@tonic-gate 	/*
236*0Sstevel@tonic-gate 	 * Load the address of the mapping register in the correct slot
237*0Sstevel@tonic-gate 	 * for the slave device.
238*0Sstevel@tonic-gate 	 */
239*0Sstevel@tonic-gate 	intr_map_reg[affin_upaid] = addr;
240*0Sstevel@tonic-gate }
241*0Sstevel@tonic-gate 
242*0Sstevel@tonic-gate uint64_t *
243*0Sstevel@tonic-gate get_intr_mapping_reg(int upaid, int slave)
244*0Sstevel@tonic-gate {
245*0Sstevel@tonic-gate 	int affin_upaid;
246*0Sstevel@tonic-gate 	dev_info_t *affin_dip;
247*0Sstevel@tonic-gate 	uint64_t *addr = intr_map_reg[upaid];
248*0Sstevel@tonic-gate 
249*0Sstevel@tonic-gate 	/* If we're a UPA master, or we have a valid mapping register. */
250*0Sstevel@tonic-gate 	if (!slave || addr != NULL)
251*0Sstevel@tonic-gate 		return (addr);
252*0Sstevel@tonic-gate 
253*0Sstevel@tonic-gate 	/*
254*0Sstevel@tonic-gate 	 * We only get here if we're a UPA slave only device whose interrupt
255*0Sstevel@tonic-gate 	 * mapping register has not been set.
256*0Sstevel@tonic-gate 	 * We need to try and install the nexus whose physical address
257*0Sstevel@tonic-gate 	 * space is where the slaves mapping register resides.  They
258*0Sstevel@tonic-gate 	 * should call set_intr_mapping_reg() in their xxattach() to register
259*0Sstevel@tonic-gate 	 * the mapping register with the system.
260*0Sstevel@tonic-gate 	 */
261*0Sstevel@tonic-gate 
262*0Sstevel@tonic-gate 	/*
263*0Sstevel@tonic-gate 	 * We don't know if a single- or multi-interrupt proxy is fielding
264*0Sstevel@tonic-gate 	 * our UPA slave interrupt, we must check both cases.
265*0Sstevel@tonic-gate 	 * Start out by assuming the multi-interrupt case.
266*0Sstevel@tonic-gate 	 * We assume that single- and multi- interrupters are not
267*0Sstevel@tonic-gate 	 * overlapping in UPA portid space.
268*0Sstevel@tonic-gate 	 */
269*0Sstevel@tonic-gate 
270*0Sstevel@tonic-gate 	affin_upaid = upaid | 3;
271*0Sstevel@tonic-gate 
272*0Sstevel@tonic-gate 	/*
273*0Sstevel@tonic-gate 	 * We start looking for the multi-interrupter affinity node.
274*0Sstevel@tonic-gate 	 * We know it's ONLY a child of the root node since the root
275*0Sstevel@tonic-gate 	 * node defines UPA space.
276*0Sstevel@tonic-gate 	 */
277*0Sstevel@tonic-gate 	for (affin_dip = ddi_get_child(ddi_root_node()); affin_dip;
278*0Sstevel@tonic-gate 	    affin_dip = ddi_get_next_sibling(affin_dip))
279*0Sstevel@tonic-gate 		if (ddi_prop_get_int(DDI_DEV_T_ANY, affin_dip,
280*0Sstevel@tonic-gate 		    DDI_PROP_DONTPASS, "upa-portid", -1) == affin_upaid)
281*0Sstevel@tonic-gate 			break;
282*0Sstevel@tonic-gate 
283*0Sstevel@tonic-gate 	if (affin_dip) {
284*0Sstevel@tonic-gate 		if (i_ddi_attach_node_hierarchy(affin_dip) == DDI_SUCCESS) {
285*0Sstevel@tonic-gate 			/* try again to get the mapping register. */
286*0Sstevel@tonic-gate 			addr = intr_map_reg[upaid];
287*0Sstevel@tonic-gate 		}
288*0Sstevel@tonic-gate 	}
289*0Sstevel@tonic-gate 
290*0Sstevel@tonic-gate 	/*
291*0Sstevel@tonic-gate 	 * If we still don't have a mapping register try single -interrupter
292*0Sstevel@tonic-gate 	 * case.
293*0Sstevel@tonic-gate 	 */
294*0Sstevel@tonic-gate 	if (addr == NULL) {
295*0Sstevel@tonic-gate 
296*0Sstevel@tonic-gate 		affin_upaid = upaid | 1;
297*0Sstevel@tonic-gate 
298*0Sstevel@tonic-gate 		for (affin_dip = ddi_get_child(ddi_root_node()); affin_dip;
299*0Sstevel@tonic-gate 		    affin_dip = ddi_get_next_sibling(affin_dip))
300*0Sstevel@tonic-gate 			if (ddi_prop_get_int(DDI_DEV_T_ANY, affin_dip,
301*0Sstevel@tonic-gate 			    DDI_PROP_DONTPASS, "upa-portid", -1) == affin_upaid)
302*0Sstevel@tonic-gate 				break;
303*0Sstevel@tonic-gate 
304*0Sstevel@tonic-gate 		if (affin_dip) {
305*0Sstevel@tonic-gate 			if (i_ddi_attach_node_hierarchy(affin_dip)
306*0Sstevel@tonic-gate 			    == DDI_SUCCESS) {
307*0Sstevel@tonic-gate 				/* try again to get the mapping register. */
308*0Sstevel@tonic-gate 				addr = intr_map_reg[upaid];
309*0Sstevel@tonic-gate 			}
310*0Sstevel@tonic-gate 		}
311*0Sstevel@tonic-gate 	}
312*0Sstevel@tonic-gate 	return (addr);
313*0Sstevel@tonic-gate }
314*0Sstevel@tonic-gate 
315*0Sstevel@tonic-gate 
316*0Sstevel@tonic-gate static struct upa_dma_pfns {
317*0Sstevel@tonic-gate 	pfn_t hipfn;
318*0Sstevel@tonic-gate 	pfn_t lopfn;
319*0Sstevel@tonic-gate } upa_dma_pfn_array[MAX_UPA];
320*0Sstevel@tonic-gate 
321*0Sstevel@tonic-gate static int upa_dma_pfn_ndx = 0;
322*0Sstevel@tonic-gate 
323*0Sstevel@tonic-gate /*
324*0Sstevel@tonic-gate  * Certain UPA busses cannot accept dma transactions from any other source
325*0Sstevel@tonic-gate  * except for memory due to livelock conditions in their hardware. (e.g. sbus
326*0Sstevel@tonic-gate  * and PCI). These routines allow devices or busses on the UPA to register
327*0Sstevel@tonic-gate  * a physical address block within it's own register space where DMA can be
328*0Sstevel@tonic-gate  * performed.  Currently, the FFB is the only such device which supports
329*0Sstevel@tonic-gate  * device DMA on the UPA.
330*0Sstevel@tonic-gate  */
331*0Sstevel@tonic-gate void
332*0Sstevel@tonic-gate pf_set_dmacapable(pfn_t hipfn, pfn_t lopfn)
333*0Sstevel@tonic-gate {
334*0Sstevel@tonic-gate 	int i = upa_dma_pfn_ndx;
335*0Sstevel@tonic-gate 
336*0Sstevel@tonic-gate 	upa_dma_pfn_ndx++;
337*0Sstevel@tonic-gate 
338*0Sstevel@tonic-gate 	upa_dma_pfn_array[i].hipfn = hipfn;
339*0Sstevel@tonic-gate 	upa_dma_pfn_array[i].lopfn = lopfn;
340*0Sstevel@tonic-gate }
341*0Sstevel@tonic-gate 
342*0Sstevel@tonic-gate void
343*0Sstevel@tonic-gate pf_unset_dmacapable(pfn_t pfn)
344*0Sstevel@tonic-gate {
345*0Sstevel@tonic-gate 	int i;
346*0Sstevel@tonic-gate 
347*0Sstevel@tonic-gate 	for (i = 0; i < upa_dma_pfn_ndx; i++) {
348*0Sstevel@tonic-gate 		if (pfn <= upa_dma_pfn_array[i].hipfn &&
349*0Sstevel@tonic-gate 		    pfn >= upa_dma_pfn_array[i].lopfn) {
350*0Sstevel@tonic-gate 			upa_dma_pfn_array[i].hipfn =
351*0Sstevel@tonic-gate 			    upa_dma_pfn_array[upa_dma_pfn_ndx - 1].hipfn;
352*0Sstevel@tonic-gate 			upa_dma_pfn_array[i].lopfn =
353*0Sstevel@tonic-gate 			    upa_dma_pfn_array[upa_dma_pfn_ndx - 1].lopfn;
354*0Sstevel@tonic-gate 			upa_dma_pfn_ndx--;
355*0Sstevel@tonic-gate 			break;
356*0Sstevel@tonic-gate 		}
357*0Sstevel@tonic-gate 	}
358*0Sstevel@tonic-gate }
359*0Sstevel@tonic-gate 
360*0Sstevel@tonic-gate /*
361*0Sstevel@tonic-gate  * This routine should only be called using a pfn that is known to reside
362*0Sstevel@tonic-gate  * in IO space.  The function pf_is_memory() can be used to determine this.
363*0Sstevel@tonic-gate  */
364*0Sstevel@tonic-gate int
365*0Sstevel@tonic-gate pf_is_dmacapable(pfn_t pfn)
366*0Sstevel@tonic-gate {
367*0Sstevel@tonic-gate 	int i, j;
368*0Sstevel@tonic-gate 
369*0Sstevel@tonic-gate 	/* If the caller passed in a memory pfn, return true. */
370*0Sstevel@tonic-gate 	if (pf_is_memory(pfn))
371*0Sstevel@tonic-gate 		return (1);
372*0Sstevel@tonic-gate 
373*0Sstevel@tonic-gate 	for (i = upa_dma_pfn_ndx, j = 0; j < i; j++)
374*0Sstevel@tonic-gate 		if (pfn <= upa_dma_pfn_array[j].hipfn &&
375*0Sstevel@tonic-gate 		    pfn >= upa_dma_pfn_array[j].lopfn)
376*0Sstevel@tonic-gate 			return (1);
377*0Sstevel@tonic-gate 
378*0Sstevel@tonic-gate 	return (0);
379*0Sstevel@tonic-gate }
380*0Sstevel@tonic-gate 
381*0Sstevel@tonic-gate 
382*0Sstevel@tonic-gate /*
383*0Sstevel@tonic-gate  * Find cpu_id corresponding to the dip of a CPU device node
384*0Sstevel@tonic-gate  */
385*0Sstevel@tonic-gate int
386*0Sstevel@tonic-gate dip_to_cpu_id(dev_info_t *dip, processorid_t *cpu_id)
387*0Sstevel@tonic-gate {
388*0Sstevel@tonic-gate 	dnode_t		nodeid;
389*0Sstevel@tonic-gate 	int		i;
390*0Sstevel@tonic-gate 
391*0Sstevel@tonic-gate 	nodeid = (dnode_t)ddi_get_nodeid(dip);
392*0Sstevel@tonic-gate 	for (i = 0; i < NCPU; i++) {
393*0Sstevel@tonic-gate 		if (cpunodes[i].nodeid == nodeid) {
394*0Sstevel@tonic-gate 			*cpu_id = i;
395*0Sstevel@tonic-gate 			return (DDI_SUCCESS);
396*0Sstevel@tonic-gate 		}
397*0Sstevel@tonic-gate 	}
398*0Sstevel@tonic-gate 	return (DDI_FAILURE);
399*0Sstevel@tonic-gate }
400*0Sstevel@tonic-gate 
401*0Sstevel@tonic-gate /*
402*0Sstevel@tonic-gate  * Platform independent DR routines
403*0Sstevel@tonic-gate  */
404*0Sstevel@tonic-gate 
405*0Sstevel@tonic-gate static int
406*0Sstevel@tonic-gate ndi2errno(int n)
407*0Sstevel@tonic-gate {
408*0Sstevel@tonic-gate 	int err = 0;
409*0Sstevel@tonic-gate 
410*0Sstevel@tonic-gate 	switch (n) {
411*0Sstevel@tonic-gate 		case NDI_NOMEM:
412*0Sstevel@tonic-gate 			err = ENOMEM;
413*0Sstevel@tonic-gate 			break;
414*0Sstevel@tonic-gate 		case NDI_BUSY:
415*0Sstevel@tonic-gate 			err = EBUSY;
416*0Sstevel@tonic-gate 			break;
417*0Sstevel@tonic-gate 		case NDI_FAULT:
418*0Sstevel@tonic-gate 			err = EFAULT;
419*0Sstevel@tonic-gate 			break;
420*0Sstevel@tonic-gate 		case NDI_FAILURE:
421*0Sstevel@tonic-gate 			err = EIO;
422*0Sstevel@tonic-gate 			break;
423*0Sstevel@tonic-gate 		case NDI_SUCCESS:
424*0Sstevel@tonic-gate 			break;
425*0Sstevel@tonic-gate 		case NDI_BADHANDLE:
426*0Sstevel@tonic-gate 		default:
427*0Sstevel@tonic-gate 			err = EINVAL;
428*0Sstevel@tonic-gate 			break;
429*0Sstevel@tonic-gate 	}
430*0Sstevel@tonic-gate 	return (err);
431*0Sstevel@tonic-gate }
432*0Sstevel@tonic-gate 
433*0Sstevel@tonic-gate /*
434*0Sstevel@tonic-gate  * Prom tree node list
435*0Sstevel@tonic-gate  */
436*0Sstevel@tonic-gate struct ptnode {
437*0Sstevel@tonic-gate 	dnode_t		nodeid;
438*0Sstevel@tonic-gate 	struct ptnode	*next;
439*0Sstevel@tonic-gate };
440*0Sstevel@tonic-gate 
441*0Sstevel@tonic-gate /*
442*0Sstevel@tonic-gate  * Prom tree walk arg
443*0Sstevel@tonic-gate  */
444*0Sstevel@tonic-gate struct pta {
445*0Sstevel@tonic-gate 	dev_info_t	*pdip;
446*0Sstevel@tonic-gate 	devi_branch_t	*bp;
447*0Sstevel@tonic-gate 	uint_t		flags;
448*0Sstevel@tonic-gate 	dev_info_t	*fdip;
449*0Sstevel@tonic-gate 	struct ptnode	*head;
450*0Sstevel@tonic-gate };
451*0Sstevel@tonic-gate 
452*0Sstevel@tonic-gate static void
453*0Sstevel@tonic-gate visit_node(dnode_t nodeid, struct pta *ap)
454*0Sstevel@tonic-gate {
455*0Sstevel@tonic-gate 	struct ptnode	**nextp;
456*0Sstevel@tonic-gate 	int		(*select)(dnode_t, void *, uint_t);
457*0Sstevel@tonic-gate 
458*0Sstevel@tonic-gate 	ASSERT(nodeid != OBP_NONODE && nodeid != OBP_BADNODE);
459*0Sstevel@tonic-gate 
460*0Sstevel@tonic-gate 	select = ap->bp->create.prom_branch_select;
461*0Sstevel@tonic-gate 
462*0Sstevel@tonic-gate 	ASSERT(select);
463*0Sstevel@tonic-gate 
464*0Sstevel@tonic-gate 	if (select(nodeid, ap->bp->arg, 0) == DDI_SUCCESS) {
465*0Sstevel@tonic-gate 
466*0Sstevel@tonic-gate 		for (nextp = &ap->head; *nextp; nextp = &(*nextp)->next)
467*0Sstevel@tonic-gate 			;
468*0Sstevel@tonic-gate 
469*0Sstevel@tonic-gate 		*nextp = kmem_zalloc(sizeof (struct ptnode), KM_SLEEP);
470*0Sstevel@tonic-gate 
471*0Sstevel@tonic-gate 		(*nextp)->nodeid = nodeid;
472*0Sstevel@tonic-gate 	}
473*0Sstevel@tonic-gate 
474*0Sstevel@tonic-gate 	if ((ap->flags & DEVI_BRANCH_CHILD) == DEVI_BRANCH_CHILD)
475*0Sstevel@tonic-gate 		return;
476*0Sstevel@tonic-gate 
477*0Sstevel@tonic-gate 	nodeid = prom_childnode(nodeid);
478*0Sstevel@tonic-gate 	while (nodeid != OBP_NONODE && nodeid != OBP_BADNODE) {
479*0Sstevel@tonic-gate 		visit_node(nodeid, ap);
480*0Sstevel@tonic-gate 		nodeid = prom_nextnode(nodeid);
481*0Sstevel@tonic-gate 	}
482*0Sstevel@tonic-gate }
483*0Sstevel@tonic-gate 
484*0Sstevel@tonic-gate /*ARGSUSED*/
485*0Sstevel@tonic-gate static int
486*0Sstevel@tonic-gate set_dip_offline(dev_info_t *dip, void *arg)
487*0Sstevel@tonic-gate {
488*0Sstevel@tonic-gate 	ASSERT(dip);
489*0Sstevel@tonic-gate 
490*0Sstevel@tonic-gate 	if (!DEVI_IS_DEVICE_OFFLINE(dip))
491*0Sstevel@tonic-gate 		DEVI_SET_DEVICE_OFFLINE(dip);
492*0Sstevel@tonic-gate 
493*0Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
494*0Sstevel@tonic-gate }
495*0Sstevel@tonic-gate 
496*0Sstevel@tonic-gate /*ARGSUSED*/
497*0Sstevel@tonic-gate static int
498*0Sstevel@tonic-gate create_prom_branch(void *arg, int has_changed)
499*0Sstevel@tonic-gate {
500*0Sstevel@tonic-gate 	int		circ, c;
501*0Sstevel@tonic-gate 	int		exists, rv;
502*0Sstevel@tonic-gate 	dnode_t		nodeid;
503*0Sstevel@tonic-gate 	struct ptnode	*tnp;
504*0Sstevel@tonic-gate 	dev_info_t	*dip;
505*0Sstevel@tonic-gate 	struct pta	*ap = arg;
506*0Sstevel@tonic-gate 	devi_branch_t	*bp;
507*0Sstevel@tonic-gate 
508*0Sstevel@tonic-gate 	ASSERT(ap);
509*0Sstevel@tonic-gate 	ASSERT(ap->fdip == NULL);
510*0Sstevel@tonic-gate 	ASSERT(ap->pdip && ndi_dev_is_prom_node(ap->pdip));
511*0Sstevel@tonic-gate 
512*0Sstevel@tonic-gate 	bp = ap->bp;
513*0Sstevel@tonic-gate 
514*0Sstevel@tonic-gate 	nodeid = ddi_get_nodeid(ap->pdip);
515*0Sstevel@tonic-gate 	if (nodeid == OBP_NONODE || nodeid == OBP_BADNODE) {
516*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "create_prom_branch: invalid "
517*0Sstevel@tonic-gate 		    "nodeid: 0x%x", nodeid);
518*0Sstevel@tonic-gate 		return (EINVAL);
519*0Sstevel@tonic-gate 	}
520*0Sstevel@tonic-gate 
521*0Sstevel@tonic-gate 	ap->head = NULL;
522*0Sstevel@tonic-gate 
523*0Sstevel@tonic-gate 	nodeid = prom_childnode(nodeid);
524*0Sstevel@tonic-gate 	while (nodeid != OBP_NONODE && nodeid != OBP_BADNODE) {
525*0Sstevel@tonic-gate 		visit_node(nodeid, ap);
526*0Sstevel@tonic-gate 		nodeid = prom_nextnode(nodeid);
527*0Sstevel@tonic-gate 	}
528*0Sstevel@tonic-gate 
529*0Sstevel@tonic-gate 	if (ap->head == NULL)
530*0Sstevel@tonic-gate 		return (ENODEV);
531*0Sstevel@tonic-gate 
532*0Sstevel@tonic-gate 	rv = 0;
533*0Sstevel@tonic-gate 	while ((tnp = ap->head) != NULL) {
534*0Sstevel@tonic-gate 		ap->head = tnp->next;
535*0Sstevel@tonic-gate 
536*0Sstevel@tonic-gate 		ndi_devi_enter(ap->pdip, &circ);
537*0Sstevel@tonic-gate 
538*0Sstevel@tonic-gate 		/*
539*0Sstevel@tonic-gate 		 * Check if the branch already exists.
540*0Sstevel@tonic-gate 		 */
541*0Sstevel@tonic-gate 		exists = 0;
542*0Sstevel@tonic-gate 		dip = e_ddi_nodeid_to_dip(tnp->nodeid);
543*0Sstevel@tonic-gate 		if (dip != NULL) {
544*0Sstevel@tonic-gate 			exists = 1;
545*0Sstevel@tonic-gate 
546*0Sstevel@tonic-gate 			/* Parent is held busy, so release hold */
547*0Sstevel@tonic-gate 			ndi_rele_devi(dip);
548*0Sstevel@tonic-gate #ifdef	DEBUG
549*0Sstevel@tonic-gate 			cmn_err(CE_WARN, "create_prom_branch: dip(%p) exists"
550*0Sstevel@tonic-gate 			    " for nodeid 0x%x", (void *)dip, tnp->nodeid);
551*0Sstevel@tonic-gate #endif
552*0Sstevel@tonic-gate 		} else {
553*0Sstevel@tonic-gate 			dip = i_ddi_create_branch(ap->pdip, tnp->nodeid);
554*0Sstevel@tonic-gate 		}
555*0Sstevel@tonic-gate 
556*0Sstevel@tonic-gate 		kmem_free(tnp, sizeof (struct ptnode));
557*0Sstevel@tonic-gate 
558*0Sstevel@tonic-gate 		if (dip == NULL) {
559*0Sstevel@tonic-gate 			ndi_devi_exit(ap->pdip, circ);
560*0Sstevel@tonic-gate 			rv = EIO;
561*0Sstevel@tonic-gate 			continue;
562*0Sstevel@tonic-gate 		}
563*0Sstevel@tonic-gate 
564*0Sstevel@tonic-gate 		ASSERT(ddi_get_parent(dip) == ap->pdip);
565*0Sstevel@tonic-gate 
566*0Sstevel@tonic-gate 		/*
567*0Sstevel@tonic-gate 		 * Hold the branch if it is not already held
568*0Sstevel@tonic-gate 		 */
569*0Sstevel@tonic-gate 		if (!exists)
570*0Sstevel@tonic-gate 			e_ddi_branch_hold(dip);
571*0Sstevel@tonic-gate 
572*0Sstevel@tonic-gate 		ASSERT(e_ddi_branch_held(dip));
573*0Sstevel@tonic-gate 
574*0Sstevel@tonic-gate 		/*
575*0Sstevel@tonic-gate 		 * Set all dips in the branch offline so that
576*0Sstevel@tonic-gate 		 * only a "configure" operation can attach
577*0Sstevel@tonic-gate 		 * the branch
578*0Sstevel@tonic-gate 		 */
579*0Sstevel@tonic-gate 		(void) set_dip_offline(dip, NULL);
580*0Sstevel@tonic-gate 
581*0Sstevel@tonic-gate 		ndi_devi_enter(dip, &c);
582*0Sstevel@tonic-gate 		ddi_walk_devs(ddi_get_child(dip), set_dip_offline, NULL);
583*0Sstevel@tonic-gate 		ndi_devi_exit(dip, c);
584*0Sstevel@tonic-gate 
585*0Sstevel@tonic-gate 		ndi_devi_exit(ap->pdip, circ);
586*0Sstevel@tonic-gate 
587*0Sstevel@tonic-gate 		if (ap->flags & DEVI_BRANCH_CONFIGURE) {
588*0Sstevel@tonic-gate 			int error = e_ddi_branch_configure(dip, &ap->fdip, 0);
589*0Sstevel@tonic-gate 			if (error && rv == 0)
590*0Sstevel@tonic-gate 				rv = error;
591*0Sstevel@tonic-gate 		}
592*0Sstevel@tonic-gate 
593*0Sstevel@tonic-gate 		/*
594*0Sstevel@tonic-gate 		 * Invoke devi_branch_callback() (if it exists) only for
595*0Sstevel@tonic-gate 		 * newly created branches
596*0Sstevel@tonic-gate 		 */
597*0Sstevel@tonic-gate 		if (bp->devi_branch_callback && !exists)
598*0Sstevel@tonic-gate 			bp->devi_branch_callback(dip, bp->arg, 0);
599*0Sstevel@tonic-gate 	}
600*0Sstevel@tonic-gate 
601*0Sstevel@tonic-gate 	return (rv);
602*0Sstevel@tonic-gate }
603*0Sstevel@tonic-gate 
604*0Sstevel@tonic-gate static int
605*0Sstevel@tonic-gate sid_node_create(dev_info_t *pdip, devi_branch_t *bp, dev_info_t **rdipp)
606*0Sstevel@tonic-gate {
607*0Sstevel@tonic-gate 	int			rv, circ, len;
608*0Sstevel@tonic-gate 	int			i, flags;
609*0Sstevel@tonic-gate 	dev_info_t		*dip;
610*0Sstevel@tonic-gate 	char			*nbuf;
611*0Sstevel@tonic-gate 	static const char	*noname = "<none>";
612*0Sstevel@tonic-gate 
613*0Sstevel@tonic-gate 	ASSERT(pdip);
614*0Sstevel@tonic-gate 	ASSERT(DEVI_BUSY_OWNED(pdip));
615*0Sstevel@tonic-gate 
616*0Sstevel@tonic-gate 	flags = 0;
617*0Sstevel@tonic-gate 
618*0Sstevel@tonic-gate 	/*
619*0Sstevel@tonic-gate 	 * Creating the root of a branch ?
620*0Sstevel@tonic-gate 	 */
621*0Sstevel@tonic-gate 	if (rdipp) {
622*0Sstevel@tonic-gate 		*rdipp = NULL;
623*0Sstevel@tonic-gate 		flags = DEVI_BRANCH_ROOT;
624*0Sstevel@tonic-gate 	}
625*0Sstevel@tonic-gate 
626*0Sstevel@tonic-gate 	ndi_devi_alloc_sleep(pdip, (char *)noname, DEVI_SID_NODEID, &dip);
627*0Sstevel@tonic-gate 	rv = bp->create.sid_branch_create(dip, bp->arg, flags);
628*0Sstevel@tonic-gate 
629*0Sstevel@tonic-gate 	nbuf = kmem_alloc(OBP_MAXDRVNAME, KM_SLEEP);
630*0Sstevel@tonic-gate 
631*0Sstevel@tonic-gate 	if (rv == DDI_WALK_ERROR) {
632*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "e_ddi_branch_create: Error setting"
633*0Sstevel@tonic-gate 		    " properties on devinfo node %p",  (void *)dip);
634*0Sstevel@tonic-gate 		goto fail;
635*0Sstevel@tonic-gate 	}
636*0Sstevel@tonic-gate 
637*0Sstevel@tonic-gate 	len = OBP_MAXDRVNAME;
638*0Sstevel@tonic-gate 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
639*0Sstevel@tonic-gate 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "name", nbuf, &len)
640*0Sstevel@tonic-gate 	    != DDI_PROP_SUCCESS) {
641*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "e_ddi_branch_create: devinfo node %p has"
642*0Sstevel@tonic-gate 		    "no name property", (void *)dip);
643*0Sstevel@tonic-gate 		goto fail;
644*0Sstevel@tonic-gate 	}
645*0Sstevel@tonic-gate 
646*0Sstevel@tonic-gate 	ASSERT(i_ddi_node_state(dip) == DS_PROTO);
647*0Sstevel@tonic-gate 	if (ndi_devi_set_nodename(dip, nbuf, 0) != NDI_SUCCESS) {
648*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "e_ddi_branch_create: cannot set name (%s)"
649*0Sstevel@tonic-gate 		    " for devinfo node %p", nbuf, (void *)dip);
650*0Sstevel@tonic-gate 		goto fail;
651*0Sstevel@tonic-gate 	}
652*0Sstevel@tonic-gate 
653*0Sstevel@tonic-gate 	kmem_free(nbuf, OBP_MAXDRVNAME);
654*0Sstevel@tonic-gate 
655*0Sstevel@tonic-gate 	/*
656*0Sstevel@tonic-gate 	 * Ignore bind failures just like boot does
657*0Sstevel@tonic-gate 	 */
658*0Sstevel@tonic-gate 	(void) ndi_devi_bind_driver(dip, 0);
659*0Sstevel@tonic-gate 
660*0Sstevel@tonic-gate 	switch (rv) {
661*0Sstevel@tonic-gate 	case DDI_WALK_CONTINUE:
662*0Sstevel@tonic-gate 	case DDI_WALK_PRUNESIB:
663*0Sstevel@tonic-gate 		ndi_devi_enter(dip, &circ);
664*0Sstevel@tonic-gate 
665*0Sstevel@tonic-gate 		i = DDI_WALK_CONTINUE;
666*0Sstevel@tonic-gate 		for (; i == DDI_WALK_CONTINUE; ) {
667*0Sstevel@tonic-gate 			i = sid_node_create(dip, bp, NULL);
668*0Sstevel@tonic-gate 		}
669*0Sstevel@tonic-gate 
670*0Sstevel@tonic-gate 		ASSERT(i == DDI_WALK_ERROR || i == DDI_WALK_PRUNESIB);
671*0Sstevel@tonic-gate 		if (i == DDI_WALK_ERROR)
672*0Sstevel@tonic-gate 			rv = i;
673*0Sstevel@tonic-gate 		/*
674*0Sstevel@tonic-gate 		 * If PRUNESIB stop creating siblings
675*0Sstevel@tonic-gate 		 * of dip's child. Subsequent walk behavior
676*0Sstevel@tonic-gate 		 * is determined by rv returned by dip.
677*0Sstevel@tonic-gate 		 */
678*0Sstevel@tonic-gate 
679*0Sstevel@tonic-gate 		ndi_devi_exit(dip, circ);
680*0Sstevel@tonic-gate 		break;
681*0Sstevel@tonic-gate 	case DDI_WALK_TERMINATE:
682*0Sstevel@tonic-gate 		/*
683*0Sstevel@tonic-gate 		 * Don't create children and ask our parent
684*0Sstevel@tonic-gate 		 * to not create siblings either.
685*0Sstevel@tonic-gate 		 */
686*0Sstevel@tonic-gate 		rv = DDI_WALK_PRUNESIB;
687*0Sstevel@tonic-gate 		break;
688*0Sstevel@tonic-gate 	case DDI_WALK_PRUNECHILD:
689*0Sstevel@tonic-gate 		/*
690*0Sstevel@tonic-gate 		 * Don't create children, but ask parent to continue
691*0Sstevel@tonic-gate 		 * with siblings.
692*0Sstevel@tonic-gate 		 */
693*0Sstevel@tonic-gate 		rv = DDI_WALK_CONTINUE;
694*0Sstevel@tonic-gate 		break;
695*0Sstevel@tonic-gate 	default:
696*0Sstevel@tonic-gate 		ASSERT(0);
697*0Sstevel@tonic-gate 		break;
698*0Sstevel@tonic-gate 	}
699*0Sstevel@tonic-gate 
700*0Sstevel@tonic-gate 	if (rdipp)
701*0Sstevel@tonic-gate 		*rdipp = dip;
702*0Sstevel@tonic-gate 
703*0Sstevel@tonic-gate 	/*
704*0Sstevel@tonic-gate 	 * Set device offline - only the "configure" op should cause an attach
705*0Sstevel@tonic-gate 	 */
706*0Sstevel@tonic-gate 	(void) set_dip_offline(dip, NULL);
707*0Sstevel@tonic-gate 
708*0Sstevel@tonic-gate 	return (rv);
709*0Sstevel@tonic-gate fail:
710*0Sstevel@tonic-gate 	(void) ndi_devi_free(dip);
711*0Sstevel@tonic-gate 	kmem_free(nbuf, OBP_MAXDRVNAME);
712*0Sstevel@tonic-gate 	return (DDI_WALK_ERROR);
713*0Sstevel@tonic-gate }
714*0Sstevel@tonic-gate 
715*0Sstevel@tonic-gate static int
716*0Sstevel@tonic-gate create_sid_branch(
717*0Sstevel@tonic-gate 	dev_info_t	*pdip,
718*0Sstevel@tonic-gate 	devi_branch_t	*bp,
719*0Sstevel@tonic-gate 	dev_info_t	**dipp,
720*0Sstevel@tonic-gate 	uint_t		flags)
721*0Sstevel@tonic-gate {
722*0Sstevel@tonic-gate 	int		rv = 0, state = DDI_WALK_CONTINUE;
723*0Sstevel@tonic-gate 	dev_info_t	*rdip;
724*0Sstevel@tonic-gate 
725*0Sstevel@tonic-gate 	while (state == DDI_WALK_CONTINUE) {
726*0Sstevel@tonic-gate 		int	circ;
727*0Sstevel@tonic-gate 
728*0Sstevel@tonic-gate 		ndi_devi_enter(pdip, &circ);
729*0Sstevel@tonic-gate 
730*0Sstevel@tonic-gate 		state = sid_node_create(pdip, bp, &rdip);
731*0Sstevel@tonic-gate 		if (rdip == NULL) {
732*0Sstevel@tonic-gate 			ndi_devi_exit(pdip, circ);
733*0Sstevel@tonic-gate 			ASSERT(state == DDI_WALK_ERROR);
734*0Sstevel@tonic-gate 			break;
735*0Sstevel@tonic-gate 		}
736*0Sstevel@tonic-gate 
737*0Sstevel@tonic-gate 		e_ddi_branch_hold(rdip);
738*0Sstevel@tonic-gate 
739*0Sstevel@tonic-gate 		ndi_devi_exit(pdip, circ);
740*0Sstevel@tonic-gate 
741*0Sstevel@tonic-gate 		if (flags & DEVI_BRANCH_CONFIGURE) {
742*0Sstevel@tonic-gate 			int error = e_ddi_branch_configure(rdip, dipp, 0);
743*0Sstevel@tonic-gate 			if (error && rv == 0)
744*0Sstevel@tonic-gate 				rv = error;
745*0Sstevel@tonic-gate 		}
746*0Sstevel@tonic-gate 
747*0Sstevel@tonic-gate 		/*
748*0Sstevel@tonic-gate 		 * devi_branch_callback() is optional
749*0Sstevel@tonic-gate 		 */
750*0Sstevel@tonic-gate 		if (bp->devi_branch_callback)
751*0Sstevel@tonic-gate 			bp->devi_branch_callback(rdip, bp->arg, 0);
752*0Sstevel@tonic-gate 	}
753*0Sstevel@tonic-gate 
754*0Sstevel@tonic-gate 	ASSERT(state == DDI_WALK_ERROR || state == DDI_WALK_PRUNESIB);
755*0Sstevel@tonic-gate 
756*0Sstevel@tonic-gate 	return (state == DDI_WALK_ERROR ? EIO : rv);
757*0Sstevel@tonic-gate }
758*0Sstevel@tonic-gate 
759*0Sstevel@tonic-gate int
760*0Sstevel@tonic-gate e_ddi_branch_create(
761*0Sstevel@tonic-gate 	dev_info_t	*pdip,
762*0Sstevel@tonic-gate 	devi_branch_t	*bp,
763*0Sstevel@tonic-gate 	dev_info_t	**dipp,
764*0Sstevel@tonic-gate 	uint_t		flags)
765*0Sstevel@tonic-gate {
766*0Sstevel@tonic-gate 	int prom_devi, sid_devi, error;
767*0Sstevel@tonic-gate 
768*0Sstevel@tonic-gate 	if (pdip == NULL || bp == NULL || bp->type == 0)
769*0Sstevel@tonic-gate 		return (EINVAL);
770*0Sstevel@tonic-gate 
771*0Sstevel@tonic-gate 	prom_devi = (bp->type == DEVI_BRANCH_PROM) ? 1 : 0;
772*0Sstevel@tonic-gate 	sid_devi = (bp->type == DEVI_BRANCH_SID) ? 1 : 0;
773*0Sstevel@tonic-gate 
774*0Sstevel@tonic-gate 	if (prom_devi && bp->create.prom_branch_select == NULL)
775*0Sstevel@tonic-gate 		return (EINVAL);
776*0Sstevel@tonic-gate 	else if (sid_devi && bp->create.sid_branch_create == NULL)
777*0Sstevel@tonic-gate 		return (EINVAL);
778*0Sstevel@tonic-gate 	else if (!prom_devi && !sid_devi)
779*0Sstevel@tonic-gate 		return (EINVAL);
780*0Sstevel@tonic-gate 
781*0Sstevel@tonic-gate 	if (flags & DEVI_BRANCH_EVENT)
782*0Sstevel@tonic-gate 		return (EINVAL);
783*0Sstevel@tonic-gate 
784*0Sstevel@tonic-gate 	if (prom_devi) {
785*0Sstevel@tonic-gate 		struct pta pta = {0};
786*0Sstevel@tonic-gate 
787*0Sstevel@tonic-gate 		pta.pdip = pdip;
788*0Sstevel@tonic-gate 		pta.bp = bp;
789*0Sstevel@tonic-gate 		pta.flags = flags;
790*0Sstevel@tonic-gate 
791*0Sstevel@tonic-gate 		error = prom_tree_access(create_prom_branch, &pta, NULL);
792*0Sstevel@tonic-gate 
793*0Sstevel@tonic-gate 		if (dipp)
794*0Sstevel@tonic-gate 			*dipp = pta.fdip;
795*0Sstevel@tonic-gate 		else if (pta.fdip)
796*0Sstevel@tonic-gate 			ndi_rele_devi(pta.fdip);
797*0Sstevel@tonic-gate 	} else {
798*0Sstevel@tonic-gate 		error = create_sid_branch(pdip, bp, dipp, flags);
799*0Sstevel@tonic-gate 	}
800*0Sstevel@tonic-gate 
801*0Sstevel@tonic-gate 	return (error);
802*0Sstevel@tonic-gate }
803*0Sstevel@tonic-gate 
804*0Sstevel@tonic-gate int
805*0Sstevel@tonic-gate e_ddi_branch_configure(dev_info_t *rdip, dev_info_t **dipp, uint_t flags)
806*0Sstevel@tonic-gate {
807*0Sstevel@tonic-gate 	int		circ, rv;
808*0Sstevel@tonic-gate 	char		*devnm;
809*0Sstevel@tonic-gate 	dev_info_t	*pdip;
810*0Sstevel@tonic-gate 
811*0Sstevel@tonic-gate 	if (dipp)
812*0Sstevel@tonic-gate 		*dipp = NULL;
813*0Sstevel@tonic-gate 
814*0Sstevel@tonic-gate 	if (rdip == NULL || flags != 0 || (flags & DEVI_BRANCH_EVENT))
815*0Sstevel@tonic-gate 		return (EINVAL);
816*0Sstevel@tonic-gate 
817*0Sstevel@tonic-gate 	pdip = ddi_get_parent(rdip);
818*0Sstevel@tonic-gate 
819*0Sstevel@tonic-gate 	ndi_devi_enter(pdip, &circ);
820*0Sstevel@tonic-gate 
821*0Sstevel@tonic-gate 	if (!e_ddi_branch_held(rdip)) {
822*0Sstevel@tonic-gate 		ndi_devi_exit(pdip, circ);
823*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "e_ddi_branch_configure: "
824*0Sstevel@tonic-gate 		    "dip(%p) not held", (void *)rdip);
825*0Sstevel@tonic-gate 		return (EINVAL);
826*0Sstevel@tonic-gate 	}
827*0Sstevel@tonic-gate 
828*0Sstevel@tonic-gate 	if (i_ddi_node_state(rdip) < DS_INITIALIZED) {
829*0Sstevel@tonic-gate 		/*
830*0Sstevel@tonic-gate 		 * First attempt to bind a driver. If we fail, return
831*0Sstevel@tonic-gate 		 * success (On some platforms, dips for some device
832*0Sstevel@tonic-gate 		 * types (CPUs) may not have a driver)
833*0Sstevel@tonic-gate 		 */
834*0Sstevel@tonic-gate 		if (ndi_devi_bind_driver(rdip, 0) != NDI_SUCCESS) {
835*0Sstevel@tonic-gate 			ndi_devi_exit(pdip, circ);
836*0Sstevel@tonic-gate 			return (0);
837*0Sstevel@tonic-gate 		}
838*0Sstevel@tonic-gate 
839*0Sstevel@tonic-gate 		if (ddi_initchild(pdip, rdip) != DDI_SUCCESS) {
840*0Sstevel@tonic-gate 			rv = NDI_FAILURE;
841*0Sstevel@tonic-gate 			goto out;
842*0Sstevel@tonic-gate 		}
843*0Sstevel@tonic-gate 	}
844*0Sstevel@tonic-gate 
845*0Sstevel@tonic-gate 	ASSERT(i_ddi_node_state(rdip) >= DS_INITIALIZED);
846*0Sstevel@tonic-gate 
847*0Sstevel@tonic-gate 	devnm = kmem_alloc(MAXNAMELEN + 1, KM_SLEEP);
848*0Sstevel@tonic-gate 
849*0Sstevel@tonic-gate 	(void) ddi_deviname(rdip, devnm);
850*0Sstevel@tonic-gate 
851*0Sstevel@tonic-gate 	if ((rv = ndi_devi_config_one(pdip, devnm+1, &rdip,
852*0Sstevel@tonic-gate 	    NDI_DEVI_ONLINE | NDI_CONFIG)) == NDI_SUCCESS) {
853*0Sstevel@tonic-gate 		/* release hold from ndi_devi_config_one() */
854*0Sstevel@tonic-gate 		ndi_rele_devi(rdip);
855*0Sstevel@tonic-gate 	}
856*0Sstevel@tonic-gate 
857*0Sstevel@tonic-gate 	kmem_free(devnm, MAXNAMELEN + 1);
858*0Sstevel@tonic-gate out:
859*0Sstevel@tonic-gate 	if (rv != NDI_SUCCESS && dipp) {
860*0Sstevel@tonic-gate 		ndi_hold_devi(rdip);
861*0Sstevel@tonic-gate 		*dipp = rdip;
862*0Sstevel@tonic-gate 	}
863*0Sstevel@tonic-gate 	ndi_devi_exit(pdip, circ);
864*0Sstevel@tonic-gate 	return (ndi2errno(rv));
865*0Sstevel@tonic-gate }
866*0Sstevel@tonic-gate 
867*0Sstevel@tonic-gate void
868*0Sstevel@tonic-gate e_ddi_branch_hold(dev_info_t *rdip)
869*0Sstevel@tonic-gate {
870*0Sstevel@tonic-gate 	if (e_ddi_branch_held(rdip)) {
871*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "e_ddi_branch_hold: branch already held");
872*0Sstevel@tonic-gate 		return;
873*0Sstevel@tonic-gate 	}
874*0Sstevel@tonic-gate 
875*0Sstevel@tonic-gate 	mutex_enter(&DEVI(rdip)->devi_lock);
876*0Sstevel@tonic-gate 	if ((DEVI(rdip)->devi_flags & DEVI_BRANCH_HELD) == 0) {
877*0Sstevel@tonic-gate 		DEVI(rdip)->devi_flags |= DEVI_BRANCH_HELD;
878*0Sstevel@tonic-gate 		DEVI(rdip)->devi_ref++;
879*0Sstevel@tonic-gate 	}
880*0Sstevel@tonic-gate 	ASSERT(DEVI(rdip)->devi_ref > 0);
881*0Sstevel@tonic-gate 	mutex_exit(&DEVI(rdip)->devi_lock);
882*0Sstevel@tonic-gate }
883*0Sstevel@tonic-gate 
884*0Sstevel@tonic-gate int
885*0Sstevel@tonic-gate e_ddi_branch_held(dev_info_t *rdip)
886*0Sstevel@tonic-gate {
887*0Sstevel@tonic-gate 	int rv = 0;
888*0Sstevel@tonic-gate 
889*0Sstevel@tonic-gate 	mutex_enter(&DEVI(rdip)->devi_lock);
890*0Sstevel@tonic-gate 	if ((DEVI(rdip)->devi_flags & DEVI_BRANCH_HELD) &&
891*0Sstevel@tonic-gate 	    DEVI(rdip)->devi_ref > 0) {
892*0Sstevel@tonic-gate 		rv = 1;
893*0Sstevel@tonic-gate 	}
894*0Sstevel@tonic-gate 	mutex_exit(&DEVI(rdip)->devi_lock);
895*0Sstevel@tonic-gate 
896*0Sstevel@tonic-gate 	return (rv);
897*0Sstevel@tonic-gate }
898*0Sstevel@tonic-gate void
899*0Sstevel@tonic-gate e_ddi_branch_rele(dev_info_t *rdip)
900*0Sstevel@tonic-gate {
901*0Sstevel@tonic-gate 	mutex_enter(&DEVI(rdip)->devi_lock);
902*0Sstevel@tonic-gate 	DEVI(rdip)->devi_flags &= ~DEVI_BRANCH_HELD;
903*0Sstevel@tonic-gate 	DEVI(rdip)->devi_ref--;
904*0Sstevel@tonic-gate 	mutex_exit(&DEVI(rdip)->devi_lock);
905*0Sstevel@tonic-gate }
906*0Sstevel@tonic-gate 
907*0Sstevel@tonic-gate int
908*0Sstevel@tonic-gate e_ddi_branch_unconfigure(
909*0Sstevel@tonic-gate 	dev_info_t *rdip,
910*0Sstevel@tonic-gate 	dev_info_t **dipp,
911*0Sstevel@tonic-gate 	uint_t flags)
912*0Sstevel@tonic-gate {
913*0Sstevel@tonic-gate 	int	circ, rv;
914*0Sstevel@tonic-gate 	int	destroy;
915*0Sstevel@tonic-gate 	char	*devnm;
916*0Sstevel@tonic-gate 	uint_t	nflags;
917*0Sstevel@tonic-gate 	dev_info_t *pdip;
918*0Sstevel@tonic-gate 
919*0Sstevel@tonic-gate 	if (dipp)
920*0Sstevel@tonic-gate 		*dipp = NULL;
921*0Sstevel@tonic-gate 
922*0Sstevel@tonic-gate 	if (rdip == NULL)
923*0Sstevel@tonic-gate 		return (EINVAL);
924*0Sstevel@tonic-gate 
925*0Sstevel@tonic-gate 	pdip = ddi_get_parent(rdip);
926*0Sstevel@tonic-gate 
927*0Sstevel@tonic-gate 	ASSERT(pdip);
928*0Sstevel@tonic-gate 
929*0Sstevel@tonic-gate 	/*
930*0Sstevel@tonic-gate 	 * Check if caller holds pdip busy - can cause deadlocks during
931*0Sstevel@tonic-gate 	 * devfs_clean()
932*0Sstevel@tonic-gate 	 */
933*0Sstevel@tonic-gate 	if (DEVI_BUSY_OWNED(pdip)) {
934*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "e_ddi_branch_unconfigure: failed: parent"
935*0Sstevel@tonic-gate 		    " devinfo node(%p) is busy held", (void *)pdip);
936*0Sstevel@tonic-gate 		return (EINVAL);
937*0Sstevel@tonic-gate 	}
938*0Sstevel@tonic-gate 
939*0Sstevel@tonic-gate 	destroy = (flags & DEVI_BRANCH_DESTROY) ? 1 : 0;
940*0Sstevel@tonic-gate 
941*0Sstevel@tonic-gate 	devnm = kmem_alloc(MAXNAMELEN + 1, KM_SLEEP);
942*0Sstevel@tonic-gate 
943*0Sstevel@tonic-gate 	ndi_devi_enter(pdip, &circ);
944*0Sstevel@tonic-gate 	(void) ddi_deviname(rdip, devnm);
945*0Sstevel@tonic-gate 	ndi_devi_exit(pdip, circ);
946*0Sstevel@tonic-gate 
947*0Sstevel@tonic-gate 	/*
948*0Sstevel@tonic-gate 	 * ddi_deviname() returns a component name with / prepended.
949*0Sstevel@tonic-gate 	 */
950*0Sstevel@tonic-gate 	rv = devfs_clean(pdip, devnm + 1, DV_CLEAN_FORCE);
951*0Sstevel@tonic-gate 	if (rv) {
952*0Sstevel@tonic-gate 		kmem_free(devnm, MAXNAMELEN + 1);
953*0Sstevel@tonic-gate 		return (rv);
954*0Sstevel@tonic-gate 	}
955*0Sstevel@tonic-gate 
956*0Sstevel@tonic-gate 	ndi_devi_enter(pdip, &circ);
957*0Sstevel@tonic-gate 
958*0Sstevel@tonic-gate 	/*
959*0Sstevel@tonic-gate 	 * Recreate device name as it may have changed state (init/uninit)
960*0Sstevel@tonic-gate 	 * when parent busy lock was dropped for devfs_clean()
961*0Sstevel@tonic-gate 	 */
962*0Sstevel@tonic-gate 	(void) ddi_deviname(rdip, devnm);
963*0Sstevel@tonic-gate 
964*0Sstevel@tonic-gate 	if (!e_ddi_branch_held(rdip)) {
965*0Sstevel@tonic-gate 		kmem_free(devnm, MAXNAMELEN + 1);
966*0Sstevel@tonic-gate 		ndi_devi_exit(pdip, circ);
967*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "e_ddi_%s_branch: dip(%p) not held",
968*0Sstevel@tonic-gate 		    destroy ? "destroy" : "unconfigure", (void *)rdip);
969*0Sstevel@tonic-gate 		return (EINVAL);
970*0Sstevel@tonic-gate 	}
971*0Sstevel@tonic-gate 
972*0Sstevel@tonic-gate 	/*
973*0Sstevel@tonic-gate 	 * Release hold on the branch. This is ok since we are holding the
974*0Sstevel@tonic-gate 	 * parent busy. If rdip is not removed, we must do a hold on the
975*0Sstevel@tonic-gate 	 * branch before returning.
976*0Sstevel@tonic-gate 	 */
977*0Sstevel@tonic-gate 	e_ddi_branch_rele(rdip);
978*0Sstevel@tonic-gate 
979*0Sstevel@tonic-gate 	nflags = NDI_DEVI_OFFLINE;
980*0Sstevel@tonic-gate 	if (destroy || (flags & DEVI_BRANCH_DESTROY)) {
981*0Sstevel@tonic-gate 		nflags |= NDI_DEVI_REMOVE;
982*0Sstevel@tonic-gate 		destroy = 1;
983*0Sstevel@tonic-gate 	} else {
984*0Sstevel@tonic-gate 		nflags |= NDI_UNCONFIG;		/* uninit but don't remove */
985*0Sstevel@tonic-gate 	}
986*0Sstevel@tonic-gate 
987*0Sstevel@tonic-gate 	if (flags & DEVI_BRANCH_EVENT)
988*0Sstevel@tonic-gate 		nflags |= NDI_POST_EVENT;
989*0Sstevel@tonic-gate 
990*0Sstevel@tonic-gate 	if (i_ddi_node_state(pdip) == DS_READY &&
991*0Sstevel@tonic-gate 	    i_ddi_node_state(rdip) >= DS_INITIALIZED) {
992*0Sstevel@tonic-gate 		rv = ndi_devi_unconfig_one(pdip, devnm+1, dipp, nflags);
993*0Sstevel@tonic-gate 	} else {
994*0Sstevel@tonic-gate 		rv = e_ddi_devi_unconfig(rdip, dipp, nflags);
995*0Sstevel@tonic-gate 		if (rv == NDI_SUCCESS) {
996*0Sstevel@tonic-gate 			ASSERT(!destroy || ddi_get_child(rdip) == NULL);
997*0Sstevel@tonic-gate 			rv = ndi_devi_offline(rdip, nflags);
998*0Sstevel@tonic-gate 		}
999*0Sstevel@tonic-gate 	}
1000*0Sstevel@tonic-gate 
1001*0Sstevel@tonic-gate 	if (!destroy || rv != NDI_SUCCESS) {
1002*0Sstevel@tonic-gate 		/* The dip still exists, so do a hold */
1003*0Sstevel@tonic-gate 		e_ddi_branch_hold(rdip);
1004*0Sstevel@tonic-gate 	}
1005*0Sstevel@tonic-gate out:
1006*0Sstevel@tonic-gate 	kmem_free(devnm, MAXNAMELEN + 1);
1007*0Sstevel@tonic-gate 	ndi_devi_exit(pdip, circ);
1008*0Sstevel@tonic-gate 	return (ndi2errno(rv));
1009*0Sstevel@tonic-gate }
1010*0Sstevel@tonic-gate 
1011*0Sstevel@tonic-gate int
1012*0Sstevel@tonic-gate e_ddi_branch_destroy(dev_info_t *rdip, dev_info_t **dipp, uint_t flag)
1013*0Sstevel@tonic-gate {
1014*0Sstevel@tonic-gate 	return (e_ddi_branch_unconfigure(rdip, dipp,
1015*0Sstevel@tonic-gate 	    flag|DEVI_BRANCH_DESTROY));
1016*0Sstevel@tonic-gate }
1017*0Sstevel@tonic-gate 
1018*0Sstevel@tonic-gate /*
1019*0Sstevel@tonic-gate  * Number of chains for hash table
1020*0Sstevel@tonic-gate  */
1021*0Sstevel@tonic-gate #define	NUMCHAINS	17
1022*0Sstevel@tonic-gate 
1023*0Sstevel@tonic-gate /*
1024*0Sstevel@tonic-gate  * Devinfo busy arg
1025*0Sstevel@tonic-gate  */
1026*0Sstevel@tonic-gate struct devi_busy {
1027*0Sstevel@tonic-gate 	int dv_total;
1028*0Sstevel@tonic-gate 	int s_total;
1029*0Sstevel@tonic-gate 	mod_hash_t *dv_hash;
1030*0Sstevel@tonic-gate 	mod_hash_t *s_hash;
1031*0Sstevel@tonic-gate 	int (*callback)(dev_info_t *, void *, uint_t);
1032*0Sstevel@tonic-gate 	void *arg;
1033*0Sstevel@tonic-gate };
1034*0Sstevel@tonic-gate 
1035*0Sstevel@tonic-gate static int
1036*0Sstevel@tonic-gate visit_dip(dev_info_t *dip, void *arg)
1037*0Sstevel@tonic-gate {
1038*0Sstevel@tonic-gate 	uintptr_t sbusy, dvbusy, ref;
1039*0Sstevel@tonic-gate 	struct devi_busy *bsp = arg;
1040*0Sstevel@tonic-gate 
1041*0Sstevel@tonic-gate 	ASSERT(bsp->callback);
1042*0Sstevel@tonic-gate 
1043*0Sstevel@tonic-gate 	/*
1044*0Sstevel@tonic-gate 	 * A dip cannot be busy if its reference count is 0
1045*0Sstevel@tonic-gate 	 */
1046*0Sstevel@tonic-gate 	if ((ref = e_ddi_devi_holdcnt(dip)) == 0) {
1047*0Sstevel@tonic-gate 		return (bsp->callback(dip, bsp->arg, 0));
1048*0Sstevel@tonic-gate 	}
1049*0Sstevel@tonic-gate 
1050*0Sstevel@tonic-gate 	if (mod_hash_find(bsp->dv_hash, dip, (mod_hash_val_t *)&dvbusy))
1051*0Sstevel@tonic-gate 		dvbusy = 0;
1052*0Sstevel@tonic-gate 
1053*0Sstevel@tonic-gate 	/*
1054*0Sstevel@tonic-gate 	 * To catch device opens currently maintained on specfs common snodes.
1055*0Sstevel@tonic-gate 	 */
1056*0Sstevel@tonic-gate 	if (mod_hash_find(bsp->s_hash, dip, (mod_hash_val_t *)&sbusy))
1057*0Sstevel@tonic-gate 		sbusy = 0;
1058*0Sstevel@tonic-gate 
1059*0Sstevel@tonic-gate #ifdef	DEBUG
1060*0Sstevel@tonic-gate 	if (ref < sbusy || ref < dvbusy) {
1061*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "dip(%p): sopen = %lu, dvopen = %lu "
1062*0Sstevel@tonic-gate 		    "dip ref = %lu\n", (void *)dip, sbusy, dvbusy, ref);
1063*0Sstevel@tonic-gate 	}
1064*0Sstevel@tonic-gate #endif
1065*0Sstevel@tonic-gate 
1066*0Sstevel@tonic-gate 	dvbusy = (sbusy > dvbusy) ? sbusy : dvbusy;
1067*0Sstevel@tonic-gate 
1068*0Sstevel@tonic-gate 	return (bsp->callback(dip, bsp->arg, dvbusy));
1069*0Sstevel@tonic-gate }
1070*0Sstevel@tonic-gate 
1071*0Sstevel@tonic-gate static int
1072*0Sstevel@tonic-gate visit_snode(struct snode *sp, void *arg)
1073*0Sstevel@tonic-gate {
1074*0Sstevel@tonic-gate 	uintptr_t sbusy;
1075*0Sstevel@tonic-gate 	dev_info_t *dip;
1076*0Sstevel@tonic-gate 	int count;
1077*0Sstevel@tonic-gate 	struct devi_busy *bsp = arg;
1078*0Sstevel@tonic-gate 
1079*0Sstevel@tonic-gate 	ASSERT(sp);
1080*0Sstevel@tonic-gate 
1081*0Sstevel@tonic-gate 	/*
1082*0Sstevel@tonic-gate 	 * The stable lock is held. This prevents
1083*0Sstevel@tonic-gate 	 * the snode and its associated dip from
1084*0Sstevel@tonic-gate 	 * going away.
1085*0Sstevel@tonic-gate 	 */
1086*0Sstevel@tonic-gate 	dip = NULL;
1087*0Sstevel@tonic-gate 	count = spec_devi_open_count(sp, &dip);
1088*0Sstevel@tonic-gate 
1089*0Sstevel@tonic-gate 	if (count <= 0)
1090*0Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
1091*0Sstevel@tonic-gate 
1092*0Sstevel@tonic-gate 	ASSERT(dip);
1093*0Sstevel@tonic-gate 
1094*0Sstevel@tonic-gate 	if (mod_hash_remove(bsp->s_hash, dip, (mod_hash_val_t *)&sbusy))
1095*0Sstevel@tonic-gate 		sbusy = count;
1096*0Sstevel@tonic-gate 	else
1097*0Sstevel@tonic-gate 		sbusy += count;
1098*0Sstevel@tonic-gate 
1099*0Sstevel@tonic-gate 	if (mod_hash_insert(bsp->s_hash, dip, (mod_hash_val_t)sbusy)) {
1100*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s: s_hash insert failed: dip=0x%p, "
1101*0Sstevel@tonic-gate 		    "sbusy = %lu", "e_ddi_branch_referenced",
1102*0Sstevel@tonic-gate 		    (void *)dip, sbusy);
1103*0Sstevel@tonic-gate 	}
1104*0Sstevel@tonic-gate 
1105*0Sstevel@tonic-gate 	bsp->s_total += count;
1106*0Sstevel@tonic-gate 
1107*0Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
1108*0Sstevel@tonic-gate }
1109*0Sstevel@tonic-gate 
1110*0Sstevel@tonic-gate static void
1111*0Sstevel@tonic-gate visit_dvnode(struct dv_node *dv, void *arg)
1112*0Sstevel@tonic-gate {
1113*0Sstevel@tonic-gate 	uintptr_t dvbusy;
1114*0Sstevel@tonic-gate 	uint_t count;
1115*0Sstevel@tonic-gate 	struct vnode *vp;
1116*0Sstevel@tonic-gate 	struct devi_busy *bsp = arg;
1117*0Sstevel@tonic-gate 
1118*0Sstevel@tonic-gate 	ASSERT(dv && dv->dv_devi);
1119*0Sstevel@tonic-gate 
1120*0Sstevel@tonic-gate 	vp = DVTOV(dv);
1121*0Sstevel@tonic-gate 
1122*0Sstevel@tonic-gate 	mutex_enter(&vp->v_lock);
1123*0Sstevel@tonic-gate 	count = vp->v_count;
1124*0Sstevel@tonic-gate 	mutex_exit(&vp->v_lock);
1125*0Sstevel@tonic-gate 
1126*0Sstevel@tonic-gate 	if (!count)
1127*0Sstevel@tonic-gate 		return;
1128*0Sstevel@tonic-gate 
1129*0Sstevel@tonic-gate 	if (mod_hash_remove(bsp->dv_hash, dv->dv_devi,
1130*0Sstevel@tonic-gate 	    (mod_hash_val_t *)&dvbusy))
1131*0Sstevel@tonic-gate 		dvbusy = count;
1132*0Sstevel@tonic-gate 	else
1133*0Sstevel@tonic-gate 		dvbusy += count;
1134*0Sstevel@tonic-gate 
1135*0Sstevel@tonic-gate 	if (mod_hash_insert(bsp->dv_hash, dv->dv_devi,
1136*0Sstevel@tonic-gate 	    (mod_hash_val_t)dvbusy)) {
1137*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s: dv_hash insert failed: dip=0x%p, "
1138*0Sstevel@tonic-gate 		    "dvbusy=%lu", "e_ddi_branch_referenced",
1139*0Sstevel@tonic-gate 		    (void *)dv->dv_devi, dvbusy);
1140*0Sstevel@tonic-gate 	}
1141*0Sstevel@tonic-gate 
1142*0Sstevel@tonic-gate 	bsp->dv_total += count;
1143*0Sstevel@tonic-gate }
1144*0Sstevel@tonic-gate 
1145*0Sstevel@tonic-gate /*
1146*0Sstevel@tonic-gate  * Returns reference count on success or -1 on failure.
1147*0Sstevel@tonic-gate  */
1148*0Sstevel@tonic-gate int
1149*0Sstevel@tonic-gate e_ddi_branch_referenced(
1150*0Sstevel@tonic-gate 	dev_info_t *rdip,
1151*0Sstevel@tonic-gate 	int (*callback)(dev_info_t *dip, void *arg, uint_t ref),
1152*0Sstevel@tonic-gate 	void *arg)
1153*0Sstevel@tonic-gate {
1154*0Sstevel@tonic-gate 	int circ;
1155*0Sstevel@tonic-gate 	char *path;
1156*0Sstevel@tonic-gate 	dev_info_t *pdip;
1157*0Sstevel@tonic-gate 	struct devi_busy bsa = {0};
1158*0Sstevel@tonic-gate 
1159*0Sstevel@tonic-gate 	ASSERT(rdip);
1160*0Sstevel@tonic-gate 
1161*0Sstevel@tonic-gate 	path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
1162*0Sstevel@tonic-gate 
1163*0Sstevel@tonic-gate 	ndi_hold_devi(rdip);
1164*0Sstevel@tonic-gate 
1165*0Sstevel@tonic-gate 	pdip = ddi_get_parent(rdip);
1166*0Sstevel@tonic-gate 
1167*0Sstevel@tonic-gate 	ASSERT(pdip);
1168*0Sstevel@tonic-gate 
1169*0Sstevel@tonic-gate 	/*
1170*0Sstevel@tonic-gate 	 * Check if caller holds pdip busy - can cause deadlocks during
1171*0Sstevel@tonic-gate 	 * devfs_walk()
1172*0Sstevel@tonic-gate 	 */
1173*0Sstevel@tonic-gate 	if (!e_ddi_branch_held(rdip) || DEVI_BUSY_OWNED(pdip)) {
1174*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "e_ddi_branch_referenced: failed: "
1175*0Sstevel@tonic-gate 		    "devinfo branch(%p) not held or parent busy held",
1176*0Sstevel@tonic-gate 		    (void *)rdip);
1177*0Sstevel@tonic-gate 		ndi_rele_devi(rdip);
1178*0Sstevel@tonic-gate 		kmem_free(path, MAXPATHLEN);
1179*0Sstevel@tonic-gate 		return (-1);
1180*0Sstevel@tonic-gate 	}
1181*0Sstevel@tonic-gate 
1182*0Sstevel@tonic-gate 	ndi_devi_enter(pdip, &circ);
1183*0Sstevel@tonic-gate 	(void) ddi_pathname(rdip, path);
1184*0Sstevel@tonic-gate 	ndi_devi_exit(pdip, circ);
1185*0Sstevel@tonic-gate 
1186*0Sstevel@tonic-gate 	bsa.dv_hash = mod_hash_create_ptrhash("dv_node busy hash", NUMCHAINS,
1187*0Sstevel@tonic-gate 	    mod_hash_null_valdtor, sizeof (struct dev_info));
1188*0Sstevel@tonic-gate 
1189*0Sstevel@tonic-gate 	bsa.s_hash = mod_hash_create_ptrhash("snode busy hash", NUMCHAINS,
1190*0Sstevel@tonic-gate 	    mod_hash_null_valdtor, sizeof (struct snode));
1191*0Sstevel@tonic-gate 
1192*0Sstevel@tonic-gate 	if (devfs_walk(path, visit_dvnode, &bsa)) {
1193*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "e_ddi_branch_referenced: "
1194*0Sstevel@tonic-gate 		    "devfs walk failed for: %s", path);
1195*0Sstevel@tonic-gate 		kmem_free(path, MAXPATHLEN);
1196*0Sstevel@tonic-gate 		bsa.s_total = bsa.dv_total = -1;
1197*0Sstevel@tonic-gate 		goto out;
1198*0Sstevel@tonic-gate 	}
1199*0Sstevel@tonic-gate 
1200*0Sstevel@tonic-gate 	kmem_free(path, MAXPATHLEN);
1201*0Sstevel@tonic-gate 
1202*0Sstevel@tonic-gate 	/*
1203*0Sstevel@tonic-gate 	 * Walk the snode table to detect device opens, which are currently
1204*0Sstevel@tonic-gate 	 * maintained on specfs common snodes.
1205*0Sstevel@tonic-gate 	 */
1206*0Sstevel@tonic-gate 	spec_snode_walk(visit_snode, &bsa);
1207*0Sstevel@tonic-gate 
1208*0Sstevel@tonic-gate 	if (callback == NULL)
1209*0Sstevel@tonic-gate 		goto out;
1210*0Sstevel@tonic-gate 
1211*0Sstevel@tonic-gate 	bsa.callback = callback;
1212*0Sstevel@tonic-gate 	bsa.arg = arg;
1213*0Sstevel@tonic-gate 
1214*0Sstevel@tonic-gate 	if (visit_dip(rdip, &bsa) == DDI_WALK_CONTINUE) {
1215*0Sstevel@tonic-gate 		ndi_devi_enter(rdip, &circ);
1216*0Sstevel@tonic-gate 		ddi_walk_devs(ddi_get_child(rdip), visit_dip, &bsa);
1217*0Sstevel@tonic-gate 		ndi_devi_exit(rdip, circ);
1218*0Sstevel@tonic-gate 	}
1219*0Sstevel@tonic-gate 
1220*0Sstevel@tonic-gate out:
1221*0Sstevel@tonic-gate 	ndi_rele_devi(rdip);
1222*0Sstevel@tonic-gate 	mod_hash_destroy_ptrhash(bsa.s_hash);
1223*0Sstevel@tonic-gate 	mod_hash_destroy_ptrhash(bsa.dv_hash);
1224*0Sstevel@tonic-gate 	return (bsa.s_total > bsa.dv_total ? bsa.s_total : bsa.dv_total);
1225*0Sstevel@tonic-gate }
1226