1 /* $OpenBSD: uha.c,v 1.22 2011/04/03 12:42:36 krw Exp $ */ 2 /* $NetBSD: uha.c,v 1.3 1996/10/13 01:37:29 christos Exp $ */ 3 4 #undef UHADEBUG 5 #ifdef DDB 6 #define integrate 7 #else 8 #define integrate static inline 9 #endif 10 11 /* 12 * Copyright (c) 1994, 1996 Charles M. Hannum. All rights reserved. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions 16 * are met: 17 * 1. Redistributions of source code must retain the above copyright 18 * notice, this list of conditions and the following disclaimer. 19 * 2. Redistributions in binary form must reproduce the above copyright 20 * notice, this list of conditions and the following disclaimer in the 21 * documentation and/or other materials provided with the distribution. 22 * 3. All advertising materials mentioning features or use of this software 23 * must display the following acknowledgement: 24 * This product includes software developed by Charles M. Hannum. 25 * 4. The name of the author may not be used to endorse or promote products 26 * derived from this software without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 29 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 30 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 31 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 32 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 33 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 34 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 35 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 36 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 37 * THIS SOFTWARE, EVEN IF ADVISED OF THE 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/types.h> 63 #include <sys/param.h> 64 #include <sys/systm.h> 65 #include <sys/kernel.h> 66 #include <sys/errno.h> 67 #include <sys/ioctl.h> 68 #include <sys/device.h> 69 #include <sys/malloc.h> 70 #include <sys/buf.h> 71 #include <sys/proc.h> 72 #include <uvm/uvm_extern.h> 73 74 #include <machine/bus.h> 75 #include <machine/intr.h> 76 77 #include <scsi/scsi_all.h> 78 #include <scsi/scsiconf.h> 79 80 #include <dev/ic/uhareg.h> 81 #include <dev/ic/uhavar.h> 82 83 #define KVTOPHYS(x) vtophys((vaddr_t)x) 84 85 integrate void uha_reset_mscp(struct uha_softc *, struct uha_mscp *); 86 void uha_mscp_free(void *, void *); 87 integrate void uha_init_mscp(struct uha_softc *, struct uha_mscp *); 88 void *uha_mscp_alloc(void *); 89 void uhaminphys(struct buf *, struct scsi_link *); 90 void uha_scsi_cmd(struct scsi_xfer *); 91 92 struct scsi_adapter uha_switch = { 93 uha_scsi_cmd, 94 uhaminphys, 95 0, 96 0, 97 }; 98 99 struct cfdriver uha_cd = { 100 NULL, "uha", DV_DULL 101 }; 102 103 #define UHA_ABORT_TIMEOUT 2000 /* time to wait for abort (mSec) */ 104 105 #ifdef __OpenBSD__ 106 int uhaprint(void *, const char *); 107 108 int 109 uhaprint(aux, name) 110 void *aux; 111 const char *name; 112 { 113 114 if (name != NULL) 115 printf("%s: scsibus ", name); 116 return UNCONF; 117 } 118 #endif 119 120 /* 121 * Attach all the sub-devices we can find 122 */ 123 void 124 uha_attach(sc) 125 struct uha_softc *sc; 126 { 127 struct scsibus_attach_args saa; 128 129 (sc->init)(sc); 130 SLIST_INIT(&sc->sc_free_mscp); 131 132 mtx_init(&sc->sc_mscp_mtx, IPL_BIO); 133 scsi_iopool_init(&sc->sc_iopool, sc, uha_mscp_alloc, uha_mscp_free); 134 135 /* 136 * fill in the prototype scsi_link. 137 */ 138 sc->sc_link.adapter_softc = sc; 139 sc->sc_link.adapter_target = sc->sc_scsi_dev; 140 sc->sc_link.adapter = &uha_switch; 141 sc->sc_link.openings = 2; 142 sc->sc_link.pool = &sc->sc_iopool; 143 144 bzero(&saa, sizeof(saa)); 145 saa.saa_sc_link = &sc->sc_link; 146 147 /* 148 * ask the adapter what subunits are present 149 */ 150 config_found(&sc->sc_dev, &saa, uhaprint); 151 } 152 153 integrate void 154 uha_reset_mscp(sc, mscp) 155 struct uha_softc *sc; 156 struct uha_mscp *mscp; 157 { 158 159 mscp->flags = 0; 160 } 161 162 /* 163 * A mscp (and hence a mbx-out) is put onto the free list. 164 */ 165 void 166 uha_mscp_free(xsc, xmscp) 167 void *xsc, *xmscp; 168 { 169 struct uha_softc *sc = xmscp; 170 struct uha_mscp *mscp; 171 172 uha_reset_mscp(sc, mscp); 173 174 mtx_enter(&sc->sc_mscp_mtx); 175 SLIST_INSERT_HEAD(&sc->sc_free_mscp, mscp, chain); 176 mtx_leave(&sc->sc_mscp_mtx); 177 } 178 179 integrate void 180 uha_init_mscp(sc, mscp) 181 struct uha_softc *sc; 182 struct uha_mscp *mscp; 183 { 184 int hashnum; 185 186 bzero(mscp, sizeof(struct uha_mscp)); 187 /* 188 * put in the phystokv hash table 189 * Never gets taken out. 190 */ 191 mscp->hashkey = KVTOPHYS(mscp); 192 hashnum = MSCP_HASH(mscp->hashkey); 193 mscp->nexthash = sc->sc_mscphash[hashnum]; 194 sc->sc_mscphash[hashnum] = mscp; 195 uha_reset_mscp(sc, mscp); 196 } 197 198 /* 199 * Get a free mscp 200 */ 201 void * 202 uha_mscp_alloc(xsc) 203 void *xsc; 204 { 205 struct uha_softc *sc = xsc; 206 struct uha_mscp *mscp; 207 208 mtx_enter(&sc->sc_mscp_mtx); 209 mscp = SLIST_FIRST(&sc->sc_free_mscp); 210 if (mscp) { 211 SLIST_REMOVE_HEAD(&sc->sc_free_mscp, chain); 212 mscp->flags |= MSCP_ALLOC; 213 } 214 mtx_leave(&sc->sc_mscp_mtx); 215 216 return (mscp); 217 } 218 219 /* 220 * given a physical address, find the mscp that it corresponds to. 221 */ 222 struct uha_mscp * 223 uha_mscp_phys_kv(sc, mscp_phys) 224 struct uha_softc *sc; 225 u_long mscp_phys; 226 { 227 int hashnum = MSCP_HASH(mscp_phys); 228 struct uha_mscp *mscp = sc->sc_mscphash[hashnum]; 229 230 while (mscp) { 231 if (mscp->hashkey == mscp_phys) 232 break; 233 mscp = mscp->nexthash; 234 } 235 return (mscp); 236 } 237 238 /* 239 * We have a mscp which has been processed by the adaptor, now we look to see 240 * how the operation went. 241 */ 242 void 243 uha_done(sc, mscp) 244 struct uha_softc *sc; 245 struct uha_mscp *mscp; 246 { 247 struct scsi_sense_data *s1, *s2; 248 struct scsi_xfer *xs = mscp->xs; 249 250 SC_DEBUG(xs->sc_link, SDEV_DB2, ("uha_done\n")); 251 /* 252 * Otherwise, put the results of the operation 253 * into the xfer and call whoever started it 254 */ 255 if ((mscp->flags & MSCP_ALLOC) == 0) { 256 panic("%s: exiting ccb not allocated!", sc->sc_dev.dv_xname); 257 return; 258 } 259 if (xs->error == XS_NOERROR) { 260 if (mscp->host_stat != UHA_NO_ERR) { 261 switch (mscp->host_stat) { 262 case UHA_SBUS_TIMEOUT: /* No response */ 263 xs->error = XS_SELTIMEOUT; 264 break; 265 default: /* Other scsi protocol messes */ 266 printf("%s: host_stat %x\n", 267 sc->sc_dev.dv_xname, mscp->host_stat); 268 xs->error = XS_DRIVER_STUFFUP; 269 } 270 } else if (mscp->target_stat != SCSI_OK) { 271 switch (mscp->target_stat) { 272 case SCSI_CHECK: 273 s1 = &mscp->mscp_sense; 274 s2 = &xs->sense; 275 *s2 = *s1; 276 xs->error = XS_SENSE; 277 break; 278 case SCSI_BUSY: 279 xs->error = XS_BUSY; 280 break; 281 default: 282 printf("%s: target_stat %x\n", 283 sc->sc_dev.dv_xname, mscp->target_stat); 284 xs->error = XS_DRIVER_STUFFUP; 285 } 286 } else 287 xs->resid = 0; 288 } 289 290 scsi_done(xs); 291 } 292 293 void 294 uhaminphys(struct buf *bp, struct scsi_link *sl) 295 { 296 if (bp->b_bcount > ((UHA_NSEG - 1) << PGSHIFT)) 297 bp->b_bcount = ((UHA_NSEG - 1) << PGSHIFT); 298 minphys(bp); 299 } 300 301 /* 302 * start a scsi operation given the command and the data address. Also 303 * needs the unit, target and lu. 304 */ 305 void 306 uha_scsi_cmd(xs) 307 struct scsi_xfer *xs; 308 { 309 struct scsi_link *sc_link = xs->sc_link; 310 struct uha_softc *sc = sc_link->adapter_softc; 311 struct uha_mscp *mscp; 312 struct uha_dma_seg *sg; 313 int seg; /* scatter gather seg being worked on */ 314 u_long thiskv, thisphys, nextphys; 315 int bytes_this_seg, bytes_this_page, datalen, flags; 316 int s; 317 318 SC_DEBUG(sc_link, SDEV_DB2, ("uha_scsi_cmd\n")); 319 /* 320 * get a mscp (mbox-out) to use. If the transfer 321 * is from a buf (possibly from interrupt time) 322 * then we can't allow it to sleep 323 */ 324 flags = xs->flags; 325 mscp = xs->io; 326 327 mscp->xs = xs; 328 mscp->timeout = xs->timeout; 329 timeout_set(&xs->stimeout, uha_timeout, xs); 330 331 /* 332 * Put all the arguments for the xfer in the mscp 333 */ 334 if (flags & SCSI_RESET) { 335 mscp->opcode = UHA_SDR; 336 mscp->ca = 0x01; 337 } else { 338 mscp->opcode = UHA_TSP; 339 /* XXX Not for tapes. */ 340 mscp->ca = 0x01; 341 bcopy(xs->cmd, &mscp->scsi_cmd, mscp->scsi_cmd_length); 342 } 343 mscp->xdir = UHA_SDET; 344 mscp->dcn = 0x00; 345 mscp->chan = 0x00; 346 mscp->target = sc_link->target; 347 mscp->lun = sc_link->lun; 348 mscp->scsi_cmd_length = xs->cmdlen; 349 mscp->sense_ptr = KVTOPHYS(&mscp->mscp_sense); 350 mscp->req_sense_length = sizeof(mscp->mscp_sense); 351 mscp->host_stat = 0x00; 352 mscp->target_stat = 0x00; 353 354 if (xs->datalen) { 355 sg = mscp->uha_dma; 356 seg = 0; 357 358 /* 359 * Set up the scatter gather block 360 */ 361 SC_DEBUG(sc_link, SDEV_DB4, 362 ("%d @0x%x:- ", xs->datalen, xs->data)); 363 datalen = xs->datalen; 364 thiskv = (int) xs->data; 365 thisphys = KVTOPHYS(thiskv); 366 367 while (datalen && seg < UHA_NSEG) { 368 bytes_this_seg = 0; 369 370 /* put in the base address */ 371 sg->seg_addr = thisphys; 372 373 SC_DEBUGN(sc_link, SDEV_DB4, ("0x%x", thisphys)); 374 375 /* do it at least once */ 376 nextphys = thisphys; 377 while (datalen && thisphys == nextphys) { 378 /* 379 * This page is contiguous (physically) 380 * with the last, just extend the 381 * length 382 */ 383 /* how far to the end of the page */ 384 nextphys = (thisphys & ~PGOFSET) + NBPG; 385 bytes_this_page = nextphys - thisphys; 386 /**** or the data ****/ 387 bytes_this_page = min(bytes_this_page, 388 datalen); 389 bytes_this_seg += bytes_this_page; 390 datalen -= bytes_this_page; 391 392 /* get more ready for the next page */ 393 thiskv = (thiskv & ~PGOFSET) + NBPG; 394 if (datalen) 395 thisphys = KVTOPHYS(thiskv); 396 } 397 /* 398 * next page isn't contiguous, finish the seg 399 */ 400 SC_DEBUGN(sc_link, SDEV_DB4, 401 ("(0x%x)", bytes_this_seg)); 402 sg->seg_len = bytes_this_seg; 403 sg++; 404 seg++; 405 } 406 407 SC_DEBUGN(sc_link, SDEV_DB4, ("\n")); 408 if (datalen) { 409 /* 410 * there's still data, must have run out of segs! 411 */ 412 printf("%s: uha_scsi_cmd, more than %d dma segs\n", 413 sc->sc_dev.dv_xname, UHA_NSEG); 414 goto bad; 415 } 416 mscp->data_addr = KVTOPHYS(mscp->uha_dma); 417 mscp->data_length = xs->datalen; 418 mscp->sgth = 0x01; 419 mscp->sg_num = seg; 420 } else { /* No data xfer, use non S/G values */ 421 mscp->data_addr = (physaddr)0; 422 mscp->data_length = 0; 423 mscp->sgth = 0x00; 424 mscp->sg_num = 0; 425 } 426 mscp->link_id = 0; 427 mscp->link_addr = (physaddr)0; 428 429 s = splbio(); 430 (sc->start_mbox)(sc, mscp); 431 splx(s); 432 433 /* 434 * Usually return SUCCESSFULLY QUEUED 435 */ 436 if ((flags & SCSI_POLL) == 0) 437 return; 438 439 /* 440 * If we can't use interrupts, poll on completion 441 */ 442 if ((sc->poll)(sc, xs, mscp->timeout)) { 443 uha_timeout(mscp); 444 if ((sc->poll)(sc, xs, mscp->timeout)) 445 uha_timeout(mscp); 446 } 447 return; 448 449 bad: 450 xs->error = XS_DRIVER_STUFFUP; 451 scsi_done(xs); 452 return; 453 } 454 455 void 456 uha_timeout(arg) 457 void *arg; 458 { 459 struct uha_mscp *mscp = arg; 460 struct scsi_xfer *xs = mscp->xs; 461 struct scsi_link *sc_link = xs->sc_link; 462 struct uha_softc *sc = sc_link->adapter_softc; 463 int s; 464 465 sc_print_addr(sc_link); 466 printf("timed out"); 467 468 s = splbio(); 469 470 if (mscp->flags & MSCP_ABORT) { 471 /* abort timed out */ 472 printf(" AGAIN\n"); 473 /* XXX Must reset! */ 474 } else { 475 /* abort the operation that has timed out */ 476 printf("\n"); 477 mscp->xs->error = XS_TIMEOUT; 478 mscp->timeout = UHA_ABORT_TIMEOUT; 479 mscp->flags |= MSCP_ABORT; 480 (sc->start_mbox)(sc, mscp); 481 } 482 483 splx(s); 484 } 485