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