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