xref: /openbsd/sys/dev/ic/oosiop.c (revision 891d7ab6)
1 /*	$OpenBSD: oosiop.c,v 1.19 2011/06/27 21:33:20 miod Exp $	*/
2 /*	$NetBSD: oosiop.c,v 1.4 2003/10/29 17:45:55 tsutsui Exp $	*/
3 
4 /*
5  * Copyright (c) 2001 Shuichiro URATA.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. The name of the author may not be used to endorse or promote products
16  *    derived from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 /*
31  * NCR53C700 SCSI I/O processor (OOSIOP) driver
32  *
33  * TODO:
34  *   - Better error handling.
35  *   - Implement tagged queuing.
36  */
37 
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/timeout.h>
41 #include <sys/kernel.h>
42 #include <sys/device.h>
43 #include <sys/buf.h>
44 #include <sys/malloc.h>
45 #include <sys/queue.h>
46 
47 #include <uvm/uvm_extern.h>
48 
49 #include <scsi/scsi_all.h>
50 #include <scsi/scsiconf.h>
51 #include <scsi/scsi_message.h>
52 
53 #include <machine/cpu.h>
54 #include <machine/bus.h>
55 
56 #include <dev/ic/oosiopreg.h>
57 #include <dev/ic/oosiopvar.h>
58 
59 /* 53C700 script */
60 #include <dev/microcode/siop/oosiop.out>
61 
62 int	oosiop_alloc_cb(struct oosiop_softc *, int);
63 
64 static __inline void oosiop_relocate_io(struct oosiop_softc *, bus_addr_t);
65 static __inline void oosiop_relocate_tc(struct oosiop_softc *, bus_addr_t);
66 static __inline void oosiop_fixup_select(struct oosiop_softc *, bus_addr_t,
67 		         int);
68 static __inline void oosiop_fixup_jump(struct oosiop_softc *, bus_addr_t,
69 		         bus_addr_t);
70 static __inline void oosiop_fixup_move(struct oosiop_softc *, bus_addr_t,
71 		         bus_size_t, bus_addr_t);
72 
73 void	oosiop_load_script(struct oosiop_softc *);
74 void	oosiop_setup_sgdma(struct oosiop_softc *, struct oosiop_cb *);
75 void	oosiop_setup_dma(struct oosiop_softc *);
76 void	oosiop_flush_fifo(struct oosiop_softc *);
77 void	oosiop_clear_fifo(struct oosiop_softc *);
78 void	oosiop_phasemismatch(struct oosiop_softc *);
79 void	oosiop_setup_syncxfer(struct oosiop_softc *);
80 void	oosiop_set_syncparam(struct oosiop_softc *, int, int, int);
81 void	oosiop_minphys(struct buf *, struct scsi_link *);
82 void	oosiop_scsicmd(struct scsi_xfer *);
83 void	oosiop_done(struct oosiop_softc *, struct oosiop_cb *);
84 void	oosiop_timeout(void *);
85 void	oosiop_reset(struct oosiop_softc *);
86 void	oosiop_reset_bus(struct oosiop_softc *);
87 void	oosiop_scriptintr(struct oosiop_softc *);
88 void	oosiop_msgin(struct oosiop_softc *, struct oosiop_cb *);
89 void	oosiop_setup(struct oosiop_softc *, struct oosiop_cb *);
90 void	oosiop_poll(struct oosiop_softc *, struct oosiop_cb *);
91 void	oosiop_processintr(struct oosiop_softc *, u_int8_t);
92 
93 void	*oosiop_cb_alloc(void *);
94 void	oosiop_cb_free(void *, void *);
95 
96 /* Trap interrupt code for unexpected data I/O */
97 #define	DATAIN_TRAP	0xdead0001
98 #define	DATAOUT_TRAP	0xdead0002
99 
100 /* Possible TP and SCF conbination */
101 static const struct {
102 	u_int8_t	tp;
103 	u_int8_t	scf;
104 } synctbl[] = {
105 	{0, 1},		/* SCLK /  4.0 */
106 	{1, 1},		/* SCLK /  5.0 */
107 	{2, 1},		/* SCLK /  6.0 */
108 	{3, 1},		/* SCLK /  7.0 */
109 	{1, 2},		/* SCLK /  7.5 */
110 	{4, 1},		/* SCLK /  8.0 */
111 	{5, 1},		/* SCLK /  9.0 */
112 	{6, 1},		/* SCLK / 10.0 */
113 	{3, 2},		/* SCLK / 10.5 */
114 	{7, 1},		/* SCLK / 11.0 */
115 	{4, 2},		/* SCLK / 12.0 */
116 	{5, 2},		/* SCLK / 13.5 */
117 	{3, 3},		/* SCLK / 14.0 */
118 	{6, 2},		/* SCLK / 15.0 */
119 	{4, 3},		/* SCLK / 16.0 */
120 	{7, 2},		/* SCLK / 16.5 */
121 	{5, 3},		/* SCLK / 18.0 */
122 	{6, 3},		/* SCLK / 20.0 */
123 	{7, 3}		/* SCLK / 22.0 */
124 };
125 #define	NSYNCTBL	(sizeof(synctbl) / sizeof(synctbl[0]))
126 
127 #define	oosiop_period(sc, tp, scf)					\
128 	    (((1000000000 / (sc)->sc_freq) * (tp) * (scf)) / 40)
129 
130 struct cfdriver oosiop_cd = {
131 	NULL, "oosiop", DV_DULL
132 };
133 
134 struct scsi_adapter oosiop_adapter = {
135 	oosiop_scsicmd,
136 	oosiop_minphys,
137 	NULL,
138 	NULL
139 };
140 
141 void *
142 oosiop_cb_alloc(void *xsc)
143 {
144 	struct oosiop_softc *sc = xsc;
145 	struct oosiop_cb *cb;
146 
147 	mtx_enter(&sc->sc_cb_mtx);
148 	cb = TAILQ_FIRST(&sc->sc_free_cb);
149 	if (cb)
150 		TAILQ_REMOVE(&sc->sc_free_cb, cb, chain);
151 	mtx_leave(&sc->sc_cb_mtx);
152 
153 	return (cb);
154 }
155 
156 void
157 oosiop_cb_free(void *xsc, void *xcb)
158 {
159 	struct oosiop_softc *sc = xsc;
160 	struct oosiop_cb *cb = xcb;
161 
162 	mtx_enter(&sc->sc_cb_mtx);
163 	TAILQ_INSERT_TAIL(&sc->sc_free_cb, cb, chain);
164 	mtx_leave(&sc->sc_cb_mtx);
165 }
166 
167 void
168 oosiop_attach(struct oosiop_softc *sc)
169 {
170 	struct scsibus_attach_args saa;
171 	bus_size_t scrsize;
172 	bus_dma_segment_t seg;
173 	struct oosiop_cb *cb;
174 	int err, i, nseg;
175 
176 	/*
177 	 * Allocate DMA-safe memory for the script and map it.
178 	 */
179 	scrsize = round_page(sizeof(oosiop_script));
180 	err = bus_dmamem_alloc(sc->sc_dmat, scrsize, PAGE_SIZE, 0, &seg, 1,
181 	    &nseg, BUS_DMA_NOWAIT | BUS_DMA_ZERO);
182 	if (err) {
183 		printf(": failed to allocate script memory, err=%d\n", err);
184 		return;
185 	}
186 	err = bus_dmamem_map(sc->sc_dmat, &seg, nseg, scrsize,
187 	    (caddr_t *)&sc->sc_scr, BUS_DMA_NOWAIT | BUS_DMA_COHERENT);
188 	if (err) {
189 		printf(": failed to map script memory, err=%d\n", err);
190 		return;
191 	}
192 	err = bus_dmamap_create(sc->sc_dmat, scrsize, 1, scrsize, 0,
193 	    BUS_DMA_NOWAIT, &sc->sc_scrdma);
194 	if (err) {
195 		printf(": failed to create script map, err=%d\n", err);
196 		return;
197 	}
198 	err = bus_dmamap_load_raw(sc->sc_dmat, sc->sc_scrdma,
199 	    &seg, nseg, scrsize, BUS_DMA_NOWAIT | BUS_DMA_WRITE);
200 	if (err) {
201 		printf(": failed to load script map, err=%d\n", err);
202 		return;
203 	}
204 	sc->sc_scrbase = sc->sc_scrdma->dm_segs[0].ds_addr;
205 
206 	/* Initialize command block array */
207 	TAILQ_INIT(&sc->sc_free_cb);
208 	TAILQ_INIT(&sc->sc_cbq);
209 	if (oosiop_alloc_cb(sc, OOSIOP_NCB) != 0)
210 		return;
211 
212 	/* Use first cb to reselection msgin buffer */
213 	cb = TAILQ_FIRST(&sc->sc_free_cb);
214 	sc->sc_reselbuf = cb->xferdma->dm_segs[0].ds_addr +
215 	    offsetof(struct oosiop_xfer, msgin[0]);
216 
217 	for (i = 0; i < OOSIOP_NTGT; i++) {
218 		sc->sc_tgt[i].nexus = NULL;
219 		sc->sc_tgt[i].flags = 0;
220 	}
221 
222 	/* Setup asynchronous clock divisor parameters */
223 	if (sc->sc_freq <= 25000000) {
224 		sc->sc_ccf = 10;
225 		sc->sc_dcntl = OOSIOP_DCNTL_CF_1;
226 	} else if (sc->sc_freq <= 37500000) {
227 		sc->sc_ccf = 15;
228 		sc->sc_dcntl = OOSIOP_DCNTL_CF_1_5;
229 	} else if (sc->sc_freq <= 50000000) {
230 		sc->sc_ccf = 20;
231 		sc->sc_dcntl = OOSIOP_DCNTL_CF_2;
232 	} else {
233 		sc->sc_ccf = 30;
234 		sc->sc_dcntl = OOSIOP_DCNTL_CF_3;
235 	}
236 
237 	if (sc->sc_chip == OOSIOP_700)
238 		sc->sc_minperiod = oosiop_period(sc, 4, sc->sc_ccf);
239 	else
240 		sc->sc_minperiod = oosiop_period(sc, 4, 10);
241 
242 	if (sc->sc_minperiod < 25)
243 		sc->sc_minperiod = 25;	/* limit to 10MB/s */
244 
245 	mtx_init(&sc->sc_cb_mtx, IPL_BIO);
246 	scsi_iopool_init(&sc->sc_iopool, sc, oosiop_cb_alloc, oosiop_cb_free);
247 
248 	printf(": NCR53C700%s rev %d, %dMHz\n",
249 	    sc->sc_chip == OOSIOP_700_66 ? "-66" : "",
250 	    oosiop_read_1(sc, OOSIOP_CTEST7) >> 4,
251 	    sc->sc_freq / 1000000);
252 	/*
253 	 * Reset all
254 	 */
255 	oosiop_reset(sc);
256 	oosiop_reset_bus(sc);
257 
258 	/*
259 	 * Start SCRIPTS processor
260 	 */
261 	oosiop_load_script(sc);
262 	sc->sc_active = 0;
263 	oosiop_write_4(sc, OOSIOP_DSP, sc->sc_scrbase + Ent_wait_reselect);
264 
265 	/*
266 	 * Fill in the sc_link.
267 	 */
268 	sc->sc_link.adapter = &oosiop_adapter;
269 	sc->sc_link.adapter_softc = sc;
270 	sc->sc_link.openings = 1;	/* XXX */
271 	sc->sc_link.adapter_buswidth = OOSIOP_NTGT;
272 	sc->sc_link.adapter_target = sc->sc_id;
273 	sc->sc_link.pool = &sc->sc_iopool;
274 	sc->sc_link.quirks = ADEV_NODOORLOCK;
275 
276 	bzero(&saa, sizeof(saa));
277 	saa.saa_sc_link = &sc->sc_link;
278 
279 	/*
280 	 * Now try to attach all the sub devices.
281 	 */
282 	config_found(&sc->sc_dev, &saa, scsiprint);
283 }
284 
285 int
286 oosiop_alloc_cb(struct oosiop_softc *sc, int ncb)
287 {
288 	struct oosiop_cb *cb;
289 	struct oosiop_xfer *xfer;
290 	bus_size_t xfersize;
291 	bus_dma_segment_t seg;
292 	int i, s, err, nseg;
293 
294 	/*
295 	 * Allocate oosiop_cb.
296 	 */
297 	cb = malloc(sizeof(*cb) * ncb, M_DEVBUF, M_NOWAIT | M_ZERO);
298 	if (cb == NULL) {
299 		printf(": failed to allocate cb memory\n");
300 		return (ENOMEM);
301 	}
302 
303 	/*
304 	 * Allocate DMA-safe memory for the oosiop_xfer and map it.
305 	 */
306 	xfersize = sizeof(struct oosiop_xfer) * ncb;
307 	err = bus_dmamem_alloc(sc->sc_dmat, xfersize, PAGE_SIZE, 0, &seg, 1,
308 	    &nseg, BUS_DMA_NOWAIT);
309 	if (err) {
310 		printf(": failed to allocate xfer block memory, err=%d\n", err);
311 		return (err);
312 	}
313 	err = bus_dmamem_map(sc->sc_dmat, &seg, nseg, xfersize,
314 	    (caddr_t *)(void *)&xfer, BUS_DMA_NOWAIT | BUS_DMA_COHERENT);
315 	if (err) {
316 		printf(": failed to map xfer block memory, err=%d\n", err);
317 		return (err);
318 	}
319 
320 	/* Initialize each command block */
321 	for (i = 0; i < ncb; i++) {
322 		err = bus_dmamap_create(sc->sc_dmat, PAGE_SIZE, 1, PAGE_SIZE,
323 		    0, BUS_DMA_NOWAIT, &cb->cmddma);
324 		if (err) {
325 			printf(": failed to create cmddma map, err=%d\n", err);
326 			return (err);
327 		}
328 
329 		err = bus_dmamap_create(sc->sc_dmat, OOSIOP_MAX_XFER,
330 		    OOSIOP_NSG, OOSIOP_DBC_MAX, 0, BUS_DMA_NOWAIT,
331 		    &cb->datadma);
332 		if (err) {
333 			printf(": failed to create datadma map, err=%d\n", err);
334 			return (err);
335 		}
336 
337 		err = bus_dmamap_create(sc->sc_dmat,
338 		    sizeof(struct oosiop_xfer), 1, sizeof(struct oosiop_xfer),
339 		    0, BUS_DMA_NOWAIT, &cb->xferdma);
340 		if (err) {
341 			printf(": failed to create xfer block map, err=%d\n",
342 			    err);
343 			return (err);
344 		}
345 		err = bus_dmamap_load(sc->sc_dmat, cb->xferdma, xfer,
346 		    sizeof(struct oosiop_xfer), NULL, BUS_DMA_NOWAIT);
347 		if (err) {
348 			printf(": failed to load xfer block, err=%d\n", err);
349 			return (err);
350 		}
351 
352 		cb->xfer = xfer;
353 
354 		s = splbio();
355 		TAILQ_INSERT_TAIL(&sc->sc_free_cb, cb, chain);
356 		splx(s);
357 
358 		cb++;
359 		xfer++;
360 	}
361 
362 	return (0);
363 }
364 
365 static __inline void
366 oosiop_relocate_io(struct oosiop_softc *sc, bus_addr_t addr)
367 {
368 	u_int32_t dcmd;
369 	int32_t dsps;
370 
371 	dcmd = letoh32(sc->sc_scr[addr / 4 + 0]);
372 	dsps = letoh32(sc->sc_scr[addr / 4 + 1]);
373 
374 	/* convert relative to absolute */
375 	if (dcmd & 0x04000000) {
376 		dcmd &= ~0x04000000;
377 #if 0
378 		/*
379 		 * sign extension isn't needed here because
380 		 * ncr53cxxx.c generates 32 bit dsps.
381 		 */
382 		dsps <<= 8;
383 		dsps >>= 8;
384 #endif
385 		sc->sc_scr[addr / 4 + 0] = htole32(dcmd);
386 		dsps += addr + 8;
387 	}
388 
389 	sc->sc_scr[addr / 4 + 1] = htole32(dsps + sc->sc_scrbase);
390 }
391 
392 static __inline void
393 oosiop_relocate_tc(struct oosiop_softc *sc, bus_addr_t addr)
394 {
395 	u_int32_t dcmd;
396 	int32_t dsps;
397 
398 	dcmd = letoh32(sc->sc_scr[addr / 4 + 0]);
399 	dsps = letoh32(sc->sc_scr[addr / 4 + 1]);
400 
401 	/* convert relative to absolute */
402 	if (dcmd & 0x00800000) {
403 		dcmd &= ~0x00800000;
404 		sc->sc_scr[addr / 4] = htole32(dcmd);
405 #if 0
406 		/*
407 		 * sign extension isn't needed here because
408 		 * ncr53cxxx.c generates 32 bit dsps.
409 		 */
410 		dsps <<= 8;
411 		dsps >>= 8;
412 #endif
413 		dsps += addr + 8;
414 	}
415 
416 	sc->sc_scr[addr / 4 + 1] = htole32(dsps + sc->sc_scrbase);
417 }
418 
419 static __inline void
420 oosiop_fixup_select(struct oosiop_softc *sc, bus_addr_t addr, int id)
421 {
422 	u_int32_t dcmd;
423 
424 	dcmd = letoh32(sc->sc_scr[addr / 4]);
425 	dcmd &= 0xff00ffff;
426 	dcmd |= 0x00010000 << id;
427 	sc->sc_scr[addr / 4] = htole32(dcmd);
428 }
429 
430 static __inline void
431 oosiop_fixup_jump(struct oosiop_softc *sc, bus_addr_t addr, bus_addr_t dst)
432 {
433 
434 	sc->sc_scr[addr / 4 + 1] = htole32(dst);
435 }
436 
437 static __inline void
438 oosiop_fixup_move(struct oosiop_softc *sc, bus_addr_t addr, bus_size_t dbc,
439     bus_addr_t dsps)
440 {
441 	u_int32_t dcmd;
442 
443 	dcmd = letoh32(sc->sc_scr[addr / 4]);
444 	dcmd &= 0xff000000;
445 	dcmd |= dbc & 0x00ffffff;
446 	sc->sc_scr[addr / 4 + 0] = htole32(dcmd);
447 	sc->sc_scr[addr / 4 + 1] = htole32(dsps);
448 }
449 
450 void
451 oosiop_load_script(struct oosiop_softc *sc)
452 {
453 	int i;
454 
455 	/* load script */
456 	for (i = 0; i < sizeof(oosiop_script) / sizeof(oosiop_script[0]); i++)
457 		sc->sc_scr[i] = htole32(oosiop_script[i]);
458 
459 	/* relocate script */
460 	for (i = 0; i < (sizeof(oosiop_script) / 8); i++) {
461 		switch (oosiop_script[i * 2] >> 27) {
462 		case 0x08:	/* select */
463 		case 0x0a:	/* wait reselect */
464 			oosiop_relocate_io(sc, i * 8);
465 			break;
466 		case 0x10:	/* jump */
467 		case 0x11:	/* call */
468 			oosiop_relocate_tc(sc, i * 8);
469 			break;
470 		}
471 	}
472 
473 	oosiop_fixup_move(sc, Ent_p_resel_msgin_move, 1, sc->sc_reselbuf);
474 	OOSIOP_SCRIPT_SYNC(sc, BUS_DMASYNC_PREWRITE);
475 }
476 
477 void
478 oosiop_setup_sgdma(struct oosiop_softc *sc, struct oosiop_cb *cb)
479 {
480 	struct oosiop_xfer *xfer = cb->xfer;
481 	struct scsi_xfer *xs = cb->xs;
482 	int i, n, off;
483 
484 	OOSIOP_XFERSCR_SYNC(sc, cb,
485 	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
486 
487 	off = cb->curdp;
488 
489 	if (xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) {
490 		/* Find start segment */
491 		for (i = 0; i < cb->datadma->dm_nsegs; i++) {
492 			if (off < cb->datadma->dm_segs[i].ds_len)
493 				break;
494 			off -= cb->datadma->dm_segs[i].ds_len;
495 		}
496 
497 		/* build MOVE block */
498 		if (xs->flags & SCSI_DATA_IN) {
499 			n = 0;
500 			while (i < cb->datadma->dm_nsegs) {
501 				xfer->datain_scr[n * 2 + 0] =
502 				    htole32(0x09000000 |
503 				    (cb->datadma->dm_segs[i].ds_len - off));
504 				xfer->datain_scr[n * 2 + 1] =
505 				    htole32(cb->datadma->dm_segs[i].ds_addr +
506 				    off);
507 				n++;
508 				i++;
509 				off = 0;
510 			}
511 			xfer->datain_scr[n * 2 + 0] = htole32(0x80080000);
512 			xfer->datain_scr[n * 2 + 1] =
513 			    htole32(sc->sc_scrbase + Ent_phasedispatch);
514 		}
515 		if (xs->flags & SCSI_DATA_OUT) {
516 			n = 0;
517 			while (i < cb->datadma->dm_nsegs) {
518 				xfer->dataout_scr[n * 2 + 0] =
519 				    htole32(0x08000000 |
520 				    (cb->datadma->dm_segs[i].ds_len - off));
521 				xfer->dataout_scr[n * 2 + 1] =
522 				    htole32(cb->datadma->dm_segs[i].ds_addr +
523 				    off);
524 				n++;
525 				i++;
526 				off = 0;
527 			}
528 			xfer->dataout_scr[n * 2 + 0] = htole32(0x80080000);
529 			xfer->dataout_scr[n * 2 + 1] =
530 			    htole32(sc->sc_scrbase + Ent_phasedispatch);
531 		}
532 	}
533 	if ((xs->flags & SCSI_DATA_IN) == 0) {
534 		xfer->datain_scr[0] = htole32(0x98080000);
535 		xfer->datain_scr[1] = htole32(DATAIN_TRAP);
536 	}
537 	if ((xs->flags & SCSI_DATA_OUT) == 0) {
538 		xfer->dataout_scr[0] = htole32(0x98080000);
539 		xfer->dataout_scr[1] = htole32(DATAOUT_TRAP);
540 	}
541 	OOSIOP_XFERSCR_SYNC(sc, cb,
542 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
543 }
544 
545 /*
546  * Setup DMA pointer into script.
547  */
548 void
549 oosiop_setup_dma(struct oosiop_softc *sc)
550 {
551 	struct oosiop_cb *cb;
552 	bus_addr_t xferbase;
553 
554 	cb = sc->sc_curcb;
555 	xferbase = cb->xferdma->dm_segs[0].ds_addr;
556 
557 	OOSIOP_SCRIPT_SYNC(sc, BUS_DMASYNC_POSTWRITE);
558 
559 	oosiop_fixup_select(sc, Ent_p_select, cb->id);
560 	oosiop_fixup_jump(sc, Ent_p_datain_jump, xferbase +
561 	    offsetof(struct oosiop_xfer, datain_scr[0]));
562 	oosiop_fixup_jump(sc, Ent_p_dataout_jump, xferbase +
563 	    offsetof(struct oosiop_xfer, dataout_scr[0]));
564 	oosiop_fixup_move(sc, Ent_p_msgin_move, 1, xferbase +
565 	    offsetof(struct oosiop_xfer, msgin[0]));
566 	oosiop_fixup_move(sc, Ent_p_extmsglen_move, 1, xferbase +
567 	    offsetof(struct oosiop_xfer, msgin[1]));
568 	oosiop_fixup_move(sc, Ent_p_msgout_move, cb->msgoutlen, xferbase +
569 	    offsetof(struct oosiop_xfer, msgout[0]));
570 	oosiop_fixup_move(sc, Ent_p_status_move, 1, xferbase +
571 	    offsetof(struct oosiop_xfer, status));
572 	oosiop_fixup_move(sc, Ent_p_cmdout_move, cb->cmdlen,
573 	    cb->cmddma->dm_segs[0].ds_addr);
574 
575 	OOSIOP_SCRIPT_SYNC(sc, BUS_DMASYNC_PREWRITE);
576 }
577 
578 void
579 oosiop_flush_fifo(struct oosiop_softc *sc)
580 {
581 
582 	oosiop_write_1(sc, OOSIOP_DFIFO, oosiop_read_1(sc, OOSIOP_DFIFO) |
583 	    OOSIOP_DFIFO_FLF);
584 	while ((oosiop_read_1(sc, OOSIOP_CTEST1) & OOSIOP_CTEST1_FMT) !=
585 	    OOSIOP_CTEST1_FMT)
586 		;
587 	oosiop_write_1(sc, OOSIOP_DFIFO, oosiop_read_1(sc, OOSIOP_DFIFO) &
588 	    ~OOSIOP_DFIFO_FLF);
589 }
590 
591 void
592 oosiop_clear_fifo(struct oosiop_softc *sc)
593 {
594 
595 	oosiop_write_1(sc, OOSIOP_DFIFO, oosiop_read_1(sc, OOSIOP_DFIFO) |
596 	    OOSIOP_DFIFO_CLF);
597 	while ((oosiop_read_1(sc, OOSIOP_CTEST1) & OOSIOP_CTEST1_FMT) !=
598 	    OOSIOP_CTEST1_FMT)
599 		;
600 	oosiop_write_1(sc, OOSIOP_DFIFO, oosiop_read_1(sc, OOSIOP_DFIFO) &
601 	    ~OOSIOP_DFIFO_CLF);
602 }
603 
604 void
605 oosiop_phasemismatch(struct oosiop_softc *sc)
606 {
607 	struct oosiop_cb *cb;
608 	u_int32_t dsp, dbc, n, i, len;
609 	u_int8_t dfifo, sstat1;
610 
611 	cb = sc->sc_curcb;
612 	if (cb == NULL)
613 		return;
614 
615 	dsp = oosiop_read_4(sc, OOSIOP_DSP);
616 	dbc = oosiop_read_4(sc, OOSIOP_DBC) & OOSIOP_DBC_MAX;
617 	len = 0;
618 
619 	n = dsp - cb->xferdma->dm_segs[0].ds_addr - 8;
620 	if (n >= offsetof(struct oosiop_xfer, datain_scr[0]) &&
621 	    n < offsetof(struct oosiop_xfer, datain_scr[OOSIOP_NSG * 2])) {
622 		n -= offsetof(struct oosiop_xfer, datain_scr[0]);
623 		n >>= 3;
624 		OOSIOP_DINSCR_SYNC(sc, cb,
625 		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
626 		for (i = 0; i <= n; i++)
627 			len += letoh32(cb->xfer->datain_scr[i * 2]) &
628 			    0x00ffffff;
629 		OOSIOP_DINSCR_SYNC(sc, cb,
630 		    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
631 		/* All data in the chip are already flushed */
632 	} else if (n >= offsetof(struct oosiop_xfer, dataout_scr[0]) &&
633 	    n < offsetof(struct oosiop_xfer, dataout_scr[OOSIOP_NSG * 2])) {
634 		n -= offsetof(struct oosiop_xfer, dataout_scr[0]);
635 		n >>= 3;
636 		OOSIOP_DOUTSCR_SYNC(sc, cb,
637 		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
638 		for (i = 0; i <= n; i++)
639 			len += letoh32(cb->xfer->dataout_scr[i * 2]) &
640 			    0x00ffffff;
641 		OOSIOP_DOUTSCR_SYNC(sc, cb,
642 		    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
643 
644 		dfifo = oosiop_read_1(sc, OOSIOP_DFIFO);
645 		dbc += ((dfifo & OOSIOP_DFIFO_BO) - (dbc & OOSIOP_DFIFO_BO)) &
646 		    OOSIOP_DFIFO_BO;
647 
648 		sstat1 = oosiop_read_1(sc, OOSIOP_SSTAT1);
649 		if (sstat1 & OOSIOP_SSTAT1_OLF)
650 			dbc++;
651 		if ((sc->sc_tgt[cb->id].sxfer != 0) &&
652 		    (sstat1 & OOSIOP_SSTAT1_ORF) != 0)
653 			dbc++;
654 
655 		oosiop_clear_fifo(sc);
656 	} else {
657 		printf("%s: phase mismatch addr=%08x\n", sc->sc_dev.dv_xname,
658 		    oosiop_read_4(sc, OOSIOP_DSP) - 8);
659 		oosiop_clear_fifo(sc);
660 		return;
661 	}
662 
663 	len -= dbc;
664 	if (len) {
665 		cb->curdp += len;
666 		oosiop_setup_sgdma(sc, cb);
667 	}
668 }
669 
670 void
671 oosiop_setup_syncxfer(struct oosiop_softc *sc)
672 {
673 	int id;
674 
675 	id = sc->sc_curcb->id;
676 	if (sc->sc_chip != OOSIOP_700)
677 		oosiop_write_1(sc, OOSIOP_SBCL, sc->sc_tgt[id].scf);
678 
679 	oosiop_write_1(sc, OOSIOP_SXFER, sc->sc_tgt[id].sxfer);
680 }
681 
682 void
683 oosiop_set_syncparam(struct oosiop_softc *sc, int id, int period, int offset)
684 {
685 	int i, p;
686 
687 	printf("%s: target %d now using 8 bit ", sc->sc_dev.dv_xname, id);
688 
689 	if (offset == 0) {
690 		/* Asynchronous */
691 		sc->sc_tgt[id].scf = 0;
692 		sc->sc_tgt[id].sxfer = 0;
693 		printf("asynchronous");
694 	} else {
695 		/* Synchronous */
696 		if (sc->sc_chip == OOSIOP_700) {
697 			for (i = 4; i < 12; i++) {
698 				p = oosiop_period(sc, i, sc->sc_ccf);
699 				if (p >= period)
700 					break;
701 			}
702 			if (i == 12) {
703 				printf("%s: target %d period too large\n",
704 				    sc->sc_dev.dv_xname, id);
705 				i = 11;	/* XXX */
706 			}
707 			sc->sc_tgt[id].scf = 0;
708 			sc->sc_tgt[id].sxfer = ((i - 4) << 4) | offset;
709 		} else {
710 			for (i = 0; i < NSYNCTBL; i++) {
711 				p = oosiop_period(sc, synctbl[i].tp + 4,
712 				    (synctbl[i].scf + 1) * 5);
713 				if (p >= period)
714 					break;
715 			}
716 			if (i == NSYNCTBL) {
717 				printf("%s: target %d period too large\n",
718 				    sc->sc_dev.dv_xname, id);
719 				i = NSYNCTBL - 1;	/* XXX */
720 			}
721 			sc->sc_tgt[id].scf = synctbl[i].scf;
722 			sc->sc_tgt[id].sxfer = (synctbl[i].tp << 4) | offset;
723 		}
724 		/* XXX print actual ns period... */
725 		printf(" synchronous");
726 	}
727 	printf(" xfers\n");
728 }
729 
730 void
731 oosiop_minphys(struct buf *bp, struct scsi_link *sl)
732 {
733 
734 	if (bp->b_bcount > OOSIOP_MAX_XFER)
735 		bp->b_bcount = OOSIOP_MAX_XFER;
736 	minphys(bp);
737 }
738 
739 void
740 oosiop_scsicmd(struct scsi_xfer *xs)
741 {
742 	struct oosiop_softc *sc;
743 	struct oosiop_cb *cb;
744 	struct oosiop_xfer *xfer;
745 	int s, err;
746 	int dopoll;
747 
748 	sc = (struct oosiop_softc *)xs->sc_link->adapter_softc;
749 
750 	s = splbio();
751 
752 	cb = xs->io;
753 
754 	cb->xs = xs;
755 	cb->xsflags = xs->flags;
756 	cb->cmdlen = xs->cmdlen;
757 	cb->datalen = 0;
758 	cb->flags = 0;
759 	cb->id = xs->sc_link->target;
760 	cb->lun = xs->sc_link->lun;
761 	xfer = cb->xfer;
762 
763 	/* Setup SCSI command buffer DMA */
764 	err = bus_dmamap_load(sc->sc_dmat, cb->cmddma, xs->cmd,
765 	    xs->cmdlen, NULL, ((xs->flags & SCSI_NOSLEEP) ?
766 	    BUS_DMA_NOWAIT : BUS_DMA_WAITOK) |
767 	    BUS_DMA_STREAMING | BUS_DMA_WRITE);
768 	if (err) {
769 		printf("%s: unable to load cmd DMA map: %d",
770 		    sc->sc_dev.dv_xname, err);
771 		splx(s);
772 		xs->error = XS_DRIVER_STUFFUP;
773 		scsi_done(xs);
774 		return;
775 	}
776 	bus_dmamap_sync(sc->sc_dmat, cb->cmddma, 0, xs->cmdlen,
777 	    BUS_DMASYNC_PREWRITE);
778 
779 	/* Setup data buffer DMA */
780 	if (xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) {
781 		cb->datalen = xs->datalen;
782 		err = bus_dmamap_load(sc->sc_dmat, cb->datadma,
783 		    xs->data, xs->datalen, NULL,
784 		    ((xs->flags & SCSI_NOSLEEP) ?
785 		    BUS_DMA_NOWAIT : BUS_DMA_WAITOK) |
786 		    BUS_DMA_STREAMING |
787 		    ((xs->flags & SCSI_DATA_IN) ? BUS_DMA_READ :
788 		    BUS_DMA_WRITE));
789 		if (err) {
790 			printf("%s: unable to load data DMA map: %d",
791 			    sc->sc_dev.dv_xname, err);
792 			bus_dmamap_unload(sc->sc_dmat, cb->cmddma);
793 			splx(s);
794 			xs->error = XS_DRIVER_STUFFUP;
795 			scsi_done(xs);
796 			return;
797 		}
798 		bus_dmamap_sync(sc->sc_dmat, cb->datadma,
799 		    0, xs->datalen,
800 		    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
801 	}
802 
803 	xfer->status = SCSI_OOSIOP_NOSTATUS;
804 
805 	/*
806 	 * Always initialize timeout so it does not contain trash
807 	 * that could confuse timeout_del().
808 	 */
809 	timeout_set(&xs->stimeout, oosiop_timeout, cb);
810 
811 	if (xs->flags & SCSI_POLL)
812 		dopoll = 1;
813 	else {
814 		dopoll = 0;
815 		/* start expire timer */
816 		timeout_add_msec(&xs->stimeout, xs->timeout);
817 	}
818 
819 	splx(s);
820 
821 	oosiop_setup(sc, cb);
822 
823 	TAILQ_INSERT_TAIL(&sc->sc_cbq, cb, chain);
824 
825 	if (!sc->sc_active) {
826 		/* Abort script to start selection */
827 		oosiop_write_1(sc, OOSIOP_ISTAT, OOSIOP_ISTAT_ABRT);
828 	}
829 	if (dopoll)
830 		oosiop_poll(sc, cb);
831 }
832 
833 void
834 oosiop_poll(struct oosiop_softc *sc, struct oosiop_cb *cb)
835 {
836 	struct scsi_xfer *xs = cb->xs;
837 	int i, s, to;
838 	u_int8_t istat;
839 
840 	s = splbio();
841 	to = xs->timeout / 1000;
842 	for (;;) {
843 		i = 1000;
844 		while (((istat = oosiop_read_1(sc, OOSIOP_ISTAT)) &
845 		    (OOSIOP_ISTAT_SIP | OOSIOP_ISTAT_DIP)) == 0) {
846 			if (i <= 0) {
847 				i = 1000;
848 				to--;
849 				if (to <= 0) {
850 					oosiop_reset(sc);
851 					splx(s);
852 					return;
853 				}
854 			}
855 			delay(1000);
856 			i--;
857 		}
858 		oosiop_processintr(sc, istat);
859 
860 		if (xs->flags & ITSDONE)
861 			break;
862 	}
863 
864 	splx(s);
865 }
866 
867 void
868 oosiop_setup(struct oosiop_softc *sc, struct oosiop_cb *cb)
869 {
870 	struct oosiop_xfer *xfer = cb->xfer;
871 
872 	cb->curdp = 0;
873 	cb->savedp = 0;
874 
875 	oosiop_setup_sgdma(sc, cb);
876 
877 	/* Setup msgout buffer */
878 	OOSIOP_XFERMSG_SYNC(sc, cb,
879 	   BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
880 	xfer->msgout[0] = MSG_IDENTIFY(cb->lun,
881 	    (cb->xs->cmd->opcode != REQUEST_SENSE));
882 	cb->msgoutlen = 1;
883 
884 	if (sc->sc_tgt[cb->id].flags & TGTF_SYNCNEG) {
885 		/* Send SDTR */
886 		xfer->msgout[1] = MSG_EXTENDED;
887 		xfer->msgout[2] = MSG_EXT_SDTR_LEN;
888 		xfer->msgout[3] = MSG_EXT_SDTR;
889 		xfer->msgout[4] = sc->sc_minperiod;
890 		xfer->msgout[5] = OOSIOP_MAX_OFFSET;
891 		cb->msgoutlen = 6;
892 		sc->sc_tgt[cb->id].flags &= ~TGTF_SYNCNEG;
893 		sc->sc_tgt[cb->id].flags |= TGTF_WAITSDTR;
894 	}
895 
896 	OOSIOP_XFERMSG_SYNC(sc, cb,
897 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
898 }
899 
900 void
901 oosiop_done(struct oosiop_softc *sc, struct oosiop_cb *cb)
902 {
903 	struct scsi_xfer *xs;
904 	struct scsi_link *periph;
905 	int autosense;
906 
907 	xs = cb->xs;
908 	periph = xs->sc_link;
909 
910 	/*
911 	 * Record if this is the completion of an auto sense
912 	 * scsi command, and then reset the flag so we don't loop
913 	 * when such a command fails or times out.
914 	 */
915 	autosense = cb->flags & CBF_AUTOSENSE;
916 	cb->flags &= ~CBF_AUTOSENSE;
917 
918 	bus_dmamap_sync(sc->sc_dmat, cb->cmddma, 0, cb->cmdlen,
919 	    BUS_DMASYNC_POSTWRITE);
920 	bus_dmamap_unload(sc->sc_dmat, cb->cmddma);
921 
922 	if (cb->xsflags & (SCSI_DATA_IN | SCSI_DATA_OUT)) {
923 		bus_dmamap_sync(sc->sc_dmat, cb->datadma, 0, cb->datalen,
924 		    (cb->xsflags & SCSI_DATA_IN) ?
925 		    BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
926 		bus_dmamap_unload(sc->sc_dmat, cb->datadma);
927 	}
928 
929 	timeout_del(&xs->stimeout);
930 
931 	xs->status = cb->xfer->status;
932 
933 	if (cb->flags & CBF_SELTOUT)
934 		xs->error = XS_SELTIMEOUT;
935 	else if (cb->flags & CBF_TIMEOUT)
936 		xs->error = XS_TIMEOUT;
937 	else switch (xs->status) {
938 	case SCSI_OK:
939 		if (autosense == 0)
940 			xs->error = XS_NOERROR;
941 		else
942 			xs->error = XS_SENSE;
943 		break;
944 
945 	case SCSI_BUSY:
946 		xs->error = XS_BUSY;
947 		break;
948 	case SCSI_CHECK:
949 #ifdef notyet
950 		if (autosense == 0)
951 			cb->flags |= CBF_AUTOSENSE;
952 		else
953 #endif
954 			xs->error = XS_DRIVER_STUFFUP;
955 		break;
956 	case SCSI_OOSIOP_NOSTATUS:
957 		/* the status byte was not updated, cmd was aborted. */
958 		xs->error = XS_SELTIMEOUT;
959 		break;
960 
961 	default:
962 		xs->error = XS_RESET;
963 		break;
964 	}
965 
966 	if ((cb->flags & CBF_AUTOSENSE) == 0) {
967 		/* Put it on the free list. */
968 FREE:
969 		xs->resid = 0;
970 		scsi_done(xs);
971 
972 		if (cb == sc->sc_curcb)
973 			sc->sc_curcb = NULL;
974 		if (cb == sc->sc_lastcb)
975 			sc->sc_lastcb = NULL;
976 		sc->sc_tgt[cb->id].nexus = NULL;
977 	} else {
978 		/* Set up REQUEST_SENSE command */
979 		struct scsi_sense *cmd = (struct scsi_sense *)xs->cmd;
980 		int err;
981 
982 		bzero(cmd, sizeof(*cmd));
983 		cmd->opcode = REQUEST_SENSE;
984 		cmd->byte2 = xs->sc_link->lun << 5;
985 		cb->cmdlen = cmd->length = sizeof(xs->sense);
986 
987 		cb->xsflags &= SCSI_POLL | SCSI_NOSLEEP;
988 		cb->xsflags |= SCSI_DATA_IN;
989 		cb->datalen = sizeof xs->sense;
990 
991 		/* Setup SCSI command buffer DMA */
992 		err = bus_dmamap_load(sc->sc_dmat, cb->cmddma, cmd,
993 		    cb->cmdlen, NULL,
994 		    BUS_DMA_NOWAIT | BUS_DMA_STREAMING | BUS_DMA_WRITE);
995 		if (err) {
996 			printf("%s: unable to load REQUEST_SENSE cmd DMA map: %d",
997 			    sc->sc_dev.dv_xname, err);
998 			xs->error = XS_DRIVER_STUFFUP;
999 			goto FREE;
1000 		}
1001 		bus_dmamap_sync(sc->sc_dmat, cb->cmddma, 0, cb->cmdlen,
1002 		    BUS_DMASYNC_PREWRITE);
1003 
1004 		/* Setup data buffer DMA */
1005 		err = bus_dmamap_load(sc->sc_dmat, cb->datadma,
1006 		    &xs->sense, sizeof(xs->sense), NULL,
1007 		    BUS_DMA_NOWAIT | BUS_DMA_STREAMING | BUS_DMA_READ);
1008 		if (err) {
1009 			printf("%s: unable to load REQUEST_SENSE data DMA map: %d",
1010 			    sc->sc_dev.dv_xname, err);
1011 			xs->error = XS_DRIVER_STUFFUP;
1012 			bus_dmamap_unload(sc->sc_dmat, cb->cmddma);
1013 			goto FREE;
1014 		}
1015 		bus_dmamap_sync(sc->sc_dmat, cb->datadma,
1016 		    0, sizeof(xs->sense), BUS_DMASYNC_PREREAD);
1017 
1018 		oosiop_setup(sc, cb);
1019 
1020 		TAILQ_INSERT_HEAD(&sc->sc_cbq, cb, chain);
1021 		if ((cb->xs->flags & SCSI_POLL) == 0) {
1022 			/* start expire timer */
1023 			timeout_add_msec(&xs->stimeout, xs->timeout);
1024 		}
1025 	}
1026 }
1027 
1028 void
1029 oosiop_timeout(void *arg)
1030 {
1031 	struct oosiop_cb *cb = arg;
1032 	struct scsi_xfer *xs = cb->xs;
1033 	struct oosiop_softc *sc = xs->sc_link->adapter_softc;
1034 	int s;
1035 
1036 	sc_print_addr(xs->sc_link);
1037 	printf("command 0x%02x timeout on xs %p\n", xs->cmd->opcode, xs);
1038 
1039 	s = splbio();
1040 
1041 	oosiop_reset_bus(sc);
1042 
1043 	cb->flags |= CBF_TIMEOUT;
1044 	oosiop_done(sc, cb);
1045 
1046 	splx(s);
1047 }
1048 
1049 void
1050 oosiop_reset(struct oosiop_softc *sc)
1051 {
1052 	int i, s;
1053 
1054 	s = splbio();
1055 
1056 	/* Stop SCRIPTS processor */
1057 	oosiop_write_1(sc, OOSIOP_ISTAT, OOSIOP_ISTAT_ABRT);
1058 	delay(100);
1059 	oosiop_write_1(sc, OOSIOP_ISTAT, 0);
1060 
1061 	/* Reset the chip */
1062 	oosiop_write_1(sc, OOSIOP_DCNTL, sc->sc_dcntl | OOSIOP_DCNTL_RST);
1063 	delay(100);
1064 	oosiop_write_1(sc, OOSIOP_DCNTL, sc->sc_dcntl);
1065 	delay(10000);
1066 
1067 	/* Set up various chip parameters */
1068 	oosiop_write_1(sc, OOSIOP_SCNTL0, OOSIOP_ARB_FULL | sc->sc_scntl0);
1069 	oosiop_write_1(sc, OOSIOP_SCNTL1, OOSIOP_SCNTL1_ESR);
1070 	oosiop_write_1(sc, OOSIOP_DCNTL, sc->sc_dcntl);
1071 	oosiop_write_1(sc, OOSIOP_DMODE, sc->sc_dmode);
1072 	oosiop_write_1(sc, OOSIOP_SCID, OOSIOP_SCID_VALUE(sc->sc_id));
1073 	oosiop_write_1(sc, OOSIOP_DWT, sc->sc_dwt);
1074 	oosiop_write_1(sc, OOSIOP_CTEST7, sc->sc_ctest7);
1075 	oosiop_write_1(sc, OOSIOP_SXFER, 0);
1076 
1077 	/* Clear all interrupts */
1078 	(void)oosiop_read_1(sc, OOSIOP_SSTAT0);
1079 	(void)oosiop_read_1(sc, OOSIOP_SSTAT1);
1080 	(void)oosiop_read_1(sc, OOSIOP_DSTAT);
1081 
1082 	/* Enable interrupts */
1083 	oosiop_write_1(sc, OOSIOP_SIEN,
1084 	    OOSIOP_SIEN_M_A | OOSIOP_SIEN_STO | OOSIOP_SIEN_SGE |
1085 	    OOSIOP_SIEN_UDC | OOSIOP_SIEN_RST | OOSIOP_SIEN_PAR);
1086 	oosiop_write_1(sc, OOSIOP_DIEN,
1087 	    OOSIOP_DIEN_ABRT | OOSIOP_DIEN_SSI | OOSIOP_DIEN_SIR |
1088 	    OOSIOP_DIEN_WTD | OOSIOP_DIEN_IID);
1089 
1090 	/* Set target state to asynchronous */
1091 	for (i = 0; i < OOSIOP_NTGT; i++) {
1092 		sc->sc_tgt[i].flags = 0;
1093 		sc->sc_tgt[i].scf = 0;
1094 		sc->sc_tgt[i].sxfer = 0;
1095 	}
1096 
1097 	splx(s);
1098 }
1099 
1100 void
1101 oosiop_reset_bus(struct oosiop_softc *sc)
1102 {
1103 	int s, i;
1104 
1105 	s = splbio();
1106 
1107 	/* Assert SCSI RST */
1108 	oosiop_write_1(sc, OOSIOP_SCNTL1, OOSIOP_SCNTL1_RST);
1109 	delay(25);	/* Reset hold time (25us) */
1110 	oosiop_write_1(sc, OOSIOP_SCNTL1, 0);
1111 
1112 	/* Remove all nexuses */
1113 	for (i = 0; i < OOSIOP_NTGT; i++) {
1114 		if (sc->sc_tgt[i].nexus) {
1115 			sc->sc_tgt[i].nexus->xfer->status =
1116 			    SCSI_OOSIOP_NOSTATUS; /* XXX */
1117 			oosiop_done(sc, sc->sc_tgt[i].nexus);
1118 		}
1119 	}
1120 
1121 	sc->sc_curcb = NULL;
1122 
1123 	delay(250000);	/* Reset to selection (250ms) */
1124 
1125 	splx(s);
1126 }
1127 
1128 /*
1129  * interrupt handler
1130  */
1131 int
1132 oosiop_intr(struct oosiop_softc *sc)
1133 {
1134 	u_int8_t istat;
1135 
1136 	istat = oosiop_read_1(sc, OOSIOP_ISTAT);
1137 
1138 	if ((istat & (OOSIOP_ISTAT_SIP | OOSIOP_ISTAT_DIP)) == 0)
1139 		return (0);
1140 
1141 	oosiop_processintr(sc, istat);
1142 	return (1);
1143 }
1144 
1145 void
1146 oosiop_processintr(struct oosiop_softc *sc, u_int8_t istat)
1147 {
1148 	struct oosiop_cb *cb;
1149 	u_int32_t dcmd;
1150 	u_int8_t dstat, sstat0;
1151 
1152 	sc->sc_nextdsp = Ent_wait_reselect;
1153 
1154 	/* DMA interrupts */
1155 	if (istat & OOSIOP_ISTAT_DIP) {
1156 		oosiop_write_1(sc, OOSIOP_ISTAT, 0);
1157 
1158 		dstat = oosiop_read_1(sc, OOSIOP_DSTAT);
1159 
1160 		if (dstat & OOSIOP_DSTAT_ABRT) {
1161 			sc->sc_nextdsp = oosiop_read_4(sc, OOSIOP_DSP) -
1162 			    sc->sc_scrbase - 8;
1163 
1164 			if (sc->sc_nextdsp == Ent_p_resel_msgin_move &&
1165 			    (oosiop_read_1(sc, OOSIOP_SBCL) & OOSIOP_ACK)) {
1166 				if ((dstat & OOSIOP_DSTAT_DFE) == 0)
1167 					oosiop_flush_fifo(sc);
1168 				sc->sc_nextdsp += 8;
1169 			}
1170 		}
1171 
1172 		if (dstat & OOSIOP_DSTAT_SSI) {
1173 			sc->sc_nextdsp = oosiop_read_4(sc, OOSIOP_DSP) -
1174 			    sc->sc_scrbase;
1175 			printf("%s: single step %08x\n", sc->sc_dev.dv_xname,
1176 			    sc->sc_nextdsp);
1177 		}
1178 
1179 		if (dstat & OOSIOP_DSTAT_SIR) {
1180 			if ((dstat & OOSIOP_DSTAT_DFE) == 0)
1181 				oosiop_flush_fifo(sc);
1182 			oosiop_scriptintr(sc);
1183 		}
1184 
1185 		if (dstat & OOSIOP_DSTAT_WTD) {
1186 			printf("%s: DMA time out\n", sc->sc_dev.dv_xname);
1187 			oosiop_reset(sc);
1188 		}
1189 
1190 		if (dstat & OOSIOP_DSTAT_IID) {
1191 			dcmd = oosiop_read_4(sc, OOSIOP_DBC);
1192 			if ((dcmd & 0xf8000000) == 0x48000000) {
1193 				printf("%s: REQ asserted on WAIT DISCONNECT\n",
1194 				    sc->sc_dev.dv_xname);
1195 				sc->sc_nextdsp = Ent_phasedispatch; /* XXX */
1196 			} else {
1197 				printf("%s: invalid SCRIPTS instruction "
1198 				    "addr=%08x dcmd=%08x dsps=%08x\n",
1199 				    sc->sc_dev.dv_xname,
1200 				    oosiop_read_4(sc, OOSIOP_DSP) - 8, dcmd,
1201 				    oosiop_read_4(sc, OOSIOP_DSPS));
1202 				oosiop_reset(sc);
1203 				OOSIOP_SCRIPT_SYNC(sc, BUS_DMASYNC_POSTWRITE);
1204 				oosiop_load_script(sc);
1205 			}
1206 		}
1207 
1208 		if ((dstat & OOSIOP_DSTAT_DFE) == 0)
1209 			oosiop_clear_fifo(sc);
1210 	}
1211 
1212 	/* SCSI interrupts */
1213 	if (istat & OOSIOP_ISTAT_SIP) {
1214 		if (istat & OOSIOP_ISTAT_DIP)
1215 			delay(1);
1216 		sstat0 = oosiop_read_1(sc, OOSIOP_SSTAT0);
1217 
1218 		if (sstat0 & OOSIOP_SSTAT0_M_A) {
1219 			/* SCSI phase mismatch during MOVE operation */
1220 			oosiop_phasemismatch(sc);
1221 			sc->sc_nextdsp = Ent_phasedispatch;
1222 		}
1223 
1224 		if (sstat0 & OOSIOP_SSTAT0_STO) {
1225 			if (sc->sc_curcb) {
1226 				sc->sc_curcb->flags |= CBF_SELTOUT;
1227 				oosiop_done(sc, sc->sc_curcb);
1228 			}
1229 		}
1230 
1231 		if (sstat0 & OOSIOP_SSTAT0_SGE) {
1232 			printf("%s: SCSI gross error\n", sc->sc_dev.dv_xname);
1233 			oosiop_reset(sc);
1234 		}
1235 
1236 		if (sstat0 & OOSIOP_SSTAT0_UDC) {
1237 			/* XXX */
1238 			if (sc->sc_curcb) {
1239 				printf("%s: unexpected disconnect\n",
1240 				    sc->sc_dev.dv_xname);
1241 				oosiop_done(sc, sc->sc_curcb);
1242 			}
1243 		}
1244 
1245 		if (sstat0 & OOSIOP_SSTAT0_RST)
1246 			oosiop_reset(sc);
1247 
1248 		if (sstat0 & OOSIOP_SSTAT0_PAR)
1249 			printf("%s: parity error\n", sc->sc_dev.dv_xname);
1250 	}
1251 
1252 	/* Start next command if available */
1253 	if (sc->sc_nextdsp == Ent_wait_reselect && TAILQ_FIRST(&sc->sc_cbq)) {
1254 		cb = sc->sc_curcb = TAILQ_FIRST(&sc->sc_cbq);
1255 		TAILQ_REMOVE(&sc->sc_cbq, cb, chain);
1256 		sc->sc_tgt[cb->id].nexus = cb;
1257 
1258 		oosiop_setup_dma(sc);
1259 		oosiop_setup_syncxfer(sc);
1260 		sc->sc_lastcb = cb;
1261 		sc->sc_nextdsp = Ent_start_select;
1262 
1263 		/* Schedule timeout */
1264 		if ((cb->xs->flags & SCSI_POLL) == 0) {
1265 			/* start expire timer */
1266 			timeout_add_msec(&cb->xs->stimeout, cb->xs->timeout);
1267 		}
1268 	}
1269 
1270 	sc->sc_active = (sc->sc_nextdsp != Ent_wait_reselect);
1271 
1272 	/* Restart script */
1273 	oosiop_write_4(sc, OOSIOP_DSP, sc->sc_nextdsp + sc->sc_scrbase);
1274 }
1275 
1276 void
1277 oosiop_scriptintr(struct oosiop_softc *sc)
1278 {
1279 	struct oosiop_cb *cb;
1280 	u_int32_t icode;
1281 	u_int32_t dsp;
1282 	int i;
1283 	u_int8_t sfbr, resid, resmsg;
1284 
1285 	cb = sc->sc_curcb;
1286 	icode = oosiop_read_4(sc, OOSIOP_DSPS);
1287 
1288 	switch (icode) {
1289 	case A_int_done:
1290 		if (cb)
1291 			oosiop_done(sc, cb);
1292 		break;
1293 
1294 	case A_int_msgin:
1295 		if (cb)
1296 			oosiop_msgin(sc, cb);
1297 		break;
1298 
1299 	case A_int_extmsg:
1300 		/* extended message in DMA setup request */
1301 		sfbr = oosiop_read_1(sc, OOSIOP_SFBR);
1302 		OOSIOP_SCRIPT_SYNC(sc, BUS_DMASYNC_POSTWRITE);
1303 		oosiop_fixup_move(sc, Ent_p_extmsgin_move, sfbr,
1304 		    cb->xferdma->dm_segs[0].ds_addr +
1305 		    offsetof(struct oosiop_xfer, msgin[2]));
1306 		OOSIOP_SCRIPT_SYNC(sc, BUS_DMASYNC_PREWRITE);
1307 		sc->sc_nextdsp = Ent_rcv_extmsg;
1308 		break;
1309 
1310 	case A_int_resel:
1311 		/* reselected */
1312 		resid = oosiop_read_1(sc, OOSIOP_SFBR);
1313 		for (i = 0; i < OOSIOP_NTGT; i++)
1314 			if (resid & (1 << i))
1315 				break;
1316 		if (i == OOSIOP_NTGT) {
1317 			printf("%s: missing reselection target id\n",
1318 			    sc->sc_dev.dv_xname);
1319 			break;
1320 		}
1321 		sc->sc_resid = i;
1322 		sc->sc_nextdsp = Ent_wait_resel_identify;
1323 
1324 		if (cb) {
1325 			/* Current command was lost arbitration */
1326 			sc->sc_tgt[cb->id].nexus = NULL;
1327 			TAILQ_INSERT_HEAD(&sc->sc_cbq, cb, chain);
1328 			sc->sc_curcb = NULL;
1329 		}
1330 
1331 		break;
1332 
1333 	case A_int_res_id:
1334 		cb = sc->sc_tgt[sc->sc_resid].nexus;
1335 		resmsg = oosiop_read_1(sc, OOSIOP_SFBR);
1336 		if (MSG_ISIDENTIFY(resmsg) && cb &&
1337 		    (resmsg & MSG_IDENTIFY_LUNMASK) == cb->lun) {
1338 			sc->sc_curcb = cb;
1339 			if (cb != sc->sc_lastcb) {
1340 				oosiop_setup_dma(sc);
1341 				oosiop_setup_syncxfer(sc);
1342 				sc->sc_lastcb = cb;
1343 			}
1344 			if (cb->curdp != cb->savedp) {
1345 				cb->curdp = cb->savedp;
1346 				oosiop_setup_sgdma(sc, cb);
1347 			}
1348 			sc->sc_nextdsp = Ent_ack_msgin;
1349 		} else {
1350 			/* Reselection from invalid target */
1351 			oosiop_reset_bus(sc);
1352 		}
1353 		break;
1354 
1355 	case A_int_resfail:
1356 		/* reselect failed */
1357 		break;
1358 
1359 	case A_int_disc:
1360 		/* disconnected */
1361 		sc->sc_curcb = NULL;
1362 		break;
1363 
1364 	case A_int_err:
1365 		/* generic error */
1366 		dsp = oosiop_read_4(sc, OOSIOP_DSP);
1367 		printf("%s: script error at 0x%08x\n", sc->sc_dev.dv_xname,
1368 		    dsp - 8);
1369 		sc->sc_curcb = NULL;
1370 		break;
1371 
1372 	case DATAIN_TRAP:
1373 		printf("%s: unexpected datain\n", sc->sc_dev.dv_xname);
1374 		/* XXX: need to reset? */
1375 		break;
1376 
1377 	case DATAOUT_TRAP:
1378 		printf("%s: unexpected dataout\n", sc->sc_dev.dv_xname);
1379 		/* XXX: need to reset? */
1380 		break;
1381 
1382 	default:
1383 		printf("%s: unknown intr code %08x\n", sc->sc_dev.dv_xname,
1384 		    icode);
1385 		break;
1386 	}
1387 }
1388 
1389 void
1390 oosiop_msgin(struct oosiop_softc *sc, struct oosiop_cb *cb)
1391 {
1392 	struct oosiop_xfer *xfer;
1393 	int msgout;
1394 
1395 	xfer = cb->xfer;
1396 	sc->sc_nextdsp = Ent_ack_msgin;
1397 	msgout = 0;
1398 
1399 	OOSIOP_XFERMSG_SYNC(sc, cb,
1400 	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
1401 
1402 	switch (xfer->msgin[0]) {
1403 	case MSG_EXTENDED:
1404 		switch (xfer->msgin[2]) {
1405 		case MSG_EXT_SDTR:
1406 			if (sc->sc_tgt[cb->id].flags & TGTF_WAITSDTR) {
1407 				/* Host initiated SDTR */
1408 				sc->sc_tgt[cb->id].flags &= ~TGTF_WAITSDTR;
1409 			} else {
1410 				/* Target initiated SDTR */
1411 				if (xfer->msgin[3] < sc->sc_minperiod)
1412 					xfer->msgin[3] = sc->sc_minperiod;
1413 				if (xfer->msgin[4] > OOSIOP_MAX_OFFSET)
1414 					xfer->msgin[4] = OOSIOP_MAX_OFFSET;
1415 				xfer->msgout[0] = MSG_EXTENDED;
1416 				xfer->msgout[1] = MSG_EXT_SDTR_LEN;
1417 				xfer->msgout[2] = MSG_EXT_SDTR;
1418 				xfer->msgout[3] = xfer->msgin[3];
1419 				xfer->msgout[4] = xfer->msgin[4];
1420 				cb->msgoutlen = 5;
1421 				msgout = 1;
1422 			}
1423 			oosiop_set_syncparam(sc, cb->id, (int)xfer->msgin[3],
1424 			    (int)xfer->msgin[4]);
1425 			oosiop_setup_syncxfer(sc);
1426 			break;
1427 
1428 		default:
1429 			/* Reject message */
1430 			xfer->msgout[0] = MSG_MESSAGE_REJECT;
1431 			cb->msgoutlen = 1;
1432 			msgout = 1;
1433 			break;
1434 		}
1435 		break;
1436 
1437 	case MSG_SAVEDATAPOINTER:
1438 		cb->savedp = cb->curdp;
1439 		break;
1440 
1441 	case MSG_RESTOREPOINTERS:
1442 		if (cb->curdp != cb->savedp) {
1443 			cb->curdp = cb->savedp;
1444 			oosiop_setup_sgdma(sc, cb);
1445 		}
1446 		break;
1447 
1448 	case MSG_MESSAGE_REJECT:
1449 		if (sc->sc_tgt[cb->id].flags & TGTF_WAITSDTR) {
1450 			/* SDTR rejected */
1451 			sc->sc_tgt[cb->id].flags &= ~TGTF_WAITSDTR;
1452 			oosiop_set_syncparam(sc, cb->id, 0, 0);
1453 			oosiop_setup_syncxfer(sc);
1454 		}
1455 		break;
1456 
1457 	default:
1458 		/* Reject message */
1459 		xfer->msgout[0] = MSG_MESSAGE_REJECT;
1460 		cb->msgoutlen = 1;
1461 		msgout = 1;
1462 	}
1463 
1464 	OOSIOP_XFERMSG_SYNC(sc, cb,
1465 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
1466 
1467 	if (msgout) {
1468 		OOSIOP_SCRIPT_SYNC(sc, BUS_DMASYNC_POSTWRITE);
1469 		oosiop_fixup_move(sc, Ent_p_msgout_move, cb->msgoutlen,
1470 		    cb->xferdma->dm_segs[0].ds_addr +
1471 		    offsetof(struct oosiop_xfer, msgout[0]));
1472 		OOSIOP_SCRIPT_SYNC(sc, BUS_DMASYNC_PREWRITE);
1473 		sc->sc_nextdsp = Ent_sendmsg;
1474 	}
1475 }
1476