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