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