162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 1996 John Shifflett, GeoLog Consulting
462306a36Sopenharmony_ci *    john@geolog.com
562306a36Sopenharmony_ci *    jshiffle@netcom.com
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/*
962306a36Sopenharmony_ci * Drew Eckhardt's excellent 'Generic NCR5380' sources from Linux-PC
1062306a36Sopenharmony_ci * provided much of the inspiration and some of the code for this
1162306a36Sopenharmony_ci * driver. Everything I know about Amiga DMA was gleaned from careful
1262306a36Sopenharmony_ci * reading of Hamish Mcdonald's original wd33c93 driver; in fact, I
1362306a36Sopenharmony_ci * borrowed shamelessly from all over that source. Thanks Hamish!
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * _This_ driver is (I feel) an improvement over the old one in
1662306a36Sopenharmony_ci * several respects:
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci *    -  Target Disconnection/Reconnection  is now supported. Any
1962306a36Sopenharmony_ci *          system with more than one device active on the SCSI bus
2062306a36Sopenharmony_ci *          will benefit from this. The driver defaults to what I
2162306a36Sopenharmony_ci *          call 'adaptive disconnect' - meaning that each command
2262306a36Sopenharmony_ci *          is evaluated individually as to whether or not it should
2362306a36Sopenharmony_ci *          be run with the option to disconnect/reselect (if the
2462306a36Sopenharmony_ci *          device chooses), or as a "SCSI-bus-hog".
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci *    -  Synchronous data transfers are now supported. Because of
2762306a36Sopenharmony_ci *          a few devices that choke after telling the driver that
2862306a36Sopenharmony_ci *          they can do sync transfers, we don't automatically use
2962306a36Sopenharmony_ci *          this faster protocol - it can be enabled via the command-
3062306a36Sopenharmony_ci *          line on a device-by-device basis.
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci *    -  Runtime operating parameters can now be specified through
3362306a36Sopenharmony_ci *       the 'amiboot' or the 'insmod' command line. For amiboot do:
3462306a36Sopenharmony_ci *          "amiboot [usual stuff] wd33c93=blah,blah,blah"
3562306a36Sopenharmony_ci *       The defaults should be good for most people. See the comment
3662306a36Sopenharmony_ci *       for 'setup_strings' below for more details.
3762306a36Sopenharmony_ci *
3862306a36Sopenharmony_ci *    -  The old driver relied exclusively on what the Western Digital
3962306a36Sopenharmony_ci *          docs call "Combination Level 2 Commands", which are a great
4062306a36Sopenharmony_ci *          idea in that the CPU is relieved of a lot of interrupt
4162306a36Sopenharmony_ci *          overhead. However, by accepting a certain (user-settable)
4262306a36Sopenharmony_ci *          amount of additional interrupts, this driver achieves
4362306a36Sopenharmony_ci *          better control over the SCSI bus, and data transfers are
4462306a36Sopenharmony_ci *          almost as fast while being much easier to define, track,
4562306a36Sopenharmony_ci *          and debug.
4662306a36Sopenharmony_ci *
4762306a36Sopenharmony_ci *
4862306a36Sopenharmony_ci * TODO:
4962306a36Sopenharmony_ci *       more speed. linked commands.
5062306a36Sopenharmony_ci *
5162306a36Sopenharmony_ci *
5262306a36Sopenharmony_ci * People with bug reports, wish-lists, complaints, comments,
5362306a36Sopenharmony_ci * or improvements are asked to pah-leeez email me (John Shifflett)
5462306a36Sopenharmony_ci * at john@geolog.com or jshiffle@netcom.com! I'm anxious to get
5562306a36Sopenharmony_ci * this thing into as good a shape as possible, and I'm positive
5662306a36Sopenharmony_ci * there are lots of lurking bugs and "Stupid Places".
5762306a36Sopenharmony_ci *
5862306a36Sopenharmony_ci * Updates:
5962306a36Sopenharmony_ci *
6062306a36Sopenharmony_ci * Added support for pre -A chips, which don't have advanced features
6162306a36Sopenharmony_ci * and will generate CSR_RESEL rather than CSR_RESEL_AM.
6262306a36Sopenharmony_ci *	Richard Hirst <richard@sleepie.demon.co.uk>  August 2000
6362306a36Sopenharmony_ci *
6462306a36Sopenharmony_ci * Added support for Burst Mode DMA and Fast SCSI. Enabled the use of
6562306a36Sopenharmony_ci * default_sx_per for asynchronous data transfers. Added adjustment
6662306a36Sopenharmony_ci * of transfer periods in sx_table to the actual input-clock.
6762306a36Sopenharmony_ci *  peter fuerst <post@pfrst.de>  February 2007
6862306a36Sopenharmony_ci */
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci#include <linux/module.h>
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci#include <linux/string.h>
7362306a36Sopenharmony_ci#include <linux/delay.h>
7462306a36Sopenharmony_ci#include <linux/init.h>
7562306a36Sopenharmony_ci#include <linux/interrupt.h>
7662306a36Sopenharmony_ci#include <linux/blkdev.h>
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci#include <scsi/scsi.h>
7962306a36Sopenharmony_ci#include <scsi/scsi_cmnd.h>
8062306a36Sopenharmony_ci#include <scsi/scsi_device.h>
8162306a36Sopenharmony_ci#include <scsi/scsi_host.h>
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci#include <asm/irq.h>
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci#include "wd33c93.h"
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci#define optimum_sx_per(hostdata) (hostdata)->sx_table[1].period_ns
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci#define WD33C93_VERSION    "1.26++"
9162306a36Sopenharmony_ci#define WD33C93_DATE       "10/Feb/2007"
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ciMODULE_AUTHOR("John Shifflett");
9462306a36Sopenharmony_ciMODULE_DESCRIPTION("Generic WD33C93 SCSI driver");
9562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci/*
9862306a36Sopenharmony_ci * 'setup_strings' is a single string used to pass operating parameters and
9962306a36Sopenharmony_ci * settings from the kernel/module command-line to the driver. 'setup_args[]'
10062306a36Sopenharmony_ci * is an array of strings that define the compile-time default values for
10162306a36Sopenharmony_ci * these settings. If Linux boots with an amiboot or insmod command-line,
10262306a36Sopenharmony_ci * those settings are combined with 'setup_args[]'. Note that amiboot
10362306a36Sopenharmony_ci * command-lines are prefixed with "wd33c93=" while insmod uses a
10462306a36Sopenharmony_ci * "setup_strings=" prefix. The driver recognizes the following keywords
10562306a36Sopenharmony_ci * (lower case required) and arguments:
10662306a36Sopenharmony_ci *
10762306a36Sopenharmony_ci * -  nosync:bitmask -bitmask is a byte where the 1st 7 bits correspond with
10862306a36Sopenharmony_ci *                    the 7 possible SCSI devices. Set a bit to negotiate for
10962306a36Sopenharmony_ci *                    asynchronous transfers on that device. To maintain
11062306a36Sopenharmony_ci *                    backwards compatibility, a command-line such as
11162306a36Sopenharmony_ci *                    "wd33c93=255" will be automatically translated to
11262306a36Sopenharmony_ci *                    "wd33c93=nosync:0xff".
11362306a36Sopenharmony_ci * -  nodma:x        -x = 1 to disable DMA, x = 0 to enable it. Argument is
11462306a36Sopenharmony_ci *                    optional - if not present, same as "nodma:1".
11562306a36Sopenharmony_ci * -  period:ns      -ns is the minimum # of nanoseconds in a SCSI data transfer
11662306a36Sopenharmony_ci *                    period. Default is 500; acceptable values are 250 - 1000.
11762306a36Sopenharmony_ci * -  disconnect:x   -x = 0 to never allow disconnects, 2 to always allow them.
11862306a36Sopenharmony_ci *                    x = 1 does 'adaptive' disconnects, which is the default
11962306a36Sopenharmony_ci *                    and generally the best choice.
12062306a36Sopenharmony_ci * -  debug:x        -If 'DEBUGGING_ON' is defined, x is a bit mask that causes
12162306a36Sopenharmony_ci *                    various types of debug output to printed - see the DB_xxx
12262306a36Sopenharmony_ci *                    defines in wd33c93.h
12362306a36Sopenharmony_ci * -  clock:x        -x = clock input in MHz for WD33c93 chip. Normal values
12462306a36Sopenharmony_ci *                    would be from 8 through 20. Default is 8.
12562306a36Sopenharmony_ci * -  burst:x        -x = 1 to use Burst Mode (or Demand-Mode) DMA, x = 0 to use
12662306a36Sopenharmony_ci *                    Single Byte DMA, which is the default. Argument is
12762306a36Sopenharmony_ci *                    optional - if not present, same as "burst:1".
12862306a36Sopenharmony_ci * -  fast:x         -x = 1 to enable Fast SCSI, which is only effective with
12962306a36Sopenharmony_ci *                    input-clock divisor 4 (WD33C93_FS_16_20), x = 0 to disable
13062306a36Sopenharmony_ci *                    it, which is the default.  Argument is optional - if not
13162306a36Sopenharmony_ci *                    present, same as "fast:1".
13262306a36Sopenharmony_ci * -  next           -No argument. Used to separate blocks of keywords when
13362306a36Sopenharmony_ci *                    there's more than one host adapter in the system.
13462306a36Sopenharmony_ci *
13562306a36Sopenharmony_ci * Syntax Notes:
13662306a36Sopenharmony_ci * -  Numeric arguments can be decimal or the '0x' form of hex notation. There
13762306a36Sopenharmony_ci *    _must_ be a colon between a keyword and its numeric argument, with no
13862306a36Sopenharmony_ci *    spaces.
13962306a36Sopenharmony_ci * -  Keywords are separated by commas, no spaces, in the standard kernel
14062306a36Sopenharmony_ci *    command-line manner.
14162306a36Sopenharmony_ci * -  A keyword in the 'nth' comma-separated command-line member will overwrite
14262306a36Sopenharmony_ci *    the 'nth' element of setup_args[]. A blank command-line member (in
14362306a36Sopenharmony_ci *    other words, a comma with no preceding keyword) will _not_ overwrite
14462306a36Sopenharmony_ci *    the corresponding setup_args[] element.
14562306a36Sopenharmony_ci * -  If a keyword is used more than once, the first one applies to the first
14662306a36Sopenharmony_ci *    SCSI host found, the second to the second card, etc, unless the 'next'
14762306a36Sopenharmony_ci *    keyword is used to change the order.
14862306a36Sopenharmony_ci *
14962306a36Sopenharmony_ci * Some amiboot examples (for insmod, use 'setup_strings' instead of 'wd33c93'):
15062306a36Sopenharmony_ci * -  wd33c93=nosync:255
15162306a36Sopenharmony_ci * -  wd33c93=nodma
15262306a36Sopenharmony_ci * -  wd33c93=nodma:1
15362306a36Sopenharmony_ci * -  wd33c93=disconnect:2,nosync:0x08,period:250
15462306a36Sopenharmony_ci * -  wd33c93=debug:0x1c
15562306a36Sopenharmony_ci */
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci/* Normally, no defaults are specified */
15862306a36Sopenharmony_cistatic char *setup_args[] = { "", "", "", "", "", "", "", "", "", "" };
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic char *setup_strings;
16162306a36Sopenharmony_cimodule_param(setup_strings, charp, 0);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic void wd33c93_execute(struct Scsi_Host *instance);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic inline uchar
16662306a36Sopenharmony_ciread_wd33c93(const wd33c93_regs regs, uchar reg_num)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	*regs.SASR = reg_num;
16962306a36Sopenharmony_ci	mb();
17062306a36Sopenharmony_ci	return (*regs.SCMD);
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic unsigned long
17462306a36Sopenharmony_ciread_wd33c93_count(const wd33c93_regs regs)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	unsigned long value;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	*regs.SASR = WD_TRANSFER_COUNT_MSB;
17962306a36Sopenharmony_ci	mb();
18062306a36Sopenharmony_ci	value = *regs.SCMD << 16;
18162306a36Sopenharmony_ci	value |= *regs.SCMD << 8;
18262306a36Sopenharmony_ci	value |= *regs.SCMD;
18362306a36Sopenharmony_ci	mb();
18462306a36Sopenharmony_ci	return value;
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic inline uchar
18862306a36Sopenharmony_ciread_aux_stat(const wd33c93_regs regs)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	return *regs.SASR;
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic inline void
19462306a36Sopenharmony_ciwrite_wd33c93(const wd33c93_regs regs, uchar reg_num, uchar value)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	*regs.SASR = reg_num;
19762306a36Sopenharmony_ci	mb();
19862306a36Sopenharmony_ci	*regs.SCMD = value;
19962306a36Sopenharmony_ci	mb();
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic void
20362306a36Sopenharmony_ciwrite_wd33c93_count(const wd33c93_regs regs, unsigned long value)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	*regs.SASR = WD_TRANSFER_COUNT_MSB;
20662306a36Sopenharmony_ci	mb();
20762306a36Sopenharmony_ci	*regs.SCMD = value >> 16;
20862306a36Sopenharmony_ci	*regs.SCMD = value >> 8;
20962306a36Sopenharmony_ci	*regs.SCMD = value;
21062306a36Sopenharmony_ci	mb();
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic inline void
21462306a36Sopenharmony_ciwrite_wd33c93_cmd(const wd33c93_regs regs, uchar cmd)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	*regs.SASR = WD_COMMAND;
21762306a36Sopenharmony_ci	mb();
21862306a36Sopenharmony_ci	*regs.SCMD = cmd;
21962306a36Sopenharmony_ci	mb();
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic inline void
22362306a36Sopenharmony_ciwrite_wd33c93_cdb(const wd33c93_regs regs, uint len, uchar cmnd[])
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	int i;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	*regs.SASR = WD_CDB_1;
22862306a36Sopenharmony_ci	for (i = 0; i < len; i++)
22962306a36Sopenharmony_ci		*regs.SCMD = cmnd[i];
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic inline uchar
23362306a36Sopenharmony_ciread_1_byte(const wd33c93_regs regs)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	uchar asr;
23662306a36Sopenharmony_ci	uchar x = 0;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
23962306a36Sopenharmony_ci	write_wd33c93_cmd(regs, WD_CMD_TRANS_INFO | 0x80);
24062306a36Sopenharmony_ci	do {
24162306a36Sopenharmony_ci		asr = read_aux_stat(regs);
24262306a36Sopenharmony_ci		if (asr & ASR_DBR)
24362306a36Sopenharmony_ci			x = read_wd33c93(regs, WD_DATA);
24462306a36Sopenharmony_ci	} while (!(asr & ASR_INT));
24562306a36Sopenharmony_ci	return x;
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic int
24962306a36Sopenharmony_ciround_period(unsigned int period, const struct sx_period *sx_table)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	int x;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	for (x = 1; sx_table[x].period_ns; x++) {
25462306a36Sopenharmony_ci		if ((period <= sx_table[x - 0].period_ns) &&
25562306a36Sopenharmony_ci		    (period > sx_table[x - 1].period_ns)) {
25662306a36Sopenharmony_ci			return x;
25762306a36Sopenharmony_ci		}
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci	return 7;
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci/*
26362306a36Sopenharmony_ci * Calculate Synchronous Transfer Register value from SDTR code.
26462306a36Sopenharmony_ci */
26562306a36Sopenharmony_cistatic uchar
26662306a36Sopenharmony_cicalc_sync_xfer(unsigned int period, unsigned int offset, unsigned int fast,
26762306a36Sopenharmony_ci               const struct sx_period *sx_table)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	/* When doing Fast SCSI synchronous data transfers, the corresponding
27062306a36Sopenharmony_ci	 * value in 'sx_table' is two times the actually used transfer period.
27162306a36Sopenharmony_ci	 */
27262306a36Sopenharmony_ci	uchar result;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	if (offset && fast) {
27562306a36Sopenharmony_ci		fast = STR_FSS;
27662306a36Sopenharmony_ci		period *= 2;
27762306a36Sopenharmony_ci	} else {
27862306a36Sopenharmony_ci		fast = 0;
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci	period *= 4;		/* convert SDTR code to ns */
28162306a36Sopenharmony_ci	result = sx_table[round_period(period,sx_table)].reg_value;
28262306a36Sopenharmony_ci	result |= (offset < OPTIMUM_SX_OFF) ? offset : OPTIMUM_SX_OFF;
28362306a36Sopenharmony_ci	result |= fast;
28462306a36Sopenharmony_ci	return result;
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci/*
28862306a36Sopenharmony_ci * Calculate SDTR code bytes [3],[4] from period and offset.
28962306a36Sopenharmony_ci */
29062306a36Sopenharmony_cistatic inline void
29162306a36Sopenharmony_cicalc_sync_msg(unsigned int period, unsigned int offset, unsigned int fast,
29262306a36Sopenharmony_ci                uchar  msg[2])
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	/* 'period' is a "normal"-mode value, like the ones in 'sx_table'. The
29562306a36Sopenharmony_ci	 * actually used transfer period for Fast SCSI synchronous data
29662306a36Sopenharmony_ci	 * transfers is half that value.
29762306a36Sopenharmony_ci	 */
29862306a36Sopenharmony_ci	period /= 4;
29962306a36Sopenharmony_ci	if (offset && fast)
30062306a36Sopenharmony_ci		period /= 2;
30162306a36Sopenharmony_ci	msg[0] = period;
30262306a36Sopenharmony_ci	msg[1] = offset;
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic int wd33c93_queuecommand_lck(struct scsi_cmnd *cmd)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	struct scsi_pointer *scsi_pointer = WD33C93_scsi_pointer(cmd);
30862306a36Sopenharmony_ci	struct WD33C93_hostdata *hostdata;
30962306a36Sopenharmony_ci	struct scsi_cmnd *tmp;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	hostdata = (struct WD33C93_hostdata *) cmd->device->host->hostdata;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	DB(DB_QUEUE_COMMAND,
31462306a36Sopenharmony_ci	   printk("Q-%d-%02x( ", cmd->device->id, cmd->cmnd[0]))
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci/* Set up a few fields in the scsi_cmnd structure for our own use:
31762306a36Sopenharmony_ci *  - host_scribble is the pointer to the next cmd in the input queue
31862306a36Sopenharmony_ci *  - result is what you'd expect
31962306a36Sopenharmony_ci */
32062306a36Sopenharmony_ci	cmd->host_scribble = NULL;
32162306a36Sopenharmony_ci	cmd->result = 0;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci/* We use the Scsi_Pointer structure that's included with each command
32462306a36Sopenharmony_ci * as a scratchpad (as it's intended to be used!). The handy thing about
32562306a36Sopenharmony_ci * the SCp.xxx fields is that they're always associated with a given
32662306a36Sopenharmony_ci * cmd, and are preserved across disconnect-reselect. This means we
32762306a36Sopenharmony_ci * can pretty much ignore SAVE_POINTERS and RESTORE_POINTERS messages
32862306a36Sopenharmony_ci * if we keep all the critical pointers and counters in SCp:
32962306a36Sopenharmony_ci *  - SCp.ptr is the pointer into the RAM buffer
33062306a36Sopenharmony_ci *  - SCp.this_residual is the size of that buffer
33162306a36Sopenharmony_ci *  - SCp.buffer points to the current scatter-gather buffer
33262306a36Sopenharmony_ci *  - SCp.buffers_residual tells us how many S.G. buffers there are
33362306a36Sopenharmony_ci *  - SCp.have_data_in is not used
33462306a36Sopenharmony_ci *  - SCp.sent_command is not used
33562306a36Sopenharmony_ci *  - SCp.phase records this command's SRCID_ER bit setting
33662306a36Sopenharmony_ci */
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	if (scsi_bufflen(cmd)) {
33962306a36Sopenharmony_ci		scsi_pointer->buffer = scsi_sglist(cmd);
34062306a36Sopenharmony_ci		scsi_pointer->buffers_residual = scsi_sg_count(cmd) - 1;
34162306a36Sopenharmony_ci		scsi_pointer->ptr = sg_virt(scsi_pointer->buffer);
34262306a36Sopenharmony_ci		scsi_pointer->this_residual = scsi_pointer->buffer->length;
34362306a36Sopenharmony_ci	} else {
34462306a36Sopenharmony_ci		scsi_pointer->buffer = NULL;
34562306a36Sopenharmony_ci		scsi_pointer->buffers_residual = 0;
34662306a36Sopenharmony_ci		scsi_pointer->ptr = NULL;
34762306a36Sopenharmony_ci		scsi_pointer->this_residual = 0;
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci/* WD docs state that at the conclusion of a "LEVEL2" command, the
35162306a36Sopenharmony_ci * status byte can be retrieved from the LUN register. Apparently,
35262306a36Sopenharmony_ci * this is the case only for *uninterrupted* LEVEL2 commands! If
35362306a36Sopenharmony_ci * there are any unexpected phases entered, even if they are 100%
35462306a36Sopenharmony_ci * legal (different devices may choose to do things differently),
35562306a36Sopenharmony_ci * the LEVEL2 command sequence is exited. This often occurs prior
35662306a36Sopenharmony_ci * to receiving the status byte, in which case the driver does a
35762306a36Sopenharmony_ci * status phase interrupt and gets the status byte on its own.
35862306a36Sopenharmony_ci * While such a command can then be "resumed" (ie restarted to
35962306a36Sopenharmony_ci * finish up as a LEVEL2 command), the LUN register will NOT be
36062306a36Sopenharmony_ci * a valid status byte at the command's conclusion, and we must
36162306a36Sopenharmony_ci * use the byte obtained during the earlier interrupt. Here, we
36262306a36Sopenharmony_ci * preset SCp.Status to an illegal value (0xff) so that when
36362306a36Sopenharmony_ci * this command finally completes, we can tell where the actual
36462306a36Sopenharmony_ci * status byte is stored.
36562306a36Sopenharmony_ci */
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	scsi_pointer->Status = ILLEGAL_STATUS_BYTE;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	/*
37062306a36Sopenharmony_ci	 * Add the cmd to the end of 'input_Q'. Note that REQUEST SENSE
37162306a36Sopenharmony_ci	 * commands are added to the head of the queue so that the desired
37262306a36Sopenharmony_ci	 * sense data is not lost before REQUEST_SENSE executes.
37362306a36Sopenharmony_ci	 */
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	spin_lock_irq(&hostdata->lock);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	if (!(hostdata->input_Q) || (cmd->cmnd[0] == REQUEST_SENSE)) {
37862306a36Sopenharmony_ci		cmd->host_scribble = (uchar *) hostdata->input_Q;
37962306a36Sopenharmony_ci		hostdata->input_Q = cmd;
38062306a36Sopenharmony_ci	} else {		/* find the end of the queue */
38162306a36Sopenharmony_ci		for (tmp = (struct scsi_cmnd *) hostdata->input_Q;
38262306a36Sopenharmony_ci		     tmp->host_scribble;
38362306a36Sopenharmony_ci		     tmp = (struct scsi_cmnd *) tmp->host_scribble) ;
38462306a36Sopenharmony_ci		tmp->host_scribble = (uchar *) cmd;
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci/* We know that there's at least one command in 'input_Q' now.
38862306a36Sopenharmony_ci * Go see if any of them are runnable!
38962306a36Sopenharmony_ci */
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	wd33c93_execute(cmd->device->host);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	DB(DB_QUEUE_COMMAND, printk(")Q "))
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	spin_unlock_irq(&hostdata->lock);
39662306a36Sopenharmony_ci	return 0;
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ciDEF_SCSI_QCMD(wd33c93_queuecommand)
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci/*
40262306a36Sopenharmony_ci * This routine attempts to start a scsi command. If the host_card is
40362306a36Sopenharmony_ci * already connected, we give up immediately. Otherwise, look through
40462306a36Sopenharmony_ci * the input_Q, using the first command we find that's intended
40562306a36Sopenharmony_ci * for a currently non-busy target/lun.
40662306a36Sopenharmony_ci *
40762306a36Sopenharmony_ci * wd33c93_execute() is always called with interrupts disabled or from
40862306a36Sopenharmony_ci * the wd33c93_intr itself, which means that a wd33c93 interrupt
40962306a36Sopenharmony_ci * cannot occur while we are in here.
41062306a36Sopenharmony_ci */
41162306a36Sopenharmony_cistatic void
41262306a36Sopenharmony_ciwd33c93_execute(struct Scsi_Host *instance)
41362306a36Sopenharmony_ci{
41462306a36Sopenharmony_ci	struct scsi_pointer *scsi_pointer;
41562306a36Sopenharmony_ci	struct WD33C93_hostdata *hostdata =
41662306a36Sopenharmony_ci	    (struct WD33C93_hostdata *) instance->hostdata;
41762306a36Sopenharmony_ci	const wd33c93_regs regs = hostdata->regs;
41862306a36Sopenharmony_ci	struct scsi_cmnd *cmd, *prev;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	DB(DB_EXECUTE, printk("EX("))
42162306a36Sopenharmony_ci	if (hostdata->selecting || hostdata->connected) {
42262306a36Sopenharmony_ci		DB(DB_EXECUTE, printk(")EX-0 "))
42362306a36Sopenharmony_ci		return;
42462306a36Sopenharmony_ci	}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	/*
42762306a36Sopenharmony_ci	 * Search through the input_Q for a command destined
42862306a36Sopenharmony_ci	 * for an idle target/lun.
42962306a36Sopenharmony_ci	 */
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	cmd = (struct scsi_cmnd *) hostdata->input_Q;
43262306a36Sopenharmony_ci	prev = NULL;
43362306a36Sopenharmony_ci	while (cmd) {
43462306a36Sopenharmony_ci		if (!(hostdata->busy[cmd->device->id] &
43562306a36Sopenharmony_ci		      (1 << (cmd->device->lun & 0xff))))
43662306a36Sopenharmony_ci			break;
43762306a36Sopenharmony_ci		prev = cmd;
43862306a36Sopenharmony_ci		cmd = (struct scsi_cmnd *) cmd->host_scribble;
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	/* quit if queue empty or all possible targets are busy */
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	if (!cmd) {
44462306a36Sopenharmony_ci		DB(DB_EXECUTE, printk(")EX-1 "))
44562306a36Sopenharmony_ci		return;
44662306a36Sopenharmony_ci	}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	/*  remove command from queue */
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	if (prev)
45162306a36Sopenharmony_ci		prev->host_scribble = cmd->host_scribble;
45262306a36Sopenharmony_ci	else
45362306a36Sopenharmony_ci		hostdata->input_Q = (struct scsi_cmnd *) cmd->host_scribble;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci#ifdef PROC_STATISTICS
45662306a36Sopenharmony_ci	hostdata->cmd_cnt[cmd->device->id]++;
45762306a36Sopenharmony_ci#endif
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	/*
46062306a36Sopenharmony_ci	 * Start the selection process
46162306a36Sopenharmony_ci	 */
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	if (cmd->sc_data_direction == DMA_TO_DEVICE)
46462306a36Sopenharmony_ci		write_wd33c93(regs, WD_DESTINATION_ID, cmd->device->id);
46562306a36Sopenharmony_ci	else
46662306a36Sopenharmony_ci		write_wd33c93(regs, WD_DESTINATION_ID, cmd->device->id | DSTID_DPD);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci/* Now we need to figure out whether or not this command is a good
46962306a36Sopenharmony_ci * candidate for disconnect/reselect. We guess to the best of our
47062306a36Sopenharmony_ci * ability, based on a set of hierarchical rules. When several
47162306a36Sopenharmony_ci * devices are operating simultaneously, disconnects are usually
47262306a36Sopenharmony_ci * an advantage. In a single device system, or if only 1 device
47362306a36Sopenharmony_ci * is being accessed, transfers usually go faster if disconnects
47462306a36Sopenharmony_ci * are not allowed:
47562306a36Sopenharmony_ci *
47662306a36Sopenharmony_ci * + Commands should NEVER disconnect if hostdata->disconnect =
47762306a36Sopenharmony_ci *   DIS_NEVER (this holds for tape drives also), and ALWAYS
47862306a36Sopenharmony_ci *   disconnect if hostdata->disconnect = DIS_ALWAYS.
47962306a36Sopenharmony_ci * + Tape drive commands should always be allowed to disconnect.
48062306a36Sopenharmony_ci * + Disconnect should be allowed if disconnected_Q isn't empty.
48162306a36Sopenharmony_ci * + Commands should NOT disconnect if input_Q is empty.
48262306a36Sopenharmony_ci * + Disconnect should be allowed if there are commands in input_Q
48362306a36Sopenharmony_ci *   for a different target/lun. In this case, the other commands
48462306a36Sopenharmony_ci *   should be made disconnect-able, if not already.
48562306a36Sopenharmony_ci *
48662306a36Sopenharmony_ci * I know, I know - this code would flunk me out of any
48762306a36Sopenharmony_ci * "C Programming 101" class ever offered. But it's easy
48862306a36Sopenharmony_ci * to change around and experiment with for now.
48962306a36Sopenharmony_ci */
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	scsi_pointer = WD33C93_scsi_pointer(cmd);
49262306a36Sopenharmony_ci	scsi_pointer->phase = 0;	/* assume no disconnect */
49362306a36Sopenharmony_ci	if (hostdata->disconnect == DIS_NEVER)
49462306a36Sopenharmony_ci		goto no;
49562306a36Sopenharmony_ci	if (hostdata->disconnect == DIS_ALWAYS)
49662306a36Sopenharmony_ci		goto yes;
49762306a36Sopenharmony_ci	if (cmd->device->type == 1)	/* tape drive? */
49862306a36Sopenharmony_ci		goto yes;
49962306a36Sopenharmony_ci	if (hostdata->disconnected_Q)	/* other commands disconnected? */
50062306a36Sopenharmony_ci		goto yes;
50162306a36Sopenharmony_ci	if (!(hostdata->input_Q))	/* input_Q empty? */
50262306a36Sopenharmony_ci		goto no;
50362306a36Sopenharmony_ci	for (prev = (struct scsi_cmnd *) hostdata->input_Q; prev;
50462306a36Sopenharmony_ci	     prev = (struct scsi_cmnd *) prev->host_scribble) {
50562306a36Sopenharmony_ci		if ((prev->device->id != cmd->device->id) ||
50662306a36Sopenharmony_ci		    (prev->device->lun != cmd->device->lun)) {
50762306a36Sopenharmony_ci			for (prev = (struct scsi_cmnd *) hostdata->input_Q; prev;
50862306a36Sopenharmony_ci			     prev = (struct scsi_cmnd *) prev->host_scribble)
50962306a36Sopenharmony_ci				WD33C93_scsi_pointer(prev)->phase = 1;
51062306a36Sopenharmony_ci			goto yes;
51162306a36Sopenharmony_ci		}
51262306a36Sopenharmony_ci	}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	goto no;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci yes:
51762306a36Sopenharmony_ci	scsi_pointer->phase = 1;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci#ifdef PROC_STATISTICS
52062306a36Sopenharmony_ci	hostdata->disc_allowed_cnt[cmd->device->id]++;
52162306a36Sopenharmony_ci#endif
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci no:
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	write_wd33c93(regs, WD_SOURCE_ID, scsi_pointer->phase ? SRCID_ER : 0);
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	write_wd33c93(regs, WD_TARGET_LUN, (u8)cmd->device->lun);
52862306a36Sopenharmony_ci	write_wd33c93(regs, WD_SYNCHRONOUS_TRANSFER,
52962306a36Sopenharmony_ci		      hostdata->sync_xfer[cmd->device->id]);
53062306a36Sopenharmony_ci	hostdata->busy[cmd->device->id] |= (1 << (cmd->device->lun & 0xFF));
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	if ((hostdata->level2 == L2_NONE) ||
53362306a36Sopenharmony_ci	    (hostdata->sync_stat[cmd->device->id] == SS_UNSET)) {
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci		/*
53662306a36Sopenharmony_ci		 * Do a 'Select-With-ATN' command. This will end with
53762306a36Sopenharmony_ci		 * one of the following interrupts:
53862306a36Sopenharmony_ci		 *    CSR_RESEL_AM:  failure - can try again later.
53962306a36Sopenharmony_ci		 *    CSR_TIMEOUT:   failure - give up.
54062306a36Sopenharmony_ci		 *    CSR_SELECT:    success - proceed.
54162306a36Sopenharmony_ci		 */
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci		hostdata->selecting = cmd;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci/* Every target has its own synchronous transfer setting, kept in the
54662306a36Sopenharmony_ci * sync_xfer array, and a corresponding status byte in sync_stat[].
54762306a36Sopenharmony_ci * Each target's sync_stat[] entry is initialized to SX_UNSET, and its
54862306a36Sopenharmony_ci * sync_xfer[] entry is initialized to the default/safe value. SS_UNSET
54962306a36Sopenharmony_ci * means that the parameters are undetermined as yet, and that we
55062306a36Sopenharmony_ci * need to send an SDTR message to this device after selection is
55162306a36Sopenharmony_ci * complete: We set SS_FIRST to tell the interrupt routine to do so.
55262306a36Sopenharmony_ci * If we've been asked not to try synchronous transfers on this
55362306a36Sopenharmony_ci * target (and _all_ luns within it), we'll still send the SDTR message
55462306a36Sopenharmony_ci * later, but at that time we'll negotiate for async by specifying a
55562306a36Sopenharmony_ci * sync fifo depth of 0.
55662306a36Sopenharmony_ci */
55762306a36Sopenharmony_ci		if (hostdata->sync_stat[cmd->device->id] == SS_UNSET)
55862306a36Sopenharmony_ci			hostdata->sync_stat[cmd->device->id] = SS_FIRST;
55962306a36Sopenharmony_ci		hostdata->state = S_SELECTING;
56062306a36Sopenharmony_ci		write_wd33c93_count(regs, 0);	/* guarantee a DATA_PHASE interrupt */
56162306a36Sopenharmony_ci		write_wd33c93_cmd(regs, WD_CMD_SEL_ATN);
56262306a36Sopenharmony_ci	} else {
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci		/*
56562306a36Sopenharmony_ci		 * Do a 'Select-With-ATN-Xfer' command. This will end with
56662306a36Sopenharmony_ci		 * one of the following interrupts:
56762306a36Sopenharmony_ci		 *    CSR_RESEL_AM:  failure - can try again later.
56862306a36Sopenharmony_ci		 *    CSR_TIMEOUT:   failure - give up.
56962306a36Sopenharmony_ci		 *    anything else: success - proceed.
57062306a36Sopenharmony_ci		 */
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci		hostdata->connected = cmd;
57362306a36Sopenharmony_ci		write_wd33c93(regs, WD_COMMAND_PHASE, 0);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci		/* copy command_descriptor_block into WD chip
57662306a36Sopenharmony_ci		 * (take advantage of auto-incrementing)
57762306a36Sopenharmony_ci		 */
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci		write_wd33c93_cdb(regs, cmd->cmd_len, cmd->cmnd);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci		/* The wd33c93 only knows about Group 0, 1, and 5 commands when
58262306a36Sopenharmony_ci		 * it's doing a 'select-and-transfer'. To be safe, we write the
58362306a36Sopenharmony_ci		 * size of the CDB into the OWN_ID register for every case. This
58462306a36Sopenharmony_ci		 * way there won't be problems with vendor-unique, audio, etc.
58562306a36Sopenharmony_ci		 */
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci		write_wd33c93(regs, WD_OWN_ID, cmd->cmd_len);
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci		/* When doing a non-disconnect command with DMA, we can save
59062306a36Sopenharmony_ci		 * ourselves a DATA phase interrupt later by setting everything
59162306a36Sopenharmony_ci		 * up ahead of time.
59262306a36Sopenharmony_ci		 */
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci		if (scsi_pointer->phase == 0 && hostdata->no_dma == 0) {
59562306a36Sopenharmony_ci			if (hostdata->dma_setup(cmd,
59662306a36Sopenharmony_ci			    (cmd->sc_data_direction == DMA_TO_DEVICE) ?
59762306a36Sopenharmony_ci			     DATA_OUT_DIR : DATA_IN_DIR))
59862306a36Sopenharmony_ci				write_wd33c93_count(regs, 0);	/* guarantee a DATA_PHASE interrupt */
59962306a36Sopenharmony_ci			else {
60062306a36Sopenharmony_ci				write_wd33c93_count(regs,
60162306a36Sopenharmony_ci						scsi_pointer->this_residual);
60262306a36Sopenharmony_ci				write_wd33c93(regs, WD_CONTROL,
60362306a36Sopenharmony_ci					      CTRL_IDI | CTRL_EDI | hostdata->dma_mode);
60462306a36Sopenharmony_ci				hostdata->dma = D_DMA_RUNNING;
60562306a36Sopenharmony_ci			}
60662306a36Sopenharmony_ci		} else
60762306a36Sopenharmony_ci			write_wd33c93_count(regs, 0);	/* guarantee a DATA_PHASE interrupt */
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci		hostdata->state = S_RUNNING_LEVEL2;
61062306a36Sopenharmony_ci		write_wd33c93_cmd(regs, WD_CMD_SEL_ATN_XFER);
61162306a36Sopenharmony_ci	}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	/*
61462306a36Sopenharmony_ci	 * Since the SCSI bus can handle only 1 connection at a time,
61562306a36Sopenharmony_ci	 * we get out of here now. If the selection fails, or when
61662306a36Sopenharmony_ci	 * the command disconnects, we'll come back to this routine
61762306a36Sopenharmony_ci	 * to search the input_Q again...
61862306a36Sopenharmony_ci	 */
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	DB(DB_EXECUTE,
62162306a36Sopenharmony_ci	   printk("%s)EX-2 ", scsi_pointer->phase ? "d:" : ""))
62262306a36Sopenharmony_ci}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_cistatic void
62562306a36Sopenharmony_citransfer_pio(const wd33c93_regs regs, uchar * buf, int cnt,
62662306a36Sopenharmony_ci	     int data_in_dir, struct WD33C93_hostdata *hostdata)
62762306a36Sopenharmony_ci{
62862306a36Sopenharmony_ci	uchar asr;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	DB(DB_TRANSFER,
63162306a36Sopenharmony_ci	   printk("(%p,%d,%s:", buf, cnt, data_in_dir ? "in" : "out"))
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
63462306a36Sopenharmony_ci	write_wd33c93_count(regs, cnt);
63562306a36Sopenharmony_ci	write_wd33c93_cmd(regs, WD_CMD_TRANS_INFO);
63662306a36Sopenharmony_ci	if (data_in_dir) {
63762306a36Sopenharmony_ci		do {
63862306a36Sopenharmony_ci			asr = read_aux_stat(regs);
63962306a36Sopenharmony_ci			if (asr & ASR_DBR)
64062306a36Sopenharmony_ci				*buf++ = read_wd33c93(regs, WD_DATA);
64162306a36Sopenharmony_ci		} while (!(asr & ASR_INT));
64262306a36Sopenharmony_ci	} else {
64362306a36Sopenharmony_ci		do {
64462306a36Sopenharmony_ci			asr = read_aux_stat(regs);
64562306a36Sopenharmony_ci			if (asr & ASR_DBR)
64662306a36Sopenharmony_ci				write_wd33c93(regs, WD_DATA, *buf++);
64762306a36Sopenharmony_ci		} while (!(asr & ASR_INT));
64862306a36Sopenharmony_ci	}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	/* Note: we are returning with the interrupt UN-cleared.
65162306a36Sopenharmony_ci	 * Since (presumably) an entire I/O operation has
65262306a36Sopenharmony_ci	 * completed, the bus phase is probably different, and
65362306a36Sopenharmony_ci	 * the interrupt routine will discover this when it
65462306a36Sopenharmony_ci	 * responds to the uncleared int.
65562306a36Sopenharmony_ci	 */
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_cistatic void
66062306a36Sopenharmony_citransfer_bytes(const wd33c93_regs regs, struct scsi_cmnd *cmd,
66162306a36Sopenharmony_ci		int data_in_dir)
66262306a36Sopenharmony_ci{
66362306a36Sopenharmony_ci	struct scsi_pointer *scsi_pointer = WD33C93_scsi_pointer(cmd);
66462306a36Sopenharmony_ci	struct WD33C93_hostdata *hostdata;
66562306a36Sopenharmony_ci	unsigned long length;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	hostdata = (struct WD33C93_hostdata *) cmd->device->host->hostdata;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci/* Normally, you'd expect 'this_residual' to be non-zero here.
67062306a36Sopenharmony_ci * In a series of scatter-gather transfers, however, this
67162306a36Sopenharmony_ci * routine will usually be called with 'this_residual' equal
67262306a36Sopenharmony_ci * to 0 and 'buffers_residual' non-zero. This means that a
67362306a36Sopenharmony_ci * previous transfer completed, clearing 'this_residual', and
67462306a36Sopenharmony_ci * now we need to setup the next scatter-gather buffer as the
67562306a36Sopenharmony_ci * source or destination for THIS transfer.
67662306a36Sopenharmony_ci */
67762306a36Sopenharmony_ci	if (!scsi_pointer->this_residual && scsi_pointer->buffers_residual) {
67862306a36Sopenharmony_ci		scsi_pointer->buffer = sg_next(scsi_pointer->buffer);
67962306a36Sopenharmony_ci		--scsi_pointer->buffers_residual;
68062306a36Sopenharmony_ci		scsi_pointer->this_residual = scsi_pointer->buffer->length;
68162306a36Sopenharmony_ci		scsi_pointer->ptr = sg_virt(scsi_pointer->buffer);
68262306a36Sopenharmony_ci	}
68362306a36Sopenharmony_ci	if (!scsi_pointer->this_residual) /* avoid bogus setups */
68462306a36Sopenharmony_ci		return;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	write_wd33c93(regs, WD_SYNCHRONOUS_TRANSFER,
68762306a36Sopenharmony_ci		      hostdata->sync_xfer[cmd->device->id]);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci/* 'hostdata->no_dma' is TRUE if we don't even want to try DMA.
69062306a36Sopenharmony_ci * Update 'this_residual' and 'ptr' after 'transfer_pio()' returns.
69162306a36Sopenharmony_ci */
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	if (hostdata->no_dma || hostdata->dma_setup(cmd, data_in_dir)) {
69462306a36Sopenharmony_ci#ifdef PROC_STATISTICS
69562306a36Sopenharmony_ci		hostdata->pio_cnt++;
69662306a36Sopenharmony_ci#endif
69762306a36Sopenharmony_ci		transfer_pio(regs, (uchar *) scsi_pointer->ptr,
69862306a36Sopenharmony_ci			     scsi_pointer->this_residual, data_in_dir,
69962306a36Sopenharmony_ci			     hostdata);
70062306a36Sopenharmony_ci		length = scsi_pointer->this_residual;
70162306a36Sopenharmony_ci		scsi_pointer->this_residual = read_wd33c93_count(regs);
70262306a36Sopenharmony_ci		scsi_pointer->ptr += length - scsi_pointer->this_residual;
70362306a36Sopenharmony_ci	}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci/* We are able to do DMA (in fact, the Amiga hardware is
70662306a36Sopenharmony_ci * already going!), so start up the wd33c93 in DMA mode.
70762306a36Sopenharmony_ci * We set 'hostdata->dma' = D_DMA_RUNNING so that when the
70862306a36Sopenharmony_ci * transfer completes and causes an interrupt, we're
70962306a36Sopenharmony_ci * reminded to tell the Amiga to shut down its end. We'll
71062306a36Sopenharmony_ci * postpone the updating of 'this_residual' and 'ptr'
71162306a36Sopenharmony_ci * until then.
71262306a36Sopenharmony_ci */
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	else {
71562306a36Sopenharmony_ci#ifdef PROC_STATISTICS
71662306a36Sopenharmony_ci		hostdata->dma_cnt++;
71762306a36Sopenharmony_ci#endif
71862306a36Sopenharmony_ci		write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | hostdata->dma_mode);
71962306a36Sopenharmony_ci		write_wd33c93_count(regs, scsi_pointer->this_residual);
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci		if ((hostdata->level2 >= L2_DATA) ||
72262306a36Sopenharmony_ci		    (hostdata->level2 == L2_BASIC && scsi_pointer->phase == 0)) {
72362306a36Sopenharmony_ci			write_wd33c93(regs, WD_COMMAND_PHASE, 0x45);
72462306a36Sopenharmony_ci			write_wd33c93_cmd(regs, WD_CMD_SEL_ATN_XFER);
72562306a36Sopenharmony_ci			hostdata->state = S_RUNNING_LEVEL2;
72662306a36Sopenharmony_ci		} else
72762306a36Sopenharmony_ci			write_wd33c93_cmd(regs, WD_CMD_TRANS_INFO);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci		hostdata->dma = D_DMA_RUNNING;
73062306a36Sopenharmony_ci	}
73162306a36Sopenharmony_ci}
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_civoid
73462306a36Sopenharmony_ciwd33c93_intr(struct Scsi_Host *instance)
73562306a36Sopenharmony_ci{
73662306a36Sopenharmony_ci	struct scsi_pointer *scsi_pointer;
73762306a36Sopenharmony_ci	struct WD33C93_hostdata *hostdata =
73862306a36Sopenharmony_ci	    (struct WD33C93_hostdata *) instance->hostdata;
73962306a36Sopenharmony_ci	const wd33c93_regs regs = hostdata->regs;
74062306a36Sopenharmony_ci	struct scsi_cmnd *patch, *cmd;
74162306a36Sopenharmony_ci	uchar asr, sr, phs, id, lun, *ucp, msg;
74262306a36Sopenharmony_ci	unsigned long length, flags;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	asr = read_aux_stat(regs);
74562306a36Sopenharmony_ci	if (!(asr & ASR_INT) || (asr & ASR_BSY))
74662306a36Sopenharmony_ci		return;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	spin_lock_irqsave(&hostdata->lock, flags);
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci#ifdef PROC_STATISTICS
75162306a36Sopenharmony_ci	hostdata->int_cnt++;
75262306a36Sopenharmony_ci#endif
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	cmd = (struct scsi_cmnd *) hostdata->connected;	/* assume we're connected */
75562306a36Sopenharmony_ci	scsi_pointer = WD33C93_scsi_pointer(cmd);
75662306a36Sopenharmony_ci	sr = read_wd33c93(regs, WD_SCSI_STATUS);	/* clear the interrupt */
75762306a36Sopenharmony_ci	phs = read_wd33c93(regs, WD_COMMAND_PHASE);
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	DB(DB_INTR, printk("{%02x:%02x-", asr, sr))
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci/* After starting a DMA transfer, the next interrupt
76262306a36Sopenharmony_ci * is guaranteed to be in response to completion of
76362306a36Sopenharmony_ci * the transfer. Since the Amiga DMA hardware runs in
76462306a36Sopenharmony_ci * in an open-ended fashion, it needs to be told when
76562306a36Sopenharmony_ci * to stop; do that here if D_DMA_RUNNING is true.
76662306a36Sopenharmony_ci * Also, we have to update 'this_residual' and 'ptr'
76762306a36Sopenharmony_ci * based on the contents of the TRANSFER_COUNT register,
76862306a36Sopenharmony_ci * in case the device decided to do an intermediate
76962306a36Sopenharmony_ci * disconnect (a device may do this if it has to do a
77062306a36Sopenharmony_ci * seek, or just to be nice and let other devices have
77162306a36Sopenharmony_ci * some bus time during long transfers). After doing
77262306a36Sopenharmony_ci * whatever is needed, we go on and service the WD3393
77362306a36Sopenharmony_ci * interrupt normally.
77462306a36Sopenharmony_ci */
77562306a36Sopenharmony_ci	    if (hostdata->dma == D_DMA_RUNNING) {
77662306a36Sopenharmony_ci		DB(DB_TRANSFER,
77762306a36Sopenharmony_ci		   printk("[%p/%d:", scsi_pointer->ptr, scsi_pointer->this_residual))
77862306a36Sopenharmony_ci		    hostdata->dma_stop(cmd->device->host, cmd, 1);
77962306a36Sopenharmony_ci		hostdata->dma = D_DMA_OFF;
78062306a36Sopenharmony_ci		length = scsi_pointer->this_residual;
78162306a36Sopenharmony_ci		scsi_pointer->this_residual = read_wd33c93_count(regs);
78262306a36Sopenharmony_ci		scsi_pointer->ptr += length - scsi_pointer->this_residual;
78362306a36Sopenharmony_ci		DB(DB_TRANSFER,
78462306a36Sopenharmony_ci		   printk("%p/%d]", scsi_pointer->ptr, scsi_pointer->this_residual))
78562306a36Sopenharmony_ci	}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci/* Respond to the specific WD3393 interrupt - there are quite a few! */
78862306a36Sopenharmony_ci	switch (sr) {
78962306a36Sopenharmony_ci	case CSR_TIMEOUT:
79062306a36Sopenharmony_ci		DB(DB_INTR, printk("TIMEOUT"))
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci		    if (hostdata->state == S_RUNNING_LEVEL2)
79362306a36Sopenharmony_ci			hostdata->connected = NULL;
79462306a36Sopenharmony_ci		else {
79562306a36Sopenharmony_ci			cmd = (struct scsi_cmnd *) hostdata->selecting;	/* get a valid cmd */
79662306a36Sopenharmony_ci			hostdata->selecting = NULL;
79762306a36Sopenharmony_ci		}
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci		cmd->result = DID_NO_CONNECT << 16;
80062306a36Sopenharmony_ci		hostdata->busy[cmd->device->id] &= ~(1 << (cmd->device->lun & 0xff));
80162306a36Sopenharmony_ci		hostdata->state = S_UNCONNECTED;
80262306a36Sopenharmony_ci		scsi_done(cmd);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci		/* From esp.c:
80562306a36Sopenharmony_ci		 * There is a window of time within the scsi_done() path
80662306a36Sopenharmony_ci		 * of execution where interrupts are turned back on full
80762306a36Sopenharmony_ci		 * blast and left that way.  During that time we could
80862306a36Sopenharmony_ci		 * reconnect to a disconnected command, then we'd bomb
80962306a36Sopenharmony_ci		 * out below.  We could also end up executing two commands
81062306a36Sopenharmony_ci		 * at _once_.  ...just so you know why the restore_flags()
81162306a36Sopenharmony_ci		 * is here...
81262306a36Sopenharmony_ci		 */
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci		spin_unlock_irqrestore(&hostdata->lock, flags);
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci/* We are not connected to a target - check to see if there
81762306a36Sopenharmony_ci * are commands waiting to be executed.
81862306a36Sopenharmony_ci */
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci		wd33c93_execute(instance);
82162306a36Sopenharmony_ci		break;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci/* Note: this interrupt should not occur in a LEVEL2 command */
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	case CSR_SELECT:
82662306a36Sopenharmony_ci		DB(DB_INTR, printk("SELECT"))
82762306a36Sopenharmony_ci		    hostdata->connected = cmd =
82862306a36Sopenharmony_ci		    (struct scsi_cmnd *) hostdata->selecting;
82962306a36Sopenharmony_ci		hostdata->selecting = NULL;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci		/* construct an IDENTIFY message with correct disconnect bit */
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci		hostdata->outgoing_msg[0] = IDENTIFY(0, cmd->device->lun);
83462306a36Sopenharmony_ci		if (scsi_pointer->phase)
83562306a36Sopenharmony_ci			hostdata->outgoing_msg[0] |= 0x40;
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci		if (hostdata->sync_stat[cmd->device->id] == SS_FIRST) {
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci			hostdata->sync_stat[cmd->device->id] = SS_WAITING;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci/* Tack on a 2nd message to ask about synchronous transfers. If we've
84262306a36Sopenharmony_ci * been asked to do only asynchronous transfers on this device, we
84362306a36Sopenharmony_ci * request a fifo depth of 0, which is equivalent to async - should
84462306a36Sopenharmony_ci * solve the problems some people have had with GVP's Guru ROM.
84562306a36Sopenharmony_ci */
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci			hostdata->outgoing_msg[1] = EXTENDED_MESSAGE;
84862306a36Sopenharmony_ci			hostdata->outgoing_msg[2] = 3;
84962306a36Sopenharmony_ci			hostdata->outgoing_msg[3] = EXTENDED_SDTR;
85062306a36Sopenharmony_ci			if (hostdata->no_sync & (1 << cmd->device->id)) {
85162306a36Sopenharmony_ci				calc_sync_msg(hostdata->default_sx_per, 0,
85262306a36Sopenharmony_ci						0, hostdata->outgoing_msg + 4);
85362306a36Sopenharmony_ci			} else {
85462306a36Sopenharmony_ci				calc_sync_msg(optimum_sx_per(hostdata),
85562306a36Sopenharmony_ci						OPTIMUM_SX_OFF,
85662306a36Sopenharmony_ci						hostdata->fast,
85762306a36Sopenharmony_ci						hostdata->outgoing_msg + 4);
85862306a36Sopenharmony_ci			}
85962306a36Sopenharmony_ci			hostdata->outgoing_len = 6;
86062306a36Sopenharmony_ci#ifdef SYNC_DEBUG
86162306a36Sopenharmony_ci			ucp = hostdata->outgoing_msg + 1;
86262306a36Sopenharmony_ci			printk(" sending SDTR %02x03%02x%02x%02x ",
86362306a36Sopenharmony_ci				ucp[0], ucp[2], ucp[3], ucp[4]);
86462306a36Sopenharmony_ci#endif
86562306a36Sopenharmony_ci		} else
86662306a36Sopenharmony_ci			hostdata->outgoing_len = 1;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci		hostdata->state = S_CONNECTED;
86962306a36Sopenharmony_ci		spin_unlock_irqrestore(&hostdata->lock, flags);
87062306a36Sopenharmony_ci		break;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	case CSR_XFER_DONE | PHS_DATA_IN:
87362306a36Sopenharmony_ci	case CSR_UNEXP | PHS_DATA_IN:
87462306a36Sopenharmony_ci	case CSR_SRV_REQ | PHS_DATA_IN:
87562306a36Sopenharmony_ci		DB(DB_INTR,
87662306a36Sopenharmony_ci		   printk("IN-%d.%d", scsi_pointer->this_residual,
87762306a36Sopenharmony_ci			  scsi_pointer->buffers_residual))
87862306a36Sopenharmony_ci		    transfer_bytes(regs, cmd, DATA_IN_DIR);
87962306a36Sopenharmony_ci		if (hostdata->state != S_RUNNING_LEVEL2)
88062306a36Sopenharmony_ci			hostdata->state = S_CONNECTED;
88162306a36Sopenharmony_ci		spin_unlock_irqrestore(&hostdata->lock, flags);
88262306a36Sopenharmony_ci		break;
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	case CSR_XFER_DONE | PHS_DATA_OUT:
88562306a36Sopenharmony_ci	case CSR_UNEXP | PHS_DATA_OUT:
88662306a36Sopenharmony_ci	case CSR_SRV_REQ | PHS_DATA_OUT:
88762306a36Sopenharmony_ci		DB(DB_INTR,
88862306a36Sopenharmony_ci		   printk("OUT-%d.%d", scsi_pointer->this_residual,
88962306a36Sopenharmony_ci			  scsi_pointer->buffers_residual))
89062306a36Sopenharmony_ci		    transfer_bytes(regs, cmd, DATA_OUT_DIR);
89162306a36Sopenharmony_ci		if (hostdata->state != S_RUNNING_LEVEL2)
89262306a36Sopenharmony_ci			hostdata->state = S_CONNECTED;
89362306a36Sopenharmony_ci		spin_unlock_irqrestore(&hostdata->lock, flags);
89462306a36Sopenharmony_ci		break;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci/* Note: this interrupt should not occur in a LEVEL2 command */
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	case CSR_XFER_DONE | PHS_COMMAND:
89962306a36Sopenharmony_ci	case CSR_UNEXP | PHS_COMMAND:
90062306a36Sopenharmony_ci	case CSR_SRV_REQ | PHS_COMMAND:
90162306a36Sopenharmony_ci		DB(DB_INTR, printk("CMND-%02x", cmd->cmnd[0]))
90262306a36Sopenharmony_ci		    transfer_pio(regs, cmd->cmnd, cmd->cmd_len, DATA_OUT_DIR,
90362306a36Sopenharmony_ci				 hostdata);
90462306a36Sopenharmony_ci		hostdata->state = S_CONNECTED;
90562306a36Sopenharmony_ci		spin_unlock_irqrestore(&hostdata->lock, flags);
90662306a36Sopenharmony_ci		break;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	case CSR_XFER_DONE | PHS_STATUS:
90962306a36Sopenharmony_ci	case CSR_UNEXP | PHS_STATUS:
91062306a36Sopenharmony_ci	case CSR_SRV_REQ | PHS_STATUS:
91162306a36Sopenharmony_ci		DB(DB_INTR, printk("STATUS="))
91262306a36Sopenharmony_ci		scsi_pointer->Status = read_1_byte(regs);
91362306a36Sopenharmony_ci		DB(DB_INTR, printk("%02x", scsi_pointer->Status))
91462306a36Sopenharmony_ci		    if (hostdata->level2 >= L2_BASIC) {
91562306a36Sopenharmony_ci			sr = read_wd33c93(regs, WD_SCSI_STATUS);	/* clear interrupt */
91662306a36Sopenharmony_ci			udelay(7);
91762306a36Sopenharmony_ci			hostdata->state = S_RUNNING_LEVEL2;
91862306a36Sopenharmony_ci			write_wd33c93(regs, WD_COMMAND_PHASE, 0x50);
91962306a36Sopenharmony_ci			write_wd33c93_cmd(regs, WD_CMD_SEL_ATN_XFER);
92062306a36Sopenharmony_ci		} else {
92162306a36Sopenharmony_ci			hostdata->state = S_CONNECTED;
92262306a36Sopenharmony_ci		}
92362306a36Sopenharmony_ci		spin_unlock_irqrestore(&hostdata->lock, flags);
92462306a36Sopenharmony_ci		break;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	case CSR_XFER_DONE | PHS_MESS_IN:
92762306a36Sopenharmony_ci	case CSR_UNEXP | PHS_MESS_IN:
92862306a36Sopenharmony_ci	case CSR_SRV_REQ | PHS_MESS_IN:
92962306a36Sopenharmony_ci		DB(DB_INTR, printk("MSG_IN="))
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci		msg = read_1_byte(regs);
93262306a36Sopenharmony_ci		sr = read_wd33c93(regs, WD_SCSI_STATUS);	/* clear interrupt */
93362306a36Sopenharmony_ci		udelay(7);
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci		hostdata->incoming_msg[hostdata->incoming_ptr] = msg;
93662306a36Sopenharmony_ci		if (hostdata->incoming_msg[0] == EXTENDED_MESSAGE)
93762306a36Sopenharmony_ci			msg = EXTENDED_MESSAGE;
93862306a36Sopenharmony_ci		else
93962306a36Sopenharmony_ci			hostdata->incoming_ptr = 0;
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci		scsi_pointer->Message = msg;
94262306a36Sopenharmony_ci		switch (msg) {
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci		case COMMAND_COMPLETE:
94562306a36Sopenharmony_ci			DB(DB_INTR, printk("CCMP"))
94662306a36Sopenharmony_ci			    write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK);
94762306a36Sopenharmony_ci			hostdata->state = S_PRE_CMP_DISC;
94862306a36Sopenharmony_ci			break;
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci		case SAVE_POINTERS:
95162306a36Sopenharmony_ci			DB(DB_INTR, printk("SDP"))
95262306a36Sopenharmony_ci			    write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK);
95362306a36Sopenharmony_ci			hostdata->state = S_CONNECTED;
95462306a36Sopenharmony_ci			break;
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci		case RESTORE_POINTERS:
95762306a36Sopenharmony_ci			DB(DB_INTR, printk("RDP"))
95862306a36Sopenharmony_ci			    if (hostdata->level2 >= L2_BASIC) {
95962306a36Sopenharmony_ci				write_wd33c93(regs, WD_COMMAND_PHASE, 0x45);
96062306a36Sopenharmony_ci				write_wd33c93_cmd(regs, WD_CMD_SEL_ATN_XFER);
96162306a36Sopenharmony_ci				hostdata->state = S_RUNNING_LEVEL2;
96262306a36Sopenharmony_ci			} else {
96362306a36Sopenharmony_ci				write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK);
96462306a36Sopenharmony_ci				hostdata->state = S_CONNECTED;
96562306a36Sopenharmony_ci			}
96662306a36Sopenharmony_ci			break;
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci		case DISCONNECT:
96962306a36Sopenharmony_ci			DB(DB_INTR, printk("DIS"))
97062306a36Sopenharmony_ci			    cmd->device->disconnect = 1;
97162306a36Sopenharmony_ci			write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK);
97262306a36Sopenharmony_ci			hostdata->state = S_PRE_TMP_DISC;
97362306a36Sopenharmony_ci			break;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci		case MESSAGE_REJECT:
97662306a36Sopenharmony_ci			DB(DB_INTR, printk("REJ"))
97762306a36Sopenharmony_ci#ifdef SYNC_DEBUG
97862306a36Sopenharmony_ci			    printk("-REJ-");
97962306a36Sopenharmony_ci#endif
98062306a36Sopenharmony_ci			if (hostdata->sync_stat[cmd->device->id] == SS_WAITING) {
98162306a36Sopenharmony_ci				hostdata->sync_stat[cmd->device->id] = SS_SET;
98262306a36Sopenharmony_ci				/* we want default_sx_per, not DEFAULT_SX_PER */
98362306a36Sopenharmony_ci				hostdata->sync_xfer[cmd->device->id] =
98462306a36Sopenharmony_ci					calc_sync_xfer(hostdata->default_sx_per
98562306a36Sopenharmony_ci						/ 4, 0, 0, hostdata->sx_table);
98662306a36Sopenharmony_ci			}
98762306a36Sopenharmony_ci			write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK);
98862306a36Sopenharmony_ci			hostdata->state = S_CONNECTED;
98962306a36Sopenharmony_ci			break;
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci		case EXTENDED_MESSAGE:
99262306a36Sopenharmony_ci			DB(DB_INTR, printk("EXT"))
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci			    ucp = hostdata->incoming_msg;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci#ifdef SYNC_DEBUG
99762306a36Sopenharmony_ci			printk("%02x", ucp[hostdata->incoming_ptr]);
99862306a36Sopenharmony_ci#endif
99962306a36Sopenharmony_ci			/* Is this the last byte of the extended message? */
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci			if ((hostdata->incoming_ptr >= 2) &&
100262306a36Sopenharmony_ci			    (hostdata->incoming_ptr == (ucp[1] + 1))) {
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci				switch (ucp[2]) {	/* what's the EXTENDED code? */
100562306a36Sopenharmony_ci				case EXTENDED_SDTR:
100662306a36Sopenharmony_ci					/* default to default async period */
100762306a36Sopenharmony_ci					id = calc_sync_xfer(hostdata->
100862306a36Sopenharmony_ci							default_sx_per / 4, 0,
100962306a36Sopenharmony_ci							0, hostdata->sx_table);
101062306a36Sopenharmony_ci					if (hostdata->sync_stat[cmd->device->id] !=
101162306a36Sopenharmony_ci					    SS_WAITING) {
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci/* A device has sent an unsolicited SDTR message; rather than go
101462306a36Sopenharmony_ci * through the effort of decoding it and then figuring out what
101562306a36Sopenharmony_ci * our reply should be, we're just gonna say that we have a
101662306a36Sopenharmony_ci * synchronous fifo depth of 0. This will result in asynchronous
101762306a36Sopenharmony_ci * transfers - not ideal but so much easier.
101862306a36Sopenharmony_ci * Actually, this is OK because it assures us that if we don't
101962306a36Sopenharmony_ci * specifically ask for sync transfers, we won't do any.
102062306a36Sopenharmony_ci */
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci						write_wd33c93_cmd(regs, WD_CMD_ASSERT_ATN);	/* want MESS_OUT */
102362306a36Sopenharmony_ci						hostdata->outgoing_msg[0] =
102462306a36Sopenharmony_ci						    EXTENDED_MESSAGE;
102562306a36Sopenharmony_ci						hostdata->outgoing_msg[1] = 3;
102662306a36Sopenharmony_ci						hostdata->outgoing_msg[2] =
102762306a36Sopenharmony_ci						    EXTENDED_SDTR;
102862306a36Sopenharmony_ci						calc_sync_msg(hostdata->
102962306a36Sopenharmony_ci							default_sx_per, 0,
103062306a36Sopenharmony_ci							0, hostdata->outgoing_msg + 3);
103162306a36Sopenharmony_ci						hostdata->outgoing_len = 5;
103262306a36Sopenharmony_ci					} else {
103362306a36Sopenharmony_ci						if (ucp[4]) /* well, sync transfer */
103462306a36Sopenharmony_ci							id = calc_sync_xfer(ucp[3], ucp[4],
103562306a36Sopenharmony_ci									hostdata->fast,
103662306a36Sopenharmony_ci									hostdata->sx_table);
103762306a36Sopenharmony_ci						else if (ucp[3]) /* very unlikely... */
103862306a36Sopenharmony_ci							id = calc_sync_xfer(ucp[3], ucp[4],
103962306a36Sopenharmony_ci									0, hostdata->sx_table);
104062306a36Sopenharmony_ci					}
104162306a36Sopenharmony_ci					hostdata->sync_xfer[cmd->device->id] = id;
104262306a36Sopenharmony_ci#ifdef SYNC_DEBUG
104362306a36Sopenharmony_ci					printk(" sync_xfer=%02x\n",
104462306a36Sopenharmony_ci					       hostdata->sync_xfer[cmd->device->id]);
104562306a36Sopenharmony_ci#endif
104662306a36Sopenharmony_ci					hostdata->sync_stat[cmd->device->id] =
104762306a36Sopenharmony_ci					    SS_SET;
104862306a36Sopenharmony_ci					write_wd33c93_cmd(regs,
104962306a36Sopenharmony_ci							  WD_CMD_NEGATE_ACK);
105062306a36Sopenharmony_ci					hostdata->state = S_CONNECTED;
105162306a36Sopenharmony_ci					break;
105262306a36Sopenharmony_ci				case EXTENDED_WDTR:
105362306a36Sopenharmony_ci					write_wd33c93_cmd(regs, WD_CMD_ASSERT_ATN);	/* want MESS_OUT */
105462306a36Sopenharmony_ci					printk("sending WDTR ");
105562306a36Sopenharmony_ci					hostdata->outgoing_msg[0] =
105662306a36Sopenharmony_ci					    EXTENDED_MESSAGE;
105762306a36Sopenharmony_ci					hostdata->outgoing_msg[1] = 2;
105862306a36Sopenharmony_ci					hostdata->outgoing_msg[2] =
105962306a36Sopenharmony_ci					    EXTENDED_WDTR;
106062306a36Sopenharmony_ci					hostdata->outgoing_msg[3] = 0;	/* 8 bit transfer width */
106162306a36Sopenharmony_ci					hostdata->outgoing_len = 4;
106262306a36Sopenharmony_ci					write_wd33c93_cmd(regs,
106362306a36Sopenharmony_ci							  WD_CMD_NEGATE_ACK);
106462306a36Sopenharmony_ci					hostdata->state = S_CONNECTED;
106562306a36Sopenharmony_ci					break;
106662306a36Sopenharmony_ci				default:
106762306a36Sopenharmony_ci					write_wd33c93_cmd(regs, WD_CMD_ASSERT_ATN);	/* want MESS_OUT */
106862306a36Sopenharmony_ci					printk
106962306a36Sopenharmony_ci					    ("Rejecting Unknown Extended Message(%02x). ",
107062306a36Sopenharmony_ci					     ucp[2]);
107162306a36Sopenharmony_ci					hostdata->outgoing_msg[0] =
107262306a36Sopenharmony_ci					    MESSAGE_REJECT;
107362306a36Sopenharmony_ci					hostdata->outgoing_len = 1;
107462306a36Sopenharmony_ci					write_wd33c93_cmd(regs,
107562306a36Sopenharmony_ci							  WD_CMD_NEGATE_ACK);
107662306a36Sopenharmony_ci					hostdata->state = S_CONNECTED;
107762306a36Sopenharmony_ci					break;
107862306a36Sopenharmony_ci				}
107962306a36Sopenharmony_ci				hostdata->incoming_ptr = 0;
108062306a36Sopenharmony_ci			}
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci			/* We need to read more MESS_IN bytes for the extended message */
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci			else {
108562306a36Sopenharmony_ci				hostdata->incoming_ptr++;
108662306a36Sopenharmony_ci				write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK);
108762306a36Sopenharmony_ci				hostdata->state = S_CONNECTED;
108862306a36Sopenharmony_ci			}
108962306a36Sopenharmony_ci			break;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci		default:
109262306a36Sopenharmony_ci			printk("Rejecting Unknown Message(%02x) ", msg);
109362306a36Sopenharmony_ci			write_wd33c93_cmd(regs, WD_CMD_ASSERT_ATN);	/* want MESS_OUT */
109462306a36Sopenharmony_ci			hostdata->outgoing_msg[0] = MESSAGE_REJECT;
109562306a36Sopenharmony_ci			hostdata->outgoing_len = 1;
109662306a36Sopenharmony_ci			write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK);
109762306a36Sopenharmony_ci			hostdata->state = S_CONNECTED;
109862306a36Sopenharmony_ci		}
109962306a36Sopenharmony_ci		spin_unlock_irqrestore(&hostdata->lock, flags);
110062306a36Sopenharmony_ci		break;
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci/* Note: this interrupt will occur only after a LEVEL2 command */
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	case CSR_SEL_XFER_DONE:
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci/* Make sure that reselection is enabled at this point - it may
110762306a36Sopenharmony_ci * have been turned off for the command that just completed.
110862306a36Sopenharmony_ci */
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci		write_wd33c93(regs, WD_SOURCE_ID, SRCID_ER);
111162306a36Sopenharmony_ci		if (phs == 0x60) {
111262306a36Sopenharmony_ci			DB(DB_INTR, printk("SX-DONE"))
111362306a36Sopenharmony_ci			    scsi_pointer->Message = COMMAND_COMPLETE;
111462306a36Sopenharmony_ci			lun = read_wd33c93(regs, WD_TARGET_LUN);
111562306a36Sopenharmony_ci			DB(DB_INTR, printk(":%d.%d", scsi_pointer->Status, lun))
111662306a36Sopenharmony_ci			    hostdata->connected = NULL;
111762306a36Sopenharmony_ci			hostdata->busy[cmd->device->id] &= ~(1 << (cmd->device->lun & 0xff));
111862306a36Sopenharmony_ci			hostdata->state = S_UNCONNECTED;
111962306a36Sopenharmony_ci			if (scsi_pointer->Status == ILLEGAL_STATUS_BYTE)
112062306a36Sopenharmony_ci				scsi_pointer->Status = lun;
112162306a36Sopenharmony_ci			if (cmd->cmnd[0] == REQUEST_SENSE
112262306a36Sopenharmony_ci			    && scsi_pointer->Status != SAM_STAT_GOOD) {
112362306a36Sopenharmony_ci				set_host_byte(cmd, DID_ERROR);
112462306a36Sopenharmony_ci			} else {
112562306a36Sopenharmony_ci				set_host_byte(cmd, DID_OK);
112662306a36Sopenharmony_ci				scsi_msg_to_host_byte(cmd, scsi_pointer->Message);
112762306a36Sopenharmony_ci				set_status_byte(cmd, scsi_pointer->Status);
112862306a36Sopenharmony_ci			}
112962306a36Sopenharmony_ci			scsi_done(cmd);
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci/* We are no longer  connected to a target - check to see if
113262306a36Sopenharmony_ci * there are commands waiting to be executed.
113362306a36Sopenharmony_ci */
113462306a36Sopenharmony_ci			spin_unlock_irqrestore(&hostdata->lock, flags);
113562306a36Sopenharmony_ci			wd33c93_execute(instance);
113662306a36Sopenharmony_ci		} else {
113762306a36Sopenharmony_ci			printk
113862306a36Sopenharmony_ci			    ("%02x:%02x:%02x: Unknown SEL_XFER_DONE phase!!---",
113962306a36Sopenharmony_ci			     asr, sr, phs);
114062306a36Sopenharmony_ci			spin_unlock_irqrestore(&hostdata->lock, flags);
114162306a36Sopenharmony_ci		}
114262306a36Sopenharmony_ci		break;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci/* Note: this interrupt will occur only after a LEVEL2 command */
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	case CSR_SDP:
114762306a36Sopenharmony_ci		DB(DB_INTR, printk("SDP"))
114862306a36Sopenharmony_ci		    hostdata->state = S_RUNNING_LEVEL2;
114962306a36Sopenharmony_ci		write_wd33c93(regs, WD_COMMAND_PHASE, 0x41);
115062306a36Sopenharmony_ci		write_wd33c93_cmd(regs, WD_CMD_SEL_ATN_XFER);
115162306a36Sopenharmony_ci		spin_unlock_irqrestore(&hostdata->lock, flags);
115262306a36Sopenharmony_ci		break;
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	case CSR_XFER_DONE | PHS_MESS_OUT:
115562306a36Sopenharmony_ci	case CSR_UNEXP | PHS_MESS_OUT:
115662306a36Sopenharmony_ci	case CSR_SRV_REQ | PHS_MESS_OUT:
115762306a36Sopenharmony_ci		DB(DB_INTR, printk("MSG_OUT="))
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci/* To get here, we've probably requested MESSAGE_OUT and have
116062306a36Sopenharmony_ci * already put the correct bytes in outgoing_msg[] and filled
116162306a36Sopenharmony_ci * in outgoing_len. We simply send them out to the SCSI bus.
116262306a36Sopenharmony_ci * Sometimes we get MESSAGE_OUT phase when we're not expecting
116362306a36Sopenharmony_ci * it - like when our SDTR message is rejected by a target. Some
116462306a36Sopenharmony_ci * targets send the REJECT before receiving all of the extended
116562306a36Sopenharmony_ci * message, and then seem to go back to MESSAGE_OUT for a byte
116662306a36Sopenharmony_ci * or two. Not sure why, or if I'm doing something wrong to
116762306a36Sopenharmony_ci * cause this to happen. Regardless, it seems that sending
116862306a36Sopenharmony_ci * NOP messages in these situations results in no harm and
116962306a36Sopenharmony_ci * makes everyone happy.
117062306a36Sopenharmony_ci */
117162306a36Sopenharmony_ci		    if (hostdata->outgoing_len == 0) {
117262306a36Sopenharmony_ci			hostdata->outgoing_len = 1;
117362306a36Sopenharmony_ci			hostdata->outgoing_msg[0] = NOP;
117462306a36Sopenharmony_ci		}
117562306a36Sopenharmony_ci		transfer_pio(regs, hostdata->outgoing_msg,
117662306a36Sopenharmony_ci			     hostdata->outgoing_len, DATA_OUT_DIR, hostdata);
117762306a36Sopenharmony_ci		DB(DB_INTR, printk("%02x", hostdata->outgoing_msg[0]))
117862306a36Sopenharmony_ci		    hostdata->outgoing_len = 0;
117962306a36Sopenharmony_ci		hostdata->state = S_CONNECTED;
118062306a36Sopenharmony_ci		spin_unlock_irqrestore(&hostdata->lock, flags);
118162306a36Sopenharmony_ci		break;
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	case CSR_UNEXP_DISC:
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci/* I think I've seen this after a request-sense that was in response
118662306a36Sopenharmony_ci * to an error condition, but not sure. We certainly need to do
118762306a36Sopenharmony_ci * something when we get this interrupt - the question is 'what?'.
118862306a36Sopenharmony_ci * Let's think positively, and assume some command has finished
118962306a36Sopenharmony_ci * in a legal manner (like a command that provokes a request-sense),
119062306a36Sopenharmony_ci * so we treat it as a normal command-complete-disconnect.
119162306a36Sopenharmony_ci */
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci/* Make sure that reselection is enabled at this point - it may
119462306a36Sopenharmony_ci * have been turned off for the command that just completed.
119562306a36Sopenharmony_ci */
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci		write_wd33c93(regs, WD_SOURCE_ID, SRCID_ER);
119862306a36Sopenharmony_ci		if (cmd == NULL) {
119962306a36Sopenharmony_ci			printk(" - Already disconnected! ");
120062306a36Sopenharmony_ci			hostdata->state = S_UNCONNECTED;
120162306a36Sopenharmony_ci			spin_unlock_irqrestore(&hostdata->lock, flags);
120262306a36Sopenharmony_ci			return;
120362306a36Sopenharmony_ci		}
120462306a36Sopenharmony_ci		DB(DB_INTR, printk("UNEXP_DISC"))
120562306a36Sopenharmony_ci		    hostdata->connected = NULL;
120662306a36Sopenharmony_ci		hostdata->busy[cmd->device->id] &= ~(1 << (cmd->device->lun & 0xff));
120762306a36Sopenharmony_ci		hostdata->state = S_UNCONNECTED;
120862306a36Sopenharmony_ci		if (cmd->cmnd[0] == REQUEST_SENSE &&
120962306a36Sopenharmony_ci		    scsi_pointer->Status != SAM_STAT_GOOD) {
121062306a36Sopenharmony_ci			set_host_byte(cmd, DID_ERROR);
121162306a36Sopenharmony_ci		} else {
121262306a36Sopenharmony_ci			set_host_byte(cmd, DID_OK);
121362306a36Sopenharmony_ci			scsi_msg_to_host_byte(cmd, scsi_pointer->Message);
121462306a36Sopenharmony_ci			set_status_byte(cmd, scsi_pointer->Status);
121562306a36Sopenharmony_ci		}
121662306a36Sopenharmony_ci		scsi_done(cmd);
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci/* We are no longer connected to a target - check to see if
121962306a36Sopenharmony_ci * there are commands waiting to be executed.
122062306a36Sopenharmony_ci */
122162306a36Sopenharmony_ci		/* look above for comments on scsi_done() */
122262306a36Sopenharmony_ci		spin_unlock_irqrestore(&hostdata->lock, flags);
122362306a36Sopenharmony_ci		wd33c93_execute(instance);
122462306a36Sopenharmony_ci		break;
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	case CSR_DISC:
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci/* Make sure that reselection is enabled at this point - it may
122962306a36Sopenharmony_ci * have been turned off for the command that just completed.
123062306a36Sopenharmony_ci */
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci		write_wd33c93(regs, WD_SOURCE_ID, SRCID_ER);
123362306a36Sopenharmony_ci		DB(DB_INTR, printk("DISC"))
123462306a36Sopenharmony_ci		    if (cmd == NULL) {
123562306a36Sopenharmony_ci			printk(" - Already disconnected! ");
123662306a36Sopenharmony_ci			hostdata->state = S_UNCONNECTED;
123762306a36Sopenharmony_ci		}
123862306a36Sopenharmony_ci		switch (hostdata->state) {
123962306a36Sopenharmony_ci		case S_PRE_CMP_DISC:
124062306a36Sopenharmony_ci			hostdata->connected = NULL;
124162306a36Sopenharmony_ci			hostdata->busy[cmd->device->id] &= ~(1 << (cmd->device->lun & 0xff));
124262306a36Sopenharmony_ci			hostdata->state = S_UNCONNECTED;
124362306a36Sopenharmony_ci			DB(DB_INTR, printk(":%d", scsi_pointer->Status))
124462306a36Sopenharmony_ci			if (cmd->cmnd[0] == REQUEST_SENSE
124562306a36Sopenharmony_ci			    && scsi_pointer->Status != SAM_STAT_GOOD) {
124662306a36Sopenharmony_ci				set_host_byte(cmd, DID_ERROR);
124762306a36Sopenharmony_ci			} else {
124862306a36Sopenharmony_ci				set_host_byte(cmd, DID_OK);
124962306a36Sopenharmony_ci				scsi_msg_to_host_byte(cmd, scsi_pointer->Message);
125062306a36Sopenharmony_ci				set_status_byte(cmd, scsi_pointer->Status);
125162306a36Sopenharmony_ci			}
125262306a36Sopenharmony_ci			scsi_done(cmd);
125362306a36Sopenharmony_ci			break;
125462306a36Sopenharmony_ci		case S_PRE_TMP_DISC:
125562306a36Sopenharmony_ci		case S_RUNNING_LEVEL2:
125662306a36Sopenharmony_ci			cmd->host_scribble = (uchar *) hostdata->disconnected_Q;
125762306a36Sopenharmony_ci			hostdata->disconnected_Q = cmd;
125862306a36Sopenharmony_ci			hostdata->connected = NULL;
125962306a36Sopenharmony_ci			hostdata->state = S_UNCONNECTED;
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci#ifdef PROC_STATISTICS
126262306a36Sopenharmony_ci			hostdata->disc_done_cnt[cmd->device->id]++;
126362306a36Sopenharmony_ci#endif
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci			break;
126662306a36Sopenharmony_ci		default:
126762306a36Sopenharmony_ci			printk("*** Unexpected DISCONNECT interrupt! ***");
126862306a36Sopenharmony_ci			hostdata->state = S_UNCONNECTED;
126962306a36Sopenharmony_ci		}
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci/* We are no longer connected to a target - check to see if
127262306a36Sopenharmony_ci * there are commands waiting to be executed.
127362306a36Sopenharmony_ci */
127462306a36Sopenharmony_ci		spin_unlock_irqrestore(&hostdata->lock, flags);
127562306a36Sopenharmony_ci		wd33c93_execute(instance);
127662306a36Sopenharmony_ci		break;
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	case CSR_RESEL_AM:
127962306a36Sopenharmony_ci	case CSR_RESEL:
128062306a36Sopenharmony_ci		DB(DB_INTR, printk("RESEL%s", sr == CSR_RESEL_AM ? "_AM" : ""))
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci		    /* Old chips (pre -A ???) don't have advanced features and will
128362306a36Sopenharmony_ci		     * generate CSR_RESEL.  In that case we have to extract the LUN the
128462306a36Sopenharmony_ci		     * hard way (see below).
128562306a36Sopenharmony_ci		     * First we have to make sure this reselection didn't
128662306a36Sopenharmony_ci		     * happen during Arbitration/Selection of some other device.
128762306a36Sopenharmony_ci		     * If yes, put losing command back on top of input_Q.
128862306a36Sopenharmony_ci		     */
128962306a36Sopenharmony_ci		    if (hostdata->level2 <= L2_NONE) {
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci			if (hostdata->selecting) {
129262306a36Sopenharmony_ci				cmd = (struct scsi_cmnd *) hostdata->selecting;
129362306a36Sopenharmony_ci				hostdata->selecting = NULL;
129462306a36Sopenharmony_ci				hostdata->busy[cmd->device->id] &= ~(1 << (cmd->device->lun & 0xff));
129562306a36Sopenharmony_ci				cmd->host_scribble =
129662306a36Sopenharmony_ci				    (uchar *) hostdata->input_Q;
129762306a36Sopenharmony_ci				hostdata->input_Q = cmd;
129862306a36Sopenharmony_ci			}
129962306a36Sopenharmony_ci		}
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci		else {
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci			if (cmd) {
130462306a36Sopenharmony_ci				if (phs == 0x00) {
130562306a36Sopenharmony_ci					hostdata->busy[cmd->device->id] &=
130662306a36Sopenharmony_ci						~(1 << (cmd->device->lun & 0xff));
130762306a36Sopenharmony_ci					cmd->host_scribble =
130862306a36Sopenharmony_ci					    (uchar *) hostdata->input_Q;
130962306a36Sopenharmony_ci					hostdata->input_Q = cmd;
131062306a36Sopenharmony_ci				} else {
131162306a36Sopenharmony_ci					printk
131262306a36Sopenharmony_ci					    ("---%02x:%02x:%02x-TROUBLE: Intrusive ReSelect!---",
131362306a36Sopenharmony_ci					     asr, sr, phs);
131462306a36Sopenharmony_ci					while (1)
131562306a36Sopenharmony_ci						printk("\r");
131662306a36Sopenharmony_ci				}
131762306a36Sopenharmony_ci			}
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci		}
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci		/* OK - find out which device reselected us. */
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci		id = read_wd33c93(regs, WD_SOURCE_ID);
132462306a36Sopenharmony_ci		id &= SRCID_MASK;
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci		/* and extract the lun from the ID message. (Note that we don't
132762306a36Sopenharmony_ci		 * bother to check for a valid message here - I guess this is
132862306a36Sopenharmony_ci		 * not the right way to go, but...)
132962306a36Sopenharmony_ci		 */
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci		if (sr == CSR_RESEL_AM) {
133262306a36Sopenharmony_ci			lun = read_wd33c93(regs, WD_DATA);
133362306a36Sopenharmony_ci			if (hostdata->level2 < L2_RESELECT)
133462306a36Sopenharmony_ci				write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK);
133562306a36Sopenharmony_ci			lun &= 7;
133662306a36Sopenharmony_ci		} else {
133762306a36Sopenharmony_ci			/* Old chip; wait for msgin phase to pick up the LUN. */
133862306a36Sopenharmony_ci			for (lun = 255; lun; lun--) {
133962306a36Sopenharmony_ci				if ((asr = read_aux_stat(regs)) & ASR_INT)
134062306a36Sopenharmony_ci					break;
134162306a36Sopenharmony_ci				udelay(10);
134262306a36Sopenharmony_ci			}
134362306a36Sopenharmony_ci			if (!(asr & ASR_INT)) {
134462306a36Sopenharmony_ci				printk
134562306a36Sopenharmony_ci				    ("wd33c93: Reselected without IDENTIFY\n");
134662306a36Sopenharmony_ci				lun = 0;
134762306a36Sopenharmony_ci			} else {
134862306a36Sopenharmony_ci				/* Verify this is a change to MSG_IN and read the message */
134962306a36Sopenharmony_ci				sr = read_wd33c93(regs, WD_SCSI_STATUS);
135062306a36Sopenharmony_ci				udelay(7);
135162306a36Sopenharmony_ci				if (sr == (CSR_ABORT | PHS_MESS_IN) ||
135262306a36Sopenharmony_ci				    sr == (CSR_UNEXP | PHS_MESS_IN) ||
135362306a36Sopenharmony_ci				    sr == (CSR_SRV_REQ | PHS_MESS_IN)) {
135462306a36Sopenharmony_ci					/* Got MSG_IN, grab target LUN */
135562306a36Sopenharmony_ci					lun = read_1_byte(regs);
135662306a36Sopenharmony_ci					/* Now we expect a 'paused with ACK asserted' int.. */
135762306a36Sopenharmony_ci					asr = read_aux_stat(regs);
135862306a36Sopenharmony_ci					if (!(asr & ASR_INT)) {
135962306a36Sopenharmony_ci						udelay(10);
136062306a36Sopenharmony_ci						asr = read_aux_stat(regs);
136162306a36Sopenharmony_ci						if (!(asr & ASR_INT))
136262306a36Sopenharmony_ci							printk
136362306a36Sopenharmony_ci							    ("wd33c93: No int after LUN on RESEL (%02x)\n",
136462306a36Sopenharmony_ci							     asr);
136562306a36Sopenharmony_ci					}
136662306a36Sopenharmony_ci					sr = read_wd33c93(regs, WD_SCSI_STATUS);
136762306a36Sopenharmony_ci					udelay(7);
136862306a36Sopenharmony_ci					if (sr != CSR_MSGIN)
136962306a36Sopenharmony_ci						printk
137062306a36Sopenharmony_ci						    ("wd33c93: Not paused with ACK on RESEL (%02x)\n",
137162306a36Sopenharmony_ci						     sr);
137262306a36Sopenharmony_ci					lun &= 7;
137362306a36Sopenharmony_ci					write_wd33c93_cmd(regs,
137462306a36Sopenharmony_ci							  WD_CMD_NEGATE_ACK);
137562306a36Sopenharmony_ci				} else {
137662306a36Sopenharmony_ci					printk
137762306a36Sopenharmony_ci					    ("wd33c93: Not MSG_IN on reselect (%02x)\n",
137862306a36Sopenharmony_ci					     sr);
137962306a36Sopenharmony_ci					lun = 0;
138062306a36Sopenharmony_ci				}
138162306a36Sopenharmony_ci			}
138262306a36Sopenharmony_ci		}
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci		/* Now we look for the command that's reconnecting. */
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci		cmd = (struct scsi_cmnd *) hostdata->disconnected_Q;
138762306a36Sopenharmony_ci		patch = NULL;
138862306a36Sopenharmony_ci		while (cmd) {
138962306a36Sopenharmony_ci			if (id == cmd->device->id && lun == (u8)cmd->device->lun)
139062306a36Sopenharmony_ci				break;
139162306a36Sopenharmony_ci			patch = cmd;
139262306a36Sopenharmony_ci			cmd = (struct scsi_cmnd *) cmd->host_scribble;
139362306a36Sopenharmony_ci		}
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci		/* Hmm. Couldn't find a valid command.... What to do? */
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci		if (!cmd) {
139862306a36Sopenharmony_ci			printk
139962306a36Sopenharmony_ci			    ("---TROUBLE: target %d.%d not in disconnect queue---",
140062306a36Sopenharmony_ci			     id, (u8)lun);
140162306a36Sopenharmony_ci			spin_unlock_irqrestore(&hostdata->lock, flags);
140262306a36Sopenharmony_ci			return;
140362306a36Sopenharmony_ci		}
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci		/* Ok, found the command - now start it up again. */
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci		if (patch)
140862306a36Sopenharmony_ci			patch->host_scribble = cmd->host_scribble;
140962306a36Sopenharmony_ci		else
141062306a36Sopenharmony_ci			hostdata->disconnected_Q =
141162306a36Sopenharmony_ci			    (struct scsi_cmnd *) cmd->host_scribble;
141262306a36Sopenharmony_ci		hostdata->connected = cmd;
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci		/* We don't need to worry about 'initialize_SCp()' or 'hostdata->busy[]'
141562306a36Sopenharmony_ci		 * because these things are preserved over a disconnect.
141662306a36Sopenharmony_ci		 * But we DO need to fix the DPD bit so it's correct for this command.
141762306a36Sopenharmony_ci		 */
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci		if (cmd->sc_data_direction == DMA_TO_DEVICE)
142062306a36Sopenharmony_ci			write_wd33c93(regs, WD_DESTINATION_ID, cmd->device->id);
142162306a36Sopenharmony_ci		else
142262306a36Sopenharmony_ci			write_wd33c93(regs, WD_DESTINATION_ID,
142362306a36Sopenharmony_ci				      cmd->device->id | DSTID_DPD);
142462306a36Sopenharmony_ci		if (hostdata->level2 >= L2_RESELECT) {
142562306a36Sopenharmony_ci			write_wd33c93_count(regs, 0);	/* we want a DATA_PHASE interrupt */
142662306a36Sopenharmony_ci			write_wd33c93(regs, WD_COMMAND_PHASE, 0x45);
142762306a36Sopenharmony_ci			write_wd33c93_cmd(regs, WD_CMD_SEL_ATN_XFER);
142862306a36Sopenharmony_ci			hostdata->state = S_RUNNING_LEVEL2;
142962306a36Sopenharmony_ci		} else
143062306a36Sopenharmony_ci			hostdata->state = S_CONNECTED;
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci		    spin_unlock_irqrestore(&hostdata->lock, flags);
143362306a36Sopenharmony_ci		break;
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	default:
143662306a36Sopenharmony_ci		printk("--UNKNOWN INTERRUPT:%02x:%02x:%02x--", asr, sr, phs);
143762306a36Sopenharmony_ci		spin_unlock_irqrestore(&hostdata->lock, flags);
143862306a36Sopenharmony_ci	}
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci	DB(DB_INTR, printk("} "))
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci}
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_cistatic void
144562306a36Sopenharmony_cireset_wd33c93(struct Scsi_Host *instance)
144662306a36Sopenharmony_ci{
144762306a36Sopenharmony_ci	struct WD33C93_hostdata *hostdata =
144862306a36Sopenharmony_ci	    (struct WD33C93_hostdata *) instance->hostdata;
144962306a36Sopenharmony_ci	const wd33c93_regs regs = hostdata->regs;
145062306a36Sopenharmony_ci	uchar sr;
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci#ifdef CONFIG_SGI_IP22
145362306a36Sopenharmony_ci	{
145462306a36Sopenharmony_ci		int busycount = 0;
145562306a36Sopenharmony_ci		extern void sgiwd93_reset(unsigned long);
145662306a36Sopenharmony_ci		/* wait 'til the chip gets some time for us */
145762306a36Sopenharmony_ci		while ((read_aux_stat(regs) & ASR_BSY) && busycount++ < 100)
145862306a36Sopenharmony_ci			udelay (10);
145962306a36Sopenharmony_ci	/*
146062306a36Sopenharmony_ci 	 * there are scsi devices out there, which manage to lock up
146162306a36Sopenharmony_ci	 * the wd33c93 in a busy condition. In this state it won't
146262306a36Sopenharmony_ci	 * accept the reset command. The only way to solve this is to
146362306a36Sopenharmony_ci 	 * give the chip a hardware reset (if possible). The code below
146462306a36Sopenharmony_ci	 * does this for the SGI Indy, where this is possible
146562306a36Sopenharmony_ci	 */
146662306a36Sopenharmony_ci	/* still busy ? */
146762306a36Sopenharmony_ci	if (read_aux_stat(regs) & ASR_BSY)
146862306a36Sopenharmony_ci		sgiwd93_reset(instance->base); /* yeah, give it the hard one */
146962306a36Sopenharmony_ci	}
147062306a36Sopenharmony_ci#endif
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	write_wd33c93(regs, WD_OWN_ID, OWNID_EAF | OWNID_RAF |
147362306a36Sopenharmony_ci		      instance->this_id | hostdata->clock_freq);
147462306a36Sopenharmony_ci	write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
147562306a36Sopenharmony_ci	write_wd33c93(regs, WD_SYNCHRONOUS_TRANSFER,
147662306a36Sopenharmony_ci		      calc_sync_xfer(hostdata->default_sx_per / 4,
147762306a36Sopenharmony_ci				     DEFAULT_SX_OFF, 0, hostdata->sx_table));
147862306a36Sopenharmony_ci	write_wd33c93(regs, WD_COMMAND, WD_CMD_RESET);
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci#ifdef CONFIG_MVME147_SCSI
148262306a36Sopenharmony_ci	udelay(25);		/* The old wd33c93 on MVME147 needs this, at least */
148362306a36Sopenharmony_ci#endif
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	while (!(read_aux_stat(regs) & ASR_INT))
148662306a36Sopenharmony_ci		;
148762306a36Sopenharmony_ci	sr = read_wd33c93(regs, WD_SCSI_STATUS);
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	hostdata->microcode = read_wd33c93(regs, WD_CDB_1);
149062306a36Sopenharmony_ci	if (sr == 0x00)
149162306a36Sopenharmony_ci		hostdata->chip = C_WD33C93;
149262306a36Sopenharmony_ci	else if (sr == 0x01) {
149362306a36Sopenharmony_ci		write_wd33c93(regs, WD_QUEUE_TAG, 0xa5);	/* any random number */
149462306a36Sopenharmony_ci		sr = read_wd33c93(regs, WD_QUEUE_TAG);
149562306a36Sopenharmony_ci		if (sr == 0xa5) {
149662306a36Sopenharmony_ci			hostdata->chip = C_WD33C93B;
149762306a36Sopenharmony_ci			write_wd33c93(regs, WD_QUEUE_TAG, 0);
149862306a36Sopenharmony_ci		} else
149962306a36Sopenharmony_ci			hostdata->chip = C_WD33C93A;
150062306a36Sopenharmony_ci	} else
150162306a36Sopenharmony_ci		hostdata->chip = C_UNKNOWN_CHIP;
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	if (hostdata->chip != C_WD33C93B)	/* Fast SCSI unavailable */
150462306a36Sopenharmony_ci		hostdata->fast = 0;
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	write_wd33c93(regs, WD_TIMEOUT_PERIOD, TIMEOUT_PERIOD_VALUE);
150762306a36Sopenharmony_ci	write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
150862306a36Sopenharmony_ci}
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ciint
151162306a36Sopenharmony_ciwd33c93_host_reset(struct scsi_cmnd * SCpnt)
151262306a36Sopenharmony_ci{
151362306a36Sopenharmony_ci	struct Scsi_Host *instance;
151462306a36Sopenharmony_ci	struct WD33C93_hostdata *hostdata;
151562306a36Sopenharmony_ci	int i;
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci	instance = SCpnt->device->host;
151862306a36Sopenharmony_ci	spin_lock_irq(instance->host_lock);
151962306a36Sopenharmony_ci	hostdata = (struct WD33C93_hostdata *) instance->hostdata;
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci	printk("scsi%d: reset. ", instance->host_no);
152262306a36Sopenharmony_ci	disable_irq(instance->irq);
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci	hostdata->dma_stop(instance, NULL, 0);
152562306a36Sopenharmony_ci	for (i = 0; i < 8; i++) {
152662306a36Sopenharmony_ci		hostdata->busy[i] = 0;
152762306a36Sopenharmony_ci		hostdata->sync_xfer[i] =
152862306a36Sopenharmony_ci			calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF,
152962306a36Sopenharmony_ci					0, hostdata->sx_table);
153062306a36Sopenharmony_ci		hostdata->sync_stat[i] = SS_UNSET;	/* using default sync values */
153162306a36Sopenharmony_ci	}
153262306a36Sopenharmony_ci	hostdata->input_Q = NULL;
153362306a36Sopenharmony_ci	hostdata->selecting = NULL;
153462306a36Sopenharmony_ci	hostdata->connected = NULL;
153562306a36Sopenharmony_ci	hostdata->disconnected_Q = NULL;
153662306a36Sopenharmony_ci	hostdata->state = S_UNCONNECTED;
153762306a36Sopenharmony_ci	hostdata->dma = D_DMA_OFF;
153862306a36Sopenharmony_ci	hostdata->incoming_ptr = 0;
153962306a36Sopenharmony_ci	hostdata->outgoing_len = 0;
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	reset_wd33c93(instance);
154262306a36Sopenharmony_ci	SCpnt->result = DID_RESET << 16;
154362306a36Sopenharmony_ci	enable_irq(instance->irq);
154462306a36Sopenharmony_ci	spin_unlock_irq(instance->host_lock);
154562306a36Sopenharmony_ci	return SUCCESS;
154662306a36Sopenharmony_ci}
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ciint
154962306a36Sopenharmony_ciwd33c93_abort(struct scsi_cmnd * cmd)
155062306a36Sopenharmony_ci{
155162306a36Sopenharmony_ci	struct Scsi_Host *instance;
155262306a36Sopenharmony_ci	struct WD33C93_hostdata *hostdata;
155362306a36Sopenharmony_ci	wd33c93_regs regs;
155462306a36Sopenharmony_ci	struct scsi_cmnd *tmp, *prev;
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci	disable_irq(cmd->device->host->irq);
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci	instance = cmd->device->host;
155962306a36Sopenharmony_ci	hostdata = (struct WD33C93_hostdata *) instance->hostdata;
156062306a36Sopenharmony_ci	regs = hostdata->regs;
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci/*
156362306a36Sopenharmony_ci * Case 1 : If the command hasn't been issued yet, we simply remove it
156462306a36Sopenharmony_ci *     from the input_Q.
156562306a36Sopenharmony_ci */
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	tmp = (struct scsi_cmnd *) hostdata->input_Q;
156862306a36Sopenharmony_ci	prev = NULL;
156962306a36Sopenharmony_ci	while (tmp) {
157062306a36Sopenharmony_ci		if (tmp == cmd) {
157162306a36Sopenharmony_ci			if (prev)
157262306a36Sopenharmony_ci				prev->host_scribble = cmd->host_scribble;
157362306a36Sopenharmony_ci			else
157462306a36Sopenharmony_ci				hostdata->input_Q =
157562306a36Sopenharmony_ci				    (struct scsi_cmnd *) cmd->host_scribble;
157662306a36Sopenharmony_ci			cmd->host_scribble = NULL;
157762306a36Sopenharmony_ci			cmd->result = DID_ABORT << 16;
157862306a36Sopenharmony_ci			printk
157962306a36Sopenharmony_ci			    ("scsi%d: Abort - removing command from input_Q. ",
158062306a36Sopenharmony_ci			     instance->host_no);
158162306a36Sopenharmony_ci			enable_irq(cmd->device->host->irq);
158262306a36Sopenharmony_ci			scsi_done(cmd);
158362306a36Sopenharmony_ci			return SUCCESS;
158462306a36Sopenharmony_ci		}
158562306a36Sopenharmony_ci		prev = tmp;
158662306a36Sopenharmony_ci		tmp = (struct scsi_cmnd *) tmp->host_scribble;
158762306a36Sopenharmony_ci	}
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci/*
159062306a36Sopenharmony_ci * Case 2 : If the command is connected, we're going to fail the abort
159162306a36Sopenharmony_ci *     and let the high level SCSI driver retry at a later time or
159262306a36Sopenharmony_ci *     issue a reset.
159362306a36Sopenharmony_ci *
159462306a36Sopenharmony_ci *     Timeouts, and therefore aborted commands, will be highly unlikely
159562306a36Sopenharmony_ci *     and handling them cleanly in this situation would make the common
159662306a36Sopenharmony_ci *     case of noresets less efficient, and would pollute our code.  So,
159762306a36Sopenharmony_ci *     we fail.
159862306a36Sopenharmony_ci */
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_ci	if (hostdata->connected == cmd) {
160162306a36Sopenharmony_ci		uchar sr, asr;
160262306a36Sopenharmony_ci		unsigned long timeout;
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci		printk("scsi%d: Aborting connected command - ",
160562306a36Sopenharmony_ci		       instance->host_no);
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci		printk("stopping DMA - ");
160862306a36Sopenharmony_ci		if (hostdata->dma == D_DMA_RUNNING) {
160962306a36Sopenharmony_ci			hostdata->dma_stop(instance, cmd, 0);
161062306a36Sopenharmony_ci			hostdata->dma = D_DMA_OFF;
161162306a36Sopenharmony_ci		}
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci		printk("sending wd33c93 ABORT command - ");
161462306a36Sopenharmony_ci		write_wd33c93(regs, WD_CONTROL,
161562306a36Sopenharmony_ci			      CTRL_IDI | CTRL_EDI | CTRL_POLLED);
161662306a36Sopenharmony_ci		write_wd33c93_cmd(regs, WD_CMD_ABORT);
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci/* Now we have to attempt to flush out the FIFO... */
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci		printk("flushing fifo - ");
162162306a36Sopenharmony_ci		timeout = 1000000;
162262306a36Sopenharmony_ci		do {
162362306a36Sopenharmony_ci			asr = read_aux_stat(regs);
162462306a36Sopenharmony_ci			if (asr & ASR_DBR)
162562306a36Sopenharmony_ci				read_wd33c93(regs, WD_DATA);
162662306a36Sopenharmony_ci		} while (!(asr & ASR_INT) && timeout-- > 0);
162762306a36Sopenharmony_ci		sr = read_wd33c93(regs, WD_SCSI_STATUS);
162862306a36Sopenharmony_ci		printk
162962306a36Sopenharmony_ci		    ("asr=%02x, sr=%02x, %ld bytes un-transferred (timeout=%ld) - ",
163062306a36Sopenharmony_ci		     asr, sr, read_wd33c93_count(regs), timeout);
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci		/*
163362306a36Sopenharmony_ci		 * Abort command processed.
163462306a36Sopenharmony_ci		 * Still connected.
163562306a36Sopenharmony_ci		 * We must disconnect.
163662306a36Sopenharmony_ci		 */
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci		printk("sending wd33c93 DISCONNECT command - ");
163962306a36Sopenharmony_ci		write_wd33c93_cmd(regs, WD_CMD_DISCONNECT);
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci		timeout = 1000000;
164262306a36Sopenharmony_ci		asr = read_aux_stat(regs);
164362306a36Sopenharmony_ci		while ((asr & ASR_CIP) && timeout-- > 0)
164462306a36Sopenharmony_ci			asr = read_aux_stat(regs);
164562306a36Sopenharmony_ci		sr = read_wd33c93(regs, WD_SCSI_STATUS);
164662306a36Sopenharmony_ci		printk("asr=%02x, sr=%02x.", asr, sr);
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci		hostdata->busy[cmd->device->id] &= ~(1 << (cmd->device->lun & 0xff));
164962306a36Sopenharmony_ci		hostdata->connected = NULL;
165062306a36Sopenharmony_ci		hostdata->state = S_UNCONNECTED;
165162306a36Sopenharmony_ci		cmd->result = DID_ABORT << 16;
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci/*      sti();*/
165462306a36Sopenharmony_ci		wd33c93_execute(instance);
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci		enable_irq(cmd->device->host->irq);
165762306a36Sopenharmony_ci		scsi_done(cmd);
165862306a36Sopenharmony_ci		return SUCCESS;
165962306a36Sopenharmony_ci	}
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci/*
166262306a36Sopenharmony_ci * Case 3: If the command is currently disconnected from the bus,
166362306a36Sopenharmony_ci * we're not going to expend much effort here: Let's just return
166462306a36Sopenharmony_ci * an ABORT_SNOOZE and hope for the best...
166562306a36Sopenharmony_ci */
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ci	tmp = (struct scsi_cmnd *) hostdata->disconnected_Q;
166862306a36Sopenharmony_ci	while (tmp) {
166962306a36Sopenharmony_ci		if (tmp == cmd) {
167062306a36Sopenharmony_ci			printk
167162306a36Sopenharmony_ci			    ("scsi%d: Abort - command found on disconnected_Q - ",
167262306a36Sopenharmony_ci			     instance->host_no);
167362306a36Sopenharmony_ci			printk("Abort SNOOZE. ");
167462306a36Sopenharmony_ci			enable_irq(cmd->device->host->irq);
167562306a36Sopenharmony_ci			return FAILED;
167662306a36Sopenharmony_ci		}
167762306a36Sopenharmony_ci		tmp = (struct scsi_cmnd *) tmp->host_scribble;
167862306a36Sopenharmony_ci	}
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_ci/*
168162306a36Sopenharmony_ci * Case 4 : If we reached this point, the command was not found in any of
168262306a36Sopenharmony_ci *     the queues.
168362306a36Sopenharmony_ci *
168462306a36Sopenharmony_ci * We probably reached this point because of an unlikely race condition
168562306a36Sopenharmony_ci * between the command completing successfully and the abortion code,
168662306a36Sopenharmony_ci * so we won't panic, but we will notify the user in case something really
168762306a36Sopenharmony_ci * broke.
168862306a36Sopenharmony_ci */
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci/*   sti();*/
169162306a36Sopenharmony_ci	wd33c93_execute(instance);
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci	enable_irq(cmd->device->host->irq);
169462306a36Sopenharmony_ci	printk("scsi%d: warning : SCSI command probably completed successfully"
169562306a36Sopenharmony_ci	       "         before abortion. ", instance->host_no);
169662306a36Sopenharmony_ci	return FAILED;
169762306a36Sopenharmony_ci}
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci#define MAX_WD33C93_HOSTS 4
170062306a36Sopenharmony_ci#define MAX_SETUP_ARGS ARRAY_SIZE(setup_args)
170162306a36Sopenharmony_ci#define SETUP_BUFFER_SIZE 200
170262306a36Sopenharmony_cistatic char setup_buffer[SETUP_BUFFER_SIZE];
170362306a36Sopenharmony_cistatic char setup_used[MAX_SETUP_ARGS];
170462306a36Sopenharmony_cistatic int done_setup = 0;
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_cistatic int
170762306a36Sopenharmony_ciwd33c93_setup(char *str)
170862306a36Sopenharmony_ci{
170962306a36Sopenharmony_ci	int i;
171062306a36Sopenharmony_ci	char *p1, *p2;
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	/* The kernel does some processing of the command-line before calling
171362306a36Sopenharmony_ci	 * this function: If it begins with any decimal or hex number arguments,
171462306a36Sopenharmony_ci	 * ints[0] = how many numbers found and ints[1] through [n] are the values
171562306a36Sopenharmony_ci	 * themselves. str points to where the non-numeric arguments (if any)
171662306a36Sopenharmony_ci	 * start: We do our own parsing of those. We construct synthetic 'nosync'
171762306a36Sopenharmony_ci	 * keywords out of numeric args (to maintain compatibility with older
171862306a36Sopenharmony_ci	 * versions) and then add the rest of the arguments.
171962306a36Sopenharmony_ci	 */
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci	p1 = setup_buffer;
172262306a36Sopenharmony_ci	*p1 = '\0';
172362306a36Sopenharmony_ci	if (str)
172462306a36Sopenharmony_ci		strncpy(p1, str, SETUP_BUFFER_SIZE - strlen(setup_buffer));
172562306a36Sopenharmony_ci	setup_buffer[SETUP_BUFFER_SIZE - 1] = '\0';
172662306a36Sopenharmony_ci	p1 = setup_buffer;
172762306a36Sopenharmony_ci	i = 0;
172862306a36Sopenharmony_ci	while (*p1 && (i < MAX_SETUP_ARGS)) {
172962306a36Sopenharmony_ci		p2 = strchr(p1, ',');
173062306a36Sopenharmony_ci		if (p2) {
173162306a36Sopenharmony_ci			*p2 = '\0';
173262306a36Sopenharmony_ci			if (p1 != p2)
173362306a36Sopenharmony_ci				setup_args[i] = p1;
173462306a36Sopenharmony_ci			p1 = p2 + 1;
173562306a36Sopenharmony_ci			i++;
173662306a36Sopenharmony_ci		} else {
173762306a36Sopenharmony_ci			setup_args[i] = p1;
173862306a36Sopenharmony_ci			break;
173962306a36Sopenharmony_ci		}
174062306a36Sopenharmony_ci	}
174162306a36Sopenharmony_ci	for (i = 0; i < MAX_SETUP_ARGS; i++)
174262306a36Sopenharmony_ci		setup_used[i] = 0;
174362306a36Sopenharmony_ci	done_setup = 1;
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_ci	return 1;
174662306a36Sopenharmony_ci}
174762306a36Sopenharmony_ci__setup("wd33c93=", wd33c93_setup);
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_ci/* check_setup_args() returns index if key found, 0 if not
175062306a36Sopenharmony_ci */
175162306a36Sopenharmony_cistatic int
175262306a36Sopenharmony_cicheck_setup_args(char *key, int *flags, int *val, char *buf)
175362306a36Sopenharmony_ci{
175462306a36Sopenharmony_ci	int x;
175562306a36Sopenharmony_ci	char *cp;
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci	for (x = 0; x < MAX_SETUP_ARGS; x++) {
175862306a36Sopenharmony_ci		if (setup_used[x])
175962306a36Sopenharmony_ci			continue;
176062306a36Sopenharmony_ci		if (!strncmp(setup_args[x], key, strlen(key)))
176162306a36Sopenharmony_ci			break;
176262306a36Sopenharmony_ci		if (!strncmp(setup_args[x], "next", strlen("next")))
176362306a36Sopenharmony_ci			return 0;
176462306a36Sopenharmony_ci	}
176562306a36Sopenharmony_ci	if (x == MAX_SETUP_ARGS)
176662306a36Sopenharmony_ci		return 0;
176762306a36Sopenharmony_ci	setup_used[x] = 1;
176862306a36Sopenharmony_ci	cp = setup_args[x] + strlen(key);
176962306a36Sopenharmony_ci	*val = -1;
177062306a36Sopenharmony_ci	if (*cp != ':')
177162306a36Sopenharmony_ci		return ++x;
177262306a36Sopenharmony_ci	cp++;
177362306a36Sopenharmony_ci	if ((*cp >= '0') && (*cp <= '9')) {
177462306a36Sopenharmony_ci		*val = simple_strtoul(cp, NULL, 0);
177562306a36Sopenharmony_ci	}
177662306a36Sopenharmony_ci	return ++x;
177762306a36Sopenharmony_ci}
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ci/*
178062306a36Sopenharmony_ci * Calculate internal data-transfer-clock cycle from input-clock
178162306a36Sopenharmony_ci * frequency (/MHz) and fill 'sx_table'.
178262306a36Sopenharmony_ci *
178362306a36Sopenharmony_ci * The original driver used to rely on a fixed sx_table, containing periods
178462306a36Sopenharmony_ci * for (only) the lower limits of the respective input-clock-frequency ranges
178562306a36Sopenharmony_ci * (8-10/12-15/16-20 MHz). Although it seems, that no problems occurred with
178662306a36Sopenharmony_ci * this setting so far, it might be desirable to adjust the transfer periods
178762306a36Sopenharmony_ci * closer to the really attached, possibly 25% higher, input-clock, since
178862306a36Sopenharmony_ci * - the wd33c93 may really use a significant shorter period, than it has
178962306a36Sopenharmony_ci *   negotiated (eg. thrashing the target, which expects 4/8MHz, with 5/10MHz
179062306a36Sopenharmony_ci *   instead).
179162306a36Sopenharmony_ci * - the wd33c93 may ask the target for a lower transfer rate, than the target
179262306a36Sopenharmony_ci *   is capable of (eg. negotiating for an assumed minimum of 252ns instead of
179362306a36Sopenharmony_ci *   possible 200ns, which indeed shows up in tests as an approx. 10% lower
179462306a36Sopenharmony_ci *   transfer rate).
179562306a36Sopenharmony_ci */
179662306a36Sopenharmony_cistatic inline unsigned int
179762306a36Sopenharmony_ciround_4(unsigned int x)
179862306a36Sopenharmony_ci{
179962306a36Sopenharmony_ci	switch (x & 3) {
180062306a36Sopenharmony_ci		case 1: --x;
180162306a36Sopenharmony_ci			break;
180262306a36Sopenharmony_ci		case 2: ++x;
180362306a36Sopenharmony_ci			fallthrough;
180462306a36Sopenharmony_ci		case 3: ++x;
180562306a36Sopenharmony_ci	}
180662306a36Sopenharmony_ci	return x;
180762306a36Sopenharmony_ci}
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_cistatic void
181062306a36Sopenharmony_cicalc_sx_table(unsigned int mhz, struct sx_period sx_table[9])
181162306a36Sopenharmony_ci{
181262306a36Sopenharmony_ci	unsigned int d, i;
181362306a36Sopenharmony_ci	if (mhz < 11)
181462306a36Sopenharmony_ci		d = 2;	/* divisor for  8-10 MHz input-clock */
181562306a36Sopenharmony_ci	else if (mhz < 16)
181662306a36Sopenharmony_ci		d = 3;	/* divisor for 12-15 MHz input-clock */
181762306a36Sopenharmony_ci	else
181862306a36Sopenharmony_ci		d = 4;	/* divisor for 16-20 MHz input-clock */
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_ci	d = (100000 * d) / 2 / mhz; /* 100 x DTCC / nanosec */
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci	sx_table[0].period_ns = 1;
182362306a36Sopenharmony_ci	sx_table[0].reg_value = 0x20;
182462306a36Sopenharmony_ci	for (i = 1; i < 8; i++) {
182562306a36Sopenharmony_ci		sx_table[i].period_ns = round_4((i+1)*d / 100);
182662306a36Sopenharmony_ci		sx_table[i].reg_value = (i+1)*0x10;
182762306a36Sopenharmony_ci	}
182862306a36Sopenharmony_ci	sx_table[7].reg_value = 0;
182962306a36Sopenharmony_ci	sx_table[8].period_ns = 0;
183062306a36Sopenharmony_ci	sx_table[8].reg_value = 0;
183162306a36Sopenharmony_ci}
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci/*
183462306a36Sopenharmony_ci * check and, maybe, map an init- or "clock:"- argument.
183562306a36Sopenharmony_ci */
183662306a36Sopenharmony_cistatic uchar
183762306a36Sopenharmony_ciset_clk_freq(int freq, int *mhz)
183862306a36Sopenharmony_ci{
183962306a36Sopenharmony_ci	int x = freq;
184062306a36Sopenharmony_ci	if (WD33C93_FS_8_10 == freq)
184162306a36Sopenharmony_ci		freq = 8;
184262306a36Sopenharmony_ci	else if (WD33C93_FS_12_15 == freq)
184362306a36Sopenharmony_ci		freq = 12;
184462306a36Sopenharmony_ci	else if (WD33C93_FS_16_20 == freq)
184562306a36Sopenharmony_ci		freq = 16;
184662306a36Sopenharmony_ci	else if (freq > 7 && freq < 11)
184762306a36Sopenharmony_ci		x = WD33C93_FS_8_10;
184862306a36Sopenharmony_ci		else if (freq > 11 && freq < 16)
184962306a36Sopenharmony_ci		x = WD33C93_FS_12_15;
185062306a36Sopenharmony_ci		else if (freq > 15 && freq < 21)
185162306a36Sopenharmony_ci		x = WD33C93_FS_16_20;
185262306a36Sopenharmony_ci	else {
185362306a36Sopenharmony_ci			/* Hmm, wouldn't it be safer to assume highest freq here? */
185462306a36Sopenharmony_ci		x = WD33C93_FS_8_10;
185562306a36Sopenharmony_ci		freq = 8;
185662306a36Sopenharmony_ci	}
185762306a36Sopenharmony_ci	*mhz = freq;
185862306a36Sopenharmony_ci	return x;
185962306a36Sopenharmony_ci}
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_ci/*
186262306a36Sopenharmony_ci * to be used with the resync: fast: ... options
186362306a36Sopenharmony_ci */
186462306a36Sopenharmony_cistatic inline void set_resync ( struct WD33C93_hostdata *hd, int mask )
186562306a36Sopenharmony_ci{
186662306a36Sopenharmony_ci	int i;
186762306a36Sopenharmony_ci	for (i = 0; i < 8; i++)
186862306a36Sopenharmony_ci		if (mask & (1 << i))
186962306a36Sopenharmony_ci			hd->sync_stat[i] = SS_UNSET;
187062306a36Sopenharmony_ci}
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_civoid
187362306a36Sopenharmony_ciwd33c93_init(struct Scsi_Host *instance, const wd33c93_regs regs,
187462306a36Sopenharmony_ci	     dma_setup_t setup, dma_stop_t stop, int clock_freq)
187562306a36Sopenharmony_ci{
187662306a36Sopenharmony_ci	struct WD33C93_hostdata *hostdata;
187762306a36Sopenharmony_ci	int i;
187862306a36Sopenharmony_ci	int flags;
187962306a36Sopenharmony_ci	int val;
188062306a36Sopenharmony_ci	char buf[32];
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_ci	if (!done_setup && setup_strings)
188362306a36Sopenharmony_ci		wd33c93_setup(setup_strings);
188462306a36Sopenharmony_ci
188562306a36Sopenharmony_ci	hostdata = (struct WD33C93_hostdata *) instance->hostdata;
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci	hostdata->regs = regs;
188862306a36Sopenharmony_ci	hostdata->clock_freq = set_clk_freq(clock_freq, &i);
188962306a36Sopenharmony_ci	calc_sx_table(i, hostdata->sx_table);
189062306a36Sopenharmony_ci	hostdata->dma_setup = setup;
189162306a36Sopenharmony_ci	hostdata->dma_stop = stop;
189262306a36Sopenharmony_ci	hostdata->dma_bounce_buffer = NULL;
189362306a36Sopenharmony_ci	hostdata->dma_bounce_len = 0;
189462306a36Sopenharmony_ci	for (i = 0; i < 8; i++) {
189562306a36Sopenharmony_ci		hostdata->busy[i] = 0;
189662306a36Sopenharmony_ci		hostdata->sync_xfer[i] =
189762306a36Sopenharmony_ci			calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF,
189862306a36Sopenharmony_ci					0, hostdata->sx_table);
189962306a36Sopenharmony_ci		hostdata->sync_stat[i] = SS_UNSET;	/* using default sync values */
190062306a36Sopenharmony_ci#ifdef PROC_STATISTICS
190162306a36Sopenharmony_ci		hostdata->cmd_cnt[i] = 0;
190262306a36Sopenharmony_ci		hostdata->disc_allowed_cnt[i] = 0;
190362306a36Sopenharmony_ci		hostdata->disc_done_cnt[i] = 0;
190462306a36Sopenharmony_ci#endif
190562306a36Sopenharmony_ci	}
190662306a36Sopenharmony_ci	hostdata->input_Q = NULL;
190762306a36Sopenharmony_ci	hostdata->selecting = NULL;
190862306a36Sopenharmony_ci	hostdata->connected = NULL;
190962306a36Sopenharmony_ci	hostdata->disconnected_Q = NULL;
191062306a36Sopenharmony_ci	hostdata->state = S_UNCONNECTED;
191162306a36Sopenharmony_ci	hostdata->dma = D_DMA_OFF;
191262306a36Sopenharmony_ci	hostdata->level2 = L2_BASIC;
191362306a36Sopenharmony_ci	hostdata->disconnect = DIS_ADAPTIVE;
191462306a36Sopenharmony_ci	hostdata->args = DEBUG_DEFAULTS;
191562306a36Sopenharmony_ci	hostdata->incoming_ptr = 0;
191662306a36Sopenharmony_ci	hostdata->outgoing_len = 0;
191762306a36Sopenharmony_ci	hostdata->default_sx_per = DEFAULT_SX_PER;
191862306a36Sopenharmony_ci	hostdata->no_dma = 0;	/* default is DMA enabled */
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_ci#ifdef PROC_INTERFACE
192162306a36Sopenharmony_ci	hostdata->proc = PR_VERSION | PR_INFO | PR_STATISTICS |
192262306a36Sopenharmony_ci	    PR_CONNECTED | PR_INPUTQ | PR_DISCQ | PR_STOP;
192362306a36Sopenharmony_ci#ifdef PROC_STATISTICS
192462306a36Sopenharmony_ci	hostdata->dma_cnt = 0;
192562306a36Sopenharmony_ci	hostdata->pio_cnt = 0;
192662306a36Sopenharmony_ci	hostdata->int_cnt = 0;
192762306a36Sopenharmony_ci#endif
192862306a36Sopenharmony_ci#endif
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ci	if (check_setup_args("clock", &flags, &val, buf)) {
193162306a36Sopenharmony_ci		hostdata->clock_freq = set_clk_freq(val, &val);
193262306a36Sopenharmony_ci		calc_sx_table(val, hostdata->sx_table);
193362306a36Sopenharmony_ci	}
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci	if (check_setup_args("nosync", &flags, &val, buf))
193662306a36Sopenharmony_ci		hostdata->no_sync = val;
193762306a36Sopenharmony_ci
193862306a36Sopenharmony_ci	if (check_setup_args("nodma", &flags, &val, buf))
193962306a36Sopenharmony_ci		hostdata->no_dma = (val == -1) ? 1 : val;
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ci	if (check_setup_args("period", &flags, &val, buf))
194262306a36Sopenharmony_ci		hostdata->default_sx_per =
194362306a36Sopenharmony_ci		    hostdata->sx_table[round_period((unsigned int) val,
194462306a36Sopenharmony_ci		                                    hostdata->sx_table)].period_ns;
194562306a36Sopenharmony_ci
194662306a36Sopenharmony_ci	if (check_setup_args("disconnect", &flags, &val, buf)) {
194762306a36Sopenharmony_ci		if ((val >= DIS_NEVER) && (val <= DIS_ALWAYS))
194862306a36Sopenharmony_ci			hostdata->disconnect = val;
194962306a36Sopenharmony_ci		else
195062306a36Sopenharmony_ci			hostdata->disconnect = DIS_ADAPTIVE;
195162306a36Sopenharmony_ci	}
195262306a36Sopenharmony_ci
195362306a36Sopenharmony_ci	if (check_setup_args("level2", &flags, &val, buf))
195462306a36Sopenharmony_ci		hostdata->level2 = val;
195562306a36Sopenharmony_ci
195662306a36Sopenharmony_ci	if (check_setup_args("debug", &flags, &val, buf))
195762306a36Sopenharmony_ci		hostdata->args = val & DB_MASK;
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_ci	if (check_setup_args("burst", &flags, &val, buf))
196062306a36Sopenharmony_ci		hostdata->dma_mode = val ? CTRL_BURST:CTRL_DMA;
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_ci	if (WD33C93_FS_16_20 == hostdata->clock_freq /* divisor 4 */
196362306a36Sopenharmony_ci		&& check_setup_args("fast", &flags, &val, buf))
196462306a36Sopenharmony_ci		hostdata->fast = !!val;
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_ci	if ((i = check_setup_args("next", &flags, &val, buf))) {
196762306a36Sopenharmony_ci		while (i)
196862306a36Sopenharmony_ci			setup_used[--i] = 1;
196962306a36Sopenharmony_ci	}
197062306a36Sopenharmony_ci#ifdef PROC_INTERFACE
197162306a36Sopenharmony_ci	if (check_setup_args("proc", &flags, &val, buf))
197262306a36Sopenharmony_ci		hostdata->proc = val;
197362306a36Sopenharmony_ci#endif
197462306a36Sopenharmony_ci
197562306a36Sopenharmony_ci	spin_lock_irq(&hostdata->lock);
197662306a36Sopenharmony_ci	reset_wd33c93(instance);
197762306a36Sopenharmony_ci	spin_unlock_irq(&hostdata->lock);
197862306a36Sopenharmony_ci
197962306a36Sopenharmony_ci	printk("wd33c93-%d: chip=%s/%d no_sync=0x%x no_dma=%d",
198062306a36Sopenharmony_ci	       instance->host_no,
198162306a36Sopenharmony_ci	       (hostdata->chip == C_WD33C93) ? "WD33c93" : (hostdata->chip ==
198262306a36Sopenharmony_ci							    C_WD33C93A) ?
198362306a36Sopenharmony_ci	       "WD33c93A" : (hostdata->chip ==
198462306a36Sopenharmony_ci			     C_WD33C93B) ? "WD33c93B" : "unknown",
198562306a36Sopenharmony_ci	       hostdata->microcode, hostdata->no_sync, hostdata->no_dma);
198662306a36Sopenharmony_ci#ifdef DEBUGGING_ON
198762306a36Sopenharmony_ci	printk(" debug_flags=0x%02x\n", hostdata->args);
198862306a36Sopenharmony_ci#else
198962306a36Sopenharmony_ci	printk(" debugging=OFF\n");
199062306a36Sopenharmony_ci#endif
199162306a36Sopenharmony_ci	printk("           setup_args=");
199262306a36Sopenharmony_ci	for (i = 0; i < MAX_SETUP_ARGS; i++)
199362306a36Sopenharmony_ci		printk("%s,", setup_args[i]);
199462306a36Sopenharmony_ci	printk("\n");
199562306a36Sopenharmony_ci	printk("           Version %s - %s\n", WD33C93_VERSION, WD33C93_DATE);
199662306a36Sopenharmony_ci}
199762306a36Sopenharmony_ci
199862306a36Sopenharmony_ciint wd33c93_write_info(struct Scsi_Host *instance, char *buf, int len)
199962306a36Sopenharmony_ci{
200062306a36Sopenharmony_ci#ifdef PROC_INTERFACE
200162306a36Sopenharmony_ci	char *bp;
200262306a36Sopenharmony_ci	struct WD33C93_hostdata *hd;
200362306a36Sopenharmony_ci	int x;
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_ci	hd = (struct WD33C93_hostdata *) instance->hostdata;
200662306a36Sopenharmony_ci
200762306a36Sopenharmony_ci/* We accept the following
200862306a36Sopenharmony_ci * keywords (same format as command-line, but arguments are not optional):
200962306a36Sopenharmony_ci *    debug
201062306a36Sopenharmony_ci *    disconnect
201162306a36Sopenharmony_ci *    period
201262306a36Sopenharmony_ci *    resync
201362306a36Sopenharmony_ci *    proc
201462306a36Sopenharmony_ci *    nodma
201562306a36Sopenharmony_ci *    level2
201662306a36Sopenharmony_ci *    burst
201762306a36Sopenharmony_ci *    fast
201862306a36Sopenharmony_ci *    nosync
201962306a36Sopenharmony_ci */
202062306a36Sopenharmony_ci
202162306a36Sopenharmony_ci	buf[len] = '\0';
202262306a36Sopenharmony_ci	for (bp = buf; *bp; ) {
202362306a36Sopenharmony_ci		while (',' == *bp || ' ' == *bp)
202462306a36Sopenharmony_ci			++bp;
202562306a36Sopenharmony_ci	if (!strncmp(bp, "debug:", 6)) {
202662306a36Sopenharmony_ci			hd->args = simple_strtoul(bp+6, &bp, 0) & DB_MASK;
202762306a36Sopenharmony_ci	} else if (!strncmp(bp, "disconnect:", 11)) {
202862306a36Sopenharmony_ci			x = simple_strtoul(bp+11, &bp, 0);
202962306a36Sopenharmony_ci		if (x < DIS_NEVER || x > DIS_ALWAYS)
203062306a36Sopenharmony_ci			x = DIS_ADAPTIVE;
203162306a36Sopenharmony_ci		hd->disconnect = x;
203262306a36Sopenharmony_ci	} else if (!strncmp(bp, "period:", 7)) {
203362306a36Sopenharmony_ci		x = simple_strtoul(bp+7, &bp, 0);
203462306a36Sopenharmony_ci		hd->default_sx_per =
203562306a36Sopenharmony_ci			hd->sx_table[round_period((unsigned int) x,
203662306a36Sopenharmony_ci						  hd->sx_table)].period_ns;
203762306a36Sopenharmony_ci	} else if (!strncmp(bp, "resync:", 7)) {
203862306a36Sopenharmony_ci			set_resync(hd, (int)simple_strtoul(bp+7, &bp, 0));
203962306a36Sopenharmony_ci	} else if (!strncmp(bp, "proc:", 5)) {
204062306a36Sopenharmony_ci			hd->proc = simple_strtoul(bp+5, &bp, 0);
204162306a36Sopenharmony_ci	} else if (!strncmp(bp, "nodma:", 6)) {
204262306a36Sopenharmony_ci			hd->no_dma = simple_strtoul(bp+6, &bp, 0);
204362306a36Sopenharmony_ci	} else if (!strncmp(bp, "level2:", 7)) {
204462306a36Sopenharmony_ci			hd->level2 = simple_strtoul(bp+7, &bp, 0);
204562306a36Sopenharmony_ci		} else if (!strncmp(bp, "burst:", 6)) {
204662306a36Sopenharmony_ci			hd->dma_mode =
204762306a36Sopenharmony_ci				simple_strtol(bp+6, &bp, 0) ? CTRL_BURST:CTRL_DMA;
204862306a36Sopenharmony_ci		} else if (!strncmp(bp, "fast:", 5)) {
204962306a36Sopenharmony_ci			x = !!simple_strtol(bp+5, &bp, 0);
205062306a36Sopenharmony_ci			if (x != hd->fast)
205162306a36Sopenharmony_ci				set_resync(hd, 0xff);
205262306a36Sopenharmony_ci			hd->fast = x;
205362306a36Sopenharmony_ci		} else if (!strncmp(bp, "nosync:", 7)) {
205462306a36Sopenharmony_ci			x = simple_strtoul(bp+7, &bp, 0);
205562306a36Sopenharmony_ci			set_resync(hd, x ^ hd->no_sync);
205662306a36Sopenharmony_ci			hd->no_sync = x;
205762306a36Sopenharmony_ci		} else {
205862306a36Sopenharmony_ci			break; /* unknown keyword,syntax-error,... */
205962306a36Sopenharmony_ci		}
206062306a36Sopenharmony_ci	}
206162306a36Sopenharmony_ci	return len;
206262306a36Sopenharmony_ci#else
206362306a36Sopenharmony_ci	return 0;
206462306a36Sopenharmony_ci#endif
206562306a36Sopenharmony_ci}
206662306a36Sopenharmony_ci
206762306a36Sopenharmony_ciint
206862306a36Sopenharmony_ciwd33c93_show_info(struct seq_file *m, struct Scsi_Host *instance)
206962306a36Sopenharmony_ci{
207062306a36Sopenharmony_ci#ifdef PROC_INTERFACE
207162306a36Sopenharmony_ci	struct WD33C93_hostdata *hd;
207262306a36Sopenharmony_ci	struct scsi_cmnd *cmd;
207362306a36Sopenharmony_ci	int x;
207462306a36Sopenharmony_ci
207562306a36Sopenharmony_ci	hd = (struct WD33C93_hostdata *) instance->hostdata;
207662306a36Sopenharmony_ci
207762306a36Sopenharmony_ci	spin_lock_irq(&hd->lock);
207862306a36Sopenharmony_ci	if (hd->proc & PR_VERSION)
207962306a36Sopenharmony_ci		seq_printf(m, "\nVersion %s - %s.",
208062306a36Sopenharmony_ci			WD33C93_VERSION, WD33C93_DATE);
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_ci	if (hd->proc & PR_INFO) {
208362306a36Sopenharmony_ci		seq_printf(m, "\nclock_freq=%02x no_sync=%02x no_dma=%d"
208462306a36Sopenharmony_ci			" dma_mode=%02x fast=%d",
208562306a36Sopenharmony_ci			hd->clock_freq, hd->no_sync, hd->no_dma, hd->dma_mode, hd->fast);
208662306a36Sopenharmony_ci		seq_puts(m, "\nsync_xfer[] =       ");
208762306a36Sopenharmony_ci		for (x = 0; x < 7; x++)
208862306a36Sopenharmony_ci			seq_printf(m, "\t%02x", hd->sync_xfer[x]);
208962306a36Sopenharmony_ci		seq_puts(m, "\nsync_stat[] =       ");
209062306a36Sopenharmony_ci		for (x = 0; x < 7; x++)
209162306a36Sopenharmony_ci			seq_printf(m, "\t%02x", hd->sync_stat[x]);
209262306a36Sopenharmony_ci	}
209362306a36Sopenharmony_ci#ifdef PROC_STATISTICS
209462306a36Sopenharmony_ci	if (hd->proc & PR_STATISTICS) {
209562306a36Sopenharmony_ci		seq_puts(m, "\ncommands issued:    ");
209662306a36Sopenharmony_ci		for (x = 0; x < 7; x++)
209762306a36Sopenharmony_ci			seq_printf(m, "\t%ld", hd->cmd_cnt[x]);
209862306a36Sopenharmony_ci		seq_puts(m, "\ndisconnects allowed:");
209962306a36Sopenharmony_ci		for (x = 0; x < 7; x++)
210062306a36Sopenharmony_ci			seq_printf(m, "\t%ld", hd->disc_allowed_cnt[x]);
210162306a36Sopenharmony_ci		seq_puts(m, "\ndisconnects done:   ");
210262306a36Sopenharmony_ci		for (x = 0; x < 7; x++)
210362306a36Sopenharmony_ci			seq_printf(m, "\t%ld", hd->disc_done_cnt[x]);
210462306a36Sopenharmony_ci		seq_printf(m,
210562306a36Sopenharmony_ci			"\ninterrupts: %ld, DATA_PHASE ints: %ld DMA, %ld PIO",
210662306a36Sopenharmony_ci			hd->int_cnt, hd->dma_cnt, hd->pio_cnt);
210762306a36Sopenharmony_ci	}
210862306a36Sopenharmony_ci#endif
210962306a36Sopenharmony_ci	if (hd->proc & PR_CONNECTED) {
211062306a36Sopenharmony_ci		seq_puts(m, "\nconnected:     ");
211162306a36Sopenharmony_ci		if (hd->connected) {
211262306a36Sopenharmony_ci			cmd = (struct scsi_cmnd *) hd->connected;
211362306a36Sopenharmony_ci			seq_printf(m, " %d:%llu(%02x)",
211462306a36Sopenharmony_ci				cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
211562306a36Sopenharmony_ci		}
211662306a36Sopenharmony_ci	}
211762306a36Sopenharmony_ci	if (hd->proc & PR_INPUTQ) {
211862306a36Sopenharmony_ci		seq_puts(m, "\ninput_Q:       ");
211962306a36Sopenharmony_ci		cmd = (struct scsi_cmnd *) hd->input_Q;
212062306a36Sopenharmony_ci		while (cmd) {
212162306a36Sopenharmony_ci			seq_printf(m, " %d:%llu(%02x)",
212262306a36Sopenharmony_ci				cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
212362306a36Sopenharmony_ci			cmd = (struct scsi_cmnd *) cmd->host_scribble;
212462306a36Sopenharmony_ci		}
212562306a36Sopenharmony_ci	}
212662306a36Sopenharmony_ci	if (hd->proc & PR_DISCQ) {
212762306a36Sopenharmony_ci		seq_puts(m, "\ndisconnected_Q:");
212862306a36Sopenharmony_ci		cmd = (struct scsi_cmnd *) hd->disconnected_Q;
212962306a36Sopenharmony_ci		while (cmd) {
213062306a36Sopenharmony_ci			seq_printf(m, " %d:%llu(%02x)",
213162306a36Sopenharmony_ci				cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
213262306a36Sopenharmony_ci			cmd = (struct scsi_cmnd *) cmd->host_scribble;
213362306a36Sopenharmony_ci		}
213462306a36Sopenharmony_ci	}
213562306a36Sopenharmony_ci	seq_putc(m, '\n');
213662306a36Sopenharmony_ci	spin_unlock_irq(&hd->lock);
213762306a36Sopenharmony_ci#endif				/* PROC_INTERFACE */
213862306a36Sopenharmony_ci	return 0;
213962306a36Sopenharmony_ci}
214062306a36Sopenharmony_ci
214162306a36Sopenharmony_ciEXPORT_SYMBOL(wd33c93_host_reset);
214262306a36Sopenharmony_ciEXPORT_SYMBOL(wd33c93_init);
214362306a36Sopenharmony_ciEXPORT_SYMBOL(wd33c93_abort);
214462306a36Sopenharmony_ciEXPORT_SYMBOL(wd33c93_queuecommand);
214562306a36Sopenharmony_ciEXPORT_SYMBOL(wd33c93_intr);
214662306a36Sopenharmony_ciEXPORT_SYMBOL(wd33c93_show_info);
214762306a36Sopenharmony_ciEXPORT_SYMBOL(wd33c93_write_info);
2148