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