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