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 */ 210Sstevel@tonic-gate /* 22*3752Scindi * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate /* 290Sstevel@tonic-gate * Fault Management for Nexus Device Drivers 300Sstevel@tonic-gate * 310Sstevel@tonic-gate * In addition to implementing and supporting Fault Management for Device 320Sstevel@tonic-gate * Drivers (ddifm.c), nexus drivers must support their children by 330Sstevel@tonic-gate * reporting FM capabilities, intializing interrupt block cookies 340Sstevel@tonic-gate * for error handling callbacks and caching mapped resources for lookup 350Sstevel@tonic-gate * during the detection of an IO transaction error. 360Sstevel@tonic-gate * 370Sstevel@tonic-gate * It is typically the nexus driver that receives an error indication 380Sstevel@tonic-gate * for a fault that may have occurred in the data path of an IO transaction. 390Sstevel@tonic-gate * Errors may be detected or received via an interrupt, a callback from 400Sstevel@tonic-gate * another subsystem (e.g. a cpu trap) or examination of control data. 410Sstevel@tonic-gate * 420Sstevel@tonic-gate * Upon detection of an error, the nexus has a responsibility to alert 430Sstevel@tonic-gate * its children of the error and the transaction associated with that 440Sstevel@tonic-gate * error. The actual implementation may vary depending upon the capabilities 450Sstevel@tonic-gate * of the nexus, its underlying hardware and its children. In this file, 460Sstevel@tonic-gate * we provide support for typical nexus driver fault management tasks. 470Sstevel@tonic-gate * 480Sstevel@tonic-gate * Fault Management Initialization 490Sstevel@tonic-gate * 500Sstevel@tonic-gate * Nexus drivers must implement two new busops, bus_fm_init() and 510Sstevel@tonic-gate * bus_fm_fini(). bus_fm_init() is called from a child nexus or device 520Sstevel@tonic-gate * driver and is expected to initialize any per-child state and return 530Sstevel@tonic-gate * the FM and error interrupt priority levels of the nexus driver. 540Sstevel@tonic-gate * Similarly, bus_fm_fini() is called by child drivers and should 550Sstevel@tonic-gate * clean-up any resources allocated during bus_fm_init(). 560Sstevel@tonic-gate * These functions are called from passive kernel context, typically from 570Sstevel@tonic-gate * driver attach(9F) and detach(9F) entry points. 580Sstevel@tonic-gate * 590Sstevel@tonic-gate * Error Handler Dispatching 600Sstevel@tonic-gate * 610Sstevel@tonic-gate * Nexus drivers implemented to support error handler capabilities 620Sstevel@tonic-gate * should invoke registered error handler callbacks for child drivers 630Sstevel@tonic-gate * thought to be involved in the error. 640Sstevel@tonic-gate * ndi_fm_handler_dispatch() is used to invoke 650Sstevel@tonic-gate * all error handlers and returns one of the following status 660Sstevel@tonic-gate * indications: 670Sstevel@tonic-gate * 680Sstevel@tonic-gate * DDI_FM_OK - No errors found by any child 690Sstevel@tonic-gate * DDI_FM_FATAL - one or more children have detected a fatal error 700Sstevel@tonic-gate * DDI_FM_NONFATAL - no fatal errors, but one or more children have 710Sstevel@tonic-gate * detected a non-fatal error 720Sstevel@tonic-gate * 730Sstevel@tonic-gate * ndi_fm_handler_dispatch() may be called in any context 740Sstevel@tonic-gate * subject to the constraints specified by the interrupt iblock cookie 750Sstevel@tonic-gate * returned during initialization. 760Sstevel@tonic-gate * 770Sstevel@tonic-gate * Protected Accesses 780Sstevel@tonic-gate * 790Sstevel@tonic-gate * When an access handle is mapped or a DMA handle is bound via the 800Sstevel@tonic-gate * standard busops, bus_map() or bus_dma_bindhdl(), a child driver 810Sstevel@tonic-gate * implemented to support DDI_FM_ACCCHK_CAPABLE or 820Sstevel@tonic-gate * DDI_FM_DMACHK_CAPABLE capabilites 830Sstevel@tonic-gate * expects the nexus to flag any errors detected for transactions 840Sstevel@tonic-gate * associated with the mapped or bound handles. 850Sstevel@tonic-gate * 860Sstevel@tonic-gate * Children nexus or device drivers will set the following flags 870Sstevel@tonic-gate * in their ddi_device_access or dma_attr_flags when requesting 880Sstevel@tonic-gate * the an access or DMA handle mapping: 890Sstevel@tonic-gate * 900Sstevel@tonic-gate * DDI_DMA_FLAGERR - nexus should set error status for any errors 910Sstevel@tonic-gate * detected for a failed DMA transaction. 920Sstevel@tonic-gate * DDI_ACC_FLAGERR - nexus should set error status for any errors 930Sstevel@tonic-gate * detected for a failed PIO transaction. 940Sstevel@tonic-gate * 950Sstevel@tonic-gate * A nexus is expected to provide additional error detection and 960Sstevel@tonic-gate * handling for handles with these flags set. 970Sstevel@tonic-gate * 980Sstevel@tonic-gate * Exclusive Bus Access 990Sstevel@tonic-gate * 1000Sstevel@tonic-gate * In cases where a driver requires a high level of fault tolerance 1010Sstevel@tonic-gate * for a programmed IO transaction, it is neccessary to grant exclusive 1020Sstevel@tonic-gate * access to the bus resource. Exclusivity guarantees that a fault 1030Sstevel@tonic-gate * resulting from a transaction on the bus can be easily traced and 1040Sstevel@tonic-gate * reported to the driver requesting the transaction. 1050Sstevel@tonic-gate * 1060Sstevel@tonic-gate * Nexus drivers must implement two new busops to support exclusive 1070Sstevel@tonic-gate * access, bus_fm_access_enter() and bus_fm_access_exit(). The IO 1080Sstevel@tonic-gate * framework will use these functions when it must set-up access 1090Sstevel@tonic-gate * handles that set devacc_attr_access to DDI_ACC_CAUTIOUS in 1100Sstevel@tonic-gate * their ddi_device_acc_attr_t request. 1110Sstevel@tonic-gate * 1120Sstevel@tonic-gate * Upon receipt of a bus_fm_access_enter() request, the nexus must prevent 1130Sstevel@tonic-gate * all other access requests until it receives bus_fm_access_exit() 1140Sstevel@tonic-gate * for the requested bus instance. bus_fm_access_enter() and 1150Sstevel@tonic-gate * bus_fm_access_exit() may be called from user, kernel or kernel 1160Sstevel@tonic-gate * interrupt context. 1170Sstevel@tonic-gate * 1180Sstevel@tonic-gate * Access and DMA Handle Caching 1190Sstevel@tonic-gate * 1200Sstevel@tonic-gate * To aid a nexus driver in associating access or DMA handles with 1210Sstevel@tonic-gate * a detected error, the nexus should cache all handles that are 1220Sstevel@tonic-gate * associated with DDI_ACC_FLAGERR, DDI_ACC_CAUTIOUS_ACC or 1230Sstevel@tonic-gate * DDI_DMA_FLAGERR requests from its children. ndi_fmc_insert() is 1240Sstevel@tonic-gate * called by a nexus to cache handles with the above protection flags 1250Sstevel@tonic-gate * and ndi_fmc_remove() is called when that handle is unmapped or 1260Sstevel@tonic-gate * unbound by the requesting child. ndi_fmc_insert() and 1270Sstevel@tonic-gate * ndi_fmc_remove() may be called from any user or kernel context. 1280Sstevel@tonic-gate * 1290Sstevel@tonic-gate * FM caches are allocated during ddi_fm_init() and maintained 1300Sstevel@tonic-gate * as an array of elements that may be on one of two lists: 1310Sstevel@tonic-gate * free or active. The free list is a singly-linked list of 1320Sstevel@tonic-gate * elements available for activity. ndi_fm_insert() moves the 1330Sstevel@tonic-gate * element at the head of the free to the active list. The active 1340Sstevel@tonic-gate * list is a doubly-linked searchable list. 1350Sstevel@tonic-gate * When a handle is unmapped or unbound, its associated cache 1360Sstevel@tonic-gate * entry is removed from the active list back to the free list. 1370Sstevel@tonic-gate * 1380Sstevel@tonic-gate * Upon detection of an error, the nexus may invoke ndi_fmc_error() to 1390Sstevel@tonic-gate * iterate over the handle cache of one or more of its FM compliant 1400Sstevel@tonic-gate * children. A comparison callback function is provided upon each 1410Sstevel@tonic-gate * invocation of ndi_fmc_error() to tell the IO framework if a 1420Sstevel@tonic-gate * handle is associated with an error. If so, the framework will 1430Sstevel@tonic-gate * set the error status for that handle before returning from 1440Sstevel@tonic-gate * ndi_fmc_error(). 1450Sstevel@tonic-gate * 1460Sstevel@tonic-gate * ndi_fmc_error() may be called in any context 1470Sstevel@tonic-gate * subject to the constraints specified by the interrupt iblock cookie 1480Sstevel@tonic-gate * returned during initialization of the nexus and its children. 1490Sstevel@tonic-gate * 1500Sstevel@tonic-gate */ 1510Sstevel@tonic-gate 1520Sstevel@tonic-gate #include <sys/types.h> 1530Sstevel@tonic-gate #include <sys/param.h> 1540Sstevel@tonic-gate #include <sys/debug.h> 1550Sstevel@tonic-gate #include <sys/sunddi.h> 1560Sstevel@tonic-gate #include <sys/sunndi.h> 1570Sstevel@tonic-gate #include <sys/ddi.h> 1580Sstevel@tonic-gate #include <sys/ndi_impldefs.h> 1590Sstevel@tonic-gate #include <sys/devctl.h> 1600Sstevel@tonic-gate #include <sys/nvpair.h> 1610Sstevel@tonic-gate #include <sys/ddifm.h> 1620Sstevel@tonic-gate #include <sys/ndifm.h> 1630Sstevel@tonic-gate #include <sys/spl.h> 1640Sstevel@tonic-gate #include <sys/sysmacros.h> 1650Sstevel@tonic-gate #include <sys/devops.h> 1660Sstevel@tonic-gate #include <sys/atomic.h> 1670Sstevel@tonic-gate #include <sys/fm/io/ddi.h> 1680Sstevel@tonic-gate 1690Sstevel@tonic-gate /* 1700Sstevel@tonic-gate * Allocate and initialize a fault management resource cache 1710Sstevel@tonic-gate * A fault management cache consists of a set of cache elements that 1720Sstevel@tonic-gate * may be on one of two lists: free or active. 1730Sstevel@tonic-gate * 1740Sstevel@tonic-gate * At creation time, every element but one is placed on the free list 1750Sstevel@tonic-gate * except for the first element. This element is reserved as the first 1760Sstevel@tonic-gate * element of the active list and serves as an anchor for the active 1770Sstevel@tonic-gate * list in ndi_fmc_insert() and ndi_fmc_remove(). In these functions, 1780Sstevel@tonic-gate * it is not neccessary to check for the existence or validity of 1790Sstevel@tonic-gate * the active list. 1800Sstevel@tonic-gate */ 1810Sstevel@tonic-gate void 1820Sstevel@tonic-gate i_ndi_fmc_create(ndi_fmc_t **fcpp, int qlen, ddi_iblock_cookie_t ibc) 1830Sstevel@tonic-gate { 1840Sstevel@tonic-gate ndi_fmc_t *fcp; 1850Sstevel@tonic-gate ndi_fmcentry_t *fep; 1860Sstevel@tonic-gate 1870Sstevel@tonic-gate ASSERT(qlen > 1); 1880Sstevel@tonic-gate 1890Sstevel@tonic-gate fcp = kmem_zalloc(sizeof (ndi_fmc_t), KM_SLEEP); 1900Sstevel@tonic-gate mutex_init(&fcp->fc_lock, NULL, MUTEX_DRIVER, ibc); 1913114Scindi mutex_init(&fcp->fc_free_lock, NULL, MUTEX_DRIVER, NULL); 1920Sstevel@tonic-gate 1930Sstevel@tonic-gate /* Preallocate and initialize entries for this fm cache */ 1940Sstevel@tonic-gate fcp->fc_elems = kmem_zalloc(qlen * sizeof (ndi_fmcentry_t), KM_SLEEP); 1950Sstevel@tonic-gate 1960Sstevel@tonic-gate fcp->fc_len = qlen; 1970Sstevel@tonic-gate 1980Sstevel@tonic-gate /* Intialize the active and free lists */ 1990Sstevel@tonic-gate fcp->fc_active = fcp->fc_tail = fcp->fc_elems; 2000Sstevel@tonic-gate fcp->fc_free = fcp->fc_elems + 1; 2010Sstevel@tonic-gate qlen--; 2020Sstevel@tonic-gate for (fep = fcp->fc_free; qlen > 1; qlen--) { 2030Sstevel@tonic-gate fep->fce_prev = fep + 1; 2040Sstevel@tonic-gate fep++; 2050Sstevel@tonic-gate } 2060Sstevel@tonic-gate 2070Sstevel@tonic-gate *fcpp = fcp; 2080Sstevel@tonic-gate } 2090Sstevel@tonic-gate 2100Sstevel@tonic-gate /* 2110Sstevel@tonic-gate * Destroy and resources associated with the given fault management cache. 2120Sstevel@tonic-gate */ 2130Sstevel@tonic-gate void 2140Sstevel@tonic-gate i_ndi_fmc_destroy(ndi_fmc_t *fcp) 2150Sstevel@tonic-gate { 2160Sstevel@tonic-gate if (fcp == NULL) 2170Sstevel@tonic-gate return; 2180Sstevel@tonic-gate 2190Sstevel@tonic-gate kmem_free(fcp->fc_elems, fcp->fc_len * sizeof (ndi_fmcentry_t)); 2200Sstevel@tonic-gate kmem_free(fcp, sizeof (ndi_fmc_t)); 2210Sstevel@tonic-gate } 2220Sstevel@tonic-gate 2230Sstevel@tonic-gate /* 2240Sstevel@tonic-gate * Grow an existing fault management cache by grow_sz number of entries 2250Sstevel@tonic-gate */ 2260Sstevel@tonic-gate static int 2270Sstevel@tonic-gate fmc_grow(ndi_fmc_t *fcp, int flag, int grow_sz) 2280Sstevel@tonic-gate { 2290Sstevel@tonic-gate int olen, nlen; 2300Sstevel@tonic-gate void *resource; 2310Sstevel@tonic-gate ndi_fmcentry_t *ncp, *oep, *nep, *nnep; 2320Sstevel@tonic-gate 2330Sstevel@tonic-gate ASSERT(grow_sz); 2343114Scindi ASSERT(MUTEX_HELD(&fcp->fc_free_lock)); 2350Sstevel@tonic-gate 2360Sstevel@tonic-gate /* Allocate a new cache */ 2370Sstevel@tonic-gate nlen = grow_sz + fcp->fc_len; 2380Sstevel@tonic-gate if ((ncp = kmem_zalloc(nlen * sizeof (ndi_fmcentry_t), 2390Sstevel@tonic-gate KM_NOSLEEP)) == NULL) 2400Sstevel@tonic-gate return (1); 2410Sstevel@tonic-gate 2420Sstevel@tonic-gate /* Migrate old cache to new cache */ 2430Sstevel@tonic-gate oep = fcp->fc_elems; 2440Sstevel@tonic-gate olen = fcp->fc_len; 2450Sstevel@tonic-gate for (nep = ncp; ; olen--) { 2460Sstevel@tonic-gate resource = nep->fce_resource = oep->fce_resource; 2470Sstevel@tonic-gate nep->fce_bus_specific = oep->fce_bus_specific; 2480Sstevel@tonic-gate if (resource) { 2490Sstevel@tonic-gate if (flag == DMA_HANDLE) { 2500Sstevel@tonic-gate ((ddi_dma_impl_t *)resource)-> 2510Sstevel@tonic-gate dmai_error.err_fep = nep; 2520Sstevel@tonic-gate } else if (flag == ACC_HANDLE) { 2530Sstevel@tonic-gate ((ddi_acc_impl_t *)resource)-> 2540Sstevel@tonic-gate ahi_err->err_fep = nep; 2550Sstevel@tonic-gate } 2560Sstevel@tonic-gate } 2570Sstevel@tonic-gate 2580Sstevel@tonic-gate /* 2590Sstevel@tonic-gate * This is the last entry. Set the tail pointer and 2600Sstevel@tonic-gate * terminate processing of the old cache. 2610Sstevel@tonic-gate */ 2620Sstevel@tonic-gate if (olen == 1) { 2630Sstevel@tonic-gate fcp->fc_tail = nep; 2640Sstevel@tonic-gate ++nep; 2650Sstevel@tonic-gate break; 2660Sstevel@tonic-gate } 2670Sstevel@tonic-gate 2680Sstevel@tonic-gate /* 2690Sstevel@tonic-gate * Set the next and previous pointer for the new cache 2700Sstevel@tonic-gate * entry. 2710Sstevel@tonic-gate */ 2720Sstevel@tonic-gate nnep = nep + 1; 2730Sstevel@tonic-gate nep->fce_next = nnep; 2740Sstevel@tonic-gate nnep->fce_prev = nep; 2750Sstevel@tonic-gate 2760Sstevel@tonic-gate /* Advance to the next entry */ 2770Sstevel@tonic-gate ++oep; 2780Sstevel@tonic-gate nep = nnep; 2790Sstevel@tonic-gate } 2800Sstevel@tonic-gate 2810Sstevel@tonic-gate /* Initialize and add remaining new cache entries to the free list */ 282*3752Scindi for (fcp->fc_free = nep; nlen > fcp->fc_len + 1; nlen--) { 2830Sstevel@tonic-gate nep->fce_prev = nep + 1; 2840Sstevel@tonic-gate nep++; 2850Sstevel@tonic-gate } 2860Sstevel@tonic-gate 287*3752Scindi oep = fcp->fc_elems; 288*3752Scindi olen = fcp->fc_len; 289*3752Scindi nlen = grow_sz + olen; 290*3752Scindi 291*3752Scindi /* 292*3752Scindi * Update the FM cache array and active list pointers. 293*3752Scindi * Updates to these pointers require us to acquire the 294*3752Scindi * FMA cache lock to prevent accesses to a stale active 295*3752Scindi * list in ndi_fmc_error(). 296*3752Scindi */ 297*3752Scindi 298*3752Scindi mutex_enter(&fcp->fc_lock); 2990Sstevel@tonic-gate fcp->fc_active = ncp; 3000Sstevel@tonic-gate fcp->fc_elems = ncp; 301*3752Scindi fcp->fc_len = nlen; 302*3752Scindi mutex_exit(&fcp->fc_lock); 303*3752Scindi 304*3752Scindi kmem_free(oep, olen * sizeof (ndi_fmcentry_t)); 3050Sstevel@tonic-gate 3060Sstevel@tonic-gate return (0); 3070Sstevel@tonic-gate } 3080Sstevel@tonic-gate 3090Sstevel@tonic-gate /* 3100Sstevel@tonic-gate * ndi_fmc_insert - 3110Sstevel@tonic-gate * Add a new entry to the specified cache. 3120Sstevel@tonic-gate * 3130Sstevel@tonic-gate * This function must be called at or below LOCK_LEVEL 3140Sstevel@tonic-gate */ 3150Sstevel@tonic-gate void 3160Sstevel@tonic-gate ndi_fmc_insert(dev_info_t *dip, int flag, void *resource, void *bus_specific) 3170Sstevel@tonic-gate { 3180Sstevel@tonic-gate struct dev_info *devi = DEVI(dip); 3190Sstevel@tonic-gate ndi_fmc_t *fcp; 3200Sstevel@tonic-gate ndi_fmcentry_t *fep, **fpp; 3210Sstevel@tonic-gate struct i_ddi_fmhdl *fmhdl; 3220Sstevel@tonic-gate 3230Sstevel@tonic-gate ASSERT(devi); 3240Sstevel@tonic-gate ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE); 3250Sstevel@tonic-gate 3260Sstevel@tonic-gate fmhdl = devi->devi_fmhdl; 3270Sstevel@tonic-gate if (fmhdl == NULL) { 3280Sstevel@tonic-gate i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_NOSLEEP); 3290Sstevel@tonic-gate return; 3300Sstevel@tonic-gate } 3310Sstevel@tonic-gate 3320Sstevel@tonic-gate if (flag == DMA_HANDLE) { 3330Sstevel@tonic-gate if (!DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap)) { 3340Sstevel@tonic-gate i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, 3350Sstevel@tonic-gate DDI_NOSLEEP); 3360Sstevel@tonic-gate return; 3370Sstevel@tonic-gate } 3380Sstevel@tonic-gate fcp = fmhdl->fh_dma_cache; 3390Sstevel@tonic-gate fpp = &((ddi_dma_impl_t *)resource)->dmai_error.err_fep; 3400Sstevel@tonic-gate } else if (flag == ACC_HANDLE) { 3410Sstevel@tonic-gate if (!DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) { 3420Sstevel@tonic-gate i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, 3430Sstevel@tonic-gate DDI_NOSLEEP); 3440Sstevel@tonic-gate return; 3450Sstevel@tonic-gate } 3460Sstevel@tonic-gate fcp = fmhdl->fh_acc_cache; 3470Sstevel@tonic-gate fpp = &((ddi_acc_impl_t *)resource)->ahi_err->err_fep; 3480Sstevel@tonic-gate } 3490Sstevel@tonic-gate ASSERT(*fpp == NULL); 3500Sstevel@tonic-gate 3513114Scindi mutex_enter(&fcp->fc_free_lock); 3520Sstevel@tonic-gate 3530Sstevel@tonic-gate /* Get an entry from the free list */ 3540Sstevel@tonic-gate fep = fcp->fc_free; 3550Sstevel@tonic-gate if (fep == NULL) { 3560Sstevel@tonic-gate if (fmc_grow(fcp, flag, 3570Sstevel@tonic-gate (flag == ACC_HANDLE ? default_acccache_sz : 3580Sstevel@tonic-gate default_dmacache_sz)) != 0) { 3590Sstevel@tonic-gate 3600Sstevel@tonic-gate /* Unable to get an entry or grow this cache */ 3610Sstevel@tonic-gate atomic_add_64( 3620Sstevel@tonic-gate &fmhdl->fh_kstat.fek_fmc_full.value.ui64, 1); 3633114Scindi mutex_exit(&fcp->fc_free_lock); 3640Sstevel@tonic-gate return; 3650Sstevel@tonic-gate } 3660Sstevel@tonic-gate atomic_add_64(&fmhdl->fh_kstat.fek_fmc_grew.value.ui64, 1); 3670Sstevel@tonic-gate fep = fcp->fc_free; 3680Sstevel@tonic-gate } 3690Sstevel@tonic-gate fcp->fc_free = fep->fce_prev; 3703114Scindi mutex_exit(&fcp->fc_free_lock); 3710Sstevel@tonic-gate 3720Sstevel@tonic-gate /* 3730Sstevel@tonic-gate * Set-up the handle resource and bus_specific information. 3740Sstevel@tonic-gate * Also remember the pointer back to the cache for quick removal. 3750Sstevel@tonic-gate */ 3760Sstevel@tonic-gate fep->fce_bus_specific = bus_specific; 3770Sstevel@tonic-gate fep->fce_resource = resource; 3780Sstevel@tonic-gate fep->fce_next = NULL; 3790Sstevel@tonic-gate *fpp = fep; 3800Sstevel@tonic-gate 3810Sstevel@tonic-gate /* Add entry to the end of the active list */ 3823114Scindi mutex_enter(&fcp->fc_lock); 3830Sstevel@tonic-gate fep->fce_prev = fcp->fc_tail; 3840Sstevel@tonic-gate fcp->fc_tail->fce_next = fep; 3850Sstevel@tonic-gate fcp->fc_tail = fep; 3860Sstevel@tonic-gate mutex_exit(&fcp->fc_lock); 3870Sstevel@tonic-gate } 3880Sstevel@tonic-gate 3890Sstevel@tonic-gate /* 3900Sstevel@tonic-gate * Remove an entry from the specified cache of access or dma mappings 3910Sstevel@tonic-gate * 3920Sstevel@tonic-gate * This function must be called at or below LOCK_LEVEL. 3930Sstevel@tonic-gate */ 3940Sstevel@tonic-gate void 3950Sstevel@tonic-gate ndi_fmc_remove(dev_info_t *dip, int flag, const void *resource) 3960Sstevel@tonic-gate { 3970Sstevel@tonic-gate ndi_fmc_t *fcp; 3980Sstevel@tonic-gate ndi_fmcentry_t *fep; 3990Sstevel@tonic-gate struct dev_info *devi = DEVI(dip); 4000Sstevel@tonic-gate struct i_ddi_fmhdl *fmhdl; 4010Sstevel@tonic-gate 4020Sstevel@tonic-gate ASSERT(devi); 4030Sstevel@tonic-gate ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE); 4040Sstevel@tonic-gate 4050Sstevel@tonic-gate fmhdl = devi->devi_fmhdl; 4060Sstevel@tonic-gate if (fmhdl == NULL) { 4070Sstevel@tonic-gate i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_NOSLEEP); 4080Sstevel@tonic-gate return; 4090Sstevel@tonic-gate } 4100Sstevel@tonic-gate 4110Sstevel@tonic-gate /* Find cache entry pointer for this resource */ 4120Sstevel@tonic-gate if (flag == DMA_HANDLE) { 4130Sstevel@tonic-gate if (!DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap)) { 4140Sstevel@tonic-gate i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, 4150Sstevel@tonic-gate DDI_NOSLEEP); 4160Sstevel@tonic-gate return; 4170Sstevel@tonic-gate } 4180Sstevel@tonic-gate fcp = fmhdl->fh_dma_cache; 4190Sstevel@tonic-gate 4200Sstevel@tonic-gate ASSERT(fcp); 4210Sstevel@tonic-gate 422*3752Scindi mutex_enter(&fcp->fc_free_lock); 4230Sstevel@tonic-gate fep = ((ddi_dma_impl_t *)resource)->dmai_error.err_fep; 4240Sstevel@tonic-gate ((ddi_dma_impl_t *)resource)->dmai_error.err_fep = NULL; 4250Sstevel@tonic-gate } else if (flag == ACC_HANDLE) { 4260Sstevel@tonic-gate if (!DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) { 4270Sstevel@tonic-gate i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, 4280Sstevel@tonic-gate DDI_NOSLEEP); 4290Sstevel@tonic-gate return; 4300Sstevel@tonic-gate } 4310Sstevel@tonic-gate fcp = fmhdl->fh_acc_cache; 4320Sstevel@tonic-gate 4330Sstevel@tonic-gate ASSERT(fcp); 4340Sstevel@tonic-gate 435*3752Scindi mutex_enter(&fcp->fc_free_lock); 4360Sstevel@tonic-gate fep = ((ddi_acc_impl_t *)resource)->ahi_err->err_fep; 4370Sstevel@tonic-gate ((ddi_acc_impl_t *)resource)->ahi_err->err_fep = NULL; 438*3752Scindi } else { 439*3752Scindi return; 4400Sstevel@tonic-gate } 4410Sstevel@tonic-gate 4420Sstevel@tonic-gate /* 4430Sstevel@tonic-gate * Resource not in cache, return 4440Sstevel@tonic-gate */ 445*3752Scindi if (fep == NULL) { 446*3752Scindi mutex_exit(&fcp->fc_free_lock); 4470Sstevel@tonic-gate return; 448*3752Scindi } 4490Sstevel@tonic-gate 450*3752Scindi /* 451*3752Scindi * Updates to FM cache pointers require us to grab fmc_lock 452*3752Scindi * to synchronize access to the cache for ndi_fmc_insert() 453*3752Scindi * and ndi_fmc_error() 454*3752Scindi */ 4553114Scindi mutex_enter(&fcp->fc_lock); 4560Sstevel@tonic-gate fep->fce_prev->fce_next = fep->fce_next; 4570Sstevel@tonic-gate if (fep == fcp->fc_tail) 4580Sstevel@tonic-gate fcp->fc_tail = fep->fce_prev; 4590Sstevel@tonic-gate else 4600Sstevel@tonic-gate fep->fce_next->fce_prev = fep->fce_prev; 4613114Scindi mutex_exit(&fcp->fc_lock); 4620Sstevel@tonic-gate 4630Sstevel@tonic-gate /* Add entry back to the free list */ 4640Sstevel@tonic-gate fep->fce_prev = fcp->fc_free; 4650Sstevel@tonic-gate fcp->fc_free = fep; 4663114Scindi mutex_exit(&fcp->fc_free_lock); 4670Sstevel@tonic-gate } 4680Sstevel@tonic-gate 4691865Sdilpreet int 4701865Sdilpreet ndi_fmc_entry_error(dev_info_t *dip, int flag, ddi_fm_error_t *derr, 4711865Sdilpreet const void *bus_err_state) 4721865Sdilpreet { 4731865Sdilpreet int status, fatal = 0, nonfatal = 0; 4741865Sdilpreet ndi_fmc_t *fcp = NULL; 4751865Sdilpreet ndi_fmcentry_t *fep; 4761865Sdilpreet struct i_ddi_fmhdl *fmhdl; 4771865Sdilpreet 4781865Sdilpreet ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE); 4791865Sdilpreet 4801865Sdilpreet fmhdl = DEVI(dip)->devi_fmhdl; 4811865Sdilpreet ASSERT(fmhdl); 4821865Sdilpreet status = DDI_FM_UNKNOWN; 4831865Sdilpreet 4841865Sdilpreet if (flag == DMA_HANDLE && DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap)) { 4851865Sdilpreet fcp = fmhdl->fh_dma_cache; 4861865Sdilpreet ASSERT(fcp); 4871865Sdilpreet } else if (flag == ACC_HANDLE && DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) { 4881865Sdilpreet fcp = fmhdl->fh_acc_cache; 4891865Sdilpreet ASSERT(fcp); 4901865Sdilpreet } 4911865Sdilpreet 4921865Sdilpreet if (fcp != NULL) { 4931865Sdilpreet 4941865Sdilpreet /* 4951865Sdilpreet * Check active resource entries 4961865Sdilpreet */ 4971865Sdilpreet mutex_enter(&fcp->fc_lock); 4981865Sdilpreet for (fep = fcp->fc_active->fce_next; fep != NULL; 4991865Sdilpreet fep = fep->fce_next) { 5001865Sdilpreet ddi_fmcompare_t compare_func; 5011865Sdilpreet 5021865Sdilpreet /* 5031865Sdilpreet * Compare captured error state with handle 5041865Sdilpreet * resources. During the comparison and 5051865Sdilpreet * subsequent error handling, we block 5061865Sdilpreet * attempts to free the cache entry. 5071865Sdilpreet */ 5081865Sdilpreet compare_func = (flag == ACC_HANDLE) ? 5091865Sdilpreet i_ddi_fm_acc_err_cf_get((ddi_acc_handle_t) 5101865Sdilpreet fep->fce_resource) : 5111865Sdilpreet i_ddi_fm_dma_err_cf_get((ddi_dma_handle_t) 5121865Sdilpreet fep->fce_resource); 5131865Sdilpreet 5141865Sdilpreet status = compare_func(dip, fep->fce_resource, 5151865Sdilpreet bus_err_state, fep->fce_bus_specific); 5161865Sdilpreet if (status == DDI_FM_UNKNOWN || status == DDI_FM_OK) 5171865Sdilpreet continue; 5181865Sdilpreet 5191865Sdilpreet if (status == DDI_FM_FATAL) 5201865Sdilpreet ++fatal; 5211865Sdilpreet else if (status == DDI_FM_NONFATAL) 5221865Sdilpreet ++nonfatal; 5231865Sdilpreet 5241865Sdilpreet /* Set the error for this resource handle */ 5251865Sdilpreet if (flag == ACC_HANDLE) { 5261865Sdilpreet ddi_acc_handle_t ap = fep->fce_resource; 5271865Sdilpreet 5281865Sdilpreet i_ddi_fm_acc_err_set(ap, derr->fme_ena, status, 5291865Sdilpreet DDI_FM_ERR_UNEXPECTED); 5301865Sdilpreet ddi_fm_acc_err_get(ap, derr, DDI_FME_VERSION); 5311865Sdilpreet derr->fme_acc_handle = ap; 5321865Sdilpreet } else { 5331865Sdilpreet ddi_dma_handle_t dp = fep->fce_resource; 5341865Sdilpreet 5351865Sdilpreet i_ddi_fm_dma_err_set(dp, derr->fme_ena, status, 5361865Sdilpreet DDI_FM_ERR_UNEXPECTED); 5371865Sdilpreet ddi_fm_dma_err_get(dp, derr, DDI_FME_VERSION); 5381865Sdilpreet derr->fme_dma_handle = dp; 5391865Sdilpreet } 5401865Sdilpreet break; 5411865Sdilpreet } 5421865Sdilpreet mutex_exit(&fcp->fc_lock); 5431865Sdilpreet } 5441865Sdilpreet return (fatal ? DDI_FM_FATAL : nonfatal ? DDI_FM_NONFATAL : 5451865Sdilpreet DDI_FM_UNKNOWN); 5461865Sdilpreet } 5470Sstevel@tonic-gate 5480Sstevel@tonic-gate /* 5490Sstevel@tonic-gate * Check error state against the handle resource stored in the specified 5500Sstevel@tonic-gate * FM cache. If tdip != NULL, we check only the cache entries for tdip. 5510Sstevel@tonic-gate * The caller must ensure that tdip is valid throughout the call and 5520Sstevel@tonic-gate * all FM data structures can be safely accesses. 5530Sstevel@tonic-gate * 5540Sstevel@tonic-gate * If tdip == NULL, we check all children that have registered their 5550Sstevel@tonic-gate * FM_DMA_CHK or FM_ACC_CHK capabilities. 5560Sstevel@tonic-gate * 5570Sstevel@tonic-gate * The following status values may be returned: 5580Sstevel@tonic-gate * 5590Sstevel@tonic-gate * DDI_FM_FATAL - if at least one cache entry comparison yields a 5600Sstevel@tonic-gate * fatal error. 5610Sstevel@tonic-gate * 5620Sstevel@tonic-gate * DDI_FM_NONFATAL - if at least one cache entry comparison yields a 5630Sstevel@tonic-gate * non-fatal error and no comparison yields a fatal error. 5640Sstevel@tonic-gate * 5650Sstevel@tonic-gate * DDI_FM_UNKNOWN - cache entry comparisons did not yield fatal or 5660Sstevel@tonic-gate * non-fatal errors. 5670Sstevel@tonic-gate * 5680Sstevel@tonic-gate */ 5690Sstevel@tonic-gate int 5701865Sdilpreet ndi_fmc_error(dev_info_t *dip, dev_info_t *tdip, int flag, uint64_t ena, 5711865Sdilpreet const void *bus_err_state) 5720Sstevel@tonic-gate { 5730Sstevel@tonic-gate int status, fatal = 0, nonfatal = 0; 5740Sstevel@tonic-gate ddi_fm_error_t derr; 5750Sstevel@tonic-gate struct i_ddi_fmhdl *fmhdl; 5760Sstevel@tonic-gate struct i_ddi_fmtgt *tgt; 5770Sstevel@tonic-gate 5780Sstevel@tonic-gate ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE); 5790Sstevel@tonic-gate 5800Sstevel@tonic-gate i_ddi_fm_handler_enter(dip); 5810Sstevel@tonic-gate fmhdl = DEVI(dip)->devi_fmhdl; 5820Sstevel@tonic-gate ASSERT(fmhdl); 5831865Sdilpreet 5841865Sdilpreet bzero(&derr, sizeof (ddi_fm_error_t)); 5851865Sdilpreet derr.fme_version = DDI_FME_VERSION; 5861865Sdilpreet derr.fme_flag = DDI_FM_ERR_UNEXPECTED; 5871865Sdilpreet derr.fme_ena = ena; 5881865Sdilpreet 5890Sstevel@tonic-gate for (tgt = fmhdl->fh_tgts; tgt != NULL; tgt = tgt->ft_next) { 5900Sstevel@tonic-gate 5910Sstevel@tonic-gate if (tdip != NULL && tdip != tgt->ft_dip) 5920Sstevel@tonic-gate continue; 5930Sstevel@tonic-gate 5941865Sdilpreet /* 5951865Sdilpreet * Attempt to find the entry in this childs handle cache 5961865Sdilpreet */ 5971865Sdilpreet status = ndi_fmc_entry_error(tgt->ft_dip, flag, &derr, 5981865Sdilpreet bus_err_state); 5990Sstevel@tonic-gate 6001865Sdilpreet if (status == DDI_FM_FATAL) 6011865Sdilpreet ++fatal; 6021865Sdilpreet else if (status == DDI_FM_NONFATAL) 6031865Sdilpreet ++nonfatal; 6041865Sdilpreet else 6051865Sdilpreet continue; 6060Sstevel@tonic-gate 6071865Sdilpreet /* 6081865Sdilpreet * Call our child to process this error. 6091865Sdilpreet */ 6101865Sdilpreet status = tgt->ft_errhdl->eh_func(tgt->ft_dip, &derr, 6111865Sdilpreet tgt->ft_errhdl->eh_impl); 6120Sstevel@tonic-gate 6131865Sdilpreet if (status == DDI_FM_FATAL) 6141865Sdilpreet ++fatal; 6151865Sdilpreet else if (status == DDI_FM_NONFATAL) 6161865Sdilpreet ++nonfatal; 6171865Sdilpreet } 6180Sstevel@tonic-gate 6190Sstevel@tonic-gate i_ddi_fm_handler_exit(dip); 6200Sstevel@tonic-gate 6210Sstevel@tonic-gate if (fatal) 6220Sstevel@tonic-gate return (DDI_FM_FATAL); 6230Sstevel@tonic-gate else if (nonfatal) 6240Sstevel@tonic-gate return (DDI_FM_NONFATAL); 6250Sstevel@tonic-gate 6260Sstevel@tonic-gate return (DDI_FM_UNKNOWN); 6270Sstevel@tonic-gate } 6280Sstevel@tonic-gate 6293159Sstephh int 6303159Sstephh ndi_fmc_entry_error_all(dev_info_t *dip, int flag, ddi_fm_error_t *derr) 6313159Sstephh { 6323159Sstephh ndi_fmc_t *fcp = NULL; 6333159Sstephh ndi_fmcentry_t *fep; 6343159Sstephh struct i_ddi_fmhdl *fmhdl; 6353159Sstephh int nonfatal = 0; 6363159Sstephh 6373159Sstephh ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE); 6383159Sstephh 6393159Sstephh fmhdl = DEVI(dip)->devi_fmhdl; 6403159Sstephh ASSERT(fmhdl); 6413159Sstephh 6423159Sstephh if (flag == DMA_HANDLE && DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap)) { 6433159Sstephh fcp = fmhdl->fh_dma_cache; 6443159Sstephh ASSERT(fcp); 6453159Sstephh } else if (flag == ACC_HANDLE && DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) { 6463159Sstephh fcp = fmhdl->fh_acc_cache; 6473159Sstephh ASSERT(fcp); 6483159Sstephh } 6493159Sstephh 6503159Sstephh if (fcp != NULL) { 6513159Sstephh /* 6523159Sstephh * Check active resource entries 6533159Sstephh */ 6543159Sstephh mutex_enter(&fcp->fc_lock); 6553159Sstephh for (fep = fcp->fc_active->fce_next; fep != NULL; 6563159Sstephh fep = fep->fce_next) { 6573159Sstephh /* Set the error for this resource handle */ 6583159Sstephh nonfatal++; 6593159Sstephh if (flag == ACC_HANDLE) { 6603159Sstephh ddi_acc_handle_t ap = fep->fce_resource; 6613159Sstephh 6623159Sstephh i_ddi_fm_acc_err_set(ap, derr->fme_ena, 6633159Sstephh DDI_FM_NONFATAL, DDI_FM_ERR_UNEXPECTED); 6643159Sstephh ddi_fm_acc_err_get(ap, derr, DDI_FME_VERSION); 6653159Sstephh derr->fme_acc_handle = ap; 6663159Sstephh } else { 6673159Sstephh ddi_dma_handle_t dp = fep->fce_resource; 6683159Sstephh 6693159Sstephh i_ddi_fm_dma_err_set(dp, derr->fme_ena, 6703159Sstephh DDI_FM_NONFATAL, DDI_FM_ERR_UNEXPECTED); 6713159Sstephh ddi_fm_dma_err_get(dp, derr, DDI_FME_VERSION); 6723159Sstephh derr->fme_dma_handle = dp; 6733159Sstephh } 6743159Sstephh } 6753159Sstephh mutex_exit(&fcp->fc_lock); 6763159Sstephh } 6773159Sstephh return (nonfatal ? DDI_FM_NONFATAL : DDI_FM_UNKNOWN); 6783159Sstephh } 6793159Sstephh 6800Sstevel@tonic-gate /* 6810Sstevel@tonic-gate * Dispatch registered error handlers for dip. If tdip != NULL, only 6820Sstevel@tonic-gate * the error handler (if available) for tdip is invoked. Otherwise, 6830Sstevel@tonic-gate * all registered error handlers are invoked. 6840Sstevel@tonic-gate * 6850Sstevel@tonic-gate * The following status values may be returned: 6860Sstevel@tonic-gate * 6870Sstevel@tonic-gate * DDI_FM_FATAL - if at least one error handler returns a 6880Sstevel@tonic-gate * fatal error. 6890Sstevel@tonic-gate * 6900Sstevel@tonic-gate * DDI_FM_NONFATAL - if at least one error handler returns a 6910Sstevel@tonic-gate * non-fatal error and none returned a fatal error. 6920Sstevel@tonic-gate * 6930Sstevel@tonic-gate * DDI_FM_UNKNOWN - if at least one error handler returns 6940Sstevel@tonic-gate * unknown status and none return fatal or non-fatal. 6950Sstevel@tonic-gate * 6960Sstevel@tonic-gate * DDI_FM_OK - if all error handlers return DDI_FM_OK 6970Sstevel@tonic-gate */ 6980Sstevel@tonic-gate int 6990Sstevel@tonic-gate ndi_fm_handler_dispatch(dev_info_t *dip, dev_info_t *tdip, 7000Sstevel@tonic-gate const ddi_fm_error_t *nerr) 7010Sstevel@tonic-gate { 7020Sstevel@tonic-gate int status; 7030Sstevel@tonic-gate int unknown = 0, fatal = 0, nonfatal = 0; 7040Sstevel@tonic-gate struct i_ddi_fmhdl *hdl; 7050Sstevel@tonic-gate struct i_ddi_fmtgt *tgt; 7060Sstevel@tonic-gate 7070Sstevel@tonic-gate status = DDI_FM_UNKNOWN; 7080Sstevel@tonic-gate 7090Sstevel@tonic-gate i_ddi_fm_handler_enter(dip); 7100Sstevel@tonic-gate hdl = DEVI(dip)->devi_fmhdl; 7110Sstevel@tonic-gate tgt = hdl->fh_tgts; 7120Sstevel@tonic-gate while (tgt != NULL) { 7130Sstevel@tonic-gate if (tdip == NULL || tdip == tgt->ft_dip) { 7140Sstevel@tonic-gate struct i_ddi_errhdl *errhdl; 7150Sstevel@tonic-gate 7160Sstevel@tonic-gate errhdl = tgt->ft_errhdl; 7170Sstevel@tonic-gate status = errhdl->eh_func(tgt->ft_dip, nerr, 7180Sstevel@tonic-gate errhdl->eh_impl); 7190Sstevel@tonic-gate 7200Sstevel@tonic-gate if (status == DDI_FM_FATAL) 7210Sstevel@tonic-gate ++fatal; 7220Sstevel@tonic-gate else if (status == DDI_FM_NONFATAL) 7230Sstevel@tonic-gate ++nonfatal; 7240Sstevel@tonic-gate else if (status == DDI_FM_UNKNOWN) 7250Sstevel@tonic-gate ++unknown; 7260Sstevel@tonic-gate 7270Sstevel@tonic-gate /* Only interested in one target */ 7280Sstevel@tonic-gate if (tdip != NULL) 7290Sstevel@tonic-gate break; 7300Sstevel@tonic-gate } 7310Sstevel@tonic-gate tgt = tgt->ft_next; 7320Sstevel@tonic-gate } 7330Sstevel@tonic-gate i_ddi_fm_handler_exit(dip); 7340Sstevel@tonic-gate 7350Sstevel@tonic-gate if (fatal) 7360Sstevel@tonic-gate return (DDI_FM_FATAL); 7370Sstevel@tonic-gate else if (nonfatal) 7380Sstevel@tonic-gate return (DDI_FM_NONFATAL); 7390Sstevel@tonic-gate else if (unknown) 7400Sstevel@tonic-gate return (DDI_FM_UNKNOWN); 7410Sstevel@tonic-gate else 7420Sstevel@tonic-gate return (DDI_FM_OK); 7430Sstevel@tonic-gate } 7440Sstevel@tonic-gate 7450Sstevel@tonic-gate /* 7460Sstevel@tonic-gate * Set error status for specified access or DMA handle 7470Sstevel@tonic-gate * 7480Sstevel@tonic-gate * May be called in any context but caller must insure validity of 7490Sstevel@tonic-gate * handle. 7500Sstevel@tonic-gate */ 7510Sstevel@tonic-gate void 7520Sstevel@tonic-gate ndi_fm_acc_err_set(ddi_acc_handle_t handle, ddi_fm_error_t *dfe) 7530Sstevel@tonic-gate { 7540Sstevel@tonic-gate i_ddi_fm_acc_err_set(handle, dfe->fme_ena, dfe->fme_status, 7550Sstevel@tonic-gate dfe->fme_flag); 7560Sstevel@tonic-gate } 7570Sstevel@tonic-gate 7580Sstevel@tonic-gate void 7590Sstevel@tonic-gate ndi_fm_dma_err_set(ddi_dma_handle_t handle, ddi_fm_error_t *dfe) 7600Sstevel@tonic-gate { 7610Sstevel@tonic-gate i_ddi_fm_dma_err_set(handle, dfe->fme_ena, dfe->fme_status, 7620Sstevel@tonic-gate dfe->fme_flag); 7630Sstevel@tonic-gate } 7640Sstevel@tonic-gate 7650Sstevel@tonic-gate /* 7660Sstevel@tonic-gate * Call parent busop fm initialization routine. 7670Sstevel@tonic-gate * 7680Sstevel@tonic-gate * Called during driver attach(1M) 7690Sstevel@tonic-gate */ 7700Sstevel@tonic-gate int 7710Sstevel@tonic-gate i_ndi_busop_fm_init(dev_info_t *dip, int tcap, ddi_iblock_cookie_t *ibc) 7720Sstevel@tonic-gate { 7730Sstevel@tonic-gate int pcap; 7740Sstevel@tonic-gate dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent; 7750Sstevel@tonic-gate 7760Sstevel@tonic-gate if (dip == ddi_root_node()) 7770Sstevel@tonic-gate return (ddi_system_fmcap | DDI_FM_EREPORT_CAPABLE); 7780Sstevel@tonic-gate 7790Sstevel@tonic-gate /* Valid operation for BUSO_REV_6 and above */ 7800Sstevel@tonic-gate if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6) 7810Sstevel@tonic-gate return (DDI_FM_NOT_CAPABLE); 7820Sstevel@tonic-gate 7830Sstevel@tonic-gate if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_init == NULL) 7840Sstevel@tonic-gate return (DDI_FM_NOT_CAPABLE); 7850Sstevel@tonic-gate 7860Sstevel@tonic-gate pcap = (*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_init) 7870Sstevel@tonic-gate (pdip, dip, tcap, ibc); 7880Sstevel@tonic-gate 7890Sstevel@tonic-gate return (pcap); 7900Sstevel@tonic-gate } 7910Sstevel@tonic-gate 7920Sstevel@tonic-gate /* 7930Sstevel@tonic-gate * Call parent busop fm clean-up routine. 7940Sstevel@tonic-gate * 7950Sstevel@tonic-gate * Called during driver detach(1M) 7960Sstevel@tonic-gate */ 7970Sstevel@tonic-gate void 7980Sstevel@tonic-gate i_ndi_busop_fm_fini(dev_info_t *dip) 7990Sstevel@tonic-gate { 8000Sstevel@tonic-gate dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent; 8010Sstevel@tonic-gate 8020Sstevel@tonic-gate if (dip == ddi_root_node()) 8030Sstevel@tonic-gate return; 8040Sstevel@tonic-gate 8050Sstevel@tonic-gate /* Valid operation for BUSO_REV_6 and above */ 8060Sstevel@tonic-gate if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6) 8070Sstevel@tonic-gate return; 8080Sstevel@tonic-gate 8090Sstevel@tonic-gate if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_fini == NULL) 8100Sstevel@tonic-gate return; 8110Sstevel@tonic-gate 8120Sstevel@tonic-gate (*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_fini)(pdip, dip); 8130Sstevel@tonic-gate } 8140Sstevel@tonic-gate 8150Sstevel@tonic-gate /* 8160Sstevel@tonic-gate * The following routines provide exclusive access to a nexus resource 8170Sstevel@tonic-gate * 8180Sstevel@tonic-gate * These busops may be called in user or kernel driver context. 8190Sstevel@tonic-gate */ 8200Sstevel@tonic-gate void 8210Sstevel@tonic-gate i_ndi_busop_access_enter(dev_info_t *dip, ddi_acc_handle_t handle) 8220Sstevel@tonic-gate { 8230Sstevel@tonic-gate dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent; 8240Sstevel@tonic-gate 8250Sstevel@tonic-gate /* Valid operation for BUSO_REV_6 and above */ 8260Sstevel@tonic-gate if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6) 8270Sstevel@tonic-gate return; 8280Sstevel@tonic-gate 8290Sstevel@tonic-gate if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_enter == NULL) 8300Sstevel@tonic-gate return; 8310Sstevel@tonic-gate 8320Sstevel@tonic-gate (*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_enter) 8330Sstevel@tonic-gate (pdip, handle); 8340Sstevel@tonic-gate } 8350Sstevel@tonic-gate 8360Sstevel@tonic-gate void 8370Sstevel@tonic-gate i_ndi_busop_access_exit(dev_info_t *dip, ddi_acc_handle_t handle) 8380Sstevel@tonic-gate { 8390Sstevel@tonic-gate dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent; 8400Sstevel@tonic-gate 8410Sstevel@tonic-gate /* Valid operation for BUSO_REV_6 and above */ 8420Sstevel@tonic-gate if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6) 8430Sstevel@tonic-gate return; 8440Sstevel@tonic-gate 8450Sstevel@tonic-gate if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_exit == NULL) 8460Sstevel@tonic-gate return; 8470Sstevel@tonic-gate 8480Sstevel@tonic-gate (*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_exit)(pdip, handle); 8490Sstevel@tonic-gate } 850