1 /* $OpenBSD: uha.c,v 1.40 2021/03/07 06:21:38 jsg 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(void *aux, const char *name) 95 { 96 97 if (name != NULL) 98 printf("%s: scsibus ", name); 99 return UNCONF; 100 } 101 #endif 102 103 /* 104 * Attach all the sub-devices we can find 105 */ 106 void 107 uha_attach(struct uha_softc *sc) 108 { 109 struct scsibus_attach_args saa; 110 111 (sc->init)(sc); 112 SLIST_INIT(&sc->sc_free_mscp); 113 114 mtx_init(&sc->sc_mscp_mtx, IPL_BIO); 115 scsi_iopool_init(&sc->sc_iopool, sc, uha_mscp_alloc, uha_mscp_free); 116 117 saa.saa_adapter_softc = sc; 118 saa.saa_adapter_target = sc->sc_scsi_dev; 119 saa.saa_adapter = &uha_switch; 120 saa.saa_luns = saa.saa_adapter_buswidth = 8; 121 saa.saa_openings = 2; 122 saa.saa_pool = &sc->sc_iopool; 123 saa.saa_quirks = saa.saa_flags = 0; 124 saa.saa_wwpn = saa.saa_wwnn = 0; 125 126 config_found(&sc->sc_dev, &saa, uhaprint); 127 } 128 129 void 130 uha_reset_mscp(struct uha_softc *sc, struct uha_mscp *mscp) 131 { 132 133 mscp->flags = 0; 134 } 135 136 /* 137 * A mscp (and hence a mbx-out) is put onto the free list. 138 */ 139 void 140 uha_mscp_free(void *xsc, void *xmscp) 141 { 142 struct uha_softc *sc = xsc; 143 struct uha_mscp *mscp = xmscp; 144 145 uha_reset_mscp(sc, mscp); 146 147 mtx_enter(&sc->sc_mscp_mtx); 148 SLIST_INSERT_HEAD(&sc->sc_free_mscp, mscp, chain); 149 mtx_leave(&sc->sc_mscp_mtx); 150 } 151 152 /* 153 * Get a free mscp 154 */ 155 void * 156 uha_mscp_alloc(void *xsc) 157 { 158 struct uha_softc *sc = xsc; 159 struct uha_mscp *mscp; 160 161 mtx_enter(&sc->sc_mscp_mtx); 162 mscp = SLIST_FIRST(&sc->sc_free_mscp); 163 if (mscp) { 164 SLIST_REMOVE_HEAD(&sc->sc_free_mscp, chain); 165 mscp->flags |= MSCP_ALLOC; 166 } 167 mtx_leave(&sc->sc_mscp_mtx); 168 169 return (mscp); 170 } 171 172 /* 173 * given a physical address, find the mscp that it corresponds to. 174 */ 175 struct uha_mscp * 176 uha_mscp_phys_kv(struct uha_softc *sc, u_long mscp_phys) 177 { 178 int hashnum = MSCP_HASH(mscp_phys); 179 struct uha_mscp *mscp = sc->sc_mscphash[hashnum]; 180 181 while (mscp) { 182 if (mscp->hashkey == mscp_phys) 183 break; 184 mscp = mscp->nexthash; 185 } 186 return (mscp); 187 } 188 189 /* 190 * We have a mscp which has been processed by the adaptor, now we look to see 191 * how the operation went. 192 */ 193 void 194 uha_done(struct uha_softc *sc, struct uha_mscp *mscp) 195 { 196 struct scsi_sense_data *s1, *s2; 197 struct scsi_xfer *xs = mscp->xs; 198 199 #ifdef UHADEBUG 200 printf("%s: uha_done\n", sc->sc_dev.dv_xname); 201 #endif 202 203 /* 204 * Otherwise, put the results of the operation 205 * into the xfer and call whoever started it 206 */ 207 if ((mscp->flags & MSCP_ALLOC) == 0) { 208 panic("%s: exiting ccb not allocated!", sc->sc_dev.dv_xname); 209 return; 210 } 211 if (xs->error == XS_NOERROR) { 212 if (mscp->host_stat != UHA_NO_ERR) { 213 switch (mscp->host_stat) { 214 case UHA_SBUS_TIMEOUT: /* No response */ 215 xs->error = XS_SELTIMEOUT; 216 break; 217 default: /* Other scsi protocol messes */ 218 printf("%s: host_stat %x\n", 219 sc->sc_dev.dv_xname, mscp->host_stat); 220 xs->error = XS_DRIVER_STUFFUP; 221 } 222 } else if (mscp->target_stat != SCSI_OK) { 223 switch (mscp->target_stat) { 224 case SCSI_CHECK: 225 s1 = &mscp->mscp_sense; 226 s2 = &xs->sense; 227 *s2 = *s1; 228 xs->error = XS_SENSE; 229 break; 230 case SCSI_BUSY: 231 xs->error = XS_BUSY; 232 break; 233 default: 234 printf("%s: target_stat %x\n", 235 sc->sc_dev.dv_xname, mscp->target_stat); 236 xs->error = XS_DRIVER_STUFFUP; 237 } 238 } else 239 xs->resid = 0; 240 } 241 242 scsi_done(xs); 243 } 244 245 /* 246 * start a scsi operation given the command and the data address. Also 247 * needs the unit, target and lu. 248 */ 249 void 250 uha_scsi_cmd(struct scsi_xfer *xs) 251 { 252 struct scsi_link *sc_link = xs->sc_link; 253 struct uha_softc *sc = sc_link->bus->sb_adapter_softc; 254 struct uha_mscp *mscp; 255 struct uha_dma_seg *sg; 256 int seg; /* scatter gather seg being worked on */ 257 u_long thiskv, thisphys, nextphys; 258 int bytes_this_seg, bytes_this_page, datalen, flags; 259 int s; 260 261 #ifdef UHADEBUG 262 printf("%s: uha_scsi_cmd\n", sc->sc_dev.dv_xname); 263 #endif 264 /* 265 * get a mscp (mbox-out) to use. If the transfer 266 * is from a buf (possibly from interrupt time) 267 * then we can't allow it to sleep 268 */ 269 flags = xs->flags; 270 mscp = xs->io; 271 272 mscp->xs = xs; 273 mscp->timeout = xs->timeout; 274 timeout_set(&xs->stimeout, uha_timeout, xs); 275 276 /* 277 * Put all the arguments for the xfer in the mscp 278 */ 279 if (flags & SCSI_RESET) { 280 mscp->opcode = UHA_SDR; 281 mscp->ca = 0x01; 282 } else { 283 mscp->opcode = UHA_TSP; 284 /* XXX Not for tapes. */ 285 mscp->ca = 0x01; 286 bcopy(&xs->cmd, &mscp->scsi_cmd, mscp->scsi_cmd_length); 287 } 288 mscp->xdir = UHA_SDET; 289 mscp->dcn = 0x00; 290 mscp->chan = 0x00; 291 mscp->target = sc_link->target; 292 mscp->lun = sc_link->lun; 293 mscp->scsi_cmd_length = xs->cmdlen; 294 mscp->sense_ptr = KVTOPHYS(&mscp->mscp_sense); 295 mscp->req_sense_length = sizeof(mscp->mscp_sense); 296 mscp->host_stat = 0x00; 297 mscp->target_stat = 0x00; 298 299 if (xs->datalen) { 300 sg = mscp->uha_dma; 301 seg = 0; 302 303 /* 304 * Set up the scatter gather block 305 */ 306 #ifdef UHADEBUG 307 printf("%s: %d @%p- ", sc->sc_dev.dv_xname, xs->datalen, xs->data); 308 #endif 309 datalen = xs->datalen; 310 thiskv = (int) xs->data; 311 thisphys = KVTOPHYS(thiskv); 312 313 while (datalen && seg < UHA_NSEG) { 314 bytes_this_seg = 0; 315 316 /* put in the base address */ 317 sg->seg_addr = thisphys; 318 319 #ifdef UHADEBUG 320 printf("0x%lx", thisphys); 321 #endif 322 323 /* do it at least once */ 324 nextphys = thisphys; 325 while (datalen && thisphys == nextphys) { 326 /* 327 * This page is contiguous (physically) 328 * with the last, just extend the 329 * length 330 */ 331 /* how far to the end of the page */ 332 nextphys = (thisphys & ~PGOFSET) + NBPG; 333 bytes_this_page = nextphys - thisphys; 334 /**** or the data ****/ 335 bytes_this_page = min(bytes_this_page, 336 datalen); 337 bytes_this_seg += bytes_this_page; 338 datalen -= bytes_this_page; 339 340 /* get more ready for the next page */ 341 thiskv = (thiskv & ~PGOFSET) + NBPG; 342 if (datalen) 343 thisphys = KVTOPHYS(thiskv); 344 } 345 /* 346 * next page isn't contiguous, finish the seg 347 */ 348 #ifdef UHADEBUG 349 printf("(0x%x)", bytes_this_seg); 350 #endif 351 sg->seg_len = bytes_this_seg; 352 sg++; 353 seg++; 354 } 355 356 #ifdef UHADEBUG 357 printf("\n"); 358 #endif 359 if (datalen) { 360 /* 361 * there's still data, must have run out of segs! 362 */ 363 printf("%s: uha_scsi_cmd, more than %d dma segs\n", 364 sc->sc_dev.dv_xname, UHA_NSEG); 365 goto bad; 366 } 367 mscp->data_addr = KVTOPHYS(mscp->uha_dma); 368 mscp->data_length = xs->datalen; 369 mscp->sgth = 0x01; 370 mscp->sg_num = seg; 371 } else { /* No data xfer, use non S/G values */ 372 mscp->data_addr = (physaddr)0; 373 mscp->data_length = 0; 374 mscp->sgth = 0x00; 375 mscp->sg_num = 0; 376 } 377 mscp->link_id = 0; 378 mscp->link_addr = (physaddr)0; 379 380 s = splbio(); 381 (sc->start_mbox)(sc, mscp); 382 splx(s); 383 384 /* 385 * Usually return SUCCESSFULLY QUEUED 386 */ 387 if ((flags & SCSI_POLL) == 0) 388 return; 389 390 /* 391 * If we can't use interrupts, poll on completion 392 */ 393 if ((sc->poll)(sc, xs, mscp->timeout)) { 394 uha_timeout(mscp); 395 if ((sc->poll)(sc, xs, mscp->timeout)) 396 uha_timeout(mscp); 397 } 398 return; 399 400 bad: 401 xs->error = XS_DRIVER_STUFFUP; 402 scsi_done(xs); 403 return; 404 } 405 406 void 407 uha_timeout(void *arg) 408 { 409 struct uha_mscp *mscp = arg; 410 struct scsi_xfer *xs = mscp->xs; 411 struct scsi_link *sc_link = xs->sc_link; 412 struct uha_softc *sc = sc_link->bus->sb_adapter_softc; 413 int s; 414 415 sc_print_addr(sc_link); 416 printf("timed out"); 417 418 s = splbio(); 419 420 if (mscp->flags & MSCP_ABORT) { 421 /* abort timed out */ 422 printf(" AGAIN\n"); 423 /* XXX Must reset! */ 424 } else { 425 /* abort the operation that has timed out */ 426 printf("\n"); 427 mscp->xs->error = XS_TIMEOUT; 428 mscp->timeout = UHA_ABORT_TIMEOUT; 429 mscp->flags |= MSCP_ABORT; 430 (sc->start_mbox)(sc, mscp); 431 } 432 433 splx(s); 434 } 435