1 /* $NetBSD: cac.c,v 1.5 2000/05/03 18:58:15 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2000 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Andy 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.5 2000/05/03 18:58:15 thorpej 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 <machine/bswap.h> 58 #include <machine/bus.h> 59 60 #include <dev/ic/cacreg.h> 61 #include <dev/ic/cacvar.h> 62 63 static void cac_ccb_done __P((struct cac_softc *, struct cac_ccb *)); 64 static int cac_print __P((void *, const char *)); 65 static int cac_submatch __P((struct device *, struct cfdata *, void *)); 66 static void cac_ccb_poll __P((struct cac_softc *, struct cac_ccb *, int)); 67 static void cac_shutdown __P((void *)); 68 69 static void *cac_sdh; /* shutdown hook */ 70 71 /* 72 * Initialise our interface to the controller. 73 */ 74 int 75 cac_init(sc, intrstr) 76 struct cac_softc *sc; 77 const char *intrstr; 78 { 79 struct cac_controller_info cinfo; 80 struct cac_attach_args caca; 81 int error, rseg, size, i; 82 bus_dma_segment_t seg; 83 struct cac_ccb *ccb; 84 85 printf("Compaq %s\n", sc->sc_typestr); 86 if (intrstr != NULL) 87 printf("%s: interrupting at %s\n", sc->sc_dv.dv_xname, intrstr); 88 89 SIMPLEQ_INIT(&sc->sc_ccb_free); 90 SIMPLEQ_INIT(&sc->sc_ccb_queue); 91 92 size = sizeof(struct cac_ccb) * CAC_MAX_CCBS; 93 94 if ((error = bus_dmamem_alloc(sc->sc_dmat, size, NBPG, 0, &seg, 1, 95 &rseg, BUS_DMA_NOWAIT)) != 0) { 96 printf("%s: unable to allocate CCBs, error = %d\n", 97 sc->sc_dv.dv_xname, error); 98 return (-1); 99 } 100 101 if ((error = bus_dmamem_map(sc->sc_dmat, &seg, rseg, size, 102 (caddr_t *)&sc->sc_ccbs, BUS_DMA_NOWAIT|BUS_DMA_COHERENT)) != 0) { 103 printf("%s: unable to map CCBs, error = %d\n", 104 sc->sc_dv.dv_xname, error); 105 return (-1); 106 } 107 108 if ((error = bus_dmamap_create(sc->sc_dmat, size, size, 1, 0, 109 BUS_DMA_NOWAIT, &sc->sc_dmamap)) != 0) { 110 printf("%s: unable to create CCB DMA map, error = %d\n", 111 sc->sc_dv.dv_xname, error); 112 return (-1); 113 } 114 115 if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, sc->sc_ccbs, 116 size, NULL, BUS_DMA_NOWAIT)) != 0) { 117 printf("%s: unable to load CCB DMA map, error = %d\n", 118 sc->sc_dv.dv_xname, error); 119 return (-1); 120 } 121 122 sc->sc_ccbs_paddr = sc->sc_dmamap->dm_segs[0].ds_addr; 123 memset(sc->sc_ccbs, 0, size); 124 ccb = (struct cac_ccb *)sc->sc_ccbs; 125 126 for (i = 0; i < CAC_MAX_CCBS; i++, ccb++) { 127 /* Create the DMA map for this CCB's data */ 128 error = bus_dmamap_create(sc->sc_dmat, CAC_MAX_XFER, 129 CAC_SG_SIZE, CAC_MAX_XFER, 0, 130 BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &ccb->ccb_dmamap_xfer); 131 132 if (error) { 133 printf("%s: can't create ccb dmamap (%d)\n", 134 sc->sc_dv.dv_xname, error); 135 break; 136 } 137 138 ccb->ccb_flags = 0; 139 ccb->ccb_paddr = sc->sc_ccbs_paddr + i * sizeof(struct cac_ccb); 140 SIMPLEQ_INSERT_TAIL(&sc->sc_ccb_free, ccb, ccb_chain); 141 } 142 143 if (cac_cmd(sc, CAC_CMD_GET_CTRL_INFO, &cinfo, sizeof(cinfo), 0, 0, 144 CAC_CCB_DATA_IN, NULL)) { 145 printf("%s: CAC_CMD_GET_CTRL_INFO failed\n", 146 sc->sc_dv.dv_xname); 147 return (-1); 148 } 149 150 for (i = 0; i < cinfo.num_drvs; i++) { 151 caca.caca_unit = i; 152 config_found_sm(&sc->sc_dv, &caca, cac_print, cac_submatch); 153 } 154 155 /* Set shutdownhook before we start any device activity. */ 156 if (cac_sdh == NULL) 157 cac_sdh = shutdownhook_establish(cac_shutdown, NULL); 158 159 sc->sc_cl->cl_intr_enable(sc, CAC_INT_ENABLE); 160 return (0); 161 } 162 163 /* 164 * Shutdown the controller. 165 */ 166 static void 167 cac_shutdown(cookie) 168 void *cookie; 169 { 170 extern struct cfdriver cac_cd; 171 struct cac_softc *sc; 172 char buf[512]; 173 int i; 174 175 printf("shutting down cac devices..."); 176 177 for (i = 0; i < cac_cd.cd_ndevs; i++) { 178 if ((sc = cac_cd.cd_devs[i]) == NULL) 179 continue; 180 /* XXX documentation on this is a bit fuzzy. */ 181 memset(buf, 0, sizeof (buf)); 182 buf[0] = 1; 183 cac_cmd(sc, CAC_CMD_FLUSH_CACHE, buf, sizeof(buf), 0, 0, 184 CAC_CCB_DATA_OUT, NULL); 185 } 186 187 DELAY(5000*1000); 188 printf(" done\n"); 189 } 190 191 /* 192 * Print attach message for a subdevice. 193 */ 194 static int 195 cac_print(aux, pnp) 196 void *aux; 197 const char *pnp; 198 { 199 struct cac_attach_args *caca; 200 201 caca = (struct cac_attach_args *)aux; 202 203 if (pnp) 204 printf("block device at %s", pnp); 205 printf(" unit %d", caca->caca_unit); 206 return (UNCONF); 207 } 208 209 /* 210 * Match a subdevice. 211 */ 212 static int 213 cac_submatch(parent, cf, aux) 214 struct device *parent; 215 struct cfdata *cf; 216 void *aux; 217 { 218 struct cac_attach_args *caca; 219 220 caca = (struct cac_attach_args *)aux; 221 222 if (cf->cacacf_unit != CACACF_UNIT_UNKNOWN && 223 cf->cacacf_unit != caca->caca_unit) 224 return (0); 225 226 return (cf->cf_attach->ca_match(parent, cf, aux)); 227 } 228 229 /* 230 * Handle an interrupt from the controller: process finished CCBs and 231 * dequeue any waiting CCBs. 232 */ 233 int 234 cac_intr(xxx_sc) 235 void *xxx_sc; 236 { 237 struct cac_softc *sc; 238 struct cac_ccb *ccb; 239 paddr_t completed; 240 int off; 241 242 sc = (struct cac_softc *)xxx_sc; 243 244 if (!sc->sc_cl->cl_intr_pending(sc)) 245 return (0); 246 247 while ((completed = sc->sc_cl->cl_completed(sc)) != 0) { 248 off = (completed & ~3) - sc->sc_ccbs_paddr; 249 ccb = (struct cac_ccb *)(sc->sc_ccbs + off); 250 251 bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, off, 252 sizeof(struct cac_ccb), 253 BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); 254 255 cac_ccb_done(sc, ccb); 256 } 257 258 cac_ccb_start(sc, NULL); 259 return (1); 260 } 261 262 /* 263 * Execute a [polled] command. 264 */ 265 int 266 cac_cmd(sc, command, data, datasize, drive, blkno, flags, context) 267 struct cac_softc *sc; 268 int command; 269 void *data; 270 int datasize; 271 int drive; 272 int blkno; 273 int flags; 274 struct cac_context *context; 275 { 276 struct cac_ccb *ccb; 277 struct cac_sgb *sgb; 278 int s, i, rv, size, nsegs; 279 280 size = 0; 281 282 if ((ccb = cac_ccb_alloc(sc, 0)) == NULL) { 283 printf("%s: unable to alloc CCB", sc->sc_dv.dv_xname); 284 return (1); 285 } 286 287 if ((flags & (CAC_CCB_DATA_IN | CAC_CCB_DATA_OUT)) != 0) { 288 bus_dmamap_load(sc->sc_dmat, ccb->ccb_dmamap_xfer, (void *)data, 289 datasize, NULL, BUS_DMA_NOWAIT); 290 291 bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap_xfer, 0, datasize, 292 (flags & CAC_CCB_DATA_IN) != 0 ? BUS_DMASYNC_PREREAD : 293 BUS_DMASYNC_PREWRITE); 294 295 sgb = ccb->ccb_seg; 296 nsegs = min(ccb->ccb_dmamap_xfer->dm_nsegs, CAC_SG_SIZE); 297 298 for (i = 0; i < nsegs; i++, sgb++) { 299 size += ccb->ccb_dmamap_xfer->dm_segs[i].ds_len; 300 sgb->length = 301 htole32(ccb->ccb_dmamap_xfer->dm_segs[i].ds_len); 302 sgb->addr = 303 htole32(ccb->ccb_dmamap_xfer->dm_segs[i].ds_addr); 304 } 305 } else { 306 size = datasize; 307 nsegs = 0; 308 } 309 310 ccb->ccb_hdr.drive = drive; 311 ccb->ccb_hdr.size = htole16((sizeof(struct cac_req) + 312 sizeof(struct cac_sgb) * CAC_SG_SIZE) >> 2); 313 314 ccb->ccb_req.bcount = htole16(howmany(size, DEV_BSIZE)); 315 ccb->ccb_req.command = command; 316 ccb->ccb_req.sgcount = nsegs; 317 ccb->ccb_req.blkno = htole32(blkno); 318 319 ccb->ccb_flags = flags; 320 ccb->ccb_datasize = size; 321 322 if (context == NULL) { 323 memset(&ccb->ccb_context, 0, sizeof(struct cac_context)); 324 s = splbio(); 325 if (cac_ccb_start(sc, ccb)) { 326 cac_ccb_free(sc, ccb); 327 rv = -1; 328 } else { 329 cac_ccb_poll(sc, ccb, 2000); 330 cac_ccb_free(sc, ccb); 331 rv = 0; 332 } 333 } else { 334 memcpy(&ccb->ccb_context, context, sizeof(struct cac_context)); 335 s = splbio(); 336 rv = cac_ccb_start(sc, ccb); 337 } 338 339 splx(s); 340 return (rv); 341 } 342 343 /* 344 * Wait for the specified CCB to complete. Must be called at splbio. 345 */ 346 static void 347 cac_ccb_poll(sc, ccb, timo) 348 struct cac_softc *sc; 349 struct cac_ccb *ccb; 350 int timo; 351 { 352 struct cac_ccb *ccb_done; 353 paddr_t completed; 354 int off; 355 356 ccb_done = NULL; 357 358 for (;;) { 359 for (completed = 0; timo != 0; timo--) { 360 if ((completed = sc->sc_cl->cl_completed(sc)) != 0) 361 break; 362 DELAY(100); 363 } 364 365 if (timo == 0) 366 panic("%s: cac_ccb_poll: timeout", sc->sc_dv.dv_xname); 367 368 off = (completed & ~3) - sc->sc_ccbs_paddr; 369 ccb_done = (struct cac_ccb *)(sc->sc_ccbs + off); 370 371 bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, off, 372 sizeof(struct cac_ccb), 373 BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); 374 375 cac_ccb_done(sc, ccb_done); 376 if (ccb_done == ccb) 377 break; 378 } 379 } 380 381 /* 382 * Enqueue the specifed command (if any) and attempt to start all enqueued 383 * commands. Must be called at splbio. 384 */ 385 int 386 cac_ccb_start(sc, ccb) 387 struct cac_softc *sc; 388 struct cac_ccb *ccb; 389 { 390 391 if (ccb != NULL) 392 SIMPLEQ_INSERT_TAIL(&sc->sc_ccb_queue, ccb, ccb_chain); 393 394 while ((ccb = SIMPLEQ_FIRST(&sc->sc_ccb_queue)) != NULL) { 395 if (sc->sc_cl->cl_fifo_full(sc)) 396 return (-1); 397 SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_queue, ccb, ccb_chain); 398 bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, 399 (caddr_t)ccb - sc->sc_ccbs, sizeof(struct cac_ccb), 400 BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); 401 sc->sc_cl->cl_submit(sc, ccb->ccb_paddr); 402 } 403 404 return (0); 405 } 406 407 /* 408 * Process a finished CCB. 409 */ 410 static void 411 cac_ccb_done(sc, ccb) 412 struct cac_softc *sc; 413 struct cac_ccb *ccb; 414 { 415 int error; 416 417 error = 0; 418 419 if ((ccb->ccb_flags & (CAC_CCB_DATA_IN | CAC_CCB_DATA_OUT)) != 0) { 420 bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap_xfer, 0, 421 ccb->ccb_datasize, ccb->ccb_flags & CAC_CCB_DATA_IN ? 422 BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); 423 bus_dmamap_unload(sc->sc_dmat, ccb->ccb_dmamap_xfer); 424 } 425 426 if ((ccb->ccb_req.error & CAC_RET_SOFT_ERROR) != 0) 427 printf("%s: soft error\n", sc->sc_dv.dv_xname); 428 if ((ccb->ccb_req.error & CAC_RET_HARD_ERROR) != 0) { 429 error = 1; 430 printf("%s: hard error\n", sc->sc_dv.dv_xname); 431 } 432 if ((ccb->ccb_req.error & CAC_RET_CMD_REJECTED) != 0) { 433 error = 1; 434 printf("%s: invalid request\n", sc->sc_dv.dv_xname); 435 } 436 437 if (ccb->ccb_context.cc_handler != NULL) 438 ccb->ccb_context.cc_handler(ccb, error); 439 } 440 441 /* 442 * Get a free CCB. 443 */ 444 struct cac_ccb * 445 cac_ccb_alloc(sc, nosleep) 446 struct cac_softc *sc; 447 int nosleep; 448 { 449 struct cac_ccb *ccb; 450 int s; 451 452 s = splbio(); 453 454 for (;;) { 455 if ((ccb = SIMPLEQ_FIRST(&sc->sc_ccb_free)) != NULL) { 456 SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_free, ccb, ccb_chain); 457 break; 458 } 459 if (nosleep) { 460 ccb = NULL; 461 break; 462 } 463 tsleep(&sc->sc_ccb_free, PRIBIO, "cacccb", 0); 464 } 465 466 splx(s); 467 return (ccb); 468 } 469 470 /* 471 * Put a CCB onto the freelist. 472 */ 473 void 474 cac_ccb_free(sc, ccb) 475 struct cac_softc *sc; 476 struct cac_ccb *ccb; 477 { 478 int s; 479 480 s = splbio(); 481 ccb->ccb_flags = 0; 482 SIMPLEQ_INSERT_HEAD(&sc->sc_ccb_free, ccb, ccb_chain); 483 484 /* Wake anybody waiting for a free ccb */ 485 if (SIMPLEQ_NEXT(ccb, ccb_chain) == NULL) 486 wakeup(&sc->sc_ccb_free); 487 splx(s); 488 } 489 490 /* 491 * Adjust the size of a transfer. 492 */ 493 void 494 cac_minphys(bp) 495 struct buf *bp; 496 { 497 498 if (bp->b_bcount > CAC_MAX_XFER) 499 bp->b_bcount = CAC_MAX_XFER; 500 minphys(bp); 501 } 502