1 /* $OpenBSD: uha.c,v 1.9 2006/11/28 23:59:45 dlg 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 <sys/user.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 #ifndef DDB
84 #define Debugger() panic("should call debugger here (ultra14f.c)")
85 #endif /* ! DDB */
86
87 #define KVTOPHYS(x) vtophys((vaddr_t)x)
88
89 integrate void uha_reset_mscp(struct uha_softc *, struct uha_mscp *);
90 void uha_free_mscp(struct uha_softc *, struct uha_mscp *);
91 integrate void uha_init_mscp(struct uha_softc *, struct uha_mscp *);
92 struct uha_mscp *uha_get_mscp(struct uha_softc *, int);
93 void uhaminphys(struct buf *);
94 int uha_scsi_cmd(struct scsi_xfer *);
95
96 struct scsi_adapter uha_switch = {
97 uha_scsi_cmd,
98 uhaminphys,
99 0,
100 0,
101 };
102
103 /* the below structure is so we have a default dev struct for out link struct */
104 struct scsi_device uha_dev = {
105 NULL, /* Use default error handler */
106 NULL, /* have a queue, served by this */
107 NULL, /* have no async handler */
108 NULL, /* Use default 'done' routine */
109 };
110
111 struct cfdriver uha_cd = {
112 NULL, "uha", DV_DULL
113 };
114
115 #define UHA_ABORT_TIMEOUT 2000 /* time to wait for abort (mSec) */
116
117 #ifdef __OpenBSD__
118 int uhaprint(void *, const char *);
119
120 int
121 uhaprint(aux, name)
122 void *aux;
123 const char *name;
124 {
125
126 if (name != NULL)
127 printf("%s: scsibus ", name);
128 return UNCONF;
129 }
130 #endif
131
132 /*
133 * Attach all the sub-devices we can find
134 */
135 void
136 uha_attach(sc)
137 struct uha_softc *sc;
138 {
139 struct scsibus_attach_args saa;
140
141 (sc->init)(sc);
142 TAILQ_INIT(&sc->sc_free_mscp);
143
144 /*
145 * fill in the prototype scsi_link.
146 */
147 sc->sc_link.adapter_softc = sc;
148 sc->sc_link.adapter_target = sc->sc_scsi_dev;
149 sc->sc_link.adapter = &uha_switch;
150 sc->sc_link.device = &uha_dev;
151 sc->sc_link.openings = 2;
152
153 bzero(&saa, sizeof(saa));
154 saa.saa_sc_link = &sc->sc_link;
155
156 /*
157 * ask the adapter what subunits are present
158 */
159 config_found(&sc->sc_dev, &saa, uhaprint);
160 }
161
162 integrate void
163 uha_reset_mscp(sc, mscp)
164 struct uha_softc *sc;
165 struct uha_mscp *mscp;
166 {
167
168 mscp->flags = 0;
169 }
170
171 /*
172 * A mscp (and hence a mbx-out) is put onto the free list.
173 */
174 void
175 uha_free_mscp(sc, mscp)
176 struct uha_softc *sc;
177 struct uha_mscp *mscp;
178 {
179 int s;
180
181 s = splbio();
182
183 uha_reset_mscp(sc, mscp);
184 TAILQ_INSERT_HEAD(&sc->sc_free_mscp, mscp, chain);
185
186 /*
187 * If there were none, wake anybody waiting for one to come free,
188 * starting with queued entries.
189 */
190 if (TAILQ_NEXT(mscp, chain) == NULL)
191 wakeup(&sc->sc_free_mscp);
192
193 splx(s);
194 }
195
196 integrate void
197 uha_init_mscp(sc, mscp)
198 struct uha_softc *sc;
199 struct uha_mscp *mscp;
200 {
201 int hashnum;
202
203 bzero(mscp, sizeof(struct uha_mscp));
204 /*
205 * put in the phystokv hash table
206 * Never gets taken out.
207 */
208 mscp->hashkey = KVTOPHYS(mscp);
209 hashnum = MSCP_HASH(mscp->hashkey);
210 mscp->nexthash = sc->sc_mscphash[hashnum];
211 sc->sc_mscphash[hashnum] = mscp;
212 uha_reset_mscp(sc, mscp);
213 }
214
215 /*
216 * Get a free mscp
217 *
218 * If there are none, see if we can allocate a new one. If so, put it in the
219 * hash table too otherwise either return an error or sleep.
220 */
221 struct uha_mscp *
222 uha_get_mscp(sc, flags)
223 struct uha_softc *sc;
224 int flags;
225 {
226 struct uha_mscp *mscp;
227 int s;
228
229 s = splbio();
230
231 /*
232 * If we can and have to, sleep waiting for one to come free
233 * but only if we can't allocate a new one
234 */
235 for (;;) {
236 mscp = TAILQ_FIRST(&sc->sc_free_mscp);
237 if (mscp) {
238 TAILQ_REMOVE(&sc->sc_free_mscp, mscp, chain);
239 break;
240 }
241 if (sc->sc_nummscps < UHA_MSCP_MAX) {
242 mscp = (struct uha_mscp *) malloc(sizeof(struct uha_mscp),
243 M_TEMP, M_NOWAIT);
244 if (!mscp) {
245 printf("%s: can't malloc mscp\n",
246 sc->sc_dev.dv_xname);
247 goto out;
248 }
249 uha_init_mscp(sc, mscp);
250 sc->sc_nummscps++;
251 break;
252 }
253 if ((flags & SCSI_NOSLEEP) != 0)
254 goto out;
255 tsleep(&sc->sc_free_mscp, PRIBIO, "uhamsc", 0);
256 }
257
258 mscp->flags |= MSCP_ALLOC;
259
260 out:
261 splx(s);
262 return (mscp);
263 }
264
265 /*
266 * given a physical address, find the mscp that it corresponds to.
267 */
268 struct uha_mscp *
269 uha_mscp_phys_kv(sc, mscp_phys)
270 struct uha_softc *sc;
271 u_long mscp_phys;
272 {
273 int hashnum = MSCP_HASH(mscp_phys);
274 struct uha_mscp *mscp = sc->sc_mscphash[hashnum];
275
276 while (mscp) {
277 if (mscp->hashkey == mscp_phys)
278 break;
279 mscp = mscp->nexthash;
280 }
281 return (mscp);
282 }
283
284 /*
285 * We have a mscp which has been processed by the adaptor, now we look to see
286 * how the operation went.
287 */
288 void
289 uha_done(sc, mscp)
290 struct uha_softc *sc;
291 struct uha_mscp *mscp;
292 {
293 struct scsi_sense_data *s1, *s2;
294 struct scsi_xfer *xs = mscp->xs;
295
296 SC_DEBUG(xs->sc_link, SDEV_DB2, ("uha_done\n"));
297 /*
298 * Otherwise, put the results of the operation
299 * into the xfer and call whoever started it
300 */
301 if ((mscp->flags & MSCP_ALLOC) == 0) {
302 printf("%s: exiting ccb not allocated!\n", sc->sc_dev.dv_xname);
303 Debugger();
304 return;
305 }
306 if (xs->error == XS_NOERROR) {
307 if (mscp->host_stat != UHA_NO_ERR) {
308 switch (mscp->host_stat) {
309 case UHA_SBUS_TIMEOUT: /* No response */
310 xs->error = XS_SELTIMEOUT;
311 break;
312 default: /* Other scsi protocol messes */
313 printf("%s: host_stat %x\n",
314 sc->sc_dev.dv_xname, mscp->host_stat);
315 xs->error = XS_DRIVER_STUFFUP;
316 }
317 } else if (mscp->target_stat != SCSI_OK) {
318 switch (mscp->target_stat) {
319 case SCSI_CHECK:
320 s1 = &mscp->mscp_sense;
321 s2 = &xs->sense;
322 *s2 = *s1;
323 xs->error = XS_SENSE;
324 break;
325 case SCSI_BUSY:
326 xs->error = XS_BUSY;
327 break;
328 default:
329 printf("%s: target_stat %x\n",
330 sc->sc_dev.dv_xname, mscp->target_stat);
331 xs->error = XS_DRIVER_STUFFUP;
332 }
333 } else
334 xs->resid = 0;
335 }
336 uha_free_mscp(sc, mscp);
337 xs->flags |= ITSDONE;
338 scsi_done(xs);
339 }
340
341 void
342 uhaminphys(bp)
343 struct buf *bp;
344 {
345
346 if (bp->b_bcount > ((UHA_NSEG - 1) << PGSHIFT))
347 bp->b_bcount = ((UHA_NSEG - 1) << PGSHIFT);
348 minphys(bp);
349 }
350
351 /*
352 * start a scsi operation given the command and the data address. Also
353 * needs the unit, target and lu.
354 */
355 int
356 uha_scsi_cmd(xs)
357 struct scsi_xfer *xs;
358 {
359 struct scsi_link *sc_link = xs->sc_link;
360 struct uha_softc *sc = sc_link->adapter_softc;
361 struct uha_mscp *mscp;
362 struct uha_dma_seg *sg;
363 int seg; /* scatter gather seg being worked on */
364 u_long thiskv, thisphys, nextphys;
365 int bytes_this_seg, bytes_this_page, datalen, flags;
366 int s;
367
368 SC_DEBUG(sc_link, SDEV_DB2, ("uha_scsi_cmd\n"));
369 /*
370 * get a mscp (mbox-out) to use. If the transfer
371 * is from a buf (possibly from interrupt time)
372 * then we can't allow it to sleep
373 */
374 flags = xs->flags;
375 if ((mscp = uha_get_mscp(sc, flags)) == NULL) {
376 return (TRY_AGAIN_LATER);
377 }
378 mscp->xs = xs;
379 mscp->timeout = xs->timeout;
380 timeout_set(&xs->stimeout, uha_timeout, xs);
381
382 /*
383 * Put all the arguments for the xfer in the mscp
384 */
385 if (flags & SCSI_RESET) {
386 mscp->opcode = UHA_SDR;
387 mscp->ca = 0x01;
388 } else {
389 mscp->opcode = UHA_TSP;
390 /* XXX Not for tapes. */
391 mscp->ca = 0x01;
392 bcopy(xs->cmd, &mscp->scsi_cmd, mscp->scsi_cmd_length);
393 }
394 mscp->xdir = UHA_SDET;
395 mscp->dcn = 0x00;
396 mscp->chan = 0x00;
397 mscp->target = sc_link->target;
398 mscp->lun = sc_link->lun;
399 mscp->scsi_cmd_length = xs->cmdlen;
400 mscp->sense_ptr = KVTOPHYS(&mscp->mscp_sense);
401 mscp->req_sense_length = sizeof(mscp->mscp_sense);
402 mscp->host_stat = 0x00;
403 mscp->target_stat = 0x00;
404
405 if (xs->datalen) {
406 sg = mscp->uha_dma;
407 seg = 0;
408 #ifdef TFS
409 if (flags & SCSI_DATA_UIO) {
410 struct iovec *iovp;
411 iovp = ((struct uio *) xs->data)->uio_iov;
412 datalen = ((struct uio *) xs->data)->uio_iovcnt;
413 xs->datalen = 0;
414 while (datalen && seg < UHA_NSEG) {
415 sg->seg_addr = (physaddr)iovp->iov_base;
416 sg->seg_len = iovp->iov_len;
417 xs->datalen += iovp->iov_len;
418 SC_DEBUGN(sc_link, SDEV_DB4, ("(0x%x@0x%x)",
419 iovp->iov_len, iovp->iov_base));
420 sg++;
421 iovp++;
422 seg++;
423 datalen--;
424 }
425 } else
426 #endif /*TFS */
427 {
428 /*
429 * Set up the scatter gather block
430 */
431 SC_DEBUG(sc_link, SDEV_DB4,
432 ("%d @0x%x:- ", xs->datalen, xs->data));
433 datalen = xs->datalen;
434 thiskv = (int) xs->data;
435 thisphys = KVTOPHYS(thiskv);
436
437 while (datalen && seg < UHA_NSEG) {
438 bytes_this_seg = 0;
439
440 /* put in the base address */
441 sg->seg_addr = thisphys;
442
443 SC_DEBUGN(sc_link, SDEV_DB4, ("0x%x", thisphys));
444
445 /* do it at least once */
446 nextphys = thisphys;
447 while (datalen && thisphys == nextphys) {
448 /*
449 * This page is contiguous (physically)
450 * with the last, just extend the
451 * length
452 */
453 /* how far to the end of the page */
454 nextphys = (thisphys & ~PGOFSET) + NBPG;
455 bytes_this_page = nextphys - thisphys;
456 /**** or the data ****/
457 bytes_this_page = min(bytes_this_page,
458 datalen);
459 bytes_this_seg += bytes_this_page;
460 datalen -= bytes_this_page;
461
462 /* get more ready for the next page */
463 thiskv = (thiskv & ~PGOFSET) + NBPG;
464 if (datalen)
465 thisphys = KVTOPHYS(thiskv);
466 }
467 /*
468 * next page isn't contiguous, finish the seg
469 */
470 SC_DEBUGN(sc_link, SDEV_DB4,
471 ("(0x%x)", bytes_this_seg));
472 sg->seg_len = bytes_this_seg;
473 sg++;
474 seg++;
475 }
476 }
477 /* end of iov/kv decision */
478 SC_DEBUGN(sc_link, SDEV_DB4, ("\n"));
479 if (datalen) {
480 /*
481 * there's still data, must have run out of segs!
482 */
483 printf("%s: uha_scsi_cmd, more than %d dma segs\n",
484 sc->sc_dev.dv_xname, UHA_NSEG);
485 goto bad;
486 }
487 mscp->data_addr = KVTOPHYS(mscp->uha_dma);
488 mscp->data_length = xs->datalen;
489 mscp->sgth = 0x01;
490 mscp->sg_num = seg;
491 } else { /* No data xfer, use non S/G values */
492 mscp->data_addr = (physaddr)0;
493 mscp->data_length = 0;
494 mscp->sgth = 0x00;
495 mscp->sg_num = 0;
496 }
497 mscp->link_id = 0;
498 mscp->link_addr = (physaddr)0;
499
500 s = splbio();
501 (sc->start_mbox)(sc, mscp);
502 splx(s);
503
504 /*
505 * Usually return SUCCESSFULLY QUEUED
506 */
507 if ((flags & SCSI_POLL) == 0)
508 return (SUCCESSFULLY_QUEUED);
509
510 /*
511 * If we can't use interrupts, poll on completion
512 */
513 if ((sc->poll)(sc, xs, mscp->timeout)) {
514 uha_timeout(mscp);
515 if ((sc->poll)(sc, xs, mscp->timeout))
516 uha_timeout(mscp);
517 }
518 return (COMPLETE);
519
520 bad:
521 xs->error = XS_DRIVER_STUFFUP;
522 uha_free_mscp(sc, mscp);
523 return (COMPLETE);
524 }
525
526 void
527 uha_timeout(arg)
528 void *arg;
529 {
530 struct uha_mscp *mscp = arg;
531 struct scsi_xfer *xs = mscp->xs;
532 struct scsi_link *sc_link = xs->sc_link;
533 struct uha_softc *sc = sc_link->adapter_softc;
534 int s;
535
536 sc_print_addr(sc_link);
537 printf("timed out");
538
539 s = splbio();
540
541 if (mscp->flags & MSCP_ABORT) {
542 /* abort timed out */
543 printf(" AGAIN\n");
544 /* XXX Must reset! */
545 } else {
546 /* abort the operation that has timed out */
547 printf("\n");
548 mscp->xs->error = XS_TIMEOUT;
549 mscp->timeout = UHA_ABORT_TIMEOUT;
550 mscp->flags |= MSCP_ABORT;
551 (sc->start_mbox)(sc, mscp);
552 }
553
554 splx(s);
555 }