1 /* $NetBSD: cac.c,v 1.18 2001/07/19 16:25:24 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 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/param.h> 44 #include <sys/systm.h> 45 #include <sys/kernel.h> 46 #include <sys/device.h> 47 #include <sys/queue.h> 48 #include <sys/proc.h> 49 #include <sys/buf.h> 50 #include <sys/endian.h> 51 #include <sys/malloc.h> 52 #include <sys/pool.h> 53 54 #include <uvm/uvm_extern.h> 55 56 #include <machine/bswap.h> 57 #include <machine/bus.h> 58 59 #include <dev/ic/cacreg.h> 60 #include <dev/ic/cacvar.h> 61 62 static struct cac_ccb *cac_ccb_alloc(struct cac_softc *, int); 63 static void cac_ccb_done(struct cac_softc *, struct cac_ccb *); 64 static void cac_ccb_free(struct cac_softc *, struct cac_ccb *); 65 static int cac_ccb_poll(struct cac_softc *, struct cac_ccb *, int); 66 static int cac_ccb_start(struct cac_softc *, struct cac_ccb *); 67 static int cac_print(void *, const char *); 68 static void cac_shutdown(void *); 69 static int cac_submatch(struct device *, struct cfdata *, void *); 70 71 static struct cac_ccb *cac_l0_completed(struct cac_softc *); 72 static int cac_l0_fifo_full(struct cac_softc *); 73 static void cac_l0_intr_enable(struct cac_softc *, int); 74 static int cac_l0_intr_pending(struct cac_softc *); 75 static void cac_l0_submit(struct cac_softc *, struct cac_ccb *); 76 77 static void *cac_sdh; /* shutdown hook */ 78 79 struct cac_linkage cac_l0 = { 80 cac_l0_completed, 81 cac_l0_fifo_full, 82 cac_l0_intr_enable, 83 cac_l0_intr_pending, 84 cac_l0_submit 85 }; 86 87 /* 88 * Initialise our interface to the controller. 89 */ 90 int 91 cac_init(struct cac_softc *sc, const char *intrstr, int startfw) 92 { 93 struct cac_controller_info cinfo; 94 struct cac_attach_args caca; 95 int error, rseg, size, i; 96 bus_dma_segment_t seg; 97 struct cac_ccb *ccb; 98 99 if (intrstr != NULL) 100 printf("%s: interrupting at %s\n", sc->sc_dv.dv_xname, 101 intrstr); 102 103 SIMPLEQ_INIT(&sc->sc_ccb_free); 104 SIMPLEQ_INIT(&sc->sc_ccb_queue); 105 106 size = sizeof(struct cac_ccb) * CAC_MAX_CCBS; 107 108 if ((error = bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &seg, 1, 109 &rseg, BUS_DMA_NOWAIT)) != 0) { 110 printf("%s: unable to allocate CCBs, error = %d\n", 111 sc->sc_dv.dv_xname, error); 112 return (-1); 113 } 114 115 if ((error = bus_dmamem_map(sc->sc_dmat, &seg, rseg, size, 116 (caddr_t *)&sc->sc_ccbs, 117 BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) { 118 printf("%s: unable to map CCBs, error = %d\n", 119 sc->sc_dv.dv_xname, error); 120 return (-1); 121 } 122 123 if ((error = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, 124 BUS_DMA_NOWAIT, &sc->sc_dmamap)) != 0) { 125 printf("%s: unable to create CCB DMA map, error = %d\n", 126 sc->sc_dv.dv_xname, error); 127 return (-1); 128 } 129 130 if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, sc->sc_ccbs, 131 size, NULL, BUS_DMA_NOWAIT)) != 0) { 132 printf("%s: unable to load CCB DMA map, error = %d\n", 133 sc->sc_dv.dv_xname, error); 134 return (-1); 135 } 136 137 sc->sc_ccbs_paddr = sc->sc_dmamap->dm_segs[0].ds_addr; 138 memset(sc->sc_ccbs, 0, size); 139 ccb = (struct cac_ccb *)sc->sc_ccbs; 140 141 for (i = 0; i < CAC_MAX_CCBS; i++, ccb++) { 142 /* Create the DMA map for this CCB's data */ 143 error = bus_dmamap_create(sc->sc_dmat, CAC_MAX_XFER, 144 CAC_SG_SIZE, CAC_MAX_XFER, 0, 145 BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, 146 &ccb->ccb_dmamap_xfer); 147 148 if (error) { 149 printf("%s: can't create ccb dmamap (%d)\n", 150 sc->sc_dv.dv_xname, error); 151 break; 152 } 153 154 ccb->ccb_flags = 0; 155 ccb->ccb_paddr = sc->sc_ccbs_paddr + i * sizeof(struct cac_ccb); 156 SIMPLEQ_INSERT_TAIL(&sc->sc_ccb_free, ccb, ccb_chain); 157 } 158 159 /* Start firmware background tasks, if needed. */ 160 if (startfw) { 161 if (cac_cmd(sc, CAC_CMD_START_FIRMWARE, &cinfo, sizeof(cinfo), 162 0, 0, CAC_CCB_DATA_IN, NULL)) { 163 printf("%s: CAC_CMD_START_FIRMWARE failed\n", 164 sc->sc_dv.dv_xname); 165 return (-1); 166 } 167 } 168 169 if (cac_cmd(sc, CAC_CMD_GET_CTRL_INFO, &cinfo, sizeof(cinfo), 0, 0, 170 CAC_CCB_DATA_IN, NULL)) { 171 printf("%s: CAC_CMD_GET_CTRL_INFO failed\n", 172 sc->sc_dv.dv_xname); 173 return (-1); 174 } 175 176 sc->sc_nunits = cinfo.num_drvs; 177 for (i = 0; i < cinfo.num_drvs; i++) { 178 caca.caca_unit = i; 179 config_found_sm(&sc->sc_dv, &caca, cac_print, cac_submatch); 180 } 181 182 /* Set our `shutdownhook' before we start any device activity. */ 183 if (cac_sdh == NULL) 184 cac_sdh = shutdownhook_establish(cac_shutdown, NULL); 185 186 (*sc->sc_cl->cl_intr_enable)(sc, CAC_INTR_ENABLE); 187 return (0); 188 } 189 190 /* 191 * Shut down all `cac' controllers. 192 */ 193 static void 194 cac_shutdown(void *cookie) 195 { 196 extern struct cfdriver cac_cd; 197 struct cac_softc *sc; 198 u_int8_t buf[512]; 199 int i; 200 201 for (i = 0; i < cac_cd.cd_ndevs; i++) { 202 if ((sc = device_lookup(&cac_cd, i)) == NULL) 203 continue; 204 memset(buf, 0, sizeof(buf)); 205 buf[0] = 1; 206 cac_cmd(sc, CAC_CMD_FLUSH_CACHE, buf, sizeof(buf), 0, 0, 207 CAC_CCB_DATA_OUT, NULL); 208 } 209 } 210 211 /* 212 * Print autoconfiguration message for a sub-device. 213 */ 214 static int 215 cac_print(void *aux, const char *pnp) 216 { 217 struct cac_attach_args *caca; 218 219 caca = (struct cac_attach_args *)aux; 220 221 if (pnp != NULL) 222 printf("block device at %s", pnp); 223 printf(" unit %d", caca->caca_unit); 224 return (UNCONF); 225 } 226 227 /* 228 * Match a sub-device. 229 */ 230 static int 231 cac_submatch(struct device *parent, struct cfdata *cf, void *aux) 232 { 233 struct cac_attach_args *caca; 234 235 caca = (struct cac_attach_args *)aux; 236 237 if (cf->cacacf_unit != CACCF_UNIT_DEFAULT && 238 cf->cacacf_unit != caca->caca_unit) 239 return (0); 240 241 return (cf->cf_attach->ca_match(parent, cf, aux)); 242 } 243 244 /* 245 * Handle an interrupt from the controller: process finished CCBs and 246 * dequeue any waiting CCBs. 247 */ 248 int 249 cac_intr(void *cookie) 250 { 251 struct cac_softc *sc; 252 struct cac_ccb *ccb; 253 254 sc = (struct cac_softc *)cookie; 255 256 if (!(*sc->sc_cl->cl_intr_pending)(sc)) { 257 #ifdef DEBUG 258 printf("%s: spurious intr\n", sc->sc_dv.dv_xname); 259 #endif 260 return (0); 261 } 262 263 while ((ccb = (*sc->sc_cl->cl_completed)(sc)) != NULL) { 264 cac_ccb_done(sc, ccb); 265 cac_ccb_start(sc, NULL); 266 } 267 268 return (1); 269 } 270 271 /* 272 * Execute a [polled] command. 273 */ 274 int 275 cac_cmd(struct cac_softc *sc, int command, void *data, int datasize, 276 int drive, int blkno, int flags, struct cac_context *context) 277 { 278 struct cac_ccb *ccb; 279 struct cac_sgb *sgb; 280 int s, i, rv, size, nsegs; 281 282 size = 0; 283 284 if ((ccb = cac_ccb_alloc(sc, 0)) == NULL) { 285 printf("%s: unable to alloc CCB", sc->sc_dv.dv_xname); 286 return (ENOMEM); 287 } 288 289 if ((flags & (CAC_CCB_DATA_IN | CAC_CCB_DATA_OUT)) != 0) { 290 bus_dmamap_load(sc->sc_dmat, ccb->ccb_dmamap_xfer, 291 (void *)data, datasize, NULL, BUS_DMA_NOWAIT | 292 BUS_DMA_STREAMING | ((flags & CAC_CCB_DATA_IN) ? 293 BUS_DMA_READ : BUS_DMA_WRITE)); 294 295 bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap_xfer, 0, datasize, 296 (flags & CAC_CCB_DATA_IN) != 0 ? BUS_DMASYNC_PREREAD : 297 BUS_DMASYNC_PREWRITE); 298 299 sgb = ccb->ccb_seg; 300 nsegs = min(ccb->ccb_dmamap_xfer->dm_nsegs, CAC_SG_SIZE); 301 302 for (i = 0; i < nsegs; i++, sgb++) { 303 size += ccb->ccb_dmamap_xfer->dm_segs[i].ds_len; 304 sgb->length = 305 htole32(ccb->ccb_dmamap_xfer->dm_segs[i].ds_len); 306 sgb->addr = 307 htole32(ccb->ccb_dmamap_xfer->dm_segs[i].ds_addr); 308 } 309 } else { 310 size = datasize; 311 nsegs = 0; 312 } 313 314 ccb->ccb_hdr.drive = drive; 315 ccb->ccb_hdr.size = htole16((sizeof(struct cac_req) + 316 sizeof(struct cac_sgb) * CAC_SG_SIZE) >> 2); 317 318 ccb->ccb_req.bcount = htole16(howmany(size, DEV_BSIZE)); 319 ccb->ccb_req.command = command; 320 ccb->ccb_req.sgcount = nsegs; 321 ccb->ccb_req.blkno = htole32(blkno); 322 323 ccb->ccb_flags = flags; 324 ccb->ccb_datasize = size; 325 326 if (context == NULL) { 327 memset(&ccb->ccb_context, 0, sizeof(struct cac_context)); 328 s = splbio(); 329 330 /* Synchronous commands musn't wait. */ 331 if ((*sc->sc_cl->cl_fifo_full)(sc)) { 332 cac_ccb_free(sc, ccb); 333 rv = -1; 334 } else { 335 #ifdef DIAGNOSTIC 336 ccb->ccb_flags |= CAC_CCB_ACTIVE; 337 #endif 338 (*sc->sc_cl->cl_submit)(sc, ccb); 339 rv = cac_ccb_poll(sc, ccb, 2000); 340 cac_ccb_free(sc, ccb); 341 } 342 } else { 343 memcpy(&ccb->ccb_context, context, sizeof(struct cac_context)); 344 s = splbio(); 345 rv = cac_ccb_start(sc, ccb); 346 } 347 348 splx(s); 349 return (rv); 350 } 351 352 /* 353 * Wait for the specified CCB to complete. Must be called at splbio. 354 */ 355 static int 356 cac_ccb_poll(struct cac_softc *sc, struct cac_ccb *wantccb, int timo) 357 { 358 struct cac_ccb *ccb; 359 360 timo *= 10; 361 362 do { 363 for (; timo != 0; timo--) { 364 if ((ccb = (*sc->sc_cl->cl_completed)(sc)) != NULL) 365 break; 366 DELAY(100); 367 } 368 369 if (timo == 0) { 370 printf("%s: timeout", sc->sc_dv.dv_xname); 371 return (EBUSY); 372 } 373 cac_ccb_done(sc, ccb); 374 } while (ccb != wantccb); 375 376 return (0); 377 } 378 379 /* 380 * Enqueue the specifed command (if any) and attempt to start all enqueued 381 * commands. Must be called at splbio. 382 */ 383 static int 384 cac_ccb_start(struct cac_softc *sc, struct cac_ccb *ccb) 385 { 386 387 if (ccb != NULL) 388 SIMPLEQ_INSERT_TAIL(&sc->sc_ccb_queue, ccb, ccb_chain); 389 390 while ((ccb = SIMPLEQ_FIRST(&sc->sc_ccb_queue)) != NULL) { 391 if ((*sc->sc_cl->cl_fifo_full)(sc)) 392 return (EBUSY); 393 SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_queue, ccb, ccb_chain); 394 #ifdef DIAGNOSTIC 395 ccb->ccb_flags |= CAC_CCB_ACTIVE; 396 #endif 397 (*sc->sc_cl->cl_submit)(sc, ccb); 398 } 399 400 return (0); 401 } 402 403 /* 404 * Process a finished CCB. 405 */ 406 static void 407 cac_ccb_done(struct cac_softc *sc, struct cac_ccb *ccb) 408 { 409 struct device *dv; 410 void *context; 411 int error; 412 413 error = 0; 414 415 #ifdef DIAGNOSTIC 416 if ((ccb->ccb_flags & CAC_CCB_ACTIVE) == 0) 417 panic("cac_ccb_done: CCB not active"); 418 ccb->ccb_flags &= ~CAC_CCB_ACTIVE; 419 #endif 420 421 if ((ccb->ccb_flags & (CAC_CCB_DATA_IN | CAC_CCB_DATA_OUT)) != 0) { 422 bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap_xfer, 0, 423 ccb->ccb_datasize, ccb->ccb_flags & CAC_CCB_DATA_IN ? 424 BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); 425 bus_dmamap_unload(sc->sc_dmat, ccb->ccb_dmamap_xfer); 426 } 427 428 error = ccb->ccb_req.error; 429 if (ccb->ccb_context.cc_handler != NULL) { 430 dv = ccb->ccb_context.cc_dv; 431 context = ccb->ccb_context.cc_context; 432 cac_ccb_free(sc, ccb); 433 (*ccb->ccb_context.cc_handler)(dv, context, error); 434 } else { 435 if ((error & CAC_RET_SOFT_ERROR) != 0) 436 printf("%s: soft error; array may be degraded\n", 437 sc->sc_dv.dv_xname); 438 if ((error & CAC_RET_HARD_ERROR) != 0) 439 printf("%s: hard error\n", sc->sc_dv.dv_xname); 440 if ((error & CAC_RET_CMD_REJECTED) != 0) { 441 error = 1; 442 printf("%s: invalid request\n", sc->sc_dv.dv_xname); 443 } 444 } 445 } 446 447 /* 448 * Allocate a CCB. 449 */ 450 struct cac_ccb * 451 cac_ccb_alloc(struct cac_softc *sc, int nosleep) 452 { 453 struct cac_ccb *ccb; 454 int s; 455 456 s = splbio(); 457 458 for (;;) { 459 if ((ccb = SIMPLEQ_FIRST(&sc->sc_ccb_free)) != NULL) { 460 SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_free, ccb, ccb_chain); 461 break; 462 } 463 if (nosleep) { 464 ccb = NULL; 465 break; 466 } 467 tsleep(&sc->sc_ccb_free, PRIBIO, "cacccb", 0); 468 } 469 470 splx(s); 471 return (ccb); 472 } 473 474 /* 475 * Put a CCB onto the freelist. 476 */ 477 void 478 cac_ccb_free(struct cac_softc *sc, struct cac_ccb *ccb) 479 { 480 int s; 481 482 ccb->ccb_flags = 0; 483 s = splbio(); 484 SIMPLEQ_INSERT_HEAD(&sc->sc_ccb_free, ccb, ccb_chain); 485 if (SIMPLEQ_NEXT(ccb, ccb_chain) == NULL) 486 wakeup_one(&sc->sc_ccb_free); 487 splx(s); 488 } 489 490 /* 491 * Board specific linkage shared between multiple bus types. 492 */ 493 494 static int 495 cac_l0_fifo_full(struct cac_softc *sc) 496 { 497 498 return (cac_inl(sc, CAC_REG_CMD_FIFO) == 0); 499 } 500 501 static void 502 cac_l0_submit(struct cac_softc *sc, struct cac_ccb *ccb) 503 { 504 505 bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, (caddr_t)ccb - sc->sc_ccbs, 506 sizeof(struct cac_ccb), BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); 507 cac_outl(sc, CAC_REG_CMD_FIFO, ccb->ccb_paddr); 508 } 509 510 static struct cac_ccb * 511 cac_l0_completed(struct cac_softc *sc) 512 { 513 struct cac_ccb *ccb; 514 paddr_t off; 515 516 off = (cac_inl(sc, CAC_REG_DONE_FIFO) & ~3) - sc->sc_ccbs_paddr; 517 ccb = (struct cac_ccb *)(sc->sc_ccbs + off); 518 519 bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, off, sizeof(struct cac_ccb), 520 BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); 521 522 return (ccb); 523 } 524 525 static int 526 cac_l0_intr_pending(struct cac_softc *sc) 527 { 528 529 return (cac_inl(sc, CAC_REG_INTR_PENDING)); 530 } 531 532 static void 533 cac_l0_intr_enable(struct cac_softc *sc, int state) 534 { 535 536 cac_outl(sc, CAC_REG_INTR_MASK, 537 state ? CAC_INTR_ENABLE : CAC_INTR_DISABLE); 538 } 539