1 /* $NetBSD: uha.c,v 1.31 2001/11/15 09:48:08 lukem Exp $ */ 2 3 /*- 4 * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Charles M. Hannum and by Jason R. Thorpe of the Numerical Aerospace 9 * Simulation Facility, NASA Ames Research Center. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the NetBSD 22 * Foundation, Inc. and its contributors. 23 * 4. Neither the name of The NetBSD Foundation nor the names of its 24 * contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40 /* 41 * Ported for use with the UltraStor 14f by Gary Close (gclose@wvnvms.wvnet.edu) 42 * Slight fixes to timeouts to run with the 34F 43 * Thanks to Julian Elischer for advice and help with this port. 44 * 45 * Originally written by Julian Elischer (julian@tfs.com) 46 * for TRW Financial Systems for use under the MACH(2.5) operating system. 47 * 48 * TRW Financial Systems, in accordance with their agreement with Carnegie 49 * Mellon University, makes this software available to CMU to distribute 50 * or use in any manner that they see fit as long as this message is kept with 51 * the software. For this reason TFS also grants any other persons or 52 * organisations permission to use or modify this software. 53 * 54 * TFS supplies this software to be publicly redistributed 55 * on the understanding that TFS is not responsible for the correct 56 * functioning of this software in any circumstances. 57 * 58 * commenced: Sun Sep 27 18:14:01 PDT 1992 59 * slight mod to make work with 34F as well: Wed Jun 2 18:05:48 WST 1993 60 */ 61 62 #include <sys/cdefs.h> 63 __KERNEL_RCSID(0, "$NetBSD: uha.c,v 1.31 2001/11/15 09:48:08 lukem Exp $"); 64 65 #undef UHADEBUG 66 #ifdef DDB 67 #define integrate 68 #else 69 #define integrate static inline 70 #endif 71 72 #include <sys/param.h> 73 #include <sys/systm.h> 74 #include <sys/kernel.h> 75 #include <sys/errno.h> 76 #include <sys/ioctl.h> 77 #include <sys/device.h> 78 #include <sys/malloc.h> 79 #include <sys/buf.h> 80 #include <sys/proc.h> 81 #include <sys/user.h> 82 83 #include <uvm/uvm_extern.h> 84 85 #include <machine/bus.h> 86 #include <machine/intr.h> 87 88 #include <dev/scsipi/scsi_all.h> 89 #include <dev/scsipi/scsipi_all.h> 90 #include <dev/scsipi/scsiconf.h> 91 92 #include <dev/ic/uhareg.h> 93 #include <dev/ic/uhavar.h> 94 95 #ifndef DDB 96 #define Debugger() panic("should call debugger here (uha.c)") 97 #endif /* ! DDB */ 98 99 #define UHA_MAXXFER ((UHA_NSEG - 1) << PGSHIFT) 100 101 integrate void uha_reset_mscp __P((struct uha_softc *, struct uha_mscp *)); 102 void uha_free_mscp __P((struct uha_softc *, struct uha_mscp *)); 103 integrate int uha_init_mscp __P((struct uha_softc *, struct uha_mscp *)); 104 struct uha_mscp *uha_get_mscp __P((struct uha_softc *)); 105 void uhaminphys __P((struct buf *)); 106 void uha_scsipi_request __P((struct scsipi_channel *, 107 scsipi_adapter_req_t, void *)); 108 int uha_create_mscps __P((struct uha_softc *, struct uha_mscp *, int)); 109 110 #define UHA_ABORT_TIMEOUT 2000 /* time to wait for abort (mSec) */ 111 112 /* 113 * Attach all the sub-devices we can find 114 */ 115 void 116 uha_attach(sc, upd) 117 struct uha_softc *sc; 118 struct uha_probe_data *upd; 119 { 120 struct scsipi_adapter *adapt = &sc->sc_adapter; 121 struct scsipi_channel *chan = &sc->sc_channel; 122 bus_dma_segment_t seg; 123 int i, error, rseg; 124 125 TAILQ_INIT(&sc->sc_free_mscp); 126 127 (sc->init)(sc); 128 129 /* 130 * Fill in the scsipi_adapter. 131 */ 132 memset(adapt, 0, sizeof(*adapt)); 133 adapt->adapt_dev = &sc->sc_dev; 134 adapt->adapt_nchannels = 1; 135 /* adapt_openings initialized below */ 136 /* adapt_max_periph initialized below */ 137 adapt->adapt_request = uha_scsipi_request; 138 adapt->adapt_minphys = uhaminphys; 139 140 /* 141 * Fill in the scsipi_channel. 142 */ 143 memset(chan, 0, sizeof(*chan)); 144 chan->chan_adapter = adapt; 145 chan->chan_bustype = &scsi_bustype; 146 chan->chan_channel = 0; 147 chan->chan_ntargets = 8; 148 chan->chan_nluns = 8; 149 chan->chan_id = upd->sc_scsi_dev; 150 151 #define MSCPSIZE (UHA_MSCP_MAX * sizeof(struct uha_mscp)) 152 153 /* 154 * Allocate the MSCPs. 155 */ 156 if ((error = bus_dmamem_alloc(sc->sc_dmat, MSCPSIZE, 157 PAGE_SIZE, 0, &seg, 1, &rseg, BUS_DMA_NOWAIT)) != 0) { 158 printf("%s: unable to allocate mscps, error = %d\n", 159 sc->sc_dev.dv_xname, error); 160 return; 161 } 162 if ((error = bus_dmamem_map(sc->sc_dmat, &seg, rseg, 163 MSCPSIZE, (caddr_t *)&sc->sc_mscps, 164 BUS_DMA_NOWAIT|BUS_DMA_COHERENT)) != 0) { 165 printf("%s: unable to map mscps, error = %d\n", 166 sc->sc_dev.dv_xname, error); 167 return; 168 } 169 170 /* 171 * Create and load the DMA map used for the mscps. 172 */ 173 if ((error = bus_dmamap_create(sc->sc_dmat, MSCPSIZE, 174 1, MSCPSIZE, 0, BUS_DMA_NOWAIT | sc->sc_dmaflags, 175 &sc->sc_dmamap_mscp)) != 0) { 176 printf("%s: unable to create mscp DMA map, error = %d\n", 177 sc->sc_dev.dv_xname, error); 178 return; 179 } 180 if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap_mscp, 181 sc->sc_mscps, MSCPSIZE, NULL, BUS_DMA_NOWAIT)) != 0) { 182 printf("%s: unable to load mscp DMA map, error = %d\n", 183 sc->sc_dev.dv_xname, error); 184 return; 185 } 186 187 #undef MSCPSIZE 188 189 /* 190 * Initialize the mscps. 191 */ 192 i = uha_create_mscps(sc, sc->sc_mscps, UHA_MSCP_MAX); 193 if (i == 0) { 194 printf("%s: unable to create mscps\n", 195 sc->sc_dev.dv_xname); 196 return; 197 } else if (i != UHA_MSCP_MAX) { 198 printf("%s: WARNING: only %d of %d mscps created\n", 199 sc->sc_dev.dv_xname, i, UHA_MSCP_MAX); 200 } 201 202 adapt->adapt_openings = i; 203 adapt->adapt_max_periph = adapt->adapt_openings; 204 205 /* 206 * ask the adapter what subunits are present 207 */ 208 config_found(&sc->sc_dev, &sc->sc_channel, scsiprint); 209 } 210 211 integrate void 212 uha_reset_mscp(sc, mscp) 213 struct uha_softc *sc; 214 struct uha_mscp *mscp; 215 { 216 217 mscp->flags = 0; 218 } 219 220 /* 221 * A mscp (and hence a mbx-out) is put onto the free list. 222 */ 223 void 224 uha_free_mscp(sc, mscp) 225 struct uha_softc *sc; 226 struct uha_mscp *mscp; 227 { 228 int s; 229 230 s = splbio(); 231 uha_reset_mscp(sc, mscp); 232 TAILQ_INSERT_HEAD(&sc->sc_free_mscp, mscp, chain); 233 splx(s); 234 } 235 236 integrate int 237 uha_init_mscp(sc, mscp) 238 struct uha_softc *sc; 239 struct uha_mscp *mscp; 240 { 241 bus_dma_tag_t dmat = sc->sc_dmat; 242 int hashnum, error; 243 244 /* 245 * Create the DMA map for this MSCP. 246 */ 247 error = bus_dmamap_create(dmat, UHA_MAXXFER, UHA_NSEG, UHA_MAXXFER, 248 0, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW | sc->sc_dmaflags, 249 &mscp->dmamap_xfer); 250 if (error) { 251 printf("%s: can't create mscp DMA map, error = %d\n", 252 sc->sc_dev.dv_xname, error); 253 return (error); 254 } 255 256 /* 257 * put in the phystokv hash table 258 * Never gets taken out. 259 */ 260 mscp->hashkey = sc->sc_dmamap_mscp->dm_segs[0].ds_addr + 261 UHA_MSCP_OFF(mscp); 262 hashnum = MSCP_HASH(mscp->hashkey); 263 mscp->nexthash = sc->sc_mscphash[hashnum]; 264 sc->sc_mscphash[hashnum] = mscp; 265 uha_reset_mscp(sc, mscp); 266 return (0); 267 } 268 269 /* 270 * Create a set of MSCPs and add them to the free list. 271 */ 272 int 273 uha_create_mscps(sc, mscpstore, count) 274 struct uha_softc *sc; 275 struct uha_mscp *mscpstore; 276 int count; 277 { 278 struct uha_mscp *mscp; 279 int i, error; 280 281 memset(mscpstore, 0, sizeof(struct uha_mscp) * count); 282 for (i = 0; i < count; i++) { 283 mscp = &mscpstore[i]; 284 if ((error = uha_init_mscp(sc, mscp)) != 0) { 285 printf("%s: unable to initialize mscp, error = %d\n", 286 sc->sc_dev.dv_xname, error); 287 goto out; 288 } 289 TAILQ_INSERT_TAIL(&sc->sc_free_mscp, mscp, chain); 290 } 291 out: 292 return (i); 293 } 294 295 /* 296 * Get a free mscp 297 * 298 * If there are none, see if we can allocate a new one. If so, put it in the 299 * hash table too otherwise either return an error or sleep. 300 */ 301 struct uha_mscp * 302 uha_get_mscp(sc) 303 struct uha_softc *sc; 304 { 305 struct uha_mscp *mscp; 306 int s; 307 308 s = splbio(); 309 mscp = TAILQ_FIRST(&sc->sc_free_mscp); 310 if (mscp != NULL) { 311 TAILQ_REMOVE(&sc->sc_free_mscp, mscp, chain); 312 mscp->flags |= MSCP_ALLOC; 313 } 314 splx(s); 315 return (mscp); 316 } 317 318 /* 319 * given a physical address, find the mscp that it corresponds to. 320 */ 321 struct uha_mscp * 322 uha_mscp_phys_kv(sc, mscp_phys) 323 struct uha_softc *sc; 324 u_long mscp_phys; 325 { 326 int hashnum = MSCP_HASH(mscp_phys); 327 struct uha_mscp *mscp = sc->sc_mscphash[hashnum]; 328 329 while (mscp) { 330 if (mscp->hashkey == mscp_phys) 331 break; 332 mscp = mscp->nexthash; 333 } 334 return (mscp); 335 } 336 337 /* 338 * We have a mscp which has been processed by the adaptor, now we look to see 339 * how the operation went. 340 */ 341 void 342 uha_done(sc, mscp) 343 struct uha_softc *sc; 344 struct uha_mscp *mscp; 345 { 346 bus_dma_tag_t dmat = sc->sc_dmat; 347 struct scsipi_sense_data *s1, *s2; 348 struct scsipi_xfer *xs = mscp->xs; 349 350 SC_DEBUG(xs->xs_periph, SCSIPI_DB2, ("uha_done\n")); 351 352 bus_dmamap_sync(dmat, sc->sc_dmamap_mscp, 353 UHA_MSCP_OFF(mscp), sizeof(struct uha_mscp), 354 BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); 355 356 /* 357 * If we were a data transfer, unload the map that described 358 * the data buffer. 359 */ 360 if (xs->datalen) { 361 bus_dmamap_sync(dmat, mscp->dmamap_xfer, 0, 362 mscp->dmamap_xfer->dm_mapsize, 363 (xs->xs_control & XS_CTL_DATA_IN) ? BUS_DMASYNC_POSTREAD : 364 BUS_DMASYNC_POSTWRITE); 365 bus_dmamap_unload(dmat, mscp->dmamap_xfer); 366 } 367 368 /* 369 * Otherwise, put the results of the operation 370 * into the xfer and call whoever started it 371 */ 372 if ((mscp->flags & MSCP_ALLOC) == 0) { 373 printf("%s: exiting ccb not allocated!\n", sc->sc_dev.dv_xname); 374 Debugger(); 375 return; 376 } 377 if (xs->error == XS_NOERROR) { 378 if (mscp->host_stat != UHA_NO_ERR) { 379 switch (mscp->host_stat) { 380 case UHA_SBUS_TIMEOUT: /* No response */ 381 xs->error = XS_SELTIMEOUT; 382 break; 383 default: /* Other scsi protocol messes */ 384 printf("%s: host_stat %x\n", 385 sc->sc_dev.dv_xname, mscp->host_stat); 386 xs->error = XS_DRIVER_STUFFUP; 387 } 388 } else if (mscp->target_stat != SCSI_OK) { 389 switch (mscp->target_stat) { 390 case SCSI_CHECK: 391 s1 = &mscp->mscp_sense; 392 s2 = &xs->sense.scsi_sense; 393 *s2 = *s1; 394 xs->error = XS_SENSE; 395 break; 396 case SCSI_BUSY: 397 xs->error = XS_BUSY; 398 break; 399 default: 400 printf("%s: target_stat %x\n", 401 sc->sc_dev.dv_xname, mscp->target_stat); 402 xs->error = XS_DRIVER_STUFFUP; 403 } 404 } else 405 xs->resid = 0; 406 } 407 uha_free_mscp(sc, mscp); 408 scsipi_done(xs); 409 } 410 411 void 412 uhaminphys(bp) 413 struct buf *bp; 414 { 415 416 if (bp->b_bcount > UHA_MAXXFER) 417 bp->b_bcount = UHA_MAXXFER; 418 minphys(bp); 419 } 420 421 /* 422 * start a scsi operation given the command and the data address. Also 423 * needs the unit, target and lu. 424 */ 425 426 void 427 uha_scsipi_request(chan, req, arg) 428 struct scsipi_channel *chan; 429 scsipi_adapter_req_t req; 430 void *arg; 431 { 432 struct scsipi_xfer *xs; 433 struct scsipi_periph *periph; 434 struct uha_softc *sc = (void *)chan->chan_adapter->adapt_dev; 435 bus_dma_tag_t dmat = sc->sc_dmat; 436 struct uha_mscp *mscp; 437 struct uha_dma_seg *sg; 438 int error, seg, flags, s; 439 440 441 switch (req) { 442 case ADAPTER_REQ_RUN_XFER: 443 xs = arg; 444 periph = xs->xs_periph; 445 flags = xs->xs_control; 446 447 SC_DEBUG(periph, SCSIPI_DB2, ("uha_scsipi_request\n")); 448 449 /* Get an MSCP to use. */ 450 mscp = uha_get_mscp(sc); 451 #ifdef DIAGNOSTIC 452 /* 453 * This should never happen as we track the resources 454 * in the mid-layer. 455 */ 456 if (mscp == NULL) { 457 scsipi_printaddr(periph); 458 printf("unable to allocate mscp\n"); 459 panic("uha_scsipi_request"); 460 } 461 #endif 462 463 mscp->xs = xs; 464 mscp->timeout = xs->timeout; 465 466 /* 467 * Put all the arguments for the xfer in the mscp 468 */ 469 if (flags & XS_CTL_RESET) { 470 mscp->opcode = UHA_SDR; 471 mscp->ca = 0x01; 472 } else { 473 mscp->opcode = UHA_TSP; 474 /* XXX Not for tapes. */ 475 mscp->ca = 0x01; 476 memcpy(&mscp->scsi_cmd, xs->cmd, mscp->scsi_cmd_length); 477 } 478 mscp->xdir = UHA_SDET; 479 mscp->dcn = 0x00; 480 mscp->chan = 0x00; 481 mscp->target = periph->periph_target; 482 mscp->lun = periph->periph_lun; 483 mscp->scsi_cmd_length = xs->cmdlen; 484 mscp->sense_ptr = sc->sc_dmamap_mscp->dm_segs[0].ds_addr + 485 UHA_MSCP_OFF(mscp) + offsetof(struct uha_mscp, mscp_sense); 486 mscp->req_sense_length = sizeof(mscp->mscp_sense); 487 mscp->host_stat = 0x00; 488 mscp->target_stat = 0x00; 489 490 if (xs->datalen) { 491 sg = mscp->uha_dma; 492 seg = 0; 493 #ifdef TFS 494 if (flags & SCSI_DATA_UIO) { 495 error = bus_dmamap_load_uio(dmat, 496 mscp->dmamap_xfer, (struct uio *)xs->data, 497 ((flags & XS_CTL_NOSLEEP) ? BUS_DMA_NOWAIT : 498 BUS_DMA_WAITOK) | BUS_DMA_STREAMING | 499 ((flags & XS_CTL_DATA_IN) ? BUS_DMA_READ : 500 BUS_DMA_WRITE)); 501 } else 502 #endif /*TFS */ 503 { 504 error = bus_dmamap_load(dmat, 505 mscp->dmamap_xfer, xs->data, xs->datalen, 506 NULL, 507 ((flags & XS_CTL_NOSLEEP) ? BUS_DMA_NOWAIT : 508 BUS_DMA_WAITOK) | BUS_DMA_STREAMING | 509 ((flags & XS_CTL_DATA_IN) ? BUS_DMA_READ : 510 BUS_DMA_WRITE)); 511 } 512 513 switch (error) { 514 case 0: 515 break; 516 517 case ENOMEM: 518 case EAGAIN: 519 xs->error = XS_RESOURCE_SHORTAGE; 520 goto out_bad; 521 522 default: 523 xs->error = XS_DRIVER_STUFFUP; 524 printf("%s: error %d loading DMA map\n", 525 sc->sc_dev.dv_xname, error); 526 out_bad: 527 uha_free_mscp(sc, mscp); 528 scsipi_done(xs); 529 return; 530 } 531 532 bus_dmamap_sync(dmat, mscp->dmamap_xfer, 0, 533 mscp->dmamap_xfer->dm_mapsize, 534 (flags & XS_CTL_DATA_IN) ? BUS_DMASYNC_PREREAD : 535 BUS_DMASYNC_PREWRITE); 536 537 /* 538 * Load the hardware scatter/gather map with the 539 * contents of the DMA map. 540 */ 541 for (seg = 0; 542 seg < mscp->dmamap_xfer->dm_nsegs; seg++) { 543 mscp->uha_dma[seg].seg_addr = 544 mscp->dmamap_xfer->dm_segs[seg].ds_addr; 545 mscp->uha_dma[seg].seg_len = 546 mscp->dmamap_xfer->dm_segs[seg].ds_len; 547 } 548 549 mscp->data_addr = 550 sc->sc_dmamap_mscp->dm_segs[0].ds_addr + 551 UHA_MSCP_OFF(mscp) + offsetof(struct uha_mscp, 552 uha_dma); 553 mscp->data_length = xs->datalen; 554 mscp->sgth = 0x01; 555 mscp->sg_num = seg; 556 } else { /* No data xfer, use non S/G values */ 557 mscp->data_addr = (physaddr)0; 558 mscp->data_length = 0; 559 mscp->sgth = 0x00; 560 mscp->sg_num = 0; 561 } 562 mscp->link_id = 0; 563 mscp->link_addr = (physaddr)0; 564 565 bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap_mscp, 566 UHA_MSCP_OFF(mscp), sizeof(struct uha_mscp), 567 BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); 568 569 s = splbio(); 570 (sc->start_mbox)(sc, mscp); 571 splx(s); 572 573 if ((flags & XS_CTL_POLL) == 0) 574 return; 575 576 /* 577 * If we can't use interrupts, poll on completion 578 */ 579 if ((sc->poll)(sc, xs, mscp->timeout)) { 580 uha_timeout(mscp); 581 if ((sc->poll)(sc, xs, mscp->timeout)) 582 uha_timeout(mscp); 583 } 584 return; 585 586 case ADAPTER_REQ_GROW_RESOURCES: 587 /* XXX Not supported. */ 588 return; 589 590 case ADAPTER_REQ_SET_XFER_MODE: 591 /* 592 * We can't really do this (the UltraStor controllers 593 * have their own config). 594 * 595 * XXX How do we query the config? 596 */ 597 return; 598 } 599 } 600 void 601 uha_timeout(arg) 602 void *arg; 603 { 604 struct uha_mscp *mscp = arg; 605 struct scsipi_xfer *xs = mscp->xs; 606 struct scsipi_periph *periph = xs->xs_periph; 607 struct uha_softc *sc = 608 (void *)periph->periph_channel->chan_adapter->adapt_dev; 609 int s; 610 611 scsipi_printaddr(periph); 612 printf("timed out"); 613 614 s = splbio(); 615 616 if (mscp->flags & MSCP_ABORT) { 617 /* abort timed out */ 618 printf(" AGAIN\n"); 619 /* XXX Must reset! */ 620 } else { 621 /* abort the operation that has timed out */ 622 printf("\n"); 623 mscp->xs->error = XS_TIMEOUT; 624 mscp->timeout = UHA_ABORT_TIMEOUT; 625 mscp->flags |= MSCP_ABORT; 626 (sc->start_mbox)(sc, mscp); 627 } 628 629 splx(s); 630 } 631