xref: /csrg-svn/sys/pmax/dev/asc.c (revision 56525)
152889Sbostic /*-
252889Sbostic  * Copyright (c) 1992 The Regents of the University of California.
352889Sbostic  * All rights reserved.
452889Sbostic  *
552889Sbostic  * This code is derived from software contributed to Berkeley by
652889Sbostic  * Ralph Campbell.
752889Sbostic  *
852889Sbostic  * %sccs.include.redist.c%
952889Sbostic  *
10*56525Sbostic  *	@(#)asc.c	7.6 (Berkeley) 10/11/92
1152889Sbostic  */
1252889Sbostic 
1352889Sbostic /*
1452889Sbostic  * Mach Operating System
1552889Sbostic  * Copyright (c) 1991,1990,1989 Carnegie Mellon University
1652889Sbostic  * All Rights Reserved.
1752889Sbostic  *
1852889Sbostic  * Permission to use, copy, modify and distribute this software and its
1952889Sbostic  * documentation is hereby granted, provided that both the copyright
2052889Sbostic  * notice and this permission notice appear in all copies of the
2152889Sbostic  * software, derivative works or modified versions, and any portions
2252889Sbostic  * thereof, and that both notices appear in supporting documentation.
2352889Sbostic  *
2452889Sbostic  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
2552889Sbostic  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
2652889Sbostic  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
2752889Sbostic  *
2852889Sbostic  * Carnegie Mellon requests users of this software to return to
2952889Sbostic  *
3052889Sbostic  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
3152889Sbostic  *  School of Computer Science
3252889Sbostic  *  Carnegie Mellon University
3352889Sbostic  *  Pittsburgh PA 15213-3890
3452889Sbostic  *
3552889Sbostic  * any improvements or extensions that they make and grant Carnegie the
3652889Sbostic  * rights to redistribute these changes.
3752889Sbostic  */
3852889Sbostic 
3952889Sbostic /*
4052889Sbostic  * HISTORY
4152889Sbostic  * $Log:	scsi_53C94_hdw.c,v $
4252889Sbostic  * Revision 2.5  91/02/05  17:45:07  mrt
4352889Sbostic  * 	Added author notices
4452889Sbostic  * 	[91/02/04  11:18:43  mrt]
4552889Sbostic  *
4652889Sbostic  * 	Changed to use new Mach copyright
4752889Sbostic  * 	[91/02/02  12:17:20  mrt]
4852889Sbostic  *
4952889Sbostic  * Revision 2.4  91/01/08  15:48:24  rpd
5052889Sbostic  * 	Added continuation argument to thread_block.
5152889Sbostic  * 	[90/12/27            rpd]
5252889Sbostic  *
5352889Sbostic  * Revision 2.3  90/12/05  23:34:48  af
5452889Sbostic  * 	Recovered from pmax merge.. and from the destruction of a disk.
5552889Sbostic  * 	[90/12/03  23:40:40  af]
5652889Sbostic  *
5752889Sbostic  * Revision 2.1.1.1  90/11/01  03:39:09  af
5852889Sbostic  * 	Created, from the DEC specs:
5952889Sbostic  * 	"PMAZ-AA TURBOchannel SCSI Module Functional Specification"
6052889Sbostic  * 	Workstation Systems Engineering, Palo Alto, CA. Aug 27, 1990.
6152889Sbostic  * 	And from the NCR data sheets
6252889Sbostic  * 	"NCR 53C94, 53C95, 53C96 Advances SCSI Controller"
6352889Sbostic  * 	[90/09/03            af]
6452889Sbostic  */
6552889Sbostic 
6652889Sbostic /*
6752889Sbostic  *	File: scsi_53C94_hdw.h
6852889Sbostic  * 	Author: Alessandro Forin, Carnegie Mellon University
6952889Sbostic  *	Date:	9/90
7052889Sbostic  *
7152889Sbostic  *	Bottom layer of the SCSI driver: chip-dependent functions
7252889Sbostic  *
7352889Sbostic  *	This file contains the code that is specific to the NCR 53C94
7452889Sbostic  *	SCSI chip (Host Bus Adapter in SCSI parlance): probing, start
7552889Sbostic  *	operation, and interrupt routine.
7652889Sbostic  */
7752889Sbostic 
7852889Sbostic /*
7952889Sbostic  * This layer works based on small simple 'scripts' that are installed
8052889Sbostic  * at the start of the command and drive the chip to completion.
8152889Sbostic  * The idea comes from the specs of the NCR 53C700 'script' processor.
8252889Sbostic  *
8352889Sbostic  * There are various reasons for this, mainly
8452889Sbostic  * - Performance: identify the common (successful) path, and follow it;
8552889Sbostic  *   at interrupt time no code is needed to find the current status
8652889Sbostic  * - Code size: it should be easy to compact common operations
8752889Sbostic  * - Adaptability: the code skeleton should adapt to different chips without
8852889Sbostic  *   terrible complications.
8952889Sbostic  * - Error handling: and it is easy to modify the actions performed
9052889Sbostic  *   by the scripts to cope with strange but well identified sequences
9152889Sbostic  *
9252889Sbostic  */
9352889Sbostic 
9452889Sbostic #include "asc.h"
9552889Sbostic #if NASC > 0
9652889Sbostic 
9756522Sbostic #include <sys/param.h>
9856522Sbostic #include <sys/systm.h>
9956522Sbostic #include <sys/dkstat.h>
10056522Sbostic #include <sys/buf.h>
10156522Sbostic #include <sys/conf.h>
10256522Sbostic #include <sys/errno.h>
10352889Sbostic 
104*56525Sbostic #include <pmax/dev/device.h>
105*56525Sbostic #include <pmax/dev/scsi.h>
106*56525Sbostic #include <pmax/dev/ascreg.h>
10752889Sbostic 
10852889Sbostic #define ASC_OFFSET_53C94	0x0		/* from module base */
10952889Sbostic #define ASC_OFFSET_DMAR		0x40000		/* DMA Address Register */
11052889Sbostic #define ASC_OFFSET_RAM		0x80000		/* SRAM Buffer */
11152889Sbostic #define ASC_OFFSET_ROM		0xc0000		/* Diagnostic ROM */
11252889Sbostic 
11352889Sbostic #define	ASC_RAM_SIZE		0x20000		/* 128k (32k*32) */
11452889Sbostic 
11552889Sbostic /*
11652889Sbostic  * DMA Address Register
11752889Sbostic  */
11852889Sbostic #define ASC_DMAR_MASK		0x1ffff		/* 17 bits, 128k */
11952889Sbostic #define ASC_DMAR_WRITE		0x80000000	/* DMA direction bit */
12053080Sralph #define	ASC_DMA_ADDR(x)		((unsigned)(x) & ASC_DMAR_MASK)
12152889Sbostic 
12252889Sbostic /*
12352889Sbostic  * Synch xfer parameters, and timing conversions
12452889Sbostic  */
12552889Sbostic #define SCSI_MIN_PERIOD	50	/* in 4 nsecs units */
12652889Sbostic #define ASC_MIN_PERIOD	5	/* in CLKS/BYTE, 1 CLK = 40nsecs */
12752889Sbostic #define ASC_MAX_PERIOD	35	/* in CLKS/BYTE, 1 CLK = 40nsecs */
12852889Sbostic #define ASC_MAX_OFFSET	15	/* pure number */
12952889Sbostic 
13052889Sbostic int	asc_to_scsi_period[] = {
13152889Sbostic 	320,
13252889Sbostic 	330,
13352889Sbostic 	340,
13452889Sbostic 	350,
13552889Sbostic 	50,
13652889Sbostic 	50,
13752889Sbostic 	60,
13852889Sbostic 	70,
13952889Sbostic 	80,
14052889Sbostic 	90,
14152889Sbostic 	100,
14252889Sbostic 	110,
14352889Sbostic 	120,
14452889Sbostic 	130,
14552889Sbostic 	140,
14652889Sbostic 	150,
14752889Sbostic 	160,
14852889Sbostic 	170,
14952889Sbostic 	180,
15052889Sbostic 	190,
15152889Sbostic 	200,
15252889Sbostic 	210,
15352889Sbostic 	220,
15452889Sbostic 	230,
15552889Sbostic 	240,
15652889Sbostic 	250,
15752889Sbostic 	260,
15852889Sbostic 	270,
15952889Sbostic 	280,
16052889Sbostic 	290,
16152889Sbostic 	300,
16252889Sbostic 	310,
16352889Sbostic };
16452889Sbostic 
16552889Sbostic /*
16652889Sbostic  * Internal forward declarations.
16752889Sbostic  */
16852889Sbostic static void asc_reset();
16952889Sbostic static void asc_startcmd();
17052889Sbostic 
17152889Sbostic #ifdef DEBUG
17252889Sbostic int	asc_debug = 1;
17352889Sbostic int	asc_debug_cmd;
17452889Sbostic int	asc_debug_bn;
17552889Sbostic int	asc_debug_sz;
17652889Sbostic #define NLOG 16
17752889Sbostic struct asc_log {
17852889Sbostic 	u_int	status;
17952889Sbostic 	u_char	state;
18052889Sbostic 	u_char	msg;
18152889Sbostic 	int	target;
18252889Sbostic } asc_log[NLOG], *asc_logp = asc_log;
18352889Sbostic #define PACK(unit, status, ss, ir) \
18452889Sbostic 	((unit << 24) | (status << 16) | (ss << 8) | ir)
18552889Sbostic #endif
18652889Sbostic 
18752889Sbostic /*
18852889Sbostic  * Scripts are entries in a state machine table.
18952889Sbostic  * A script has four parts: a pre-condition, an action, a command to the chip,
19052889Sbostic  * and an index into asc_scripts for the next state. The first triggers error
19152889Sbostic  * handling if not satisfied and in our case it is formed by the
19252889Sbostic  * values of the interrupt register and status register, this
19352889Sbostic  * basically captures the phase of the bus and the TC and BS
19452889Sbostic  * bits.  The action part is just a function pointer, and the
19552889Sbostic  * command is what the 53C94 should be told to do at the end
19652889Sbostic  * of the action processing.  This command is only issued and the
19752889Sbostic  * script proceeds if the action routine returns TRUE.
19852889Sbostic  * See asc_intr() for how and where this is all done.
19952889Sbostic  */
20052889Sbostic typedef struct script {
20152889Sbostic 	int		condition;	/* expected state at interrupt time */
20252889Sbostic 	int		(*action)();	/* extra operations */
20352889Sbostic 	int		command;	/* command to the chip */
20452889Sbostic 	struct script	*next;		/* index into asc_scripts for next state */
20552889Sbostic } script_t;
20652889Sbostic 
20752889Sbostic /* Matching on the condition value */
20852889Sbostic #define	SCRIPT_MATCH(ir, csr)		((ir) | (ASC_PHASE(csr) << 8))
20952889Sbostic 
21052889Sbostic /* forward decls of script actions */
21154145Sralph static int	script_nop();		/* when nothing needed */
21254145Sralph static int	asc_end();		/* all come to an end */
21354145Sralph static int	asc_get_status();	/* get status from target */
21454145Sralph static int	asc_dma_in();		/* start reading data from target */
21554145Sralph static int	asc_last_dma_in();	/* cleanup after all data is read */
21654145Sralph static int	asc_resume_in();	/* resume data in after a message */
21754145Sralph static int	asc_resume_dma_in();	/* resume DMA after a disconnect */
21854145Sralph static int	asc_dma_out();		/* send data to target via dma */
21954145Sralph static int	asc_last_dma_out();	/* cleanup after all data is written */
22054145Sralph static int	asc_resume_out();	/* resume data out after a message */
22154145Sralph static int	asc_resume_dma_out();	/* resume DMA after a disconnect */
22254145Sralph static int	asc_sendsync();		/* negotiate sync xfer */
22354145Sralph static int	asc_replysync();	/* negotiate sync xfer */
22454145Sralph static int	asc_msg_in();		/* process a message byte */
22554145Sralph static int	asc_disconnect();	/* process an expected disconnect */
22652889Sbostic 
22752889Sbostic /* Define the index into asc_scripts for various state transitions */
22852889Sbostic #define	SCRIPT_DATA_IN		0
22952942Sralph #define	SCRIPT_CONTINUE_IN	2
23052942Sralph #define	SCRIPT_DATA_OUT		3
23152942Sralph #define	SCRIPT_CONTINUE_OUT	5
23252942Sralph #define	SCRIPT_SIMPLE		6
23352942Sralph #define	SCRIPT_GET_STATUS	7
23452942Sralph #define	SCRIPT_MSG_IN		9
23552942Sralph #define	SCRIPT_REPLY_SYNC	11
23652889Sbostic #define	SCRIPT_TRY_SYNC		12
23752942Sralph #define	SCRIPT_DISCONNECT	15
23852942Sralph #define	SCRIPT_RESEL		16
23952942Sralph #define	SCRIPT_RESUME_IN	17
24052942Sralph #define	SCRIPT_RESUME_DMA_IN	18
24152942Sralph #define	SCRIPT_RESUME_OUT	19
24252942Sralph #define	SCRIPT_RESUME_DMA_OUT	20
24352942Sralph #define	SCRIPT_RESUME_NO_DATA	21
24452889Sbostic 
24552889Sbostic /*
24652889Sbostic  * Scripts
24752889Sbostic  */
24852889Sbostic script_t asc_scripts[] = {
24952942Sralph 	/* start data in */
25052889Sbostic 	{SCRIPT_MATCH(ASC_INT_FC | ASC_INT_BS, ASC_PHASE_DATAI),	/*  0 */
25152889Sbostic 		asc_dma_in, ASC_CMD_XFER_INFO | ASC_CMD_DMA,
25252942Sralph 		&asc_scripts[SCRIPT_DATA_IN + 1]},
25352889Sbostic 	{SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_STATUS),			/*  1 */
25452889Sbostic 		asc_last_dma_in, ASC_CMD_I_COMPLETE,
25552942Sralph 		&asc_scripts[SCRIPT_GET_STATUS]},
25652889Sbostic 
25752942Sralph 	/* continue data in after a chuck is finished */
25852942Sralph 	{SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAI),			/*  2 */
25952942Sralph 		asc_dma_in, ASC_CMD_XFER_INFO | ASC_CMD_DMA,
26052942Sralph 		&asc_scripts[SCRIPT_DATA_IN + 1]},
26152942Sralph 
26252942Sralph 	/* start data out */
26352942Sralph 	{SCRIPT_MATCH(ASC_INT_FC | ASC_INT_BS, ASC_PHASE_DATAO),	/*  3 */
26452889Sbostic 		asc_dma_out, ASC_CMD_XFER_INFO | ASC_CMD_DMA,
26552942Sralph 		&asc_scripts[SCRIPT_DATA_OUT + 1]},
26652942Sralph 	{SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_STATUS),			/*  4 */
26752889Sbostic 		asc_last_dma_out, ASC_CMD_I_COMPLETE,
26852942Sralph 		&asc_scripts[SCRIPT_GET_STATUS]},
26952889Sbostic 
27052942Sralph 	/* continue data out after a chuck is finished */
27152942Sralph 	{SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAO),			/*  5 */
27252942Sralph 		asc_dma_out, ASC_CMD_XFER_INFO | ASC_CMD_DMA,
27352942Sralph 		&asc_scripts[SCRIPT_DATA_OUT + 1]},
27452942Sralph 
27552889Sbostic 	/* simple command with no data transfer */
27652942Sralph 	{SCRIPT_MATCH(ASC_INT_FC | ASC_INT_BS, ASC_PHASE_STATUS),	/*  6 */
27752889Sbostic 		script_nop, ASC_CMD_I_COMPLETE,
27852942Sralph 		&asc_scripts[SCRIPT_GET_STATUS]},
27952889Sbostic 
28052889Sbostic 	/* get status and finish command */
28152942Sralph 	{SCRIPT_MATCH(ASC_INT_FC, ASC_PHASE_MSG_IN),			/*  7 */
28252889Sbostic 		asc_get_status, ASC_CMD_MSG_ACPT,
28352942Sralph 		&asc_scripts[SCRIPT_GET_STATUS + 1]},
28452942Sralph 	{SCRIPT_MATCH(ASC_INT_DISC, 0),					/*  8 */
28552889Sbostic 		asc_end, ASC_CMD_NOP,
28652942Sralph 		&asc_scripts[SCRIPT_GET_STATUS + 1]},
28752889Sbostic 
28852889Sbostic 	/* message in */
28952942Sralph 	{SCRIPT_MATCH(ASC_INT_FC, ASC_PHASE_MSG_IN),			/*  9 */
29052942Sralph 		asc_msg_in, ASC_CMD_MSG_ACPT,
29152942Sralph 		&asc_scripts[SCRIPT_MSG_IN + 1]},
29252942Sralph 	{SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_MSG_IN),			/* 10 */
29352889Sbostic 		script_nop, ASC_CMD_XFER_INFO,
29452942Sralph 		&asc_scripts[SCRIPT_MSG_IN]},
29552889Sbostic 
29652889Sbostic 	/* send synchonous negotiation reply */
29752942Sralph 	{SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_MSG_OUT),			/* 11 */
29852889Sbostic 		asc_replysync, ASC_CMD_XFER_INFO,
29952942Sralph 		&asc_scripts[SCRIPT_REPLY_SYNC]},
30052889Sbostic 
30152889Sbostic 	/* try to negotiate synchonous transfer parameters */
30252889Sbostic 	{SCRIPT_MATCH(ASC_INT_FC | ASC_INT_BS, ASC_PHASE_MSG_OUT),	/* 12 */
30352889Sbostic 		asc_sendsync, ASC_CMD_XFER_INFO,
30453080Sralph 		&asc_scripts[SCRIPT_TRY_SYNC + 1]},
30553080Sralph 	{SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_MSG_IN),			/* 13 */
30652889Sbostic 		script_nop, ASC_CMD_XFER_INFO,
30753080Sralph 		&asc_scripts[SCRIPT_MSG_IN]},
30853080Sralph 	{SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_COMMAND),			/* 14 */
30953080Sralph 		script_nop, ASC_CMD_XFER_INFO | ASC_CMD_DMA,
31053080Sralph 		&asc_scripts[SCRIPT_RESUME_NO_DATA]},
31152889Sbostic 
31252942Sralph 	/* handle a disconnect */
31352942Sralph 	{SCRIPT_MATCH(ASC_INT_DISC, ASC_PHASE_DATAO),			/* 15 */
31452942Sralph 		asc_disconnect, ASC_CMD_ENABLE_SEL,
31552942Sralph 		&asc_scripts[SCRIPT_RESEL]},
31652942Sralph 
31752889Sbostic 	/* reselect sequence: this is just a placeholder so match fails */
31852942Sralph 	{SCRIPT_MATCH(0, ASC_PHASE_MSG_IN),				/* 16 */
31952889Sbostic 		script_nop, ASC_CMD_MSG_ACPT,
32052942Sralph 		&asc_scripts[SCRIPT_RESEL]},
32152889Sbostic 
32252942Sralph 	/* resume data in after a message */
32352942Sralph 	{SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAI),			/* 17 */
32452942Sralph 		asc_resume_in, ASC_CMD_XFER_INFO | ASC_CMD_DMA,
32552942Sralph 		&asc_scripts[SCRIPT_DATA_IN + 1]},
32652942Sralph 
32752942Sralph 	/* resume partial DMA data in after a message */
32852942Sralph 	{SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAI),			/* 18 */
32952889Sbostic 		asc_resume_dma_in, ASC_CMD_XFER_INFO | ASC_CMD_DMA,
33052942Sralph 		&asc_scripts[SCRIPT_DATA_IN + 1]},
33152889Sbostic 
33252942Sralph 	/* resume data out after a message */
33352942Sralph 	{SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAO),			/* 19 */
33452942Sralph 		asc_resume_out, ASC_CMD_XFER_INFO | ASC_CMD_DMA,
33552942Sralph 		&asc_scripts[SCRIPT_DATA_OUT + 1]},
33652942Sralph 
33752942Sralph 	/* resume partial DMA data out after a message */
33852942Sralph 	{SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAO),			/* 20 */
33952889Sbostic 		asc_resume_dma_out, ASC_CMD_XFER_INFO | ASC_CMD_DMA,
34052942Sralph 		&asc_scripts[SCRIPT_DATA_OUT + 1]},
34152942Sralph 
34252942Sralph 	/* resume after a message when there is no more data */
34352942Sralph 	{SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_STATUS),			/* 21 */
34452942Sralph 		script_nop, ASC_CMD_I_COMPLETE,
34552942Sralph 		&asc_scripts[SCRIPT_GET_STATUS]},
34652889Sbostic };
34752889Sbostic 
34852889Sbostic /*
34952889Sbostic  * State kept for each active SCSI device.
35052889Sbostic  */
35152889Sbostic typedef struct scsi_state {
35252889Sbostic 	script_t *script;	/* saved script while processing error */
35352889Sbostic 	int	statusByte;	/* status byte returned during STATUS_PHASE */
35452889Sbostic 	int	error;		/* errno to pass back to device driver */
35552889Sbostic 	u_char	*dmaBufAddr;	/* DMA buffer address */
35652889Sbostic 	u_int	dmaBufSize;	/* DMA buffer size */
35752889Sbostic 	int	dmalen;		/* amount to transfer in this chunk */
35852889Sbostic 	int	dmaresid;	/* amount not transfered if chunk suspended */
35952889Sbostic 	int	buflen;		/* total remaining amount of data to transfer */
36052889Sbostic 	char	*buf;		/* current pointer within scsicmd->buf */
36152889Sbostic 	int	flags;		/* see below */
36252889Sbostic 	int	msglen;		/* number of message bytes to read */
36352889Sbostic 	int	msgcnt;		/* number of message bytes received */
36452889Sbostic 	u_char	sync_period;	/* DMA synchronous period */
36552889Sbostic 	u_char	sync_offset;	/* DMA synchronous xfer offset or 0 if async */
36652889Sbostic 	u_char	msg_out;	/* next MSG_OUT byte to send */
36752889Sbostic 	u_char	msg_in[16];	/* buffer for multibyte messages */
36852889Sbostic } State;
36952889Sbostic 
37052889Sbostic /* state flags */
37152889Sbostic #define DISCONN		0x01	/* true if currently disconnected from bus */
37253080Sralph #define DMA_IN_PROGRESS	0x02	/* true if data DMA started */
37352889Sbostic #define DMA_IN		0x04	/* true if reading from SCSI device */
37452889Sbostic #define DMA_OUT		0x10	/* true if writing to SCSI device */
37552889Sbostic #define DID_SYNC	0x20	/* true if synchronous offset was negotiated */
37652889Sbostic #define TRY_SYNC	0x40	/* true if try neg. synchronous offset */
37752889Sbostic 
37852889Sbostic #define ASC_NCMD	7
37952889Sbostic /*
38052889Sbostic  * State kept for each active SCSI host interface (53C94).
38152889Sbostic  */
38252889Sbostic struct asc_softc {
38352889Sbostic 	asc_regmap_t	*regs;		/* chip address */
38452889Sbostic 	volatile int	*dmar;		/* DMA address register address */
38554145Sralph 	u_char		*buff;		/* RAM buffer address */
38652889Sbostic 	int		myid;		/* SCSI ID of this interface */
38752889Sbostic 	int		myidmask;	/* ~(1 << myid) */
38852889Sbostic 	int		state;		/* current SCSI connection state */
38952889Sbostic 	int		target;		/* target SCSI ID if busy */
39052889Sbostic 	script_t	*script;	/* next expected interrupt & action */
39152889Sbostic 	ScsiCmd		*cmd[ASC_NCMD];	/* active command indexed by SCSI ID */
39252889Sbostic 	State		st[ASC_NCMD];	/* state info for each active command */
39352889Sbostic } asc_softc[NASC];
39452889Sbostic 
39552889Sbostic #define	ASC_STATE_IDLE		0	/* idle state */
39652889Sbostic #define	ASC_STATE_BUSY		1	/* selecting or currently connected */
39752889Sbostic #define ASC_STATE_TARGET	2	/* currently selected as target */
39852889Sbostic #define ASC_STATE_RESEL		3	/* currently waiting for reselect */
39952889Sbostic 
40052889Sbostic typedef struct asc_softc *asc_softc_t;
40152889Sbostic 
40252889Sbostic /*
40352889Sbostic  * Definition of the controller for the auto-configuration program.
40452889Sbostic  */
40552889Sbostic int	asc_probe();
40652889Sbostic void	asc_start();
40752889Sbostic void	asc_intr();
40852889Sbostic struct	driver ascdriver = {
40952889Sbostic 	"asc", asc_probe, asc_start, 0, asc_intr,
41052889Sbostic };
41152889Sbostic 
41252889Sbostic /*
41352889Sbostic  * Test to see if device is present.
41452889Sbostic  * Return true if found and initialized ok.
41552889Sbostic  */
41652889Sbostic asc_probe(cp)
41752889Sbostic 	register struct pmax_ctlr *cp;
41852889Sbostic {
41952889Sbostic 	register asc_softc_t asc;
42052889Sbostic 	register asc_regmap_t *regs;
42152889Sbostic 	int unit, id, s, i;
42252889Sbostic 
42352889Sbostic 	if ((unit = cp->pmax_unit) >= NASC)
42452889Sbostic 		return (0);
42552889Sbostic 	if (badaddr(cp->pmax_addr + ASC_OFFSET_53C94, 1))
42652889Sbostic 		return (0);
42752889Sbostic 	asc = &asc_softc[unit];
42852889Sbostic 
42952889Sbostic 	/*
43052889Sbostic 	 * Initialize hw descriptor, cache some pointers
43152889Sbostic 	 */
43252889Sbostic 	asc->regs = (asc_regmap_t *)(cp->pmax_addr + ASC_OFFSET_53C94);
43352889Sbostic 	asc->dmar = (volatile int *)(cp->pmax_addr + ASC_OFFSET_DMAR);
43454145Sralph 	asc->buff = (u_char *)(cp->pmax_addr + ASC_OFFSET_RAM);
43552889Sbostic 
43652889Sbostic 	asc->state = ASC_STATE_IDLE;
43752889Sbostic 	asc->target = -1;
43852889Sbostic 
43952889Sbostic 	regs = asc->regs;
44052889Sbostic 
44152889Sbostic 	/*
44252889Sbostic 	 * Reset chip, fully.  Note that interrupts are already enabled.
44352889Sbostic 	 */
44452889Sbostic 	s = splbio();
44552889Sbostic 
44652889Sbostic 	/* preserve our ID for now */
44752889Sbostic 	asc->myid = regs->asc_cnfg1 & ASC_CNFG1_MY_BUS_ID;
44852889Sbostic 	asc->myidmask = ~(1 << asc->myid);
44952889Sbostic 
45052889Sbostic 	asc_reset(asc, regs);
45152889Sbostic 
45252889Sbostic 	/*
45352889Sbostic 	 * Our SCSI id on the bus.
45452889Sbostic 	 * The user can set this via the prom on 3maxen/pmaxen.
45552889Sbostic 	 * If this changes it is easy to fix: make a default that
45652889Sbostic 	 * can be changed as boot arg.
45752889Sbostic 	 */
45852889Sbostic #ifdef	unneeded
45952889Sbostic 	regs->asc_cnfg1 = (regs->asc_cnfg1 & ~ASC_CNFG1_MY_BUS_ID) |
46052889Sbostic 			      (scsi_initiator_id[unit] & 0x7);
46152889Sbostic #endif
46252889Sbostic 	id = regs->asc_cnfg1 & ASC_CNFG1_MY_BUS_ID;
46352889Sbostic 	splx(s);
46452889Sbostic 
46552889Sbostic 	/*
46652889Sbostic 	 * Statically partition the DMA buffer between targets.
46752889Sbostic 	 * This way we will eventually be able to attach/detach
46852889Sbostic 	 * drives on-fly.  And 18k/target is plenty for normal use.
46952889Sbostic 	 */
47052889Sbostic #define PER_TGT_DMA_SIZE	((ASC_RAM_SIZE/7) & ~(sizeof(int)-1))
47152889Sbostic 
47252889Sbostic 	/*
47352889Sbostic 	 * Give each target its own DMA buffer region.
47452889Sbostic 	 * We may want to try ping ponging buffers later.
47552889Sbostic 	 */
47652889Sbostic 	for (i = 0; i < ASC_NCMD; i++) {
47752889Sbostic 		asc->st[i].dmaBufAddr = asc->buff + PER_TGT_DMA_SIZE * i;
47852889Sbostic 		asc->st[i].dmaBufSize = PER_TGT_DMA_SIZE;
47952889Sbostic 	}
48052889Sbostic 	printf("asc%d at nexus0 csr 0x%x priority %d SCSI id %d\n",
48152889Sbostic 		unit, cp->pmax_addr, cp->pmax_pri, id);
48252889Sbostic 	return (1);
48352889Sbostic }
48452889Sbostic 
48552889Sbostic /*
48652889Sbostic  * Start activity on a SCSI device.
48752889Sbostic  * We maintain information on each device separately since devices can
48852889Sbostic  * connect/disconnect during an operation.
48952889Sbostic  */
49052889Sbostic void
49152889Sbostic asc_start(scsicmd)
49252889Sbostic 	register ScsiCmd *scsicmd;	/* command to start */
49352889Sbostic {
49452889Sbostic 	register struct scsi_device *sdp = scsicmd->sd;
49552889Sbostic 	register asc_softc_t asc = &asc_softc[sdp->sd_ctlr];
49652889Sbostic 	int s;
49752889Sbostic 
49852889Sbostic 	s = splbio();
49952889Sbostic 	/*
50052889Sbostic 	 * Check if another command is already in progress.
50152889Sbostic 	 * We may have to change this if we allow SCSI devices with
50252889Sbostic 	 * separate LUNs.
50352889Sbostic 	 */
50452889Sbostic 	if (asc->cmd[sdp->sd_drive]) {
50552889Sbostic 		printf("asc%d: device %s busy at start\n", sdp->sd_ctlr,
50652889Sbostic 			sdp->sd_driver->d_name);
50752889Sbostic 		(*sdp->sd_driver->d_done)(scsicmd->unit, EBUSY,
50852889Sbostic 			scsicmd->buflen, 0);
50952889Sbostic 		splx(s);
51052889Sbostic 	}
51152889Sbostic 	asc->cmd[sdp->sd_drive] = scsicmd;
51252889Sbostic 	asc_startcmd(asc, sdp->sd_drive);
51352889Sbostic 	splx(s);
51452889Sbostic }
51552889Sbostic 
51652889Sbostic static void
51752889Sbostic asc_reset(asc, regs)
51852889Sbostic 	asc_softc_t asc;
51952889Sbostic 	asc_regmap_t *regs;
52052889Sbostic {
52152889Sbostic 
52252889Sbostic 	/*
52352889Sbostic 	 * Reset chip and wait till done
52452889Sbostic 	 */
52552889Sbostic 	regs->asc_cmd = ASC_CMD_RESET;
52652889Sbostic 	MachEmptyWriteBuffer(); DELAY(25);
52752889Sbostic 
52852889Sbostic 	/* spec says this is needed after reset */
52952889Sbostic 	regs->asc_cmd = ASC_CMD_NOP;
53052889Sbostic 	MachEmptyWriteBuffer(); DELAY(25);
53152889Sbostic 
53252889Sbostic 	/*
53352889Sbostic 	 * Set up various chip parameters
53452889Sbostic 	 */
53552889Sbostic 	regs->asc_ccf = ASC_CCF_25MHz;	/* 25 MHz clock */
53652889Sbostic 	MachEmptyWriteBuffer(); DELAY(25);
53752889Sbostic 	regs->asc_sel_timo = ASC_TIMEOUT_250;
53852889Sbostic 	/* restore our ID */
53952889Sbostic 	regs->asc_cnfg1 = asc->myid | ASC_CNFG1_P_CHECK;
54052889Sbostic 	regs->asc_cnfg2 = /* ASC_CNFG2_RFB | */ ASC_CNFG2_EPL;
54152889Sbostic 	regs->asc_cnfg3 = 0;
54252889Sbostic 	/* zero anything else */
54352889Sbostic 	ASC_TC_PUT(regs, 0);
54452889Sbostic 	regs->asc_sel_timo = ASC_TIMEOUT_250;
54552889Sbostic 	regs->asc_syn_p = ASC_MIN_PERIOD;
54652889Sbostic 	regs->asc_syn_o = 0;	/* async for now */
54752889Sbostic 	MachEmptyWriteBuffer();
54852889Sbostic }
54952889Sbostic 
55052889Sbostic /*
55152889Sbostic  * Start a SCSI command on a target.
55252889Sbostic  */
55352889Sbostic static void
55452889Sbostic asc_startcmd(asc, target)
55552889Sbostic 	asc_softc_t asc;
55652889Sbostic 	int target;
55752889Sbostic {
55852889Sbostic 	register asc_regmap_t *regs;
55952889Sbostic 	register ScsiCmd *scsicmd;
56052889Sbostic 	register State *state;
56152889Sbostic 	int len;
56252889Sbostic 
56352889Sbostic 	/*
56452889Sbostic 	 * See if another target is currently selected on this SCSI bus.
56552889Sbostic 	 */
56652889Sbostic 	if (asc->target >= 0)
56752889Sbostic 		return;
56852889Sbostic 
56952889Sbostic 	regs = asc->regs;
57052889Sbostic 
57152889Sbostic 	/*
57252889Sbostic 	 * Check to see if a reselection is in progress and if so,
57352889Sbostic 	 * try to cancel it or respond to the reselection if it won.
57452889Sbostic 	 */
57552889Sbostic 	if (asc->state == ASC_STATE_RESEL) {
57652889Sbostic 		regs->asc_cmd = ASC_CMD_DISABLE_SEL;
57752889Sbostic 		while (!(regs->asc_status & ASC_CSR_INT))
57852889Sbostic 			DELAY(1);
57952889Sbostic 		asc_intr(asc - asc_softc);
58052889Sbostic 		/* we will be busy if a reselecting device won */
58152889Sbostic 		if (asc->state == ASC_STATE_BUSY)
58252889Sbostic 			return;
58352889Sbostic 	}
58452889Sbostic 
58552889Sbostic 	asc->state = ASC_STATE_BUSY;
58652889Sbostic 	asc->target = target;
58752889Sbostic 
58852889Sbostic 	/* cache some pointers */
58952889Sbostic 	scsicmd = asc->cmd[target];
59052889Sbostic 	state = &asc->st[target];
59152889Sbostic 
59252889Sbostic #ifdef DEBUG
59352889Sbostic 	if (asc_debug > 1) {
59452889Sbostic 		printf("asc_startcmd: %s target %d cmd %x len %d\n",
59552889Sbostic 			scsicmd->sd->sd_driver->d_name, target,
59652889Sbostic 			scsicmd->cmd[0], scsicmd->buflen);
59752889Sbostic 	}
59852889Sbostic 	asc_debug_cmd = scsicmd->cmd[0];
59952889Sbostic 	if (scsicmd->cmd[0] == SCSI_READ_EXT) {
60052889Sbostic 		asc_debug_bn = (scsicmd->cmd[2] << 24) |
60152889Sbostic 			(scsicmd->cmd[3] << 16) |
60252889Sbostic 			(scsicmd->cmd[4] << 8) |
60352889Sbostic 			scsicmd->cmd[5];
60452889Sbostic 		asc_debug_sz = (scsicmd->cmd[7] << 8) | scsicmd->cmd[8];
60552889Sbostic 	}
60653080Sralph 	asc_logp->status = PACK(asc - asc_softc, 0, 0, asc_debug_cmd);
60752889Sbostic 	asc_logp->target = asc->target;
60852889Sbostic 	asc_logp->state = 0;
60953080Sralph 	asc_logp->msg = 0xff;
61052889Sbostic 	if (++asc_logp >= &asc_log[NLOG])
61152889Sbostic 		asc_logp = asc_log;
61252889Sbostic #endif
61352889Sbostic 
61452889Sbostic 	/*
61552889Sbostic 	 * Init the chip and target state.
61652889Sbostic 	 */
61752889Sbostic 	regs->asc_cmd = ASC_CMD_FLUSH;
61853080Sralph 	state->flags = state->flags & DID_SYNC;
61952889Sbostic 	state->error = 0;
62052889Sbostic 	state->script = (script_t *)0;
62152889Sbostic 	state->msg_out = SCSI_NO_OP;
62252889Sbostic 
62352889Sbostic 	/*
62452889Sbostic 	 * Copy command data to the DMA buffer.
62552889Sbostic 	 */
62652889Sbostic 	len = scsicmd->cmdlen;
62752889Sbostic 	state->dmalen = len;
62852889Sbostic 	bcopy(scsicmd->cmd, state->dmaBufAddr, len);
62952889Sbostic 
63052889Sbostic 	/* check for simple SCSI command with no data transfer */
63152889Sbostic 	if ((state->buflen = scsicmd->buflen) == 0) {
63252889Sbostic 		/* check for sync negotiation */
63352889Sbostic 		if ((scsicmd->flags & SCSICMD_USE_SYNC) &&
63452889Sbostic 		    !(state->flags & DID_SYNC)) {
63552889Sbostic 			asc->script = &asc_scripts[SCRIPT_TRY_SYNC];
63652889Sbostic 			state->flags |= TRY_SYNC;
63752889Sbostic 		} else
63852889Sbostic 			asc->script = &asc_scripts[SCRIPT_SIMPLE];
63952889Sbostic 		state->buf = (char *)0;
64052889Sbostic 	} else if (scsicmd->flags & SCSICMD_DATA_TO_DEVICE) {
64152889Sbostic 		asc->script = &asc_scripts[SCRIPT_DATA_OUT];
64253080Sralph 		state->buf = scsicmd->buf;
64352889Sbostic 		state->flags |= DMA_OUT;
64452889Sbostic 	} else {
64552889Sbostic 		asc->script = &asc_scripts[SCRIPT_DATA_IN];
64652889Sbostic 		state->buf = scsicmd->buf;
64752889Sbostic 		state->flags |= DMA_IN;
64852889Sbostic 	}
64952889Sbostic 
65052889Sbostic 	/* preload the FIFO with the message to be sent */
65152942Sralph 	regs->asc_fifo = SCSI_DIS_REC_IDENTIFY;
65252889Sbostic 
65352889Sbostic 	/* start the asc */
65452889Sbostic 	*asc->dmar = ASC_DMAR_WRITE | ASC_DMA_ADDR(state->dmaBufAddr);
65552889Sbostic 	ASC_TC_PUT(regs, len);
65652889Sbostic 
65752889Sbostic 	regs->asc_dbus_id = target;
65852889Sbostic 	regs->asc_syn_p = state->sync_period;
65952889Sbostic 	regs->asc_syn_o = state->sync_offset;
66052889Sbostic 
66152889Sbostic 	if (state->flags & TRY_SYNC)
66253080Sralph 		regs->asc_cmd = ASC_CMD_SEL_ATN_STOP;
66352889Sbostic 	else
66452889Sbostic 		regs->asc_cmd = ASC_CMD_SEL_ATN | ASC_CMD_DMA;
66552889Sbostic }
66652889Sbostic 
66752889Sbostic /*
66852889Sbostic  * Interrupt routine
66952889Sbostic  *	Take interrupts from the chip
67052889Sbostic  *
67152889Sbostic  * Implementation:
67252889Sbostic  *	Move along the current command's script if
67352889Sbostic  *	all is well, invoke error handler if not.
67452889Sbostic  */
67552889Sbostic void
67652889Sbostic asc_intr(unit)
67752889Sbostic 	int unit;
67852889Sbostic {
67952889Sbostic 	register asc_softc_t asc = &asc_softc[unit];
68052889Sbostic 	register asc_regmap_t *regs = asc->regs;
68152889Sbostic 	register State *state;
68252889Sbostic 	register script_t *scpt;
68352889Sbostic 	register int ss, ir, status;
68452889Sbostic 
68552889Sbostic 	/* collect ephemeral information */
68652889Sbostic 	status = regs->asc_status;
68753080Sralph again:
68852889Sbostic 	ss = regs->asc_ss;
68952889Sbostic 	ir = regs->asc_intr;	/* this resets the previous two */
69052889Sbostic 	scpt = asc->script;
69152889Sbostic 
69252889Sbostic #ifdef DEBUG
69352889Sbostic 	asc_logp->status = PACK(unit, status, ss, ir);
69452889Sbostic 	asc_logp->target = (asc->state == ASC_STATE_BUSY) ? asc->target : -1;
69552889Sbostic 	asc_logp->state = scpt - asc_scripts;
69652889Sbostic 	asc_logp->msg = -1;
69752889Sbostic 	if (++asc_logp >= &asc_log[NLOG])
69852889Sbostic 		asc_logp = asc_log;
69952889Sbostic 	if (asc_debug > 2)
70052889Sbostic 		printf("asc_intr: status %x ss %x ir %x cond %d:%x\n",
70152889Sbostic 			status, ss, ir, scpt - asc_scripts, scpt->condition);
70252889Sbostic #endif
70352889Sbostic 
70452889Sbostic 	/* check the expected state */
70552889Sbostic 	if (SCRIPT_MATCH(ir, status) == scpt->condition) {
70652889Sbostic 		/*
70752889Sbostic 		 * Perform the appropriate operation, then proceed.
70852889Sbostic 		 */
70952889Sbostic 		if ((*scpt->action)(asc, status, ss, ir)) {
71052889Sbostic 			regs->asc_cmd = scpt->command;
71152889Sbostic 			asc->script = scpt->next;
71252889Sbostic 		}
71352889Sbostic 		goto done;
71452889Sbostic 	}
71552889Sbostic 
71652889Sbostic 	/* check for message in or out */
71752889Sbostic 	if ((ir & ~ASC_INT_FC) == ASC_INT_BS) {
71852889Sbostic 		register int len, fifo;
71952889Sbostic 
72052889Sbostic 		state = &asc->st[asc->target];
72152889Sbostic 		switch (ASC_PHASE(status)) {
72253080Sralph 		case ASC_PHASE_DATAI:
72353080Sralph 		case ASC_PHASE_DATAO:
72453080Sralph 			ASC_TC_GET(regs, len);
72553080Sralph 			fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT;
72653080Sralph 			printf("asc_intr: data overrun: buflen %d dmalen %d tc %d fifo %d\n",
72753080Sralph 				state->buflen, state->dmalen, len, fifo);
72853080Sralph 			goto abort;
72953080Sralph 
73052889Sbostic 		case ASC_PHASE_MSG_IN:
73152889Sbostic 			break;
73252889Sbostic 
73352889Sbostic 		case ASC_PHASE_MSG_OUT:
73452889Sbostic 			regs->asc_fifo = state->msg_out;
73552889Sbostic 			state->msg_out = SCSI_NO_OP;
73652889Sbostic 			regs->asc_cmd = ASC_CMD_XFER_INFO;
73752889Sbostic 			goto done;
73852889Sbostic 
73952889Sbostic 		case ASC_PHASE_STATUS:
74052889Sbostic 			/* probably an error in the SCSI command */
74152889Sbostic 			asc->script = &asc_scripts[SCRIPT_GET_STATUS];
74252889Sbostic 			regs->asc_cmd = ASC_CMD_I_COMPLETE;
74352889Sbostic 			goto done;
74452889Sbostic 
74552889Sbostic 		default:
74652889Sbostic 			goto abort;
74752889Sbostic 		}
74852889Sbostic 
74952889Sbostic 		if (state->script)
75052889Sbostic 			goto abort;
75152889Sbostic 
75252889Sbostic 		/* check for DMA in progress */
75352889Sbostic 		ASC_TC_GET(regs, len);
75452889Sbostic 		fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT;
75552889Sbostic 		/* flush any data in the FIFO */
75652889Sbostic 		if (fifo) {
75753080Sralph 			if (state->flags & DMA_OUT)
75853080Sralph 				len += fifo;
75953080Sralph 			else if (state->flags & DMA_IN) {
76053080Sralph 				u_char *cp;
76153080Sralph 
76253080Sralph 				printf("asc_intr: IN: dmalen %d len %d fifo %d\n",
76353080Sralph 					state->dmalen, len, fifo); /* XXX */
76453080Sralph 				len += fifo;
76553080Sralph 				cp = state->dmaBufAddr + (state->dmalen - len);
76653080Sralph 				while (fifo-- > 0)
76753080Sralph 					*cp++ = regs->asc_fifo;
76853080Sralph 			} else
76953080Sralph 				printf("asc_intr: dmalen %d len %d fifo %d\n",
77053080Sralph 					state->dmalen, len, fifo); /* XXX */
77152889Sbostic 			regs->asc_cmd = ASC_CMD_FLUSH;
77252889Sbostic 			MachEmptyWriteBuffer();
77352889Sbostic 		}
77452889Sbostic 		if (len) {
77552889Sbostic 			/* save number of bytes still to be sent or received */
77652889Sbostic 			state->dmaresid = len;
77752889Sbostic 			/* setup state to resume to */
77852889Sbostic 			if (state->flags & DMA_IN)
77952889Sbostic 				state->script =
78052889Sbostic 					&asc_scripts[SCRIPT_RESUME_DMA_IN];
78152889Sbostic 			else if (state->flags & DMA_OUT)
78252889Sbostic 				state->script =
78352889Sbostic 					&asc_scripts[SCRIPT_RESUME_DMA_OUT];
78452889Sbostic 			else
78552889Sbostic 				state->script = asc->script;
78652889Sbostic 		} else {
78752889Sbostic 			/* setup state to resume to */
78852942Sralph 			if (state->flags & DMA_IN) {
78953080Sralph 				if (state->flags & DMA_IN_PROGRESS) {
79053080Sralph 					state->flags &= ~DMA_IN_PROGRESS;
79152942Sralph 					len = state->dmalen;
79252942Sralph 					bcopy(state->dmaBufAddr, state->buf,
79352942Sralph 						len);
79452942Sralph 					state->buf += len;
79552942Sralph 					state->buflen -= len;
79653080Sralph 				}
79752942Sralph 				if (state->buflen)
79852942Sralph 					state->script =
79952942Sralph 					    &asc_scripts[SCRIPT_RESUME_IN];
80052942Sralph 				else
80152942Sralph 					state->script =
80252942Sralph 					    &asc_scripts[SCRIPT_RESUME_NO_DATA];
80352942Sralph 			} else if (state->flags & DMA_OUT) {
80452942Sralph 				/*
80552942Sralph 				 * If this is the last chunk, the next expected
80652942Sralph 				 * state is to get status.
80752942Sralph 				 */
80853080Sralph 				if (state->flags & DMA_IN_PROGRESS) {
80953080Sralph 					state->flags &= ~DMA_IN_PROGRESS;
81053080Sralph 					len = state->dmalen;
81153080Sralph 					state->buf += len;
81253080Sralph 					state->buflen -= len;
81353080Sralph 				}
81452942Sralph 				if (state->buflen)
81552942Sralph 					state->script =
81652942Sralph 					    &asc_scripts[SCRIPT_RESUME_OUT];
81752942Sralph 				else
81852942Sralph 					state->script =
81952942Sralph 					    &asc_scripts[SCRIPT_RESUME_NO_DATA];
82053080Sralph 			} else if (asc->script == &asc_scripts[SCRIPT_SIMPLE])
82153080Sralph 				state->script =
82253080Sralph 					    &asc_scripts[SCRIPT_RESUME_NO_DATA];
82353080Sralph 			else
82452889Sbostic 				state->script = asc->script;
82552889Sbostic 		}
82652889Sbostic 
82752889Sbostic 		/* setup to receive a message */
82852889Sbostic 		asc->script = &asc_scripts[SCRIPT_MSG_IN];
82952889Sbostic 		state->msglen = 0;
83052889Sbostic 		regs->asc_cmd = ASC_CMD_XFER_INFO;
83152889Sbostic 		goto done;
83252889Sbostic 	}
83352889Sbostic 
83452889Sbostic 	/* check for SCSI bus reset */
83552889Sbostic 	if (ir & ASC_INT_RESET) {
83652889Sbostic 		register int i;
83752889Sbostic 
83852889Sbostic 		printf("asc%d: SCSI bus reset!!\n", asc - asc_softc);
83952889Sbostic 		/* need to flush any pending commands */
84052889Sbostic 		for (i = 0; i < ASC_NCMD; i++) {
84152889Sbostic 			if (!asc->cmd[i])
84252889Sbostic 				continue;
84352889Sbostic 			asc->st[i].error = EIO;
84452889Sbostic 			asc_end(asc, 0, 0, 0);
84552889Sbostic 		}
84652889Sbostic 		/* rearbitrate synchronous offset */
84752889Sbostic 		for (i = 0; i < ASC_NCMD; i++) {
84852889Sbostic 			asc->st[i].sync_offset = 0;
84952889Sbostic 			asc->st[i].flags = 0;
85052889Sbostic 		}
85152889Sbostic 		asc->target = -1;
85252889Sbostic 		return;
85352889Sbostic 	}
85452889Sbostic 
85552889Sbostic 	/* check for command errors */
85652889Sbostic 	if (ir & ASC_INT_ILL)
85752889Sbostic 		goto abort;
85852889Sbostic 
85952889Sbostic 	/* check for disconnect */
86052889Sbostic 	if (ir & ASC_INT_DISC) {
86152889Sbostic 		state = &asc->st[asc->target];
86252889Sbostic 		switch (ASC_SS(ss)) {
86352889Sbostic 		case 0: /* device did not respond */
86452889Sbostic 			state->error = ENXIO;
86552889Sbostic 			asc_end(asc, status, ss, ir);
86652889Sbostic 			return;
86752889Sbostic 
86852889Sbostic 		default:
86952889Sbostic 			goto abort;
87052889Sbostic 		}
87152889Sbostic 	}
87252889Sbostic 
87352889Sbostic 	/* check for reselect */
87452889Sbostic 	if (ir & ASC_INT_RESEL) {
87552889Sbostic 		unsigned fifo, id, msg;
87652889Sbostic 
87752889Sbostic 		fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT;
87852889Sbostic 		if (fifo < 2)
87952889Sbostic 			goto abort;
88052889Sbostic 		/* read unencoded SCSI ID and convert to binary */
88152889Sbostic 		msg = regs->asc_fifo & asc->myidmask;
88252889Sbostic 		for (id = 0; (msg & 1) == 0; id++)
88352889Sbostic 			msg >>= 1;
88452889Sbostic 		/* read identify message */
88552889Sbostic 		msg = regs->asc_fifo;
88652889Sbostic #ifdef DEBUG
88752889Sbostic 		if (asc_logp == asc_log)
88852889Sbostic 			asc_log[NLOG - 1].msg = msg;
88952889Sbostic 		else
89052889Sbostic 			asc_logp[-1].msg = msg;
89152889Sbostic #endif
89252889Sbostic 		if (asc->state != ASC_STATE_RESEL)
89352889Sbostic 			goto abort;
89452889Sbostic 		asc->state = ASC_STATE_BUSY;
89552889Sbostic 		asc->target = id;
89652889Sbostic 		state = &asc->st[id];
89752889Sbostic 		asc->script = state->script;
89852889Sbostic 		state->script = (script_t *)0;
89952889Sbostic 		if (!(state->flags & DISCONN))
90052889Sbostic 			goto abort;
90152889Sbostic 		state->flags &= ~DISCONN;
90253080Sralph 		regs->asc_syn_p = state->sync_period;
90353080Sralph 		regs->asc_syn_o = state->sync_offset;
90452889Sbostic 		regs->asc_cmd = ASC_CMD_MSG_ACPT;
90552889Sbostic 		goto done;
90652889Sbostic 	}
90752889Sbostic 
90852889Sbostic 	/* check if we are being selected as a target */
90952889Sbostic 	if (ir & (ASC_INT_SEL | ASC_INT_SEL_ATN))
91052889Sbostic 		goto abort;
91152889Sbostic 
91252889Sbostic 	/* must be just a ASC_INT_FC */
91352889Sbostic done:
91452889Sbostic 	MachEmptyWriteBuffer();
91553080Sralph 	/* watch out for HW race conditions and setup & hold time violations */
91653080Sralph 	ir = regs->asc_status;
91753080Sralph 	while (ir != (status = regs->asc_status))
91853080Sralph 		ir = status;
91953080Sralph 	if (status & ASC_CSR_INT)
92052889Sbostic 		goto again;
92152889Sbostic 	return;
92252889Sbostic 
92352889Sbostic abort:
92452889Sbostic #ifdef DEBUG
92552889Sbostic 	asc_DumpLog("asc_intr");
92652889Sbostic #endif
92752889Sbostic #if 0
92852889Sbostic 	panic("asc_intr");
92952889Sbostic #else
93052889Sbostic 	for (;;);
93152889Sbostic #endif
93252889Sbostic }
93352889Sbostic 
93452889Sbostic /*
93552889Sbostic  * All the many little things that the interrupt
93652889Sbostic  * routine might switch to.
93752889Sbostic  */
93852889Sbostic 
93952889Sbostic /* ARGSUSED */
94052889Sbostic static int
94152889Sbostic script_nop(asc, status, ss, ir)
94252889Sbostic 	register asc_softc_t asc;
94352889Sbostic 	register int status, ss, ir;
94452889Sbostic {
94552889Sbostic 	return (1);
94652889Sbostic }
94752889Sbostic 
94852889Sbostic /* ARGSUSED */
94952889Sbostic static int
95052889Sbostic asc_get_status(asc, status, ss, ir)
95152889Sbostic 	register asc_softc_t asc;
95252889Sbostic 	register int status, ss, ir;
95352889Sbostic {
95452889Sbostic 	register asc_regmap_t *regs = asc->regs;
95552889Sbostic 	register int data;
95652889Sbostic 
95752889Sbostic 	/*
95852889Sbostic 	 * Get the last two bytes in the FIFO.
95952889Sbostic 	 */
96052889Sbostic 	if ((data = regs->asc_flags & ASC_FLAGS_FIFO_CNT) != 2) {
96152889Sbostic 		printf("asc_get_status: fifo cnt %d\n", data); /* XXX */
96252889Sbostic 		if (data < 2) {
96352889Sbostic 			asc->regs->asc_cmd = ASC_CMD_MSG_ACPT;
96452889Sbostic 			return (0);
96552889Sbostic 		}
96652889Sbostic 		do {
96752889Sbostic 			data = regs->asc_fifo;
96852889Sbostic 		} while ((regs->asc_flags & ASC_FLAGS_FIFO_CNT) > 2);
96952889Sbostic 	}
97052889Sbostic 
97152889Sbostic 	/* save the status byte */
97252889Sbostic 	asc->st[asc->target].statusByte = data = regs->asc_fifo;
97352889Sbostic #ifdef DEBUG
97452889Sbostic 	if (asc_logp == asc_log)
97552889Sbostic 		asc_log[NLOG - 1].msg = data;
97652889Sbostic 	else
97752889Sbostic 		asc_logp[-1].msg = data;
97852889Sbostic #endif
97952889Sbostic 
98052889Sbostic 	/* get the (presumed) command_complete message */
98152889Sbostic 	if ((data = regs->asc_fifo) == SCSI_COMMAND_COMPLETE)
98252889Sbostic 		return (1);
98352889Sbostic 
98452889Sbostic #ifdef DEBUG
98552889Sbostic 	printf("asc_get_status: status %x cmd %x\n",
98652889Sbostic 		asc->st[asc->target].statusByte, data);
98752889Sbostic 	asc_DumpLog("asc_get_status");
98852889Sbostic #endif
98952889Sbostic 	return (0);
99052889Sbostic }
99152889Sbostic 
99252889Sbostic /* ARGSUSED */
99352889Sbostic static int
99452889Sbostic asc_end(asc, status, ss, ir)
99552889Sbostic 	register asc_softc_t asc;
99652889Sbostic 	register int status, ss, ir;
99752889Sbostic {
99852889Sbostic 	register ScsiCmd *scsicmd;
99952889Sbostic 	register State *state;
100052889Sbostic 	register int i, target;
100152889Sbostic 
100252889Sbostic 	asc->state = ASC_STATE_IDLE;
100352889Sbostic 	target = asc->target;
100452889Sbostic 	asc->target = -1;
100552889Sbostic 	scsicmd = asc->cmd[target];
100652889Sbostic 	asc->cmd[target] = (ScsiCmd *)0;
100752889Sbostic 	state = &asc->st[target];
100852889Sbostic 
100952889Sbostic #ifdef DEBUG
101052889Sbostic 	if (asc_debug > 1) {
101152889Sbostic 		printf("asc_end: %s target %d cmd %x err %d resid %d\n",
101252889Sbostic 			scsicmd->sd->sd_driver->d_name, target,
101352889Sbostic 			scsicmd->cmd[0], state->error, state->buflen);
101452889Sbostic 	}
101552889Sbostic #endif
101652889Sbostic #ifdef DIAGNOSTIC
101752889Sbostic 	if (target < 0 || !scsicmd)
101852889Sbostic 		panic("asc_end");
101952889Sbostic #endif
102052889Sbostic 
102152889Sbostic 	/* look for disconnected devices */
102252889Sbostic 	for (i = 0; i < ASC_NCMD; i++) {
102352889Sbostic 		if (!asc->cmd[i] || !(asc->st[i].flags & DISCONN))
102452889Sbostic 			continue;
102552889Sbostic 		asc->regs->asc_cmd = ASC_CMD_ENABLE_SEL;
102652889Sbostic 		asc->state = ASC_STATE_RESEL;
102752889Sbostic 		asc->script = &asc_scripts[SCRIPT_RESEL];
102852889Sbostic 		break;
102952889Sbostic 	}
103052889Sbostic 
103152889Sbostic 	/* look for another device that is ready */
103252889Sbostic 	for (i = 0; i < ASC_NCMD; i++) {
103352889Sbostic 		/* don't restart a disconnected command */
103452889Sbostic 		if (!asc->cmd[i] || (asc->st[i].flags & DISCONN))
103552889Sbostic 			continue;
103652889Sbostic 		asc_startcmd(asc, i);
103752889Sbostic 		break;
103852889Sbostic 	}
103952889Sbostic 
104052889Sbostic 	/* signal device driver that the command is done */
104152889Sbostic 	(*scsicmd->sd->sd_driver->d_done)(scsicmd->unit, state->error,
104252889Sbostic 		state->buflen, state->statusByte);
104352889Sbostic 
104452889Sbostic 	return (0);
104552889Sbostic }
104652889Sbostic 
104752889Sbostic /* ARGSUSED */
104852889Sbostic static int
104952889Sbostic asc_dma_in(asc, status, ss, ir)
105052889Sbostic 	register asc_softc_t asc;
105152889Sbostic 	register int status, ss, ir;
105252889Sbostic {
105352889Sbostic 	register asc_regmap_t *regs = asc->regs;
105452889Sbostic 	register State *state = &asc->st[asc->target];
105553080Sralph 	register int len;
105652889Sbostic 
105752889Sbostic 	/* check for previous chunk in buffer */
105853080Sralph 	if (state->flags & DMA_IN_PROGRESS) {
105952889Sbostic 		/*
106052889Sbostic 		 * Only count bytes that have been copied to memory.
106152889Sbostic 		 * There may be some bytes in the FIFO if synchonous transfers
106252889Sbostic 		 * are in progress.
106352889Sbostic 		 */
106452889Sbostic 		ASC_TC_GET(regs, len);
106552889Sbostic 		len = state->dmalen - len;
106652889Sbostic 		bcopy(state->dmaBufAddr, state->buf, len);
106752889Sbostic 		state->buf += len;
106852889Sbostic 		state->buflen -= len;
106953080Sralph 	}
107052889Sbostic 
107152942Sralph 	/* setup to start reading the next chunk */
107252889Sbostic 	len = state->buflen;
107352889Sbostic 	if (len > state->dmaBufSize)
107452889Sbostic 		len = state->dmaBufSize;
107552889Sbostic 	state->dmalen = len;
107652889Sbostic 	*asc->dmar = ASC_DMA_ADDR(state->dmaBufAddr);
107752889Sbostic 	ASC_TC_PUT(regs, len);
107852942Sralph #ifdef DEBUG
107952942Sralph 	if (asc_debug > 2)
108052942Sralph 		printf("asc_dma_in: buflen %d, len %d\n", state->buflen, len);
108152942Sralph #endif
108252942Sralph 
108352942Sralph 	/* check for next chunk */
108453080Sralph 	state->flags |= DMA_IN_PROGRESS;
108552889Sbostic 	if (len != state->buflen) {
108652889Sbostic 		regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA;
108752942Sralph 		asc->script = &asc_scripts[SCRIPT_CONTINUE_IN];
108852889Sbostic 		return (0);
108952889Sbostic 	}
109052889Sbostic 	return (1);
109152889Sbostic }
109252889Sbostic 
109352889Sbostic /* ARGSUSED */
109452889Sbostic static int
109552889Sbostic asc_last_dma_in(asc, status, ss, ir)
109652889Sbostic 	register asc_softc_t asc;
109752889Sbostic 	register int status, ss, ir;
109852889Sbostic {
109952889Sbostic 	register asc_regmap_t *regs = asc->regs;
110052889Sbostic 	register State *state = &asc->st[asc->target];
110152889Sbostic 	register int len, fifo;
110252889Sbostic 
110352889Sbostic 	/* copy data from buffer to main memory */
110452889Sbostic 	ASC_TC_GET(regs, len);
110552889Sbostic 	fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT;
110652889Sbostic #ifdef DEBUG
110752942Sralph 	if (asc_debug > 2)
110852889Sbostic 		printf("asc_last_dma_in: buflen %d dmalen %d tc %d fifo %d\n",
110952889Sbostic 			state->buflen, state->dmalen, len, fifo);
111052889Sbostic #endif
111152889Sbostic 	if (fifo) {
111252942Sralph 		/* device must be trying to send more than we expect */
111352889Sbostic 		regs->asc_cmd = ASC_CMD_FLUSH;
111452889Sbostic 		MachEmptyWriteBuffer();
111552889Sbostic 	}
111653080Sralph 	state->flags &= ~DMA_IN_PROGRESS;
111752889Sbostic 	len = state->dmalen - len;
111852889Sbostic 	state->buflen -= len;
111952889Sbostic 	bcopy(state->dmaBufAddr, state->buf, len);
112052889Sbostic 
112152889Sbostic 	return (1);
112252889Sbostic }
112352889Sbostic 
112452889Sbostic /* ARGSUSED */
112552889Sbostic static int
112652942Sralph asc_resume_in(asc, status, ss, ir)
112752942Sralph 	register asc_softc_t asc;
112852942Sralph 	register int status, ss, ir;
112952942Sralph {
113052942Sralph 	register asc_regmap_t *regs = asc->regs;
113152942Sralph 	register State *state = &asc->st[asc->target];
113252942Sralph 	register int len;
113352942Sralph 
113452942Sralph 	/* setup to start reading the next chunk */
113552942Sralph 	len = state->buflen;
113652942Sralph 	if (len > state->dmaBufSize)
113752942Sralph 		len = state->dmaBufSize;
113852942Sralph 	state->dmalen = len;
113952942Sralph 	*asc->dmar = ASC_DMA_ADDR(state->dmaBufAddr);
114052942Sralph 	ASC_TC_PUT(regs, len);
114152942Sralph #ifdef DEBUG
114252942Sralph 	if (asc_debug > 2)
114352942Sralph 		printf("asc_resume_in: buflen %d, len %d\n", state->buflen,
114452942Sralph 			len);
114552942Sralph #endif
114652942Sralph 
114752942Sralph 	/* check for next chunk */
114853080Sralph 	state->flags |= DMA_IN_PROGRESS;
114952942Sralph 	if (len != state->buflen) {
115052942Sralph 		regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA;
115152942Sralph 		asc->script = &asc_scripts[SCRIPT_CONTINUE_IN];
115252942Sralph 		return (0);
115352942Sralph 	}
115452942Sralph 	return (1);
115552942Sralph }
115652942Sralph 
115752942Sralph /* ARGSUSED */
115852942Sralph static int
115952889Sbostic asc_resume_dma_in(asc, status, ss, ir)
116052889Sbostic 	register asc_softc_t asc;
116152889Sbostic 	register int status, ss, ir;
116252889Sbostic {
116352889Sbostic 	register asc_regmap_t *regs = asc->regs;
116452889Sbostic 	register State *state = &asc->st[asc->target];
116552889Sbostic 	register int len, off;
116652889Sbostic 
116752889Sbostic 	/* setup to finish reading the current chunk */
116852889Sbostic 	len = state->dmaresid;
116952889Sbostic 	off = state->dmalen - len;
117052889Sbostic 	if ((off & 1) && state->sync_offset) {
117152889Sbostic 		printf("asc_resume_dma_in: odd xfer dmalen %d len %d off %d\n",
117252889Sbostic 			state->dmalen, len, off); /* XXX */
117352889Sbostic 		regs->asc_res_fifo = state->dmaBufAddr[off];
117452889Sbostic 	}
117552889Sbostic 	*asc->dmar = ASC_DMA_ADDR(state->dmaBufAddr + off);
117652889Sbostic 	ASC_TC_PUT(regs, len);
117752942Sralph #ifdef DEBUG
117852942Sralph 	if (asc_debug > 2)
117952942Sralph 		printf("asc_resume_dma_in: buflen %d dmalen %d len %d off %d\n",
118052942Sralph 			state->dmalen, state->buflen, len, off);
118152942Sralph #endif
118252942Sralph 
118352942Sralph 	/* check for next chunk */
118453080Sralph 	state->flags |= DMA_IN_PROGRESS;
118552889Sbostic 	if (state->dmalen != state->buflen) {
118652889Sbostic 		regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA;
118752942Sralph 		asc->script = &asc_scripts[SCRIPT_CONTINUE_IN];
118852889Sbostic 		return (0);
118952889Sbostic 	}
119052889Sbostic 	return (1);
119152889Sbostic }
119252889Sbostic 
119352889Sbostic /* ARGSUSED */
119452889Sbostic static int
119552889Sbostic asc_dma_out(asc, status, ss, ir)
119652889Sbostic 	register asc_softc_t asc;
119752889Sbostic 	register int status, ss, ir;
119852889Sbostic {
119952889Sbostic 	register asc_regmap_t *regs = asc->regs;
120052889Sbostic 	register State *state = &asc->st[asc->target];
120152889Sbostic 	register int len, fifo;
120252889Sbostic 
120353080Sralph 	if (state->flags & DMA_IN_PROGRESS) {
120452889Sbostic 		/* check to be sure previous chunk was finished */
120552889Sbostic 		ASC_TC_GET(regs, len);
120652889Sbostic 		fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT;
120752889Sbostic 		if (len || fifo)
120852889Sbostic 			printf("asc_dma_out: buflen %d dmalen %d tc %d fifo %d\n",
120952889Sbostic 				state->buflen, state->dmalen, len, fifo); /* XXX */
121052889Sbostic 		len += fifo;
121152889Sbostic 		len = state->dmalen - len;
121253080Sralph 		state->buf += len;
121352889Sbostic 		state->buflen -= len;
121453080Sralph 	}
121552889Sbostic 
121653080Sralph 	/* setup for this chunck */
121753080Sralph 	len = state->buflen;
121853080Sralph 	if (len > state->dmaBufSize)
121953080Sralph 		len = state->dmaBufSize;
122053080Sralph 	state->dmalen = len;
122153080Sralph 	bcopy(state->buf, state->dmaBufAddr, len);
122253080Sralph 	*asc->dmar = ASC_DMAR_WRITE | ASC_DMA_ADDR(state->dmaBufAddr);
122352942Sralph 	ASC_TC_PUT(regs, len);
122452889Sbostic #ifdef DEBUG
122552889Sbostic 	if (asc_debug > 2)
122652942Sralph 		printf("asc_dma_out: buflen %d, len %d\n", state->buflen, len);
122752889Sbostic #endif
122852889Sbostic 
122952889Sbostic 	/* check for next chunk */
123053080Sralph 	state->flags |= DMA_IN_PROGRESS;
123152889Sbostic 	if (len != state->buflen) {
123252889Sbostic 		regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA;
123352942Sralph 		asc->script = &asc_scripts[SCRIPT_CONTINUE_OUT];
123452889Sbostic 		return (0);
123552889Sbostic 	}
123652889Sbostic 	return (1);
123752889Sbostic }
123852889Sbostic 
123952889Sbostic /* ARGSUSED */
124052889Sbostic static int
124152889Sbostic asc_last_dma_out(asc, status, ss, ir)
124252889Sbostic 	register asc_softc_t asc;
124352889Sbostic 	register int status, ss, ir;
124452889Sbostic {
124552889Sbostic 	register asc_regmap_t *regs = asc->regs;
124652889Sbostic 	register State *state = &asc->st[asc->target];
124752889Sbostic 	register int len, fifo;
124852889Sbostic 
124952889Sbostic 	ASC_TC_GET(regs, len);
125052889Sbostic 	fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT;
125152889Sbostic #ifdef DEBUG
125252889Sbostic 	if (asc_debug > 2)
125352889Sbostic 		printf("asc_last_dma_out: buflen %d dmalen %d tc %d fifo %d\n",
125453080Sralph 			state->buflen, state->dmalen, len, fifo);
125552942Sralph #endif
125652942Sralph 	if (fifo) {
125752942Sralph 		len += fifo;
125852942Sralph 		regs->asc_cmd = ASC_CMD_FLUSH;
125952942Sralph 		MachEmptyWriteBuffer();
126052942Sralph 	}
126153080Sralph 	state->flags &= ~DMA_IN_PROGRESS;
126252889Sbostic 	len = state->dmalen - len;
126352889Sbostic 	state->buflen -= len;
126452889Sbostic 	return (1);
126552889Sbostic }
126652889Sbostic 
126752889Sbostic /* ARGSUSED */
126852889Sbostic static int
126952942Sralph asc_resume_out(asc, status, ss, ir)
127052942Sralph 	register asc_softc_t asc;
127152942Sralph 	register int status, ss, ir;
127252942Sralph {
127352942Sralph 	register asc_regmap_t *regs = asc->regs;
127452942Sralph 	register State *state = &asc->st[asc->target];
127552942Sralph 	register int len;
127652942Sralph 
127752942Sralph 	/* setup for this chunck */
127852942Sralph 	len = state->buflen;
127952942Sralph 	if (len > state->dmaBufSize)
128052942Sralph 		len = state->dmaBufSize;
128152942Sralph 	state->dmalen = len;
128252942Sralph 	bcopy(state->buf, state->dmaBufAddr, len);
128352942Sralph 	*asc->dmar = ASC_DMAR_WRITE | ASC_DMA_ADDR(state->dmaBufAddr);
128452942Sralph 	ASC_TC_PUT(regs, len);
128552942Sralph #ifdef DEBUG
128652942Sralph 	if (asc_debug > 2)
128752942Sralph 		printf("asc_resume_out: buflen %d, len %d\n", state->buflen,
128852942Sralph 			len);
128952942Sralph #endif
129052942Sralph 
129152942Sralph 	/* check for next chunk */
129253080Sralph 	state->flags |= DMA_IN_PROGRESS;
129352942Sralph 	if (len != state->buflen) {
129452942Sralph 		regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA;
129552942Sralph 		asc->script = &asc_scripts[SCRIPT_CONTINUE_OUT];
129652942Sralph 		return (0);
129752942Sralph 	}
129852942Sralph 	return (1);
129952942Sralph }
130052942Sralph 
130152942Sralph /* ARGSUSED */
130252942Sralph static int
130352889Sbostic asc_resume_dma_out(asc, status, ss, ir)
130452889Sbostic 	register asc_softc_t asc;
130552889Sbostic 	register int status, ss, ir;
130652889Sbostic {
130752889Sbostic 	register asc_regmap_t *regs = asc->regs;
130852889Sbostic 	register State *state = &asc->st[asc->target];
130952889Sbostic 	register int len, off;
131052889Sbostic 
131152889Sbostic 	/* setup to finish writing this chunk */
131252889Sbostic 	len = state->dmaresid;
131352889Sbostic 	off = state->dmalen - len;
131452889Sbostic 	if (off & 1) {
131552889Sbostic 		printf("asc_resume_dma_out: odd xfer dmalen %d len %d off %d\n",
131652889Sbostic 			state->dmalen, len, off); /* XXX */
131752889Sbostic 		regs->asc_fifo = state->dmaBufAddr[off];
131852889Sbostic 		off++;
131952889Sbostic 		len--;
132052889Sbostic 	}
132152889Sbostic 	*asc->dmar = ASC_DMAR_WRITE | ASC_DMA_ADDR(state->dmaBufAddr + off);
132252889Sbostic 	ASC_TC_PUT(regs, len);
132352942Sralph #ifdef DEBUG
132452942Sralph 	if (asc_debug > 2)
132552942Sralph 		printf("asc_resume_dma_out: buflen %d dmalen %d len %d off %d\n",
132652942Sralph 			state->dmalen, state->buflen, len, off);
132752942Sralph #endif
132852942Sralph 
132952942Sralph 	/* check for next chunk */
133053080Sralph 	state->flags |= DMA_IN_PROGRESS;
133152889Sbostic 	if (state->dmalen != state->buflen) {
133252889Sbostic 		regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA;
133352942Sralph 		asc->script = &asc_scripts[SCRIPT_CONTINUE_OUT];
133452889Sbostic 		return (0);
133552889Sbostic 	}
133652889Sbostic 	return (1);
133752889Sbostic }
133852889Sbostic 
133952889Sbostic /* ARGSUSED */
134052889Sbostic static int
134152889Sbostic asc_sendsync(asc, status, ss, ir)
134252889Sbostic 	register asc_softc_t asc;
134352889Sbostic 	register int status, ss, ir;
134452889Sbostic {
134552889Sbostic 	register asc_regmap_t *regs = asc->regs;
134653080Sralph 	register State *state = &asc->st[asc->target];
134752889Sbostic 
134853080Sralph 	/* send the extended synchronous negotiation message */
134952889Sbostic 	regs->asc_fifo = SCSI_EXTENDED_MSG;
135052889Sbostic 	MachEmptyWriteBuffer();
135152889Sbostic 	regs->asc_fifo = 3;
135252889Sbostic 	MachEmptyWriteBuffer();
135352889Sbostic 	regs->asc_fifo = SCSI_SYNCHRONOUS_XFER;
135452889Sbostic 	MachEmptyWriteBuffer();
135552889Sbostic 	regs->asc_fifo = SCSI_MIN_PERIOD;
135652889Sbostic 	MachEmptyWriteBuffer();
135752889Sbostic 	regs->asc_fifo = ASC_MAX_OFFSET;
135853080Sralph 	/* state to resume after we see the sync reply message */
135953080Sralph 	state->script = asc->script + 2;
136053080Sralph 	state->msglen = 0;
136152889Sbostic 	return (1);
136252889Sbostic }
136352889Sbostic 
136452889Sbostic /* ARGSUSED */
136552889Sbostic static int
136652889Sbostic asc_replysync(asc, status, ss, ir)
136752889Sbostic 	register asc_softc_t asc;
136852889Sbostic 	register int status, ss, ir;
136952889Sbostic {
137052889Sbostic 	register asc_regmap_t *regs = asc->regs;
137152889Sbostic 	register State *state = &asc->st[asc->target];
137252889Sbostic 
137352889Sbostic #ifdef DEBUG
137452889Sbostic 	if (asc_debug > 2)
137552889Sbostic 		printf("asc_replysync: %x %x\n",
137652889Sbostic 			asc_to_scsi_period[state->sync_period],
137752889Sbostic 			state->sync_offset);
137852889Sbostic #endif
137952889Sbostic 	/* send synchronous transfer in response to a request */
138052889Sbostic 	regs->asc_fifo = SCSI_EXTENDED_MSG;
138152889Sbostic 	MachEmptyWriteBuffer();
138252889Sbostic 	regs->asc_fifo = 3;
138352889Sbostic 	MachEmptyWriteBuffer();
138452889Sbostic 	regs->asc_fifo = SCSI_SYNCHRONOUS_XFER;
138552889Sbostic 	MachEmptyWriteBuffer();
138652889Sbostic 	regs->asc_fifo = asc_to_scsi_period[state->sync_period];
138752889Sbostic 	MachEmptyWriteBuffer();
138852889Sbostic 	regs->asc_fifo = state->sync_offset;
138952889Sbostic 	regs->asc_cmd = ASC_CMD_XFER_INFO;
139052889Sbostic 
139152889Sbostic 	/* return to the appropriate script */
139252889Sbostic 	if (!state->script) {
139352889Sbostic #ifdef DEBUG
139452889Sbostic 		asc_DumpLog("asc_replsync");
139552889Sbostic #endif
139652889Sbostic 		panic("asc_replysync");
139752889Sbostic 	}
139852889Sbostic 	asc->script = state->script;
139952889Sbostic 	state->script = (script_t *)0;
140052889Sbostic 	return (0);
140152889Sbostic }
140252889Sbostic 
140352889Sbostic /* ARGSUSED */
140452889Sbostic static int
140552942Sralph asc_msg_in(asc, status, ss, ir)
140652889Sbostic 	register asc_softc_t asc;
140752889Sbostic 	register int status, ss, ir;
140852889Sbostic {
140952889Sbostic 	register asc_regmap_t *regs = asc->regs;
141052889Sbostic 	register State *state = &asc->st[asc->target];
141152889Sbostic 	register int msg;
141252889Sbostic 	int i;
141352889Sbostic 
141452889Sbostic 	/* read one message byte */
141552889Sbostic 	msg = regs->asc_fifo;
141652889Sbostic #ifdef DEBUG
141752889Sbostic 	if (asc_logp == asc_log)
141852889Sbostic 		asc_log[NLOG - 1].msg = msg;
141952889Sbostic 	else
142052889Sbostic 		asc_logp[-1].msg = msg;
142152889Sbostic #endif
142252889Sbostic 
142352889Sbostic 	/* check for multi-byte message */
142452889Sbostic 	if (state->msglen != 0) {
142552889Sbostic 		/* first byte is the message length */
142652889Sbostic 		if (state->msglen < 0) {
142752889Sbostic 			state->msglen = msg;
142852889Sbostic 			return (1);
142952889Sbostic 		}
143052889Sbostic 		if (state->msgcnt >= state->msglen)
143152889Sbostic 			goto abort;
143252889Sbostic 		state->msg_in[state->msgcnt++] = msg;
143352889Sbostic 
143452889Sbostic 		/* did we just read the last byte of the message? */
143552889Sbostic 		if (state->msgcnt != state->msglen)
143652889Sbostic 			return (1);
143752889Sbostic 
143852889Sbostic 		/* process an extended message */
143952889Sbostic #ifdef DEBUG
144052889Sbostic 		if (asc_debug > 2)
144152942Sralph 			printf("asc_msg_in: msg %x %x %x\n",
144252889Sbostic 				state->msg_in[0],
144352889Sbostic 				state->msg_in[1],
144452889Sbostic 				state->msg_in[2]);
144552889Sbostic #endif
144652889Sbostic 		switch (state->msg_in[0]) {
144752889Sbostic 		case SCSI_SYNCHRONOUS_XFER:
144852889Sbostic 			state->flags |= DID_SYNC;
144952889Sbostic 			state->sync_offset = state->msg_in[2];
145052889Sbostic 
145152889Sbostic 			/* convert SCSI period to ASC period */
145252889Sbostic 			i = state->msg_in[1] / 10;
145352889Sbostic 			if (i < ASC_MIN_PERIOD)
145452889Sbostic 				i = ASC_MIN_PERIOD;
145552889Sbostic 			else if (i >= ASC_MAX_PERIOD) {
145652889Sbostic 				/* can't do sync transfer, period too long */
145752889Sbostic 				printf("asc%d: SCSI device %d: sync xfer period too long (%d)\n",
145852889Sbostic 					asc - asc_softc, asc->target, i);
145952889Sbostic 				i = ASC_MAX_PERIOD;
146052889Sbostic 				state->sync_offset = 0;
146152889Sbostic 			}
146252889Sbostic 			if ((i * 10) != state->msg_in[1])
146352889Sbostic 				i++;
146452889Sbostic 			state->sync_period = i & 0x1F;
146552889Sbostic 
146652889Sbostic 			/*
146752889Sbostic 			 * If this is a request, check minimums and
146852889Sbostic 			 * send back an acknowledge.
146952889Sbostic 			 */
147052889Sbostic 			if (!(state->flags & TRY_SYNC)) {
147152889Sbostic 				regs->asc_cmd = ASC_CMD_SET_ATN;
147252889Sbostic 				MachEmptyWriteBuffer();
147352889Sbostic 
147452889Sbostic 				if (state->sync_period < ASC_MIN_PERIOD)
147552889Sbostic 					state->sync_period =
147652889Sbostic 						ASC_MIN_PERIOD;
147752889Sbostic 				if (state->sync_offset > ASC_MAX_OFFSET)
147852889Sbostic 					state->sync_offset =
147952889Sbostic 						ASC_MAX_OFFSET;
148052889Sbostic 				asc->script = &asc_scripts[SCRIPT_REPLY_SYNC];
148152889Sbostic 				regs->asc_syn_p = state->sync_period;
148252889Sbostic 				regs->asc_syn_o = state->sync_offset;
148352889Sbostic 				regs->asc_cmd = ASC_CMD_MSG_ACPT;
148452889Sbostic 				return (0);
148552889Sbostic 			}
148652889Sbostic 
148752889Sbostic 			regs->asc_syn_p = state->sync_period;
148852889Sbostic 			regs->asc_syn_o = state->sync_offset;
148952889Sbostic 			goto done;
149052889Sbostic 
149152889Sbostic 		default:
149252889Sbostic 			printf("asc%d: SCSI device %d: rejecting extended message 0x%x\n",
149352889Sbostic 				asc - asc_softc, asc->target,
149452889Sbostic 				state->msg_in[0]);
149552889Sbostic 			goto reject;
149652889Sbostic 		}
149752889Sbostic 	}
149852889Sbostic 
149952889Sbostic 	/* process first byte of a message */
150052889Sbostic #ifdef DEBUG
150152889Sbostic 	if (asc_debug > 2)
150252942Sralph 		printf("asc_msg_in: msg %x\n", msg);
150352889Sbostic #endif
150452889Sbostic 	switch (msg) {
150552889Sbostic #if 0
150652889Sbostic 	case SCSI_MESSAGE_REJECT:
150752889Sbostic 		printf(" did not like SYNCH xfer "); /* XXX */
150852889Sbostic 		state->flags |= DID_SYNC;
150952889Sbostic 		regs->asc_cmd = ASC_CMD_MSG_ACPT;
151052889Sbostic 		status = asc_wait(regs, ASC_CSR_INT);
151152889Sbostic 		ir = regs->asc_intr;
151252889Sbostic 		/* some just break out here, some dont */
151352889Sbostic 		if (ASC_PHASE(status) == ASC_PHASE_MSG_OUT) {
151452889Sbostic 			regs->asc_fifo = SCSI_ABORT;
151552889Sbostic 			regs->asc_cmd = ASC_CMD_XFER_INFO;
151652889Sbostic 			status = asc_wait(regs, ASC_CSR_INT);
151752889Sbostic 			ir = regs->asc_intr;
151852889Sbostic 		}
151952889Sbostic 		if (ir & ASC_INT_DISC) {
152052889Sbostic 			asc_end(asc, status, 0, ir);
152152889Sbostic 			return (0);
152252889Sbostic 		}
152352889Sbostic 		goto status;
152452889Sbostic #endif
152552889Sbostic 
152652889Sbostic 	case SCSI_EXTENDED_MSG: /* read an extended message */
152752889Sbostic 		/* setup to read message length next */
152852889Sbostic 		state->msglen = -1;
152952889Sbostic 		state->msgcnt = 0;
153052889Sbostic 		return (1);
153152889Sbostic 
153252889Sbostic 	case SCSI_NO_OP:
153352889Sbostic 		break;
153452889Sbostic 
153552889Sbostic 	case SCSI_SAVE_DATA_POINTER:
153652889Sbostic 		/* expect another message */
153752889Sbostic 		return (1);
153852889Sbostic 
153952889Sbostic 	case SCSI_RESTORE_POINTERS:
154052889Sbostic 		/*
154152889Sbostic 		 * Need to do the following if resuming synchonous data in
154252889Sbostic 		 * on an odd byte boundary.
154352889Sbostic 		regs->asc_cnfg2 |= ASC_CNFG2_RFB;
154452889Sbostic 		 */
154552889Sbostic 		break;
154652889Sbostic 
154752889Sbostic 	case SCSI_DISCONNECT:
154852889Sbostic 		if (state->flags & DISCONN)
154952889Sbostic 			goto abort;
155052889Sbostic 		state->flags |= DISCONN;
155152942Sralph 		regs->asc_cmd = ASC_CMD_MSG_ACPT;
155252942Sralph 		asc->script = &asc_scripts[SCRIPT_DISCONNECT];
155352942Sralph 		return (0);
155452889Sbostic 
155552889Sbostic 	default:
155652889Sbostic 		printf("asc%d: SCSI device %d: rejecting message 0x%x\n",
155752889Sbostic 			asc - asc_softc, asc->target, msg);
155852889Sbostic 	reject:
155952889Sbostic 		/* request a message out before acknowledging this message */
156052889Sbostic 		state->msg_out = SCSI_MESSAGE_REJECT;
156152889Sbostic 		regs->asc_cmd = ASC_CMD_SET_ATN;
156252889Sbostic 		MachEmptyWriteBuffer();
156352889Sbostic 	}
156452889Sbostic 
156552889Sbostic done:
156652889Sbostic 	/* return to original script */
156752889Sbostic 	regs->asc_cmd = ASC_CMD_MSG_ACPT;
156852889Sbostic 	if (!state->script) {
156952889Sbostic 	abort:
157052889Sbostic #ifdef DEBUG
157152942Sralph 		asc_DumpLog("asc_msg_in");
157252889Sbostic #endif
157352942Sralph 		panic("asc_msg_in");
157452889Sbostic 	}
157552889Sbostic 	asc->script = state->script;
157652889Sbostic 	state->script = (script_t *)0;
157752889Sbostic 	return (0);
157852889Sbostic }
157952889Sbostic 
158052942Sralph /* ARGSUSED */
158152942Sralph static int
158252942Sralph asc_disconnect(asc, status, ss, ir)
158352942Sralph 	register asc_softc_t asc;
158452942Sralph 	register int status, ss, ir;
158552942Sralph {
158652942Sralph 	register State *state = &asc->st[asc->target];
158752942Sralph 
158852942Sralph 	asc->target = -1;
158952942Sralph 	asc->state = ASC_STATE_RESEL;
159052942Sralph 	return (1);
159152942Sralph }
159252942Sralph 
159352889Sbostic #ifdef DEBUG
159452889Sbostic asc_DumpLog(str)
159552889Sbostic 	char *str;
159652889Sbostic {
159752889Sbostic 	register struct asc_log *lp;
159852889Sbostic 	register u_int status;
159952889Sbostic 
160052889Sbostic 	printf("asc: %s: cmd %x bn %d cnt %d\n", str, asc_debug_cmd,
160152889Sbostic 		asc_debug_bn, asc_debug_sz);
160252889Sbostic 	lp = asc_logp + 1;
160352889Sbostic 	if (lp > &asc_log[NLOG])
160452889Sbostic 		lp = asc_log;
160552889Sbostic 	while (lp != asc_logp) {
160652889Sbostic 		status = lp->status;
160752889Sbostic 		printf("asc%d tgt %d status %x ss %x ir %x cond %d:%x msg %x\n",
160852889Sbostic 			status >> 24,
160952889Sbostic 			lp->target,
161052889Sbostic 			(status >> 16) & 0xFF,
161152889Sbostic 			(status >> 8) & 0xFF,
161252889Sbostic 			status & 0XFF,
161352889Sbostic 			lp->state,
161452889Sbostic 			asc_scripts[lp->state].condition,
161552889Sbostic 			lp->msg);
161652889Sbostic 		if (++lp >= &asc_log[NLOG])
161752889Sbostic 			lp = asc_log;
161852889Sbostic 	}
161952889Sbostic }
162052889Sbostic #endif
162152889Sbostic 
162252889Sbostic #endif	/* NASC > 0 */
1623