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