xref: /openbsd-src/sys/dev/ic/siop_common.c (revision 8500990981f885cbe5e6a4958549cacc238b5ae6)
1 /*	$OpenBSD: siop_common.c,v 1.18 2003/10/27 03:07:39 mickey Exp $ */
2 /*	$NetBSD: siop_common.c,v 1.31 2002/09/27 15:37:18 provos Exp $	*/
3 
4 /*
5  * Copyright (c) 2000, 2002 Manuel Bouyer.
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. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by Manuel Bouyer.
18  * 4. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  */
33 
34 /* SYM53c7/8xx PCI-SCSI I/O Processors driver */
35 
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/device.h>
39 #include <sys/malloc.h>
40 #include <sys/buf.h>
41 #include <sys/kernel.h>
42 #include <sys/scsiio.h>
43 
44 #include <machine/endian.h>
45 #include <machine/bus.h>
46 
47 #include <scsi/scsi_all.h>
48 #include <scsi/scsi_message.h>
49 #include <scsi/scsiconf.h>
50 
51 #include <dev/ic/siopreg.h>
52 #include <dev/ic/siopvar_common.h>
53 #include <dev/ic/siopvar.h>
54 
55 #undef DEBUG
56 #undef DEBUG_DR
57 #undef DEBUG_NEG
58 
59 int
60 siop_common_attach(sc)
61 	struct siop_common_softc *sc;
62 {
63 	int error, i;
64 	bus_dma_segment_t seg;
65 	int rseg;
66 
67 	/*
68 	 * Allocate DMA-safe memory for the script and map it.
69 	 */
70 	if ((sc->features & SF_CHIP_RAM) == 0) {
71 		error = bus_dmamem_alloc(sc->sc_dmat, PAGE_SIZE,
72 		    PAGE_SIZE, 0, &seg, 1, &rseg, BUS_DMA_NOWAIT);
73 		if (error) {
74 			printf("%s: unable to allocate script DMA memory, "
75 			    "error = %d\n", sc->sc_dev.dv_xname, error);
76 			return error;
77 		}
78 		error = bus_dmamem_map(sc->sc_dmat, &seg, rseg, PAGE_SIZE,
79 		    (caddr_t *)&sc->sc_script,
80 		    BUS_DMA_NOWAIT|BUS_DMA_COHERENT);
81 		if (error) {
82 			printf("%s: unable to map script DMA memory, "
83 			    "error = %d\n", sc->sc_dev.dv_xname, error);
84 			return error;
85 		}
86 		error = bus_dmamap_create(sc->sc_dmat, PAGE_SIZE, 1,
87 		    PAGE_SIZE, 0, BUS_DMA_NOWAIT, &sc->sc_scriptdma);
88 		if (error) {
89 			printf("%s: unable to create script DMA map, "
90 			    "error = %d\n", sc->sc_dev.dv_xname, error);
91 			return error;
92 		}
93 		error = bus_dmamap_load(sc->sc_dmat, sc->sc_scriptdma,
94 		    sc->sc_script, PAGE_SIZE, NULL, BUS_DMA_NOWAIT);
95 		if (error) {
96 			printf("%s: unable to load script DMA map, "
97 			    "error = %d\n", sc->sc_dev.dv_xname, error);
98 			return error;
99 		}
100 		sc->sc_scriptaddr =
101 		    sc->sc_scriptdma->dm_segs[0].ds_addr;
102 		sc->ram_size = PAGE_SIZE;
103 	}
104 
105 	/*
106 	 * sc->sc_link is the template for all device sc_link's
107 	 * for devices attached to this adapter. It is passed to
108 	 * the upper layers in config_found().
109 	 */
110 	sc->sc_link.adapter_softc = sc;
111 	sc->sc_link.adapter_buswidth =
112 	    (sc->features & SF_BUS_WIDE) ? 16 : 8;
113 	sc->sc_link.adapter_target =
114 	    bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_SCID);
115 	if (sc->sc_link.adapter_target == 0 ||
116 	    sc->sc_link.adapter_target >=
117 	    sc->sc_link.adapter_buswidth)
118 		sc->sc_link.adapter_target = SIOP_DEFAULT_TARGET;
119 
120 	for (i = 0; i < 16; i++)
121 		sc->targets[i] = NULL;
122 
123 	/* find min/max sync period for this chip */
124 	sc->st_maxsync = 0;
125 	sc->dt_maxsync = 0;
126 	sc->st_minsync = 255;
127 	sc->dt_minsync = 255;
128 	for (i = 0; i < sizeof(scf_period) / sizeof(scf_period[0]); i++) {
129 		if (sc->clock_period != scf_period[i].clock)
130 			continue;
131 		if (sc->st_maxsync < scf_period[i].period)
132 			sc->st_maxsync = scf_period[i].period;
133 		if (sc->st_minsync > scf_period[i].period)
134 			sc->st_minsync = scf_period[i].period;
135 	}
136 	if (sc->st_maxsync == 255 || sc->st_minsync == 0)
137 		panic("siop: can't find my sync parameters");
138 	for (i = 0; i < sizeof(dt_scf_period) / sizeof(dt_scf_period[0]); i++) {
139 		if (sc->clock_period != dt_scf_period[i].clock)
140 			continue;
141 		if (sc->dt_maxsync < dt_scf_period[i].period)
142 			sc->dt_maxsync = dt_scf_period[i].period;
143 		if (sc->dt_minsync > dt_scf_period[i].period)
144 			sc->dt_minsync = dt_scf_period[i].period;
145 	}
146 	if (sc->dt_maxsync == 255 || sc->dt_minsync == 0)
147 		panic("siop: can't find my sync parameters");
148 	return 0;
149 }
150 
151 void
152 siop_common_reset(sc)
153 	struct siop_common_softc *sc;
154 {
155 	u_int32_t stest3;
156 
157 	/* reset the chip */
158 	bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_ISTAT, ISTAT_SRST);
159 	delay(1000);
160 	bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_ISTAT, 0);
161 
162 	/* init registers */
163 	bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SCNTL0,
164 	    SCNTL0_ARB_MASK | SCNTL0_EPC | SCNTL0_AAP);
165 	bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SCNTL1, 0);
166 	bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SCNTL3, sc->clock_div);
167 	bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SXFER, 0);
168 	bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_DIEN, 0xff);
169 	bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SIEN0,
170 	    0xff & ~(SIEN0_CMP | SIEN0_SEL | SIEN0_RSL));
171 	bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SIEN1,
172 	    0xff & ~(SIEN1_HTH | SIEN1_GEN));
173 	bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_STEST2, 0);
174 	bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_STEST3, STEST3_TE);
175 	bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_STIME0,
176 	    (0xb << STIME0_SEL_SHIFT));
177 	bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SCID,
178 	    sc->sc_link.adapter_target | SCID_RRE);
179 	bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_RESPID0,
180 	    1 << sc->sc_link.adapter_target);
181 	bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_DCNTL,
182 	    (sc->features & SF_CHIP_PF) ? DCNTL_COM | DCNTL_PFEN : DCNTL_COM);
183 
184 	/* enable clock doubler or quadruler if appropriate */
185 	if (sc->features & (SF_CHIP_DBLR | SF_CHIP_QUAD)) {
186 		stest3 = bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_STEST3);
187 		bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_STEST1,
188 		    STEST1_DBLEN);
189 		if (sc->features & SF_CHIP_QUAD) {
190 			/* wait for PPL to lock */
191 			while ((bus_space_read_1(sc->sc_rt, sc->sc_rh,
192 			    SIOP_STEST4) & STEST4_LOCK) == 0)
193 				delay(10);
194 		} else {
195 			/* data sheet says 20us - more won't hurt */
196 			delay(100);
197 		}
198 		/* halt scsi clock, select doubler/quad, restart clock */
199 		bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_STEST3,
200 		    stest3 | STEST3_HSC);
201 		bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_STEST1,
202 		    STEST1_DBLEN | STEST1_DBLSEL);
203 		bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_STEST3, stest3);
204 	} else {
205 		bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_STEST1, 0);
206 	}
207 	if (sc->features & SF_CHIP_FIFO)
208 		bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_CTEST5,
209 		    bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_CTEST5) |
210 		    CTEST5_DFS);
211 	if (sc->features & SF_CHIP_LED0) {
212 		/* Set GPIO0 as output if software LED control is required */
213 		bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_GPCNTL,
214 		    bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_GPCNTL) & 0xfe);
215 	}
216 	if (sc->features & SF_BUS_ULTRA3) {
217 		/* reset SCNTL4 */
218 		bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SCNTL4, 0);
219 	}
220 	sc->mode = bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_STEST4) &
221 	    STEST4_MODE_MASK;
222 
223 	/*
224 	 * initialise the RAM. Without this we may get scsi gross errors on
225 	 * the 1010
226 	 */
227 	if (sc->features & SF_CHIP_RAM)
228 		bus_space_set_region_4(sc->sc_ramt, sc->sc_ramh,
229 			0, 0, sc->ram_size / 4);
230 	sc->sc_reset(sc);
231 }
232 
233 /* prepare tables before sending a cmd */
234 void
235 siop_setuptables(siop_cmd)
236 	struct siop_common_cmd *siop_cmd;
237 {
238 	int i;
239 	struct siop_common_softc *sc = siop_cmd->siop_sc;
240 	struct scsi_xfer *xs = siop_cmd->xs;
241 	int target = xs->sc_link->target;
242 	int lun = xs->sc_link->lun;
243 	int msgoffset = 1;
244 	int *targ_flags = &sc->targets[target]->flags;
245 	int quirks;
246 
247 	siop_cmd->siop_tables->id = htole32(sc->targets[target]->id);
248 	memset(siop_cmd->siop_tables->msg_out, 0,
249 	    sizeof(siop_cmd->siop_tables->msg_out));
250 	/* request sense doesn't disconnect */
251 	if (siop_cmd->status == CMDST_SENSE)
252 		siop_cmd->siop_tables->msg_out[0] = MSG_IDENTIFY(lun, 0);
253 	else if ((sc->features & SF_CHIP_GEBUG) &&
254 	    (sc->targets[target]->flags & TARF_ISWIDE) == 0)
255 		/*
256 		 * 1010 bug: it seems that the 1010 has problems with reselect
257 		 * when not in wide mode (generate false SCSI gross error).
258 		 * The FreeBSD sym driver has comments about it but their
259 		 * workaround (disable SCSI gross error reporting) doesn't
260 		 * work with my adapter. So disable disconnect when not
261 		 * wide.
262 		 */
263 		siop_cmd->siop_tables->msg_out[0] = MSG_IDENTIFY(lun, 0);
264 	else
265 		siop_cmd->siop_tables->msg_out[0] = MSG_IDENTIFY(lun, 1);
266 	siop_cmd->siop_tables->t_msgout.count= htole32(msgoffset);
267 	if (sc->targets[target]->status == TARST_ASYNC) {
268 		*targ_flags &= TARF_DT; /* Save TARF_DT 'cuz we don't set it here */
269 		quirks = xs->sc_link->quirks;
270 
271 #ifndef __hppa__
272 		if ((quirks & SDEV_NOTAGS) == 0)
273 			*targ_flags |= TARF_TAG;
274 #endif
275 		if (((quirks & SDEV_NOWIDE) == 0) &&
276 		    (sc->features & SF_BUS_WIDE))
277 			*targ_flags |= TARF_WIDE;
278 		if ((quirks & SDEV_NOSYNC) == 0)
279 			*targ_flags |= TARF_SYNC;
280 
281 		if ((sc->features & SF_CHIP_GEBUG) &&
282 		    (*targ_flags & TARF_WIDE) == 0)
283 			/*
284 			 * 1010 workaround: can't do disconnect if not wide,
285 			 * so can't do tag
286 			 */
287 			*targ_flags &= ~TARF_TAG;
288 
289 		/* Safe to call siop_add_dev() multiple times */
290 		siop_add_dev((struct siop_softc *)sc, target, lun);
291 
292 		if ((*targ_flags & TARF_DT) &&
293 		    (sc->mode == STEST4_MODE_LVD)) {
294 			sc->targets[target]->status = TARST_PPR_NEG;
295 			 siop_ppr_msg(siop_cmd, msgoffset, sc->dt_minsync,
296 			    sc->maxoff);
297 		} else if (*targ_flags & TARF_WIDE) {
298 			sc->targets[target]->status = TARST_WIDE_NEG;
299 			siop_wdtr_msg(siop_cmd, msgoffset,
300 			    MSG_EXT_WDTR_BUS_16_BIT);
301 		} else if (*targ_flags & TARF_SYNC) {
302 			sc->targets[target]->status = TARST_SYNC_NEG;
303 			siop_sdtr_msg(siop_cmd, msgoffset, sc->st_minsync,
304 			(sc->maxoff > 31) ? 31 :  sc->maxoff);
305 		} else {
306 			sc->targets[target]->status = TARST_OK;
307 			siop_update_xfer_mode(sc, target);
308 		}
309 	} else if (sc->targets[target]->status == TARST_OK &&
310 	    (*targ_flags & TARF_TAG) &&
311 	    siop_cmd->status != CMDST_SENSE) {
312 		siop_cmd->flags |= CMDFL_TAG;
313 	}
314 	siop_cmd->siop_tables->status =
315 	    htole32(SCSI_SIOP_NOSTATUS); /* set invalid status */
316 
317 	if ((xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) ||
318 	    siop_cmd->status == CMDST_SENSE) {
319 		bzero(siop_cmd->siop_tables->data,
320 		    sizeof(siop_cmd->siop_tables->data));
321 		for (i = 0; i < siop_cmd->dmamap_data->dm_nsegs; i++) {
322 			siop_cmd->siop_tables->data[i].count =
323 			    htole32(siop_cmd->dmamap_data->dm_segs[i].ds_len);
324 			siop_cmd->siop_tables->data[i].addr =
325 			    htole32(siop_cmd->dmamap_data->dm_segs[i].ds_addr);
326 		}
327 	}
328 }
329 
330 int
331 siop_wdtr_neg(siop_cmd)
332 	struct siop_common_cmd *siop_cmd;
333 {
334 	struct siop_common_softc *sc = siop_cmd->siop_sc;
335 	struct siop_common_target *siop_target = siop_cmd->siop_target;
336 	int target = siop_cmd->xs->sc_link->target;
337 	struct siop_common_xfer *tables = siop_cmd->siop_tables;
338 
339 	if (siop_target->status == TARST_WIDE_NEG) {
340 		/* we initiated wide negotiation */
341 		switch (tables->msg_in[3]) {
342 		case MSG_EXT_WDTR_BUS_8_BIT:
343 			siop_target->flags &= ~TARF_ISWIDE;
344 			sc->targets[target]->id &= ~(SCNTL3_EWS << 24);
345 			break;
346 		case MSG_EXT_WDTR_BUS_16_BIT:
347 			if (siop_target->flags & TARF_WIDE) {
348 				siop_target->flags |= TARF_ISWIDE;
349 				sc->targets[target]->id |= (SCNTL3_EWS << 24);
350 				break;
351 			}
352 		/* FALLTHROUH */
353 		default:
354 			/*
355  			 * hum, we got more than what we can handle, shouldn't
356 			 * happen. Reject, and stay async
357 			 */
358 			siop_target->flags &= ~TARF_ISWIDE;
359 			siop_target->status = TARST_OK;
360 			siop_target->offset = siop_target->period = 0;
361 			siop_update_xfer_mode(sc, target);
362 			printf("%s: rejecting invalid wide negotiation from "
363 			    "target %d (%d)\n", sc->sc_dev.dv_xname, target,
364 			    tables->msg_in[3]);
365 			tables->t_msgout.count= htole32(1);
366 			tables->msg_out[0] = MSG_MESSAGE_REJECT;
367 			return SIOP_NEG_MSGOUT;
368 		}
369 		tables->id = htole32(sc->targets[target]->id);
370 		bus_space_write_1(sc->sc_rt, sc->sc_rh,
371 		    SIOP_SCNTL3,
372 		    (sc->targets[target]->id >> 24) & 0xff);
373 		/* we now need to do sync */
374 		if (siop_target->flags & TARF_SYNC) {
375 			siop_target->status = TARST_SYNC_NEG;
376 			siop_sdtr_msg(siop_cmd, 0, sc->st_minsync,
377 			    (sc->maxoff > 31) ? 31 : sc->maxoff);
378 			return SIOP_NEG_MSGOUT;
379 		} else {
380 			siop_target->status = TARST_OK;
381 			siop_update_xfer_mode(sc, target);
382 			return SIOP_NEG_ACK;
383 		}
384 	} else {
385 		/* target initiated wide negotiation */
386 		if (tables->msg_in[3] >= MSG_EXT_WDTR_BUS_16_BIT
387 		    && (siop_target->flags & TARF_WIDE)) {
388 			siop_target->flags |= TARF_ISWIDE;
389 			sc->targets[target]->id |= SCNTL3_EWS << 24;
390 		} else {
391 			siop_target->flags &= ~TARF_ISWIDE;
392 			sc->targets[target]->id &= ~(SCNTL3_EWS << 24);
393 		}
394 		tables->id = htole32(sc->targets[target]->id);
395 		bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SCNTL3,
396 		    (sc->targets[target]->id >> 24) & 0xff);
397 		/*
398 		 * we did reset wide parameters, so fall back to async,
399 		 * but don't schedule a sync neg, target should initiate it
400 		 */
401 		siop_target->status = TARST_OK;
402 		siop_target->offset = siop_target->period = 0;
403 		siop_update_xfer_mode(sc, target);
404 		siop_wdtr_msg(siop_cmd, 0, (siop_target->flags & TARF_ISWIDE) ?
405 		    MSG_EXT_WDTR_BUS_16_BIT : MSG_EXT_WDTR_BUS_8_BIT);
406 		return SIOP_NEG_MSGOUT;
407 	}
408 }
409 
410 int
411 siop_ppr_neg(siop_cmd)
412 	struct siop_common_cmd *siop_cmd;
413 {
414 	struct siop_common_softc *sc = siop_cmd->siop_sc;
415 	struct siop_common_target *siop_target = siop_cmd->siop_target;
416 	int target = siop_cmd->xs->sc_link->target;
417 	struct siop_common_xfer *tables = siop_cmd->siop_tables;
418 	int sync, offset, options, scf = 0;
419 	int i;
420 
421 #ifdef DEBUG_NEG
422 	printf("%s: answer on ppr negotiation:", sc->sc_dev.dv_xname);
423 	for (i = 0; i < 8; i++)
424 		printf(" 0x%x", tables->msg_in[i]);
425 	printf("\n");
426 #endif
427 
428 	if (siop_target->status == TARST_PPR_NEG) {
429 		/* we initiated PPR negotiation */
430 		sync = tables->msg_in[3];
431 		offset = tables->msg_in[5];
432 		options = tables->msg_in[7];
433 		if (options != MSG_EXT_PPR_PROT_DT) {
434 			/* should't happen */
435 			printf("%s: ppr negotiation for target %d: "
436 			    "no DT option\n", sc->sc_dev.dv_xname, target);
437 			siop_target->status = TARST_ASYNC;
438 			siop_target->flags &= ~(TARF_DT | TARF_ISDT);
439 			siop_target->offset = 0;
440 			siop_target->period = 0;
441 			goto reject;
442 		}
443 
444 		if (offset > sc->maxoff || sync < sc->dt_minsync ||
445 		    sync > sc->dt_maxsync) {
446 			printf("%s: ppr negotiation for target %d: "
447 			    "offset (%d) or sync (%d) out of range\n",
448 			    sc->sc_dev.dv_xname, target, offset, sync);
449 			/* should not happen */
450 			siop_target->status = TARST_ASYNC;
451 			siop_target->flags &= ~(TARF_DT | TARF_ISDT);
452 			siop_target->offset = 0;
453 			siop_target->period = 0;
454 			goto reject;
455 		} else {
456 			for (i = 0; i <
457 			    sizeof(dt_scf_period) / sizeof(dt_scf_period[0]);
458 			    i++) {
459 				if (sc->clock_period != dt_scf_period[i].clock)
460 					continue;
461 				if (dt_scf_period[i].period == sync) {
462 					/* ok, found it. we now are sync. */
463 					siop_target->offset = offset;
464 					siop_target->period = sync;
465 					scf = dt_scf_period[i].scf;
466 					siop_target->flags |= TARF_ISDT;
467 				}
468 			}
469 			if ((siop_target->flags & TARF_ISDT) == 0) {
470 				printf("%s: ppr negotiation for target %d: "
471 				    "sync (%d) incompatible with adapter\n",
472 				    sc->sc_dev.dv_xname, target, sync);
473 				/*
474 				 * we didn't find it in our table, do async
475 				 * send reject msg, start SDTR/WDTR neg
476 				 */
477 				siop_target->status = TARST_ASYNC;
478 				siop_target->flags &= ~(TARF_DT | TARF_ISDT);
479 				siop_target->offset = 0;
480 				siop_target->period = 0;
481 				goto reject;
482 			}
483 		}
484 		if (tables->msg_in[6] != 1) {
485 			printf("%s: ppr negotiation for target %d: "
486 			    "transfer width (%d) incompatible with dt\n",
487 			    sc->sc_dev.dv_xname, target, tables->msg_in[6]);
488 			/* DT mode can only be done with wide transfers */
489 			siop_target->status = TARST_ASYNC;
490 			siop_target->flags &= ~(TARF_DT | TARF_ISDT);
491 			siop_target->offset = 0;
492 			siop_target->period = 0;
493 			goto reject;
494 		}
495 		siop_target->flags |= TARF_ISWIDE;
496 		sc->targets[target]->id |= (SCNTL3_EWS << 24);
497 		sc->targets[target]->id &= ~(SCNTL3_SCF_MASK << 24);
498 		sc->targets[target]->id |= scf << (24 + SCNTL3_SCF_SHIFT);
499 		sc->targets[target]->id &= ~(SXFER_MO_MASK << 8);
500 		sc->targets[target]->id |=
501 		    (siop_target->offset & SXFER_MO_MASK) << 8;
502 		sc->targets[target]->id &= ~0xff;
503 		sc->targets[target]->id |= SCNTL4_U3EN;
504 		siop_target->status = TARST_OK;
505 		siop_update_xfer_mode(sc, target);
506 		bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SCNTL3,
507 		    (sc->targets[target]->id >> 24) & 0xff);
508 		bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SXFER,
509 		    (sc->targets[target]->id >> 8) & 0xff);
510 		bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SCNTL4,
511 		    sc->targets[target]->id & 0xff);
512 		return SIOP_NEG_ACK;
513 	} else {
514 		/* target initiated PPR negotiation, shouldn't happen */
515 		printf("%s: rejecting invalid PPR negotiation from "
516 		    "target %d\n", sc->sc_dev.dv_xname, target);
517 reject:
518 		tables->t_msgout.count= htole32(1);
519 		tables->msg_out[0] = MSG_MESSAGE_REJECT;
520 		return SIOP_NEG_MSGOUT;
521 	}
522 }
523 
524 int
525 siop_sdtr_neg(siop_cmd)
526 	struct siop_common_cmd *siop_cmd;
527 {
528 	struct siop_common_softc *sc = siop_cmd->siop_sc;
529 	struct siop_common_target *siop_target = siop_cmd->siop_target;
530 	int target = siop_cmd->xs->sc_link->target;
531 	int sync, maxoffset, offset, i;
532 	int send_msgout = 0;
533 	struct siop_common_xfer *tables = siop_cmd->siop_tables;
534 
535 	/* limit to Ultra/2 parameters, need PPR for Ultra/3 */
536 	maxoffset = (sc->maxoff > 31) ? 31 : sc->maxoff;
537 
538 	sync = tables->msg_in[3];
539 	offset = tables->msg_in[4];
540 
541 	if (siop_target->status == TARST_SYNC_NEG) {
542 		/* we initiated sync negotiation */
543 		siop_target->status = TARST_OK;
544 #ifdef DEBUG
545 		printf("sdtr: sync %d offset %d\n", sync, offset);
546 #endif
547 		if (offset > maxoffset || sync < sc->st_minsync ||
548 			sync > sc->st_maxsync)
549 			goto reject;
550 		for (i = 0; i < sizeof(scf_period) / sizeof(scf_period[0]);
551 		    i++) {
552 			if (sc->clock_period != scf_period[i].clock)
553 				continue;
554 			if (scf_period[i].period == sync) {
555 				/* ok, found it. we now are sync. */
556 				siop_target->offset = offset;
557 				siop_target->period = sync;
558 				sc->targets[target]->id &=
559 				    ~(SCNTL3_SCF_MASK << 24);
560 				sc->targets[target]->id |= scf_period[i].scf
561 				    << (24 + SCNTL3_SCF_SHIFT);
562 				if (sync < 25 && /* Ultra */
563 				    (sc->features & SF_BUS_ULTRA3) == 0)
564 					sc->targets[target]->id |=
565 					    SCNTL3_ULTRA << 24;
566 				else
567 					sc->targets[target]->id &=
568 					    ~(SCNTL3_ULTRA << 24);
569 				sc->targets[target]->id &=
570 				    ~(SXFER_MO_MASK << 8);
571 				sc->targets[target]->id |=
572 				    (offset & SXFER_MO_MASK) << 8;
573 				sc->targets[target]->id &= ~0xff; /* scntl4 */
574 				goto end;
575 			}
576 		}
577 		/*
578 		 * we didn't find it in our table, do async and send reject
579 		 * msg
580 		 */
581 reject:
582 		send_msgout = 1;
583 		tables->t_msgout.count= htole32(1);
584 		tables->msg_out[0] = MSG_MESSAGE_REJECT;
585 		sc->targets[target]->id &= ~(SCNTL3_SCF_MASK << 24);
586 		sc->targets[target]->id &= ~(SCNTL3_ULTRA << 24);
587 		sc->targets[target]->id &= ~(SXFER_MO_MASK << 8);
588 		sc->targets[target]->id &= ~0xff; /* scntl4 */
589 		siop_target->offset = siop_target->period = 0;
590 	} else { /* target initiated sync neg */
591 #ifdef DEBUG
592 		printf("sdtr (target): sync %d offset %d\n", sync, offset);
593 #endif
594 		if (offset == 0 || sync > sc->st_maxsync) { /* async */
595 			goto async;
596 		}
597 		if (offset > maxoffset)
598 			offset = maxoffset;
599 		if (sync < sc->st_minsync)
600 			sync = sc->st_minsync;
601 		/* look for sync period */
602 		for (i = 0; i < sizeof(scf_period) / sizeof(scf_period[0]);
603 		    i++) {
604 			if (sc->clock_period != scf_period[i].clock)
605 				continue;
606 			if (scf_period[i].period == sync) {
607 				/* ok, found it. we now are sync. */
608 				siop_target->offset = offset;
609 				siop_target->period = sync;
610 				sc->targets[target]->id &=
611 				    ~(SCNTL3_SCF_MASK << 24);
612 				sc->targets[target]->id |= scf_period[i].scf
613 				    << (24 + SCNTL3_SCF_SHIFT);
614 				if (sync < 25 && /* Ultra */
615 				    (sc->features & SF_BUS_ULTRA3) == 0)
616 					sc->targets[target]->id |=
617 					    SCNTL3_ULTRA << 24;
618 				else
619 					sc->targets[target]->id &=
620 					    ~(SCNTL3_ULTRA << 24);
621 				sc->targets[target]->id &=
622 				    ~(SXFER_MO_MASK << 8);
623 				sc->targets[target]->id |=
624 				    (offset & SXFER_MO_MASK) << 8;
625 				sc->targets[target]->id &= ~0xff; /* scntl4 */
626 				siop_sdtr_msg(siop_cmd, 0, sync, offset);
627 				send_msgout = 1;
628 				goto end;
629 			}
630 		}
631 async:
632 		siop_target->offset = siop_target->period = 0;
633 		sc->targets[target]->id &= ~(SCNTL3_SCF_MASK << 24);
634 		sc->targets[target]->id &= ~(SCNTL3_ULTRA << 24);
635 		sc->targets[target]->id &= ~(SXFER_MO_MASK << 8);
636 		sc->targets[target]->id &= ~0xff; /* scntl4 */
637 		siop_sdtr_msg(siop_cmd, 0, 0, 0);
638 		send_msgout = 1;
639 	}
640 end:
641 	if (siop_target->status == TARST_OK)
642 		siop_update_xfer_mode(sc, target);
643 #ifdef DEBUG
644 	printf("id now 0x%x\n", sc->targets[target]->id);
645 #endif
646 	tables->id = htole32(sc->targets[target]->id);
647 	bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SCNTL3,
648 	    (sc->targets[target]->id >> 24) & 0xff);
649 	bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SXFER,
650 	    (sc->targets[target]->id >> 8) & 0xff);
651 	if (send_msgout) {
652 		return SIOP_NEG_MSGOUT;
653 	} else {
654 		return SIOP_NEG_ACK;
655 	}
656 }
657 
658 void
659 siop_sdtr_msg(siop_cmd, offset, ssync, soff)
660 	struct siop_common_cmd *siop_cmd;
661 	int offset;
662 	int ssync, soff;
663 {
664 	siop_cmd->siop_tables->msg_out[offset + 0] = MSG_EXTENDED;
665 	siop_cmd->siop_tables->msg_out[offset + 1] = MSG_EXT_SDTR_LEN;
666 	siop_cmd->siop_tables->msg_out[offset + 2] = MSG_EXT_SDTR;
667 	siop_cmd->siop_tables->msg_out[offset + 3] = ssync;
668 	siop_cmd->siop_tables->msg_out[offset + 4] = soff;
669 	siop_cmd->siop_tables->t_msgout.count =
670 	    htole32(offset + MSG_EXT_SDTR_LEN + 2);
671 }
672 
673 void
674 siop_wdtr_msg(siop_cmd, offset, wide)
675 	struct siop_common_cmd *siop_cmd;
676 	int offset;
677 {
678 	siop_cmd->siop_tables->msg_out[offset + 0] = MSG_EXTENDED;
679 	siop_cmd->siop_tables->msg_out[offset + 1] = MSG_EXT_WDTR_LEN;
680 	siop_cmd->siop_tables->msg_out[offset + 2] = MSG_EXT_WDTR;
681 	siop_cmd->siop_tables->msg_out[offset + 3] = wide;
682 	siop_cmd->siop_tables->t_msgout.count =
683 	    htole32(offset + MSG_EXT_WDTR_LEN + 2);
684 }
685 
686 void
687 siop_ppr_msg(siop_cmd, offset, ssync, soff)
688 	struct siop_common_cmd *siop_cmd;
689 	int offset;
690 	int ssync, soff;
691 {
692 	siop_cmd->siop_tables->msg_out[offset + 0] = MSG_EXTENDED;
693 	siop_cmd->siop_tables->msg_out[offset + 1] = MSG_EXT_PPR_LEN;
694 	siop_cmd->siop_tables->msg_out[offset + 2] = MSG_EXT_PPR;
695 	siop_cmd->siop_tables->msg_out[offset + 3] = ssync;
696 	siop_cmd->siop_tables->msg_out[offset + 4] = 0; /* reserved */
697 	siop_cmd->siop_tables->msg_out[offset + 5] = soff;
698 	siop_cmd->siop_tables->msg_out[offset + 6] = 1; /* wide */
699 	siop_cmd->siop_tables->msg_out[offset + 7] = MSG_EXT_PPR_PROT_DT;
700 	siop_cmd->siop_tables->t_msgout.count =
701 	    htole32(offset + MSG_EXT_PPR_LEN + 2);
702 }
703 
704 void
705 siop_minphys(bp)
706 	struct buf *bp;
707 {
708 	minphys(bp);
709 }
710 
711 void
712 siop_sdp(siop_cmd)
713 	struct siop_common_cmd *siop_cmd;
714 {
715 	/* save data pointer. Handle async only for now */
716 	int offset, dbc, sstat;
717 	struct siop_common_softc *sc = siop_cmd->siop_sc;
718 	scr_table_t *table; /* table to patch */
719 
720 	if ((siop_cmd->xs->flags & (SCSI_DATA_OUT | SCSI_DATA_IN))
721 	    == 0)
722 	    return; /* no data pointers to save */
723 	offset = bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_SCRATCHA + 1);
724 	if (offset >= SIOP_NSG) {
725 		printf("%s: bad offset in siop_sdp (%d)\n",
726 		    sc->sc_dev.dv_xname, offset);
727 		return;
728 	}
729 	table = &siop_cmd->siop_tables->data[offset];
730 #ifdef DEBUG_DR
731 	printf("sdp: offset %d count=%d addr=0x%x ", offset,
732 	    letoh32(table->count), letoh32(table->addr));
733 #endif
734 	dbc = bus_space_read_4(sc->sc_rt, sc->sc_rh, SIOP_DBC) & 0x00ffffff;
735 	if (siop_cmd->xs->flags & SCSI_DATA_OUT) {
736 		if (sc->features & SF_CHIP_DFBC) {
737 			dbc +=
738 			    bus_space_read_2(sc->sc_rt, sc->sc_rh, SIOP_DFBC);
739 		} else {
740 			/* need to account stale data in FIFO */
741 			int dfifo =
742 			    bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_DFIFO);
743 			if (sc->features & SF_CHIP_FIFO) {
744 				dfifo |= (bus_space_read_1(sc->sc_rt, sc->sc_rh,
745 				    SIOP_CTEST5) & CTEST5_BOMASK) << 8;
746 				dbc += (dfifo - (dbc & 0x3ff)) & 0x3ff;
747 			} else {
748 				dbc += (dfifo - (dbc & 0x7f)) & 0x7f;
749 			}
750 		}
751 		sstat = bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_SSTAT0);
752 		if (sstat & SSTAT0_OLF)
753 			dbc++;
754 		if ((sstat & SSTAT0_ORF) && (sc->features & SF_CHIP_DFBC) == 0)
755 			dbc++;
756 		if (siop_cmd->siop_target->flags & TARF_ISWIDE) {
757 			sstat = bus_space_read_1(sc->sc_rt, sc->sc_rh,
758 			    SIOP_SSTAT2);
759 			if (sstat & SSTAT2_OLF1)
760 				dbc++;
761 			if ((sstat & SSTAT2_ORF1) &&
762 			    (sc->features & SF_CHIP_DFBC) == 0)
763 				dbc++;
764 		}
765 		/* clear the FIFO */
766 		bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_CTEST3,
767 		    bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_CTEST3) |
768 		    CTEST3_CLF);
769 	}
770 	table->addr =
771 	    htole32(letoh32(table->addr) + letoh32(table->count) - dbc);
772 	table->count = htole32(dbc);
773 #ifdef DEBUG_DR
774 	printf("now count=%d addr=0x%x\n",
775 	    letoh32(table->count), letoh32(table->addr));
776 #endif
777 }
778 
779 void
780 siop_clearfifo(sc)
781 	struct siop_common_softc *sc;
782 {
783 	int timeout = 0;
784 	int ctest3 = bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_CTEST3);
785 
786 #ifdef DEBUG_INTR
787 	printf("DMA fifo not empty !\n");
788 #endif
789 	bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_CTEST3,
790 	    ctest3 | CTEST3_CLF);
791 	while ((bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_CTEST3) &
792 	    CTEST3_CLF) != 0) {
793 		delay(1);
794 		if (++timeout > 1000) {
795 			printf("clear fifo failed\n");
796 			bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_CTEST3,
797 			    bus_space_read_1(sc->sc_rt, sc->sc_rh,
798 			    SIOP_CTEST3) & ~CTEST3_CLF);
799 			return;
800 		}
801 	}
802 }
803 
804 int
805 siop_modechange(sc)
806 	struct siop_common_softc *sc;
807 {
808 	int retry;
809 	int sist0, sist1, stest2;
810 	for (retry = 0; retry < 5; retry++) {
811 		/*
812 		 * datasheet says to wait 100ms and re-read SIST1,
813 		 * to check that DIFFSENSE is stable.
814 		 * We may delay() 5 times for  100ms at interrupt time;
815 		 * hopefully this will not happen often.
816 		 */
817 		delay(100000);
818 		sist0 = bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_SIST0);
819 		sist1 = bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_SIST1);
820 		if (sist1 & SIEN1_SBMC)
821 			continue; /* we got an irq again */
822 		sc->mode = bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_STEST4) &
823 		    STEST4_MODE_MASK;
824 		stest2 = bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_STEST2);
825 		switch(sc->mode) {
826 		case STEST4_MODE_DIF:
827 			printf("%s: switching to differential mode\n",
828 			    sc->sc_dev.dv_xname);
829 			bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_STEST2,
830 			    stest2 | STEST2_DIF);
831 			break;
832 		case STEST4_MODE_SE:
833 			printf("%s: switching to single-ended mode\n",
834 			    sc->sc_dev.dv_xname);
835 			bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_STEST2,
836 			    stest2 & ~STEST2_DIF);
837 			break;
838 		case STEST4_MODE_LVD:
839 			printf("%s: switching to LVD mode\n",
840 			    sc->sc_dev.dv_xname);
841 			bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_STEST2,
842 			    stest2 & ~STEST2_DIF);
843 			break;
844 		default:
845 			printf("%s: invalid SCSI mode 0x%x\n",
846 			    sc->sc_dev.dv_xname, sc->mode);
847 			return 0;
848 		}
849 		return 1;
850 	}
851 	printf("%s: timeout waiting for DIFFSENSE to stabilise\n",
852 	    sc->sc_dev.dv_xname);
853 	return 0;
854 }
855 
856 void
857 siop_resetbus(sc)
858 	struct siop_common_softc *sc;
859 {
860 	int scntl1;
861 	scntl1 = bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_SCNTL1);
862 	bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SCNTL1,
863 	    scntl1 | SCNTL1_RST);
864 	/* minimum 25 us, more time won't hurt */
865 	delay(100);
866 	bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SCNTL1, scntl1);
867 }
868 
869 void
870 siop_update_xfer_mode(sc, target)
871         struct siop_common_softc *sc;
872         int target;
873 {
874 	struct siop_common_target *siop_target;
875 
876 	siop_target = sc->targets[target];
877 
878 	printf("%s: target %d now using %s%s%d bit ",
879             sc->sc_dev.dv_xname, target,
880 	    (siop_target->flags & TARF_TAG) ? "tagged " : "",
881 	    (siop_target->flags & TARF_ISDT) ? "DT " : "",
882 	    (siop_target->flags & TARF_ISWIDE) ? 16 : 8);
883 
884 	if (siop_target->offset == 0)
885 		printf("async ");
886 	else {
887 		switch (siop_target->period) {
888 		case 9: /*   12.5ns cycle */
889 			printf("80.0");
890 			break;
891 		case 10: /*  25  ns cycle */
892 			printf("40.0");
893 			break;
894 		case 12: /*  48  ns cycle */
895 			printf("20.0");
896 			break;
897 		case 18: /*  72  ns cycle */
898 			printf("13.3");
899 			break;
900 		case 25: /* 100  ns cycle */
901 			printf("10.0");
902 			break;
903 		case 37: /* 118  ns cycle */
904 			printf("6.67");
905 			break;
906 		case 50: /* 200  ns cycle */
907 			printf("5.0");
908 			break;
909 		case 75: /* 300  ns cycle */
910 			printf("3.33");
911 			break;
912 		default:
913 			printf("??");
914 			break;
915 		}
916 		printf(" MHz %d REQ/ACK offset ", siop_target->offset);
917 	}
918 
919 	printf("xfers\n");
920 
921 	if ((sc->features & SF_CHIP_GEBUG) &&
922 	    (siop_target->flags & TARF_ISWIDE) == 0)
923 		/* 1010 workaround: can't do disconnect if not wide, so can't do tag */
924 		siop_target->flags &= ~TARF_TAG;
925 }
926