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