4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * Fault Management for Nexus Device Drivers
29 * In addition to implementing and supporting Fault Management for Device
30 * Drivers (ddifm.c), nexus drivers must support their children by
31 * reporting FM capabilities, intializing interrupt block cookies
32 * for error handling callbacks and caching mapped resources for lookup
33 * during the detection of an IO transaction error.
35 * It is typically the nexus driver that receives an error indication
36 * for a fault that may have occurred in the data path of an IO transaction.
37 * Errors may be detected or received via an interrupt, a callback from
38 * another subsystem (e.g. a cpu trap) or examination of control data.
40 * Upon detection of an error, the nexus has a responsibility to alert
41 * its children of the error and the transaction associated with that
42 * error. The actual implementation may vary depending upon the capabilities
43 * of the nexus, its underlying hardware and its children. In this file,
44 * we provide support for typical nexus driver fault management tasks.
46 * Fault Management Initialization
48 * Nexus drivers must implement two new busops, bus_fm_init() and
49 * bus_fm_fini(). bus_fm_init() is called from a child nexus or device
50 * driver and is expected to initialize any per-child state and return
51 * the FM and error interrupt priority levels of the nexus driver.
52 * Similarly, bus_fm_fini() is called by child drivers and should
53 * clean-up any resources allocated during bus_fm_init().
54 * These functions are called from passive kernel context, typically from
55 * driver attach(9F) and detach(9F) entry points.
57 * Error Handler Dispatching
59 * Nexus drivers implemented to support error handler capabilities
60 * should invoke registered error handler callbacks for child drivers
61 * thought to be involved in the error.
62 * ndi_fm_handler_dispatch() is used to invoke
63 * all error handlers and returns one of the following status
66 * DDI_FM_OK - No errors found by any child
67 * DDI_FM_FATAL - one or more children have detected a fatal error
68 * DDI_FM_NONFATAL - no fatal errors, but one or more children have
69 * detected a non-fatal error
71 * ndi_fm_handler_dispatch() may be called in any context
72 * subject to the constraints specified by the interrupt iblock cookie
73 * returned during initialization.
77 * When an access handle is mapped or a DMA handle is bound via the
78 * standard busops, bus_map() or bus_dma_bindhdl(), a child driver
79 * implemented to support DDI_FM_ACCCHK_CAPABLE or
80 * DDI_FM_DMACHK_CAPABLE capabilites
81 * expects the nexus to flag any errors detected for transactions
82 * associated with the mapped or bound handles.
84 * Children nexus or device drivers will set the following flags
85 * in their ddi_device_access or dma_attr_flags when requesting
86 * the an access or DMA handle mapping:
88 * DDI_DMA_FLAGERR - nexus should set error status for any errors
89 * detected for a failed DMA transaction.
90 * DDI_ACC_FLAGERR - nexus should set error status for any errors
91 * detected for a failed PIO transaction.
93 * A nexus is expected to provide additional error detection and
94 * handling for handles with these flags set.
96 * Exclusive Bus Access
98 * In cases where a driver requires a high level of fault tolerance
99 * for a programmed IO transaction, it is neccessary to grant exclusive
100 * access to the bus resource. Exclusivity guarantees that a fault
101 * resulting from a transaction on the bus can be easily traced and
102 * reported to the driver requesting the transaction.
104 * Nexus drivers must implement two new busops to support exclusive
105 * access, bus_fm_access_enter() and bus_fm_access_exit(). The IO
106 * framework will use these functions when it must set-up access
107 * handles that set devacc_attr_access to DDI_ACC_CAUTIOUS in
108 * their ddi_device_acc_attr_t request.
110 * Upon receipt of a bus_fm_access_enter() request, the nexus must prevent
111 * all other access requests until it receives bus_fm_access_exit()
112 * for the requested bus instance. bus_fm_access_enter() and
113 * bus_fm_access_exit() may be called from user, kernel or kernel
116 * Access and DMA Handle Caching
118 * To aid a nexus driver in associating access or DMA handles with
119 * a detected error, the nexus should cache all handles that are
120 * associated with DDI_ACC_FLAGERR, DDI_ACC_CAUTIOUS_ACC or
121 * DDI_DMA_FLAGERR requests from its children. ndi_fmc_insert() is
122 * called by a nexus to cache handles with the above protection flags
123 * and ndi_fmc_remove() is called when that handle is unmapped or
124 * unbound by the requesting child. ndi_fmc_insert() and
125 * ndi_fmc_remove() may be called from any user or kernel context.
127 * FM cache element is implemented by kmem_cache. The elements are
128 * stored in a doubly-linked searchable list. When a handle is created,
129 * ndi_fm_insert() allocates an entry from the kmem_cache and inserts
130 * the entry to the head of the list. When a handle is unmapped
131 * or unbound, ndi_fm_remove() removes its associated cache entry from
134 * Upon detection of an error, the nexus may invoke ndi_fmc_error() to
135 * iterate over the handle cache of one or more of its FM compliant
136 * children. A comparison callback function is provided upon each
137 * invocation of ndi_fmc_error() to tell the IO framework if a
138 * handle is associated with an error. If so, the framework will
139 * set the error status for that handle before returning from
142 * ndi_fmc_error() may be called in any context
143 * subject to the constraints specified by the interrupt iblock cookie
144 * returned during initialization of the nexus and its children.
148 #include <sys/types.h>
149 #include <sys/param.h>
150 #include <sys/debug.h>
151 #include <sys/sunddi.h>
152 #include <sys/sunndi.h>
154 #include <sys/ndi_impldefs.h>
155 #include <sys/devctl.h>
156 #include <sys/nvpair.h>
157 #include <sys/ddifm.h>
158 #include <sys/ndifm.h>
160 #include <sys/sysmacros.h>
161 #include <sys/devops.h>
162 #include <sys/atomic.h>
163 #include <sys/kmem.h>
164 #include <sys/fm/io/ddi.h>
166 kmem_cache_t
*ndi_fm_entry_cache
;
171 ndi_fm_entry_cache
= kmem_cache_create("ndi_fm_entry_cache",
172 sizeof (ndi_fmcentry_t
), 0, NULL
, NULL
, NULL
, NULL
, NULL
, 0);
176 * Allocate and initialize a fault management resource cache
177 * A fault management cache consists of a set of cache elements that
178 * are allocated from "ndi_fm_entry_cache".
182 i_ndi_fmc_create(ndi_fmc_t
**fcpp
, int qlen
, ddi_iblock_cookie_t ibc
)
186 fcp
= kmem_zalloc(sizeof (ndi_fmc_t
), KM_SLEEP
);
187 mutex_init(&fcp
->fc_lock
, NULL
, MUTEX_DRIVER
, ibc
);
193 * Destroy and resources associated with the given fault management cache.
196 i_ndi_fmc_destroy(ndi_fmc_t
*fcp
)
198 ndi_fmcentry_t
*fep
, *pp
;
203 /* Free all the cached entries, this should not happen though */
204 mutex_enter(&fcp
->fc_lock
);
205 for (fep
= fcp
->fc_head
; fep
!= NULL
; fep
= pp
) {
207 kmem_cache_free(ndi_fm_entry_cache
, fep
);
209 mutex_exit(&fcp
->fc_lock
);
210 mutex_destroy(&fcp
->fc_lock
);
211 kmem_free(fcp
, sizeof (ndi_fmc_t
));
216 * Add a new entry to the specified cache.
218 * This function must be called at or below LOCK_LEVEL
221 ndi_fmc_insert(dev_info_t
*dip
, int flag
, void *resource
, void *bus_specific
)
223 struct dev_info
*devi
= DEVI(dip
);
225 ndi_fmcentry_t
*fep
, **fpp
;
226 struct i_ddi_fmhdl
*fmhdl
;
229 ASSERT(flag
== DMA_HANDLE
|| flag
== ACC_HANDLE
);
231 fmhdl
= devi
->devi_fmhdl
;
236 if (flag
== DMA_HANDLE
) {
237 if (!DDI_FM_DMA_ERR_CAP(fmhdl
->fh_cap
)) {
240 fcp
= fmhdl
->fh_dma_cache
;
241 fpp
= &((ddi_dma_impl_t
*)resource
)->dmai_error
.err_fep
;
242 } else if (flag
== ACC_HANDLE
) {
243 if (!DDI_FM_ACC_ERR_CAP(fmhdl
->fh_cap
)) {
244 i_ddi_drv_ereport_post(dip
, DVR_EFMCAP
, NULL
,
248 fcp
= fmhdl
->fh_acc_cache
;
249 fpp
= &((ddi_acc_impl_t
*)resource
)->ahi_err
->err_fep
;
252 fep
= kmem_cache_alloc(ndi_fm_entry_cache
, KM_NOSLEEP
);
254 atomic_inc_64(&fmhdl
->fh_kstat
.fek_fmc_full
.value
.ui64
);
259 * Set-up the handle resource and bus_specific information.
260 * Also remember the pointer back to the cache for quick removal.
262 fep
->fce_bus_specific
= bus_specific
;
263 fep
->fce_resource
= resource
;
264 fep
->fce_next
= NULL
;
266 /* Add entry to the end of the active list */
267 mutex_enter(&fcp
->fc_lock
);
268 ASSERT(*fpp
== NULL
);
270 fep
->fce_prev
= fcp
->fc_tail
;
271 if (fcp
->fc_tail
!= NULL
)
272 fcp
->fc_tail
->fce_next
= fep
;
276 mutex_exit(&fcp
->fc_lock
);
280 * Remove an entry from the specified cache of access or dma mappings
282 * This function must be called at or below LOCK_LEVEL.
285 ndi_fmc_remove(dev_info_t
*dip
, int flag
, const void *resource
)
289 struct dev_info
*devi
= DEVI(dip
);
290 struct i_ddi_fmhdl
*fmhdl
;
293 ASSERT(flag
== DMA_HANDLE
|| flag
== ACC_HANDLE
);
295 fmhdl
= devi
->devi_fmhdl
;
300 /* Find cache entry pointer for this resource */
301 if (flag
== DMA_HANDLE
) {
302 if (!DDI_FM_DMA_ERR_CAP(fmhdl
->fh_cap
)) {
305 fcp
= fmhdl
->fh_dma_cache
;
309 mutex_enter(&fcp
->fc_lock
);
310 fep
= ((ddi_dma_impl_t
*)resource
)->dmai_error
.err_fep
;
311 ((ddi_dma_impl_t
*)resource
)->dmai_error
.err_fep
= NULL
;
312 } else if (flag
== ACC_HANDLE
) {
313 if (!DDI_FM_ACC_ERR_CAP(fmhdl
->fh_cap
)) {
314 i_ddi_drv_ereport_post(dip
, DVR_EFMCAP
, NULL
,
318 fcp
= fmhdl
->fh_acc_cache
;
322 mutex_enter(&fcp
->fc_lock
);
323 fep
= ((ddi_acc_impl_t
*)resource
)->ahi_err
->err_fep
;
324 ((ddi_acc_impl_t
*)resource
)->ahi_err
->err_fep
= NULL
;
330 * Resource not in cache, return
333 mutex_exit(&fcp
->fc_lock
);
334 atomic_inc_64(&fmhdl
->fh_kstat
.fek_fmc_miss
.value
.ui64
);
339 * Updates to FM cache pointers require us to grab fmc_lock
340 * to synchronize access to the cache for ndi_fmc_insert()
341 * and ndi_fmc_error()
343 if (fep
== fcp
->fc_head
)
344 fcp
->fc_head
= fep
->fce_next
;
346 fep
->fce_prev
->fce_next
= fep
->fce_next
;
347 if (fep
== fcp
->fc_tail
)
348 fcp
->fc_tail
= fep
->fce_prev
;
350 fep
->fce_next
->fce_prev
= fep
->fce_prev
;
351 mutex_exit(&fcp
->fc_lock
);
353 kmem_cache_free(ndi_fm_entry_cache
, fep
);
357 ndi_fmc_entry_error(dev_info_t
*dip
, int flag
, ddi_fm_error_t
*derr
,
358 const void *bus_err_state
)
360 int status
, fatal
= 0, nonfatal
= 0;
361 ndi_fmc_t
*fcp
= NULL
;
363 struct i_ddi_fmhdl
*fmhdl
;
365 ASSERT(flag
== DMA_HANDLE
|| flag
== ACC_HANDLE
);
367 fmhdl
= DEVI(dip
)->devi_fmhdl
;
369 status
= DDI_FM_UNKNOWN
;
371 if (flag
== DMA_HANDLE
&& DDI_FM_DMA_ERR_CAP(fmhdl
->fh_cap
)) {
372 fcp
= fmhdl
->fh_dma_cache
;
374 } else if (flag
== ACC_HANDLE
&& DDI_FM_ACC_ERR_CAP(fmhdl
->fh_cap
)) {
375 fcp
= fmhdl
->fh_acc_cache
;
382 * Check active resource entries
384 mutex_enter(&fcp
->fc_lock
);
385 for (fep
= fcp
->fc_head
; fep
!= NULL
; fep
= fep
->fce_next
) {
386 ddi_fmcompare_t compare_func
;
389 * Compare captured error state with handle
390 * resources. During the comparison and
391 * subsequent error handling, we block
392 * attempts to free the cache entry.
394 compare_func
= (flag
== ACC_HANDLE
) ?
395 i_ddi_fm_acc_err_cf_get((ddi_acc_handle_t
)
397 i_ddi_fm_dma_err_cf_get((ddi_dma_handle_t
)
400 if (compare_func
== NULL
) /* unbound or not FLAGERR */
403 status
= compare_func(dip
, fep
->fce_resource
,
404 bus_err_state
, fep
->fce_bus_specific
);
405 if (status
== DDI_FM_UNKNOWN
|| status
== DDI_FM_OK
)
408 if (status
== DDI_FM_FATAL
)
410 else if (status
== DDI_FM_NONFATAL
)
413 /* Set the error for this resource handle */
414 if (flag
== ACC_HANDLE
) {
415 ddi_acc_handle_t ap
= fep
->fce_resource
;
417 i_ddi_fm_acc_err_set(ap
, derr
->fme_ena
, status
,
418 DDI_FM_ERR_UNEXPECTED
);
419 ddi_fm_acc_err_get(ap
, derr
, DDI_FME_VERSION
);
420 derr
->fme_acc_handle
= ap
;
422 ddi_dma_handle_t dp
= fep
->fce_resource
;
424 i_ddi_fm_dma_err_set(dp
, derr
->fme_ena
, status
,
425 DDI_FM_ERR_UNEXPECTED
);
426 ddi_fm_dma_err_get(dp
, derr
, DDI_FME_VERSION
);
427 derr
->fme_dma_handle
= dp
;
430 mutex_exit(&fcp
->fc_lock
);
432 return (fatal
? DDI_FM_FATAL
: nonfatal
? DDI_FM_NONFATAL
:
437 * Check error state against the handle resource stored in the specified
438 * FM cache. If tdip != NULL, we check only the cache entries for tdip.
439 * The caller must ensure that tdip is valid throughout the call and
440 * all FM data structures can be safely accesses.
442 * If tdip == NULL, we check all children that have registered their
443 * FM_DMA_CHK or FM_ACC_CHK capabilities.
445 * The following status values may be returned:
447 * DDI_FM_FATAL - if at least one cache entry comparison yields a
450 * DDI_FM_NONFATAL - if at least one cache entry comparison yields a
451 * non-fatal error and no comparison yields a fatal error.
453 * DDI_FM_UNKNOWN - cache entry comparisons did not yield fatal or
458 ndi_fmc_error(dev_info_t
*dip
, dev_info_t
*tdip
, int flag
, uint64_t ena
,
459 const void *bus_err_state
)
461 int status
, fatal
= 0, nonfatal
= 0;
463 struct i_ddi_fmhdl
*fmhdl
;
464 struct i_ddi_fmtgt
*tgt
;
466 ASSERT(flag
== DMA_HANDLE
|| flag
== ACC_HANDLE
);
468 i_ddi_fm_handler_enter(dip
);
469 fmhdl
= DEVI(dip
)->devi_fmhdl
;
472 bzero(&derr
, sizeof (ddi_fm_error_t
));
473 derr
.fme_version
= DDI_FME_VERSION
;
474 derr
.fme_flag
= DDI_FM_ERR_UNEXPECTED
;
477 for (tgt
= fmhdl
->fh_tgts
; tgt
!= NULL
; tgt
= tgt
->ft_next
) {
479 if (tdip
!= NULL
&& tdip
!= tgt
->ft_dip
)
483 * Attempt to find the entry in this childs handle cache
485 status
= ndi_fmc_entry_error(tgt
->ft_dip
, flag
, &derr
,
488 if (status
== DDI_FM_FATAL
)
490 else if (status
== DDI_FM_NONFATAL
)
496 * Call our child to process this error.
498 status
= tgt
->ft_errhdl
->eh_func(tgt
->ft_dip
, &derr
,
499 tgt
->ft_errhdl
->eh_impl
);
501 if (status
== DDI_FM_FATAL
)
503 else if (status
== DDI_FM_NONFATAL
)
507 i_ddi_fm_handler_exit(dip
);
510 return (DDI_FM_FATAL
);
512 return (DDI_FM_NONFATAL
);
514 return (DDI_FM_UNKNOWN
);
518 ndi_fmc_entry_error_all(dev_info_t
*dip
, int flag
, ddi_fm_error_t
*derr
)
520 ndi_fmc_t
*fcp
= NULL
;
522 struct i_ddi_fmhdl
*fmhdl
;
525 ASSERT(flag
== DMA_HANDLE
|| flag
== ACC_HANDLE
);
527 fmhdl
= DEVI(dip
)->devi_fmhdl
;
530 if (flag
== DMA_HANDLE
&& DDI_FM_DMA_ERR_CAP(fmhdl
->fh_cap
)) {
531 fcp
= fmhdl
->fh_dma_cache
;
533 } else if (flag
== ACC_HANDLE
&& DDI_FM_ACC_ERR_CAP(fmhdl
->fh_cap
)) {
534 fcp
= fmhdl
->fh_acc_cache
;
540 * Check active resource entries
542 mutex_enter(&fcp
->fc_lock
);
543 for (fep
= fcp
->fc_head
; fep
!= NULL
; fep
= fep
->fce_next
) {
544 ddi_fmcompare_t compare_func
;
546 compare_func
= (flag
== ACC_HANDLE
) ?
547 i_ddi_fm_acc_err_cf_get((ddi_acc_handle_t
)
549 i_ddi_fm_dma_err_cf_get((ddi_dma_handle_t
)
552 if (compare_func
== NULL
) /* unbound or not FLAGERR */
555 /* Set the error for this resource handle */
558 if (flag
== ACC_HANDLE
) {
559 ddi_acc_handle_t ap
= fep
->fce_resource
;
561 i_ddi_fm_acc_err_set(ap
, derr
->fme_ena
,
562 DDI_FM_NONFATAL
, DDI_FM_ERR_UNEXPECTED
);
563 ddi_fm_acc_err_get(ap
, derr
, DDI_FME_VERSION
);
564 derr
->fme_acc_handle
= ap
;
566 ddi_dma_handle_t dp
= fep
->fce_resource
;
568 i_ddi_fm_dma_err_set(dp
, derr
->fme_ena
,
569 DDI_FM_NONFATAL
, DDI_FM_ERR_UNEXPECTED
);
570 ddi_fm_dma_err_get(dp
, derr
, DDI_FME_VERSION
);
571 derr
->fme_dma_handle
= dp
;
574 mutex_exit(&fcp
->fc_lock
);
576 return (nonfatal
? DDI_FM_NONFATAL
: DDI_FM_UNKNOWN
);
580 * Dispatch registered error handlers for dip. If tdip != NULL, only
581 * the error handler (if available) for tdip is invoked. Otherwise,
582 * all registered error handlers are invoked.
584 * The following status values may be returned:
586 * DDI_FM_FATAL - if at least one error handler returns a
589 * DDI_FM_NONFATAL - if at least one error handler returns a
590 * non-fatal error and none returned a fatal error.
592 * DDI_FM_UNKNOWN - if at least one error handler returns
593 * unknown status and none return fatal or non-fatal.
595 * DDI_FM_OK - if all error handlers return DDI_FM_OK
598 ndi_fm_handler_dispatch(dev_info_t
*dip
, dev_info_t
*tdip
,
599 const ddi_fm_error_t
*nerr
)
602 int unknown
= 0, fatal
= 0, nonfatal
= 0;
603 struct i_ddi_fmhdl
*hdl
;
604 struct i_ddi_fmtgt
*tgt
;
606 status
= DDI_FM_UNKNOWN
;
608 i_ddi_fm_handler_enter(dip
);
609 hdl
= DEVI(dip
)->devi_fmhdl
;
611 while (tgt
!= NULL
) {
612 if (tdip
== NULL
|| tdip
== tgt
->ft_dip
) {
613 struct i_ddi_errhdl
*errhdl
;
615 errhdl
= tgt
->ft_errhdl
;
616 status
= errhdl
->eh_func(tgt
->ft_dip
, nerr
,
619 if (status
== DDI_FM_FATAL
)
621 else if (status
== DDI_FM_NONFATAL
)
623 else if (status
== DDI_FM_UNKNOWN
)
626 /* Only interested in one target */
632 i_ddi_fm_handler_exit(dip
);
635 return (DDI_FM_FATAL
);
637 return (DDI_FM_NONFATAL
);
639 return (DDI_FM_UNKNOWN
);
645 * Set error status for specified access or DMA handle
647 * May be called in any context but caller must insure validity of
651 ndi_fm_acc_err_set(ddi_acc_handle_t handle
, ddi_fm_error_t
*dfe
)
653 i_ddi_fm_acc_err_set(handle
, dfe
->fme_ena
, dfe
->fme_status
,
658 ndi_fm_dma_err_set(ddi_dma_handle_t handle
, ddi_fm_error_t
*dfe
)
660 i_ddi_fm_dma_err_set(handle
, dfe
->fme_ena
, dfe
->fme_status
,
665 * Call parent busop fm initialization routine.
667 * Called during driver attach(1M)
670 i_ndi_busop_fm_init(dev_info_t
*dip
, int tcap
, ddi_iblock_cookie_t
*ibc
)
673 dev_info_t
*pdip
= (dev_info_t
*)DEVI(dip
)->devi_parent
;
675 if (dip
== ddi_root_node())
676 return (ddi_system_fmcap
| DDI_FM_EREPORT_CAPABLE
);
678 /* Valid operation for BUSO_REV_6 and above */
679 if (DEVI(pdip
)->devi_ops
->devo_bus_ops
->busops_rev
< BUSO_REV_6
)
680 return (DDI_FM_NOT_CAPABLE
);
682 if (DEVI(pdip
)->devi_ops
->devo_bus_ops
->bus_fm_init
== NULL
)
683 return (DDI_FM_NOT_CAPABLE
);
685 pcap
= (*DEVI(pdip
)->devi_ops
->devo_bus_ops
->bus_fm_init
)
686 (pdip
, dip
, tcap
, ibc
);
692 * Call parent busop fm clean-up routine.
694 * Called during driver detach(1M)
697 i_ndi_busop_fm_fini(dev_info_t
*dip
)
699 dev_info_t
*pdip
= (dev_info_t
*)DEVI(dip
)->devi_parent
;
701 if (dip
== ddi_root_node())
704 /* Valid operation for BUSO_REV_6 and above */
705 if (DEVI(pdip
)->devi_ops
->devo_bus_ops
->busops_rev
< BUSO_REV_6
)
708 if (DEVI(pdip
)->devi_ops
->devo_bus_ops
->bus_fm_fini
== NULL
)
711 (*DEVI(pdip
)->devi_ops
->devo_bus_ops
->bus_fm_fini
)(pdip
, dip
);
715 * The following routines provide exclusive access to a nexus resource
717 * These busops may be called in user or kernel driver context.
720 i_ndi_busop_access_enter(dev_info_t
*dip
, ddi_acc_handle_t handle
)
722 dev_info_t
*pdip
= (dev_info_t
*)DEVI(dip
)->devi_parent
;
724 /* Valid operation for BUSO_REV_6 and above */
725 if (DEVI(pdip
)->devi_ops
->devo_bus_ops
->busops_rev
< BUSO_REV_6
)
728 if (DEVI(pdip
)->devi_ops
->devo_bus_ops
->bus_fm_access_enter
== NULL
)
731 (*DEVI(pdip
)->devi_ops
->devo_bus_ops
->bus_fm_access_enter
)
736 i_ndi_busop_access_exit(dev_info_t
*dip
, ddi_acc_handle_t handle
)
738 dev_info_t
*pdip
= (dev_info_t
*)DEVI(dip
)->devi_parent
;
740 /* Valid operation for BUSO_REV_6 and above */
741 if (DEVI(pdip
)->devi_ops
->devo_bus_ops
->busops_rev
< BUSO_REV_6
)
744 if (DEVI(pdip
)->devi_ops
->devo_bus_ops
->bus_fm_access_exit
== NULL
)
747 (*DEVI(pdip
)->devi_ops
->devo_bus_ops
->bus_fm_access_exit
)(pdip
, handle
);