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