xref: /csrg-svn/sys/pmax/dev/asc.c (revision 56628)
1 /*-
2  * Copyright (c) 1992 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Ralph Campbell.
7  *
8  * %sccs.include.redist.c%
9  *
10  *	@(#)asc.c	7.7 (Berkeley) 10/24/92
11  */
12 
13 /*
14  * Mach Operating System
15  * Copyright (c) 1991,1990,1989 Carnegie Mellon University
16  * All Rights Reserved.
17  *
18  * Permission to use, copy, modify and distribute this software and its
19  * documentation is hereby granted, provided that both the copyright
20  * notice and this permission notice appear in all copies of the
21  * software, derivative works or modified versions, and any portions
22  * thereof, and that both notices appear in supporting documentation.
23  *
24  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
25  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
26  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
27  *
28  * Carnegie Mellon requests users of this software to return to
29  *
30  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
31  *  School of Computer Science
32  *  Carnegie Mellon University
33  *  Pittsburgh PA 15213-3890
34  *
35  * any improvements or extensions that they make and grant Carnegie the
36  * rights to redistribute these changes.
37  */
38 
39 /*
40  * HISTORY
41  * $Log:	scsi_53C94_hdw.c,v $
42  * Revision 2.5  91/02/05  17:45:07  mrt
43  * 	Added author notices
44  * 	[91/02/04  11:18:43  mrt]
45  *
46  * 	Changed to use new Mach copyright
47  * 	[91/02/02  12:17:20  mrt]
48  *
49  * Revision 2.4  91/01/08  15:48:24  rpd
50  * 	Added continuation argument to thread_block.
51  * 	[90/12/27            rpd]
52  *
53  * Revision 2.3  90/12/05  23:34:48  af
54  * 	Recovered from pmax merge.. and from the destruction of a disk.
55  * 	[90/12/03  23:40:40  af]
56  *
57  * Revision 2.1.1.1  90/11/01  03:39:09  af
58  * 	Created, from the DEC specs:
59  * 	"PMAZ-AA TURBOchannel SCSI Module Functional Specification"
60  * 	Workstation Systems Engineering, Palo Alto, CA. Aug 27, 1990.
61  * 	And from the NCR data sheets
62  * 	"NCR 53C94, 53C95, 53C96 Advances SCSI Controller"
63  * 	[90/09/03            af]
64  */
65 
66 /*
67  *	File: scsi_53C94_hdw.h
68  * 	Author: Alessandro Forin, Carnegie Mellon University
69  *	Date:	9/90
70  *
71  *	Bottom layer of the SCSI driver: chip-dependent functions
72  *
73  *	This file contains the code that is specific to the NCR 53C94
74  *	SCSI chip (Host Bus Adapter in SCSI parlance): probing, start
75  *	operation, and interrupt routine.
76  */
77 
78 /*
79  * This layer works based on small simple 'scripts' that are installed
80  * at the start of the command and drive the chip to completion.
81  * The idea comes from the specs of the NCR 53C700 'script' processor.
82  *
83  * There are various reasons for this, mainly
84  * - Performance: identify the common (successful) path, and follow it;
85  *   at interrupt time no code is needed to find the current status
86  * - Code size: it should be easy to compact common operations
87  * - Adaptability: the code skeleton should adapt to different chips without
88  *   terrible complications.
89  * - Error handling: and it is easy to modify the actions performed
90  *   by the scripts to cope with strange but well identified sequences
91  *
92  */
93 
94 #include "asc.h"
95 #if NASC > 0
96 
97 #include <sys/param.h>
98 #include <sys/systm.h>
99 #include <sys/errno.h>
100 
101 #include <pmax/dev/device.h>
102 #include <pmax/dev/scsi.h>
103 #include <pmax/dev/ascreg.h>
104 
105 #define ASC_OFFSET_53C94	0x0		/* from module base */
106 #define ASC_OFFSET_DMAR		0x40000		/* DMA Address Register */
107 #define ASC_OFFSET_RAM		0x80000		/* SRAM Buffer */
108 #define ASC_OFFSET_ROM		0xc0000		/* Diagnostic ROM */
109 
110 #define	ASC_RAM_SIZE		0x20000		/* 128k (32k*32) */
111 
112 /*
113  * DMA Address Register
114  */
115 #define ASC_DMAR_MASK		0x1ffff		/* 17 bits, 128k */
116 #define ASC_DMAR_WRITE		0x80000000	/* DMA direction bit */
117 #define	ASC_DMA_ADDR(x)		((unsigned)(x) & ASC_DMAR_MASK)
118 
119 /*
120  * Synch xfer parameters, and timing conversions
121  */
122 #define SCSI_MIN_PERIOD	50	/* in 4 nsecs units */
123 #define ASC_MIN_PERIOD	5	/* in CLKS/BYTE, 1 CLK = 40nsecs */
124 #define ASC_MAX_PERIOD	35	/* in CLKS/BYTE, 1 CLK = 40nsecs */
125 #define ASC_MAX_OFFSET	15	/* pure number */
126 
127 int	asc_to_scsi_period[] = {
128 	320,
129 	330,
130 	340,
131 	350,
132 	50,
133 	50,
134 	60,
135 	70,
136 	80,
137 	90,
138 	100,
139 	110,
140 	120,
141 	130,
142 	140,
143 	150,
144 	160,
145 	170,
146 	180,
147 	190,
148 	200,
149 	210,
150 	220,
151 	230,
152 	240,
153 	250,
154 	260,
155 	270,
156 	280,
157 	290,
158 	300,
159 	310,
160 };
161 
162 /*
163  * Internal forward declarations.
164  */
165 static void asc_reset();
166 static void asc_startcmd();
167 
168 #ifdef DEBUG
169 int	asc_debug = 1;
170 int	asc_debug_cmd;
171 int	asc_debug_bn;
172 int	asc_debug_sz;
173 #define NLOG 16
174 struct asc_log {
175 	u_int	status;
176 	u_char	state;
177 	u_char	msg;
178 	int	target;
179 } asc_log[NLOG], *asc_logp = asc_log;
180 #define PACK(unit, status, ss, ir) \
181 	((unit << 24) | (status << 16) | (ss << 8) | ir)
182 #endif
183 
184 /*
185  * Scripts are entries in a state machine table.
186  * A script has four parts: a pre-condition, an action, a command to the chip,
187  * and an index into asc_scripts for the next state. The first triggers error
188  * handling if not satisfied and in our case it is formed by the
189  * values of the interrupt register and status register, this
190  * basically captures the phase of the bus and the TC and BS
191  * bits.  The action part is just a function pointer, and the
192  * command is what the 53C94 should be told to do at the end
193  * of the action processing.  This command is only issued and the
194  * script proceeds if the action routine returns TRUE.
195  * See asc_intr() for how and where this is all done.
196  */
197 typedef struct script {
198 	int		condition;	/* expected state at interrupt time */
199 	int		(*action)();	/* extra operations */
200 	int		command;	/* command to the chip */
201 	struct script	*next;		/* index into asc_scripts for next state */
202 } script_t;
203 
204 /* Matching on the condition value */
205 #define	SCRIPT_MATCH(ir, csr)		((ir) | (ASC_PHASE(csr) << 8))
206 
207 /* forward decls of script actions */
208 static int	script_nop();		/* when nothing needed */
209 static int	asc_end();		/* all come to an end */
210 static int	asc_get_status();	/* get status from target */
211 static int	asc_dma_in();		/* start reading data from target */
212 static int	asc_last_dma_in();	/* cleanup after all data is read */
213 static int	asc_resume_in();	/* resume data in after a message */
214 static int	asc_resume_dma_in();	/* resume DMA after a disconnect */
215 static int	asc_dma_out();		/* send data to target via dma */
216 static int	asc_last_dma_out();	/* cleanup after all data is written */
217 static int	asc_resume_out();	/* resume data out after a message */
218 static int	asc_resume_dma_out();	/* resume DMA after a disconnect */
219 static int	asc_sendsync();		/* negotiate sync xfer */
220 static int	asc_replysync();	/* negotiate sync xfer */
221 static int	asc_msg_in();		/* process a message byte */
222 static int	asc_disconnect();	/* process an expected disconnect */
223 
224 /* Define the index into asc_scripts for various state transitions */
225 #define	SCRIPT_DATA_IN		0
226 #define	SCRIPT_CONTINUE_IN	2
227 #define	SCRIPT_DATA_OUT		3
228 #define	SCRIPT_CONTINUE_OUT	5
229 #define	SCRIPT_SIMPLE		6
230 #define	SCRIPT_GET_STATUS	7
231 #define	SCRIPT_MSG_IN		9
232 #define	SCRIPT_REPLY_SYNC	11
233 #define	SCRIPT_TRY_SYNC		12
234 #define	SCRIPT_DISCONNECT	15
235 #define	SCRIPT_RESEL		16
236 #define	SCRIPT_RESUME_IN	17
237 #define	SCRIPT_RESUME_DMA_IN	18
238 #define	SCRIPT_RESUME_OUT	19
239 #define	SCRIPT_RESUME_DMA_OUT	20
240 #define	SCRIPT_RESUME_NO_DATA	21
241 
242 /*
243  * Scripts
244  */
245 script_t asc_scripts[] = {
246 	/* start data in */
247 	{SCRIPT_MATCH(ASC_INT_FC | ASC_INT_BS, ASC_PHASE_DATAI),	/*  0 */
248 		asc_dma_in, ASC_CMD_XFER_INFO | ASC_CMD_DMA,
249 		&asc_scripts[SCRIPT_DATA_IN + 1]},
250 	{SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_STATUS),			/*  1 */
251 		asc_last_dma_in, ASC_CMD_I_COMPLETE,
252 		&asc_scripts[SCRIPT_GET_STATUS]},
253 
254 	/* continue data in after a chuck is finished */
255 	{SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAI),			/*  2 */
256 		asc_dma_in, ASC_CMD_XFER_INFO | ASC_CMD_DMA,
257 		&asc_scripts[SCRIPT_DATA_IN + 1]},
258 
259 	/* start data out */
260 	{SCRIPT_MATCH(ASC_INT_FC | ASC_INT_BS, ASC_PHASE_DATAO),	/*  3 */
261 		asc_dma_out, ASC_CMD_XFER_INFO | ASC_CMD_DMA,
262 		&asc_scripts[SCRIPT_DATA_OUT + 1]},
263 	{SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_STATUS),			/*  4 */
264 		asc_last_dma_out, ASC_CMD_I_COMPLETE,
265 		&asc_scripts[SCRIPT_GET_STATUS]},
266 
267 	/* continue data out after a chuck is finished */
268 	{SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAO),			/*  5 */
269 		asc_dma_out, ASC_CMD_XFER_INFO | ASC_CMD_DMA,
270 		&asc_scripts[SCRIPT_DATA_OUT + 1]},
271 
272 	/* simple command with no data transfer */
273 	{SCRIPT_MATCH(ASC_INT_FC | ASC_INT_BS, ASC_PHASE_STATUS),	/*  6 */
274 		script_nop, ASC_CMD_I_COMPLETE,
275 		&asc_scripts[SCRIPT_GET_STATUS]},
276 
277 	/* get status and finish command */
278 	{SCRIPT_MATCH(ASC_INT_FC, ASC_PHASE_MSG_IN),			/*  7 */
279 		asc_get_status, ASC_CMD_MSG_ACPT,
280 		&asc_scripts[SCRIPT_GET_STATUS + 1]},
281 	{SCRIPT_MATCH(ASC_INT_DISC, 0),					/*  8 */
282 		asc_end, ASC_CMD_NOP,
283 		&asc_scripts[SCRIPT_GET_STATUS + 1]},
284 
285 	/* message in */
286 	{SCRIPT_MATCH(ASC_INT_FC, ASC_PHASE_MSG_IN),			/*  9 */
287 		asc_msg_in, ASC_CMD_MSG_ACPT,
288 		&asc_scripts[SCRIPT_MSG_IN + 1]},
289 	{SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_MSG_IN),			/* 10 */
290 		script_nop, ASC_CMD_XFER_INFO,
291 		&asc_scripts[SCRIPT_MSG_IN]},
292 
293 	/* send synchonous negotiation reply */
294 	{SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_MSG_OUT),			/* 11 */
295 		asc_replysync, ASC_CMD_XFER_INFO,
296 		&asc_scripts[SCRIPT_REPLY_SYNC]},
297 
298 	/* try to negotiate synchonous transfer parameters */
299 	{SCRIPT_MATCH(ASC_INT_FC | ASC_INT_BS, ASC_PHASE_MSG_OUT),	/* 12 */
300 		asc_sendsync, ASC_CMD_XFER_INFO,
301 		&asc_scripts[SCRIPT_TRY_SYNC + 1]},
302 	{SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_MSG_IN),			/* 13 */
303 		script_nop, ASC_CMD_XFER_INFO,
304 		&asc_scripts[SCRIPT_MSG_IN]},
305 	{SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_COMMAND),			/* 14 */
306 		script_nop, ASC_CMD_XFER_INFO | ASC_CMD_DMA,
307 		&asc_scripts[SCRIPT_RESUME_NO_DATA]},
308 
309 	/* handle a disconnect */
310 	{SCRIPT_MATCH(ASC_INT_DISC, ASC_PHASE_DATAO),			/* 15 */
311 		asc_disconnect, ASC_CMD_ENABLE_SEL,
312 		&asc_scripts[SCRIPT_RESEL]},
313 
314 	/* reselect sequence: this is just a placeholder so match fails */
315 	{SCRIPT_MATCH(0, ASC_PHASE_MSG_IN),				/* 16 */
316 		script_nop, ASC_CMD_MSG_ACPT,
317 		&asc_scripts[SCRIPT_RESEL]},
318 
319 	/* resume data in after a message */
320 	{SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAI),			/* 17 */
321 		asc_resume_in, ASC_CMD_XFER_INFO | ASC_CMD_DMA,
322 		&asc_scripts[SCRIPT_DATA_IN + 1]},
323 
324 	/* resume partial DMA data in after a message */
325 	{SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAI),			/* 18 */
326 		asc_resume_dma_in, ASC_CMD_XFER_INFO | ASC_CMD_DMA,
327 		&asc_scripts[SCRIPT_DATA_IN + 1]},
328 
329 	/* resume data out after a message */
330 	{SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAO),			/* 19 */
331 		asc_resume_out, ASC_CMD_XFER_INFO | ASC_CMD_DMA,
332 		&asc_scripts[SCRIPT_DATA_OUT + 1]},
333 
334 	/* resume partial DMA data out after a message */
335 	{SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAO),			/* 20 */
336 		asc_resume_dma_out, ASC_CMD_XFER_INFO | ASC_CMD_DMA,
337 		&asc_scripts[SCRIPT_DATA_OUT + 1]},
338 
339 	/* resume after a message when there is no more data */
340 	{SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_STATUS),			/* 21 */
341 		script_nop, ASC_CMD_I_COMPLETE,
342 		&asc_scripts[SCRIPT_GET_STATUS]},
343 };
344 
345 /*
346  * State kept for each active SCSI device.
347  */
348 typedef struct scsi_state {
349 	script_t *script;	/* saved script while processing error */
350 	int	statusByte;	/* status byte returned during STATUS_PHASE */
351 	int	error;		/* errno to pass back to device driver */
352 	u_char	*dmaBufAddr;	/* DMA buffer address */
353 	u_int	dmaBufSize;	/* DMA buffer size */
354 	int	dmalen;		/* amount to transfer in this chunk */
355 	int	dmaresid;	/* amount not transfered if chunk suspended */
356 	int	buflen;		/* total remaining amount of data to transfer */
357 	char	*buf;		/* current pointer within scsicmd->buf */
358 	int	flags;		/* see below */
359 	int	msglen;		/* number of message bytes to read */
360 	int	msgcnt;		/* number of message bytes received */
361 	u_char	sync_period;	/* DMA synchronous period */
362 	u_char	sync_offset;	/* DMA synchronous xfer offset or 0 if async */
363 	u_char	msg_out;	/* next MSG_OUT byte to send */
364 	u_char	msg_in[16];	/* buffer for multibyte messages */
365 } State;
366 
367 /* state flags */
368 #define DISCONN		0x01	/* true if currently disconnected from bus */
369 #define DMA_IN_PROGRESS	0x02	/* true if data DMA started */
370 #define DMA_IN		0x04	/* true if reading from SCSI device */
371 #define DMA_OUT		0x10	/* true if writing to SCSI device */
372 #define DID_SYNC	0x20	/* true if synchronous offset was negotiated */
373 #define TRY_SYNC	0x40	/* true if try neg. synchronous offset */
374 
375 #define ASC_NCMD	7
376 /*
377  * State kept for each active SCSI host interface (53C94).
378  */
379 struct asc_softc {
380 	asc_regmap_t	*regs;		/* chip address */
381 	volatile int	*dmar;		/* DMA address register address */
382 	u_char		*buff;		/* RAM buffer address */
383 	int		myid;		/* SCSI ID of this interface */
384 	int		myidmask;	/* ~(1 << myid) */
385 	int		state;		/* current SCSI connection state */
386 	int		target;		/* target SCSI ID if busy */
387 	script_t	*script;	/* next expected interrupt & action */
388 	ScsiCmd		*cmd[ASC_NCMD];	/* active command indexed by SCSI ID */
389 	State		st[ASC_NCMD];	/* state info for each active command */
390 } asc_softc[NASC];
391 
392 #define	ASC_STATE_IDLE		0	/* idle state */
393 #define	ASC_STATE_BUSY		1	/* selecting or currently connected */
394 #define ASC_STATE_TARGET	2	/* currently selected as target */
395 #define ASC_STATE_RESEL		3	/* currently waiting for reselect */
396 
397 typedef struct asc_softc *asc_softc_t;
398 
399 /*
400  * Definition of the controller for the auto-configuration program.
401  */
402 int	asc_probe();
403 void	asc_start();
404 void	asc_intr();
405 struct	driver ascdriver = {
406 	"asc", asc_probe, asc_start, 0, asc_intr,
407 };
408 
409 /*
410  * Test to see if device is present.
411  * Return true if found and initialized ok.
412  */
413 asc_probe(cp)
414 	register struct pmax_ctlr *cp;
415 {
416 	register asc_softc_t asc;
417 	register asc_regmap_t *regs;
418 	int unit, id, s, i;
419 
420 	if ((unit = cp->pmax_unit) >= NASC)
421 		return (0);
422 	if (badaddr(cp->pmax_addr + ASC_OFFSET_53C94, 1))
423 		return (0);
424 	asc = &asc_softc[unit];
425 
426 	/*
427 	 * Initialize hw descriptor, cache some pointers
428 	 */
429 	asc->regs = (asc_regmap_t *)(cp->pmax_addr + ASC_OFFSET_53C94);
430 	asc->dmar = (volatile int *)(cp->pmax_addr + ASC_OFFSET_DMAR);
431 	asc->buff = (u_char *)(cp->pmax_addr + ASC_OFFSET_RAM);
432 
433 	asc->state = ASC_STATE_IDLE;
434 	asc->target = -1;
435 
436 	regs = asc->regs;
437 
438 	/*
439 	 * Reset chip, fully.  Note that interrupts are already enabled.
440 	 */
441 	s = splbio();
442 
443 	/* preserve our ID for now */
444 	asc->myid = regs->asc_cnfg1 & ASC_CNFG1_MY_BUS_ID;
445 	asc->myidmask = ~(1 << asc->myid);
446 
447 	asc_reset(asc, regs);
448 
449 	/*
450 	 * Our SCSI id on the bus.
451 	 * The user can set this via the prom on 3maxen/pmaxen.
452 	 * If this changes it is easy to fix: make a default that
453 	 * can be changed as boot arg.
454 	 */
455 #ifdef	unneeded
456 	regs->asc_cnfg1 = (regs->asc_cnfg1 & ~ASC_CNFG1_MY_BUS_ID) |
457 			      (scsi_initiator_id[unit] & 0x7);
458 #endif
459 	id = regs->asc_cnfg1 & ASC_CNFG1_MY_BUS_ID;
460 	splx(s);
461 
462 	/*
463 	 * Statically partition the DMA buffer between targets.
464 	 * This way we will eventually be able to attach/detach
465 	 * drives on-fly.  And 18k/target is plenty for normal use.
466 	 */
467 #define PER_TGT_DMA_SIZE	((ASC_RAM_SIZE/7) & ~(sizeof(int)-1))
468 
469 	/*
470 	 * Give each target its own DMA buffer region.
471 	 * We may want to try ping ponging buffers later.
472 	 */
473 	for (i = 0; i < ASC_NCMD; i++) {
474 		asc->st[i].dmaBufAddr = asc->buff + PER_TGT_DMA_SIZE * i;
475 		asc->st[i].dmaBufSize = PER_TGT_DMA_SIZE;
476 	}
477 	printf("asc%d at nexus0 csr 0x%x priority %d SCSI id %d\n",
478 		unit, cp->pmax_addr, cp->pmax_pri, id);
479 	return (1);
480 }
481 
482 /*
483  * Start activity on a SCSI device.
484  * We maintain information on each device separately since devices can
485  * connect/disconnect during an operation.
486  */
487 void
488 asc_start(scsicmd)
489 	register ScsiCmd *scsicmd;	/* command to start */
490 {
491 	register struct scsi_device *sdp = scsicmd->sd;
492 	register asc_softc_t asc = &asc_softc[sdp->sd_ctlr];
493 	int s;
494 
495 	s = splbio();
496 	/*
497 	 * Check if another command is already in progress.
498 	 * We may have to change this if we allow SCSI devices with
499 	 * separate LUNs.
500 	 */
501 	if (asc->cmd[sdp->sd_drive]) {
502 		printf("asc%d: device %s busy at start\n", sdp->sd_ctlr,
503 			sdp->sd_driver->d_name);
504 		(*sdp->sd_driver->d_done)(scsicmd->unit, EBUSY,
505 			scsicmd->buflen, 0);
506 		splx(s);
507 	}
508 	asc->cmd[sdp->sd_drive] = scsicmd;
509 	asc_startcmd(asc, sdp->sd_drive);
510 	splx(s);
511 }
512 
513 static void
514 asc_reset(asc, regs)
515 	asc_softc_t asc;
516 	asc_regmap_t *regs;
517 {
518 
519 	/*
520 	 * Reset chip and wait till done
521 	 */
522 	regs->asc_cmd = ASC_CMD_RESET;
523 	MachEmptyWriteBuffer(); DELAY(25);
524 
525 	/* spec says this is needed after reset */
526 	regs->asc_cmd = ASC_CMD_NOP;
527 	MachEmptyWriteBuffer(); DELAY(25);
528 
529 	/*
530 	 * Set up various chip parameters
531 	 */
532 	regs->asc_ccf = ASC_CCF_25MHz;	/* 25 MHz clock */
533 	MachEmptyWriteBuffer(); DELAY(25);
534 	regs->asc_sel_timo = ASC_TIMEOUT_250;
535 	/* restore our ID */
536 	regs->asc_cnfg1 = asc->myid | ASC_CNFG1_P_CHECK;
537 	regs->asc_cnfg2 = /* ASC_CNFG2_RFB | */ ASC_CNFG2_EPL;
538 	regs->asc_cnfg3 = 0;
539 	/* zero anything else */
540 	ASC_TC_PUT(regs, 0);
541 	regs->asc_sel_timo = ASC_TIMEOUT_250;
542 	regs->asc_syn_p = ASC_MIN_PERIOD;
543 	regs->asc_syn_o = 0;	/* async for now */
544 	MachEmptyWriteBuffer();
545 }
546 
547 /*
548  * Start a SCSI command on a target.
549  */
550 static void
551 asc_startcmd(asc, target)
552 	asc_softc_t asc;
553 	int target;
554 {
555 	register asc_regmap_t *regs;
556 	register ScsiCmd *scsicmd;
557 	register State *state;
558 	int len;
559 
560 	/*
561 	 * See if another target is currently selected on this SCSI bus.
562 	 */
563 	if (asc->target >= 0)
564 		return;
565 
566 	regs = asc->regs;
567 
568 	/*
569 	 * Check to see if a reselection is in progress and if so,
570 	 * try to cancel it or respond to the reselection if it won.
571 	 */
572 	if (asc->state == ASC_STATE_RESEL) {
573 		regs->asc_cmd = ASC_CMD_DISABLE_SEL;
574 		while (!(regs->asc_status & ASC_CSR_INT))
575 			DELAY(1);
576 		asc_intr(asc - asc_softc);
577 		/* we will be busy if a reselecting device won */
578 		if (asc->state == ASC_STATE_BUSY)
579 			return;
580 	}
581 
582 	asc->state = ASC_STATE_BUSY;
583 	asc->target = target;
584 
585 	/* cache some pointers */
586 	scsicmd = asc->cmd[target];
587 	state = &asc->st[target];
588 
589 #ifdef DEBUG
590 	if (asc_debug > 1) {
591 		printf("asc_startcmd: %s target %d cmd %x len %d\n",
592 			scsicmd->sd->sd_driver->d_name, target,
593 			scsicmd->cmd[0], scsicmd->buflen);
594 	}
595 	asc_debug_cmd = scsicmd->cmd[0];
596 	if (scsicmd->cmd[0] == SCSI_READ_EXT) {
597 		asc_debug_bn = (scsicmd->cmd[2] << 24) |
598 			(scsicmd->cmd[3] << 16) |
599 			(scsicmd->cmd[4] << 8) |
600 			scsicmd->cmd[5];
601 		asc_debug_sz = (scsicmd->cmd[7] << 8) | scsicmd->cmd[8];
602 	}
603 	asc_logp->status = PACK(asc - asc_softc, 0, 0, asc_debug_cmd);
604 	asc_logp->target = asc->target;
605 	asc_logp->state = 0;
606 	asc_logp->msg = 0xff;
607 	if (++asc_logp >= &asc_log[NLOG])
608 		asc_logp = asc_log;
609 #endif
610 
611 	/*
612 	 * Init the chip and target state.
613 	 */
614 	regs->asc_cmd = ASC_CMD_FLUSH;
615 	state->flags = state->flags & DID_SYNC;
616 	state->error = 0;
617 	state->script = (script_t *)0;
618 	state->msg_out = SCSI_NO_OP;
619 
620 	/*
621 	 * Copy command data to the DMA buffer.
622 	 */
623 	len = scsicmd->cmdlen;
624 	state->dmalen = len;
625 	bcopy(scsicmd->cmd, state->dmaBufAddr, len);
626 
627 	/* check for simple SCSI command with no data transfer */
628 	if ((state->buflen = scsicmd->buflen) == 0) {
629 		/* check for sync negotiation */
630 		if ((scsicmd->flags & SCSICMD_USE_SYNC) &&
631 		    !(state->flags & DID_SYNC)) {
632 			asc->script = &asc_scripts[SCRIPT_TRY_SYNC];
633 			state->flags |= TRY_SYNC;
634 		} else
635 			asc->script = &asc_scripts[SCRIPT_SIMPLE];
636 		state->buf = (char *)0;
637 	} else if (scsicmd->flags & SCSICMD_DATA_TO_DEVICE) {
638 		asc->script = &asc_scripts[SCRIPT_DATA_OUT];
639 		state->buf = scsicmd->buf;
640 		state->flags |= DMA_OUT;
641 	} else {
642 		asc->script = &asc_scripts[SCRIPT_DATA_IN];
643 		state->buf = scsicmd->buf;
644 		state->flags |= DMA_IN;
645 	}
646 
647 	/* preload the FIFO with the message to be sent */
648 	regs->asc_fifo = SCSI_DIS_REC_IDENTIFY;
649 
650 	/* start the asc */
651 	*asc->dmar = ASC_DMAR_WRITE | ASC_DMA_ADDR(state->dmaBufAddr);
652 	ASC_TC_PUT(regs, len);
653 
654 	regs->asc_dbus_id = target;
655 	regs->asc_syn_p = state->sync_period;
656 	regs->asc_syn_o = state->sync_offset;
657 
658 	if (state->flags & TRY_SYNC)
659 		regs->asc_cmd = ASC_CMD_SEL_ATN_STOP;
660 	else
661 		regs->asc_cmd = ASC_CMD_SEL_ATN | ASC_CMD_DMA;
662 }
663 
664 /*
665  * Interrupt routine
666  *	Take interrupts from the chip
667  *
668  * Implementation:
669  *	Move along the current command's script if
670  *	all is well, invoke error handler if not.
671  */
672 void
673 asc_intr(unit)
674 	int unit;
675 {
676 	register asc_softc_t asc = &asc_softc[unit];
677 	register asc_regmap_t *regs = asc->regs;
678 	register State *state;
679 	register script_t *scpt;
680 	register int ss, ir, status;
681 
682 	/* collect ephemeral information */
683 	status = regs->asc_status;
684 again:
685 	ss = regs->asc_ss;
686 	ir = regs->asc_intr;	/* this resets the previous two */
687 	scpt = asc->script;
688 
689 #ifdef DEBUG
690 	asc_logp->status = PACK(unit, status, ss, ir);
691 	asc_logp->target = (asc->state == ASC_STATE_BUSY) ? asc->target : -1;
692 	asc_logp->state = scpt - asc_scripts;
693 	asc_logp->msg = -1;
694 	if (++asc_logp >= &asc_log[NLOG])
695 		asc_logp = asc_log;
696 	if (asc_debug > 2)
697 		printf("asc_intr: status %x ss %x ir %x cond %d:%x\n",
698 			status, ss, ir, scpt - asc_scripts, scpt->condition);
699 #endif
700 
701 	/* check the expected state */
702 	if (SCRIPT_MATCH(ir, status) == scpt->condition) {
703 		/*
704 		 * Perform the appropriate operation, then proceed.
705 		 */
706 		if ((*scpt->action)(asc, status, ss, ir)) {
707 			regs->asc_cmd = scpt->command;
708 			asc->script = scpt->next;
709 		}
710 		goto done;
711 	}
712 
713 	/* check for message in or out */
714 	if ((ir & ~ASC_INT_FC) == ASC_INT_BS) {
715 		register int len, fifo;
716 
717 		state = &asc->st[asc->target];
718 		switch (ASC_PHASE(status)) {
719 		case ASC_PHASE_DATAI:
720 		case ASC_PHASE_DATAO:
721 			ASC_TC_GET(regs, len);
722 			fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT;
723 			printf("asc_intr: data overrun: buflen %d dmalen %d tc %d fifo %d\n",
724 				state->buflen, state->dmalen, len, fifo);
725 			goto abort;
726 
727 		case ASC_PHASE_MSG_IN:
728 			break;
729 
730 		case ASC_PHASE_MSG_OUT:
731 			regs->asc_fifo = state->msg_out;
732 			state->msg_out = SCSI_NO_OP;
733 			regs->asc_cmd = ASC_CMD_XFER_INFO;
734 			goto done;
735 
736 		case ASC_PHASE_STATUS:
737 			/* probably an error in the SCSI command */
738 			asc->script = &asc_scripts[SCRIPT_GET_STATUS];
739 			regs->asc_cmd = ASC_CMD_I_COMPLETE;
740 			goto done;
741 
742 		default:
743 			goto abort;
744 		}
745 
746 		if (state->script)
747 			goto abort;
748 
749 		/* check for DMA in progress */
750 		ASC_TC_GET(regs, len);
751 		fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT;
752 		/* flush any data in the FIFO */
753 		if (fifo) {
754 			if (state->flags & DMA_OUT)
755 				len += fifo;
756 			else if (state->flags & DMA_IN) {
757 				u_char *cp;
758 
759 				printf("asc_intr: IN: dmalen %d len %d fifo %d\n",
760 					state->dmalen, len, fifo); /* XXX */
761 				len += fifo;
762 				cp = state->dmaBufAddr + (state->dmalen - len);
763 				while (fifo-- > 0)
764 					*cp++ = regs->asc_fifo;
765 			} else
766 				printf("asc_intr: dmalen %d len %d fifo %d\n",
767 					state->dmalen, len, fifo); /* XXX */
768 			regs->asc_cmd = ASC_CMD_FLUSH;
769 			MachEmptyWriteBuffer();
770 		}
771 		if (len) {
772 			/* save number of bytes still to be sent or received */
773 			state->dmaresid = len;
774 			/* setup state to resume to */
775 			if (state->flags & DMA_IN)
776 				state->script =
777 					&asc_scripts[SCRIPT_RESUME_DMA_IN];
778 			else if (state->flags & DMA_OUT)
779 				state->script =
780 					&asc_scripts[SCRIPT_RESUME_DMA_OUT];
781 			else
782 				state->script = asc->script;
783 		} else {
784 			/* setup state to resume to */
785 			if (state->flags & DMA_IN) {
786 				if (state->flags & DMA_IN_PROGRESS) {
787 					state->flags &= ~DMA_IN_PROGRESS;
788 					len = state->dmalen;
789 					bcopy(state->dmaBufAddr, state->buf,
790 						len);
791 					state->buf += len;
792 					state->buflen -= len;
793 				}
794 				if (state->buflen)
795 					state->script =
796 					    &asc_scripts[SCRIPT_RESUME_IN];
797 				else
798 					state->script =
799 					    &asc_scripts[SCRIPT_RESUME_NO_DATA];
800 			} else if (state->flags & DMA_OUT) {
801 				/*
802 				 * If this is the last chunk, the next expected
803 				 * state is to get status.
804 				 */
805 				if (state->flags & DMA_IN_PROGRESS) {
806 					state->flags &= ~DMA_IN_PROGRESS;
807 					len = state->dmalen;
808 					state->buf += len;
809 					state->buflen -= len;
810 				}
811 				if (state->buflen)
812 					state->script =
813 					    &asc_scripts[SCRIPT_RESUME_OUT];
814 				else
815 					state->script =
816 					    &asc_scripts[SCRIPT_RESUME_NO_DATA];
817 			} else if (asc->script == &asc_scripts[SCRIPT_SIMPLE])
818 				state->script =
819 					    &asc_scripts[SCRIPT_RESUME_NO_DATA];
820 			else
821 				state->script = asc->script;
822 		}
823 
824 		/* setup to receive a message */
825 		asc->script = &asc_scripts[SCRIPT_MSG_IN];
826 		state->msglen = 0;
827 		regs->asc_cmd = ASC_CMD_XFER_INFO;
828 		goto done;
829 	}
830 
831 	/* check for SCSI bus reset */
832 	if (ir & ASC_INT_RESET) {
833 		register int i;
834 
835 		printf("asc%d: SCSI bus reset!!\n", asc - asc_softc);
836 		/* need to flush any pending commands */
837 		for (i = 0; i < ASC_NCMD; i++) {
838 			if (!asc->cmd[i])
839 				continue;
840 			asc->st[i].error = EIO;
841 			asc_end(asc, 0, 0, 0);
842 		}
843 		/* rearbitrate synchronous offset */
844 		for (i = 0; i < ASC_NCMD; i++) {
845 			asc->st[i].sync_offset = 0;
846 			asc->st[i].flags = 0;
847 		}
848 		asc->target = -1;
849 		return;
850 	}
851 
852 	/* check for command errors */
853 	if (ir & ASC_INT_ILL)
854 		goto abort;
855 
856 	/* check for disconnect */
857 	if (ir & ASC_INT_DISC) {
858 		state = &asc->st[asc->target];
859 		switch (ASC_SS(ss)) {
860 		case 0: /* device did not respond */
861 			state->error = ENXIO;
862 			asc_end(asc, status, ss, ir);
863 			return;
864 
865 		default:
866 			goto abort;
867 		}
868 	}
869 
870 	/* check for reselect */
871 	if (ir & ASC_INT_RESEL) {
872 		unsigned fifo, id, msg;
873 
874 		fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT;
875 		if (fifo < 2)
876 			goto abort;
877 		/* read unencoded SCSI ID and convert to binary */
878 		msg = regs->asc_fifo & asc->myidmask;
879 		for (id = 0; (msg & 1) == 0; id++)
880 			msg >>= 1;
881 		/* read identify message */
882 		msg = regs->asc_fifo;
883 #ifdef DEBUG
884 		if (asc_logp == asc_log)
885 			asc_log[NLOG - 1].msg = msg;
886 		else
887 			asc_logp[-1].msg = msg;
888 #endif
889 		if (asc->state != ASC_STATE_RESEL)
890 			goto abort;
891 		asc->state = ASC_STATE_BUSY;
892 		asc->target = id;
893 		state = &asc->st[id];
894 		asc->script = state->script;
895 		state->script = (script_t *)0;
896 		if (!(state->flags & DISCONN))
897 			goto abort;
898 		state->flags &= ~DISCONN;
899 		regs->asc_syn_p = state->sync_period;
900 		regs->asc_syn_o = state->sync_offset;
901 		regs->asc_cmd = ASC_CMD_MSG_ACPT;
902 		goto done;
903 	}
904 
905 	/* check if we are being selected as a target */
906 	if (ir & (ASC_INT_SEL | ASC_INT_SEL_ATN))
907 		goto abort;
908 
909 	/* must be just a ASC_INT_FC */
910 done:
911 	MachEmptyWriteBuffer();
912 	/* watch out for HW race conditions and setup & hold time violations */
913 	ir = regs->asc_status;
914 	while (ir != (status = regs->asc_status))
915 		ir = status;
916 	if (status & ASC_CSR_INT)
917 		goto again;
918 	return;
919 
920 abort:
921 #ifdef DEBUG
922 	asc_DumpLog("asc_intr");
923 #endif
924 #if 0
925 	panic("asc_intr");
926 #else
927 	for (;;);
928 #endif
929 }
930 
931 /*
932  * All the many little things that the interrupt
933  * routine might switch to.
934  */
935 
936 /* ARGSUSED */
937 static int
938 script_nop(asc, status, ss, ir)
939 	register asc_softc_t asc;
940 	register int status, ss, ir;
941 {
942 	return (1);
943 }
944 
945 /* ARGSUSED */
946 static int
947 asc_get_status(asc, status, ss, ir)
948 	register asc_softc_t asc;
949 	register int status, ss, ir;
950 {
951 	register asc_regmap_t *regs = asc->regs;
952 	register int data;
953 
954 	/*
955 	 * Get the last two bytes in the FIFO.
956 	 */
957 	if ((data = regs->asc_flags & ASC_FLAGS_FIFO_CNT) != 2) {
958 		printf("asc_get_status: fifo cnt %d\n", data); /* XXX */
959 		if (data < 2) {
960 			asc->regs->asc_cmd = ASC_CMD_MSG_ACPT;
961 			return (0);
962 		}
963 		do {
964 			data = regs->asc_fifo;
965 		} while ((regs->asc_flags & ASC_FLAGS_FIFO_CNT) > 2);
966 	}
967 
968 	/* save the status byte */
969 	asc->st[asc->target].statusByte = data = regs->asc_fifo;
970 #ifdef DEBUG
971 	if (asc_logp == asc_log)
972 		asc_log[NLOG - 1].msg = data;
973 	else
974 		asc_logp[-1].msg = data;
975 #endif
976 
977 	/* get the (presumed) command_complete message */
978 	if ((data = regs->asc_fifo) == SCSI_COMMAND_COMPLETE)
979 		return (1);
980 
981 #ifdef DEBUG
982 	printf("asc_get_status: status %x cmd %x\n",
983 		asc->st[asc->target].statusByte, data);
984 	asc_DumpLog("asc_get_status");
985 #endif
986 	return (0);
987 }
988 
989 /* ARGSUSED */
990 static int
991 asc_end(asc, status, ss, ir)
992 	register asc_softc_t asc;
993 	register int status, ss, ir;
994 {
995 	register ScsiCmd *scsicmd;
996 	register State *state;
997 	register int i, target;
998 
999 	asc->state = ASC_STATE_IDLE;
1000 	target = asc->target;
1001 	asc->target = -1;
1002 	scsicmd = asc->cmd[target];
1003 	asc->cmd[target] = (ScsiCmd *)0;
1004 	state = &asc->st[target];
1005 
1006 #ifdef DEBUG
1007 	if (asc_debug > 1) {
1008 		printf("asc_end: %s target %d cmd %x err %d resid %d\n",
1009 			scsicmd->sd->sd_driver->d_name, target,
1010 			scsicmd->cmd[0], state->error, state->buflen);
1011 	}
1012 #endif
1013 #ifdef DIAGNOSTIC
1014 	if (target < 0 || !scsicmd)
1015 		panic("asc_end");
1016 #endif
1017 
1018 	/* look for disconnected devices */
1019 	for (i = 0; i < ASC_NCMD; i++) {
1020 		if (!asc->cmd[i] || !(asc->st[i].flags & DISCONN))
1021 			continue;
1022 		asc->regs->asc_cmd = ASC_CMD_ENABLE_SEL;
1023 		asc->state = ASC_STATE_RESEL;
1024 		asc->script = &asc_scripts[SCRIPT_RESEL];
1025 		break;
1026 	}
1027 
1028 	/* look for another device that is ready */
1029 	for (i = 0; i < ASC_NCMD; i++) {
1030 		/* don't restart a disconnected command */
1031 		if (!asc->cmd[i] || (asc->st[i].flags & DISCONN))
1032 			continue;
1033 		asc_startcmd(asc, i);
1034 		break;
1035 	}
1036 
1037 	/* signal device driver that the command is done */
1038 	(*scsicmd->sd->sd_driver->d_done)(scsicmd->unit, state->error,
1039 		state->buflen, state->statusByte);
1040 
1041 	return (0);
1042 }
1043 
1044 /* ARGSUSED */
1045 static int
1046 asc_dma_in(asc, status, ss, ir)
1047 	register asc_softc_t asc;
1048 	register int status, ss, ir;
1049 {
1050 	register asc_regmap_t *regs = asc->regs;
1051 	register State *state = &asc->st[asc->target];
1052 	register int len;
1053 
1054 	/* check for previous chunk in buffer */
1055 	if (state->flags & DMA_IN_PROGRESS) {
1056 		/*
1057 		 * Only count bytes that have been copied to memory.
1058 		 * There may be some bytes in the FIFO if synchonous transfers
1059 		 * are in progress.
1060 		 */
1061 		ASC_TC_GET(regs, len);
1062 		len = state->dmalen - len;
1063 		bcopy(state->dmaBufAddr, state->buf, len);
1064 		state->buf += len;
1065 		state->buflen -= len;
1066 	}
1067 
1068 	/* setup to start reading the next chunk */
1069 	len = state->buflen;
1070 	if (len > state->dmaBufSize)
1071 		len = state->dmaBufSize;
1072 	state->dmalen = len;
1073 	*asc->dmar = ASC_DMA_ADDR(state->dmaBufAddr);
1074 	ASC_TC_PUT(regs, len);
1075 #ifdef DEBUG
1076 	if (asc_debug > 2)
1077 		printf("asc_dma_in: buflen %d, len %d\n", state->buflen, len);
1078 #endif
1079 
1080 	/* check for next chunk */
1081 	state->flags |= DMA_IN_PROGRESS;
1082 	if (len != state->buflen) {
1083 		regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA;
1084 		asc->script = &asc_scripts[SCRIPT_CONTINUE_IN];
1085 		return (0);
1086 	}
1087 	return (1);
1088 }
1089 
1090 /* ARGSUSED */
1091 static int
1092 asc_last_dma_in(asc, status, ss, ir)
1093 	register asc_softc_t asc;
1094 	register int status, ss, ir;
1095 {
1096 	register asc_regmap_t *regs = asc->regs;
1097 	register State *state = &asc->st[asc->target];
1098 	register int len, fifo;
1099 
1100 	/* copy data from buffer to main memory */
1101 	ASC_TC_GET(regs, len);
1102 	fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT;
1103 #ifdef DEBUG
1104 	if (asc_debug > 2)
1105 		printf("asc_last_dma_in: buflen %d dmalen %d tc %d fifo %d\n",
1106 			state->buflen, state->dmalen, len, fifo);
1107 #endif
1108 	if (fifo) {
1109 		/* device must be trying to send more than we expect */
1110 		regs->asc_cmd = ASC_CMD_FLUSH;
1111 		MachEmptyWriteBuffer();
1112 	}
1113 	state->flags &= ~DMA_IN_PROGRESS;
1114 	len = state->dmalen - len;
1115 	state->buflen -= len;
1116 	bcopy(state->dmaBufAddr, state->buf, len);
1117 
1118 	return (1);
1119 }
1120 
1121 /* ARGSUSED */
1122 static int
1123 asc_resume_in(asc, status, ss, ir)
1124 	register asc_softc_t asc;
1125 	register int status, ss, ir;
1126 {
1127 	register asc_regmap_t *regs = asc->regs;
1128 	register State *state = &asc->st[asc->target];
1129 	register int len;
1130 
1131 	/* setup to start reading the next chunk */
1132 	len = state->buflen;
1133 	if (len > state->dmaBufSize)
1134 		len = state->dmaBufSize;
1135 	state->dmalen = len;
1136 	*asc->dmar = ASC_DMA_ADDR(state->dmaBufAddr);
1137 	ASC_TC_PUT(regs, len);
1138 #ifdef DEBUG
1139 	if (asc_debug > 2)
1140 		printf("asc_resume_in: buflen %d, len %d\n", state->buflen,
1141 			len);
1142 #endif
1143 
1144 	/* check for next chunk */
1145 	state->flags |= DMA_IN_PROGRESS;
1146 	if (len != state->buflen) {
1147 		regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA;
1148 		asc->script = &asc_scripts[SCRIPT_CONTINUE_IN];
1149 		return (0);
1150 	}
1151 	return (1);
1152 }
1153 
1154 /* ARGSUSED */
1155 static int
1156 asc_resume_dma_in(asc, status, ss, ir)
1157 	register asc_softc_t asc;
1158 	register int status, ss, ir;
1159 {
1160 	register asc_regmap_t *regs = asc->regs;
1161 	register State *state = &asc->st[asc->target];
1162 	register int len, off;
1163 
1164 	/* setup to finish reading the current chunk */
1165 	len = state->dmaresid;
1166 	off = state->dmalen - len;
1167 	if ((off & 1) && state->sync_offset) {
1168 		printf("asc_resume_dma_in: odd xfer dmalen %d len %d off %d\n",
1169 			state->dmalen, len, off); /* XXX */
1170 		regs->asc_res_fifo = state->dmaBufAddr[off];
1171 	}
1172 	*asc->dmar = ASC_DMA_ADDR(state->dmaBufAddr + off);
1173 	ASC_TC_PUT(regs, len);
1174 #ifdef DEBUG
1175 	if (asc_debug > 2)
1176 		printf("asc_resume_dma_in: buflen %d dmalen %d len %d off %d\n",
1177 			state->dmalen, state->buflen, len, off);
1178 #endif
1179 
1180 	/* check for next chunk */
1181 	state->flags |= DMA_IN_PROGRESS;
1182 	if (state->dmalen != state->buflen) {
1183 		regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA;
1184 		asc->script = &asc_scripts[SCRIPT_CONTINUE_IN];
1185 		return (0);
1186 	}
1187 	return (1);
1188 }
1189 
1190 /* ARGSUSED */
1191 static int
1192 asc_dma_out(asc, status, ss, ir)
1193 	register asc_softc_t asc;
1194 	register int status, ss, ir;
1195 {
1196 	register asc_regmap_t *regs = asc->regs;
1197 	register State *state = &asc->st[asc->target];
1198 	register int len, fifo;
1199 
1200 	if (state->flags & DMA_IN_PROGRESS) {
1201 		/* check to be sure previous chunk was finished */
1202 		ASC_TC_GET(regs, len);
1203 		fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT;
1204 		if (len || fifo)
1205 			printf("asc_dma_out: buflen %d dmalen %d tc %d fifo %d\n",
1206 				state->buflen, state->dmalen, len, fifo); /* XXX */
1207 		len += fifo;
1208 		len = state->dmalen - len;
1209 		state->buf += len;
1210 		state->buflen -= len;
1211 	}
1212 
1213 	/* setup for this chunck */
1214 	len = state->buflen;
1215 	if (len > state->dmaBufSize)
1216 		len = state->dmaBufSize;
1217 	state->dmalen = len;
1218 	bcopy(state->buf, state->dmaBufAddr, len);
1219 	*asc->dmar = ASC_DMAR_WRITE | ASC_DMA_ADDR(state->dmaBufAddr);
1220 	ASC_TC_PUT(regs, len);
1221 #ifdef DEBUG
1222 	if (asc_debug > 2)
1223 		printf("asc_dma_out: buflen %d, len %d\n", state->buflen, len);
1224 #endif
1225 
1226 	/* check for next chunk */
1227 	state->flags |= DMA_IN_PROGRESS;
1228 	if (len != state->buflen) {
1229 		regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA;
1230 		asc->script = &asc_scripts[SCRIPT_CONTINUE_OUT];
1231 		return (0);
1232 	}
1233 	return (1);
1234 }
1235 
1236 /* ARGSUSED */
1237 static int
1238 asc_last_dma_out(asc, status, ss, ir)
1239 	register asc_softc_t asc;
1240 	register int status, ss, ir;
1241 {
1242 	register asc_regmap_t *regs = asc->regs;
1243 	register State *state = &asc->st[asc->target];
1244 	register int len, fifo;
1245 
1246 	ASC_TC_GET(regs, len);
1247 	fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT;
1248 #ifdef DEBUG
1249 	if (asc_debug > 2)
1250 		printf("asc_last_dma_out: buflen %d dmalen %d tc %d fifo %d\n",
1251 			state->buflen, state->dmalen, len, fifo);
1252 #endif
1253 	if (fifo) {
1254 		len += fifo;
1255 		regs->asc_cmd = ASC_CMD_FLUSH;
1256 		MachEmptyWriteBuffer();
1257 	}
1258 	state->flags &= ~DMA_IN_PROGRESS;
1259 	len = state->dmalen - len;
1260 	state->buflen -= len;
1261 	return (1);
1262 }
1263 
1264 /* ARGSUSED */
1265 static int
1266 asc_resume_out(asc, status, ss, ir)
1267 	register asc_softc_t asc;
1268 	register int status, ss, ir;
1269 {
1270 	register asc_regmap_t *regs = asc->regs;
1271 	register State *state = &asc->st[asc->target];
1272 	register int len;
1273 
1274 	/* setup for this chunck */
1275 	len = state->buflen;
1276 	if (len > state->dmaBufSize)
1277 		len = state->dmaBufSize;
1278 	state->dmalen = len;
1279 	bcopy(state->buf, state->dmaBufAddr, len);
1280 	*asc->dmar = ASC_DMAR_WRITE | ASC_DMA_ADDR(state->dmaBufAddr);
1281 	ASC_TC_PUT(regs, len);
1282 #ifdef DEBUG
1283 	if (asc_debug > 2)
1284 		printf("asc_resume_out: buflen %d, len %d\n", state->buflen,
1285 			len);
1286 #endif
1287 
1288 	/* check for next chunk */
1289 	state->flags |= DMA_IN_PROGRESS;
1290 	if (len != state->buflen) {
1291 		regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA;
1292 		asc->script = &asc_scripts[SCRIPT_CONTINUE_OUT];
1293 		return (0);
1294 	}
1295 	return (1);
1296 }
1297 
1298 /* ARGSUSED */
1299 static int
1300 asc_resume_dma_out(asc, status, ss, ir)
1301 	register asc_softc_t asc;
1302 	register int status, ss, ir;
1303 {
1304 	register asc_regmap_t *regs = asc->regs;
1305 	register State *state = &asc->st[asc->target];
1306 	register int len, off;
1307 
1308 	/* setup to finish writing this chunk */
1309 	len = state->dmaresid;
1310 	off = state->dmalen - len;
1311 	if (off & 1) {
1312 		printf("asc_resume_dma_out: odd xfer dmalen %d len %d off %d\n",
1313 			state->dmalen, len, off); /* XXX */
1314 		regs->asc_fifo = state->dmaBufAddr[off];
1315 		off++;
1316 		len--;
1317 	}
1318 	*asc->dmar = ASC_DMAR_WRITE | ASC_DMA_ADDR(state->dmaBufAddr + off);
1319 	ASC_TC_PUT(regs, len);
1320 #ifdef DEBUG
1321 	if (asc_debug > 2)
1322 		printf("asc_resume_dma_out: buflen %d dmalen %d len %d off %d\n",
1323 			state->dmalen, state->buflen, len, off);
1324 #endif
1325 
1326 	/* check for next chunk */
1327 	state->flags |= DMA_IN_PROGRESS;
1328 	if (state->dmalen != state->buflen) {
1329 		regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA;
1330 		asc->script = &asc_scripts[SCRIPT_CONTINUE_OUT];
1331 		return (0);
1332 	}
1333 	return (1);
1334 }
1335 
1336 /* ARGSUSED */
1337 static int
1338 asc_sendsync(asc, status, ss, ir)
1339 	register asc_softc_t asc;
1340 	register int status, ss, ir;
1341 {
1342 	register asc_regmap_t *regs = asc->regs;
1343 	register State *state = &asc->st[asc->target];
1344 
1345 	/* send the extended synchronous negotiation message */
1346 	regs->asc_fifo = SCSI_EXTENDED_MSG;
1347 	MachEmptyWriteBuffer();
1348 	regs->asc_fifo = 3;
1349 	MachEmptyWriteBuffer();
1350 	regs->asc_fifo = SCSI_SYNCHRONOUS_XFER;
1351 	MachEmptyWriteBuffer();
1352 	regs->asc_fifo = SCSI_MIN_PERIOD;
1353 	MachEmptyWriteBuffer();
1354 	regs->asc_fifo = ASC_MAX_OFFSET;
1355 	/* state to resume after we see the sync reply message */
1356 	state->script = asc->script + 2;
1357 	state->msglen = 0;
1358 	return (1);
1359 }
1360 
1361 /* ARGSUSED */
1362 static int
1363 asc_replysync(asc, status, ss, ir)
1364 	register asc_softc_t asc;
1365 	register int status, ss, ir;
1366 {
1367 	register asc_regmap_t *regs = asc->regs;
1368 	register State *state = &asc->st[asc->target];
1369 
1370 #ifdef DEBUG
1371 	if (asc_debug > 2)
1372 		printf("asc_replysync: %x %x\n",
1373 			asc_to_scsi_period[state->sync_period],
1374 			state->sync_offset);
1375 #endif
1376 	/* send synchronous transfer in response to a request */
1377 	regs->asc_fifo = SCSI_EXTENDED_MSG;
1378 	MachEmptyWriteBuffer();
1379 	regs->asc_fifo = 3;
1380 	MachEmptyWriteBuffer();
1381 	regs->asc_fifo = SCSI_SYNCHRONOUS_XFER;
1382 	MachEmptyWriteBuffer();
1383 	regs->asc_fifo = asc_to_scsi_period[state->sync_period];
1384 	MachEmptyWriteBuffer();
1385 	regs->asc_fifo = state->sync_offset;
1386 	regs->asc_cmd = ASC_CMD_XFER_INFO;
1387 
1388 	/* return to the appropriate script */
1389 	if (!state->script) {
1390 #ifdef DEBUG
1391 		asc_DumpLog("asc_replsync");
1392 #endif
1393 		panic("asc_replysync");
1394 	}
1395 	asc->script = state->script;
1396 	state->script = (script_t *)0;
1397 	return (0);
1398 }
1399 
1400 /* ARGSUSED */
1401 static int
1402 asc_msg_in(asc, status, ss, ir)
1403 	register asc_softc_t asc;
1404 	register int status, ss, ir;
1405 {
1406 	register asc_regmap_t *regs = asc->regs;
1407 	register State *state = &asc->st[asc->target];
1408 	register int msg;
1409 	int i;
1410 
1411 	/* read one message byte */
1412 	msg = regs->asc_fifo;
1413 #ifdef DEBUG
1414 	if (asc_logp == asc_log)
1415 		asc_log[NLOG - 1].msg = msg;
1416 	else
1417 		asc_logp[-1].msg = msg;
1418 #endif
1419 
1420 	/* check for multi-byte message */
1421 	if (state->msglen != 0) {
1422 		/* first byte is the message length */
1423 		if (state->msglen < 0) {
1424 			state->msglen = msg;
1425 			return (1);
1426 		}
1427 		if (state->msgcnt >= state->msglen)
1428 			goto abort;
1429 		state->msg_in[state->msgcnt++] = msg;
1430 
1431 		/* did we just read the last byte of the message? */
1432 		if (state->msgcnt != state->msglen)
1433 			return (1);
1434 
1435 		/* process an extended message */
1436 #ifdef DEBUG
1437 		if (asc_debug > 2)
1438 			printf("asc_msg_in: msg %x %x %x\n",
1439 				state->msg_in[0],
1440 				state->msg_in[1],
1441 				state->msg_in[2]);
1442 #endif
1443 		switch (state->msg_in[0]) {
1444 		case SCSI_SYNCHRONOUS_XFER:
1445 			state->flags |= DID_SYNC;
1446 			state->sync_offset = state->msg_in[2];
1447 
1448 			/* convert SCSI period to ASC period */
1449 			i = state->msg_in[1] / 10;
1450 			if (i < ASC_MIN_PERIOD)
1451 				i = ASC_MIN_PERIOD;
1452 			else if (i >= ASC_MAX_PERIOD) {
1453 				/* can't do sync transfer, period too long */
1454 				printf("asc%d: SCSI device %d: sync xfer period too long (%d)\n",
1455 					asc - asc_softc, asc->target, i);
1456 				i = ASC_MAX_PERIOD;
1457 				state->sync_offset = 0;
1458 			}
1459 			if ((i * 10) != state->msg_in[1])
1460 				i++;
1461 			state->sync_period = i & 0x1F;
1462 
1463 			/*
1464 			 * If this is a request, check minimums and
1465 			 * send back an acknowledge.
1466 			 */
1467 			if (!(state->flags & TRY_SYNC)) {
1468 				regs->asc_cmd = ASC_CMD_SET_ATN;
1469 				MachEmptyWriteBuffer();
1470 
1471 				if (state->sync_period < ASC_MIN_PERIOD)
1472 					state->sync_period =
1473 						ASC_MIN_PERIOD;
1474 				if (state->sync_offset > ASC_MAX_OFFSET)
1475 					state->sync_offset =
1476 						ASC_MAX_OFFSET;
1477 				asc->script = &asc_scripts[SCRIPT_REPLY_SYNC];
1478 				regs->asc_syn_p = state->sync_period;
1479 				regs->asc_syn_o = state->sync_offset;
1480 				regs->asc_cmd = ASC_CMD_MSG_ACPT;
1481 				return (0);
1482 			}
1483 
1484 			regs->asc_syn_p = state->sync_period;
1485 			regs->asc_syn_o = state->sync_offset;
1486 			goto done;
1487 
1488 		default:
1489 			printf("asc%d: SCSI device %d: rejecting extended message 0x%x\n",
1490 				asc - asc_softc, asc->target,
1491 				state->msg_in[0]);
1492 			goto reject;
1493 		}
1494 	}
1495 
1496 	/* process first byte of a message */
1497 #ifdef DEBUG
1498 	if (asc_debug > 2)
1499 		printf("asc_msg_in: msg %x\n", msg);
1500 #endif
1501 	switch (msg) {
1502 #if 0
1503 	case SCSI_MESSAGE_REJECT:
1504 		printf(" did not like SYNCH xfer "); /* XXX */
1505 		state->flags |= DID_SYNC;
1506 		regs->asc_cmd = ASC_CMD_MSG_ACPT;
1507 		status = asc_wait(regs, ASC_CSR_INT);
1508 		ir = regs->asc_intr;
1509 		/* some just break out here, some dont */
1510 		if (ASC_PHASE(status) == ASC_PHASE_MSG_OUT) {
1511 			regs->asc_fifo = SCSI_ABORT;
1512 			regs->asc_cmd = ASC_CMD_XFER_INFO;
1513 			status = asc_wait(regs, ASC_CSR_INT);
1514 			ir = regs->asc_intr;
1515 		}
1516 		if (ir & ASC_INT_DISC) {
1517 			asc_end(asc, status, 0, ir);
1518 			return (0);
1519 		}
1520 		goto status;
1521 #endif
1522 
1523 	case SCSI_EXTENDED_MSG: /* read an extended message */
1524 		/* setup to read message length next */
1525 		state->msglen = -1;
1526 		state->msgcnt = 0;
1527 		return (1);
1528 
1529 	case SCSI_NO_OP:
1530 		break;
1531 
1532 	case SCSI_SAVE_DATA_POINTER:
1533 		/* expect another message */
1534 		return (1);
1535 
1536 	case SCSI_RESTORE_POINTERS:
1537 		/*
1538 		 * Need to do the following if resuming synchonous data in
1539 		 * on an odd byte boundary.
1540 		regs->asc_cnfg2 |= ASC_CNFG2_RFB;
1541 		 */
1542 		break;
1543 
1544 	case SCSI_DISCONNECT:
1545 		if (state->flags & DISCONN)
1546 			goto abort;
1547 		state->flags |= DISCONN;
1548 		regs->asc_cmd = ASC_CMD_MSG_ACPT;
1549 		asc->script = &asc_scripts[SCRIPT_DISCONNECT];
1550 		return (0);
1551 
1552 	default:
1553 		printf("asc%d: SCSI device %d: rejecting message 0x%x\n",
1554 			asc - asc_softc, asc->target, msg);
1555 	reject:
1556 		/* request a message out before acknowledging this message */
1557 		state->msg_out = SCSI_MESSAGE_REJECT;
1558 		regs->asc_cmd = ASC_CMD_SET_ATN;
1559 		MachEmptyWriteBuffer();
1560 	}
1561 
1562 done:
1563 	/* return to original script */
1564 	regs->asc_cmd = ASC_CMD_MSG_ACPT;
1565 	if (!state->script) {
1566 	abort:
1567 #ifdef DEBUG
1568 		asc_DumpLog("asc_msg_in");
1569 #endif
1570 		panic("asc_msg_in");
1571 	}
1572 	asc->script = state->script;
1573 	state->script = (script_t *)0;
1574 	return (0);
1575 }
1576 
1577 /* ARGSUSED */
1578 static int
1579 asc_disconnect(asc, status, ss, ir)
1580 	register asc_softc_t asc;
1581 	register int status, ss, ir;
1582 {
1583 	register State *state = &asc->st[asc->target];
1584 
1585 	asc->target = -1;
1586 	asc->state = ASC_STATE_RESEL;
1587 	return (1);
1588 }
1589 
1590 #ifdef DEBUG
1591 asc_DumpLog(str)
1592 	char *str;
1593 {
1594 	register struct asc_log *lp;
1595 	register u_int status;
1596 
1597 	printf("asc: %s: cmd %x bn %d cnt %d\n", str, asc_debug_cmd,
1598 		asc_debug_bn, asc_debug_sz);
1599 	lp = asc_logp + 1;
1600 	if (lp > &asc_log[NLOG])
1601 		lp = asc_log;
1602 	while (lp != asc_logp) {
1603 		status = lp->status;
1604 		printf("asc%d tgt %d status %x ss %x ir %x cond %d:%x msg %x\n",
1605 			status >> 24,
1606 			lp->target,
1607 			(status >> 16) & 0xFF,
1608 			(status >> 8) & 0xFF,
1609 			status & 0XFF,
1610 			lp->state,
1611 			asc_scripts[lp->state].condition,
1612 			lp->msg);
1613 		if (++lp >= &asc_log[NLOG])
1614 			lp = asc_log;
1615 	}
1616 }
1617 #endif
1618 
1619 #endif	/* NASC > 0 */
1620