Home | History | Annotate | Line # | Download | only in ic
cac.c revision 1.40
      1 /*	$NetBSD: cac.c,v 1.40 2007/03/04 06:01:51 christos Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2000, 2006, 2007 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Andrew Doran.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. All advertising materials mentioning features or use of this software
     19  *    must display the following acknowledgement:
     20  *        This product includes software developed by the NetBSD
     21  *        Foundation, Inc. and its contributors.
     22  * 4. Neither the name of The NetBSD Foundation nor the names of its
     23  *    contributors may be used to endorse or promote products derived
     24  *    from this software without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     36  * POSSIBILITY OF SUCH DAMAGE.
     37  */
     38 
     39 /*
     40  * Driver for Compaq array controllers.
     41  */
     42 
     43 #include <sys/cdefs.h>
     44 __KERNEL_RCSID(0, "$NetBSD: cac.c,v 1.40 2007/03/04 06:01:51 christos Exp $");
     45 
     46 #include <sys/param.h>
     47 #include <sys/systm.h>
     48 #include <sys/kernel.h>
     49 #include <sys/device.h>
     50 #include <sys/queue.h>
     51 #include <sys/proc.h>
     52 #include <sys/buf.h>
     53 #include <sys/endian.h>
     54 #include <sys/malloc.h>
     55 #include <sys/pool.h>
     56 
     57 #include <uvm/uvm_extern.h>
     58 
     59 #include <sys/bswap.h>
     60 #include <machine/bus.h>
     61 
     62 #include <dev/ic/cacreg.h>
     63 #include <dev/ic/cacvar.h>
     64 
     65 #include "locators.h"
     66 
     67 static struct	cac_ccb *cac_ccb_alloc(struct cac_softc *, int);
     68 static void	cac_ccb_done(struct cac_softc *, struct cac_ccb *);
     69 static void	cac_ccb_free(struct cac_softc *, struct cac_ccb *);
     70 static int	cac_ccb_poll(struct cac_softc *, struct cac_ccb *, int);
     71 static int	cac_ccb_start(struct cac_softc *, struct cac_ccb *);
     72 static int	cac_print(void *, const char *);
     73 static void	cac_shutdown(void *);
     74 
     75 static struct	cac_ccb *cac_l0_completed(struct cac_softc *);
     76 static int	cac_l0_fifo_full(struct cac_softc *);
     77 static void	cac_l0_intr_enable(struct cac_softc *, int);
     78 static int	cac_l0_intr_pending(struct cac_softc *);
     79 static void	cac_l0_submit(struct cac_softc *, struct cac_ccb *);
     80 
     81 static void	*cac_sdh;	/* shutdown hook */
     82 
     83 const struct cac_linkage cac_l0 = {
     84 	cac_l0_completed,
     85 	cac_l0_fifo_full,
     86 	cac_l0_intr_enable,
     87 	cac_l0_intr_pending,
     88 	cac_l0_submit
     89 };
     90 
     91 /*
     92  * Initialise our interface to the controller.
     93  */
     94 int
     95 cac_init(struct cac_softc *sc, const char *intrstr, int startfw)
     96 {
     97 	struct cac_controller_info cinfo;
     98 	struct cac_attach_args caca;
     99 	int error, rseg, size, i;
    100 	bus_dma_segment_t seg;
    101 	struct cac_ccb *ccb;
    102 	int locs[CACCF_NLOCS];
    103 	char firm[8];
    104 
    105 	if (intrstr != NULL)
    106 		aprint_normal("%s: interrupting at %s\n", sc->sc_dv.dv_xname,
    107 		    intrstr);
    108 
    109 	SIMPLEQ_INIT(&sc->sc_ccb_free);
    110 	SIMPLEQ_INIT(&sc->sc_ccb_queue);
    111 	mutex_init(&sc->sc_mutex, MUTEX_DRIVER, IPL_BIO);
    112 	cv_init(&sc->sc_ccb_cv, "cacccb");
    113 
    114         size = sizeof(struct cac_ccb) * CAC_MAX_CCBS;
    115 
    116 	if ((error = bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &seg, 1,
    117 	    &rseg, BUS_DMA_NOWAIT)) != 0) {
    118 		aprint_error("%s: unable to allocate CCBs, error = %d\n",
    119 		    sc->sc_dv.dv_xname, error);
    120 		return (-1);
    121 	}
    122 
    123 	if ((error = bus_dmamem_map(sc->sc_dmat, &seg, rseg, size,
    124 	    (void **)&sc->sc_ccbs,
    125 	    BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) {
    126 		aprint_error("%s: unable to map CCBs, error = %d\n",
    127 		    sc->sc_dv.dv_xname, error);
    128 		return (-1);
    129 	}
    130 
    131 	if ((error = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
    132 	    BUS_DMA_NOWAIT, &sc->sc_dmamap)) != 0) {
    133 		aprint_error("%s: unable to create CCB DMA map, error = %d\n",
    134 		    sc->sc_dv.dv_xname, error);
    135 		return (-1);
    136 	}
    137 
    138 	if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, sc->sc_ccbs,
    139 	    size, NULL, BUS_DMA_NOWAIT)) != 0) {
    140 		aprint_error("%s: unable to load CCB DMA map, error = %d\n",
    141 		    sc->sc_dv.dv_xname, error);
    142 		return (-1);
    143 	}
    144 
    145 	sc->sc_ccbs_paddr = sc->sc_dmamap->dm_segs[0].ds_addr;
    146 	memset(sc->sc_ccbs, 0, size);
    147 	ccb = (struct cac_ccb *)sc->sc_ccbs;
    148 
    149 	for (i = 0; i < CAC_MAX_CCBS; i++, ccb++) {
    150 		/* Create the DMA map for this CCB's data */
    151 		error = bus_dmamap_create(sc->sc_dmat, CAC_MAX_XFER,
    152 		    CAC_SG_SIZE, CAC_MAX_XFER, 0,
    153 		    BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW,
    154 		    &ccb->ccb_dmamap_xfer);
    155 
    156 		if (error) {
    157 			aprint_error("%s: can't create ccb dmamap (%d)\n",
    158 			    sc->sc_dv.dv_xname, error);
    159 			break;
    160 		}
    161 
    162 		ccb->ccb_flags = 0;
    163 		ccb->ccb_paddr = sc->sc_ccbs_paddr + i * sizeof(struct cac_ccb);
    164 		SIMPLEQ_INSERT_TAIL(&sc->sc_ccb_free, ccb, ccb_chain);
    165 	}
    166 
    167 	/* Start firmware background tasks, if needed. */
    168 	if (startfw) {
    169 		if (cac_cmd(sc, CAC_CMD_START_FIRMWARE, &cinfo, sizeof(cinfo),
    170 		    0, 0, CAC_CCB_DATA_IN, NULL)) {
    171 			aprint_error("%s: CAC_CMD_START_FIRMWARE failed\n",
    172 			    sc->sc_dv.dv_xname);
    173 			return (-1);
    174 		}
    175 	}
    176 
    177 	if (cac_cmd(sc, CAC_CMD_GET_CTRL_INFO, &cinfo, sizeof(cinfo), 0, 0,
    178 	    CAC_CCB_DATA_IN, NULL)) {
    179 		aprint_error("%s: CAC_CMD_GET_CTRL_INFO failed\n",
    180 		    sc->sc_dv.dv_xname);
    181 		return (-1);
    182 	}
    183 
    184 	strlcpy(firm, cinfo.firm_rev, 4+1);
    185 	printf("%s: %d channels, firmware <%s>\n", sc->sc_dv.dv_xname,
    186 	    cinfo.scsi_chips, firm);
    187 
    188 	sc->sc_nunits = cinfo.num_drvs;
    189 	for (i = 0; i < cinfo.num_drvs; i++) {
    190 		caca.caca_unit = i;
    191 
    192 		locs[CACCF_UNIT] = i;
    193 
    194 		config_found_sm_loc(&sc->sc_dv, "cac", locs, &caca,
    195 		    cac_print, config_stdsubmatch);
    196 	}
    197 
    198 	/* Set our `shutdownhook' before we start any device activity. */
    199 	if (cac_sdh == NULL)
    200 		cac_sdh = shutdownhook_establish(cac_shutdown, NULL);
    201 
    202 	mutex_enter(&sc->sc_mutex);
    203 	(*sc->sc_cl.cl_intr_enable)(sc, CAC_INTR_ENABLE);
    204 	mutex_exit(&sc->sc_mutex);
    205 
    206 	return (0);
    207 }
    208 
    209 /*
    210  * Shut down all `cac' controllers.
    211  */
    212 static void
    213 cac_shutdown(void *cookie)
    214 {
    215 	extern struct cfdriver cac_cd;
    216 	struct cac_softc *sc;
    217 	u_int8_t tbuf[512];
    218 	int i;
    219 
    220 	for (i = 0; i < cac_cd.cd_ndevs; i++) {
    221 		if ((sc = device_lookup(&cac_cd, i)) == NULL)
    222 			continue;
    223 		memset(tbuf, 0, sizeof(tbuf));
    224 		tbuf[0] = 1;
    225 		cac_cmd(sc, CAC_CMD_FLUSH_CACHE, tbuf, sizeof(tbuf), 0, 0,
    226 		    CAC_CCB_DATA_OUT, NULL);
    227 	}
    228 }
    229 
    230 /*
    231  * Print autoconfiguration message for a sub-device.
    232  */
    233 static int
    234 cac_print(void *aux, const char *pnp)
    235 {
    236 	struct cac_attach_args *caca;
    237 
    238 	caca = (struct cac_attach_args *)aux;
    239 
    240 	if (pnp != NULL)
    241 		aprint_normal("block device at %s", pnp);
    242 	aprint_normal(" unit %d", caca->caca_unit);
    243 	return (UNCONF);
    244 }
    245 
    246 /*
    247  * Handle an interrupt from the controller: process finished CCBs and
    248  * dequeue any waiting CCBs.
    249  */
    250 int
    251 cac_intr(void *cookie)
    252 {
    253 	struct cac_softc *sc;
    254 	struct cac_ccb *ccb;
    255 	int rv;
    256 
    257 	sc = (struct cac_softc *)cookie;
    258 
    259 	mutex_enter(&sc->sc_mutex);
    260 
    261 	if ((*sc->sc_cl.cl_intr_pending)(sc)) {
    262 		while ((ccb = (*sc->sc_cl.cl_completed)(sc)) != NULL) {
    263 			cac_ccb_done(sc, ccb);
    264 			cac_ccb_start(sc, NULL);
    265 		}
    266 		rv = 1;
    267 	} else
    268 		rv = 0;
    269 
    270 	mutex_exit(&sc->sc_mutex);
    271 
    272 	return (rv);
    273 }
    274 
    275 /*
    276  * Execute a [polled] command.
    277  */
    278 int
    279 cac_cmd(struct cac_softc *sc, int command, void *data, int datasize,
    280 	int drive, int blkno, int flags, struct cac_context *context)
    281 {
    282 	struct cac_ccb *ccb;
    283 	struct cac_sgb *sgb;
    284 	int i, rv, size, nsegs;
    285 
    286 	size = 0;
    287 
    288 	if ((ccb = cac_ccb_alloc(sc, 1)) == NULL) {
    289 		printf("%s: unable to alloc CCB", sc->sc_dv.dv_xname);
    290 		return (EAGAIN);
    291 	}
    292 
    293 	if ((flags & (CAC_CCB_DATA_IN | CAC_CCB_DATA_OUT)) != 0) {
    294 		bus_dmamap_load(sc->sc_dmat, ccb->ccb_dmamap_xfer,
    295 		    (void *)data, datasize, NULL, BUS_DMA_NOWAIT |
    296 		    BUS_DMA_STREAMING | ((flags & CAC_CCB_DATA_IN) ?
    297 		    BUS_DMA_READ : BUS_DMA_WRITE));
    298 
    299 		bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap_xfer, 0, datasize,
    300 		    (flags & CAC_CCB_DATA_IN) != 0 ? BUS_DMASYNC_PREREAD :
    301 		    BUS_DMASYNC_PREWRITE);
    302 
    303 		sgb = ccb->ccb_seg;
    304 		nsegs = min(ccb->ccb_dmamap_xfer->dm_nsegs, CAC_SG_SIZE);
    305 
    306 		for (i = 0; i < nsegs; i++, sgb++) {
    307 			size += ccb->ccb_dmamap_xfer->dm_segs[i].ds_len;
    308 			sgb->length =
    309 			    htole32(ccb->ccb_dmamap_xfer->dm_segs[i].ds_len);
    310 			sgb->addr =
    311 			    htole32(ccb->ccb_dmamap_xfer->dm_segs[i].ds_addr);
    312 		}
    313 	} else {
    314 		size = datasize;
    315 		nsegs = 0;
    316 	}
    317 
    318 	ccb->ccb_hdr.drive = drive;
    319 	ccb->ccb_hdr.priority = 0;
    320 	ccb->ccb_hdr.size = htole16((sizeof(struct cac_req) +
    321 	    sizeof(struct cac_sgb) * CAC_SG_SIZE) >> 2);
    322 
    323 	ccb->ccb_req.next = 0;
    324 	ccb->ccb_req.error = 0;
    325 	ccb->ccb_req.reserved = 0;
    326 	ccb->ccb_req.bcount = htole16(howmany(size, DEV_BSIZE));
    327 	ccb->ccb_req.command = command;
    328 	ccb->ccb_req.sgcount = nsegs;
    329 	ccb->ccb_req.blkno = htole32(blkno);
    330 
    331 	ccb->ccb_flags = flags;
    332 	ccb->ccb_datasize = size;
    333 
    334 	mutex_enter(&sc->sc_mutex);
    335 
    336 	if (context == NULL) {
    337 		memset(&ccb->ccb_context, 0, sizeof(struct cac_context));
    338 
    339 		/* Synchronous commands musn't wait. */
    340 		if ((*sc->sc_cl.cl_fifo_full)(sc)) {
    341 			cac_ccb_free(sc, ccb);
    342 			rv = EAGAIN;
    343 		} else {
    344 #ifdef DIAGNOSTIC
    345 			ccb->ccb_flags |= CAC_CCB_ACTIVE;
    346 #endif
    347 			(*sc->sc_cl.cl_submit)(sc, ccb);
    348 			rv = cac_ccb_poll(sc, ccb, 2000);
    349 			cac_ccb_free(sc, ccb);
    350 		}
    351 	} else {
    352 		memcpy(&ccb->ccb_context, context, sizeof(struct cac_context));
    353 		(void)cac_ccb_start(sc, ccb);
    354 		rv = 0;
    355 	}
    356 
    357 	mutex_exit(&sc->sc_mutex);
    358 	return (rv);
    359 }
    360 
    361 /*
    362  * Wait for the specified CCB to complete.
    363  */
    364 static int
    365 cac_ccb_poll(struct cac_softc *sc, struct cac_ccb *wantccb, int timo)
    366 {
    367 	struct cac_ccb *ccb;
    368 
    369 	LOCK_ASSERT(mutex_owned(&sc->sc_mutex));
    370 
    371 	timo *= 1000;
    372 
    373 	do {
    374 		for (; timo != 0; timo--) {
    375 			ccb = (*sc->sc_cl.cl_completed)(sc);
    376 			if (ccb != NULL)
    377 				break;
    378 			DELAY(1);
    379 		}
    380 
    381 		if (timo == 0) {
    382 			printf("%s: timeout\n", sc->sc_dv.dv_xname);
    383 			return (EBUSY);
    384 		}
    385 		cac_ccb_done(sc, ccb);
    386 	} while (ccb != wantccb);
    387 
    388 	return (0);
    389 }
    390 
    391 /*
    392  * Enqueue the specified command (if any) and attempt to start all enqueued
    393  * commands.
    394  */
    395 static int
    396 cac_ccb_start(struct cac_softc *sc, struct cac_ccb *ccb)
    397 {
    398 
    399 	LOCK_ASSERT(mutex_owned(&sc->sc_mutex));
    400 
    401 	if (ccb != NULL)
    402 		SIMPLEQ_INSERT_TAIL(&sc->sc_ccb_queue, ccb, ccb_chain);
    403 
    404 	while ((ccb = SIMPLEQ_FIRST(&sc->sc_ccb_queue)) != NULL) {
    405 		if ((*sc->sc_cl.cl_fifo_full)(sc))
    406 			return (EAGAIN);
    407 		SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_queue, ccb_chain);
    408 #ifdef DIAGNOSTIC
    409 		ccb->ccb_flags |= CAC_CCB_ACTIVE;
    410 #endif
    411 		(*sc->sc_cl.cl_submit)(sc, ccb);
    412 	}
    413 
    414 	return (0);
    415 }
    416 
    417 /*
    418  * Process a finished CCB.
    419  */
    420 static void
    421 cac_ccb_done(struct cac_softc *sc, struct cac_ccb *ccb)
    422 {
    423 	struct device *dv;
    424 	void *context;
    425 	int error;
    426 
    427 	error = 0;
    428 
    429 	LOCK_ASSERT(mutex_owned(&sc->sc_mutex));
    430 
    431 #ifdef DIAGNOSTIC
    432 	if ((ccb->ccb_flags & CAC_CCB_ACTIVE) == 0)
    433 		panic("cac_ccb_done: CCB not active");
    434 	ccb->ccb_flags &= ~CAC_CCB_ACTIVE;
    435 #endif
    436 
    437 	if ((ccb->ccb_flags & (CAC_CCB_DATA_IN | CAC_CCB_DATA_OUT)) != 0) {
    438 		bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap_xfer, 0,
    439 		    ccb->ccb_datasize, ccb->ccb_flags & CAC_CCB_DATA_IN ?
    440 		    BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
    441 		bus_dmamap_unload(sc->sc_dmat, ccb->ccb_dmamap_xfer);
    442 	}
    443 
    444 	error = ccb->ccb_req.error;
    445 	if (ccb->ccb_context.cc_handler != NULL) {
    446 		dv = ccb->ccb_context.cc_dv;
    447 		context = ccb->ccb_context.cc_context;
    448 		cac_ccb_free(sc, ccb);
    449 		(*ccb->ccb_context.cc_handler)(dv, context, error);
    450 	} else {
    451 		if ((error & CAC_RET_SOFT_ERROR) != 0)
    452 			printf("%s: soft error; array may be degraded\n",
    453 			    sc->sc_dv.dv_xname);
    454 		if ((error & CAC_RET_HARD_ERROR) != 0)
    455 			printf("%s: hard error\n", sc->sc_dv.dv_xname);
    456 		if ((error & CAC_RET_CMD_REJECTED) != 0) {
    457 			error = 1;
    458 			printf("%s: invalid request\n", sc->sc_dv.dv_xname);
    459 		}
    460 	}
    461 }
    462 
    463 /*
    464  * Allocate a CCB.
    465  */
    466 static struct cac_ccb *
    467 cac_ccb_alloc(struct cac_softc *sc, int nosleep)
    468 {
    469 	struct cac_ccb *ccb;
    470 
    471 	mutex_enter(&sc->sc_mutex);
    472 
    473 	for (;;) {
    474 		if ((ccb = SIMPLEQ_FIRST(&sc->sc_ccb_free)) != NULL) {
    475 			SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_free, ccb_chain);
    476 			break;
    477 		}
    478 		if (nosleep) {
    479 			ccb = NULL;
    480 			break;
    481 		}
    482 		cv_wait(&sc->sc_ccb_cv, &sc->sc_mutex);
    483 	}
    484 
    485 	mutex_exit(&sc->sc_mutex);
    486 	return (ccb);
    487 }
    488 
    489 /*
    490  * Put a CCB onto the freelist.
    491  */
    492 static void
    493 cac_ccb_free(struct cac_softc *sc, struct cac_ccb *ccb)
    494 {
    495 
    496 	LOCK_ASSERT(mutex_owned(&sc->sc_mutex));
    497 
    498 	ccb->ccb_flags = 0;
    499 	if (SIMPLEQ_EMPTY(&sc->sc_ccb_free))
    500 		cv_signal(&sc->sc_ccb_cv);
    501 	SIMPLEQ_INSERT_HEAD(&sc->sc_ccb_free, ccb, ccb_chain);
    502 }
    503 
    504 /*
    505  * Board specific linkage shared between multiple bus types.
    506  */
    507 
    508 static int
    509 cac_l0_fifo_full(struct cac_softc *sc)
    510 {
    511 
    512 	LOCK_ASSERT(mutex_owned(&sc->sc_mutex));
    513 
    514 	return (cac_inl(sc, CAC_REG_CMD_FIFO) == 0);
    515 }
    516 
    517 static void
    518 cac_l0_submit(struct cac_softc *sc, struct cac_ccb *ccb)
    519 {
    520 
    521 	LOCK_ASSERT(mutex_owned(&sc->sc_mutex));
    522 
    523 	bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap,
    524 	    (char *)ccb - (char *)sc->sc_ccbs,
    525 	    sizeof(struct cac_ccb), BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
    526 	cac_outl(sc, CAC_REG_CMD_FIFO, ccb->ccb_paddr);
    527 }
    528 
    529 static struct cac_ccb *
    530 cac_l0_completed(struct cac_softc *sc)
    531 {
    532 	struct cac_ccb *ccb;
    533 	paddr_t off;
    534 
    535 	LOCK_ASSERT(mutex_owned(&sc->sc_mutex));
    536 
    537 	if ((off = cac_inl(sc, CAC_REG_DONE_FIFO)) == 0)
    538 		return (NULL);
    539 
    540 	if ((off & 3) != 0)
    541 		printf("%s: failed command list returned: %lx\n",
    542 		    sc->sc_dv.dv_xname, (long)off);
    543 
    544 	off = (off & ~3) - sc->sc_ccbs_paddr;
    545 	ccb = (struct cac_ccb *)((char *)sc->sc_ccbs + off);
    546 
    547 	bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, off, sizeof(struct cac_ccb),
    548 	    BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
    549 
    550 	if ((off & 3) != 0 && ccb->ccb_req.error == 0)
    551 		ccb->ccb_req.error = CAC_RET_CMD_REJECTED;
    552 
    553 	return (ccb);
    554 }
    555 
    556 static int
    557 cac_l0_intr_pending(struct cac_softc *sc)
    558 {
    559 
    560 	LOCK_ASSERT(mutex_owned(&sc->sc_mutex));
    561 
    562 	return (cac_inl(sc, CAC_REG_INTR_PENDING) & CAC_INTR_ENABLE);
    563 }
    564 
    565 static void
    566 cac_l0_intr_enable(struct cac_softc *sc, int state)
    567 {
    568 
    569 	LOCK_ASSERT(mutex_owned(&sc->sc_mutex));
    570 
    571 	cac_outl(sc, CAC_REG_INTR_MASK,
    572 	    state ? CAC_INTR_ENABLE : CAC_INTR_DISABLE);
    573 }
    574