1 /* $NetBSD: uha.c,v 1.30 2001/11/13 13:14:45 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.30 2001/11/13 13:14:45 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/types.h> 73 #include <sys/param.h> 74 #include <sys/systm.h> 75 #include <sys/kernel.h> 76 #include <sys/errno.h> 77 #include <sys/ioctl.h> 78 #include <sys/device.h> 79 #include <sys/malloc.h> 80 #include <sys/buf.h> 81 #include <sys/proc.h> 82 #include <sys/user.h> 83 84 #include <uvm/uvm_extern.h> 85 86 #include <machine/bus.h> 87 #include <machine/intr.h> 88 89 #include <dev/scsipi/scsi_all.h> 90 #include <dev/scsipi/scsipi_all.h> 91 #include <dev/scsipi/scsiconf.h> 92 93 #include <dev/ic/uhareg.h> 94 #include <dev/ic/uhavar.h> 95 96 #ifndef DDB 97 #define Debugger() panic("should call debugger here (uha.c)") 98 #endif /* ! DDB */ 99 100 #define UHA_MAXXFER ((UHA_NSEG - 1) << PGSHIFT) 101 102 integrate void uha_reset_mscp __P((struct uha_softc *, struct uha_mscp *)); 103 void uha_free_mscp __P((struct uha_softc *, struct uha_mscp *)); 104 integrate int uha_init_mscp __P((struct uha_softc *, struct uha_mscp *)); 105 struct uha_mscp *uha_get_mscp __P((struct uha_softc *)); 106 void uhaminphys __P((struct buf *)); 107 void uha_scsipi_request __P((struct scsipi_channel *, 108 scsipi_adapter_req_t, void *)); 109 int uha_create_mscps __P((struct uha_softc *, struct uha_mscp *, int)); 110 111 #define UHA_ABORT_TIMEOUT 2000 /* time to wait for abort (mSec) */ 112 113 /* 114 * Attach all the sub-devices we can find 115 */ 116 void 117 uha_attach(sc, upd) 118 struct uha_softc *sc; 119 struct uha_probe_data *upd; 120 { 121 struct scsipi_adapter *adapt = &sc->sc_adapter; 122 struct scsipi_channel *chan = &sc->sc_channel; 123 bus_dma_segment_t seg; 124 int i, error, rseg; 125 126 TAILQ_INIT(&sc->sc_free_mscp); 127 128 (sc->init)(sc); 129 130 /* 131 * Fill in the scsipi_adapter. 132 */ 133 memset(adapt, 0, sizeof(*adapt)); 134 adapt->adapt_dev = &sc->sc_dev; 135 adapt->adapt_nchannels = 1; 136 /* adapt_openings initialized below */ 137 /* adapt_max_periph initialized below */ 138 adapt->adapt_request = uha_scsipi_request; 139 adapt->adapt_minphys = uhaminphys; 140 141 /* 142 * Fill in the scsipi_channel. 143 */ 144 memset(chan, 0, sizeof(*chan)); 145 chan->chan_adapter = adapt; 146 chan->chan_bustype = &scsi_bustype; 147 chan->chan_channel = 0; 148 chan->chan_ntargets = 8; 149 chan->chan_nluns = 8; 150 chan->chan_id = upd->sc_scsi_dev; 151 152 #define MSCPSIZE (UHA_MSCP_MAX * sizeof(struct uha_mscp)) 153 154 /* 155 * Allocate the MSCPs. 156 */ 157 if ((error = bus_dmamem_alloc(sc->sc_dmat, MSCPSIZE, 158 PAGE_SIZE, 0, &seg, 1, &rseg, BUS_DMA_NOWAIT)) != 0) { 159 printf("%s: unable to allocate mscps, error = %d\n", 160 sc->sc_dev.dv_xname, error); 161 return; 162 } 163 if ((error = bus_dmamem_map(sc->sc_dmat, &seg, rseg, 164 MSCPSIZE, (caddr_t *)&sc->sc_mscps, 165 BUS_DMA_NOWAIT|BUS_DMA_COHERENT)) != 0) { 166 printf("%s: unable to map mscps, error = %d\n", 167 sc->sc_dev.dv_xname, error); 168 return; 169 } 170 171 /* 172 * Create and load the DMA map used for the mscps. 173 */ 174 if ((error = bus_dmamap_create(sc->sc_dmat, MSCPSIZE, 175 1, MSCPSIZE, 0, BUS_DMA_NOWAIT | sc->sc_dmaflags, 176 &sc->sc_dmamap_mscp)) != 0) { 177 printf("%s: unable to create mscp DMA map, error = %d\n", 178 sc->sc_dev.dv_xname, error); 179 return; 180 } 181 if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap_mscp, 182 sc->sc_mscps, MSCPSIZE, NULL, BUS_DMA_NOWAIT)) != 0) { 183 printf("%s: unable to load mscp DMA map, error = %d\n", 184 sc->sc_dev.dv_xname, error); 185 return; 186 } 187 188 #undef MSCPSIZE 189 190 /* 191 * Initialize the mscps. 192 */ 193 i = uha_create_mscps(sc, sc->sc_mscps, UHA_MSCP_MAX); 194 if (i == 0) { 195 printf("%s: unable to create mscps\n", 196 sc->sc_dev.dv_xname); 197 return; 198 } else if (i != UHA_MSCP_MAX) { 199 printf("%s: WARNING: only %d of %d mscps created\n", 200 sc->sc_dev.dv_xname, i, UHA_MSCP_MAX); 201 } 202 203 adapt->adapt_openings = i; 204 adapt->adapt_max_periph = adapt->adapt_openings; 205 206 /* 207 * ask the adapter what subunits are present 208 */ 209 config_found(&sc->sc_dev, &sc->sc_channel, scsiprint); 210 } 211 212 integrate void 213 uha_reset_mscp(sc, mscp) 214 struct uha_softc *sc; 215 struct uha_mscp *mscp; 216 { 217 218 mscp->flags = 0; 219 } 220 221 /* 222 * A mscp (and hence a mbx-out) is put onto the free list. 223 */ 224 void 225 uha_free_mscp(sc, mscp) 226 struct uha_softc *sc; 227 struct uha_mscp *mscp; 228 { 229 int s; 230 231 s = splbio(); 232 uha_reset_mscp(sc, mscp); 233 TAILQ_INSERT_HEAD(&sc->sc_free_mscp, mscp, chain); 234 splx(s); 235 } 236 237 integrate int 238 uha_init_mscp(sc, mscp) 239 struct uha_softc *sc; 240 struct uha_mscp *mscp; 241 { 242 bus_dma_tag_t dmat = sc->sc_dmat; 243 int hashnum, error; 244 245 /* 246 * Create the DMA map for this MSCP. 247 */ 248 error = bus_dmamap_create(dmat, UHA_MAXXFER, UHA_NSEG, UHA_MAXXFER, 249 0, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW | sc->sc_dmaflags, 250 &mscp->dmamap_xfer); 251 if (error) { 252 printf("%s: can't create mscp DMA map, error = %d\n", 253 sc->sc_dev.dv_xname, error); 254 return (error); 255 } 256 257 /* 258 * put in the phystokv hash table 259 * Never gets taken out. 260 */ 261 mscp->hashkey = sc->sc_dmamap_mscp->dm_segs[0].ds_addr + 262 UHA_MSCP_OFF(mscp); 263 hashnum = MSCP_HASH(mscp->hashkey); 264 mscp->nexthash = sc->sc_mscphash[hashnum]; 265 sc->sc_mscphash[hashnum] = mscp; 266 uha_reset_mscp(sc, mscp); 267 return (0); 268 } 269 270 /* 271 * Create a set of MSCPs and add them to the free list. 272 */ 273 int 274 uha_create_mscps(sc, mscpstore, count) 275 struct uha_softc *sc; 276 struct uha_mscp *mscpstore; 277 int count; 278 { 279 struct uha_mscp *mscp; 280 int i, error; 281 282 memset(mscpstore, 0, sizeof(struct uha_mscp) * count); 283 for (i = 0; i < count; i++) { 284 mscp = &mscpstore[i]; 285 if ((error = uha_init_mscp(sc, mscp)) != 0) { 286 printf("%s: unable to initialize mscp, error = %d\n", 287 sc->sc_dev.dv_xname, error); 288 goto out; 289 } 290 TAILQ_INSERT_TAIL(&sc->sc_free_mscp, mscp, chain); 291 } 292 out: 293 return (i); 294 } 295 296 /* 297 * Get a free mscp 298 * 299 * If there are none, see if we can allocate a new one. If so, put it in the 300 * hash table too otherwise either return an error or sleep. 301 */ 302 struct uha_mscp * 303 uha_get_mscp(sc) 304 struct uha_softc *sc; 305 { 306 struct uha_mscp *mscp; 307 int s; 308 309 s = splbio(); 310 mscp = TAILQ_FIRST(&sc->sc_free_mscp); 311 if (mscp != NULL) { 312 TAILQ_REMOVE(&sc->sc_free_mscp, mscp, chain); 313 mscp->flags |= MSCP_ALLOC; 314 } 315 splx(s); 316 return (mscp); 317 } 318 319 /* 320 * given a physical address, find the mscp that it corresponds to. 321 */ 322 struct uha_mscp * 323 uha_mscp_phys_kv(sc, mscp_phys) 324 struct uha_softc *sc; 325 u_long mscp_phys; 326 { 327 int hashnum = MSCP_HASH(mscp_phys); 328 struct uha_mscp *mscp = sc->sc_mscphash[hashnum]; 329 330 while (mscp) { 331 if (mscp->hashkey == mscp_phys) 332 break; 333 mscp = mscp->nexthash; 334 } 335 return (mscp); 336 } 337 338 /* 339 * We have a mscp which has been processed by the adaptor, now we look to see 340 * how the operation went. 341 */ 342 void 343 uha_done(sc, mscp) 344 struct uha_softc *sc; 345 struct uha_mscp *mscp; 346 { 347 bus_dma_tag_t dmat = sc->sc_dmat; 348 struct scsipi_sense_data *s1, *s2; 349 struct scsipi_xfer *xs = mscp->xs; 350 351 SC_DEBUG(xs->xs_periph, SCSIPI_DB2, ("uha_done\n")); 352 353 bus_dmamap_sync(dmat, sc->sc_dmamap_mscp, 354 UHA_MSCP_OFF(mscp), sizeof(struct uha_mscp), 355 BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); 356 357 /* 358 * If we were a data transfer, unload the map that described 359 * the data buffer. 360 */ 361 if (xs->datalen) { 362 bus_dmamap_sync(dmat, mscp->dmamap_xfer, 0, 363 mscp->dmamap_xfer->dm_mapsize, 364 (xs->xs_control & XS_CTL_DATA_IN) ? BUS_DMASYNC_POSTREAD : 365 BUS_DMASYNC_POSTWRITE); 366 bus_dmamap_unload(dmat, mscp->dmamap_xfer); 367 } 368 369 /* 370 * Otherwise, put the results of the operation 371 * into the xfer and call whoever started it 372 */ 373 if ((mscp->flags & MSCP_ALLOC) == 0) { 374 printf("%s: exiting ccb not allocated!\n", sc->sc_dev.dv_xname); 375 Debugger(); 376 return; 377 } 378 if (xs->error == XS_NOERROR) { 379 if (mscp->host_stat != UHA_NO_ERR) { 380 switch (mscp->host_stat) { 381 case UHA_SBUS_TIMEOUT: /* No response */ 382 xs->error = XS_SELTIMEOUT; 383 break; 384 default: /* Other scsi protocol messes */ 385 printf("%s: host_stat %x\n", 386 sc->sc_dev.dv_xname, mscp->host_stat); 387 xs->error = XS_DRIVER_STUFFUP; 388 } 389 } else if (mscp->target_stat != SCSI_OK) { 390 switch (mscp->target_stat) { 391 case SCSI_CHECK: 392 s1 = &mscp->mscp_sense; 393 s2 = &xs->sense.scsi_sense; 394 *s2 = *s1; 395 xs->error = XS_SENSE; 396 break; 397 case SCSI_BUSY: 398 xs->error = XS_BUSY; 399 break; 400 default: 401 printf("%s: target_stat %x\n", 402 sc->sc_dev.dv_xname, mscp->target_stat); 403 xs->error = XS_DRIVER_STUFFUP; 404 } 405 } else 406 xs->resid = 0; 407 } 408 uha_free_mscp(sc, mscp); 409 scsipi_done(xs); 410 } 411 412 void 413 uhaminphys(bp) 414 struct buf *bp; 415 { 416 417 if (bp->b_bcount > UHA_MAXXFER) 418 bp->b_bcount = UHA_MAXXFER; 419 minphys(bp); 420 } 421 422 /* 423 * start a scsi operation given the command and the data address. Also 424 * needs the unit, target and lu. 425 */ 426 427 void 428 uha_scsipi_request(chan, req, arg) 429 struct scsipi_channel *chan; 430 scsipi_adapter_req_t req; 431 void *arg; 432 { 433 struct scsipi_xfer *xs; 434 struct scsipi_periph *periph; 435 struct uha_softc *sc = (void *)chan->chan_adapter->adapt_dev; 436 bus_dma_tag_t dmat = sc->sc_dmat; 437 struct uha_mscp *mscp; 438 struct uha_dma_seg *sg; 439 int error, seg, flags, s; 440 441 442 switch (req) { 443 case ADAPTER_REQ_RUN_XFER: 444 xs = arg; 445 periph = xs->xs_periph; 446 flags = xs->xs_control; 447 448 SC_DEBUG(periph, SCSIPI_DB2, ("uha_scsipi_request\n")); 449 450 /* Get an MSCP to use. */ 451 mscp = uha_get_mscp(sc); 452 #ifdef DIAGNOSTIC 453 /* 454 * This should never happen as we track the resources 455 * in the mid-layer. 456 */ 457 if (mscp == NULL) { 458 scsipi_printaddr(periph); 459 printf("unable to allocate mscp\n"); 460 panic("uha_scsipi_request"); 461 } 462 #endif 463 464 mscp->xs = xs; 465 mscp->timeout = xs->timeout; 466 467 /* 468 * Put all the arguments for the xfer in the mscp 469 */ 470 if (flags & XS_CTL_RESET) { 471 mscp->opcode = UHA_SDR; 472 mscp->ca = 0x01; 473 } else { 474 mscp->opcode = UHA_TSP; 475 /* XXX Not for tapes. */ 476 mscp->ca = 0x01; 477 memcpy(&mscp->scsi_cmd, xs->cmd, mscp->scsi_cmd_length); 478 } 479 mscp->xdir = UHA_SDET; 480 mscp->dcn = 0x00; 481 mscp->chan = 0x00; 482 mscp->target = periph->periph_target; 483 mscp->lun = periph->periph_lun; 484 mscp->scsi_cmd_length = xs->cmdlen; 485 mscp->sense_ptr = sc->sc_dmamap_mscp->dm_segs[0].ds_addr + 486 UHA_MSCP_OFF(mscp) + offsetof(struct uha_mscp, mscp_sense); 487 mscp->req_sense_length = sizeof(mscp->mscp_sense); 488 mscp->host_stat = 0x00; 489 mscp->target_stat = 0x00; 490 491 if (xs->datalen) { 492 sg = mscp->uha_dma; 493 seg = 0; 494 #ifdef TFS 495 if (flags & SCSI_DATA_UIO) { 496 error = bus_dmamap_load_uio(dmat, 497 mscp->dmamap_xfer, (struct uio *)xs->data, 498 ((flags & XS_CTL_NOSLEEP) ? BUS_DMA_NOWAIT : 499 BUS_DMA_WAITOK) | BUS_DMA_STREAMING | 500 ((flags & XS_CTL_DATA_IN) ? BUS_DMA_READ : 501 BUS_DMA_WRITE)); 502 } else 503 #endif /*TFS */ 504 { 505 error = bus_dmamap_load(dmat, 506 mscp->dmamap_xfer, xs->data, xs->datalen, 507 NULL, 508 ((flags & XS_CTL_NOSLEEP) ? BUS_DMA_NOWAIT : 509 BUS_DMA_WAITOK) | BUS_DMA_STREAMING | 510 ((flags & XS_CTL_DATA_IN) ? BUS_DMA_READ : 511 BUS_DMA_WRITE)); 512 } 513 514 switch (error) { 515 case 0: 516 break; 517 518 case ENOMEM: 519 case EAGAIN: 520 xs->error = XS_RESOURCE_SHORTAGE; 521 goto out_bad; 522 523 default: 524 xs->error = XS_DRIVER_STUFFUP; 525 printf("%s: error %d loading DMA map\n", 526 sc->sc_dev.dv_xname, error); 527 out_bad: 528 uha_free_mscp(sc, mscp); 529 scsipi_done(xs); 530 return; 531 } 532 533 bus_dmamap_sync(dmat, mscp->dmamap_xfer, 0, 534 mscp->dmamap_xfer->dm_mapsize, 535 (flags & XS_CTL_DATA_IN) ? BUS_DMASYNC_PREREAD : 536 BUS_DMASYNC_PREWRITE); 537 538 /* 539 * Load the hardware scatter/gather map with the 540 * contents of the DMA map. 541 */ 542 for (seg = 0; 543 seg < mscp->dmamap_xfer->dm_nsegs; seg++) { 544 mscp->uha_dma[seg].seg_addr = 545 mscp->dmamap_xfer->dm_segs[seg].ds_addr; 546 mscp->uha_dma[seg].seg_len = 547 mscp->dmamap_xfer->dm_segs[seg].ds_len; 548 } 549 550 mscp->data_addr = 551 sc->sc_dmamap_mscp->dm_segs[0].ds_addr + 552 UHA_MSCP_OFF(mscp) + offsetof(struct uha_mscp, 553 uha_dma); 554 mscp->data_length = xs->datalen; 555 mscp->sgth = 0x01; 556 mscp->sg_num = seg; 557 } else { /* No data xfer, use non S/G values */ 558 mscp->data_addr = (physaddr)0; 559 mscp->data_length = 0; 560 mscp->sgth = 0x00; 561 mscp->sg_num = 0; 562 } 563 mscp->link_id = 0; 564 mscp->link_addr = (physaddr)0; 565 566 bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap_mscp, 567 UHA_MSCP_OFF(mscp), sizeof(struct uha_mscp), 568 BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); 569 570 s = splbio(); 571 (sc->start_mbox)(sc, mscp); 572 splx(s); 573 574 if ((flags & XS_CTL_POLL) == 0) 575 return; 576 577 /* 578 * If we can't use interrupts, poll on completion 579 */ 580 if ((sc->poll)(sc, xs, mscp->timeout)) { 581 uha_timeout(mscp); 582 if ((sc->poll)(sc, xs, mscp->timeout)) 583 uha_timeout(mscp); 584 } 585 return; 586 587 case ADAPTER_REQ_GROW_RESOURCES: 588 /* XXX Not supported. */ 589 return; 590 591 case ADAPTER_REQ_SET_XFER_MODE: 592 /* 593 * We can't really do this (the UltraStor controllers 594 * have their own config). 595 * 596 * XXX How do we query the config? 597 */ 598 return; 599 } 600 } 601 void 602 uha_timeout(arg) 603 void *arg; 604 { 605 struct uha_mscp *mscp = arg; 606 struct scsipi_xfer *xs = mscp->xs; 607 struct scsipi_periph *periph = xs->xs_periph; 608 struct uha_softc *sc = 609 (void *)periph->periph_channel->chan_adapter->adapt_dev; 610 int s; 611 612 scsipi_printaddr(periph); 613 printf("timed out"); 614 615 s = splbio(); 616 617 if (mscp->flags & MSCP_ABORT) { 618 /* abort timed out */ 619 printf(" AGAIN\n"); 620 /* XXX Must reset! */ 621 } else { 622 /* abort the operation that has timed out */ 623 printf("\n"); 624 mscp->xs->error = XS_TIMEOUT; 625 mscp->timeout = UHA_ABORT_TIMEOUT; 626 mscp->flags |= MSCP_ABORT; 627 (sc->start_mbox)(sc, mscp); 628 } 629 630 splx(s); 631 } 632