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 51865Sdilpreet * Common Development and Distribution License (the "License"). 61865Sdilpreet * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 211900Seota 220Sstevel@tonic-gate /* 238662SJordan.Vaughan@Sun.com * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 240Sstevel@tonic-gate * Use is subject to license terms. 250Sstevel@tonic-gate */ 260Sstevel@tonic-gate 270Sstevel@tonic-gate /* 280Sstevel@tonic-gate * sun4 specific DDI implementation 290Sstevel@tonic-gate */ 300Sstevel@tonic-gate #include <sys/cpuvar.h> 310Sstevel@tonic-gate #include <sys/ddi_subrdefs.h> 320Sstevel@tonic-gate #include <sys/machsystm.h> 330Sstevel@tonic-gate #include <sys/sunndi.h> 340Sstevel@tonic-gate #include <sys/sysmacros.h> 350Sstevel@tonic-gate #include <sys/ontrap.h> 360Sstevel@tonic-gate #include <vm/seg_kmem.h> 370Sstevel@tonic-gate #include <sys/membar.h> 380Sstevel@tonic-gate #include <sys/dditypes.h> 390Sstevel@tonic-gate #include <sys/ndifm.h> 400Sstevel@tonic-gate #include <sys/fm/io/ddi.h> 410Sstevel@tonic-gate #include <sys/ivintr.h> 420Sstevel@tonic-gate #include <sys/bootconf.h> 430Sstevel@tonic-gate #include <sys/conf.h> 440Sstevel@tonic-gate #include <sys/ethernet.h> 450Sstevel@tonic-gate #include <sys/idprom.h> 460Sstevel@tonic-gate #include <sys/promif.h> 470Sstevel@tonic-gate #include <sys/prom_plat.h> 480Sstevel@tonic-gate #include <sys/systeminfo.h> 490Sstevel@tonic-gate #include <sys/fpu/fpusystm.h> 500Sstevel@tonic-gate #include <sys/vm.h> 510Sstevel@tonic-gate #include <sys/fs/dv_node.h> 520Sstevel@tonic-gate #include <sys/fs/snode.h> 530Sstevel@tonic-gate #include <sys/ddi_isa.h> 541991Sheppo #include <sys/modhash.h> 554845Svikram #include <sys/modctl.h> 564845Svikram #include <sys/sunldi_impl.h> 570Sstevel@tonic-gate 580Sstevel@tonic-gate dev_info_t *get_intr_parent(dev_info_t *, dev_info_t *, 59693Sgovinda ddi_intr_handle_impl_t *); 600Sstevel@tonic-gate #pragma weak get_intr_parent 610Sstevel@tonic-gate 620Sstevel@tonic-gate int process_intr_ops(dev_info_t *, dev_info_t *, ddi_intr_op_t, 630Sstevel@tonic-gate ddi_intr_handle_impl_t *, void *); 640Sstevel@tonic-gate #pragma weak process_intr_ops 650Sstevel@tonic-gate 660Sstevel@tonic-gate void cells_1275_copy(prop_1275_cell_t *, prop_1275_cell_t *, int32_t); 670Sstevel@tonic-gate prop_1275_cell_t *cells_1275_cmp(prop_1275_cell_t *, prop_1275_cell_t *, 680Sstevel@tonic-gate int32_t len); 690Sstevel@tonic-gate #pragma weak cells_1275_copy 700Sstevel@tonic-gate 710Sstevel@tonic-gate /* 720Sstevel@tonic-gate * Wrapper for ddi_prop_lookup_int_array(). 730Sstevel@tonic-gate * This is handy because it returns the prop length in 740Sstevel@tonic-gate * bytes which is what most of the callers require. 750Sstevel@tonic-gate */ 760Sstevel@tonic-gate 770Sstevel@tonic-gate static int 780Sstevel@tonic-gate get_prop_int_array(dev_info_t *di, char *pname, int **pval, uint_t *plen) 790Sstevel@tonic-gate { 800Sstevel@tonic-gate int ret; 810Sstevel@tonic-gate 820Sstevel@tonic-gate if ((ret = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, di, 830Sstevel@tonic-gate DDI_PROP_DONTPASS, pname, pval, plen)) == DDI_PROP_SUCCESS) { 840Sstevel@tonic-gate *plen = (*plen) * (uint_t)sizeof (int); 850Sstevel@tonic-gate } 860Sstevel@tonic-gate return (ret); 870Sstevel@tonic-gate } 880Sstevel@tonic-gate 890Sstevel@tonic-gate /* 900Sstevel@tonic-gate * SECTION: DDI Node Configuration 910Sstevel@tonic-gate */ 920Sstevel@tonic-gate 930Sstevel@tonic-gate /* 940Sstevel@tonic-gate * init_regspec_64: 950Sstevel@tonic-gate * 960Sstevel@tonic-gate * If the parent #size-cells is 2, convert the upa-style or 970Sstevel@tonic-gate * safari-style reg property from 2-size cells to 1 size cell 980Sstevel@tonic-gate * format, ignoring the size_hi, which must be zero for devices. 990Sstevel@tonic-gate * (It won't be zero in the memory list properties in the memory 1000Sstevel@tonic-gate * nodes, but that doesn't matter here.) 1010Sstevel@tonic-gate */ 1020Sstevel@tonic-gate struct ddi_parent_private_data * 1030Sstevel@tonic-gate init_regspec_64(dev_info_t *dip) 1040Sstevel@tonic-gate { 1050Sstevel@tonic-gate struct ddi_parent_private_data *pd; 1060Sstevel@tonic-gate dev_info_t *parent; 1070Sstevel@tonic-gate int size_cells; 1080Sstevel@tonic-gate 1090Sstevel@tonic-gate /* 1100Sstevel@tonic-gate * If there are no "reg"s in the child node, return. 1110Sstevel@tonic-gate */ 1120Sstevel@tonic-gate pd = ddi_get_parent_data(dip); 1130Sstevel@tonic-gate if ((pd == NULL) || (pd->par_nreg == 0)) { 1140Sstevel@tonic-gate return (pd); 1150Sstevel@tonic-gate } 1160Sstevel@tonic-gate parent = ddi_get_parent(dip); 1170Sstevel@tonic-gate 1180Sstevel@tonic-gate size_cells = ddi_prop_get_int(DDI_DEV_T_ANY, parent, 1190Sstevel@tonic-gate DDI_PROP_DONTPASS, "#size-cells", 1); 1200Sstevel@tonic-gate 1210Sstevel@tonic-gate if (size_cells != 1) { 1220Sstevel@tonic-gate 1230Sstevel@tonic-gate int n, j; 1240Sstevel@tonic-gate struct regspec *irp; 1250Sstevel@tonic-gate struct reg_64 { 1260Sstevel@tonic-gate uint_t addr_hi, addr_lo, size_hi, size_lo; 1270Sstevel@tonic-gate }; 1280Sstevel@tonic-gate struct reg_64 *r64_rp; 1290Sstevel@tonic-gate struct regspec *rp; 1300Sstevel@tonic-gate uint_t len = 0; 1310Sstevel@tonic-gate int *reg_prop; 1320Sstevel@tonic-gate 1330Sstevel@tonic-gate ASSERT(size_cells == 2); 1340Sstevel@tonic-gate 1350Sstevel@tonic-gate /* 1360Sstevel@tonic-gate * We already looked the property up once before if 1370Sstevel@tonic-gate * pd is non-NULL. 1380Sstevel@tonic-gate */ 1390Sstevel@tonic-gate (void) ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, 1400Sstevel@tonic-gate DDI_PROP_DONTPASS, OBP_REG, ®_prop, &len); 1410Sstevel@tonic-gate ASSERT(len != 0); 1420Sstevel@tonic-gate 1430Sstevel@tonic-gate n = sizeof (struct reg_64) / sizeof (int); 1440Sstevel@tonic-gate n = len / n; 1450Sstevel@tonic-gate 1460Sstevel@tonic-gate /* 1470Sstevel@tonic-gate * We're allocating a buffer the size of the PROM's property, 1480Sstevel@tonic-gate * but we're only using a smaller portion when we assign it 1490Sstevel@tonic-gate * to a regspec. We do this so that in the 1500Sstevel@tonic-gate * impl_ddi_sunbus_removechild function, we will 1510Sstevel@tonic-gate * always free the right amount of memory. 1520Sstevel@tonic-gate */ 1530Sstevel@tonic-gate irp = rp = (struct regspec *)reg_prop; 1540Sstevel@tonic-gate r64_rp = (struct reg_64 *)pd->par_reg; 1550Sstevel@tonic-gate 1560Sstevel@tonic-gate for (j = 0; j < n; ++j, ++rp, ++r64_rp) { 1570Sstevel@tonic-gate ASSERT(r64_rp->size_hi == 0); 1580Sstevel@tonic-gate rp->regspec_bustype = r64_rp->addr_hi; 1590Sstevel@tonic-gate rp->regspec_addr = r64_rp->addr_lo; 1600Sstevel@tonic-gate rp->regspec_size = r64_rp->size_lo; 1610Sstevel@tonic-gate } 1620Sstevel@tonic-gate 1630Sstevel@tonic-gate ddi_prop_free((void *)pd->par_reg); 1640Sstevel@tonic-gate pd->par_nreg = n; 1650Sstevel@tonic-gate pd->par_reg = irp; 1660Sstevel@tonic-gate } 1670Sstevel@tonic-gate return (pd); 1680Sstevel@tonic-gate } 1690Sstevel@tonic-gate 1700Sstevel@tonic-gate /* 1710Sstevel@tonic-gate * Create a ddi_parent_private_data structure from the ddi properties of 1720Sstevel@tonic-gate * the dev_info node. 1730Sstevel@tonic-gate * 1740Sstevel@tonic-gate * The "reg" is required if the driver wishes to create mappings on behalf 1750Sstevel@tonic-gate * of the device. The "reg" property is assumed to be a list of at least 1760Sstevel@tonic-gate * one triplet 1770Sstevel@tonic-gate * 1780Sstevel@tonic-gate * <bustype, address, size>*1 1790Sstevel@tonic-gate * 1800Sstevel@tonic-gate * The "interrupt" property is no longer part of parent private data on 1810Sstevel@tonic-gate * sun4u. The interrupt parent is may not be the device tree parent. 1820Sstevel@tonic-gate * 1830Sstevel@tonic-gate * The "ranges" property describes the mapping of child addresses to parent 1840Sstevel@tonic-gate * addresses. 1850Sstevel@tonic-gate * 1860Sstevel@tonic-gate * N.B. struct rangespec is defined for the following default values: 1870Sstevel@tonic-gate * parent child 1880Sstevel@tonic-gate * #address-cells 2 2 1890Sstevel@tonic-gate * #size-cells 1 1 1900Sstevel@tonic-gate * This function doesn't deal with non-default cells and will not create 1910Sstevel@tonic-gate * ranges in such cases. 1920Sstevel@tonic-gate */ 1930Sstevel@tonic-gate void 1940Sstevel@tonic-gate make_ddi_ppd(dev_info_t *child, struct ddi_parent_private_data **ppd) 1950Sstevel@tonic-gate { 1960Sstevel@tonic-gate struct ddi_parent_private_data *pdptr; 1970Sstevel@tonic-gate int *reg_prop, *rng_prop; 1980Sstevel@tonic-gate uint_t reg_len = 0, rng_len = 0; 1990Sstevel@tonic-gate dev_info_t *parent; 2000Sstevel@tonic-gate int parent_addr_cells, parent_size_cells; 2010Sstevel@tonic-gate int child_addr_cells, child_size_cells; 2020Sstevel@tonic-gate 2030Sstevel@tonic-gate *ppd = pdptr = kmem_zalloc(sizeof (*pdptr), KM_SLEEP); 2040Sstevel@tonic-gate 2050Sstevel@tonic-gate /* 2060Sstevel@tonic-gate * root node has no parent private data, so *ppd should 2070Sstevel@tonic-gate * be initialized for naming to work properly. 2080Sstevel@tonic-gate */ 2090Sstevel@tonic-gate if ((parent = ddi_get_parent(child)) == NULL) 2100Sstevel@tonic-gate return; 2110Sstevel@tonic-gate 2120Sstevel@tonic-gate /* 2130Sstevel@tonic-gate * Set reg field of parent data from "reg" property 2140Sstevel@tonic-gate */ 2150Sstevel@tonic-gate if ((get_prop_int_array(child, OBP_REG, ®_prop, ®_len) 2160Sstevel@tonic-gate == DDI_PROP_SUCCESS) && (reg_len != 0)) { 2170Sstevel@tonic-gate pdptr->par_nreg = (int)(reg_len / sizeof (struct regspec)); 2180Sstevel@tonic-gate pdptr->par_reg = (struct regspec *)reg_prop; 2190Sstevel@tonic-gate } 2200Sstevel@tonic-gate 2210Sstevel@tonic-gate /* 2220Sstevel@tonic-gate * "ranges" property ... 2230Sstevel@tonic-gate * 2240Sstevel@tonic-gate * This function does not handle cases where #address-cells != 2 2250Sstevel@tonic-gate * and * min(parent, child) #size-cells != 1 (see bugid 4211124). 2260Sstevel@tonic-gate * 2270Sstevel@tonic-gate * Nexus drivers with such exceptions (e.g. pci ranges) 2280Sstevel@tonic-gate * should either create a separate function for handling 2290Sstevel@tonic-gate * ranges or not use parent private data to store ranges. 2300Sstevel@tonic-gate */ 2310Sstevel@tonic-gate 2320Sstevel@tonic-gate /* root node has no ranges */ 2330Sstevel@tonic-gate if ((parent = ddi_get_parent(child)) == NULL) 2340Sstevel@tonic-gate return; 2350Sstevel@tonic-gate 2360Sstevel@tonic-gate child_addr_cells = ddi_prop_get_int(DDI_DEV_T_ANY, child, 2370Sstevel@tonic-gate DDI_PROP_DONTPASS, "#address-cells", 2); 2380Sstevel@tonic-gate child_size_cells = ddi_prop_get_int(DDI_DEV_T_ANY, child, 2390Sstevel@tonic-gate DDI_PROP_DONTPASS, "#size-cells", 1); 2400Sstevel@tonic-gate parent_addr_cells = ddi_prop_get_int(DDI_DEV_T_ANY, parent, 2410Sstevel@tonic-gate DDI_PROP_DONTPASS, "#address-cells", 2); 2420Sstevel@tonic-gate parent_size_cells = ddi_prop_get_int(DDI_DEV_T_ANY, parent, 2430Sstevel@tonic-gate DDI_PROP_DONTPASS, "#size-cells", 1); 2440Sstevel@tonic-gate if (child_addr_cells != 2 || parent_addr_cells != 2 || 2450Sstevel@tonic-gate (child_size_cells != 1 && parent_size_cells != 1)) { 2460Sstevel@tonic-gate NDI_CONFIG_DEBUG((CE_NOTE, "!ranges not made in parent data; " 2470Sstevel@tonic-gate "#address-cells or #size-cells have non-default value")); 2480Sstevel@tonic-gate return; 2490Sstevel@tonic-gate } 2500Sstevel@tonic-gate 2510Sstevel@tonic-gate if (get_prop_int_array(child, OBP_RANGES, &rng_prop, &rng_len) 2520Sstevel@tonic-gate == DDI_PROP_SUCCESS) { 2530Sstevel@tonic-gate pdptr->par_nrng = rng_len / (int)(sizeof (struct rangespec)); 2540Sstevel@tonic-gate pdptr->par_rng = (struct rangespec *)rng_prop; 2550Sstevel@tonic-gate } 2560Sstevel@tonic-gate } 2570Sstevel@tonic-gate 2580Sstevel@tonic-gate /* 2590Sstevel@tonic-gate * Free ddi_parent_private_data structure 2600Sstevel@tonic-gate */ 2610Sstevel@tonic-gate void 2620Sstevel@tonic-gate impl_free_ddi_ppd(dev_info_t *dip) 2630Sstevel@tonic-gate { 2640Sstevel@tonic-gate struct ddi_parent_private_data *pdptr = ddi_get_parent_data(dip); 2650Sstevel@tonic-gate 2660Sstevel@tonic-gate if (pdptr == NULL) 2670Sstevel@tonic-gate return; 2680Sstevel@tonic-gate 2690Sstevel@tonic-gate if (pdptr->par_nrng != 0) 2700Sstevel@tonic-gate ddi_prop_free((void *)pdptr->par_rng); 2710Sstevel@tonic-gate 2720Sstevel@tonic-gate if (pdptr->par_nreg != 0) 2730Sstevel@tonic-gate ddi_prop_free((void *)pdptr->par_reg); 2740Sstevel@tonic-gate 2750Sstevel@tonic-gate kmem_free(pdptr, sizeof (*pdptr)); 2760Sstevel@tonic-gate ddi_set_parent_data(dip, NULL); 2770Sstevel@tonic-gate } 2780Sstevel@tonic-gate 2790Sstevel@tonic-gate /* 2800Sstevel@tonic-gate * Name a child of sun busses based on the reg spec. 2810Sstevel@tonic-gate * Handles the following properties: 2820Sstevel@tonic-gate * 2830Sstevel@tonic-gate * Property value 2840Sstevel@tonic-gate * Name type 2850Sstevel@tonic-gate * 2860Sstevel@tonic-gate * reg register spec 2870Sstevel@tonic-gate * interrupts new (bus-oriented) interrupt spec 2880Sstevel@tonic-gate * ranges range spec 2890Sstevel@tonic-gate * 2900Sstevel@tonic-gate * This may be called multiple times, independent of 2910Sstevel@tonic-gate * initchild calls. 2920Sstevel@tonic-gate */ 2930Sstevel@tonic-gate static int 2940Sstevel@tonic-gate impl_sunbus_name_child(dev_info_t *child, char *name, int namelen) 2950Sstevel@tonic-gate { 2960Sstevel@tonic-gate struct ddi_parent_private_data *pdptr; 2970Sstevel@tonic-gate struct regspec *rp; 2980Sstevel@tonic-gate 2990Sstevel@tonic-gate /* 3000Sstevel@tonic-gate * Fill in parent-private data and this function returns to us 3010Sstevel@tonic-gate * an indication if it used "registers" to fill in the data. 3020Sstevel@tonic-gate */ 3030Sstevel@tonic-gate if (ddi_get_parent_data(child) == NULL) { 3040Sstevel@tonic-gate make_ddi_ppd(child, &pdptr); 3050Sstevel@tonic-gate ddi_set_parent_data(child, pdptr); 3060Sstevel@tonic-gate } 3070Sstevel@tonic-gate 3080Sstevel@tonic-gate /* 3090Sstevel@tonic-gate * No reg property, return null string as address 3100Sstevel@tonic-gate * (e.g. root node) 3110Sstevel@tonic-gate */ 3120Sstevel@tonic-gate name[0] = '\0'; 3130Sstevel@tonic-gate if (sparc_pd_getnreg(child) == 0) { 3140Sstevel@tonic-gate return (DDI_SUCCESS); 3150Sstevel@tonic-gate } 3160Sstevel@tonic-gate 3170Sstevel@tonic-gate rp = sparc_pd_getreg(child, 0); 3180Sstevel@tonic-gate (void) snprintf(name, namelen, "%x,%x", 3190Sstevel@tonic-gate rp->regspec_bustype, rp->regspec_addr); 3200Sstevel@tonic-gate return (DDI_SUCCESS); 3210Sstevel@tonic-gate } 3220Sstevel@tonic-gate 3230Sstevel@tonic-gate 3240Sstevel@tonic-gate /* 3250Sstevel@tonic-gate * Called from the bus_ctl op of some drivers. 3260Sstevel@tonic-gate * to implement the DDI_CTLOPS_INITCHILD operation. 3270Sstevel@tonic-gate * 3280Sstevel@tonic-gate * NEW drivers should NOT use this function, but should declare 3290Sstevel@tonic-gate * there own initchild/uninitchild handlers. (This function assumes 3300Sstevel@tonic-gate * the layout of the parent private data and the format of "reg", 3310Sstevel@tonic-gate * "ranges", "interrupts" properties and that #address-cells and 3320Sstevel@tonic-gate * #size-cells of the parent bus are defined to be default values.) 3330Sstevel@tonic-gate */ 3340Sstevel@tonic-gate int 3350Sstevel@tonic-gate impl_ddi_sunbus_initchild(dev_info_t *child) 3360Sstevel@tonic-gate { 3370Sstevel@tonic-gate char name[MAXNAMELEN]; 3380Sstevel@tonic-gate 3390Sstevel@tonic-gate (void) impl_sunbus_name_child(child, name, MAXNAMELEN); 3400Sstevel@tonic-gate ddi_set_name_addr(child, name); 3410Sstevel@tonic-gate 3420Sstevel@tonic-gate /* 3430Sstevel@tonic-gate * Try to merge .conf node. If successful, return failure to 3440Sstevel@tonic-gate * remove this child. 3450Sstevel@tonic-gate */ 3460Sstevel@tonic-gate if ((ndi_dev_is_persistent_node(child) == 0) && 3470Sstevel@tonic-gate (ndi_merge_node(child, impl_sunbus_name_child) == DDI_SUCCESS)) { 3480Sstevel@tonic-gate impl_ddi_sunbus_removechild(child); 3490Sstevel@tonic-gate return (DDI_FAILURE); 3500Sstevel@tonic-gate } 3510Sstevel@tonic-gate return (DDI_SUCCESS); 3520Sstevel@tonic-gate } 3530Sstevel@tonic-gate 3540Sstevel@tonic-gate /* 3550Sstevel@tonic-gate * A better name for this function would be impl_ddi_sunbus_uninitchild() 3560Sstevel@tonic-gate * It does not remove the child, it uninitializes it, reclaiming the 3570Sstevel@tonic-gate * resources taken by impl_ddi_sunbus_initchild. 3580Sstevel@tonic-gate */ 3590Sstevel@tonic-gate void 3600Sstevel@tonic-gate impl_ddi_sunbus_removechild(dev_info_t *dip) 3610Sstevel@tonic-gate { 3620Sstevel@tonic-gate impl_free_ddi_ppd(dip); 3630Sstevel@tonic-gate ddi_set_name_addr(dip, NULL); 3640Sstevel@tonic-gate /* 3650Sstevel@tonic-gate * Strip the node to properly convert it back to prototype form 3660Sstevel@tonic-gate */ 3670Sstevel@tonic-gate impl_rem_dev_props(dip); 3680Sstevel@tonic-gate } 3690Sstevel@tonic-gate 3700Sstevel@tonic-gate /* 3710Sstevel@tonic-gate * SECTION: DDI Interrupt 3720Sstevel@tonic-gate */ 3730Sstevel@tonic-gate 3740Sstevel@tonic-gate void 3750Sstevel@tonic-gate cells_1275_copy(prop_1275_cell_t *from, prop_1275_cell_t *to, int32_t len) 3760Sstevel@tonic-gate { 3770Sstevel@tonic-gate int i; 3780Sstevel@tonic-gate for (i = 0; i < len; i++) 3790Sstevel@tonic-gate *to = *from; 3800Sstevel@tonic-gate } 3810Sstevel@tonic-gate 3820Sstevel@tonic-gate prop_1275_cell_t * 3830Sstevel@tonic-gate cells_1275_cmp(prop_1275_cell_t *cell1, prop_1275_cell_t *cell2, int32_t len) 3840Sstevel@tonic-gate { 3850Sstevel@tonic-gate prop_1275_cell_t *match_cell = 0; 3860Sstevel@tonic-gate int32_t i; 3870Sstevel@tonic-gate 3880Sstevel@tonic-gate for (i = 0; i < len; i++) 3890Sstevel@tonic-gate if (cell1[i] != cell2[i]) { 3900Sstevel@tonic-gate match_cell = &cell1[i]; 3910Sstevel@tonic-gate break; 3920Sstevel@tonic-gate } 3930Sstevel@tonic-gate 3940Sstevel@tonic-gate return (match_cell); 3950Sstevel@tonic-gate } 3960Sstevel@tonic-gate 3970Sstevel@tonic-gate /* 398693Sgovinda * get_intr_parent() is a generic routine that process a 1275 interrupt 399693Sgovinda * map (imap) property. This function returns a dev_info_t structure 400693Sgovinda * which claims ownership of the interrupt domain. 401693Sgovinda * It also returns the new interrupt translation within this new domain. 402693Sgovinda * If an interrupt-parent or interrupt-map property are not found, 403693Sgovinda * then we fallback to using the device tree's parent. 404693Sgovinda * 405693Sgovinda * imap entry format: 406693Sgovinda * <reg>,<interrupt>,<phandle>,<translated interrupt> 407693Sgovinda * reg - The register specification in the interrupts domain 408693Sgovinda * interrupt - The interrupt specification 409693Sgovinda * phandle - PROM handle of the device that owns the xlated interrupt domain 410693Sgovinda * translated interrupt - interrupt specifier in the parents domain 411693Sgovinda * note: <reg>,<interrupt> - The reg and interrupt can be combined to create 412693Sgovinda * a unique entry called a unit interrupt specifier. 413693Sgovinda * 414693Sgovinda * Here's the processing steps: 415693Sgovinda * step1 - If the interrupt-parent property exists, create the ispec and 416693Sgovinda * return the dip of the interrupt parent. 417693Sgovinda * step2 - Extract the interrupt-map property and the interrupt-map-mask 418693Sgovinda * If these don't exist, just return the device tree parent. 419693Sgovinda * step3 - build up the unit interrupt specifier to match against the 420693Sgovinda * interrupt map property 421693Sgovinda * step4 - Scan the interrupt-map property until a match is found 422693Sgovinda * step4a - Extract the interrupt parent 423693Sgovinda * step4b - Compare the unit interrupt specifier 4240Sstevel@tonic-gate */ 425693Sgovinda dev_info_t * 426693Sgovinda get_intr_parent(dev_info_t *pdip, dev_info_t *dip, ddi_intr_handle_impl_t *hdlp) 4270Sstevel@tonic-gate { 428693Sgovinda prop_1275_cell_t *imap, *imap_mask, *scan, *reg_p, *match_req; 429693Sgovinda int32_t imap_sz, imap_cells, imap_scan_cells, imap_mask_sz, 430693Sgovinda addr_cells, intr_cells, reg_len, i, j; 431693Sgovinda int32_t match_found = 0; 432693Sgovinda dev_info_t *intr_parent_dip = NULL; 433693Sgovinda uint32_t *intr = &hdlp->ih_vector; 434693Sgovinda uint32_t nodeid; 435693Sgovinda #ifdef DEBUG 436693Sgovinda static int debug = 0; 437693Sgovinda #endif 4380Sstevel@tonic-gate 439693Sgovinda /* 440693Sgovinda * step1 441693Sgovinda * If we have an interrupt-parent property, this property represents 442693Sgovinda * the nodeid of our interrupt parent. 443693Sgovinda */ 444693Sgovinda if ((nodeid = ddi_getprop(DDI_DEV_T_ANY, dip, 0, 445693Sgovinda "interrupt-parent", -1)) != -1) { 446693Sgovinda intr_parent_dip = e_ddi_nodeid_to_dip(nodeid); 447693Sgovinda ASSERT(intr_parent_dip); 4480Sstevel@tonic-gate 4490Sstevel@tonic-gate /* 450693Sgovinda * Attach the interrupt parent. 451693Sgovinda * 452693Sgovinda * N.B. e_ddi_nodeid_to_dip() isn't safe under DR. 453693Sgovinda * Also, interrupt parent isn't held. This needs 454693Sgovinda * to be revisited if DR-capable platforms implement 455693Sgovinda * interrupt redirection. 456693Sgovinda */ 457693Sgovinda if (i_ddi_attach_node_hierarchy(intr_parent_dip) 458693Sgovinda != DDI_SUCCESS) { 459693Sgovinda ndi_rele_devi(intr_parent_dip); 460693Sgovinda return (NULL); 461693Sgovinda } 462693Sgovinda 463693Sgovinda return (intr_parent_dip); 464693Sgovinda } 465693Sgovinda 466693Sgovinda /* 467693Sgovinda * step2 468693Sgovinda * Get interrupt map structure from PROM property 469693Sgovinda */ 470693Sgovinda if (ddi_getlongprop(DDI_DEV_T_ANY, pdip, DDI_PROP_DONTPASS, 471693Sgovinda "interrupt-map", (caddr_t)&imap, &imap_sz) 472693Sgovinda != DDI_PROP_SUCCESS) { 473693Sgovinda /* 474693Sgovinda * If we don't have an imap property, default to using the 475693Sgovinda * device tree. 4760Sstevel@tonic-gate */ 477693Sgovinda 478693Sgovinda ndi_hold_devi(pdip); 479693Sgovinda return (pdip); 480693Sgovinda } 481693Sgovinda 482693Sgovinda /* Get the interrupt mask property */ 483693Sgovinda if (ddi_getlongprop(DDI_DEV_T_ANY, pdip, DDI_PROP_DONTPASS, 484693Sgovinda "interrupt-map-mask", (caddr_t)&imap_mask, &imap_mask_sz) 485693Sgovinda != DDI_PROP_SUCCESS) { 486693Sgovinda /* 487693Sgovinda * If we don't find this property, we have to fail the request 488693Sgovinda * because the 1275 imap property wasn't defined correctly. 489693Sgovinda */ 490693Sgovinda ASSERT(intr_parent_dip == NULL); 491693Sgovinda goto exit2; 492693Sgovinda } 493693Sgovinda 494693Sgovinda /* Get the address cell size */ 495693Sgovinda addr_cells = ddi_getprop(DDI_DEV_T_ANY, pdip, 0, 496693Sgovinda "#address-cells", 2); 497693Sgovinda 498693Sgovinda /* Get the interrupts cell size */ 499693Sgovinda intr_cells = ddi_getprop(DDI_DEV_T_ANY, pdip, 0, 500693Sgovinda "#interrupt-cells", 1); 501693Sgovinda 502693Sgovinda /* 503693Sgovinda * step3 504693Sgovinda * Now lets build up the unit interrupt specifier e.g. reg,intr 505693Sgovinda * and apply the imap mask. match_req will hold this when we're 506693Sgovinda * through. 507693Sgovinda */ 508693Sgovinda if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg", 509693Sgovinda (caddr_t)®_p, ®_len) != DDI_SUCCESS) { 510693Sgovinda ASSERT(intr_parent_dip == NULL); 511693Sgovinda goto exit3; 5120Sstevel@tonic-gate } 5130Sstevel@tonic-gate 514693Sgovinda match_req = kmem_alloc(CELLS_1275_TO_BYTES(addr_cells) + 515693Sgovinda CELLS_1275_TO_BYTES(intr_cells), KM_SLEEP); 516693Sgovinda 517693Sgovinda for (i = 0; i < addr_cells; i++) 518693Sgovinda match_req[i] = (reg_p[i] & imap_mask[i]); 519693Sgovinda 520693Sgovinda for (j = 0; j < intr_cells; i++, j++) 521693Sgovinda match_req[i] = (intr[j] & imap_mask[i]); 522693Sgovinda 523693Sgovinda /* Calculate the imap size in cells */ 524693Sgovinda imap_cells = BYTES_TO_1275_CELLS(imap_sz); 525693Sgovinda 526693Sgovinda #ifdef DEBUG 527693Sgovinda if (debug) 528693Sgovinda prom_printf("reg cell size 0x%x, intr cell size 0x%x, " 529693Sgovinda "match_request 0x%p, imap 0x%p\n", addr_cells, intr_cells, 5307240Srh87107 (void *)match_req, (void *)imap); 531693Sgovinda #endif 532693Sgovinda 533693Sgovinda /* 534693Sgovinda * Scan the imap property looking for a match of the interrupt unit 535693Sgovinda * specifier. This loop is rather complex since the data within the 536693Sgovinda * imap property may vary in size. 537693Sgovinda */ 538693Sgovinda for (scan = imap, imap_scan_cells = i = 0; 539693Sgovinda imap_scan_cells < imap_cells; scan += i, imap_scan_cells += i) { 540693Sgovinda int new_intr_cells; 541693Sgovinda 542693Sgovinda /* Set the index to the nodeid field */ 543693Sgovinda i = addr_cells + intr_cells; 544693Sgovinda 545693Sgovinda /* 546693Sgovinda * step4a 547693Sgovinda * Translate the nodeid field to a dip 548693Sgovinda */ 549693Sgovinda ASSERT(intr_parent_dip == NULL); 550693Sgovinda intr_parent_dip = e_ddi_nodeid_to_dip((uint_t)scan[i++]); 551693Sgovinda 552693Sgovinda ASSERT(intr_parent_dip != 0); 553693Sgovinda #ifdef DEBUG 554693Sgovinda if (debug) 5557240Srh87107 prom_printf("scan 0x%p\n", (void *)scan); 556693Sgovinda #endif 557693Sgovinda /* 558693Sgovinda * The tmp_dip describes the new domain, get it's interrupt 559693Sgovinda * cell size 560693Sgovinda */ 561693Sgovinda new_intr_cells = ddi_getprop(DDI_DEV_T_ANY, intr_parent_dip, 0, 562693Sgovinda "#interrupts-cells", 1); 5630Sstevel@tonic-gate 564693Sgovinda /* 565693Sgovinda * step4b 566693Sgovinda * See if we have a match on the interrupt unit specifier 567693Sgovinda */ 568693Sgovinda if (cells_1275_cmp(match_req, scan, addr_cells + intr_cells) 569693Sgovinda == 0) { 570693Sgovinda uint32_t *intr; 571693Sgovinda 572693Sgovinda match_found = 1; 573693Sgovinda 574693Sgovinda /* 575693Sgovinda * If we have an imap parent whose not in our device 576693Sgovinda * tree path, we need to hold and install that driver. 577693Sgovinda */ 578693Sgovinda if (i_ddi_attach_node_hierarchy(intr_parent_dip) 579693Sgovinda != DDI_SUCCESS) { 580693Sgovinda ndi_rele_devi(intr_parent_dip); 581693Sgovinda intr_parent_dip = (dev_info_t *)NULL; 582693Sgovinda goto exit4; 583693Sgovinda } 5840Sstevel@tonic-gate 585693Sgovinda /* 586693Sgovinda * We need to handcraft an ispec along with a bus 587693Sgovinda * interrupt value, so we can dup it into our 588693Sgovinda * standard ispec structure. 589693Sgovinda */ 590693Sgovinda /* Extract the translated interrupt information */ 591693Sgovinda intr = kmem_alloc( 592693Sgovinda CELLS_1275_TO_BYTES(new_intr_cells), KM_SLEEP); 593693Sgovinda 594693Sgovinda for (j = 0; j < new_intr_cells; j++, i++) 595693Sgovinda intr[j] = scan[i]; 596693Sgovinda 597693Sgovinda cells_1275_copy(intr, &hdlp->ih_vector, new_intr_cells); 5980Sstevel@tonic-gate 599693Sgovinda kmem_free(intr, CELLS_1275_TO_BYTES(new_intr_cells)); 600693Sgovinda 601693Sgovinda #ifdef DEBUG 602693Sgovinda if (debug) 6037240Srh87107 prom_printf("dip 0x%p\n", 6047240Srh87107 (void *)intr_parent_dip); 605693Sgovinda #endif 606693Sgovinda break; 607693Sgovinda } else { 608693Sgovinda #ifdef DEBUG 609693Sgovinda if (debug) 6107240Srh87107 prom_printf("dip 0x%p\n", 6117240Srh87107 (void *)intr_parent_dip); 612693Sgovinda #endif 613693Sgovinda ndi_rele_devi(intr_parent_dip); 614693Sgovinda intr_parent_dip = NULL; 615693Sgovinda i += new_intr_cells; 6160Sstevel@tonic-gate } 6170Sstevel@tonic-gate } 6180Sstevel@tonic-gate 619693Sgovinda /* 620693Sgovinda * If we haven't found our interrupt parent at this point, fallback 621693Sgovinda * to using the device tree. 622693Sgovinda */ 623693Sgovinda if (!match_found) { 624693Sgovinda ndi_hold_devi(pdip); 625693Sgovinda ASSERT(intr_parent_dip == NULL); 626693Sgovinda intr_parent_dip = pdip; 627693Sgovinda } 628693Sgovinda 629693Sgovinda ASSERT(intr_parent_dip != NULL); 630693Sgovinda 631693Sgovinda exit4: 632693Sgovinda kmem_free(reg_p, reg_len); 633693Sgovinda kmem_free(match_req, CELLS_1275_TO_BYTES(addr_cells) + 634693Sgovinda CELLS_1275_TO_BYTES(intr_cells)); 635693Sgovinda 636693Sgovinda exit3: 637693Sgovinda kmem_free(imap_mask, imap_mask_sz); 638693Sgovinda 639693Sgovinda exit2: 640693Sgovinda kmem_free(imap, imap_sz); 641693Sgovinda 642693Sgovinda return (intr_parent_dip); 6430Sstevel@tonic-gate } 6440Sstevel@tonic-gate 6450Sstevel@tonic-gate /* 6460Sstevel@tonic-gate * process_intr_ops: 6470Sstevel@tonic-gate * 6480Sstevel@tonic-gate * Process the interrupt op via the interrupt parent. 6490Sstevel@tonic-gate */ 6500Sstevel@tonic-gate int 6510Sstevel@tonic-gate process_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t op, 6520Sstevel@tonic-gate ddi_intr_handle_impl_t *hdlp, void *result) 6530Sstevel@tonic-gate { 6540Sstevel@tonic-gate int ret = DDI_FAILURE; 6550Sstevel@tonic-gate 6560Sstevel@tonic-gate if (NEXUS_HAS_INTR_OP(pdip)) { 6570Sstevel@tonic-gate ret = (*(DEVI(pdip)->devi_ops->devo_bus_ops-> 6580Sstevel@tonic-gate bus_intr_op)) (pdip, rdip, op, hdlp, result); 6590Sstevel@tonic-gate } else { 6600Sstevel@tonic-gate cmn_err(CE_WARN, "Failed to process interrupt " 6610Sstevel@tonic-gate "for %s%d due to down-rev nexus driver %s%d", 6620Sstevel@tonic-gate ddi_get_name(rdip), ddi_get_instance(rdip), 6630Sstevel@tonic-gate ddi_get_name(pdip), ddi_get_instance(pdip)); 6640Sstevel@tonic-gate } 6650Sstevel@tonic-gate 6660Sstevel@tonic-gate return (ret); 6670Sstevel@tonic-gate } 6680Sstevel@tonic-gate 669693Sgovinda /*ARGSUSED*/ 670693Sgovinda uint_t 671693Sgovinda softlevel1(caddr_t arg) 672693Sgovinda { 673693Sgovinda softint(); 674693Sgovinda return (1); 675693Sgovinda } 676693Sgovinda 677693Sgovinda /* 678693Sgovinda * indirection table, to save us some large switch statements 679693Sgovinda * NOTE: This must agree with "INTLEVEL_foo" constants in 680693Sgovinda * <sys/avintr.h> 681693Sgovinda */ 682693Sgovinda struct autovec *const vectorlist[] = { 0 }; 683693Sgovinda 684693Sgovinda /* 685693Sgovinda * This value is exported here for the functions in avintr.c 686693Sgovinda */ 687693Sgovinda const uint_t maxautovec = (sizeof (vectorlist) / sizeof (vectorlist[0])); 688693Sgovinda 689693Sgovinda /* 690693Sgovinda * Check for machine specific interrupt levels which cannot be reassigned by 691693Sgovinda * settrap(), sun4u version. 692693Sgovinda * 693693Sgovinda * sun4u does not support V8 SPARC "fast trap" handlers. 694693Sgovinda */ 695693Sgovinda /*ARGSUSED*/ 696693Sgovinda int 697693Sgovinda exclude_settrap(int lvl) 698693Sgovinda { 699693Sgovinda return (1); 700693Sgovinda } 701693Sgovinda 702693Sgovinda /* 703693Sgovinda * Check for machine specific interrupt levels which cannot have interrupt 704693Sgovinda * handlers added. We allow levels 1 through 15; level 0 is nonsense. 705693Sgovinda */ 706693Sgovinda /*ARGSUSED*/ 707693Sgovinda int 708693Sgovinda exclude_level(int lvl) 709693Sgovinda { 710693Sgovinda return ((lvl < 1) || (lvl > 15)); 711693Sgovinda } 712693Sgovinda 713693Sgovinda /* 714693Sgovinda * Wrapper functions used by New DDI interrupt framework. 715693Sgovinda */ 716693Sgovinda 717693Sgovinda /* 718693Sgovinda * i_ddi_intr_ops: 719693Sgovinda */ 720693Sgovinda int 721693Sgovinda i_ddi_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t op, 722693Sgovinda ddi_intr_handle_impl_t *hdlp, void *result) 723693Sgovinda { 724693Sgovinda dev_info_t *pdip = ddi_get_parent(dip); 725693Sgovinda int ret = DDI_FAILURE; 726693Sgovinda 727693Sgovinda /* 728693Sgovinda * The following check is required to address 729693Sgovinda * one of the test case of ADDI test suite. 730693Sgovinda */ 731693Sgovinda if (pdip == NULL) 732693Sgovinda return (DDI_FAILURE); 733693Sgovinda 734693Sgovinda if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 735693Sgovinda return (process_intr_ops(pdip, rdip, op, hdlp, result)); 736693Sgovinda 737693Sgovinda if (hdlp->ih_vector == 0) 738693Sgovinda hdlp->ih_vector = i_ddi_get_inum(rdip, hdlp->ih_inum); 739693Sgovinda 740693Sgovinda if (hdlp->ih_pri == 0) 741693Sgovinda hdlp->ih_pri = i_ddi_get_intr_pri(rdip, hdlp->ih_inum); 742693Sgovinda 743693Sgovinda switch (op) { 744693Sgovinda case DDI_INTROP_ADDISR: 745693Sgovinda case DDI_INTROP_REMISR: 74610053SEvan.Yan@Sun.COM case DDI_INTROP_GETTARGET: 74710053SEvan.Yan@Sun.COM case DDI_INTROP_SETTARGET: 748693Sgovinda case DDI_INTROP_ENABLE: 749693Sgovinda case DDI_INTROP_DISABLE: 750693Sgovinda case DDI_INTROP_BLOCKENABLE: 751693Sgovinda case DDI_INTROP_BLOCKDISABLE: 752693Sgovinda /* 753693Sgovinda * Try and determine our parent and possibly an interrupt 754693Sgovinda * translation. intr parent dip returned held 755693Sgovinda */ 756693Sgovinda if ((pdip = get_intr_parent(pdip, dip, hdlp)) == NULL) 757693Sgovinda goto done; 758693Sgovinda } 759693Sgovinda 760693Sgovinda ret = process_intr_ops(pdip, rdip, op, hdlp, result); 761693Sgovinda 762693Sgovinda done: 763693Sgovinda switch (op) { 764693Sgovinda case DDI_INTROP_ADDISR: 765693Sgovinda case DDI_INTROP_REMISR: 766693Sgovinda case DDI_INTROP_ENABLE: 767693Sgovinda case DDI_INTROP_DISABLE: 768693Sgovinda case DDI_INTROP_BLOCKENABLE: 769693Sgovinda case DDI_INTROP_BLOCKDISABLE: 770693Sgovinda /* Release hold acquired in get_intr_parent() */ 771693Sgovinda if (pdip) 772693Sgovinda ndi_rele_devi(pdip); 773693Sgovinda } 774693Sgovinda 775693Sgovinda hdlp->ih_vector = 0; 776693Sgovinda 777693Sgovinda return (ret); 778693Sgovinda } 779693Sgovinda 7800Sstevel@tonic-gate /* 7810Sstevel@tonic-gate * i_ddi_add_ivintr: 7820Sstevel@tonic-gate */ 7830Sstevel@tonic-gate /*ARGSUSED*/ 7840Sstevel@tonic-gate int 7850Sstevel@tonic-gate i_ddi_add_ivintr(ddi_intr_handle_impl_t *hdlp) 7860Sstevel@tonic-gate { 7870Sstevel@tonic-gate /* 7880Sstevel@tonic-gate * If the PIL was set and is valid use it, otherwise 7890Sstevel@tonic-gate * default it to 1 7900Sstevel@tonic-gate */ 7910Sstevel@tonic-gate if ((hdlp->ih_pri < 1) || (hdlp->ih_pri > PIL_MAX)) 7920Sstevel@tonic-gate hdlp->ih_pri = 1; 7930Sstevel@tonic-gate 7940Sstevel@tonic-gate VERIFY(add_ivintr(hdlp->ih_vector, hdlp->ih_pri, 7952973Sgovinda (intrfunc)hdlp->ih_cb_func, hdlp->ih_cb_arg1, 7962973Sgovinda hdlp->ih_cb_arg2, NULL) == 0); 7970Sstevel@tonic-gate 7980Sstevel@tonic-gate return (DDI_SUCCESS); 7990Sstevel@tonic-gate } 8000Sstevel@tonic-gate 8010Sstevel@tonic-gate /* 8020Sstevel@tonic-gate * i_ddi_rem_ivintr: 8030Sstevel@tonic-gate */ 8040Sstevel@tonic-gate /*ARGSUSED*/ 8050Sstevel@tonic-gate void 8060Sstevel@tonic-gate i_ddi_rem_ivintr(ddi_intr_handle_impl_t *hdlp) 8070Sstevel@tonic-gate { 8082973Sgovinda VERIFY(rem_ivintr(hdlp->ih_vector, hdlp->ih_pri) == 0); 8090Sstevel@tonic-gate } 8100Sstevel@tonic-gate 8110Sstevel@tonic-gate /* 812693Sgovinda * i_ddi_get_inum - Get the interrupt number property from the 813693Sgovinda * specified device. Note that this function is called only for 814693Sgovinda * the FIXED interrupt type. 815693Sgovinda */ 816693Sgovinda uint32_t 817693Sgovinda i_ddi_get_inum(dev_info_t *dip, uint_t inumber) 818693Sgovinda { 819693Sgovinda int32_t intrlen, intr_cells, max_intrs; 820693Sgovinda prop_1275_cell_t *ip, intr_sz; 821693Sgovinda uint32_t intr = 0; 822693Sgovinda 823693Sgovinda if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS | 824693Sgovinda DDI_PROP_CANSLEEP, 825693Sgovinda "interrupts", (caddr_t)&ip, &intrlen) == DDI_SUCCESS) { 826693Sgovinda 827693Sgovinda intr_cells = ddi_getprop(DDI_DEV_T_ANY, dip, 0, 828693Sgovinda "#interrupt-cells", 1); 829693Sgovinda 830693Sgovinda /* adjust for number of bytes */ 831693Sgovinda intr_sz = CELLS_1275_TO_BYTES(intr_cells); 832693Sgovinda 833693Sgovinda /* Calculate the number of interrupts */ 834693Sgovinda max_intrs = intrlen / intr_sz; 835693Sgovinda 836693Sgovinda if (inumber < max_intrs) { 837693Sgovinda prop_1275_cell_t *intrp = ip; 838693Sgovinda 839693Sgovinda /* Index into interrupt property */ 840693Sgovinda intrp += (inumber * intr_cells); 841693Sgovinda 842693Sgovinda cells_1275_copy(intrp, &intr, intr_cells); 843693Sgovinda } 844693Sgovinda 845693Sgovinda kmem_free(ip, intrlen); 846693Sgovinda } 847693Sgovinda 848693Sgovinda return (intr); 849693Sgovinda } 850693Sgovinda 851693Sgovinda /* 852693Sgovinda * i_ddi_get_intr_pri - Get the interrupt-priorities property from 853693Sgovinda * the specified device. Note that this function is called only for 854693Sgovinda * the FIXED interrupt type. 855693Sgovinda */ 856693Sgovinda uint32_t 857693Sgovinda i_ddi_get_intr_pri(dev_info_t *dip, uint_t inumber) 858693Sgovinda { 859693Sgovinda uint32_t *intr_prio_p; 860693Sgovinda uint32_t pri = 0; 861693Sgovinda int32_t i; 862693Sgovinda 863693Sgovinda /* 864693Sgovinda * Use the "interrupt-priorities" property to determine the 865693Sgovinda * the pil/ipl for the interrupt handler. 866693Sgovinda */ 867693Sgovinda if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 868693Sgovinda "interrupt-priorities", (caddr_t)&intr_prio_p, 869693Sgovinda &i) == DDI_SUCCESS) { 870693Sgovinda if (inumber < (i / sizeof (int32_t))) 871693Sgovinda pri = intr_prio_p[inumber]; 872693Sgovinda kmem_free(intr_prio_p, i); 873693Sgovinda } 874693Sgovinda 875693Sgovinda return (pri); 876693Sgovinda } 877693Sgovinda 878693Sgovinda int 8792580Sanish i_ddi_get_intx_nintrs(dev_info_t *dip) 880693Sgovinda { 881693Sgovinda int32_t intrlen; 882693Sgovinda prop_1275_cell_t intr_sz; 883693Sgovinda prop_1275_cell_t *ip; 884693Sgovinda int32_t ret = 0; 885693Sgovinda 886693Sgovinda if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS | 887693Sgovinda DDI_PROP_CANSLEEP, 888693Sgovinda "interrupts", (caddr_t)&ip, &intrlen) == DDI_SUCCESS) { 889693Sgovinda 890693Sgovinda intr_sz = ddi_getprop(DDI_DEV_T_ANY, dip, 0, 891693Sgovinda "#interrupt-cells", 1); 892693Sgovinda /* adjust for number of bytes */ 893693Sgovinda intr_sz = CELLS_1275_TO_BYTES(intr_sz); 894693Sgovinda 895693Sgovinda ret = intrlen / intr_sz; 896693Sgovinda 897693Sgovinda kmem_free(ip, intrlen); 898693Sgovinda } 899693Sgovinda 900693Sgovinda return (ret); 901693Sgovinda } 902693Sgovinda 903693Sgovinda /* 9042973Sgovinda * i_ddi_add_softint - allocate and add a software interrupt. 9052973Sgovinda * 9062973Sgovinda * NOTE: All software interrupts that are registered through DDI 9072973Sgovinda * should be triggered only on a single target or CPU. 9080Sstevel@tonic-gate */ 9090Sstevel@tonic-gate int 9100Sstevel@tonic-gate i_ddi_add_softint(ddi_softint_hdl_impl_t *hdlp) 9110Sstevel@tonic-gate { 9122973Sgovinda if ((hdlp->ih_private = (void *)add_softintr(hdlp->ih_pri, 9132973Sgovinda hdlp->ih_cb_func, hdlp->ih_cb_arg1, SOFTINT_ST)) == NULL) 9140Sstevel@tonic-gate return (DDI_FAILURE); 9150Sstevel@tonic-gate 9160Sstevel@tonic-gate return (DDI_SUCCESS); 9170Sstevel@tonic-gate } 9180Sstevel@tonic-gate 9192973Sgovinda /* 9202973Sgovinda * i_ddi_remove_softint - remove and free a software interrupt. 9212973Sgovinda */ 9220Sstevel@tonic-gate void 9230Sstevel@tonic-gate i_ddi_remove_softint(ddi_softint_hdl_impl_t *hdlp) 9240Sstevel@tonic-gate { 9250Sstevel@tonic-gate ASSERT(hdlp->ih_private != NULL); 926693Sgovinda 9272973Sgovinda if (rem_softintr((uint64_t)hdlp->ih_private) == 0) 9282973Sgovinda hdlp->ih_private = NULL; 9290Sstevel@tonic-gate } 9300Sstevel@tonic-gate 9312973Sgovinda /* 9322973Sgovinda * i_ddi_trigger_softint - trigger a software interrupt. 9332973Sgovinda */ 9340Sstevel@tonic-gate int 935278Sgovinda i_ddi_trigger_softint(ddi_softint_hdl_impl_t *hdlp, void *arg2) 9360Sstevel@tonic-gate { 9372973Sgovinda int ret; 9382973Sgovinda 9390Sstevel@tonic-gate ASSERT(hdlp->ih_private != NULL); 9400Sstevel@tonic-gate 9412973Sgovinda /* Update the second argument for the software interrupt */ 9422973Sgovinda if ((ret = update_softint_arg2((uint64_t)hdlp->ih_private, arg2)) == 0) 9432973Sgovinda setsoftint((uint64_t)hdlp->ih_private); 9442973Sgovinda 9452973Sgovinda return (ret ? DDI_EPENDING : DDI_SUCCESS); 9460Sstevel@tonic-gate } 9470Sstevel@tonic-gate 9482973Sgovinda /* 9492973Sgovinda * i_ddi_set_softint_pri - change software interrupt priority. 9502973Sgovinda */ 9510Sstevel@tonic-gate /* ARGSUSED */ 9520Sstevel@tonic-gate int 9530Sstevel@tonic-gate i_ddi_set_softint_pri(ddi_softint_hdl_impl_t *hdlp, uint_t old_pri) 9540Sstevel@tonic-gate { 9552973Sgovinda int ret; 9562973Sgovinda 9570Sstevel@tonic-gate ASSERT(hdlp->ih_private != NULL); 9580Sstevel@tonic-gate 9592973Sgovinda /* Update the interrupt priority for the software interrupt */ 9602973Sgovinda ret = update_softint_pri((uint64_t)hdlp->ih_private, hdlp->ih_pri); 9612973Sgovinda 9622973Sgovinda return (ret ? DDI_FAILURE : DDI_SUCCESS); 9630Sstevel@tonic-gate } 9640Sstevel@tonic-gate 965916Sschwartz /*ARGSUSED*/ 966916Sschwartz void 967916Sschwartz i_ddi_alloc_intr_phdl(ddi_intr_handle_impl_t *hdlp) 968916Sschwartz { 969916Sschwartz } 970916Sschwartz 971916Sschwartz /*ARGSUSED*/ 972916Sschwartz void 973916Sschwartz i_ddi_free_intr_phdl(ddi_intr_handle_impl_t *hdlp) 974916Sschwartz { 975916Sschwartz } 976916Sschwartz 9770Sstevel@tonic-gate /* 9780Sstevel@tonic-gate * SECTION: DDI Memory/DMA 9790Sstevel@tonic-gate */ 9800Sstevel@tonic-gate 9811900Seota /* set HAT endianess attributes from ddi_device_acc_attr */ 9821900Seota void 9831900Seota i_ddi_devacc_to_hatacc(ddi_device_acc_attr_t *devaccp, uint_t *hataccp) 9841900Seota { 9851900Seota if (devaccp != NULL) { 9861900Seota if (devaccp->devacc_attr_endian_flags == DDI_STRUCTURE_LE_ACC) { 9871900Seota *hataccp &= ~HAT_ENDIAN_MASK; 9881900Seota *hataccp |= HAT_STRUCTURE_LE; 9891900Seota } 9901900Seota } 9911900Seota } 9921900Seota 9931900Seota /* 9941900Seota * Check if the specified cache attribute is supported on the platform. 9951900Seota * This function must be called before i_ddi_cacheattr_to_hatacc(). 9961900Seota */ 9971900Seota boolean_t 9981900Seota i_ddi_check_cache_attr(uint_t flags) 9991900Seota { 10001900Seota /* 10011900Seota * The cache attributes are mutually exclusive. Any combination of 10021900Seota * the attributes leads to a failure. 10031900Seota */ 10041900Seota uint_t cache_attr = IOMEM_CACHE_ATTR(flags); 10051900Seota if ((cache_attr != 0) && ((cache_attr & (cache_attr - 1)) != 0)) 10061900Seota return (B_FALSE); 10071900Seota 10081900Seota /* 10091900Seota * On the sparc architecture, only IOMEM_DATA_CACHED is meaningful, 10101900Seota * but others lead to a failure. 10111900Seota */ 10121900Seota if (cache_attr & IOMEM_DATA_CACHED) 10131900Seota return (B_TRUE); 10141900Seota else 10151900Seota return (B_FALSE); 10161900Seota } 10171900Seota 10181900Seota /* set HAT cache attributes from the cache attributes */ 10191900Seota void 10201900Seota i_ddi_cacheattr_to_hatacc(uint_t flags, uint_t *hataccp) 10211900Seota { 10221900Seota uint_t cache_attr = IOMEM_CACHE_ATTR(flags); 10231900Seota static char *fname = "i_ddi_cacheattr_to_hatacc"; 10241900Seota #if defined(lint) 10251900Seota *hataccp = *hataccp; 10261900Seota #endif 10271900Seota /* 10281900Seota * set HAT attrs according to the cache attrs. 10291900Seota */ 10301900Seota switch (cache_attr) { 10311900Seota /* 10321900Seota * The cache coherency is always maintained on SPARC, and 10331900Seota * nothing is required. 10341900Seota */ 10351900Seota case IOMEM_DATA_CACHED: 10361900Seota break; 10371900Seota /* 10381900Seota * Both IOMEM_DATA_UC_WRITE_COMBINED and IOMEM_DATA_UNCACHED are 10391900Seota * not supported on SPARC -- this case must not occur because the 10401900Seota * cache attribute is scrutinized before this function is called. 10411900Seota */ 10421900Seota case IOMEM_DATA_UNCACHED: 10431900Seota case IOMEM_DATA_UC_WR_COMBINE: 10441900Seota default: 10451900Seota cmn_err(CE_WARN, "%s: cache_attr=0x%x is ignored.", 10461900Seota fname, cache_attr); 10471900Seota } 10481900Seota } 10491900Seota 10500Sstevel@tonic-gate static vmem_t *little_endian_arena; 10510Sstevel@tonic-gate static vmem_t *big_endian_arena; 10520Sstevel@tonic-gate 10530Sstevel@tonic-gate static void * 10540Sstevel@tonic-gate segkmem_alloc_le(vmem_t *vmp, size_t size, int flag) 10550Sstevel@tonic-gate { 10560Sstevel@tonic-gate return (segkmem_xalloc(vmp, NULL, size, flag, HAT_STRUCTURE_LE, 10570Sstevel@tonic-gate segkmem_page_create, NULL)); 10580Sstevel@tonic-gate } 10590Sstevel@tonic-gate 10600Sstevel@tonic-gate static void * 10610Sstevel@tonic-gate segkmem_alloc_be(vmem_t *vmp, size_t size, int flag) 10620Sstevel@tonic-gate { 10630Sstevel@tonic-gate return (segkmem_xalloc(vmp, NULL, size, flag, HAT_STRUCTURE_BE, 10640Sstevel@tonic-gate segkmem_page_create, NULL)); 10650Sstevel@tonic-gate } 10660Sstevel@tonic-gate 10670Sstevel@tonic-gate void 10680Sstevel@tonic-gate ka_init(void) 10690Sstevel@tonic-gate { 10700Sstevel@tonic-gate little_endian_arena = vmem_create("little_endian", NULL, 0, 1, 10710Sstevel@tonic-gate segkmem_alloc_le, segkmem_free, heap_arena, 0, VM_SLEEP); 10720Sstevel@tonic-gate big_endian_arena = vmem_create("big_endian", NULL, 0, 1, 10730Sstevel@tonic-gate segkmem_alloc_be, segkmem_free, heap_arena, 0, VM_SLEEP); 10740Sstevel@tonic-gate } 10750Sstevel@tonic-gate 10760Sstevel@tonic-gate /* 10770Sstevel@tonic-gate * Allocate from the system, aligned on a specific boundary. 10780Sstevel@tonic-gate * The alignment, if non-zero, must be a power of 2. 10790Sstevel@tonic-gate */ 10800Sstevel@tonic-gate static void * 10810Sstevel@tonic-gate kalloca(size_t size, size_t align, int cansleep, uint_t endian_flags) 10820Sstevel@tonic-gate { 10830Sstevel@tonic-gate size_t *addr, *raddr, rsize; 10840Sstevel@tonic-gate size_t hdrsize = 4 * sizeof (size_t); /* must be power of 2 */ 10850Sstevel@tonic-gate 10860Sstevel@tonic-gate align = MAX(align, hdrsize); 10870Sstevel@tonic-gate ASSERT((align & (align - 1)) == 0); 10880Sstevel@tonic-gate 10890Sstevel@tonic-gate /* 10900Sstevel@tonic-gate * We need to allocate 10910Sstevel@tonic-gate * rsize = size + hdrsize + align - MIN(hdrsize, buffer_alignment) 10920Sstevel@tonic-gate * bytes to be sure we have enough freedom to satisfy the request. 10930Sstevel@tonic-gate * Since the buffer alignment depends on the request size, this is 10940Sstevel@tonic-gate * not straightforward to use directly. 10950Sstevel@tonic-gate * 10960Sstevel@tonic-gate * kmem guarantees that any allocation of a 64-byte multiple will be 10970Sstevel@tonic-gate * 64-byte aligned. Since rounding up the request could add more 10980Sstevel@tonic-gate * than we save, we compute the size with and without alignment, and 10990Sstevel@tonic-gate * use the smaller of the two. 11000Sstevel@tonic-gate */ 11010Sstevel@tonic-gate rsize = size + hdrsize + align; 11020Sstevel@tonic-gate 11030Sstevel@tonic-gate if (endian_flags == DDI_STRUCTURE_LE_ACC) { 11040Sstevel@tonic-gate raddr = vmem_alloc(little_endian_arena, rsize, 11050Sstevel@tonic-gate cansleep ? VM_SLEEP : VM_NOSLEEP); 11060Sstevel@tonic-gate } else { 11070Sstevel@tonic-gate raddr = vmem_alloc(big_endian_arena, rsize, 11080Sstevel@tonic-gate cansleep ? VM_SLEEP : VM_NOSLEEP); 11090Sstevel@tonic-gate } 11100Sstevel@tonic-gate 11110Sstevel@tonic-gate if (raddr == NULL) 11120Sstevel@tonic-gate return (NULL); 11130Sstevel@tonic-gate 11140Sstevel@tonic-gate addr = (size_t *)P2ROUNDUP((uintptr_t)raddr + hdrsize, align); 11150Sstevel@tonic-gate ASSERT((uintptr_t)addr + size - (uintptr_t)raddr <= rsize); 11160Sstevel@tonic-gate 11170Sstevel@tonic-gate addr[-3] = (size_t)endian_flags; 11180Sstevel@tonic-gate addr[-2] = (size_t)raddr; 11190Sstevel@tonic-gate addr[-1] = rsize; 11200Sstevel@tonic-gate 11210Sstevel@tonic-gate return (addr); 11220Sstevel@tonic-gate } 11230Sstevel@tonic-gate 11240Sstevel@tonic-gate static void 11250Sstevel@tonic-gate kfreea(void *addr) 11260Sstevel@tonic-gate { 11270Sstevel@tonic-gate size_t *saddr = addr; 11280Sstevel@tonic-gate 11290Sstevel@tonic-gate if (saddr[-3] == DDI_STRUCTURE_LE_ACC) 11300Sstevel@tonic-gate vmem_free(little_endian_arena, (void *)saddr[-2], saddr[-1]); 11310Sstevel@tonic-gate else 11320Sstevel@tonic-gate vmem_free(big_endian_arena, (void *)saddr[-2], saddr[-1]); 11330Sstevel@tonic-gate } 11340Sstevel@tonic-gate 11350Sstevel@tonic-gate int 11360Sstevel@tonic-gate i_ddi_mem_alloc(dev_info_t *dip, ddi_dma_attr_t *attr, 11371900Seota size_t length, int cansleep, int flags, 11380Sstevel@tonic-gate ddi_device_acc_attr_t *accattrp, 11390Sstevel@tonic-gate caddr_t *kaddrp, size_t *real_length, ddi_acc_hdl_t *handlep) 11400Sstevel@tonic-gate { 11410Sstevel@tonic-gate caddr_t a; 11421900Seota int iomin, align, streaming; 11430Sstevel@tonic-gate uint_t endian_flags = DDI_NEVERSWAP_ACC; 11440Sstevel@tonic-gate 11450Sstevel@tonic-gate #if defined(lint) 11460Sstevel@tonic-gate *handlep = *handlep; 11470Sstevel@tonic-gate #endif 11480Sstevel@tonic-gate 11490Sstevel@tonic-gate /* 11500Sstevel@tonic-gate * Check legality of arguments 11510Sstevel@tonic-gate */ 11520Sstevel@tonic-gate if (length == 0 || kaddrp == NULL || attr == NULL) { 11530Sstevel@tonic-gate return (DDI_FAILURE); 11540Sstevel@tonic-gate } 11551900Seota 11560Sstevel@tonic-gate if (attr->dma_attr_minxfer == 0 || attr->dma_attr_align == 0 || 11570Sstevel@tonic-gate (attr->dma_attr_align & (attr->dma_attr_align - 1)) || 11580Sstevel@tonic-gate (attr->dma_attr_minxfer & (attr->dma_attr_minxfer - 1))) { 11590Sstevel@tonic-gate return (DDI_FAILURE); 11600Sstevel@tonic-gate } 11610Sstevel@tonic-gate 11620Sstevel@tonic-gate /* 11631900Seota * check if a streaming sequential xfer is requested. 11641900Seota */ 11651900Seota streaming = (flags & DDI_DMA_STREAMING) ? 1 : 0; 11661900Seota 11671900Seota /* 11680Sstevel@tonic-gate * Drivers for 64-bit capable SBus devices will encode 11690Sstevel@tonic-gate * the burtsizes for 64-bit xfers in the upper 16-bits. 11700Sstevel@tonic-gate * For DMA alignment, we use the most restrictive 11710Sstevel@tonic-gate * alignment of 32-bit and 64-bit xfers. 11720Sstevel@tonic-gate */ 11730Sstevel@tonic-gate iomin = (attr->dma_attr_burstsizes & 0xffff) | 11740Sstevel@tonic-gate ((attr->dma_attr_burstsizes >> 16) & 0xffff); 11750Sstevel@tonic-gate /* 11760Sstevel@tonic-gate * If a driver set burtsizes to 0, we give him byte alignment. 11770Sstevel@tonic-gate * Otherwise align at the burtsizes boundary. 11780Sstevel@tonic-gate */ 11790Sstevel@tonic-gate if (iomin == 0) 11800Sstevel@tonic-gate iomin = 1; 11810Sstevel@tonic-gate else 11820Sstevel@tonic-gate iomin = 1 << (ddi_fls(iomin) - 1); 11830Sstevel@tonic-gate iomin = maxbit(iomin, attr->dma_attr_minxfer); 11840Sstevel@tonic-gate iomin = maxbit(iomin, attr->dma_attr_align); 11850Sstevel@tonic-gate iomin = ddi_iomin(dip, iomin, streaming); 11860Sstevel@tonic-gate if (iomin == 0) 11870Sstevel@tonic-gate return (DDI_FAILURE); 11880Sstevel@tonic-gate 11890Sstevel@tonic-gate ASSERT((iomin & (iomin - 1)) == 0); 11900Sstevel@tonic-gate ASSERT(iomin >= attr->dma_attr_minxfer); 11910Sstevel@tonic-gate ASSERT(iomin >= attr->dma_attr_align); 11920Sstevel@tonic-gate 11930Sstevel@tonic-gate length = P2ROUNDUP(length, iomin); 11940Sstevel@tonic-gate align = iomin; 11950Sstevel@tonic-gate 11960Sstevel@tonic-gate if (accattrp != NULL) 11970Sstevel@tonic-gate endian_flags = accattrp->devacc_attr_endian_flags; 11980Sstevel@tonic-gate 11990Sstevel@tonic-gate a = kalloca(length, align, cansleep, endian_flags); 12000Sstevel@tonic-gate if ((*kaddrp = a) == 0) { 12010Sstevel@tonic-gate return (DDI_FAILURE); 12020Sstevel@tonic-gate } else { 12030Sstevel@tonic-gate if (real_length) { 12040Sstevel@tonic-gate *real_length = length; 12050Sstevel@tonic-gate } 12060Sstevel@tonic-gate if (handlep) { 12070Sstevel@tonic-gate /* 12080Sstevel@tonic-gate * assign handle information 12090Sstevel@tonic-gate */ 12100Sstevel@tonic-gate impl_acc_hdl_init(handlep); 12110Sstevel@tonic-gate } 12120Sstevel@tonic-gate return (DDI_SUCCESS); 12130Sstevel@tonic-gate } 12140Sstevel@tonic-gate } 12150Sstevel@tonic-gate 12160Sstevel@tonic-gate /* 12170Sstevel@tonic-gate * covert old DMA limits structure to DMA attribute structure 12180Sstevel@tonic-gate * and continue 12190Sstevel@tonic-gate */ 12200Sstevel@tonic-gate int 12210Sstevel@tonic-gate i_ddi_mem_alloc_lim(dev_info_t *dip, ddi_dma_lim_t *limits, 12220Sstevel@tonic-gate size_t length, int cansleep, int streaming, 12230Sstevel@tonic-gate ddi_device_acc_attr_t *accattrp, caddr_t *kaddrp, 12240Sstevel@tonic-gate uint_t *real_length, ddi_acc_hdl_t *ap) 12250Sstevel@tonic-gate { 12260Sstevel@tonic-gate ddi_dma_attr_t dma_attr, *attrp; 12270Sstevel@tonic-gate size_t rlen; 12280Sstevel@tonic-gate int ret; 12290Sstevel@tonic-gate 12300Sstevel@tonic-gate ASSERT(limits); 12310Sstevel@tonic-gate attrp = &dma_attr; 12320Sstevel@tonic-gate attrp->dma_attr_version = DMA_ATTR_V0; 12330Sstevel@tonic-gate attrp->dma_attr_addr_lo = (uint64_t)limits->dlim_addr_lo; 12340Sstevel@tonic-gate attrp->dma_attr_addr_hi = (uint64_t)limits->dlim_addr_hi; 12350Sstevel@tonic-gate attrp->dma_attr_count_max = (uint64_t)-1; 12360Sstevel@tonic-gate attrp->dma_attr_align = 1; 12370Sstevel@tonic-gate attrp->dma_attr_burstsizes = (uint_t)limits->dlim_burstsizes; 12380Sstevel@tonic-gate attrp->dma_attr_minxfer = (uint32_t)limits->dlim_minxfer; 12390Sstevel@tonic-gate attrp->dma_attr_maxxfer = (uint64_t)-1; 12400Sstevel@tonic-gate attrp->dma_attr_seg = (uint64_t)limits->dlim_cntr_max; 12410Sstevel@tonic-gate attrp->dma_attr_sgllen = 1; 12420Sstevel@tonic-gate attrp->dma_attr_granular = 1; 12430Sstevel@tonic-gate attrp->dma_attr_flags = 0; 12440Sstevel@tonic-gate 12450Sstevel@tonic-gate ret = i_ddi_mem_alloc(dip, attrp, length, cansleep, streaming, 12460Sstevel@tonic-gate accattrp, kaddrp, &rlen, ap); 12470Sstevel@tonic-gate if (ret == DDI_SUCCESS) { 12480Sstevel@tonic-gate if (real_length) 12490Sstevel@tonic-gate *real_length = (uint_t)rlen; 12500Sstevel@tonic-gate } 12510Sstevel@tonic-gate return (ret); 12520Sstevel@tonic-gate } 12530Sstevel@tonic-gate 12540Sstevel@tonic-gate /* ARGSUSED */ 12550Sstevel@tonic-gate void 12561900Seota i_ddi_mem_free(caddr_t kaddr, ddi_acc_hdl_t *ap) 12570Sstevel@tonic-gate { 12580Sstevel@tonic-gate kfreea(kaddr); 12590Sstevel@tonic-gate } 12600Sstevel@tonic-gate 12610Sstevel@tonic-gate /* 12620Sstevel@tonic-gate * SECTION: DDI Data Access 12630Sstevel@tonic-gate */ 12640Sstevel@tonic-gate 12650Sstevel@tonic-gate static uintptr_t impl_acc_hdl_id = 0; 12660Sstevel@tonic-gate 12670Sstevel@tonic-gate /* 12680Sstevel@tonic-gate * access handle allocator 12690Sstevel@tonic-gate */ 12700Sstevel@tonic-gate ddi_acc_hdl_t * 12710Sstevel@tonic-gate impl_acc_hdl_get(ddi_acc_handle_t hdl) 12720Sstevel@tonic-gate { 12730Sstevel@tonic-gate /* 12740Sstevel@tonic-gate * Extract the access handle address from the DDI implemented 12750Sstevel@tonic-gate * access handle 12760Sstevel@tonic-gate */ 12770Sstevel@tonic-gate return (&((ddi_acc_impl_t *)hdl)->ahi_common); 12780Sstevel@tonic-gate } 12790Sstevel@tonic-gate 12800Sstevel@tonic-gate ddi_acc_handle_t 12810Sstevel@tonic-gate impl_acc_hdl_alloc(int (*waitfp)(caddr_t), caddr_t arg) 12820Sstevel@tonic-gate { 12830Sstevel@tonic-gate ddi_acc_impl_t *hp; 12840Sstevel@tonic-gate on_trap_data_t *otp; 12850Sstevel@tonic-gate int sleepflag; 12860Sstevel@tonic-gate 12870Sstevel@tonic-gate sleepflag = ((waitfp == (int (*)())KM_SLEEP) ? KM_SLEEP : KM_NOSLEEP); 12880Sstevel@tonic-gate 12890Sstevel@tonic-gate /* 12900Sstevel@tonic-gate * Allocate and initialize the data access handle and error status. 12910Sstevel@tonic-gate */ 12920Sstevel@tonic-gate if ((hp = kmem_zalloc(sizeof (ddi_acc_impl_t), sleepflag)) == NULL) 12930Sstevel@tonic-gate goto fail; 12940Sstevel@tonic-gate if ((hp->ahi_err = (ndi_err_t *)kmem_zalloc( 12950Sstevel@tonic-gate sizeof (ndi_err_t), sleepflag)) == NULL) { 12960Sstevel@tonic-gate kmem_free(hp, sizeof (ddi_acc_impl_t)); 12970Sstevel@tonic-gate goto fail; 12980Sstevel@tonic-gate } 12990Sstevel@tonic-gate if ((otp = (on_trap_data_t *)kmem_zalloc( 13000Sstevel@tonic-gate sizeof (on_trap_data_t), sleepflag)) == NULL) { 13010Sstevel@tonic-gate kmem_free(hp->ahi_err, sizeof (ndi_err_t)); 13020Sstevel@tonic-gate kmem_free(hp, sizeof (ddi_acc_impl_t)); 13030Sstevel@tonic-gate goto fail; 13040Sstevel@tonic-gate } 13050Sstevel@tonic-gate hp->ahi_err->err_ontrap = otp; 13060Sstevel@tonic-gate hp->ahi_common.ah_platform_private = (void *)hp; 13070Sstevel@tonic-gate 13080Sstevel@tonic-gate return ((ddi_acc_handle_t)hp); 13090Sstevel@tonic-gate fail: 13100Sstevel@tonic-gate if ((waitfp != (int (*)())KM_SLEEP) && 13110Sstevel@tonic-gate (waitfp != (int (*)())KM_NOSLEEP)) 13120Sstevel@tonic-gate ddi_set_callback(waitfp, arg, &impl_acc_hdl_id); 13130Sstevel@tonic-gate return (NULL); 13140Sstevel@tonic-gate } 13150Sstevel@tonic-gate 13160Sstevel@tonic-gate void 13170Sstevel@tonic-gate impl_acc_hdl_free(ddi_acc_handle_t handle) 13180Sstevel@tonic-gate { 13190Sstevel@tonic-gate ddi_acc_impl_t *hp; 13200Sstevel@tonic-gate 13210Sstevel@tonic-gate /* 13220Sstevel@tonic-gate * The supplied (ddi_acc_handle_t) is actually a (ddi_acc_impl_t *), 13230Sstevel@tonic-gate * because that's what we allocated in impl_acc_hdl_alloc() above. 13240Sstevel@tonic-gate */ 13250Sstevel@tonic-gate hp = (ddi_acc_impl_t *)handle; 13260Sstevel@tonic-gate if (hp) { 13270Sstevel@tonic-gate kmem_free(hp->ahi_err->err_ontrap, sizeof (on_trap_data_t)); 13280Sstevel@tonic-gate kmem_free(hp->ahi_err, sizeof (ndi_err_t)); 13290Sstevel@tonic-gate kmem_free(hp, sizeof (ddi_acc_impl_t)); 13300Sstevel@tonic-gate if (impl_acc_hdl_id) 13310Sstevel@tonic-gate ddi_run_callback(&impl_acc_hdl_id); 13320Sstevel@tonic-gate } 13330Sstevel@tonic-gate } 13340Sstevel@tonic-gate 13351865Sdilpreet #define PCI_GET_MP_PFN(mp, page_no) ((mp)->dmai_ndvmapages == 1 ? \ 13361865Sdilpreet (pfn_t)(mp)->dmai_iopte:(((pfn_t *)(mp)->dmai_iopte)[page_no])) 13371865Sdilpreet 13381865Sdilpreet /* 13391865Sdilpreet * Function called after a dma fault occurred to find out whether the 13401865Sdilpreet * fault address is associated with a driver that is able to handle faults 13411865Sdilpreet * and recover from faults. 13421865Sdilpreet */ 13431865Sdilpreet /* ARGSUSED */ 13441865Sdilpreet int 13451865Sdilpreet impl_dma_check(dev_info_t *dip, const void *handle, const void *addr, 13461865Sdilpreet const void *not_used) 13471865Sdilpreet { 13481865Sdilpreet ddi_dma_impl_t *mp = (ddi_dma_impl_t *)handle; 13491865Sdilpreet pfn_t fault_pfn = mmu_btop(*(uint64_t *)addr); 13501865Sdilpreet pfn_t comp_pfn; 13511865Sdilpreet 13521865Sdilpreet /* 13531865Sdilpreet * The driver has to set DDI_DMA_FLAGERR to recover from dma faults. 13541865Sdilpreet */ 13551865Sdilpreet int page; 13561865Sdilpreet 13571865Sdilpreet ASSERT(mp); 13581865Sdilpreet for (page = 0; page < mp->dmai_ndvmapages; page++) { 13591865Sdilpreet comp_pfn = PCI_GET_MP_PFN(mp, page); 13601865Sdilpreet if (fault_pfn == comp_pfn) 13611865Sdilpreet return (DDI_FM_NONFATAL); 13621865Sdilpreet } 13631865Sdilpreet return (DDI_FM_UNKNOWN); 13641865Sdilpreet } 13651865Sdilpreet 13661865Sdilpreet /* 13671865Sdilpreet * Function used to check if a given access handle owns the failing address. 13681865Sdilpreet * Called by ndi_fmc_error, when we detect a PIO error. 13691865Sdilpreet */ 13701865Sdilpreet /* ARGSUSED */ 13711865Sdilpreet static int 13721865Sdilpreet impl_acc_check(dev_info_t *dip, const void *handle, const void *addr, 13731865Sdilpreet const void *not_used) 13741865Sdilpreet { 13751865Sdilpreet pfn_t pfn, fault_pfn; 13761865Sdilpreet ddi_acc_hdl_t *hp; 13771865Sdilpreet 13781865Sdilpreet hp = impl_acc_hdl_get((ddi_acc_handle_t)handle); 13791865Sdilpreet 13801865Sdilpreet ASSERT(hp); 13811865Sdilpreet 13821865Sdilpreet if (addr != NULL) { 13831865Sdilpreet pfn = hp->ah_pfn; 13841865Sdilpreet fault_pfn = mmu_btop(*(uint64_t *)addr); 13851865Sdilpreet if (fault_pfn >= pfn && fault_pfn < (pfn + hp->ah_pnum)) 13861865Sdilpreet return (DDI_FM_NONFATAL); 13871865Sdilpreet } 13881865Sdilpreet return (DDI_FM_UNKNOWN); 13891865Sdilpreet } 13901865Sdilpreet 13910Sstevel@tonic-gate void 13920Sstevel@tonic-gate impl_acc_err_init(ddi_acc_hdl_t *handlep) 13930Sstevel@tonic-gate { 13940Sstevel@tonic-gate int fmcap; 13950Sstevel@tonic-gate ndi_err_t *errp; 13960Sstevel@tonic-gate on_trap_data_t *otp; 13970Sstevel@tonic-gate ddi_acc_impl_t *hp = (ddi_acc_impl_t *)handlep; 13980Sstevel@tonic-gate 13990Sstevel@tonic-gate fmcap = ddi_fm_capable(handlep->ah_dip); 14000Sstevel@tonic-gate 14010Sstevel@tonic-gate if (handlep->ah_acc.devacc_attr_version < DDI_DEVICE_ATTR_V1 || 14020Sstevel@tonic-gate !DDI_FM_ACC_ERR_CAP(fmcap)) { 14030Sstevel@tonic-gate handlep->ah_acc.devacc_attr_access = DDI_DEFAULT_ACC; 14040Sstevel@tonic-gate } else if (DDI_FM_ACC_ERR_CAP(fmcap)) { 14050Sstevel@tonic-gate if (handlep->ah_acc.devacc_attr_access == DDI_DEFAULT_ACC) { 1406*11236SStephen.Hanson@Sun.COM if (handlep->ah_xfermodes) 1407*11236SStephen.Hanson@Sun.COM return; 14080Sstevel@tonic-gate i_ddi_drv_ereport_post(handlep->ah_dip, DVR_EFMCAP, 14090Sstevel@tonic-gate NULL, DDI_NOSLEEP); 14100Sstevel@tonic-gate } else { 14110Sstevel@tonic-gate errp = hp->ahi_err; 14120Sstevel@tonic-gate otp = (on_trap_data_t *)errp->err_ontrap; 14130Sstevel@tonic-gate otp->ot_handle = (void *)(hp); 14140Sstevel@tonic-gate otp->ot_prot = OT_DATA_ACCESS; 14150Sstevel@tonic-gate if (handlep->ah_acc.devacc_attr_access == 14160Sstevel@tonic-gate DDI_CAUTIOUS_ACC) 14170Sstevel@tonic-gate otp->ot_trampoline = 14180Sstevel@tonic-gate (uintptr_t)&i_ddi_caut_trampoline; 14190Sstevel@tonic-gate else 14200Sstevel@tonic-gate otp->ot_trampoline = 14210Sstevel@tonic-gate (uintptr_t)&i_ddi_prot_trampoline; 14220Sstevel@tonic-gate errp->err_status = DDI_FM_OK; 14230Sstevel@tonic-gate errp->err_expected = DDI_FM_ERR_UNEXPECTED; 14241865Sdilpreet errp->err_cf = impl_acc_check; 14250Sstevel@tonic-gate } 14260Sstevel@tonic-gate } 14270Sstevel@tonic-gate } 14280Sstevel@tonic-gate 14290Sstevel@tonic-gate void 14300Sstevel@tonic-gate impl_acc_hdl_init(ddi_acc_hdl_t *handlep) 14310Sstevel@tonic-gate { 14320Sstevel@tonic-gate ddi_acc_impl_t *hp; 14330Sstevel@tonic-gate 14340Sstevel@tonic-gate ASSERT(handlep); 14350Sstevel@tonic-gate 14360Sstevel@tonic-gate hp = (ddi_acc_impl_t *)handlep; 14370Sstevel@tonic-gate 14380Sstevel@tonic-gate /* 14390Sstevel@tonic-gate * check for SW byte-swapping 14400Sstevel@tonic-gate */ 14410Sstevel@tonic-gate hp->ahi_get8 = i_ddi_get8; 14420Sstevel@tonic-gate hp->ahi_put8 = i_ddi_put8; 14430Sstevel@tonic-gate hp->ahi_rep_get8 = i_ddi_rep_get8; 14440Sstevel@tonic-gate hp->ahi_rep_put8 = i_ddi_rep_put8; 14450Sstevel@tonic-gate if (handlep->ah_acc.devacc_attr_endian_flags & DDI_STRUCTURE_LE_ACC) { 14460Sstevel@tonic-gate hp->ahi_get16 = i_ddi_swap_get16; 14470Sstevel@tonic-gate hp->ahi_get32 = i_ddi_swap_get32; 14480Sstevel@tonic-gate hp->ahi_get64 = i_ddi_swap_get64; 14490Sstevel@tonic-gate hp->ahi_put16 = i_ddi_swap_put16; 14500Sstevel@tonic-gate hp->ahi_put32 = i_ddi_swap_put32; 14510Sstevel@tonic-gate hp->ahi_put64 = i_ddi_swap_put64; 14520Sstevel@tonic-gate hp->ahi_rep_get16 = i_ddi_swap_rep_get16; 14530Sstevel@tonic-gate hp->ahi_rep_get32 = i_ddi_swap_rep_get32; 14540Sstevel@tonic-gate hp->ahi_rep_get64 = i_ddi_swap_rep_get64; 14550Sstevel@tonic-gate hp->ahi_rep_put16 = i_ddi_swap_rep_put16; 14560Sstevel@tonic-gate hp->ahi_rep_put32 = i_ddi_swap_rep_put32; 14570Sstevel@tonic-gate hp->ahi_rep_put64 = i_ddi_swap_rep_put64; 14580Sstevel@tonic-gate } else { 14590Sstevel@tonic-gate hp->ahi_get16 = i_ddi_get16; 14600Sstevel@tonic-gate hp->ahi_get32 = i_ddi_get32; 14610Sstevel@tonic-gate hp->ahi_get64 = i_ddi_get64; 14620Sstevel@tonic-gate hp->ahi_put16 = i_ddi_put16; 14630Sstevel@tonic-gate hp->ahi_put32 = i_ddi_put32; 14640Sstevel@tonic-gate hp->ahi_put64 = i_ddi_put64; 14650Sstevel@tonic-gate hp->ahi_rep_get16 = i_ddi_rep_get16; 14660Sstevel@tonic-gate hp->ahi_rep_get32 = i_ddi_rep_get32; 14670Sstevel@tonic-gate hp->ahi_rep_get64 = i_ddi_rep_get64; 14680Sstevel@tonic-gate hp->ahi_rep_put16 = i_ddi_rep_put16; 14690Sstevel@tonic-gate hp->ahi_rep_put32 = i_ddi_rep_put32; 14700Sstevel@tonic-gate hp->ahi_rep_put64 = i_ddi_rep_put64; 14710Sstevel@tonic-gate } 14720Sstevel@tonic-gate 14730Sstevel@tonic-gate /* Legacy fault flags and support */ 14740Sstevel@tonic-gate hp->ahi_fault_check = i_ddi_acc_fault_check; 14750Sstevel@tonic-gate hp->ahi_fault_notify = i_ddi_acc_fault_notify; 14760Sstevel@tonic-gate hp->ahi_fault = 0; 14770Sstevel@tonic-gate impl_acc_err_init(handlep); 14780Sstevel@tonic-gate } 14790Sstevel@tonic-gate 14800Sstevel@tonic-gate void 14810Sstevel@tonic-gate i_ddi_acc_set_fault(ddi_acc_handle_t handle) 14820Sstevel@tonic-gate { 14830Sstevel@tonic-gate ddi_acc_impl_t *hp = (ddi_acc_impl_t *)handle; 14840Sstevel@tonic-gate 14850Sstevel@tonic-gate if (!hp->ahi_fault) { 14860Sstevel@tonic-gate hp->ahi_fault = 1; 14870Sstevel@tonic-gate (*hp->ahi_fault_notify)(hp); 14880Sstevel@tonic-gate } 14890Sstevel@tonic-gate } 14900Sstevel@tonic-gate 14910Sstevel@tonic-gate void 14920Sstevel@tonic-gate i_ddi_acc_clr_fault(ddi_acc_handle_t handle) 14930Sstevel@tonic-gate { 14940Sstevel@tonic-gate ddi_acc_impl_t *hp = (ddi_acc_impl_t *)handle; 14950Sstevel@tonic-gate 14960Sstevel@tonic-gate if (hp->ahi_fault) { 14970Sstevel@tonic-gate hp->ahi_fault = 0; 14980Sstevel@tonic-gate (*hp->ahi_fault_notify)(hp); 14990Sstevel@tonic-gate } 15000Sstevel@tonic-gate } 15010Sstevel@tonic-gate 15020Sstevel@tonic-gate /* ARGSUSED */ 15030Sstevel@tonic-gate void 15040Sstevel@tonic-gate i_ddi_acc_fault_notify(ddi_acc_impl_t *hp) 15050Sstevel@tonic-gate { 15060Sstevel@tonic-gate /* Default version, does nothing */ 15070Sstevel@tonic-gate } 15080Sstevel@tonic-gate 15090Sstevel@tonic-gate /* 15100Sstevel@tonic-gate * SECTION: Misc functions 15110Sstevel@tonic-gate */ 15120Sstevel@tonic-gate 15130Sstevel@tonic-gate /* 15140Sstevel@tonic-gate * instance wrappers 15150Sstevel@tonic-gate */ 15160Sstevel@tonic-gate /*ARGSUSED*/ 15170Sstevel@tonic-gate uint_t 15180Sstevel@tonic-gate impl_assign_instance(dev_info_t *dip) 15190Sstevel@tonic-gate { 15200Sstevel@tonic-gate return ((uint_t)-1); 15210Sstevel@tonic-gate } 15220Sstevel@tonic-gate 15230Sstevel@tonic-gate /*ARGSUSED*/ 15240Sstevel@tonic-gate int 15250Sstevel@tonic-gate impl_keep_instance(dev_info_t *dip) 15260Sstevel@tonic-gate { 15270Sstevel@tonic-gate return (DDI_FAILURE); 15280Sstevel@tonic-gate } 15290Sstevel@tonic-gate 15300Sstevel@tonic-gate /*ARGSUSED*/ 15310Sstevel@tonic-gate int 15320Sstevel@tonic-gate impl_free_instance(dev_info_t *dip) 15330Sstevel@tonic-gate { 15340Sstevel@tonic-gate return (DDI_FAILURE); 15350Sstevel@tonic-gate } 15360Sstevel@tonic-gate 15370Sstevel@tonic-gate /*ARGSUSED*/ 15380Sstevel@tonic-gate int 15390Sstevel@tonic-gate impl_check_cpu(dev_info_t *devi) 15400Sstevel@tonic-gate { 15410Sstevel@tonic-gate return (DDI_SUCCESS); 15420Sstevel@tonic-gate } 15430Sstevel@tonic-gate 15440Sstevel@tonic-gate 15450Sstevel@tonic-gate static const char *nocopydevs[] = { 15460Sstevel@tonic-gate "SUNW,ffb", 15470Sstevel@tonic-gate "SUNW,afb", 15480Sstevel@tonic-gate NULL 15490Sstevel@tonic-gate }; 15500Sstevel@tonic-gate 15510Sstevel@tonic-gate /* 15520Sstevel@tonic-gate * Perform a copy from a memory mapped device (whose devinfo pointer is devi) 15530Sstevel@tonic-gate * separately mapped at devaddr in the kernel to a kernel buffer at kaddr. 15540Sstevel@tonic-gate */ 15550Sstevel@tonic-gate /*ARGSUSED*/ 15560Sstevel@tonic-gate int 15570Sstevel@tonic-gate e_ddi_copyfromdev(dev_info_t *devi, 15580Sstevel@tonic-gate off_t off, const void *devaddr, void *kaddr, size_t len) 15590Sstevel@tonic-gate { 15600Sstevel@tonic-gate const char **argv; 15610Sstevel@tonic-gate 15620Sstevel@tonic-gate for (argv = nocopydevs; *argv; argv++) 15630Sstevel@tonic-gate if (strcmp(ddi_binding_name(devi), *argv) == 0) { 15640Sstevel@tonic-gate bzero(kaddr, len); 15650Sstevel@tonic-gate return (0); 15660Sstevel@tonic-gate } 15670Sstevel@tonic-gate 15680Sstevel@tonic-gate bcopy(devaddr, kaddr, len); 15690Sstevel@tonic-gate return (0); 15700Sstevel@tonic-gate } 15710Sstevel@tonic-gate 15720Sstevel@tonic-gate /* 15730Sstevel@tonic-gate * Perform a copy to a memory mapped device (whose devinfo pointer is devi) 15740Sstevel@tonic-gate * separately mapped at devaddr in the kernel from a kernel buffer at kaddr. 15750Sstevel@tonic-gate */ 15760Sstevel@tonic-gate /*ARGSUSED*/ 15770Sstevel@tonic-gate int 15780Sstevel@tonic-gate e_ddi_copytodev(dev_info_t *devi, 15790Sstevel@tonic-gate off_t off, const void *kaddr, void *devaddr, size_t len) 15800Sstevel@tonic-gate { 15810Sstevel@tonic-gate const char **argv; 15820Sstevel@tonic-gate 15830Sstevel@tonic-gate for (argv = nocopydevs; *argv; argv++) 15840Sstevel@tonic-gate if (strcmp(ddi_binding_name(devi), *argv) == 0) 15850Sstevel@tonic-gate return (1); 15860Sstevel@tonic-gate 15870Sstevel@tonic-gate bcopy(kaddr, devaddr, len); 15880Sstevel@tonic-gate return (0); 15890Sstevel@tonic-gate } 15900Sstevel@tonic-gate 15910Sstevel@tonic-gate /* 15920Sstevel@tonic-gate * Boot Configuration 15930Sstevel@tonic-gate */ 15940Sstevel@tonic-gate idprom_t idprom; 15950Sstevel@tonic-gate 15960Sstevel@tonic-gate /* 15970Sstevel@tonic-gate * Configure the hardware on the system. 15980Sstevel@tonic-gate * Called before the rootfs is mounted 15990Sstevel@tonic-gate */ 16000Sstevel@tonic-gate void 16010Sstevel@tonic-gate configure(void) 16020Sstevel@tonic-gate { 16030Sstevel@tonic-gate extern void i_ddi_init_root(); 16040Sstevel@tonic-gate 16050Sstevel@tonic-gate /* We better have released boot by this time! */ 16060Sstevel@tonic-gate ASSERT(!bootops); 16070Sstevel@tonic-gate 16080Sstevel@tonic-gate /* 16090Sstevel@tonic-gate * Determine whether or not to use the fpu, V9 SPARC cpus 16100Sstevel@tonic-gate * always have one. Could check for existence of a fp queue, 16110Sstevel@tonic-gate * Ultra I, II and IIa do not have a fp queue. 16120Sstevel@tonic-gate */ 16130Sstevel@tonic-gate if (fpu_exists) 16140Sstevel@tonic-gate fpu_probe(); 16150Sstevel@tonic-gate else 16160Sstevel@tonic-gate cmn_err(CE_CONT, "FPU not in use\n"); 16170Sstevel@tonic-gate 16180Sstevel@tonic-gate #if 0 /* XXXQ - not necessary for sun4u */ 16190Sstevel@tonic-gate /* 16200Sstevel@tonic-gate * This following line fixes bugid 1041296; we need to do a 16210Sstevel@tonic-gate * prom_nextnode(0) because this call ALSO patches the DMA+ 16220Sstevel@tonic-gate * bug in Campus-B and Phoenix. The prom uncaches the traptable 16230Sstevel@tonic-gate * page as a side-effect of devr_next(0) (which prom_nextnode calls), 16240Sstevel@tonic-gate * so this *must* be executed early on. (XXX This is untrue for sun4u) 16250Sstevel@tonic-gate */ 1626789Sahrens (void) prom_nextnode((pnode_t)0); 16270Sstevel@tonic-gate #endif 16280Sstevel@tonic-gate 16290Sstevel@tonic-gate /* 16300Sstevel@tonic-gate * Initialize devices on the machine. 16310Sstevel@tonic-gate * Uses configuration tree built by the PROMs to determine what 16320Sstevel@tonic-gate * is present, and builds a tree of prototype dev_info nodes 16330Sstevel@tonic-gate * corresponding to the hardware which identified itself. 16340Sstevel@tonic-gate */ 16350Sstevel@tonic-gate i_ddi_init_root(); 16360Sstevel@tonic-gate 16370Sstevel@tonic-gate #ifdef DDI_PROP_DEBUG 16380Sstevel@tonic-gate (void) ddi_prop_debug(1); /* Enable property debugging */ 16390Sstevel@tonic-gate #endif /* DDI_PROP_DEBUG */ 16400Sstevel@tonic-gate } 16410Sstevel@tonic-gate 16420Sstevel@tonic-gate /* 16430Sstevel@tonic-gate * The "status" property indicates the operational status of a device. 16440Sstevel@tonic-gate * If this property is present, the value is a string indicating the 16450Sstevel@tonic-gate * status of the device as follows: 16460Sstevel@tonic-gate * 16470Sstevel@tonic-gate * "okay" operational. 16480Sstevel@tonic-gate * "disabled" not operational, but might become operational. 16490Sstevel@tonic-gate * "fail" not operational because a fault has been detected, 16500Sstevel@tonic-gate * and it is unlikely that the device will become 16510Sstevel@tonic-gate * operational without repair. no additional details 16520Sstevel@tonic-gate * are available. 16530Sstevel@tonic-gate * "fail-xxx" not operational because a fault has been detected, 16540Sstevel@tonic-gate * and it is unlikely that the device will become 16550Sstevel@tonic-gate * operational without repair. "xxx" is additional 16560Sstevel@tonic-gate * human-readable information about the particular 16570Sstevel@tonic-gate * fault condition that was detected. 16580Sstevel@tonic-gate * 16590Sstevel@tonic-gate * The absence of this property means that the operational status is 16600Sstevel@tonic-gate * unknown or okay. 16610Sstevel@tonic-gate * 16620Sstevel@tonic-gate * This routine checks the status property of the specified device node 16630Sstevel@tonic-gate * and returns 0 if the operational status indicates failure, and 1 otherwise. 16640Sstevel@tonic-gate * 16650Sstevel@tonic-gate * The property may exist on plug-in cards the existed before IEEE 1275-1994. 16660Sstevel@tonic-gate * And, in that case, the property may not even be a string. So we carefully 16670Sstevel@tonic-gate * check for the value "fail", in the beginning of the string, noting 16680Sstevel@tonic-gate * the property length. 16690Sstevel@tonic-gate */ 16700Sstevel@tonic-gate int 16710Sstevel@tonic-gate status_okay(int id, char *buf, int buflen) 16720Sstevel@tonic-gate { 16730Sstevel@tonic-gate char status_buf[OBP_MAXPROPNAME]; 16740Sstevel@tonic-gate char *bufp = buf; 16750Sstevel@tonic-gate int len = buflen; 16760Sstevel@tonic-gate int proplen; 16770Sstevel@tonic-gate static const char *status = "status"; 16780Sstevel@tonic-gate static const char *fail = "fail"; 16790Sstevel@tonic-gate size_t fail_len = strlen(fail); 16800Sstevel@tonic-gate 16810Sstevel@tonic-gate /* 16820Sstevel@tonic-gate * Get the proplen ... if it's smaller than "fail", 16830Sstevel@tonic-gate * or doesn't exist ... then we don't care, since 16840Sstevel@tonic-gate * the value can't begin with the char string "fail". 16850Sstevel@tonic-gate * 16860Sstevel@tonic-gate * NB: proplen, if it's a string, includes the NULL in the 16870Sstevel@tonic-gate * the size of the property, and fail_len does not. 16880Sstevel@tonic-gate */ 1689789Sahrens proplen = prom_getproplen((pnode_t)id, (caddr_t)status); 16900Sstevel@tonic-gate if (proplen <= fail_len) /* nonexistent or uninteresting len */ 16910Sstevel@tonic-gate return (1); 16920Sstevel@tonic-gate 16930Sstevel@tonic-gate /* 16940Sstevel@tonic-gate * if a buffer was provided, use it 16950Sstevel@tonic-gate */ 16960Sstevel@tonic-gate if ((buf == (char *)NULL) || (buflen <= 0)) { 16970Sstevel@tonic-gate bufp = status_buf; 16980Sstevel@tonic-gate len = sizeof (status_buf); 16990Sstevel@tonic-gate } 17000Sstevel@tonic-gate *bufp = (char)0; 17010Sstevel@tonic-gate 17020Sstevel@tonic-gate /* 17030Sstevel@tonic-gate * Get the property into the buffer, to the extent of the buffer, 17040Sstevel@tonic-gate * and in case the buffer is smaller than the property size, 17050Sstevel@tonic-gate * NULL terminate the buffer. (This handles the case where 17060Sstevel@tonic-gate * a buffer was passed in and the caller wants to print the 17070Sstevel@tonic-gate * value, but the buffer was too small). 17080Sstevel@tonic-gate */ 1709789Sahrens (void) prom_bounded_getprop((pnode_t)id, (caddr_t)status, 17100Sstevel@tonic-gate (caddr_t)bufp, len); 17110Sstevel@tonic-gate *(bufp + len - 1) = (char)0; 17120Sstevel@tonic-gate 17130Sstevel@tonic-gate /* 17140Sstevel@tonic-gate * If the value begins with the char string "fail", 17150Sstevel@tonic-gate * then it means the node is failed. We don't care 17160Sstevel@tonic-gate * about any other values. We assume the node is ok 17170Sstevel@tonic-gate * although it might be 'disabled'. 17180Sstevel@tonic-gate */ 17190Sstevel@tonic-gate if (strncmp(bufp, fail, fail_len) == 0) 17200Sstevel@tonic-gate return (0); 17210Sstevel@tonic-gate 17220Sstevel@tonic-gate return (1); 17230Sstevel@tonic-gate } 17240Sstevel@tonic-gate 17250Sstevel@tonic-gate 17260Sstevel@tonic-gate /* 17270Sstevel@tonic-gate * We set the cpu type from the idprom, if we can. 17280Sstevel@tonic-gate * Note that we just read out the contents of it, for the most part. 17290Sstevel@tonic-gate */ 17300Sstevel@tonic-gate void 17310Sstevel@tonic-gate setcputype(void) 17320Sstevel@tonic-gate { 17330Sstevel@tonic-gate /* 17340Sstevel@tonic-gate * We cache the idprom info early on so that we don't 17350Sstevel@tonic-gate * rummage through the NVRAM unnecessarily later. 17360Sstevel@tonic-gate */ 17370Sstevel@tonic-gate (void) prom_getidprom((caddr_t)&idprom, sizeof (idprom)); 17380Sstevel@tonic-gate } 17390Sstevel@tonic-gate 17400Sstevel@tonic-gate /* 17410Sstevel@tonic-gate * Here is where we actually infer meanings to the members of idprom_t 17420Sstevel@tonic-gate */ 17430Sstevel@tonic-gate void 17440Sstevel@tonic-gate parse_idprom(void) 17450Sstevel@tonic-gate { 17460Sstevel@tonic-gate if (idprom.id_format == IDFORM_1) { 17470Sstevel@tonic-gate (void) localetheraddr((struct ether_addr *)idprom.id_ether, 17480Sstevel@tonic-gate (struct ether_addr *)NULL); 17498662SJordan.Vaughan@Sun.com (void) snprintf(hw_serial, HW_HOSTID_LEN, "%u", 17508662SJordan.Vaughan@Sun.com (idprom.id_machine << 24) + idprom.id_serial); 17510Sstevel@tonic-gate } else 17520Sstevel@tonic-gate prom_printf("Invalid format code in IDprom.\n"); 17530Sstevel@tonic-gate } 17540Sstevel@tonic-gate 17550Sstevel@tonic-gate /* 17560Sstevel@tonic-gate * Allow for implementation specific correction of PROM property values. 17570Sstevel@tonic-gate */ 17580Sstevel@tonic-gate /*ARGSUSED*/ 17590Sstevel@tonic-gate void 17600Sstevel@tonic-gate impl_fix_props(dev_info_t *dip, dev_info_t *ch_dip, char *name, int len, 17610Sstevel@tonic-gate caddr_t buffer) 17620Sstevel@tonic-gate { 17630Sstevel@tonic-gate /* 17640Sstevel@tonic-gate * There are no adjustments needed in this implementation. 17650Sstevel@tonic-gate */ 17660Sstevel@tonic-gate } 17670Sstevel@tonic-gate 17680Sstevel@tonic-gate /* 17690Sstevel@tonic-gate * The following functions ready a cautious request to go up to the nexus 17700Sstevel@tonic-gate * driver. It is up to the nexus driver to decide how to process the request. 17710Sstevel@tonic-gate * It may choose to call i_ddi_do_caut_get/put in this file, or do it 17720Sstevel@tonic-gate * differently. 17730Sstevel@tonic-gate */ 17740Sstevel@tonic-gate 17750Sstevel@tonic-gate static void 17760Sstevel@tonic-gate i_ddi_caut_getput_ctlops( 17770Sstevel@tonic-gate ddi_acc_impl_t *hp, uint64_t host_addr, uint64_t dev_addr, size_t size, 17780Sstevel@tonic-gate size_t repcount, uint_t flags, ddi_ctl_enum_t cmd) 17790Sstevel@tonic-gate { 17800Sstevel@tonic-gate peekpoke_ctlops_t cautacc_ctlops_arg; 17810Sstevel@tonic-gate 17820Sstevel@tonic-gate cautacc_ctlops_arg.size = size; 17830Sstevel@tonic-gate cautacc_ctlops_arg.dev_addr = dev_addr; 17840Sstevel@tonic-gate cautacc_ctlops_arg.host_addr = host_addr; 17850Sstevel@tonic-gate cautacc_ctlops_arg.handle = (ddi_acc_handle_t)hp; 17860Sstevel@tonic-gate cautacc_ctlops_arg.repcount = repcount; 17870Sstevel@tonic-gate cautacc_ctlops_arg.flags = flags; 17880Sstevel@tonic-gate 17890Sstevel@tonic-gate (void) ddi_ctlops(hp->ahi_common.ah_dip, hp->ahi_common.ah_dip, cmd, 17900Sstevel@tonic-gate &cautacc_ctlops_arg, NULL); 17910Sstevel@tonic-gate } 17920Sstevel@tonic-gate 17930Sstevel@tonic-gate uint8_t 17940Sstevel@tonic-gate i_ddi_caut_get8(ddi_acc_impl_t *hp, uint8_t *addr) 17950Sstevel@tonic-gate { 17960Sstevel@tonic-gate uint8_t value; 17970Sstevel@tonic-gate i_ddi_caut_getput_ctlops(hp, (uint64_t)&value, (uint64_t)addr, 17980Sstevel@tonic-gate sizeof (uint8_t), 1, 0, DDI_CTLOPS_PEEK); 17990Sstevel@tonic-gate 18000Sstevel@tonic-gate return (value); 18010Sstevel@tonic-gate } 18020Sstevel@tonic-gate 18030Sstevel@tonic-gate uint16_t 18040Sstevel@tonic-gate i_ddi_caut_get16(ddi_acc_impl_t *hp, uint16_t *addr) 18050Sstevel@tonic-gate { 18060Sstevel@tonic-gate uint16_t value; 18070Sstevel@tonic-gate i_ddi_caut_getput_ctlops(hp, (uint64_t)&value, (uint64_t)addr, 18080Sstevel@tonic-gate sizeof (uint16_t), 1, 0, DDI_CTLOPS_PEEK); 18090Sstevel@tonic-gate 18100Sstevel@tonic-gate return (value); 18110Sstevel@tonic-gate } 18120Sstevel@tonic-gate 18130Sstevel@tonic-gate uint32_t 18140Sstevel@tonic-gate i_ddi_caut_get32(ddi_acc_impl_t *hp, uint32_t *addr) 18150Sstevel@tonic-gate { 18160Sstevel@tonic-gate uint32_t value; 18170Sstevel@tonic-gate i_ddi_caut_getput_ctlops(hp, (uint64_t)&value, (uint64_t)addr, 18180Sstevel@tonic-gate sizeof (uint32_t), 1, 0, DDI_CTLOPS_PEEK); 18190Sstevel@tonic-gate 18200Sstevel@tonic-gate return (value); 18210Sstevel@tonic-gate } 18220Sstevel@tonic-gate 18230Sstevel@tonic-gate uint64_t 18240Sstevel@tonic-gate i_ddi_caut_get64(ddi_acc_impl_t *hp, uint64_t *addr) 18250Sstevel@tonic-gate { 18260Sstevel@tonic-gate uint64_t value; 18270Sstevel@tonic-gate i_ddi_caut_getput_ctlops(hp, (uint64_t)&value, (uint64_t)addr, 18280Sstevel@tonic-gate sizeof (uint64_t), 1, 0, DDI_CTLOPS_PEEK); 18290Sstevel@tonic-gate 18300Sstevel@tonic-gate return (value); 18310Sstevel@tonic-gate } 18320Sstevel@tonic-gate 18330Sstevel@tonic-gate void 18340Sstevel@tonic-gate i_ddi_caut_put8(ddi_acc_impl_t *hp, uint8_t *addr, uint8_t value) 18350Sstevel@tonic-gate { 18360Sstevel@tonic-gate i_ddi_caut_getput_ctlops(hp, (uint64_t)&value, (uint64_t)addr, 18370Sstevel@tonic-gate sizeof (uint8_t), 1, 0, DDI_CTLOPS_POKE); 18380Sstevel@tonic-gate } 18390Sstevel@tonic-gate 18400Sstevel@tonic-gate void 18410Sstevel@tonic-gate i_ddi_caut_put16(ddi_acc_impl_t *hp, uint16_t *addr, uint16_t value) 18420Sstevel@tonic-gate { 18430Sstevel@tonic-gate i_ddi_caut_getput_ctlops(hp, (uint64_t)&value, (uint64_t)addr, 18440Sstevel@tonic-gate sizeof (uint16_t), 1, 0, DDI_CTLOPS_POKE); 18450Sstevel@tonic-gate } 18460Sstevel@tonic-gate 18470Sstevel@tonic-gate void 18480Sstevel@tonic-gate i_ddi_caut_put32(ddi_acc_impl_t *hp, uint32_t *addr, uint32_t value) 18490Sstevel@tonic-gate { 18500Sstevel@tonic-gate i_ddi_caut_getput_ctlops(hp, (uint64_t)&value, (uint64_t)addr, 18510Sstevel@tonic-gate sizeof (uint32_t), 1, 0, DDI_CTLOPS_POKE); 18520Sstevel@tonic-gate } 18530Sstevel@tonic-gate 18540Sstevel@tonic-gate void 18550Sstevel@tonic-gate i_ddi_caut_put64(ddi_acc_impl_t *hp, uint64_t *addr, uint64_t value) 18560Sstevel@tonic-gate { 18570Sstevel@tonic-gate i_ddi_caut_getput_ctlops(hp, (uint64_t)&value, (uint64_t)addr, 18580Sstevel@tonic-gate sizeof (uint64_t), 1, 0, DDI_CTLOPS_POKE); 18590Sstevel@tonic-gate } 18600Sstevel@tonic-gate 18610Sstevel@tonic-gate void 18620Sstevel@tonic-gate i_ddi_caut_rep_get8(ddi_acc_impl_t *hp, uint8_t *host_addr, uint8_t *dev_addr, 18630Sstevel@tonic-gate size_t repcount, uint_t flags) 18640Sstevel@tonic-gate { 18650Sstevel@tonic-gate i_ddi_caut_getput_ctlops(hp, (uint64_t)host_addr, (uint64_t)dev_addr, 18660Sstevel@tonic-gate sizeof (uint8_t), repcount, flags, DDI_CTLOPS_PEEK); 18670Sstevel@tonic-gate } 18680Sstevel@tonic-gate 18690Sstevel@tonic-gate void 18700Sstevel@tonic-gate i_ddi_caut_rep_get16(ddi_acc_impl_t *hp, uint16_t *host_addr, 18710Sstevel@tonic-gate uint16_t *dev_addr, size_t repcount, uint_t flags) 18720Sstevel@tonic-gate { 18730Sstevel@tonic-gate i_ddi_caut_getput_ctlops(hp, (uint64_t)host_addr, (uint64_t)dev_addr, 18740Sstevel@tonic-gate sizeof (uint16_t), repcount, flags, DDI_CTLOPS_PEEK); 18750Sstevel@tonic-gate } 18760Sstevel@tonic-gate 18770Sstevel@tonic-gate void 18780Sstevel@tonic-gate i_ddi_caut_rep_get32(ddi_acc_impl_t *hp, uint32_t *host_addr, 18790Sstevel@tonic-gate uint32_t *dev_addr, size_t repcount, uint_t flags) 18800Sstevel@tonic-gate { 18810Sstevel@tonic-gate i_ddi_caut_getput_ctlops(hp, (uint64_t)host_addr, (uint64_t)dev_addr, 18820Sstevel@tonic-gate sizeof (uint32_t), repcount, flags, DDI_CTLOPS_PEEK); 18830Sstevel@tonic-gate } 18840Sstevel@tonic-gate 18850Sstevel@tonic-gate void 18860Sstevel@tonic-gate i_ddi_caut_rep_get64(ddi_acc_impl_t *hp, uint64_t *host_addr, 18870Sstevel@tonic-gate uint64_t *dev_addr, size_t repcount, uint_t flags) 18880Sstevel@tonic-gate { 18890Sstevel@tonic-gate i_ddi_caut_getput_ctlops(hp, (uint64_t)host_addr, (uint64_t)dev_addr, 18900Sstevel@tonic-gate sizeof (uint64_t), repcount, flags, DDI_CTLOPS_PEEK); 18910Sstevel@tonic-gate } 18920Sstevel@tonic-gate 18930Sstevel@tonic-gate void 18940Sstevel@tonic-gate i_ddi_caut_rep_put8(ddi_acc_impl_t *hp, uint8_t *host_addr, uint8_t *dev_addr, 18950Sstevel@tonic-gate size_t repcount, uint_t flags) 18960Sstevel@tonic-gate { 18970Sstevel@tonic-gate i_ddi_caut_getput_ctlops(hp, (uint64_t)host_addr, (uint64_t)dev_addr, 18980Sstevel@tonic-gate sizeof (uint8_t), repcount, flags, DDI_CTLOPS_POKE); 18990Sstevel@tonic-gate } 19000Sstevel@tonic-gate 19010Sstevel@tonic-gate void 19020Sstevel@tonic-gate i_ddi_caut_rep_put16(ddi_acc_impl_t *hp, uint16_t *host_addr, 19030Sstevel@tonic-gate uint16_t *dev_addr, size_t repcount, uint_t flags) 19040Sstevel@tonic-gate { 19050Sstevel@tonic-gate i_ddi_caut_getput_ctlops(hp, (uint64_t)host_addr, (uint64_t)dev_addr, 19060Sstevel@tonic-gate sizeof (uint16_t), repcount, flags, DDI_CTLOPS_POKE); 19070Sstevel@tonic-gate } 19080Sstevel@tonic-gate 19090Sstevel@tonic-gate void 19100Sstevel@tonic-gate i_ddi_caut_rep_put32(ddi_acc_impl_t *hp, uint32_t *host_addr, 19110Sstevel@tonic-gate uint32_t *dev_addr, size_t repcount, uint_t flags) 19120Sstevel@tonic-gate { 19130Sstevel@tonic-gate i_ddi_caut_getput_ctlops(hp, (uint64_t)host_addr, (uint64_t)dev_addr, 19140Sstevel@tonic-gate sizeof (uint32_t), repcount, flags, DDI_CTLOPS_POKE); 19150Sstevel@tonic-gate } 19160Sstevel@tonic-gate 19170Sstevel@tonic-gate void 19180Sstevel@tonic-gate i_ddi_caut_rep_put64(ddi_acc_impl_t *hp, uint64_t *host_addr, 19190Sstevel@tonic-gate uint64_t *dev_addr, size_t repcount, uint_t flags) 19200Sstevel@tonic-gate { 19210Sstevel@tonic-gate i_ddi_caut_getput_ctlops(hp, (uint64_t)host_addr, (uint64_t)dev_addr, 19220Sstevel@tonic-gate sizeof (uint64_t), repcount, flags, DDI_CTLOPS_POKE); 19230Sstevel@tonic-gate } 19240Sstevel@tonic-gate 19250Sstevel@tonic-gate /* 19260Sstevel@tonic-gate * This is called only to process peek/poke when the DIP is NULL. 19270Sstevel@tonic-gate * Assume that this is for memory, as nexi take care of device safe accesses. 19280Sstevel@tonic-gate */ 19290Sstevel@tonic-gate int 19300Sstevel@tonic-gate peekpoke_mem(ddi_ctl_enum_t cmd, peekpoke_ctlops_t *in_args) 19310Sstevel@tonic-gate { 19320Sstevel@tonic-gate int err = DDI_SUCCESS; 19330Sstevel@tonic-gate on_trap_data_t otd; 19340Sstevel@tonic-gate 19350Sstevel@tonic-gate /* Set up protected environment. */ 19360Sstevel@tonic-gate if (!on_trap(&otd, OT_DATA_ACCESS)) { 19370Sstevel@tonic-gate uintptr_t tramp = otd.ot_trampoline; 19380Sstevel@tonic-gate 19390Sstevel@tonic-gate if (cmd == DDI_CTLOPS_POKE) { 19400Sstevel@tonic-gate otd.ot_trampoline = (uintptr_t)&poke_fault; 19410Sstevel@tonic-gate err = do_poke(in_args->size, (void *)in_args->dev_addr, 19420Sstevel@tonic-gate (void *)in_args->host_addr); 19430Sstevel@tonic-gate } else { 19440Sstevel@tonic-gate otd.ot_trampoline = (uintptr_t)&peek_fault; 19450Sstevel@tonic-gate err = do_peek(in_args->size, (void *)in_args->dev_addr, 19460Sstevel@tonic-gate (void *)in_args->host_addr); 19470Sstevel@tonic-gate } 19480Sstevel@tonic-gate otd.ot_trampoline = tramp; 19490Sstevel@tonic-gate } else 19500Sstevel@tonic-gate err = DDI_FAILURE; 19510Sstevel@tonic-gate 19520Sstevel@tonic-gate /* Take down protected environment. */ 19530Sstevel@tonic-gate no_trap(); 19540Sstevel@tonic-gate 19550Sstevel@tonic-gate return (err); 19560Sstevel@tonic-gate } 19571991Sheppo 19581991Sheppo /* 19591991Sheppo * Platform independent DR routines 19601991Sheppo */ 19611991Sheppo 19621991Sheppo static int 19631991Sheppo ndi2errno(int n) 19641991Sheppo { 19651991Sheppo int err = 0; 19661991Sheppo 19671991Sheppo switch (n) { 19681991Sheppo case NDI_NOMEM: 19691991Sheppo err = ENOMEM; 19701991Sheppo break; 19711991Sheppo case NDI_BUSY: 19721991Sheppo err = EBUSY; 19731991Sheppo break; 19741991Sheppo case NDI_FAULT: 19751991Sheppo err = EFAULT; 19761991Sheppo break; 19771991Sheppo case NDI_FAILURE: 19781991Sheppo err = EIO; 19791991Sheppo break; 19801991Sheppo case NDI_SUCCESS: 19811991Sheppo break; 19821991Sheppo case NDI_BADHANDLE: 19831991Sheppo default: 19841991Sheppo err = EINVAL; 19851991Sheppo break; 19861991Sheppo } 19871991Sheppo return (err); 19881991Sheppo } 19891991Sheppo 19901991Sheppo /* 19911991Sheppo * Prom tree node list 19921991Sheppo */ 19931991Sheppo struct ptnode { 19941991Sheppo pnode_t nodeid; 19951991Sheppo struct ptnode *next; 19961991Sheppo }; 19971991Sheppo 19981991Sheppo /* 19991991Sheppo * Prom tree walk arg 20001991Sheppo */ 20011991Sheppo struct pta { 20021991Sheppo dev_info_t *pdip; 20031991Sheppo devi_branch_t *bp; 20041991Sheppo uint_t flags; 20051991Sheppo dev_info_t *fdip; 20061991Sheppo struct ptnode *head; 20071991Sheppo }; 20081991Sheppo 20091991Sheppo static void 20101991Sheppo visit_node(pnode_t nodeid, struct pta *ap) 20111991Sheppo { 20121991Sheppo struct ptnode **nextp; 20131991Sheppo int (*select)(pnode_t, void *, uint_t); 20141991Sheppo 20151991Sheppo ASSERT(nodeid != OBP_NONODE && nodeid != OBP_BADNODE); 20161991Sheppo 20171991Sheppo select = ap->bp->create.prom_branch_select; 20181991Sheppo 20191991Sheppo ASSERT(select); 20201991Sheppo 20211991Sheppo if (select(nodeid, ap->bp->arg, 0) == DDI_SUCCESS) { 20221991Sheppo 20231991Sheppo for (nextp = &ap->head; *nextp; nextp = &(*nextp)->next) 20241991Sheppo ; 20251991Sheppo 20261991Sheppo *nextp = kmem_zalloc(sizeof (struct ptnode), KM_SLEEP); 20271991Sheppo 20281991Sheppo (*nextp)->nodeid = nodeid; 20291991Sheppo } 20301991Sheppo 20311991Sheppo if ((ap->flags & DEVI_BRANCH_CHILD) == DEVI_BRANCH_CHILD) 20321991Sheppo return; 20331991Sheppo 20341991Sheppo nodeid = prom_childnode(nodeid); 20351991Sheppo while (nodeid != OBP_NONODE && nodeid != OBP_BADNODE) { 20361991Sheppo visit_node(nodeid, ap); 20371991Sheppo nodeid = prom_nextnode(nodeid); 20381991Sheppo } 20391991Sheppo } 20401991Sheppo 20414845Svikram /* 20424845Svikram * NOTE: The caller of this function must check for device contracts 20434845Svikram * or LDI callbacks against this dip before setting the dip offline. 20444845Svikram */ 20451991Sheppo static int 20464845Svikram set_infant_dip_offline(dev_info_t *dip, void *arg) 20471991Sheppo { 20484845Svikram char *path = (char *)arg; 20494845Svikram 20501991Sheppo ASSERT(dip); 20514845Svikram ASSERT(arg); 20524845Svikram 20534845Svikram if (i_ddi_node_state(dip) >= DS_ATTACHED) { 20544845Svikram (void) ddi_pathname(dip, path); 20554845Svikram cmn_err(CE_WARN, "Attempt to set offline flag on attached " 20564845Svikram "node: %s", path); 20574845Svikram return (DDI_FAILURE); 20584845Svikram } 20591991Sheppo 20601991Sheppo mutex_enter(&(DEVI(dip)->devi_lock)); 20611991Sheppo if (!DEVI_IS_DEVICE_OFFLINE(dip)) 20621991Sheppo DEVI_SET_DEVICE_OFFLINE(dip); 20631991Sheppo mutex_exit(&(DEVI(dip)->devi_lock)); 20641991Sheppo 20654845Svikram return (DDI_SUCCESS); 20664845Svikram } 20674845Svikram 20684845Svikram typedef struct result { 20694845Svikram char *path; 20704845Svikram int result; 20714845Svikram } result_t; 20724845Svikram 20734845Svikram static int 20744845Svikram dip_set_offline(dev_info_t *dip, void *arg) 20754845Svikram { 20764845Svikram int end; 20774845Svikram result_t *resp = (result_t *)arg; 20784845Svikram 20794845Svikram ASSERT(dip); 20804845Svikram ASSERT(resp); 20814845Svikram 20824845Svikram /* 20834845Svikram * We stop the walk if e_ddi_offline_notify() returns 20844845Svikram * failure, because this implies that one or more consumers 20854845Svikram * (either LDI or contract based) has blocked the offline. 20864845Svikram * So there is no point in conitnuing the walk 20874845Svikram */ 20884845Svikram if (e_ddi_offline_notify(dip) == DDI_FAILURE) { 20894845Svikram resp->result = DDI_FAILURE; 20904845Svikram return (DDI_WALK_TERMINATE); 20914845Svikram } 20924845Svikram 20934845Svikram /* 20944845Svikram * If set_infant_dip_offline() returns failure, it implies 20954845Svikram * that we failed to set a particular dip offline. This 20964845Svikram * does not imply that the offline as a whole should fail. 20974845Svikram * We want to do the best we can, so we continue the walk. 20984845Svikram */ 20994845Svikram if (set_infant_dip_offline(dip, resp->path) == DDI_SUCCESS) 21004845Svikram end = DDI_SUCCESS; 21014845Svikram else 21024845Svikram end = DDI_FAILURE; 21034845Svikram 21044845Svikram e_ddi_offline_finalize(dip, end); 21054845Svikram 21061991Sheppo return (DDI_WALK_CONTINUE); 21071991Sheppo } 21081991Sheppo 21094845Svikram /* 21104845Svikram * The call to e_ddi_offline_notify() exists for the 21114845Svikram * unlikely error case that a branch we are trying to 21124845Svikram * create already exists and has device contracts or LDI 21134845Svikram * event callbacks against it. 21144845Svikram * 21154845Svikram * We allow create to succeed for such branches only if 21164845Svikram * no constraints block the offline. 21174845Svikram */ 21184845Svikram static int 21194845Svikram branch_set_offline(dev_info_t *dip, char *path) 21204845Svikram { 21214845Svikram int circ; 21224845Svikram int end; 21234845Svikram result_t res; 21244845Svikram 21254845Svikram 21264845Svikram if (e_ddi_offline_notify(dip) == DDI_FAILURE) { 21274845Svikram return (DDI_FAILURE); 21284845Svikram } 21294845Svikram 21304845Svikram if (set_infant_dip_offline(dip, path) == DDI_SUCCESS) 21314845Svikram end = DDI_SUCCESS; 21324845Svikram else 21334845Svikram end = DDI_FAILURE; 21344845Svikram 21354845Svikram e_ddi_offline_finalize(dip, end); 21364845Svikram 21374845Svikram if (end == DDI_FAILURE) 21384845Svikram return (DDI_FAILURE); 21394845Svikram 21404845Svikram res.result = DDI_SUCCESS; 21414845Svikram res.path = path; 21424845Svikram 21434845Svikram ndi_devi_enter(dip, &circ); 21444845Svikram ddi_walk_devs(ddi_get_child(dip), dip_set_offline, &res); 21454845Svikram ndi_devi_exit(dip, circ); 21464845Svikram 21474845Svikram return (res.result); 21484845Svikram } 21494845Svikram 21501991Sheppo /*ARGSUSED*/ 21511991Sheppo static int 21521991Sheppo create_prom_branch(void *arg, int has_changed) 21531991Sheppo { 21544845Svikram int circ; 21551991Sheppo int exists, rv; 21561991Sheppo pnode_t nodeid; 21571991Sheppo struct ptnode *tnp; 21581991Sheppo dev_info_t *dip; 21591991Sheppo struct pta *ap = arg; 21601991Sheppo devi_branch_t *bp; 21614845Svikram char *path; 21621991Sheppo 21631991Sheppo ASSERT(ap); 21641991Sheppo ASSERT(ap->fdip == NULL); 21651991Sheppo ASSERT(ap->pdip && ndi_dev_is_prom_node(ap->pdip)); 21661991Sheppo 21671991Sheppo bp = ap->bp; 21681991Sheppo 21691991Sheppo nodeid = ddi_get_nodeid(ap->pdip); 21701991Sheppo if (nodeid == OBP_NONODE || nodeid == OBP_BADNODE) { 21711991Sheppo cmn_err(CE_WARN, "create_prom_branch: invalid " 21721991Sheppo "nodeid: 0x%x", nodeid); 21731991Sheppo return (EINVAL); 21741991Sheppo } 21751991Sheppo 21761991Sheppo ap->head = NULL; 21771991Sheppo 21781991Sheppo nodeid = prom_childnode(nodeid); 21791991Sheppo while (nodeid != OBP_NONODE && nodeid != OBP_BADNODE) { 21801991Sheppo visit_node(nodeid, ap); 21811991Sheppo nodeid = prom_nextnode(nodeid); 21821991Sheppo } 21831991Sheppo 21841991Sheppo if (ap->head == NULL) 21851991Sheppo return (ENODEV); 21861991Sheppo 21874845Svikram path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 21881991Sheppo rv = 0; 21891991Sheppo while ((tnp = ap->head) != NULL) { 21901991Sheppo ap->head = tnp->next; 21911991Sheppo 21921991Sheppo ndi_devi_enter(ap->pdip, &circ); 21931991Sheppo 21941991Sheppo /* 21951991Sheppo * Check if the branch already exists. 21961991Sheppo */ 21971991Sheppo exists = 0; 21981991Sheppo dip = e_ddi_nodeid_to_dip(tnp->nodeid); 21991991Sheppo if (dip != NULL) { 22001991Sheppo exists = 1; 22011991Sheppo 22021991Sheppo /* Parent is held busy, so release hold */ 22031991Sheppo ndi_rele_devi(dip); 22041991Sheppo #ifdef DEBUG 22051991Sheppo cmn_err(CE_WARN, "create_prom_branch: dip(%p) exists" 22061991Sheppo " for nodeid 0x%x", (void *)dip, tnp->nodeid); 22071991Sheppo #endif 22081991Sheppo } else { 22091991Sheppo dip = i_ddi_create_branch(ap->pdip, tnp->nodeid); 22101991Sheppo } 22111991Sheppo 22121991Sheppo kmem_free(tnp, sizeof (struct ptnode)); 22131991Sheppo 22144845Svikram /* 22154845Svikram * Hold the branch if it is not already held 22164845Svikram */ 22174845Svikram if (dip && !exists) { 22184845Svikram e_ddi_branch_hold(dip); 22194845Svikram } 22204845Svikram 22214845Svikram ASSERT(dip == NULL || e_ddi_branch_held(dip)); 22224845Svikram 22234845Svikram /* 22244845Svikram * Set all dips in the newly created branch offline so that 22254845Svikram * only a "configure" operation can attach 22264845Svikram * the branch 22274845Svikram */ 22284845Svikram if (dip == NULL || branch_set_offline(dip, path) 22294845Svikram == DDI_FAILURE) { 22301991Sheppo ndi_devi_exit(ap->pdip, circ); 22311991Sheppo rv = EIO; 22321991Sheppo continue; 22331991Sheppo } 22341991Sheppo 22351991Sheppo ASSERT(ddi_get_parent(dip) == ap->pdip); 22361991Sheppo 22371991Sheppo ndi_devi_exit(ap->pdip, circ); 22381991Sheppo 22391991Sheppo if (ap->flags & DEVI_BRANCH_CONFIGURE) { 22401991Sheppo int error = e_ddi_branch_configure(dip, &ap->fdip, 0); 22411991Sheppo if (error && rv == 0) 22421991Sheppo rv = error; 22431991Sheppo } 22441991Sheppo 22451991Sheppo /* 22461991Sheppo * Invoke devi_branch_callback() (if it exists) only for 22471991Sheppo * newly created branches 22481991Sheppo */ 22491991Sheppo if (bp->devi_branch_callback && !exists) 22501991Sheppo bp->devi_branch_callback(dip, bp->arg, 0); 22511991Sheppo } 22521991Sheppo 22534845Svikram kmem_free(path, MAXPATHLEN); 22544845Svikram 22551991Sheppo return (rv); 22561991Sheppo } 22571991Sheppo 22581991Sheppo static int 22591991Sheppo sid_node_create(dev_info_t *pdip, devi_branch_t *bp, dev_info_t **rdipp) 22601991Sheppo { 22611991Sheppo int rv, circ, len; 22624845Svikram int i, flags, ret; 22631991Sheppo dev_info_t *dip; 22641991Sheppo char *nbuf; 22654845Svikram char *path; 22661991Sheppo static const char *noname = "<none>"; 22671991Sheppo 22681991Sheppo ASSERT(pdip); 22691991Sheppo ASSERT(DEVI_BUSY_OWNED(pdip)); 22701991Sheppo 22711991Sheppo flags = 0; 22721991Sheppo 22731991Sheppo /* 22741991Sheppo * Creating the root of a branch ? 22751991Sheppo */ 22761991Sheppo if (rdipp) { 22771991Sheppo *rdipp = NULL; 22781991Sheppo flags = DEVI_BRANCH_ROOT; 22791991Sheppo } 22801991Sheppo 22811991Sheppo ndi_devi_alloc_sleep(pdip, (char *)noname, DEVI_SID_NODEID, &dip); 22821991Sheppo rv = bp->create.sid_branch_create(dip, bp->arg, flags); 22831991Sheppo 22841991Sheppo nbuf = kmem_alloc(OBP_MAXDRVNAME, KM_SLEEP); 22851991Sheppo 22861991Sheppo if (rv == DDI_WALK_ERROR) { 22871991Sheppo cmn_err(CE_WARN, "e_ddi_branch_create: Error setting" 22881991Sheppo " properties on devinfo node %p", (void *)dip); 22891991Sheppo goto fail; 22901991Sheppo } 22911991Sheppo 22921991Sheppo len = OBP_MAXDRVNAME; 22931991Sheppo if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 22941991Sheppo DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "name", nbuf, &len) 22951991Sheppo != DDI_PROP_SUCCESS) { 22961991Sheppo cmn_err(CE_WARN, "e_ddi_branch_create: devinfo node %p has" 22971991Sheppo "no name property", (void *)dip); 22981991Sheppo goto fail; 22991991Sheppo } 23001991Sheppo 23011991Sheppo ASSERT(i_ddi_node_state(dip) == DS_PROTO); 23021991Sheppo if (ndi_devi_set_nodename(dip, nbuf, 0) != NDI_SUCCESS) { 23031991Sheppo cmn_err(CE_WARN, "e_ddi_branch_create: cannot set name (%s)" 23041991Sheppo " for devinfo node %p", nbuf, (void *)dip); 23051991Sheppo goto fail; 23061991Sheppo } 23071991Sheppo 23081991Sheppo kmem_free(nbuf, OBP_MAXDRVNAME); 23091991Sheppo 23101991Sheppo /* 23111991Sheppo * Ignore bind failures just like boot does 23121991Sheppo */ 23131991Sheppo (void) ndi_devi_bind_driver(dip, 0); 23141991Sheppo 23151991Sheppo switch (rv) { 23161991Sheppo case DDI_WALK_CONTINUE: 23171991Sheppo case DDI_WALK_PRUNESIB: 23181991Sheppo ndi_devi_enter(dip, &circ); 23191991Sheppo 23201991Sheppo i = DDI_WALK_CONTINUE; 23211991Sheppo for (; i == DDI_WALK_CONTINUE; ) { 23221991Sheppo i = sid_node_create(dip, bp, NULL); 23231991Sheppo } 23241991Sheppo 23251991Sheppo ASSERT(i == DDI_WALK_ERROR || i == DDI_WALK_PRUNESIB); 23261991Sheppo if (i == DDI_WALK_ERROR) 23271991Sheppo rv = i; 23281991Sheppo /* 23291991Sheppo * If PRUNESIB stop creating siblings 23301991Sheppo * of dip's child. Subsequent walk behavior 23311991Sheppo * is determined by rv returned by dip. 23321991Sheppo */ 23331991Sheppo 23341991Sheppo ndi_devi_exit(dip, circ); 23351991Sheppo break; 23361991Sheppo case DDI_WALK_TERMINATE: 23371991Sheppo /* 23381991Sheppo * Don't create children and ask our parent 23391991Sheppo * to not create siblings either. 23401991Sheppo */ 23411991Sheppo rv = DDI_WALK_PRUNESIB; 23421991Sheppo break; 23431991Sheppo case DDI_WALK_PRUNECHILD: 23441991Sheppo /* 23451991Sheppo * Don't create children, but ask parent to continue 23461991Sheppo * with siblings. 23471991Sheppo */ 23481991Sheppo rv = DDI_WALK_CONTINUE; 23491991Sheppo break; 23501991Sheppo default: 23511991Sheppo ASSERT(0); 23521991Sheppo break; 23531991Sheppo } 23541991Sheppo 23551991Sheppo if (rdipp) 23561991Sheppo *rdipp = dip; 23571991Sheppo 23581991Sheppo /* 23594845Svikram * Set device offline - only the "configure" op should cause an attach. 23604845Svikram * Note that it is safe to set the dip offline without checking 23614845Svikram * for either device contract or layered driver (LDI) based constraints 23624845Svikram * since there cannot be any contracts or LDI opens of this device. 23634845Svikram * This is because this node is a newly created dip with the parent busy 23644845Svikram * held, so no other thread can come in and attach this dip. A dip that 23654845Svikram * has never been attached cannot have contracts since by definition 23664845Svikram * a device contract (an agreement between a process and a device minor 23674845Svikram * node) can only be created against a device that has minor nodes 23684845Svikram * i.e is attached. Similarly an LDI open will only succeed if the 23694845Svikram * dip is attached. We assert below that the dip is not attached. 23701991Sheppo */ 23714845Svikram ASSERT(i_ddi_node_state(dip) < DS_ATTACHED); 23724845Svikram path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 23734845Svikram ret = set_infant_dip_offline(dip, path); 23744845Svikram ASSERT(ret == DDI_SUCCESS); 23754845Svikram kmem_free(path, MAXPATHLEN); 23761991Sheppo 23771991Sheppo return (rv); 23781991Sheppo fail: 23791991Sheppo (void) ndi_devi_free(dip); 23801991Sheppo kmem_free(nbuf, OBP_MAXDRVNAME); 23811991Sheppo return (DDI_WALK_ERROR); 23821991Sheppo } 23831991Sheppo 23841991Sheppo static int 23851991Sheppo create_sid_branch( 23861991Sheppo dev_info_t *pdip, 23871991Sheppo devi_branch_t *bp, 23881991Sheppo dev_info_t **dipp, 23891991Sheppo uint_t flags) 23901991Sheppo { 23911991Sheppo int rv = 0, state = DDI_WALK_CONTINUE; 23921991Sheppo dev_info_t *rdip; 23931991Sheppo 23941991Sheppo while (state == DDI_WALK_CONTINUE) { 23951991Sheppo int circ; 23961991Sheppo 23971991Sheppo ndi_devi_enter(pdip, &circ); 23981991Sheppo 23991991Sheppo state = sid_node_create(pdip, bp, &rdip); 24001991Sheppo if (rdip == NULL) { 24011991Sheppo ndi_devi_exit(pdip, circ); 24021991Sheppo ASSERT(state == DDI_WALK_ERROR); 24031991Sheppo break; 24041991Sheppo } 24051991Sheppo 24061991Sheppo e_ddi_branch_hold(rdip); 24071991Sheppo 24081991Sheppo ndi_devi_exit(pdip, circ); 24091991Sheppo 24101991Sheppo if (flags & DEVI_BRANCH_CONFIGURE) { 24111991Sheppo int error = e_ddi_branch_configure(rdip, dipp, 0); 24121991Sheppo if (error && rv == 0) 24131991Sheppo rv = error; 24141991Sheppo } 24151991Sheppo 24161991Sheppo /* 24171991Sheppo * devi_branch_callback() is optional 24181991Sheppo */ 24191991Sheppo if (bp->devi_branch_callback) 24201991Sheppo bp->devi_branch_callback(rdip, bp->arg, 0); 24211991Sheppo } 24221991Sheppo 24231991Sheppo ASSERT(state == DDI_WALK_ERROR || state == DDI_WALK_PRUNESIB); 24241991Sheppo 24251991Sheppo return (state == DDI_WALK_ERROR ? EIO : rv); 24261991Sheppo } 24271991Sheppo 24281991Sheppo int 24291991Sheppo e_ddi_branch_create( 24301991Sheppo dev_info_t *pdip, 24311991Sheppo devi_branch_t *bp, 24321991Sheppo dev_info_t **dipp, 24331991Sheppo uint_t flags) 24341991Sheppo { 24351991Sheppo int prom_devi, sid_devi, error; 24361991Sheppo 24371991Sheppo if (pdip == NULL || bp == NULL || bp->type == 0) 24381991Sheppo return (EINVAL); 24391991Sheppo 24401991Sheppo prom_devi = (bp->type == DEVI_BRANCH_PROM) ? 1 : 0; 24411991Sheppo sid_devi = (bp->type == DEVI_BRANCH_SID) ? 1 : 0; 24421991Sheppo 24431991Sheppo if (prom_devi && bp->create.prom_branch_select == NULL) 24441991Sheppo return (EINVAL); 24451991Sheppo else if (sid_devi && bp->create.sid_branch_create == NULL) 24461991Sheppo return (EINVAL); 24471991Sheppo else if (!prom_devi && !sid_devi) 24481991Sheppo return (EINVAL); 24491991Sheppo 24501991Sheppo if (flags & DEVI_BRANCH_EVENT) 24511991Sheppo return (EINVAL); 24521991Sheppo 24531991Sheppo if (prom_devi) { 24541991Sheppo struct pta pta = {0}; 24551991Sheppo 24561991Sheppo pta.pdip = pdip; 24571991Sheppo pta.bp = bp; 24581991Sheppo pta.flags = flags; 24591991Sheppo 24601991Sheppo error = prom_tree_access(create_prom_branch, &pta, NULL); 24611991Sheppo 24621991Sheppo if (dipp) 24631991Sheppo *dipp = pta.fdip; 24641991Sheppo else if (pta.fdip) 24651991Sheppo ndi_rele_devi(pta.fdip); 24661991Sheppo } else { 24671991Sheppo error = create_sid_branch(pdip, bp, dipp, flags); 24681991Sheppo } 24691991Sheppo 24701991Sheppo return (error); 24711991Sheppo } 24721991Sheppo 24731991Sheppo int 24741991Sheppo e_ddi_branch_configure(dev_info_t *rdip, dev_info_t **dipp, uint_t flags) 24751991Sheppo { 24769159SEthindra.Ramamurthy@Sun.COM int rv; 24771991Sheppo char *devnm; 24781991Sheppo dev_info_t *pdip; 24791991Sheppo 24801991Sheppo if (dipp) 24811991Sheppo *dipp = NULL; 24821991Sheppo 24831991Sheppo if (rdip == NULL || flags != 0 || (flags & DEVI_BRANCH_EVENT)) 24841991Sheppo return (EINVAL); 24851991Sheppo 24861991Sheppo pdip = ddi_get_parent(rdip); 24871991Sheppo 24889159SEthindra.Ramamurthy@Sun.COM ndi_hold_devi(pdip); 24891991Sheppo 24901991Sheppo if (!e_ddi_branch_held(rdip)) { 24919159SEthindra.Ramamurthy@Sun.COM ndi_rele_devi(pdip); 24921991Sheppo cmn_err(CE_WARN, "e_ddi_branch_configure: " 24931991Sheppo "dip(%p) not held", (void *)rdip); 24941991Sheppo return (EINVAL); 24951991Sheppo } 24961991Sheppo 24971991Sheppo if (i_ddi_node_state(rdip) < DS_INITIALIZED) { 24981991Sheppo /* 24991991Sheppo * First attempt to bind a driver. If we fail, return 25001991Sheppo * success (On some platforms, dips for some device 25011991Sheppo * types (CPUs) may not have a driver) 25021991Sheppo */ 25031991Sheppo if (ndi_devi_bind_driver(rdip, 0) != NDI_SUCCESS) { 25049159SEthindra.Ramamurthy@Sun.COM ndi_rele_devi(pdip); 25051991Sheppo return (0); 25061991Sheppo } 25071991Sheppo 25081991Sheppo if (ddi_initchild(pdip, rdip) != DDI_SUCCESS) { 25091991Sheppo rv = NDI_FAILURE; 25101991Sheppo goto out; 25111991Sheppo } 25121991Sheppo } 25131991Sheppo 25141991Sheppo ASSERT(i_ddi_node_state(rdip) >= DS_INITIALIZED); 25151991Sheppo 25161991Sheppo devnm = kmem_alloc(MAXNAMELEN + 1, KM_SLEEP); 25171991Sheppo 25181991Sheppo (void) ddi_deviname(rdip, devnm); 25191991Sheppo 25201991Sheppo if ((rv = ndi_devi_config_one(pdip, devnm+1, &rdip, 25211991Sheppo NDI_DEVI_ONLINE | NDI_CONFIG)) == NDI_SUCCESS) { 25221991Sheppo /* release hold from ndi_devi_config_one() */ 25231991Sheppo ndi_rele_devi(rdip); 25241991Sheppo } 25251991Sheppo 25261991Sheppo kmem_free(devnm, MAXNAMELEN + 1); 25271991Sheppo out: 25289159SEthindra.Ramamurthy@Sun.COM if (rv != NDI_SUCCESS && dipp && rdip) { 25291991Sheppo ndi_hold_devi(rdip); 25301991Sheppo *dipp = rdip; 25311991Sheppo } 25329159SEthindra.Ramamurthy@Sun.COM ndi_rele_devi(pdip); 25331991Sheppo return (ndi2errno(rv)); 25341991Sheppo } 25351991Sheppo 25361991Sheppo void 25371991Sheppo e_ddi_branch_hold(dev_info_t *rdip) 25381991Sheppo { 25391991Sheppo if (e_ddi_branch_held(rdip)) { 25401991Sheppo cmn_err(CE_WARN, "e_ddi_branch_hold: branch already held"); 25411991Sheppo return; 25421991Sheppo } 25431991Sheppo 25441991Sheppo mutex_enter(&DEVI(rdip)->devi_lock); 25451991Sheppo if ((DEVI(rdip)->devi_flags & DEVI_BRANCH_HELD) == 0) { 25461991Sheppo DEVI(rdip)->devi_flags |= DEVI_BRANCH_HELD; 25471991Sheppo DEVI(rdip)->devi_ref++; 25481991Sheppo } 25491991Sheppo ASSERT(DEVI(rdip)->devi_ref > 0); 25501991Sheppo mutex_exit(&DEVI(rdip)->devi_lock); 25511991Sheppo } 25521991Sheppo 25531991Sheppo int 25541991Sheppo e_ddi_branch_held(dev_info_t *rdip) 25551991Sheppo { 25561991Sheppo int rv = 0; 25571991Sheppo 25581991Sheppo mutex_enter(&DEVI(rdip)->devi_lock); 25591991Sheppo if ((DEVI(rdip)->devi_flags & DEVI_BRANCH_HELD) && 25601991Sheppo DEVI(rdip)->devi_ref > 0) { 25611991Sheppo rv = 1; 25621991Sheppo } 25631991Sheppo mutex_exit(&DEVI(rdip)->devi_lock); 25641991Sheppo 25651991Sheppo return (rv); 25661991Sheppo } 25671991Sheppo void 25681991Sheppo e_ddi_branch_rele(dev_info_t *rdip) 25691991Sheppo { 25701991Sheppo mutex_enter(&DEVI(rdip)->devi_lock); 25711991Sheppo DEVI(rdip)->devi_flags &= ~DEVI_BRANCH_HELD; 25721991Sheppo DEVI(rdip)->devi_ref--; 25731991Sheppo mutex_exit(&DEVI(rdip)->devi_lock); 25741991Sheppo } 25751991Sheppo 25761991Sheppo int 25771991Sheppo e_ddi_branch_unconfigure( 25781991Sheppo dev_info_t *rdip, 25791991Sheppo dev_info_t **dipp, 25801991Sheppo uint_t flags) 25811991Sheppo { 25821991Sheppo int circ, rv; 25831991Sheppo int destroy; 25841991Sheppo char *devnm; 25851991Sheppo uint_t nflags; 25861991Sheppo dev_info_t *pdip; 25871991Sheppo 25881991Sheppo if (dipp) 25891991Sheppo *dipp = NULL; 25901991Sheppo 25911991Sheppo if (rdip == NULL) 25921991Sheppo return (EINVAL); 25931991Sheppo 25941991Sheppo pdip = ddi_get_parent(rdip); 25951991Sheppo 25961991Sheppo ASSERT(pdip); 25971991Sheppo 25981991Sheppo /* 25991991Sheppo * Check if caller holds pdip busy - can cause deadlocks during 26001991Sheppo * devfs_clean() 26011991Sheppo */ 26021991Sheppo if (DEVI_BUSY_OWNED(pdip)) { 26031991Sheppo cmn_err(CE_WARN, "e_ddi_branch_unconfigure: failed: parent" 26041991Sheppo " devinfo node(%p) is busy held", (void *)pdip); 26051991Sheppo return (EINVAL); 26061991Sheppo } 26071991Sheppo 26081991Sheppo destroy = (flags & DEVI_BRANCH_DESTROY) ? 1 : 0; 26091991Sheppo 26101991Sheppo devnm = kmem_alloc(MAXNAMELEN + 1, KM_SLEEP); 26111991Sheppo 26121991Sheppo ndi_devi_enter(pdip, &circ); 26131991Sheppo (void) ddi_deviname(rdip, devnm); 26141991Sheppo ndi_devi_exit(pdip, circ); 26151991Sheppo 26161991Sheppo /* 26171991Sheppo * ddi_deviname() returns a component name with / prepended. 26181991Sheppo */ 26194411Svikram (void) devfs_clean(pdip, devnm + 1, DV_CLEAN_FORCE); 26201991Sheppo 26211991Sheppo ndi_devi_enter(pdip, &circ); 26221991Sheppo 26231991Sheppo /* 26241991Sheppo * Recreate device name as it may have changed state (init/uninit) 26251991Sheppo * when parent busy lock was dropped for devfs_clean() 26261991Sheppo */ 26271991Sheppo (void) ddi_deviname(rdip, devnm); 26281991Sheppo 26291991Sheppo if (!e_ddi_branch_held(rdip)) { 26301991Sheppo kmem_free(devnm, MAXNAMELEN + 1); 26311991Sheppo ndi_devi_exit(pdip, circ); 26321991Sheppo cmn_err(CE_WARN, "e_ddi_%s_branch: dip(%p) not held", 26331991Sheppo destroy ? "destroy" : "unconfigure", (void *)rdip); 26341991Sheppo return (EINVAL); 26351991Sheppo } 26361991Sheppo 26371991Sheppo /* 26381991Sheppo * Release hold on the branch. This is ok since we are holding the 26391991Sheppo * parent busy. If rdip is not removed, we must do a hold on the 26401991Sheppo * branch before returning. 26411991Sheppo */ 26421991Sheppo e_ddi_branch_rele(rdip); 26431991Sheppo 26441991Sheppo nflags = NDI_DEVI_OFFLINE; 26451991Sheppo if (destroy || (flags & DEVI_BRANCH_DESTROY)) { 26461991Sheppo nflags |= NDI_DEVI_REMOVE; 26471991Sheppo destroy = 1; 26481991Sheppo } else { 26491991Sheppo nflags |= NDI_UNCONFIG; /* uninit but don't remove */ 26501991Sheppo } 26511991Sheppo 26521991Sheppo if (flags & DEVI_BRANCH_EVENT) 26531991Sheppo nflags |= NDI_POST_EVENT; 26541991Sheppo 26551991Sheppo if (i_ddi_devi_attached(pdip) && 26561991Sheppo (i_ddi_node_state(rdip) >= DS_INITIALIZED)) { 26571991Sheppo rv = ndi_devi_unconfig_one(pdip, devnm+1, dipp, nflags); 26581991Sheppo } else { 26591991Sheppo rv = e_ddi_devi_unconfig(rdip, dipp, nflags); 26601991Sheppo if (rv == NDI_SUCCESS) { 26611991Sheppo ASSERT(!destroy || ddi_get_child(rdip) == NULL); 26621991Sheppo rv = ndi_devi_offline(rdip, nflags); 26631991Sheppo } 26641991Sheppo } 26651991Sheppo 26661991Sheppo if (!destroy || rv != NDI_SUCCESS) { 26671991Sheppo /* The dip still exists, so do a hold */ 26681991Sheppo e_ddi_branch_hold(rdip); 26691991Sheppo } 26701991Sheppo out: 26711991Sheppo kmem_free(devnm, MAXNAMELEN + 1); 26721991Sheppo ndi_devi_exit(pdip, circ); 26731991Sheppo return (ndi2errno(rv)); 26741991Sheppo } 26751991Sheppo 26761991Sheppo int 26771991Sheppo e_ddi_branch_destroy(dev_info_t *rdip, dev_info_t **dipp, uint_t flag) 26781991Sheppo { 26791991Sheppo return (e_ddi_branch_unconfigure(rdip, dipp, 26801991Sheppo flag|DEVI_BRANCH_DESTROY)); 26811991Sheppo } 26821991Sheppo 26831991Sheppo /* 26841991Sheppo * Number of chains for hash table 26851991Sheppo */ 26861991Sheppo #define NUMCHAINS 17 26871991Sheppo 26881991Sheppo /* 26891991Sheppo * Devinfo busy arg 26901991Sheppo */ 26911991Sheppo struct devi_busy { 26921991Sheppo int dv_total; 26931991Sheppo int s_total; 26941991Sheppo mod_hash_t *dv_hash; 26951991Sheppo mod_hash_t *s_hash; 26961991Sheppo int (*callback)(dev_info_t *, void *, uint_t); 26971991Sheppo void *arg; 26981991Sheppo }; 26991991Sheppo 27001991Sheppo static int 27011991Sheppo visit_dip(dev_info_t *dip, void *arg) 27021991Sheppo { 27031991Sheppo uintptr_t sbusy, dvbusy, ref; 27041991Sheppo struct devi_busy *bsp = arg; 27051991Sheppo 27061991Sheppo ASSERT(bsp->callback); 27071991Sheppo 27081991Sheppo /* 27091991Sheppo * A dip cannot be busy if its reference count is 0 27101991Sheppo */ 27111991Sheppo if ((ref = e_ddi_devi_holdcnt(dip)) == 0) { 27121991Sheppo return (bsp->callback(dip, bsp->arg, 0)); 27131991Sheppo } 27141991Sheppo 27151991Sheppo if (mod_hash_find(bsp->dv_hash, dip, (mod_hash_val_t *)&dvbusy)) 27161991Sheppo dvbusy = 0; 27171991Sheppo 27181991Sheppo /* 27191991Sheppo * To catch device opens currently maintained on specfs common snodes. 27201991Sheppo */ 27211991Sheppo if (mod_hash_find(bsp->s_hash, dip, (mod_hash_val_t *)&sbusy)) 27221991Sheppo sbusy = 0; 27231991Sheppo 27241991Sheppo #ifdef DEBUG 27251991Sheppo if (ref < sbusy || ref < dvbusy) { 27261991Sheppo cmn_err(CE_WARN, "dip(%p): sopen = %lu, dvopen = %lu " 27271991Sheppo "dip ref = %lu\n", (void *)dip, sbusy, dvbusy, ref); 27281991Sheppo } 27291991Sheppo #endif 27301991Sheppo 27311991Sheppo dvbusy = (sbusy > dvbusy) ? sbusy : dvbusy; 27321991Sheppo 27331991Sheppo return (bsp->callback(dip, bsp->arg, dvbusy)); 27341991Sheppo } 27351991Sheppo 27361991Sheppo static int 27371991Sheppo visit_snode(struct snode *sp, void *arg) 27381991Sheppo { 27391991Sheppo uintptr_t sbusy; 27401991Sheppo dev_info_t *dip; 27411991Sheppo int count; 27421991Sheppo struct devi_busy *bsp = arg; 27431991Sheppo 27441991Sheppo ASSERT(sp); 27451991Sheppo 27461991Sheppo /* 27471991Sheppo * The stable lock is held. This prevents 27481991Sheppo * the snode and its associated dip from 27491991Sheppo * going away. 27501991Sheppo */ 27511991Sheppo dip = NULL; 27521991Sheppo count = spec_devi_open_count(sp, &dip); 27531991Sheppo 27541991Sheppo if (count <= 0) 27551991Sheppo return (DDI_WALK_CONTINUE); 27561991Sheppo 27571991Sheppo ASSERT(dip); 27581991Sheppo 27591991Sheppo if (mod_hash_remove(bsp->s_hash, dip, (mod_hash_val_t *)&sbusy)) 27601991Sheppo sbusy = count; 27611991Sheppo else 27621991Sheppo sbusy += count; 27631991Sheppo 27641991Sheppo if (mod_hash_insert(bsp->s_hash, dip, (mod_hash_val_t)sbusy)) { 27651991Sheppo cmn_err(CE_WARN, "%s: s_hash insert failed: dip=0x%p, " 27661991Sheppo "sbusy = %lu", "e_ddi_branch_referenced", 27671991Sheppo (void *)dip, sbusy); 27681991Sheppo } 27691991Sheppo 27701991Sheppo bsp->s_total += count; 27711991Sheppo 27721991Sheppo return (DDI_WALK_CONTINUE); 27731991Sheppo } 27741991Sheppo 27751991Sheppo static void 27761991Sheppo visit_dvnode(struct dv_node *dv, void *arg) 27771991Sheppo { 27781991Sheppo uintptr_t dvbusy; 27791991Sheppo uint_t count; 27801991Sheppo struct vnode *vp; 27811991Sheppo struct devi_busy *bsp = arg; 27821991Sheppo 27831991Sheppo ASSERT(dv && dv->dv_devi); 27841991Sheppo 27851991Sheppo vp = DVTOV(dv); 27861991Sheppo 27871991Sheppo mutex_enter(&vp->v_lock); 27881991Sheppo count = vp->v_count; 27891991Sheppo mutex_exit(&vp->v_lock); 27901991Sheppo 27911991Sheppo if (!count) 27921991Sheppo return; 27931991Sheppo 27941991Sheppo if (mod_hash_remove(bsp->dv_hash, dv->dv_devi, 27951991Sheppo (mod_hash_val_t *)&dvbusy)) 27961991Sheppo dvbusy = count; 27971991Sheppo else 27981991Sheppo dvbusy += count; 27991991Sheppo 28001991Sheppo if (mod_hash_insert(bsp->dv_hash, dv->dv_devi, 28011991Sheppo (mod_hash_val_t)dvbusy)) { 28021991Sheppo cmn_err(CE_WARN, "%s: dv_hash insert failed: dip=0x%p, " 28031991Sheppo "dvbusy=%lu", "e_ddi_branch_referenced", 28041991Sheppo (void *)dv->dv_devi, dvbusy); 28051991Sheppo } 28061991Sheppo 28071991Sheppo bsp->dv_total += count; 28081991Sheppo } 28091991Sheppo 28101991Sheppo /* 28111991Sheppo * Returns reference count on success or -1 on failure. 28121991Sheppo */ 28131991Sheppo int 28141991Sheppo e_ddi_branch_referenced( 28151991Sheppo dev_info_t *rdip, 28161991Sheppo int (*callback)(dev_info_t *dip, void *arg, uint_t ref), 28171991Sheppo void *arg) 28181991Sheppo { 28191991Sheppo int circ; 28201991Sheppo char *path; 28211991Sheppo dev_info_t *pdip; 28221991Sheppo struct devi_busy bsa = {0}; 28231991Sheppo 28241991Sheppo ASSERT(rdip); 28251991Sheppo 28261991Sheppo path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 28271991Sheppo 28281991Sheppo ndi_hold_devi(rdip); 28291991Sheppo 28301991Sheppo pdip = ddi_get_parent(rdip); 28311991Sheppo 28321991Sheppo ASSERT(pdip); 28331991Sheppo 28341991Sheppo /* 28351991Sheppo * Check if caller holds pdip busy - can cause deadlocks during 28361991Sheppo * devfs_walk() 28371991Sheppo */ 28381991Sheppo if (!e_ddi_branch_held(rdip) || DEVI_BUSY_OWNED(pdip)) { 28391991Sheppo cmn_err(CE_WARN, "e_ddi_branch_referenced: failed: " 28401991Sheppo "devinfo branch(%p) not held or parent busy held", 28411991Sheppo (void *)rdip); 28421991Sheppo ndi_rele_devi(rdip); 28431991Sheppo kmem_free(path, MAXPATHLEN); 28441991Sheppo return (-1); 28451991Sheppo } 28461991Sheppo 28471991Sheppo ndi_devi_enter(pdip, &circ); 28481991Sheppo (void) ddi_pathname(rdip, path); 28491991Sheppo ndi_devi_exit(pdip, circ); 28501991Sheppo 28511991Sheppo bsa.dv_hash = mod_hash_create_ptrhash("dv_node busy hash", NUMCHAINS, 28521991Sheppo mod_hash_null_valdtor, sizeof (struct dev_info)); 28531991Sheppo 28541991Sheppo bsa.s_hash = mod_hash_create_ptrhash("snode busy hash", NUMCHAINS, 28551991Sheppo mod_hash_null_valdtor, sizeof (struct snode)); 28561991Sheppo 28571991Sheppo if (devfs_walk(path, visit_dvnode, &bsa)) { 28581991Sheppo cmn_err(CE_WARN, "e_ddi_branch_referenced: " 28591991Sheppo "devfs walk failed for: %s", path); 28601991Sheppo kmem_free(path, MAXPATHLEN); 28611991Sheppo bsa.s_total = bsa.dv_total = -1; 28621991Sheppo goto out; 28631991Sheppo } 28641991Sheppo 28651991Sheppo kmem_free(path, MAXPATHLEN); 28661991Sheppo 28671991Sheppo /* 28681991Sheppo * Walk the snode table to detect device opens, which are currently 28691991Sheppo * maintained on specfs common snodes. 28701991Sheppo */ 28711991Sheppo spec_snode_walk(visit_snode, &bsa); 28721991Sheppo 28731991Sheppo if (callback == NULL) 28741991Sheppo goto out; 28751991Sheppo 28761991Sheppo bsa.callback = callback; 28771991Sheppo bsa.arg = arg; 28781991Sheppo 28791991Sheppo if (visit_dip(rdip, &bsa) == DDI_WALK_CONTINUE) { 28801991Sheppo ndi_devi_enter(rdip, &circ); 28811991Sheppo ddi_walk_devs(ddi_get_child(rdip), visit_dip, &bsa); 28821991Sheppo ndi_devi_exit(rdip, circ); 28831991Sheppo } 28841991Sheppo 28851991Sheppo out: 28861991Sheppo ndi_rele_devi(rdip); 28871991Sheppo mod_hash_destroy_ptrhash(bsa.s_hash); 28881991Sheppo mod_hash_destroy_ptrhash(bsa.dv_hash); 28891991Sheppo return (bsa.s_total > bsa.dv_total ? bsa.s_total : bsa.dv_total); 28901991Sheppo } 2891