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