xref: /onnv-gate/usr/src/uts/common/os/ndifm.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate /*
30*0Sstevel@tonic-gate  * Fault Management for Nexus Device Drivers
31*0Sstevel@tonic-gate  *
32*0Sstevel@tonic-gate  * In addition to implementing and supporting Fault Management for Device
33*0Sstevel@tonic-gate  * Drivers (ddifm.c), nexus drivers must support their children by
34*0Sstevel@tonic-gate  * reporting FM capabilities, intializing interrupt block cookies
35*0Sstevel@tonic-gate  * for error handling callbacks and caching mapped resources for lookup
36*0Sstevel@tonic-gate  * during the detection of an IO transaction error.
37*0Sstevel@tonic-gate  *
38*0Sstevel@tonic-gate  * It is typically the nexus driver that receives an error indication
39*0Sstevel@tonic-gate  * for a fault that may have occurred in the data path of an IO transaction.
40*0Sstevel@tonic-gate  * Errors may be detected or received via an interrupt, a callback from
41*0Sstevel@tonic-gate  * another subsystem (e.g. a cpu trap) or examination of control data.
42*0Sstevel@tonic-gate  *
43*0Sstevel@tonic-gate  * Upon detection of an error, the nexus has a responsibility to alert
44*0Sstevel@tonic-gate  * its children of the error and the transaction associated with that
45*0Sstevel@tonic-gate  * error.  The actual implementation may vary depending upon the capabilities
46*0Sstevel@tonic-gate  * of the nexus, its underlying hardware and its children.  In this file,
47*0Sstevel@tonic-gate  * we provide support for typical nexus driver fault management tasks.
48*0Sstevel@tonic-gate  *
49*0Sstevel@tonic-gate  * Fault Management Initialization
50*0Sstevel@tonic-gate  *
51*0Sstevel@tonic-gate  *      Nexus drivers must implement two new busops, bus_fm_init() and
52*0Sstevel@tonic-gate  *      bus_fm_fini().  bus_fm_init() is called from a child nexus or device
53*0Sstevel@tonic-gate  *      driver and is expected to initialize any per-child state and return
54*0Sstevel@tonic-gate  *      the FM and error interrupt priority levels of the nexus driver.
55*0Sstevel@tonic-gate  *      Similarly, bus_fm_fini() is called by child drivers and should
56*0Sstevel@tonic-gate  *      clean-up any resources allocated during bus_fm_init().
57*0Sstevel@tonic-gate  *      These functions are called from passive kernel context, typically from
58*0Sstevel@tonic-gate  *      driver attach(9F) and detach(9F) entry points.
59*0Sstevel@tonic-gate  *
60*0Sstevel@tonic-gate  * Error Handler Dispatching
61*0Sstevel@tonic-gate  *
62*0Sstevel@tonic-gate  *      Nexus drivers implemented to support error handler capabilities
63*0Sstevel@tonic-gate  *	should invoke registered error handler callbacks for child drivers
64*0Sstevel@tonic-gate  *	thought to be involved in the error.
65*0Sstevel@tonic-gate  *	ndi_fm_handler_dispatch() is used to invoke
66*0Sstevel@tonic-gate  *      all error handlers and returns one of the following status
67*0Sstevel@tonic-gate  *      indications:
68*0Sstevel@tonic-gate  *
69*0Sstevel@tonic-gate  *      DDI_FM_OK - No errors found by any child
70*0Sstevel@tonic-gate  *      DDI_FM_FATAL - one or more children have detected a fatal error
71*0Sstevel@tonic-gate  *      DDI_FM_NONFATAL - no fatal errors, but one or more children have
72*0Sstevel@tonic-gate  *                            detected a non-fatal error
73*0Sstevel@tonic-gate  *
74*0Sstevel@tonic-gate  *      ndi_fm_handler_dispatch() may be called in any context
75*0Sstevel@tonic-gate  *      subject to the constraints specified by the interrupt iblock cookie
76*0Sstevel@tonic-gate  *      returned during initialization.
77*0Sstevel@tonic-gate  *
78*0Sstevel@tonic-gate  * Protected Accesses
79*0Sstevel@tonic-gate  *
80*0Sstevel@tonic-gate  *      When an access handle is mapped or a DMA handle is bound via the
81*0Sstevel@tonic-gate  *      standard busops, bus_map() or bus_dma_bindhdl(), a child driver
82*0Sstevel@tonic-gate  *      implemented to support DDI_FM_ACCCHK_CAPABLE or
83*0Sstevel@tonic-gate  *	DDI_FM_DMACHK_CAPABLE capabilites
84*0Sstevel@tonic-gate  *	expects the nexus to flag any errors detected for transactions
85*0Sstevel@tonic-gate  *	associated with the mapped or bound handles.
86*0Sstevel@tonic-gate  *
87*0Sstevel@tonic-gate  *      Children nexus or device drivers will set the following flags
88*0Sstevel@tonic-gate  *      in their ddi_device_access or dma_attr_flags when requesting
89*0Sstevel@tonic-gate  *      the an access or DMA handle mapping:
90*0Sstevel@tonic-gate  *
91*0Sstevel@tonic-gate  *      DDI_DMA_FLAGERR - nexus should set error status for any errors
92*0Sstevel@tonic-gate  *                              detected for a failed DMA transaction.
93*0Sstevel@tonic-gate  *      DDI_ACC_FLAGERR - nexus should set error status for any errors
94*0Sstevel@tonic-gate  *                              detected for a failed PIO transaction.
95*0Sstevel@tonic-gate  *
96*0Sstevel@tonic-gate  *      A nexus is expected to provide additional error detection and
97*0Sstevel@tonic-gate  *      handling for handles with these flags set.
98*0Sstevel@tonic-gate  *
99*0Sstevel@tonic-gate  * Exclusive Bus Access
100*0Sstevel@tonic-gate  *
101*0Sstevel@tonic-gate  *      In cases where a driver requires a high level of fault tolerance
102*0Sstevel@tonic-gate  *      for a programmed IO transaction, it is neccessary to grant exclusive
103*0Sstevel@tonic-gate  *      access to the bus resource.  Exclusivity guarantees that a fault
104*0Sstevel@tonic-gate  *      resulting from a transaction on the bus can be easily traced and
105*0Sstevel@tonic-gate  *      reported to the driver requesting the transaction.
106*0Sstevel@tonic-gate  *
107*0Sstevel@tonic-gate  *      Nexus drivers must implement two new busops to support exclusive
108*0Sstevel@tonic-gate  *      access, bus_fm_access_enter() and bus_fm_access_exit().  The IO
109*0Sstevel@tonic-gate  *      framework will use these functions when it must set-up access
110*0Sstevel@tonic-gate  *      handles that set devacc_attr_access to DDI_ACC_CAUTIOUS in
111*0Sstevel@tonic-gate  *      their ddi_device_acc_attr_t request.
112*0Sstevel@tonic-gate  *
113*0Sstevel@tonic-gate  *      Upon receipt of a bus_fm_access_enter() request, the nexus must prevent
114*0Sstevel@tonic-gate  *      all other access requests until it receives bus_fm_access_exit()
115*0Sstevel@tonic-gate  *      for the requested bus instance. bus_fm_access_enter() and
116*0Sstevel@tonic-gate  *	bus_fm_access_exit() may be called from user, kernel or kernel
117*0Sstevel@tonic-gate  *	interrupt context.
118*0Sstevel@tonic-gate  *
119*0Sstevel@tonic-gate  * Access and DMA Handle Caching
120*0Sstevel@tonic-gate  *
121*0Sstevel@tonic-gate  *      To aid a nexus driver in associating access or DMA handles with
122*0Sstevel@tonic-gate  *      a detected error, the nexus should cache all handles that are
123*0Sstevel@tonic-gate  *      associated with DDI_ACC_FLAGERR, DDI_ACC_CAUTIOUS_ACC or
124*0Sstevel@tonic-gate  *	DDI_DMA_FLAGERR requests from its children.  ndi_fmc_insert() is
125*0Sstevel@tonic-gate  *	called by a nexus to cache handles with the above protection flags
126*0Sstevel@tonic-gate  *	and ndi_fmc_remove() is called when that handle is unmapped or
127*0Sstevel@tonic-gate  *	unbound by the requesting child.  ndi_fmc_insert() and
128*0Sstevel@tonic-gate  *	ndi_fmc_remove() may be called from any user or kernel context.
129*0Sstevel@tonic-gate  *
130*0Sstevel@tonic-gate  *	FM caches are allocated during ddi_fm_init() and maintained
131*0Sstevel@tonic-gate  *	as an array of elements that may be on one of two lists:
132*0Sstevel@tonic-gate  *	free or active.  The free list is a singly-linked list of
133*0Sstevel@tonic-gate  *	elements available for activity.  ndi_fm_insert() moves the
134*0Sstevel@tonic-gate  *	element at the head of the free to the active list.  The active
135*0Sstevel@tonic-gate  *	list is a doubly-linked searchable list.
136*0Sstevel@tonic-gate  *	When a handle is unmapped or unbound, its associated cache
137*0Sstevel@tonic-gate  *	entry is removed from the active list back to the free list.
138*0Sstevel@tonic-gate  *
139*0Sstevel@tonic-gate  *      Upon detection of an error, the nexus may invoke ndi_fmc_error() to
140*0Sstevel@tonic-gate  *      iterate over the handle cache of one or more of its FM compliant
141*0Sstevel@tonic-gate  *      children.  A comparison callback function is provided upon each
142*0Sstevel@tonic-gate  *      invocation of ndi_fmc_error() to tell the IO framework if a
143*0Sstevel@tonic-gate  *      handle is associated with an error.  If so, the framework will
144*0Sstevel@tonic-gate  *      set the error status for that handle before returning from
145*0Sstevel@tonic-gate  *      ndi_fmc_error().
146*0Sstevel@tonic-gate  *
147*0Sstevel@tonic-gate  *      ndi_fmc_error() may be called in any context
148*0Sstevel@tonic-gate  *      subject to the constraints specified by the interrupt iblock cookie
149*0Sstevel@tonic-gate  *      returned during initialization of the nexus and its children.
150*0Sstevel@tonic-gate  *
151*0Sstevel@tonic-gate  */
152*0Sstevel@tonic-gate 
153*0Sstevel@tonic-gate #include <sys/types.h>
154*0Sstevel@tonic-gate #include <sys/param.h>
155*0Sstevel@tonic-gate #include <sys/debug.h>
156*0Sstevel@tonic-gate #include <sys/sunddi.h>
157*0Sstevel@tonic-gate #include <sys/sunndi.h>
158*0Sstevel@tonic-gate #include <sys/ddi.h>
159*0Sstevel@tonic-gate #include <sys/ndi_impldefs.h>
160*0Sstevel@tonic-gate #include <sys/devctl.h>
161*0Sstevel@tonic-gate #include <sys/nvpair.h>
162*0Sstevel@tonic-gate #include <sys/ddifm.h>
163*0Sstevel@tonic-gate #include <sys/ndifm.h>
164*0Sstevel@tonic-gate #include <sys/spl.h>
165*0Sstevel@tonic-gate #include <sys/sysmacros.h>
166*0Sstevel@tonic-gate #include <sys/devops.h>
167*0Sstevel@tonic-gate #include <sys/atomic.h>
168*0Sstevel@tonic-gate #include <sys/fm/io/ddi.h>
169*0Sstevel@tonic-gate 
170*0Sstevel@tonic-gate /*
171*0Sstevel@tonic-gate  * Allocate and initialize a fault management resource cache
172*0Sstevel@tonic-gate  * A fault management cache consists of a set of cache elements that
173*0Sstevel@tonic-gate  * may be on one of two lists: free or active.
174*0Sstevel@tonic-gate  *
175*0Sstevel@tonic-gate  * At creation time, every element but one is placed on the free list
176*0Sstevel@tonic-gate  * except for the first element.  This element is reserved as the first
177*0Sstevel@tonic-gate  * element of the active list and serves as an anchor for the active
178*0Sstevel@tonic-gate  * list in ndi_fmc_insert() and ndi_fmc_remove().  In these functions,
179*0Sstevel@tonic-gate  * it is not neccessary to check for the existence or validity of
180*0Sstevel@tonic-gate  * the active list.
181*0Sstevel@tonic-gate  */
182*0Sstevel@tonic-gate void
183*0Sstevel@tonic-gate i_ndi_fmc_create(ndi_fmc_t **fcpp, int qlen, ddi_iblock_cookie_t ibc)
184*0Sstevel@tonic-gate {
185*0Sstevel@tonic-gate 	ndi_fmc_t *fcp;
186*0Sstevel@tonic-gate 	ndi_fmcentry_t *fep;
187*0Sstevel@tonic-gate 
188*0Sstevel@tonic-gate 	ASSERT(qlen > 1);
189*0Sstevel@tonic-gate 
190*0Sstevel@tonic-gate 	fcp = kmem_zalloc(sizeof (ndi_fmc_t), KM_SLEEP);
191*0Sstevel@tonic-gate 	mutex_init(&fcp->fc_lock, NULL, MUTEX_DRIVER, ibc);
192*0Sstevel@tonic-gate 
193*0Sstevel@tonic-gate 	/* Preallocate and initialize entries for this fm cache */
194*0Sstevel@tonic-gate 	fcp->fc_elems = kmem_zalloc(qlen * sizeof (ndi_fmcentry_t), KM_SLEEP);
195*0Sstevel@tonic-gate 
196*0Sstevel@tonic-gate 	fcp->fc_len = qlen;
197*0Sstevel@tonic-gate 
198*0Sstevel@tonic-gate 	/* Intialize the active and free lists */
199*0Sstevel@tonic-gate 	fcp->fc_active = fcp->fc_tail = fcp->fc_elems;
200*0Sstevel@tonic-gate 	fcp->fc_free = fcp->fc_elems + 1;
201*0Sstevel@tonic-gate 	qlen--;
202*0Sstevel@tonic-gate 	for (fep = fcp->fc_free; qlen > 1; qlen--) {
203*0Sstevel@tonic-gate 		fep->fce_prev = fep + 1;
204*0Sstevel@tonic-gate 		fep++;
205*0Sstevel@tonic-gate 	}
206*0Sstevel@tonic-gate 
207*0Sstevel@tonic-gate 	*fcpp = fcp;
208*0Sstevel@tonic-gate }
209*0Sstevel@tonic-gate 
210*0Sstevel@tonic-gate /*
211*0Sstevel@tonic-gate  * Destroy and resources associated with the given fault management cache.
212*0Sstevel@tonic-gate  */
213*0Sstevel@tonic-gate void
214*0Sstevel@tonic-gate i_ndi_fmc_destroy(ndi_fmc_t *fcp)
215*0Sstevel@tonic-gate {
216*0Sstevel@tonic-gate 	if (fcp == NULL)
217*0Sstevel@tonic-gate 		return;
218*0Sstevel@tonic-gate 
219*0Sstevel@tonic-gate 	kmem_free(fcp->fc_elems, fcp->fc_len * sizeof (ndi_fmcentry_t));
220*0Sstevel@tonic-gate 	kmem_free(fcp, sizeof (ndi_fmc_t));
221*0Sstevel@tonic-gate }
222*0Sstevel@tonic-gate 
223*0Sstevel@tonic-gate /*
224*0Sstevel@tonic-gate  * Grow an existing fault management cache by grow_sz number of entries
225*0Sstevel@tonic-gate  */
226*0Sstevel@tonic-gate static int
227*0Sstevel@tonic-gate fmc_grow(ndi_fmc_t *fcp, int flag, int grow_sz)
228*0Sstevel@tonic-gate {
229*0Sstevel@tonic-gate 	int olen, nlen;
230*0Sstevel@tonic-gate 	void *resource;
231*0Sstevel@tonic-gate 	ndi_fmcentry_t *ncp, *oep, *nep, *nnep;
232*0Sstevel@tonic-gate 
233*0Sstevel@tonic-gate 	ASSERT(grow_sz);
234*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&fcp->fc_lock));
235*0Sstevel@tonic-gate 
236*0Sstevel@tonic-gate 	/* Allocate a new cache */
237*0Sstevel@tonic-gate 	nlen = grow_sz + fcp->fc_len;
238*0Sstevel@tonic-gate 	if ((ncp = kmem_zalloc(nlen * sizeof (ndi_fmcentry_t),
239*0Sstevel@tonic-gate 	    KM_NOSLEEP)) == NULL)
240*0Sstevel@tonic-gate 		return (1);
241*0Sstevel@tonic-gate 
242*0Sstevel@tonic-gate 	/* Migrate old cache to new cache */
243*0Sstevel@tonic-gate 	oep = fcp->fc_elems;
244*0Sstevel@tonic-gate 	olen = fcp->fc_len;
245*0Sstevel@tonic-gate 	for (nep = ncp; ; olen--) {
246*0Sstevel@tonic-gate 		resource = nep->fce_resource = oep->fce_resource;
247*0Sstevel@tonic-gate 		nep->fce_bus_specific = oep->fce_bus_specific;
248*0Sstevel@tonic-gate 		if (resource) {
249*0Sstevel@tonic-gate 			if (flag == DMA_HANDLE) {
250*0Sstevel@tonic-gate 				((ddi_dma_impl_t *)resource)->
251*0Sstevel@tonic-gate 				    dmai_error.err_fep = nep;
252*0Sstevel@tonic-gate 			} else if (flag == ACC_HANDLE) {
253*0Sstevel@tonic-gate 				((ddi_acc_impl_t *)resource)->
254*0Sstevel@tonic-gate 				    ahi_err->err_fep = nep;
255*0Sstevel@tonic-gate 			}
256*0Sstevel@tonic-gate 		}
257*0Sstevel@tonic-gate 
258*0Sstevel@tonic-gate 		/*
259*0Sstevel@tonic-gate 		 * This is the last entry.  Set the tail pointer and
260*0Sstevel@tonic-gate 		 * terminate processing of the old cache.
261*0Sstevel@tonic-gate 		 */
262*0Sstevel@tonic-gate 		if (olen == 1) {
263*0Sstevel@tonic-gate 			fcp->fc_tail = nep;
264*0Sstevel@tonic-gate 			++nep;
265*0Sstevel@tonic-gate 			break;
266*0Sstevel@tonic-gate 		}
267*0Sstevel@tonic-gate 
268*0Sstevel@tonic-gate 		/*
269*0Sstevel@tonic-gate 		 * Set the next and previous pointer for the new cache
270*0Sstevel@tonic-gate 		 * entry.
271*0Sstevel@tonic-gate 		 */
272*0Sstevel@tonic-gate 		nnep = nep + 1;
273*0Sstevel@tonic-gate 		nep->fce_next = nnep;
274*0Sstevel@tonic-gate 		nnep->fce_prev = nep;
275*0Sstevel@tonic-gate 
276*0Sstevel@tonic-gate 		/* Advance to the next entry */
277*0Sstevel@tonic-gate 		++oep;
278*0Sstevel@tonic-gate 		nep = nnep;
279*0Sstevel@tonic-gate 	}
280*0Sstevel@tonic-gate 
281*0Sstevel@tonic-gate 	kmem_free(fcp->fc_elems, fcp->fc_len * sizeof (ndi_fmcentry_t));
282*0Sstevel@tonic-gate 
283*0Sstevel@tonic-gate 	/* Initialize and add remaining new cache entries to the free list */
284*0Sstevel@tonic-gate 	olen = fcp->fc_len + 1;
285*0Sstevel@tonic-gate 	fcp->fc_len = nlen;
286*0Sstevel@tonic-gate 	for (fcp->fc_free = nep; nlen > olen; nlen--) {
287*0Sstevel@tonic-gate 		nep->fce_prev = nep + 1;
288*0Sstevel@tonic-gate 		nep++;
289*0Sstevel@tonic-gate 	}
290*0Sstevel@tonic-gate 
291*0Sstevel@tonic-gate 	fcp->fc_active = ncp;
292*0Sstevel@tonic-gate 	fcp->fc_elems = ncp;
293*0Sstevel@tonic-gate 
294*0Sstevel@tonic-gate 	return (0);
295*0Sstevel@tonic-gate }
296*0Sstevel@tonic-gate 
297*0Sstevel@tonic-gate /*
298*0Sstevel@tonic-gate  * ndi_fmc_insert -
299*0Sstevel@tonic-gate  * 	Add a new entry to the specified cache.
300*0Sstevel@tonic-gate  *
301*0Sstevel@tonic-gate  * 	This function must be called at or below LOCK_LEVEL
302*0Sstevel@tonic-gate  */
303*0Sstevel@tonic-gate void
304*0Sstevel@tonic-gate ndi_fmc_insert(dev_info_t *dip, int flag, void *resource, void *bus_specific)
305*0Sstevel@tonic-gate {
306*0Sstevel@tonic-gate 	struct dev_info *devi = DEVI(dip);
307*0Sstevel@tonic-gate 	ndi_fmc_t *fcp;
308*0Sstevel@tonic-gate 	ndi_fmcentry_t *fep, **fpp;
309*0Sstevel@tonic-gate 	struct i_ddi_fmhdl *fmhdl;
310*0Sstevel@tonic-gate 
311*0Sstevel@tonic-gate 	ASSERT(devi);
312*0Sstevel@tonic-gate 	ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE);
313*0Sstevel@tonic-gate 
314*0Sstevel@tonic-gate 	fmhdl = devi->devi_fmhdl;
315*0Sstevel@tonic-gate 	if (fmhdl == NULL) {
316*0Sstevel@tonic-gate 		i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_NOSLEEP);
317*0Sstevel@tonic-gate 		return;
318*0Sstevel@tonic-gate 	}
319*0Sstevel@tonic-gate 
320*0Sstevel@tonic-gate 	if (flag == DMA_HANDLE) {
321*0Sstevel@tonic-gate 		if (!DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap)) {
322*0Sstevel@tonic-gate 			i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL,
323*0Sstevel@tonic-gate 			    DDI_NOSLEEP);
324*0Sstevel@tonic-gate 			return;
325*0Sstevel@tonic-gate 		}
326*0Sstevel@tonic-gate 		fcp = fmhdl->fh_dma_cache;
327*0Sstevel@tonic-gate 		fpp = &((ddi_dma_impl_t *)resource)->dmai_error.err_fep;
328*0Sstevel@tonic-gate 	} else if (flag == ACC_HANDLE) {
329*0Sstevel@tonic-gate 		if (!DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) {
330*0Sstevel@tonic-gate 			i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL,
331*0Sstevel@tonic-gate 			    DDI_NOSLEEP);
332*0Sstevel@tonic-gate 			return;
333*0Sstevel@tonic-gate 		}
334*0Sstevel@tonic-gate 		fcp = fmhdl->fh_acc_cache;
335*0Sstevel@tonic-gate 		fpp = &((ddi_acc_impl_t *)resource)->ahi_err->err_fep;
336*0Sstevel@tonic-gate 	}
337*0Sstevel@tonic-gate 	ASSERT(*fpp == NULL);
338*0Sstevel@tonic-gate 
339*0Sstevel@tonic-gate 	mutex_enter(&fcp->fc_lock);
340*0Sstevel@tonic-gate 
341*0Sstevel@tonic-gate 	/* Get an entry from the free list */
342*0Sstevel@tonic-gate 	fep = fcp->fc_free;
343*0Sstevel@tonic-gate 	if (fep == NULL) {
344*0Sstevel@tonic-gate 		if (fmc_grow(fcp, flag,
345*0Sstevel@tonic-gate 		    (flag == ACC_HANDLE ? default_acccache_sz :
346*0Sstevel@tonic-gate 		    default_dmacache_sz)) != 0) {
347*0Sstevel@tonic-gate 
348*0Sstevel@tonic-gate 			/* Unable to get an entry or grow this cache */
349*0Sstevel@tonic-gate 			atomic_add_64(
350*0Sstevel@tonic-gate 			    &fmhdl->fh_kstat.fek_fmc_full.value.ui64, 1);
351*0Sstevel@tonic-gate 			mutex_exit(&fcp->fc_lock);
352*0Sstevel@tonic-gate 			return;
353*0Sstevel@tonic-gate 		}
354*0Sstevel@tonic-gate 		atomic_add_64(&fmhdl->fh_kstat.fek_fmc_grew.value.ui64, 1);
355*0Sstevel@tonic-gate 		fep = fcp->fc_free;
356*0Sstevel@tonic-gate 	}
357*0Sstevel@tonic-gate 	fcp->fc_free = fep->fce_prev;
358*0Sstevel@tonic-gate 
359*0Sstevel@tonic-gate 	/*
360*0Sstevel@tonic-gate 	 * Set-up the handle resource and bus_specific information.
361*0Sstevel@tonic-gate 	 * Also remember the pointer back to the cache for quick removal.
362*0Sstevel@tonic-gate 	 */
363*0Sstevel@tonic-gate 	fep->fce_bus_specific = bus_specific;
364*0Sstevel@tonic-gate 	fep->fce_resource = resource;
365*0Sstevel@tonic-gate 	fep->fce_next = NULL;
366*0Sstevel@tonic-gate 	*fpp = fep;
367*0Sstevel@tonic-gate 
368*0Sstevel@tonic-gate 	/* Add entry to the end of the active list */
369*0Sstevel@tonic-gate 	fep->fce_prev = fcp->fc_tail;
370*0Sstevel@tonic-gate 	fcp->fc_tail->fce_next = fep;
371*0Sstevel@tonic-gate 	fcp->fc_tail = fep;
372*0Sstevel@tonic-gate 	mutex_exit(&fcp->fc_lock);
373*0Sstevel@tonic-gate }
374*0Sstevel@tonic-gate 
375*0Sstevel@tonic-gate /*
376*0Sstevel@tonic-gate  * 	Remove an entry from the specified cache of access or dma mappings
377*0Sstevel@tonic-gate  *
378*0Sstevel@tonic-gate  * 	This function must be called at or below LOCK_LEVEL.
379*0Sstevel@tonic-gate  */
380*0Sstevel@tonic-gate void
381*0Sstevel@tonic-gate ndi_fmc_remove(dev_info_t *dip, int flag, const void *resource)
382*0Sstevel@tonic-gate {
383*0Sstevel@tonic-gate 	ndi_fmc_t *fcp;
384*0Sstevel@tonic-gate 	ndi_fmcentry_t *fep;
385*0Sstevel@tonic-gate 	struct dev_info *devi = DEVI(dip);
386*0Sstevel@tonic-gate 	struct i_ddi_fmhdl *fmhdl;
387*0Sstevel@tonic-gate 
388*0Sstevel@tonic-gate 	ASSERT(devi);
389*0Sstevel@tonic-gate 	ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE);
390*0Sstevel@tonic-gate 
391*0Sstevel@tonic-gate 	fmhdl = devi->devi_fmhdl;
392*0Sstevel@tonic-gate 	if (fmhdl == NULL) {
393*0Sstevel@tonic-gate 		i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_NOSLEEP);
394*0Sstevel@tonic-gate 		return;
395*0Sstevel@tonic-gate 	}
396*0Sstevel@tonic-gate 
397*0Sstevel@tonic-gate 	/* Find cache entry pointer for this resource */
398*0Sstevel@tonic-gate 	if (flag == DMA_HANDLE) {
399*0Sstevel@tonic-gate 		if (!DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap)) {
400*0Sstevel@tonic-gate 			i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL,
401*0Sstevel@tonic-gate 			    DDI_NOSLEEP);
402*0Sstevel@tonic-gate 			return;
403*0Sstevel@tonic-gate 		}
404*0Sstevel@tonic-gate 		fcp = fmhdl->fh_dma_cache;
405*0Sstevel@tonic-gate 
406*0Sstevel@tonic-gate 		ASSERT(fcp);
407*0Sstevel@tonic-gate 
408*0Sstevel@tonic-gate 		mutex_enter(&fcp->fc_lock);
409*0Sstevel@tonic-gate 		fep = ((ddi_dma_impl_t *)resource)->dmai_error.err_fep;
410*0Sstevel@tonic-gate 		((ddi_dma_impl_t *)resource)->dmai_error.err_fep = NULL;
411*0Sstevel@tonic-gate 	} else if (flag == ACC_HANDLE) {
412*0Sstevel@tonic-gate 		if (!DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) {
413*0Sstevel@tonic-gate 			i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL,
414*0Sstevel@tonic-gate 			    DDI_NOSLEEP);
415*0Sstevel@tonic-gate 			return;
416*0Sstevel@tonic-gate 		}
417*0Sstevel@tonic-gate 		fcp = fmhdl->fh_acc_cache;
418*0Sstevel@tonic-gate 
419*0Sstevel@tonic-gate 		ASSERT(fcp);
420*0Sstevel@tonic-gate 
421*0Sstevel@tonic-gate 		mutex_enter(&fcp->fc_lock);
422*0Sstevel@tonic-gate 		fep = ((ddi_acc_impl_t *)resource)->ahi_err->err_fep;
423*0Sstevel@tonic-gate 		((ddi_acc_impl_t *)resource)->ahi_err->err_fep = NULL;
424*0Sstevel@tonic-gate 	}
425*0Sstevel@tonic-gate 
426*0Sstevel@tonic-gate 	/*
427*0Sstevel@tonic-gate 	 * Resource not in cache, return
428*0Sstevel@tonic-gate 	 */
429*0Sstevel@tonic-gate 	if (fep == NULL) {
430*0Sstevel@tonic-gate 		mutex_exit(&fcp->fc_lock);
431*0Sstevel@tonic-gate 		return;
432*0Sstevel@tonic-gate 	}
433*0Sstevel@tonic-gate 
434*0Sstevel@tonic-gate 	fep->fce_prev->fce_next = fep->fce_next;
435*0Sstevel@tonic-gate 	if (fep == fcp->fc_tail)
436*0Sstevel@tonic-gate 		fcp->fc_tail = fep->fce_prev;
437*0Sstevel@tonic-gate 	else
438*0Sstevel@tonic-gate 		fep->fce_next->fce_prev = fep->fce_prev;
439*0Sstevel@tonic-gate 
440*0Sstevel@tonic-gate 	/* Add entry back to the free list */
441*0Sstevel@tonic-gate 	fep->fce_prev = fcp->fc_free;
442*0Sstevel@tonic-gate 	fcp->fc_free = fep;
443*0Sstevel@tonic-gate 	mutex_exit(&fcp->fc_lock);
444*0Sstevel@tonic-gate }
445*0Sstevel@tonic-gate 
446*0Sstevel@tonic-gate 
447*0Sstevel@tonic-gate /*
448*0Sstevel@tonic-gate  * Check error state against the handle resource stored in the specified
449*0Sstevel@tonic-gate  * FM cache.  If tdip != NULL, we check only the cache entries for tdip.
450*0Sstevel@tonic-gate  * The caller must ensure that tdip is valid throughout the call and
451*0Sstevel@tonic-gate  * all FM data structures can be safely accesses.
452*0Sstevel@tonic-gate  *
453*0Sstevel@tonic-gate  * If tdip == NULL, we check all children that have registered their
454*0Sstevel@tonic-gate  * FM_DMA_CHK or FM_ACC_CHK capabilities.
455*0Sstevel@tonic-gate  *
456*0Sstevel@tonic-gate  * The following status values may be returned:
457*0Sstevel@tonic-gate  *
458*0Sstevel@tonic-gate  *	DDI_FM_FATAL - if at least one cache entry comparison yields a
459*0Sstevel@tonic-gate  *			fatal error.
460*0Sstevel@tonic-gate  *
461*0Sstevel@tonic-gate  *	DDI_FM_NONFATAL - if at least one cache entry comparison yields a
462*0Sstevel@tonic-gate  *			non-fatal error and no comparison yields a fatal error.
463*0Sstevel@tonic-gate  *
464*0Sstevel@tonic-gate  *	DDI_FM_UNKNOWN - cache entry comparisons did not yield fatal or
465*0Sstevel@tonic-gate  *			non-fatal errors.
466*0Sstevel@tonic-gate  *
467*0Sstevel@tonic-gate  */
468*0Sstevel@tonic-gate int
469*0Sstevel@tonic-gate ndi_fmc_error(dev_info_t *dip, dev_info_t *tdip, int flag,
470*0Sstevel@tonic-gate     ndi_fmcompare_t compare_func, uint64_t ena, const void *bus_err_state)
471*0Sstevel@tonic-gate {
472*0Sstevel@tonic-gate 	int status, fatal = 0, nonfatal = 0;
473*0Sstevel@tonic-gate 	ndi_fmc_t *fcp;
474*0Sstevel@tonic-gate 	ddi_fm_error_t derr;
475*0Sstevel@tonic-gate 	ndi_fmcentry_t *fep;
476*0Sstevel@tonic-gate 	struct i_ddi_fmhdl *fmhdl;
477*0Sstevel@tonic-gate 	struct i_ddi_fmtgt *tgt;
478*0Sstevel@tonic-gate 
479*0Sstevel@tonic-gate 	ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE);
480*0Sstevel@tonic-gate 
481*0Sstevel@tonic-gate 	i_ddi_fm_handler_enter(dip);
482*0Sstevel@tonic-gate 	fmhdl = DEVI(dip)->devi_fmhdl;
483*0Sstevel@tonic-gate 	ASSERT(fmhdl);
484*0Sstevel@tonic-gate 	for (tgt = fmhdl->fh_tgts; tgt != NULL; tgt = tgt->ft_next) {
485*0Sstevel@tonic-gate 
486*0Sstevel@tonic-gate 		if (tdip != NULL && tdip != tgt->ft_dip)
487*0Sstevel@tonic-gate 			continue;
488*0Sstevel@tonic-gate 
489*0Sstevel@tonic-gate 		fmhdl = DEVI(tgt->ft_dip)->devi_fmhdl;
490*0Sstevel@tonic-gate 		fcp = NULL;
491*0Sstevel@tonic-gate 		bzero(&derr, sizeof (ddi_fm_error_t));
492*0Sstevel@tonic-gate 		derr.fme_version = DDI_FME_VERSION;
493*0Sstevel@tonic-gate 		derr.fme_flag = DDI_FM_ERR_UNEXPECTED;
494*0Sstevel@tonic-gate 		status = DDI_FM_UNKNOWN;
495*0Sstevel@tonic-gate 
496*0Sstevel@tonic-gate 		if (flag == DMA_HANDLE && DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap)) {
497*0Sstevel@tonic-gate 			fcp = fmhdl->fh_dma_cache;
498*0Sstevel@tonic-gate 			ASSERT(fcp);
499*0Sstevel@tonic-gate 		} else if (flag == ACC_HANDLE &&
500*0Sstevel@tonic-gate 		    DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) {
501*0Sstevel@tonic-gate 			fcp = fmhdl->fh_acc_cache;
502*0Sstevel@tonic-gate 			ASSERT(fcp);
503*0Sstevel@tonic-gate 		}
504*0Sstevel@tonic-gate 
505*0Sstevel@tonic-gate 		if (fcp != NULL) {
506*0Sstevel@tonic-gate 
507*0Sstevel@tonic-gate 			/*
508*0Sstevel@tonic-gate 			 * Check active resource entries
509*0Sstevel@tonic-gate 			 */
510*0Sstevel@tonic-gate 			mutex_enter(&fcp->fc_lock);
511*0Sstevel@tonic-gate 			for (fep = fcp->fc_active->fce_next; fep != NULL;
512*0Sstevel@tonic-gate 			    fep = fep->fce_next) {
513*0Sstevel@tonic-gate 
514*0Sstevel@tonic-gate 				/*
515*0Sstevel@tonic-gate 				 * Compare captured error state with handle
516*0Sstevel@tonic-gate 				 * resources.  During the comparison and
517*0Sstevel@tonic-gate 				 * subsequent error handling, we block
518*0Sstevel@tonic-gate 				 * attempts to free the cache entry.
519*0Sstevel@tonic-gate 				 */
520*0Sstevel@tonic-gate 				status = compare_func(dip, fep->fce_resource,
521*0Sstevel@tonic-gate 				    bus_err_state, fep->fce_bus_specific);
522*0Sstevel@tonic-gate 				if (status == DDI_FM_UNKNOWN ||
523*0Sstevel@tonic-gate 				    status == DDI_FM_OK)
524*0Sstevel@tonic-gate 					continue;
525*0Sstevel@tonic-gate 
526*0Sstevel@tonic-gate 				if (status == DDI_FM_FATAL)
527*0Sstevel@tonic-gate 					++fatal;
528*0Sstevel@tonic-gate 				else if (status == DDI_FM_NONFATAL)
529*0Sstevel@tonic-gate 					++nonfatal;
530*0Sstevel@tonic-gate 
531*0Sstevel@tonic-gate 				/* Set the error for this resource handle */
532*0Sstevel@tonic-gate 				if (flag == ACC_HANDLE) {
533*0Sstevel@tonic-gate 					ddi_acc_handle_t ap = fep->fce_resource;
534*0Sstevel@tonic-gate 
535*0Sstevel@tonic-gate 					i_ddi_fm_acc_err_set(ap, ena, status,
536*0Sstevel@tonic-gate 					    DDI_FM_ERR_UNEXPECTED);
537*0Sstevel@tonic-gate 					ddi_fm_acc_err_get(ap, &derr,
538*0Sstevel@tonic-gate 					    DDI_FME_VERSION);
539*0Sstevel@tonic-gate 					derr.fme_acc_handle = ap;
540*0Sstevel@tonic-gate 				} else {
541*0Sstevel@tonic-gate 					ddi_dma_handle_t dp = fep->fce_resource;
542*0Sstevel@tonic-gate 
543*0Sstevel@tonic-gate 					i_ddi_fm_dma_err_set(dp, ena, status,
544*0Sstevel@tonic-gate 					    DDI_FM_ERR_UNEXPECTED);
545*0Sstevel@tonic-gate 					ddi_fm_dma_err_get(dp, &derr,
546*0Sstevel@tonic-gate 					    DDI_FME_VERSION);
547*0Sstevel@tonic-gate 					derr.fme_dma_handle = dp;
548*0Sstevel@tonic-gate 				}
549*0Sstevel@tonic-gate 
550*0Sstevel@tonic-gate 				/*
551*0Sstevel@tonic-gate 				 * Call our child to process this error.
552*0Sstevel@tonic-gate 				 */
553*0Sstevel@tonic-gate 				derr.fme_bus_specific = (void *)bus_err_state;
554*0Sstevel@tonic-gate 				status = tgt->ft_errhdl->eh_func(tgt->ft_dip,
555*0Sstevel@tonic-gate 				    &derr, tgt->ft_errhdl->eh_impl);
556*0Sstevel@tonic-gate 
557*0Sstevel@tonic-gate 				if (status == DDI_FM_FATAL)
558*0Sstevel@tonic-gate 					++fatal;
559*0Sstevel@tonic-gate 				else if (status == DDI_FM_NONFATAL)
560*0Sstevel@tonic-gate 					++nonfatal;
561*0Sstevel@tonic-gate 			}
562*0Sstevel@tonic-gate 			mutex_exit(&fcp->fc_lock);
563*0Sstevel@tonic-gate 		}
564*0Sstevel@tonic-gate 
565*0Sstevel@tonic-gate 	}
566*0Sstevel@tonic-gate 	i_ddi_fm_handler_exit(dip);
567*0Sstevel@tonic-gate 
568*0Sstevel@tonic-gate 	if (fatal)
569*0Sstevel@tonic-gate 		return (DDI_FM_FATAL);
570*0Sstevel@tonic-gate 	else if (nonfatal)
571*0Sstevel@tonic-gate 		return (DDI_FM_NONFATAL);
572*0Sstevel@tonic-gate 
573*0Sstevel@tonic-gate 	return (DDI_FM_UNKNOWN);
574*0Sstevel@tonic-gate }
575*0Sstevel@tonic-gate 
576*0Sstevel@tonic-gate /*
577*0Sstevel@tonic-gate  * Dispatch registered error handlers for dip.  If tdip != NULL, only
578*0Sstevel@tonic-gate  * the error handler (if available) for tdip is invoked.  Otherwise,
579*0Sstevel@tonic-gate  * all registered error handlers are invoked.
580*0Sstevel@tonic-gate  *
581*0Sstevel@tonic-gate  * The following status values may be returned:
582*0Sstevel@tonic-gate  *
583*0Sstevel@tonic-gate  *	DDI_FM_FATAL - if at least one error handler returns a
584*0Sstevel@tonic-gate  *			fatal error.
585*0Sstevel@tonic-gate  *
586*0Sstevel@tonic-gate  *	DDI_FM_NONFATAL - if at least one error handler returns a
587*0Sstevel@tonic-gate  *			non-fatal error and none returned a fatal error.
588*0Sstevel@tonic-gate  *
589*0Sstevel@tonic-gate  *	DDI_FM_UNKNOWN - if at least one error handler returns
590*0Sstevel@tonic-gate  *			unknown status and none return fatal or non-fatal.
591*0Sstevel@tonic-gate  *
592*0Sstevel@tonic-gate  *	DDI_FM_OK - if all error handlers return DDI_FM_OK
593*0Sstevel@tonic-gate  */
594*0Sstevel@tonic-gate int
595*0Sstevel@tonic-gate ndi_fm_handler_dispatch(dev_info_t *dip, dev_info_t *tdip,
596*0Sstevel@tonic-gate     const ddi_fm_error_t *nerr)
597*0Sstevel@tonic-gate {
598*0Sstevel@tonic-gate 	int status;
599*0Sstevel@tonic-gate 	int unknown = 0, fatal = 0, nonfatal = 0;
600*0Sstevel@tonic-gate 	struct i_ddi_fmhdl *hdl;
601*0Sstevel@tonic-gate 	struct i_ddi_fmtgt *tgt;
602*0Sstevel@tonic-gate 
603*0Sstevel@tonic-gate 	status = DDI_FM_UNKNOWN;
604*0Sstevel@tonic-gate 
605*0Sstevel@tonic-gate 	i_ddi_fm_handler_enter(dip);
606*0Sstevel@tonic-gate 	hdl = DEVI(dip)->devi_fmhdl;
607*0Sstevel@tonic-gate 	tgt = hdl->fh_tgts;
608*0Sstevel@tonic-gate 	while (tgt != NULL) {
609*0Sstevel@tonic-gate 		if (tdip == NULL || tdip == tgt->ft_dip) {
610*0Sstevel@tonic-gate 			struct i_ddi_errhdl *errhdl;
611*0Sstevel@tonic-gate 
612*0Sstevel@tonic-gate 			errhdl = tgt->ft_errhdl;
613*0Sstevel@tonic-gate 			status = errhdl->eh_func(tgt->ft_dip, nerr,
614*0Sstevel@tonic-gate 			    errhdl->eh_impl);
615*0Sstevel@tonic-gate 
616*0Sstevel@tonic-gate 			if (status == DDI_FM_FATAL)
617*0Sstevel@tonic-gate 				++fatal;
618*0Sstevel@tonic-gate 			else if (status == DDI_FM_NONFATAL)
619*0Sstevel@tonic-gate 				++nonfatal;
620*0Sstevel@tonic-gate 			else if (status == DDI_FM_UNKNOWN)
621*0Sstevel@tonic-gate 				++unknown;
622*0Sstevel@tonic-gate 
623*0Sstevel@tonic-gate 			/* Only interested in one target */
624*0Sstevel@tonic-gate 			if (tdip != NULL)
625*0Sstevel@tonic-gate 				break;
626*0Sstevel@tonic-gate 		}
627*0Sstevel@tonic-gate 		tgt = tgt->ft_next;
628*0Sstevel@tonic-gate 	}
629*0Sstevel@tonic-gate 	i_ddi_fm_handler_exit(dip);
630*0Sstevel@tonic-gate 
631*0Sstevel@tonic-gate 	if (fatal)
632*0Sstevel@tonic-gate 		return (DDI_FM_FATAL);
633*0Sstevel@tonic-gate 	else if (nonfatal)
634*0Sstevel@tonic-gate 		return (DDI_FM_NONFATAL);
635*0Sstevel@tonic-gate 	else if (unknown)
636*0Sstevel@tonic-gate 		return (DDI_FM_UNKNOWN);
637*0Sstevel@tonic-gate 	else
638*0Sstevel@tonic-gate 		return (DDI_FM_OK);
639*0Sstevel@tonic-gate }
640*0Sstevel@tonic-gate 
641*0Sstevel@tonic-gate /*
642*0Sstevel@tonic-gate  * Set error status for specified access or DMA handle
643*0Sstevel@tonic-gate  *
644*0Sstevel@tonic-gate  * May be called in any context but caller must insure validity of
645*0Sstevel@tonic-gate  * handle.
646*0Sstevel@tonic-gate  */
647*0Sstevel@tonic-gate void
648*0Sstevel@tonic-gate ndi_fm_acc_err_set(ddi_acc_handle_t handle, ddi_fm_error_t *dfe)
649*0Sstevel@tonic-gate {
650*0Sstevel@tonic-gate 	i_ddi_fm_acc_err_set(handle, dfe->fme_ena, dfe->fme_status,
651*0Sstevel@tonic-gate 	    dfe->fme_flag);
652*0Sstevel@tonic-gate }
653*0Sstevel@tonic-gate 
654*0Sstevel@tonic-gate void
655*0Sstevel@tonic-gate ndi_fm_dma_err_set(ddi_dma_handle_t handle, ddi_fm_error_t *dfe)
656*0Sstevel@tonic-gate {
657*0Sstevel@tonic-gate 	i_ddi_fm_dma_err_set(handle, dfe->fme_ena, dfe->fme_status,
658*0Sstevel@tonic-gate 	    dfe->fme_flag);
659*0Sstevel@tonic-gate }
660*0Sstevel@tonic-gate 
661*0Sstevel@tonic-gate /*
662*0Sstevel@tonic-gate  * Call parent busop fm initialization routine.
663*0Sstevel@tonic-gate  *
664*0Sstevel@tonic-gate  * Called during driver attach(1M)
665*0Sstevel@tonic-gate  */
666*0Sstevel@tonic-gate int
667*0Sstevel@tonic-gate i_ndi_busop_fm_init(dev_info_t *dip, int tcap, ddi_iblock_cookie_t *ibc)
668*0Sstevel@tonic-gate {
669*0Sstevel@tonic-gate 	int pcap;
670*0Sstevel@tonic-gate 	dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent;
671*0Sstevel@tonic-gate 
672*0Sstevel@tonic-gate 	if (dip == ddi_root_node())
673*0Sstevel@tonic-gate 		return (ddi_system_fmcap | DDI_FM_EREPORT_CAPABLE);
674*0Sstevel@tonic-gate 
675*0Sstevel@tonic-gate 	/* Valid operation for BUSO_REV_6 and above */
676*0Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6)
677*0Sstevel@tonic-gate 		return (DDI_FM_NOT_CAPABLE);
678*0Sstevel@tonic-gate 
679*0Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_init == NULL)
680*0Sstevel@tonic-gate 		return (DDI_FM_NOT_CAPABLE);
681*0Sstevel@tonic-gate 
682*0Sstevel@tonic-gate 	pcap = (*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_init)
683*0Sstevel@tonic-gate 	    (pdip, dip, tcap, ibc);
684*0Sstevel@tonic-gate 
685*0Sstevel@tonic-gate 	return (pcap);
686*0Sstevel@tonic-gate }
687*0Sstevel@tonic-gate 
688*0Sstevel@tonic-gate /*
689*0Sstevel@tonic-gate  * Call parent busop fm clean-up routine.
690*0Sstevel@tonic-gate  *
691*0Sstevel@tonic-gate  * Called during driver detach(1M)
692*0Sstevel@tonic-gate  */
693*0Sstevel@tonic-gate void
694*0Sstevel@tonic-gate i_ndi_busop_fm_fini(dev_info_t *dip)
695*0Sstevel@tonic-gate {
696*0Sstevel@tonic-gate 	dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent;
697*0Sstevel@tonic-gate 
698*0Sstevel@tonic-gate 	if (dip == ddi_root_node())
699*0Sstevel@tonic-gate 		return;
700*0Sstevel@tonic-gate 
701*0Sstevel@tonic-gate 	/* Valid operation for BUSO_REV_6 and above */
702*0Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6)
703*0Sstevel@tonic-gate 		return;
704*0Sstevel@tonic-gate 
705*0Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_fini == NULL)
706*0Sstevel@tonic-gate 		return;
707*0Sstevel@tonic-gate 
708*0Sstevel@tonic-gate 	(*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_fini)(pdip, dip);
709*0Sstevel@tonic-gate }
710*0Sstevel@tonic-gate 
711*0Sstevel@tonic-gate /*
712*0Sstevel@tonic-gate  * The following routines provide exclusive access to a nexus resource
713*0Sstevel@tonic-gate  *
714*0Sstevel@tonic-gate  * These busops may be called in user or kernel driver context.
715*0Sstevel@tonic-gate  */
716*0Sstevel@tonic-gate void
717*0Sstevel@tonic-gate i_ndi_busop_access_enter(dev_info_t *dip, ddi_acc_handle_t handle)
718*0Sstevel@tonic-gate {
719*0Sstevel@tonic-gate 	dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent;
720*0Sstevel@tonic-gate 
721*0Sstevel@tonic-gate 	/* Valid operation for BUSO_REV_6 and above */
722*0Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6)
723*0Sstevel@tonic-gate 		return;
724*0Sstevel@tonic-gate 
725*0Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_enter == NULL)
726*0Sstevel@tonic-gate 		return;
727*0Sstevel@tonic-gate 
728*0Sstevel@tonic-gate 	(*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_enter)
729*0Sstevel@tonic-gate 	    (pdip, handle);
730*0Sstevel@tonic-gate }
731*0Sstevel@tonic-gate 
732*0Sstevel@tonic-gate void
733*0Sstevel@tonic-gate i_ndi_busop_access_exit(dev_info_t *dip, ddi_acc_handle_t handle)
734*0Sstevel@tonic-gate {
735*0Sstevel@tonic-gate 	dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent;
736*0Sstevel@tonic-gate 
737*0Sstevel@tonic-gate 	/* Valid operation for BUSO_REV_6 and above */
738*0Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6)
739*0Sstevel@tonic-gate 		return;
740*0Sstevel@tonic-gate 
741*0Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_exit == NULL)
742*0Sstevel@tonic-gate 		return;
743*0Sstevel@tonic-gate 
744*0Sstevel@tonic-gate 	(*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_exit)(pdip, handle);
745*0Sstevel@tonic-gate }
746