xref: /onnv-gate/usr/src/uts/common/os/ndifm.c (revision 12027:6c3efe08f39d)
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*12027SStephen.Hanson@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate /*
270Sstevel@tonic-gate  * Fault Management for Nexus Device Drivers
280Sstevel@tonic-gate  *
290Sstevel@tonic-gate  * In addition to implementing and supporting Fault Management for Device
300Sstevel@tonic-gate  * Drivers (ddifm.c), nexus drivers must support their children by
310Sstevel@tonic-gate  * reporting FM capabilities, intializing interrupt block cookies
320Sstevel@tonic-gate  * for error handling callbacks and caching mapped resources for lookup
330Sstevel@tonic-gate  * during the detection of an IO transaction error.
340Sstevel@tonic-gate  *
350Sstevel@tonic-gate  * It is typically the nexus driver that receives an error indication
360Sstevel@tonic-gate  * for a fault that may have occurred in the data path of an IO transaction.
370Sstevel@tonic-gate  * Errors may be detected or received via an interrupt, a callback from
380Sstevel@tonic-gate  * another subsystem (e.g. a cpu trap) or examination of control data.
390Sstevel@tonic-gate  *
400Sstevel@tonic-gate  * Upon detection of an error, the nexus has a responsibility to alert
410Sstevel@tonic-gate  * its children of the error and the transaction associated with that
420Sstevel@tonic-gate  * error.  The actual implementation may vary depending upon the capabilities
430Sstevel@tonic-gate  * of the nexus, its underlying hardware and its children.  In this file,
440Sstevel@tonic-gate  * we provide support for typical nexus driver fault management tasks.
450Sstevel@tonic-gate  *
460Sstevel@tonic-gate  * Fault Management Initialization
470Sstevel@tonic-gate  *
480Sstevel@tonic-gate  *      Nexus drivers must implement two new busops, bus_fm_init() and
490Sstevel@tonic-gate  *      bus_fm_fini().  bus_fm_init() is called from a child nexus or device
500Sstevel@tonic-gate  *      driver and is expected to initialize any per-child state and return
510Sstevel@tonic-gate  *      the FM and error interrupt priority levels of the nexus driver.
520Sstevel@tonic-gate  *      Similarly, bus_fm_fini() is called by child drivers and should
530Sstevel@tonic-gate  *      clean-up any resources allocated during bus_fm_init().
540Sstevel@tonic-gate  *      These functions are called from passive kernel context, typically from
550Sstevel@tonic-gate  *      driver attach(9F) and detach(9F) entry points.
560Sstevel@tonic-gate  *
570Sstevel@tonic-gate  * Error Handler Dispatching
580Sstevel@tonic-gate  *
590Sstevel@tonic-gate  *      Nexus drivers implemented to support error handler capabilities
600Sstevel@tonic-gate  *	should invoke registered error handler callbacks for child drivers
610Sstevel@tonic-gate  *	thought to be involved in the error.
620Sstevel@tonic-gate  *	ndi_fm_handler_dispatch() is used to invoke
630Sstevel@tonic-gate  *      all error handlers and returns one of the following status
640Sstevel@tonic-gate  *      indications:
650Sstevel@tonic-gate  *
660Sstevel@tonic-gate  *      DDI_FM_OK - No errors found by any child
670Sstevel@tonic-gate  *      DDI_FM_FATAL - one or more children have detected a fatal error
680Sstevel@tonic-gate  *      DDI_FM_NONFATAL - no fatal errors, but one or more children have
690Sstevel@tonic-gate  *                            detected a non-fatal error
700Sstevel@tonic-gate  *
710Sstevel@tonic-gate  *      ndi_fm_handler_dispatch() may be called in any context
720Sstevel@tonic-gate  *      subject to the constraints specified by the interrupt iblock cookie
730Sstevel@tonic-gate  *      returned during initialization.
740Sstevel@tonic-gate  *
750Sstevel@tonic-gate  * Protected Accesses
760Sstevel@tonic-gate  *
770Sstevel@tonic-gate  *      When an access handle is mapped or a DMA handle is bound via the
780Sstevel@tonic-gate  *      standard busops, bus_map() or bus_dma_bindhdl(), a child driver
790Sstevel@tonic-gate  *      implemented to support DDI_FM_ACCCHK_CAPABLE or
800Sstevel@tonic-gate  *	DDI_FM_DMACHK_CAPABLE capabilites
810Sstevel@tonic-gate  *	expects the nexus to flag any errors detected for transactions
820Sstevel@tonic-gate  *	associated with the mapped or bound handles.
830Sstevel@tonic-gate  *
840Sstevel@tonic-gate  *      Children nexus or device drivers will set the following flags
850Sstevel@tonic-gate  *      in their ddi_device_access or dma_attr_flags when requesting
860Sstevel@tonic-gate  *      the an access or DMA handle mapping:
870Sstevel@tonic-gate  *
880Sstevel@tonic-gate  *      DDI_DMA_FLAGERR - nexus should set error status for any errors
890Sstevel@tonic-gate  *                              detected for a failed DMA transaction.
900Sstevel@tonic-gate  *      DDI_ACC_FLAGERR - nexus should set error status for any errors
910Sstevel@tonic-gate  *                              detected for a failed PIO transaction.
920Sstevel@tonic-gate  *
930Sstevel@tonic-gate  *      A nexus is expected to provide additional error detection and
940Sstevel@tonic-gate  *      handling for handles with these flags set.
950Sstevel@tonic-gate  *
960Sstevel@tonic-gate  * Exclusive Bus Access
970Sstevel@tonic-gate  *
980Sstevel@tonic-gate  *      In cases where a driver requires a high level of fault tolerance
990Sstevel@tonic-gate  *      for a programmed IO transaction, it is neccessary to grant exclusive
1000Sstevel@tonic-gate  *      access to the bus resource.  Exclusivity guarantees that a fault
1010Sstevel@tonic-gate  *      resulting from a transaction on the bus can be easily traced and
1020Sstevel@tonic-gate  *      reported to the driver requesting the transaction.
1030Sstevel@tonic-gate  *
1040Sstevel@tonic-gate  *      Nexus drivers must implement two new busops to support exclusive
1050Sstevel@tonic-gate  *      access, bus_fm_access_enter() and bus_fm_access_exit().  The IO
1060Sstevel@tonic-gate  *      framework will use these functions when it must set-up access
1070Sstevel@tonic-gate  *      handles that set devacc_attr_access to DDI_ACC_CAUTIOUS in
1080Sstevel@tonic-gate  *      their ddi_device_acc_attr_t request.
1090Sstevel@tonic-gate  *
1100Sstevel@tonic-gate  *      Upon receipt of a bus_fm_access_enter() request, the nexus must prevent
1110Sstevel@tonic-gate  *      all other access requests until it receives bus_fm_access_exit()
1120Sstevel@tonic-gate  *      for the requested bus instance. bus_fm_access_enter() and
1130Sstevel@tonic-gate  *	bus_fm_access_exit() may be called from user, kernel or kernel
1140Sstevel@tonic-gate  *	interrupt context.
1150Sstevel@tonic-gate  *
1160Sstevel@tonic-gate  * Access and DMA Handle Caching
1170Sstevel@tonic-gate  *
1180Sstevel@tonic-gate  *      To aid a nexus driver in associating access or DMA handles with
1190Sstevel@tonic-gate  *      a detected error, the nexus should cache all handles that are
1200Sstevel@tonic-gate  *      associated with DDI_ACC_FLAGERR, DDI_ACC_CAUTIOUS_ACC or
1210Sstevel@tonic-gate  *	DDI_DMA_FLAGERR requests from its children.  ndi_fmc_insert() is
1220Sstevel@tonic-gate  *	called by a nexus to cache handles with the above protection flags
1230Sstevel@tonic-gate  *	and ndi_fmc_remove() is called when that handle is unmapped or
1240Sstevel@tonic-gate  *	unbound by the requesting child.  ndi_fmc_insert() and
1250Sstevel@tonic-gate  *	ndi_fmc_remove() may be called from any user or kernel context.
1260Sstevel@tonic-gate  *
1277931SSean.Ye@Sun.COM  *	FM cache element is implemented by kmem_cache. The elements are
1287931SSean.Ye@Sun.COM  *	stored in a doubly-linked searchable list.  When a handle is created,
1297931SSean.Ye@Sun.COM  *	ndi_fm_insert() allocates an entry from the kmem_cache and inserts
1307931SSean.Ye@Sun.COM  *	the entry to the head of the list.  When a handle is unmapped
1317931SSean.Ye@Sun.COM  *	or unbound, ndi_fm_remove() removes its associated cache entry from
1327931SSean.Ye@Sun.COM  *	the list.
1330Sstevel@tonic-gate  *
1340Sstevel@tonic-gate  *      Upon detection of an error, the nexus may invoke ndi_fmc_error() to
1350Sstevel@tonic-gate  *      iterate over the handle cache of one or more of its FM compliant
1360Sstevel@tonic-gate  *      children.  A comparison callback function is provided upon each
1370Sstevel@tonic-gate  *      invocation of ndi_fmc_error() to tell the IO framework if a
1380Sstevel@tonic-gate  *      handle is associated with an error.  If so, the framework will
1390Sstevel@tonic-gate  *      set the error status for that handle before returning from
1400Sstevel@tonic-gate  *      ndi_fmc_error().
1410Sstevel@tonic-gate  *
1420Sstevel@tonic-gate  *      ndi_fmc_error() may be called in any context
1430Sstevel@tonic-gate  *      subject to the constraints specified by the interrupt iblock cookie
1440Sstevel@tonic-gate  *      returned during initialization of the nexus and its children.
1450Sstevel@tonic-gate  *
1460Sstevel@tonic-gate  */
1470Sstevel@tonic-gate 
1480Sstevel@tonic-gate #include <sys/types.h>
1490Sstevel@tonic-gate #include <sys/param.h>
1500Sstevel@tonic-gate #include <sys/debug.h>
1510Sstevel@tonic-gate #include <sys/sunddi.h>
1520Sstevel@tonic-gate #include <sys/sunndi.h>
1530Sstevel@tonic-gate #include <sys/ddi.h>
1540Sstevel@tonic-gate #include <sys/ndi_impldefs.h>
1550Sstevel@tonic-gate #include <sys/devctl.h>
1560Sstevel@tonic-gate #include <sys/nvpair.h>
1570Sstevel@tonic-gate #include <sys/ddifm.h>
1580Sstevel@tonic-gate #include <sys/ndifm.h>
1590Sstevel@tonic-gate #include <sys/spl.h>
1600Sstevel@tonic-gate #include <sys/sysmacros.h>
1610Sstevel@tonic-gate #include <sys/devops.h>
1620Sstevel@tonic-gate #include <sys/atomic.h>
1637931SSean.Ye@Sun.COM #include <sys/kmem.h>
1640Sstevel@tonic-gate #include <sys/fm/io/ddi.h>
1650Sstevel@tonic-gate 
1667931SSean.Ye@Sun.COM kmem_cache_t *ndi_fm_entry_cache;
1677931SSean.Ye@Sun.COM 
1687931SSean.Ye@Sun.COM void
ndi_fm_init(void)1697931SSean.Ye@Sun.COM ndi_fm_init(void)
1707931SSean.Ye@Sun.COM {
1717931SSean.Ye@Sun.COM 	ndi_fm_entry_cache = kmem_cache_create("ndi_fm_entry_cache",
1727931SSean.Ye@Sun.COM 	    sizeof (ndi_fmcentry_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
1737931SSean.Ye@Sun.COM }
1747931SSean.Ye@Sun.COM 
1750Sstevel@tonic-gate /*
1760Sstevel@tonic-gate  * Allocate and initialize a fault management resource cache
1770Sstevel@tonic-gate  * A fault management cache consists of a set of cache elements that
1787931SSean.Ye@Sun.COM  * are allocated from "ndi_fm_entry_cache".
1790Sstevel@tonic-gate  */
1807931SSean.Ye@Sun.COM /* ARGSUSED */
1810Sstevel@tonic-gate void
i_ndi_fmc_create(ndi_fmc_t ** fcpp,int qlen,ddi_iblock_cookie_t ibc)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 
1860Sstevel@tonic-gate 	fcp = kmem_zalloc(sizeof (ndi_fmc_t), KM_SLEEP);
1870Sstevel@tonic-gate 	mutex_init(&fcp->fc_lock, NULL, MUTEX_DRIVER, ibc);
1880Sstevel@tonic-gate 
1890Sstevel@tonic-gate 	*fcpp = fcp;
1900Sstevel@tonic-gate }
1910Sstevel@tonic-gate 
1920Sstevel@tonic-gate /*
1930Sstevel@tonic-gate  * Destroy and resources associated with the given fault management cache.
1940Sstevel@tonic-gate  */
1950Sstevel@tonic-gate void
i_ndi_fmc_destroy(ndi_fmc_t * fcp)1960Sstevel@tonic-gate i_ndi_fmc_destroy(ndi_fmc_t *fcp)
1970Sstevel@tonic-gate {
1987931SSean.Ye@Sun.COM 	ndi_fmcentry_t *fep, *pp;
1997931SSean.Ye@Sun.COM 
2000Sstevel@tonic-gate 	if (fcp == NULL)
2010Sstevel@tonic-gate 		return;
2020Sstevel@tonic-gate 
2037931SSean.Ye@Sun.COM 	/* Free all the cached entries, this should not happen though */
2047931SSean.Ye@Sun.COM 	mutex_enter(&fcp->fc_lock);
2057931SSean.Ye@Sun.COM 	for (fep = fcp->fc_head; fep != NULL; fep = pp) {
2067931SSean.Ye@Sun.COM 		pp = fep->fce_next;
2077931SSean.Ye@Sun.COM 		kmem_cache_free(ndi_fm_entry_cache, fep);
2087931SSean.Ye@Sun.COM 	}
2097931SSean.Ye@Sun.COM 	mutex_exit(&fcp->fc_lock);
2107931SSean.Ye@Sun.COM 	mutex_destroy(&fcp->fc_lock);
2110Sstevel@tonic-gate 	kmem_free(fcp, sizeof (ndi_fmc_t));
2120Sstevel@tonic-gate }
2130Sstevel@tonic-gate 
2140Sstevel@tonic-gate /*
2150Sstevel@tonic-gate  * ndi_fmc_insert -
2160Sstevel@tonic-gate  * 	Add a new entry to the specified cache.
2170Sstevel@tonic-gate  *
2180Sstevel@tonic-gate  * 	This function must be called at or below LOCK_LEVEL
2190Sstevel@tonic-gate  */
2200Sstevel@tonic-gate void
ndi_fmc_insert(dev_info_t * dip,int flag,void * resource,void * bus_specific)2210Sstevel@tonic-gate ndi_fmc_insert(dev_info_t *dip, int flag, void *resource, void *bus_specific)
2220Sstevel@tonic-gate {
2230Sstevel@tonic-gate 	struct dev_info *devi = DEVI(dip);
2240Sstevel@tonic-gate 	ndi_fmc_t *fcp;
2250Sstevel@tonic-gate 	ndi_fmcentry_t *fep, **fpp;
2260Sstevel@tonic-gate 	struct i_ddi_fmhdl *fmhdl;
2270Sstevel@tonic-gate 
2280Sstevel@tonic-gate 	ASSERT(devi);
2290Sstevel@tonic-gate 	ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE);
2300Sstevel@tonic-gate 
2310Sstevel@tonic-gate 	fmhdl = devi->devi_fmhdl;
2320Sstevel@tonic-gate 	if (fmhdl == NULL) {
2330Sstevel@tonic-gate 		return;
2340Sstevel@tonic-gate 	}
2350Sstevel@tonic-gate 
2360Sstevel@tonic-gate 	if (flag == DMA_HANDLE) {
2370Sstevel@tonic-gate 		if (!DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap)) {
2380Sstevel@tonic-gate 			return;
2390Sstevel@tonic-gate 		}
2400Sstevel@tonic-gate 		fcp = fmhdl->fh_dma_cache;
2410Sstevel@tonic-gate 		fpp = &((ddi_dma_impl_t *)resource)->dmai_error.err_fep;
2420Sstevel@tonic-gate 	} else if (flag == ACC_HANDLE) {
2430Sstevel@tonic-gate 		if (!DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) {
2440Sstevel@tonic-gate 			i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL,
2450Sstevel@tonic-gate 			    DDI_NOSLEEP);
2460Sstevel@tonic-gate 			return;
2470Sstevel@tonic-gate 		}
2480Sstevel@tonic-gate 		fcp = fmhdl->fh_acc_cache;
2490Sstevel@tonic-gate 		fpp = &((ddi_acc_impl_t *)resource)->ahi_err->err_fep;
2500Sstevel@tonic-gate 	}
2510Sstevel@tonic-gate 
2527931SSean.Ye@Sun.COM 	fep = kmem_cache_alloc(ndi_fm_entry_cache, KM_NOSLEEP);
2530Sstevel@tonic-gate 	if (fep == NULL) {
2547931SSean.Ye@Sun.COM 		atomic_inc_64(&fmhdl->fh_kstat.fek_fmc_full.value.ui64);
2557931SSean.Ye@Sun.COM 		return;
2560Sstevel@tonic-gate 	}
2570Sstevel@tonic-gate 
2580Sstevel@tonic-gate 	/*
2590Sstevel@tonic-gate 	 * Set-up the handle resource and bus_specific information.
2600Sstevel@tonic-gate 	 * Also remember the pointer back to the cache for quick removal.
2610Sstevel@tonic-gate 	 */
2620Sstevel@tonic-gate 	fep->fce_bus_specific = bus_specific;
2630Sstevel@tonic-gate 	fep->fce_resource = resource;
2640Sstevel@tonic-gate 	fep->fce_next = NULL;
2650Sstevel@tonic-gate 
2660Sstevel@tonic-gate 	/* Add entry to the end of the active list */
2673114Scindi 	mutex_enter(&fcp->fc_lock);
2687931SSean.Ye@Sun.COM 	ASSERT(*fpp == NULL);
2697931SSean.Ye@Sun.COM 	*fpp = fep;
2700Sstevel@tonic-gate 	fep->fce_prev = fcp->fc_tail;
2717931SSean.Ye@Sun.COM 	if (fcp->fc_tail != NULL)
2727931SSean.Ye@Sun.COM 		fcp->fc_tail->fce_next = fep;
2737931SSean.Ye@Sun.COM 	else
2747931SSean.Ye@Sun.COM 		fcp->fc_head = fep;
2750Sstevel@tonic-gate 	fcp->fc_tail = fep;
2760Sstevel@tonic-gate 	mutex_exit(&fcp->fc_lock);
2770Sstevel@tonic-gate }
2780Sstevel@tonic-gate 
2790Sstevel@tonic-gate /*
2800Sstevel@tonic-gate  * 	Remove an entry from the specified cache of access or dma mappings
2810Sstevel@tonic-gate  *
2820Sstevel@tonic-gate  * 	This function must be called at or below LOCK_LEVEL.
2830Sstevel@tonic-gate  */
2840Sstevel@tonic-gate void
ndi_fmc_remove(dev_info_t * dip,int flag,const void * resource)2850Sstevel@tonic-gate ndi_fmc_remove(dev_info_t *dip, int flag, const void *resource)
2860Sstevel@tonic-gate {
2870Sstevel@tonic-gate 	ndi_fmc_t *fcp;
2880Sstevel@tonic-gate 	ndi_fmcentry_t *fep;
2890Sstevel@tonic-gate 	struct dev_info *devi = DEVI(dip);
2900Sstevel@tonic-gate 	struct i_ddi_fmhdl *fmhdl;
2910Sstevel@tonic-gate 
2920Sstevel@tonic-gate 	ASSERT(devi);
2930Sstevel@tonic-gate 	ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE);
2940Sstevel@tonic-gate 
2950Sstevel@tonic-gate 	fmhdl = devi->devi_fmhdl;
2960Sstevel@tonic-gate 	if (fmhdl == NULL) {
2970Sstevel@tonic-gate 		return;
2980Sstevel@tonic-gate 	}
2990Sstevel@tonic-gate 
3000Sstevel@tonic-gate 	/* Find cache entry pointer for this resource */
3010Sstevel@tonic-gate 	if (flag == DMA_HANDLE) {
3020Sstevel@tonic-gate 		if (!DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap)) {
3030Sstevel@tonic-gate 			return;
3040Sstevel@tonic-gate 		}
3050Sstevel@tonic-gate 		fcp = fmhdl->fh_dma_cache;
3060Sstevel@tonic-gate 
3070Sstevel@tonic-gate 		ASSERT(fcp);
3080Sstevel@tonic-gate 
3097931SSean.Ye@Sun.COM 		mutex_enter(&fcp->fc_lock);
3100Sstevel@tonic-gate 		fep = ((ddi_dma_impl_t *)resource)->dmai_error.err_fep;
3110Sstevel@tonic-gate 		((ddi_dma_impl_t *)resource)->dmai_error.err_fep = NULL;
3120Sstevel@tonic-gate 	} else if (flag == ACC_HANDLE) {
3130Sstevel@tonic-gate 		if (!DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) {
3140Sstevel@tonic-gate 			i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL,
3150Sstevel@tonic-gate 			    DDI_NOSLEEP);
3160Sstevel@tonic-gate 			return;
3170Sstevel@tonic-gate 		}
3180Sstevel@tonic-gate 		fcp = fmhdl->fh_acc_cache;
3190Sstevel@tonic-gate 
3200Sstevel@tonic-gate 		ASSERT(fcp);
3210Sstevel@tonic-gate 
3227931SSean.Ye@Sun.COM 		mutex_enter(&fcp->fc_lock);
3230Sstevel@tonic-gate 		fep = ((ddi_acc_impl_t *)resource)->ahi_err->err_fep;
3240Sstevel@tonic-gate 		((ddi_acc_impl_t *)resource)->ahi_err->err_fep = NULL;
3253752Scindi 	} else {
3263752Scindi 		return;
3270Sstevel@tonic-gate 	}
3280Sstevel@tonic-gate 
3290Sstevel@tonic-gate 	/*
3300Sstevel@tonic-gate 	 * Resource not in cache, return
3310Sstevel@tonic-gate 	 */
3323752Scindi 	if (fep == NULL) {
3337931SSean.Ye@Sun.COM 		mutex_exit(&fcp->fc_lock);
3347931SSean.Ye@Sun.COM 		atomic_inc_64(&fmhdl->fh_kstat.fek_fmc_miss.value.ui64);
3350Sstevel@tonic-gate 		return;
3363752Scindi 	}
3370Sstevel@tonic-gate 
3383752Scindi 	/*
3393752Scindi 	 * Updates to FM cache pointers require us to grab fmc_lock
3403752Scindi 	 * to synchronize access to the cache for ndi_fmc_insert()
3413752Scindi 	 * and ndi_fmc_error()
3423752Scindi 	 */
3437931SSean.Ye@Sun.COM 	if (fep == fcp->fc_head)
3447931SSean.Ye@Sun.COM 		fcp->fc_head = fep->fce_next;
3457931SSean.Ye@Sun.COM 	else
3467931SSean.Ye@Sun.COM 		fep->fce_prev->fce_next = fep->fce_next;
3470Sstevel@tonic-gate 	if (fep == fcp->fc_tail)
3480Sstevel@tonic-gate 		fcp->fc_tail = fep->fce_prev;
3490Sstevel@tonic-gate 	else
3500Sstevel@tonic-gate 		fep->fce_next->fce_prev = fep->fce_prev;
3513114Scindi 	mutex_exit(&fcp->fc_lock);
3520Sstevel@tonic-gate 
3537931SSean.Ye@Sun.COM 	kmem_cache_free(ndi_fm_entry_cache, fep);
3540Sstevel@tonic-gate }
3550Sstevel@tonic-gate 
3561865Sdilpreet int
ndi_fmc_entry_error(dev_info_t * dip,int flag,ddi_fm_error_t * derr,const void * bus_err_state)3571865Sdilpreet ndi_fmc_entry_error(dev_info_t *dip, int flag, ddi_fm_error_t *derr,
3581865Sdilpreet     const void *bus_err_state)
3591865Sdilpreet {
3601865Sdilpreet 	int status, fatal = 0, nonfatal = 0;
3611865Sdilpreet 	ndi_fmc_t *fcp = NULL;
3621865Sdilpreet 	ndi_fmcentry_t *fep;
3631865Sdilpreet 	struct i_ddi_fmhdl *fmhdl;
3641865Sdilpreet 
3651865Sdilpreet 	ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE);
3661865Sdilpreet 
3671865Sdilpreet 	fmhdl = DEVI(dip)->devi_fmhdl;
3681865Sdilpreet 	ASSERT(fmhdl);
3691865Sdilpreet 	status = DDI_FM_UNKNOWN;
3701865Sdilpreet 
3711865Sdilpreet 	if (flag == DMA_HANDLE && DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap)) {
3721865Sdilpreet 		fcp = fmhdl->fh_dma_cache;
3731865Sdilpreet 		ASSERT(fcp);
3741865Sdilpreet 	} else if (flag == ACC_HANDLE && DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) {
3751865Sdilpreet 		fcp = fmhdl->fh_acc_cache;
3761865Sdilpreet 		ASSERT(fcp);
3771865Sdilpreet 	}
3781865Sdilpreet 
3791865Sdilpreet 	if (fcp != NULL) {
3801865Sdilpreet 
3811865Sdilpreet 		/*
3821865Sdilpreet 		 * Check active resource entries
3831865Sdilpreet 		 */
3841865Sdilpreet 		mutex_enter(&fcp->fc_lock);
3857931SSean.Ye@Sun.COM 		for (fep = fcp->fc_head; fep != NULL; fep = fep->fce_next) {
3861865Sdilpreet 			ddi_fmcompare_t compare_func;
3871865Sdilpreet 
3881865Sdilpreet 			/*
3891865Sdilpreet 			 * Compare captured error state with handle
3901865Sdilpreet 			 * resources.  During the comparison and
3911865Sdilpreet 			 * subsequent error handling, we block
3921865Sdilpreet 			 * attempts to free the cache entry.
3931865Sdilpreet 			 */
3941865Sdilpreet 			compare_func = (flag == ACC_HANDLE) ?
3951865Sdilpreet 			    i_ddi_fm_acc_err_cf_get((ddi_acc_handle_t)
3965871Sstephh 			    fep->fce_resource) :
3971865Sdilpreet 			    i_ddi_fm_dma_err_cf_get((ddi_dma_handle_t)
3985871Sstephh 			    fep->fce_resource);
3991865Sdilpreet 
400*12027SStephen.Hanson@Sun.COM 			if (compare_func == NULL) /* unbound or not FLAGERR */
401*12027SStephen.Hanson@Sun.COM 				continue;
402*12027SStephen.Hanson@Sun.COM 
4031865Sdilpreet 			status = compare_func(dip, fep->fce_resource,
4041865Sdilpreet 			    bus_err_state, fep->fce_bus_specific);
4051865Sdilpreet 			if (status == DDI_FM_UNKNOWN || status == DDI_FM_OK)
4061865Sdilpreet 				continue;
4071865Sdilpreet 
4081865Sdilpreet 			if (status == DDI_FM_FATAL)
4091865Sdilpreet 				++fatal;
4101865Sdilpreet 			else if (status == DDI_FM_NONFATAL)
4111865Sdilpreet 				++nonfatal;
4121865Sdilpreet 
4131865Sdilpreet 			/* Set the error for this resource handle */
4141865Sdilpreet 			if (flag == ACC_HANDLE) {
4151865Sdilpreet 				ddi_acc_handle_t ap = fep->fce_resource;
4161865Sdilpreet 
4171865Sdilpreet 				i_ddi_fm_acc_err_set(ap, derr->fme_ena, status,
4181865Sdilpreet 				    DDI_FM_ERR_UNEXPECTED);
4191865Sdilpreet 				ddi_fm_acc_err_get(ap, derr, DDI_FME_VERSION);
4201865Sdilpreet 				derr->fme_acc_handle = ap;
4211865Sdilpreet 			} else {
4221865Sdilpreet 				ddi_dma_handle_t dp = fep->fce_resource;
4231865Sdilpreet 
4241865Sdilpreet 				i_ddi_fm_dma_err_set(dp, derr->fme_ena, status,
4251865Sdilpreet 				    DDI_FM_ERR_UNEXPECTED);
4261865Sdilpreet 				ddi_fm_dma_err_get(dp, derr, DDI_FME_VERSION);
4271865Sdilpreet 				derr->fme_dma_handle = dp;
4281865Sdilpreet 			}
4291865Sdilpreet 		}
4301865Sdilpreet 		mutex_exit(&fcp->fc_lock);
4311865Sdilpreet 	}
4321865Sdilpreet 	return (fatal ? DDI_FM_FATAL : nonfatal ? DDI_FM_NONFATAL :
4331865Sdilpreet 	    DDI_FM_UNKNOWN);
4341865Sdilpreet }
4350Sstevel@tonic-gate 
4360Sstevel@tonic-gate /*
4370Sstevel@tonic-gate  * Check error state against the handle resource stored in the specified
4380Sstevel@tonic-gate  * FM cache.  If tdip != NULL, we check only the cache entries for tdip.
4390Sstevel@tonic-gate  * The caller must ensure that tdip is valid throughout the call and
4400Sstevel@tonic-gate  * all FM data structures can be safely accesses.
4410Sstevel@tonic-gate  *
4420Sstevel@tonic-gate  * If tdip == NULL, we check all children that have registered their
4430Sstevel@tonic-gate  * FM_DMA_CHK or FM_ACC_CHK capabilities.
4440Sstevel@tonic-gate  *
4450Sstevel@tonic-gate  * The following status values may be returned:
4460Sstevel@tonic-gate  *
4470Sstevel@tonic-gate  *	DDI_FM_FATAL - if at least one cache entry comparison yields a
4480Sstevel@tonic-gate  *			fatal error.
4490Sstevel@tonic-gate  *
4500Sstevel@tonic-gate  *	DDI_FM_NONFATAL - if at least one cache entry comparison yields a
4510Sstevel@tonic-gate  *			non-fatal error and no comparison yields a fatal error.
4520Sstevel@tonic-gate  *
4530Sstevel@tonic-gate  *	DDI_FM_UNKNOWN - cache entry comparisons did not yield fatal or
4540Sstevel@tonic-gate  *			non-fatal errors.
4550Sstevel@tonic-gate  *
4560Sstevel@tonic-gate  */
4570Sstevel@tonic-gate int
ndi_fmc_error(dev_info_t * dip,dev_info_t * tdip,int flag,uint64_t ena,const void * bus_err_state)4581865Sdilpreet ndi_fmc_error(dev_info_t *dip, dev_info_t *tdip, int flag, uint64_t ena,
4591865Sdilpreet     const void *bus_err_state)
4600Sstevel@tonic-gate {
4610Sstevel@tonic-gate 	int status, fatal = 0, nonfatal = 0;
4620Sstevel@tonic-gate 	ddi_fm_error_t derr;
4630Sstevel@tonic-gate 	struct i_ddi_fmhdl *fmhdl;
4640Sstevel@tonic-gate 	struct i_ddi_fmtgt *tgt;
4650Sstevel@tonic-gate 
4660Sstevel@tonic-gate 	ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE);
4670Sstevel@tonic-gate 
4680Sstevel@tonic-gate 	i_ddi_fm_handler_enter(dip);
4690Sstevel@tonic-gate 	fmhdl = DEVI(dip)->devi_fmhdl;
4700Sstevel@tonic-gate 	ASSERT(fmhdl);
4711865Sdilpreet 
4721865Sdilpreet 	bzero(&derr, sizeof (ddi_fm_error_t));
4731865Sdilpreet 	derr.fme_version = DDI_FME_VERSION;
4741865Sdilpreet 	derr.fme_flag = DDI_FM_ERR_UNEXPECTED;
4751865Sdilpreet 	derr.fme_ena = ena;
4761865Sdilpreet 
4770Sstevel@tonic-gate 	for (tgt = fmhdl->fh_tgts; tgt != NULL; tgt = tgt->ft_next) {
4780Sstevel@tonic-gate 
4790Sstevel@tonic-gate 		if (tdip != NULL && tdip != tgt->ft_dip)
4800Sstevel@tonic-gate 			continue;
4810Sstevel@tonic-gate 
4821865Sdilpreet 		/*
4831865Sdilpreet 		 * Attempt to find the entry in this childs handle cache
4841865Sdilpreet 		 */
4851865Sdilpreet 		status = ndi_fmc_entry_error(tgt->ft_dip, flag, &derr,
4861865Sdilpreet 		    bus_err_state);
4870Sstevel@tonic-gate 
4881865Sdilpreet 		if (status == DDI_FM_FATAL)
4891865Sdilpreet 			++fatal;
4901865Sdilpreet 		else if (status == DDI_FM_NONFATAL)
4911865Sdilpreet 			++nonfatal;
4921865Sdilpreet 		else
4931865Sdilpreet 			continue;
4940Sstevel@tonic-gate 
4951865Sdilpreet 		/*
4961865Sdilpreet 		 * Call our child to process this error.
4971865Sdilpreet 		 */
4981865Sdilpreet 		status = tgt->ft_errhdl->eh_func(tgt->ft_dip, &derr,
4991865Sdilpreet 		    tgt->ft_errhdl->eh_impl);
5000Sstevel@tonic-gate 
5011865Sdilpreet 		if (status == DDI_FM_FATAL)
5021865Sdilpreet 			++fatal;
5031865Sdilpreet 		else if (status == DDI_FM_NONFATAL)
5041865Sdilpreet 			++nonfatal;
5051865Sdilpreet 	}
5060Sstevel@tonic-gate 
5070Sstevel@tonic-gate 	i_ddi_fm_handler_exit(dip);
5080Sstevel@tonic-gate 
5090Sstevel@tonic-gate 	if (fatal)
5100Sstevel@tonic-gate 		return (DDI_FM_FATAL);
5110Sstevel@tonic-gate 	else if (nonfatal)
5120Sstevel@tonic-gate 		return (DDI_FM_NONFATAL);
5130Sstevel@tonic-gate 
5140Sstevel@tonic-gate 	return (DDI_FM_UNKNOWN);
5150Sstevel@tonic-gate }
5160Sstevel@tonic-gate 
5173159Sstephh int
ndi_fmc_entry_error_all(dev_info_t * dip,int flag,ddi_fm_error_t * derr)5183159Sstephh ndi_fmc_entry_error_all(dev_info_t *dip, int flag, ddi_fm_error_t *derr)
5193159Sstephh {
5203159Sstephh 	ndi_fmc_t *fcp = NULL;
5213159Sstephh 	ndi_fmcentry_t *fep;
5223159Sstephh 	struct i_ddi_fmhdl *fmhdl;
5233159Sstephh 	int nonfatal = 0;
5243159Sstephh 
5253159Sstephh 	ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE);
5263159Sstephh 
5273159Sstephh 	fmhdl = DEVI(dip)->devi_fmhdl;
5283159Sstephh 	ASSERT(fmhdl);
5293159Sstephh 
5303159Sstephh 	if (flag == DMA_HANDLE && DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap)) {
5313159Sstephh 		fcp = fmhdl->fh_dma_cache;
5323159Sstephh 		ASSERT(fcp);
5333159Sstephh 	} else if (flag == ACC_HANDLE && DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) {
5343159Sstephh 		fcp = fmhdl->fh_acc_cache;
5353159Sstephh 		ASSERT(fcp);
5363159Sstephh 	}
5373159Sstephh 
5383159Sstephh 	if (fcp != NULL) {
5393159Sstephh 		/*
5403159Sstephh 		 * Check active resource entries
5413159Sstephh 		 */
5423159Sstephh 		mutex_enter(&fcp->fc_lock);
5437931SSean.Ye@Sun.COM 		for (fep = fcp->fc_head; fep != NULL; fep = fep->fce_next) {
544*12027SStephen.Hanson@Sun.COM 			ddi_fmcompare_t compare_func;
545*12027SStephen.Hanson@Sun.COM 
546*12027SStephen.Hanson@Sun.COM 			compare_func = (flag == ACC_HANDLE) ?
547*12027SStephen.Hanson@Sun.COM 			    i_ddi_fm_acc_err_cf_get((ddi_acc_handle_t)
548*12027SStephen.Hanson@Sun.COM 			    fep->fce_resource) :
549*12027SStephen.Hanson@Sun.COM 			    i_ddi_fm_dma_err_cf_get((ddi_dma_handle_t)
550*12027SStephen.Hanson@Sun.COM 			    fep->fce_resource);
551*12027SStephen.Hanson@Sun.COM 
552*12027SStephen.Hanson@Sun.COM 			if (compare_func == NULL) /* unbound or not FLAGERR */
553*12027SStephen.Hanson@Sun.COM 				continue;
554*12027SStephen.Hanson@Sun.COM 
5553159Sstephh 			/* Set the error for this resource handle */
5563159Sstephh 			nonfatal++;
557*12027SStephen.Hanson@Sun.COM 
5583159Sstephh 			if (flag == ACC_HANDLE) {
5593159Sstephh 				ddi_acc_handle_t ap = fep->fce_resource;
5603159Sstephh 
5613159Sstephh 				i_ddi_fm_acc_err_set(ap, derr->fme_ena,
5623159Sstephh 				    DDI_FM_NONFATAL, DDI_FM_ERR_UNEXPECTED);
5633159Sstephh 				ddi_fm_acc_err_get(ap, derr, DDI_FME_VERSION);
5643159Sstephh 				derr->fme_acc_handle = ap;
5653159Sstephh 			} else {
5663159Sstephh 				ddi_dma_handle_t dp = fep->fce_resource;
5673159Sstephh 
5683159Sstephh 				i_ddi_fm_dma_err_set(dp, derr->fme_ena,
5693159Sstephh 				    DDI_FM_NONFATAL, DDI_FM_ERR_UNEXPECTED);
5703159Sstephh 				ddi_fm_dma_err_get(dp, derr, DDI_FME_VERSION);
5713159Sstephh 				derr->fme_dma_handle = dp;
5723159Sstephh 			}
5733159Sstephh 		}
5743159Sstephh 		mutex_exit(&fcp->fc_lock);
5753159Sstephh 	}
5763159Sstephh 	return (nonfatal ? DDI_FM_NONFATAL : DDI_FM_UNKNOWN);
5773159Sstephh }
5783159Sstephh 
5790Sstevel@tonic-gate /*
5800Sstevel@tonic-gate  * Dispatch registered error handlers for dip.  If tdip != NULL, only
5810Sstevel@tonic-gate  * the error handler (if available) for tdip is invoked.  Otherwise,
5820Sstevel@tonic-gate  * all registered error handlers are invoked.
5830Sstevel@tonic-gate  *
5840Sstevel@tonic-gate  * The following status values may be returned:
5850Sstevel@tonic-gate  *
5860Sstevel@tonic-gate  *	DDI_FM_FATAL - if at least one error handler returns a
5870Sstevel@tonic-gate  *			fatal error.
5880Sstevel@tonic-gate  *
5890Sstevel@tonic-gate  *	DDI_FM_NONFATAL - if at least one error handler returns a
5900Sstevel@tonic-gate  *			non-fatal error and none returned a fatal error.
5910Sstevel@tonic-gate  *
5920Sstevel@tonic-gate  *	DDI_FM_UNKNOWN - if at least one error handler returns
5930Sstevel@tonic-gate  *			unknown status and none return fatal or non-fatal.
5940Sstevel@tonic-gate  *
5950Sstevel@tonic-gate  *	DDI_FM_OK - if all error handlers return DDI_FM_OK
5960Sstevel@tonic-gate  */
5970Sstevel@tonic-gate int
ndi_fm_handler_dispatch(dev_info_t * dip,dev_info_t * tdip,const ddi_fm_error_t * nerr)5980Sstevel@tonic-gate ndi_fm_handler_dispatch(dev_info_t *dip, dev_info_t *tdip,
5990Sstevel@tonic-gate     const ddi_fm_error_t *nerr)
6000Sstevel@tonic-gate {
6010Sstevel@tonic-gate 	int status;
6020Sstevel@tonic-gate 	int unknown = 0, fatal = 0, nonfatal = 0;
6030Sstevel@tonic-gate 	struct i_ddi_fmhdl *hdl;
6040Sstevel@tonic-gate 	struct i_ddi_fmtgt *tgt;
6050Sstevel@tonic-gate 
6060Sstevel@tonic-gate 	status = DDI_FM_UNKNOWN;
6070Sstevel@tonic-gate 
6080Sstevel@tonic-gate 	i_ddi_fm_handler_enter(dip);
6090Sstevel@tonic-gate 	hdl = DEVI(dip)->devi_fmhdl;
6100Sstevel@tonic-gate 	tgt = hdl->fh_tgts;
6110Sstevel@tonic-gate 	while (tgt != NULL) {
6120Sstevel@tonic-gate 		if (tdip == NULL || tdip == tgt->ft_dip) {
6130Sstevel@tonic-gate 			struct i_ddi_errhdl *errhdl;
6140Sstevel@tonic-gate 
6150Sstevel@tonic-gate 			errhdl = tgt->ft_errhdl;
6160Sstevel@tonic-gate 			status = errhdl->eh_func(tgt->ft_dip, nerr,
6170Sstevel@tonic-gate 			    errhdl->eh_impl);
6180Sstevel@tonic-gate 
6190Sstevel@tonic-gate 			if (status == DDI_FM_FATAL)
6200Sstevel@tonic-gate 				++fatal;
6210Sstevel@tonic-gate 			else if (status == DDI_FM_NONFATAL)
6220Sstevel@tonic-gate 				++nonfatal;
6230Sstevel@tonic-gate 			else if (status == DDI_FM_UNKNOWN)
6240Sstevel@tonic-gate 				++unknown;
6250Sstevel@tonic-gate 
6260Sstevel@tonic-gate 			/* Only interested in one target */
6270Sstevel@tonic-gate 			if (tdip != NULL)
6280Sstevel@tonic-gate 				break;
6290Sstevel@tonic-gate 		}
6300Sstevel@tonic-gate 		tgt = tgt->ft_next;
6310Sstevel@tonic-gate 	}
6320Sstevel@tonic-gate 	i_ddi_fm_handler_exit(dip);
6330Sstevel@tonic-gate 
6340Sstevel@tonic-gate 	if (fatal)
6350Sstevel@tonic-gate 		return (DDI_FM_FATAL);
6360Sstevel@tonic-gate 	else if (nonfatal)
6370Sstevel@tonic-gate 		return (DDI_FM_NONFATAL);
6380Sstevel@tonic-gate 	else if (unknown)
6390Sstevel@tonic-gate 		return (DDI_FM_UNKNOWN);
6400Sstevel@tonic-gate 	else
6410Sstevel@tonic-gate 		return (DDI_FM_OK);
6420Sstevel@tonic-gate }
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate /*
6450Sstevel@tonic-gate  * Set error status for specified access or DMA handle
6460Sstevel@tonic-gate  *
6470Sstevel@tonic-gate  * May be called in any context but caller must insure validity of
6480Sstevel@tonic-gate  * handle.
6490Sstevel@tonic-gate  */
6500Sstevel@tonic-gate void
ndi_fm_acc_err_set(ddi_acc_handle_t handle,ddi_fm_error_t * dfe)6510Sstevel@tonic-gate ndi_fm_acc_err_set(ddi_acc_handle_t handle, ddi_fm_error_t *dfe)
6520Sstevel@tonic-gate {
6530Sstevel@tonic-gate 	i_ddi_fm_acc_err_set(handle, dfe->fme_ena, dfe->fme_status,
6540Sstevel@tonic-gate 	    dfe->fme_flag);
6550Sstevel@tonic-gate }
6560Sstevel@tonic-gate 
6570Sstevel@tonic-gate void
ndi_fm_dma_err_set(ddi_dma_handle_t handle,ddi_fm_error_t * dfe)6580Sstevel@tonic-gate ndi_fm_dma_err_set(ddi_dma_handle_t handle, ddi_fm_error_t *dfe)
6590Sstevel@tonic-gate {
6600Sstevel@tonic-gate 	i_ddi_fm_dma_err_set(handle, dfe->fme_ena, dfe->fme_status,
6610Sstevel@tonic-gate 	    dfe->fme_flag);
6620Sstevel@tonic-gate }
6630Sstevel@tonic-gate 
6640Sstevel@tonic-gate /*
6650Sstevel@tonic-gate  * Call parent busop fm initialization routine.
6660Sstevel@tonic-gate  *
6670Sstevel@tonic-gate  * Called during driver attach(1M)
6680Sstevel@tonic-gate  */
6690Sstevel@tonic-gate int
i_ndi_busop_fm_init(dev_info_t * dip,int tcap,ddi_iblock_cookie_t * ibc)6700Sstevel@tonic-gate i_ndi_busop_fm_init(dev_info_t *dip, int tcap, ddi_iblock_cookie_t *ibc)
6710Sstevel@tonic-gate {
6720Sstevel@tonic-gate 	int pcap;
6730Sstevel@tonic-gate 	dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent;
6740Sstevel@tonic-gate 
6750Sstevel@tonic-gate 	if (dip == ddi_root_node())
6760Sstevel@tonic-gate 		return (ddi_system_fmcap | DDI_FM_EREPORT_CAPABLE);
6770Sstevel@tonic-gate 
6780Sstevel@tonic-gate 	/* Valid operation for BUSO_REV_6 and above */
6790Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6)
6800Sstevel@tonic-gate 		return (DDI_FM_NOT_CAPABLE);
6810Sstevel@tonic-gate 
6820Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_init == NULL)
6830Sstevel@tonic-gate 		return (DDI_FM_NOT_CAPABLE);
6840Sstevel@tonic-gate 
6850Sstevel@tonic-gate 	pcap = (*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_init)
6860Sstevel@tonic-gate 	    (pdip, dip, tcap, ibc);
6870Sstevel@tonic-gate 
6880Sstevel@tonic-gate 	return (pcap);
6890Sstevel@tonic-gate }
6900Sstevel@tonic-gate 
6910Sstevel@tonic-gate /*
6920Sstevel@tonic-gate  * Call parent busop fm clean-up routine.
6930Sstevel@tonic-gate  *
6940Sstevel@tonic-gate  * Called during driver detach(1M)
6950Sstevel@tonic-gate  */
6960Sstevel@tonic-gate void
i_ndi_busop_fm_fini(dev_info_t * dip)6970Sstevel@tonic-gate i_ndi_busop_fm_fini(dev_info_t *dip)
6980Sstevel@tonic-gate {
6990Sstevel@tonic-gate 	dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent;
7000Sstevel@tonic-gate 
7010Sstevel@tonic-gate 	if (dip == ddi_root_node())
7020Sstevel@tonic-gate 		return;
7030Sstevel@tonic-gate 
7040Sstevel@tonic-gate 	/* Valid operation for BUSO_REV_6 and above */
7050Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6)
7060Sstevel@tonic-gate 		return;
7070Sstevel@tonic-gate 
7080Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_fini == NULL)
7090Sstevel@tonic-gate 		return;
7100Sstevel@tonic-gate 
7110Sstevel@tonic-gate 	(*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_fini)(pdip, dip);
7120Sstevel@tonic-gate }
7130Sstevel@tonic-gate 
7140Sstevel@tonic-gate /*
7150Sstevel@tonic-gate  * The following routines provide exclusive access to a nexus resource
7160Sstevel@tonic-gate  *
7170Sstevel@tonic-gate  * These busops may be called in user or kernel driver context.
7180Sstevel@tonic-gate  */
7190Sstevel@tonic-gate void
i_ndi_busop_access_enter(dev_info_t * dip,ddi_acc_handle_t handle)7200Sstevel@tonic-gate i_ndi_busop_access_enter(dev_info_t *dip, ddi_acc_handle_t handle)
7210Sstevel@tonic-gate {
7220Sstevel@tonic-gate 	dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent;
7230Sstevel@tonic-gate 
7240Sstevel@tonic-gate 	/* Valid operation for BUSO_REV_6 and above */
7250Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6)
7260Sstevel@tonic-gate 		return;
7270Sstevel@tonic-gate 
7280Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_enter == NULL)
7290Sstevel@tonic-gate 		return;
7300Sstevel@tonic-gate 
7310Sstevel@tonic-gate 	(*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_enter)
7320Sstevel@tonic-gate 	    (pdip, handle);
7330Sstevel@tonic-gate }
7340Sstevel@tonic-gate 
7350Sstevel@tonic-gate void
i_ndi_busop_access_exit(dev_info_t * dip,ddi_acc_handle_t handle)7360Sstevel@tonic-gate i_ndi_busop_access_exit(dev_info_t *dip, ddi_acc_handle_t handle)
7370Sstevel@tonic-gate {
7380Sstevel@tonic-gate 	dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent;
7390Sstevel@tonic-gate 
7400Sstevel@tonic-gate 	/* Valid operation for BUSO_REV_6 and above */
7410Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6)
7420Sstevel@tonic-gate 		return;
7430Sstevel@tonic-gate 
7440Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_exit == NULL)
7450Sstevel@tonic-gate 		return;
7460Sstevel@tonic-gate 
7470Sstevel@tonic-gate 	(*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_exit)(pdip, handle);
7480Sstevel@tonic-gate }
749