18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  linux/drivers/acorn/scsi/acornscsi.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Acorn SCSI 3 driver
68c2ecf20Sopenharmony_ci *  By R.M.King.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Abandoned using the Select and Transfer command since there were
98c2ecf20Sopenharmony_ci * some nasty races between our software and the target devices that
108c2ecf20Sopenharmony_ci * were not easy to solve, and the device errata had a lot of entries
118c2ecf20Sopenharmony_ci * for this command, some of them quite nasty...
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * Changelog:
148c2ecf20Sopenharmony_ci *  26-Sep-1997	RMK	Re-jigged to use the queue module.
158c2ecf20Sopenharmony_ci *			Re-coded state machine to be based on driver
168c2ecf20Sopenharmony_ci *			state not scsi state.  Should be easier to debug.
178c2ecf20Sopenharmony_ci *			Added acornscsi_release to clean up properly.
188c2ecf20Sopenharmony_ci *			Updated proc/scsi reporting.
198c2ecf20Sopenharmony_ci *  05-Oct-1997	RMK	Implemented writing to SCSI devices.
208c2ecf20Sopenharmony_ci *  06-Oct-1997	RMK	Corrected small (non-serious) bug with the connect/
218c2ecf20Sopenharmony_ci *			reconnect race condition causing a warning message.
228c2ecf20Sopenharmony_ci *  12-Oct-1997	RMK	Added catch for re-entering interrupt routine.
238c2ecf20Sopenharmony_ci *  15-Oct-1997	RMK	Improved handling of commands.
248c2ecf20Sopenharmony_ci *  27-Jun-1998	RMK	Changed asm/delay.h to linux/delay.h.
258c2ecf20Sopenharmony_ci *  13-Dec-1998	RMK	Better abort code and command handling.  Extra state
268c2ecf20Sopenharmony_ci *			transitions added to allow dodgy devices to work.
278c2ecf20Sopenharmony_ci */
288c2ecf20Sopenharmony_ci#define DEBUG_NO_WRITE	1
298c2ecf20Sopenharmony_ci#define DEBUG_QUEUES	2
308c2ecf20Sopenharmony_ci#define DEBUG_DMA	4
318c2ecf20Sopenharmony_ci#define DEBUG_ABORT	8
328c2ecf20Sopenharmony_ci#define DEBUG_DISCON	16
338c2ecf20Sopenharmony_ci#define DEBUG_CONNECT	32
348c2ecf20Sopenharmony_ci#define DEBUG_PHASES	64
358c2ecf20Sopenharmony_ci#define DEBUG_WRITE	128
368c2ecf20Sopenharmony_ci#define DEBUG_LINK	256
378c2ecf20Sopenharmony_ci#define DEBUG_MESSAGES	512
388c2ecf20Sopenharmony_ci#define DEBUG_RESET	1024
398c2ecf20Sopenharmony_ci#define DEBUG_ALL	(DEBUG_RESET|DEBUG_MESSAGES|DEBUG_LINK|DEBUG_WRITE|\
408c2ecf20Sopenharmony_ci			 DEBUG_PHASES|DEBUG_CONNECT|DEBUG_DISCON|DEBUG_ABORT|\
418c2ecf20Sopenharmony_ci			 DEBUG_DMA|DEBUG_QUEUES)
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci/* DRIVER CONFIGURATION
448c2ecf20Sopenharmony_ci *
458c2ecf20Sopenharmony_ci * SCSI-II Tagged queue support.
468c2ecf20Sopenharmony_ci *
478c2ecf20Sopenharmony_ci * I don't have any SCSI devices that support it, so it is totally untested
488c2ecf20Sopenharmony_ci * (except to make sure that it doesn't interfere with any non-tagging
498c2ecf20Sopenharmony_ci * devices).  It is not fully implemented either - what happens when a
508c2ecf20Sopenharmony_ci * tagging device reconnects???
518c2ecf20Sopenharmony_ci *
528c2ecf20Sopenharmony_ci * You can tell if you have a device that supports tagged queueing my
538c2ecf20Sopenharmony_ci * cating (eg) /proc/scsi/acornscsi/0 and see if the SCSI revision is reported
548c2ecf20Sopenharmony_ci * as '2 TAG'.
558c2ecf20Sopenharmony_ci *
568c2ecf20Sopenharmony_ci * Also note that CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE is normally set in the config
578c2ecf20Sopenharmony_ci * scripts, but disabled here.  Once debugged, remove the #undef, otherwise to debug,
588c2ecf20Sopenharmony_ci * comment out the undef.
598c2ecf20Sopenharmony_ci */
608c2ecf20Sopenharmony_ci#undef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
618c2ecf20Sopenharmony_ci/*
628c2ecf20Sopenharmony_ci * SCSI-II Synchronous transfer support.
638c2ecf20Sopenharmony_ci *
648c2ecf20Sopenharmony_ci * Tried and tested...
658c2ecf20Sopenharmony_ci *
668c2ecf20Sopenharmony_ci * SDTR_SIZE	  - maximum number of un-acknowledged bytes (0 = off, 12 = max)
678c2ecf20Sopenharmony_ci * SDTR_PERIOD	  - period of REQ signal (min=125, max=1020)
688c2ecf20Sopenharmony_ci * DEFAULT_PERIOD - default REQ period.
698c2ecf20Sopenharmony_ci */
708c2ecf20Sopenharmony_ci#define SDTR_SIZE	12
718c2ecf20Sopenharmony_ci#define SDTR_PERIOD	125
728c2ecf20Sopenharmony_ci#define DEFAULT_PERIOD	500
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci/*
758c2ecf20Sopenharmony_ci * Debugging information
768c2ecf20Sopenharmony_ci *
778c2ecf20Sopenharmony_ci * DEBUG	  - bit mask from list above
788c2ecf20Sopenharmony_ci * DEBUG_TARGET   - is defined to the target number if you want to debug
798c2ecf20Sopenharmony_ci *		    a specific target. [only recon/write/dma].
808c2ecf20Sopenharmony_ci */
818c2ecf20Sopenharmony_ci#define DEBUG (DEBUG_RESET|DEBUG_WRITE|DEBUG_NO_WRITE)
828c2ecf20Sopenharmony_ci/* only allow writing to SCSI device 0 */
838c2ecf20Sopenharmony_ci#define NO_WRITE 0xFE
848c2ecf20Sopenharmony_ci/*#define DEBUG_TARGET 2*/
858c2ecf20Sopenharmony_ci/*
868c2ecf20Sopenharmony_ci * Select timeout time (in 10ms units)
878c2ecf20Sopenharmony_ci *
888c2ecf20Sopenharmony_ci * This is the timeout used between the start of selection and the WD33C93
898c2ecf20Sopenharmony_ci * chip deciding that the device isn't responding.
908c2ecf20Sopenharmony_ci */
918c2ecf20Sopenharmony_ci#define TIMEOUT_TIME 10
928c2ecf20Sopenharmony_ci/*
938c2ecf20Sopenharmony_ci * Define this if you want to have verbose explanation of SCSI
948c2ecf20Sopenharmony_ci * status/messages.
958c2ecf20Sopenharmony_ci */
968c2ecf20Sopenharmony_ci#undef CONFIG_ACORNSCSI_CONSTANTS
978c2ecf20Sopenharmony_ci/*
988c2ecf20Sopenharmony_ci * Define this if you want to use the on board DMAC [don't remove this option]
998c2ecf20Sopenharmony_ci * If not set, then use PIO mode (not currently supported).
1008c2ecf20Sopenharmony_ci */
1018c2ecf20Sopenharmony_ci#define USE_DMAC
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci/*
1048c2ecf20Sopenharmony_ci * ====================================================================================
1058c2ecf20Sopenharmony_ci */
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci#ifdef DEBUG_TARGET
1088c2ecf20Sopenharmony_ci#define DBG(cmd,xxx...) \
1098c2ecf20Sopenharmony_ci  if (cmd->device->id == DEBUG_TARGET) { \
1108c2ecf20Sopenharmony_ci    xxx; \
1118c2ecf20Sopenharmony_ci  }
1128c2ecf20Sopenharmony_ci#else
1138c2ecf20Sopenharmony_ci#define DBG(cmd,xxx...) xxx
1148c2ecf20Sopenharmony_ci#endif
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci#include <linux/module.h>
1178c2ecf20Sopenharmony_ci#include <linux/kernel.h>
1188c2ecf20Sopenharmony_ci#include <linux/string.h>
1198c2ecf20Sopenharmony_ci#include <linux/signal.h>
1208c2ecf20Sopenharmony_ci#include <linux/errno.h>
1218c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
1228c2ecf20Sopenharmony_ci#include <linux/ioport.h>
1238c2ecf20Sopenharmony_ci#include <linux/blkdev.h>
1248c2ecf20Sopenharmony_ci#include <linux/delay.h>
1258c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
1268c2ecf20Sopenharmony_ci#include <linux/init.h>
1278c2ecf20Sopenharmony_ci#include <linux/bitops.h>
1288c2ecf20Sopenharmony_ci#include <linux/stringify.h>
1298c2ecf20Sopenharmony_ci#include <linux/io.h>
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci#include <asm/ecard.h>
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci#include "../scsi.h"
1348c2ecf20Sopenharmony_ci#include <scsi/scsi_dbg.h>
1358c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h>
1368c2ecf20Sopenharmony_ci#include <scsi/scsi_transport_spi.h>
1378c2ecf20Sopenharmony_ci#include "acornscsi.h"
1388c2ecf20Sopenharmony_ci#include "msgqueue.h"
1398c2ecf20Sopenharmony_ci#include "scsi.h"
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci#include <scsi/scsicam.h>
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci#define VER_MAJOR 2
1448c2ecf20Sopenharmony_ci#define VER_MINOR 0
1458c2ecf20Sopenharmony_ci#define VER_PATCH 6
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci#ifndef ABORT_TAG
1488c2ecf20Sopenharmony_ci#define ABORT_TAG 0xd
1498c2ecf20Sopenharmony_ci#else
1508c2ecf20Sopenharmony_ci#error "Yippee!  ABORT TAG is now defined!  Remove this error!"
1518c2ecf20Sopenharmony_ci#endif
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci#ifdef USE_DMAC
1548c2ecf20Sopenharmony_ci/*
1558c2ecf20Sopenharmony_ci * DMAC setup parameters
1568c2ecf20Sopenharmony_ci */
1578c2ecf20Sopenharmony_ci#define INIT_DEVCON0	(DEVCON0_RQL|DEVCON0_EXW|DEVCON0_CMP)
1588c2ecf20Sopenharmony_ci#define INIT_DEVCON1	(DEVCON1_BHLD)
1598c2ecf20Sopenharmony_ci#define DMAC_READ	(MODECON_READ)
1608c2ecf20Sopenharmony_ci#define DMAC_WRITE	(MODECON_WRITE)
1618c2ecf20Sopenharmony_ci#define INIT_SBICDMA	(CTRL_DMABURST)
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci#define scsi_xferred	have_data_in
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci/*
1668c2ecf20Sopenharmony_ci * Size of on-board DMA buffer
1678c2ecf20Sopenharmony_ci */
1688c2ecf20Sopenharmony_ci#define DMAC_BUFFER_SIZE	65536
1698c2ecf20Sopenharmony_ci#endif
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci#define STATUS_BUFFER_TO_PRINT	24
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ciunsigned int sdtr_period = SDTR_PERIOD;
1748c2ecf20Sopenharmony_ciunsigned int sdtr_size   = SDTR_SIZE;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic void acornscsi_done(AS_Host *host, struct scsi_cmnd **SCpntp,
1778c2ecf20Sopenharmony_ci			   unsigned int result);
1788c2ecf20Sopenharmony_cistatic int acornscsi_reconnect_finish(AS_Host *host);
1798c2ecf20Sopenharmony_cistatic void acornscsi_dma_cleanup(AS_Host *host);
1808c2ecf20Sopenharmony_cistatic void acornscsi_abortcmd(AS_Host *host, unsigned char tag);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci/* ====================================================================================
1838c2ecf20Sopenharmony_ci * Miscellaneous
1848c2ecf20Sopenharmony_ci */
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci/* Offsets from MEMC base */
1878c2ecf20Sopenharmony_ci#define SBIC_REGIDX	0x2000
1888c2ecf20Sopenharmony_ci#define SBIC_REGVAL	0x2004
1898c2ecf20Sopenharmony_ci#define DMAC_OFFSET	0x3000
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci/* Offsets from FAST IOC base */
1928c2ecf20Sopenharmony_ci#define INT_REG		0x2000
1938c2ecf20Sopenharmony_ci#define PAGE_REG	0x3000
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistatic inline void sbic_arm_write(AS_Host *host, unsigned int reg, unsigned int value)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci    writeb(reg, host->base + SBIC_REGIDX);
1988c2ecf20Sopenharmony_ci    writeb(value, host->base + SBIC_REGVAL);
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cistatic inline int sbic_arm_read(AS_Host *host, unsigned int reg)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci    if(reg == SBIC_ASR)
2048c2ecf20Sopenharmony_ci	   return readl(host->base + SBIC_REGIDX) & 255;
2058c2ecf20Sopenharmony_ci    writeb(reg, host->base + SBIC_REGIDX);
2068c2ecf20Sopenharmony_ci    return readl(host->base + SBIC_REGVAL) & 255;
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci#define sbic_arm_writenext(host, val)	writeb((val), (host)->base + SBIC_REGVAL)
2108c2ecf20Sopenharmony_ci#define sbic_arm_readnext(host) 	readb((host)->base + SBIC_REGVAL)
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci#ifdef USE_DMAC
2138c2ecf20Sopenharmony_ci#define dmac_read(host,reg) \
2148c2ecf20Sopenharmony_ci	readb((host)->base + DMAC_OFFSET + ((reg) << 2))
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci#define dmac_write(host,reg,value) \
2178c2ecf20Sopenharmony_ci	({ writeb((value), (host)->base + DMAC_OFFSET + ((reg) << 2)); })
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci#define dmac_clearintr(host) 	writeb(0, (host)->fast + INT_REG)
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_cistatic inline unsigned int dmac_address(AS_Host *host)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci    return dmac_read(host, DMAC_TXADRHI) << 16 |
2248c2ecf20Sopenharmony_ci	   dmac_read(host, DMAC_TXADRMD) << 8 |
2258c2ecf20Sopenharmony_ci	   dmac_read(host, DMAC_TXADRLO);
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic
2298c2ecf20Sopenharmony_civoid acornscsi_dumpdma(AS_Host *host, char *where)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	unsigned int mode, addr, len;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	mode = dmac_read(host, DMAC_MODECON);
2348c2ecf20Sopenharmony_ci	addr = dmac_address(host);
2358c2ecf20Sopenharmony_ci	len  = dmac_read(host, DMAC_TXCNTHI) << 8 |
2368c2ecf20Sopenharmony_ci	       dmac_read(host, DMAC_TXCNTLO);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	printk("scsi%d: %s: DMAC %02x @%06x+%04x msk %02x, ",
2398c2ecf20Sopenharmony_ci		host->host->host_no, where,
2408c2ecf20Sopenharmony_ci		mode, addr, (len + 1) & 0xffff,
2418c2ecf20Sopenharmony_ci		dmac_read(host, DMAC_MASKREG));
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	printk("DMA @%06x, ", host->dma.start_addr);
2448c2ecf20Sopenharmony_ci	printk("BH @%p +%04x, ", host->scsi.SCp.ptr,
2458c2ecf20Sopenharmony_ci		host->scsi.SCp.this_residual);
2468c2ecf20Sopenharmony_ci	printk("DT @+%04x ST @+%04x", host->dma.transferred,
2478c2ecf20Sopenharmony_ci		host->scsi.SCp.scsi_xferred);
2488c2ecf20Sopenharmony_ci	printk("\n");
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci#endif
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic
2538c2ecf20Sopenharmony_ciunsigned long acornscsi_sbic_xfcount(AS_Host *host)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci    unsigned long length;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci    length = sbic_arm_read(host, SBIC_TRANSCNTH) << 16;
2588c2ecf20Sopenharmony_ci    length |= sbic_arm_readnext(host) << 8;
2598c2ecf20Sopenharmony_ci    length |= sbic_arm_readnext(host);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci    return length;
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic int
2658c2ecf20Sopenharmony_ciacornscsi_sbic_wait(AS_Host *host, int stat_mask, int stat, int timeout, char *msg)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	int asr;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	do {
2708c2ecf20Sopenharmony_ci		asr = sbic_arm_read(host, SBIC_ASR);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci		if ((asr & stat_mask) == stat)
2738c2ecf20Sopenharmony_ci			return 0;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci		udelay(1);
2768c2ecf20Sopenharmony_ci	} while (--timeout);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	printk("scsi%d: timeout while %s\n", host->host->host_no, msg);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	return -1;
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic
2848c2ecf20Sopenharmony_ciint acornscsi_sbic_issuecmd(AS_Host *host, int command)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci    if (acornscsi_sbic_wait(host, ASR_CIP, 0, 1000, "issuing command"))
2878c2ecf20Sopenharmony_ci	return -1;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci    sbic_arm_write(host, SBIC_CMND, command);
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci    return 0;
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic void
2958c2ecf20Sopenharmony_ciacornscsi_csdelay(unsigned int cs)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci    unsigned long target_jiffies, flags;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci    target_jiffies = jiffies + 1 + cs * HZ / 100;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci    local_save_flags(flags);
3028c2ecf20Sopenharmony_ci    local_irq_enable();
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci    while (time_before(jiffies, target_jiffies)) barrier();
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci    local_irq_restore(flags);
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_cistatic
3108c2ecf20Sopenharmony_civoid acornscsi_resetcard(AS_Host *host)
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci    unsigned int i, timeout;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci    /* assert reset line */
3158c2ecf20Sopenharmony_ci    host->card.page_reg = 0x80;
3168c2ecf20Sopenharmony_ci    writeb(host->card.page_reg, host->fast + PAGE_REG);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci    /* wait 3 cs.  SCSI standard says 25ms. */
3198c2ecf20Sopenharmony_ci    acornscsi_csdelay(3);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci    host->card.page_reg = 0;
3228c2ecf20Sopenharmony_ci    writeb(host->card.page_reg, host->fast + PAGE_REG);
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci    /*
3258c2ecf20Sopenharmony_ci     * Should get a reset from the card
3268c2ecf20Sopenharmony_ci     */
3278c2ecf20Sopenharmony_ci    timeout = 1000;
3288c2ecf20Sopenharmony_ci    do {
3298c2ecf20Sopenharmony_ci	if (readb(host->fast + INT_REG) & 8)
3308c2ecf20Sopenharmony_ci	    break;
3318c2ecf20Sopenharmony_ci	udelay(1);
3328c2ecf20Sopenharmony_ci    } while (--timeout);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci    if (timeout == 0)
3358c2ecf20Sopenharmony_ci	printk("scsi%d: timeout while resetting card\n",
3368c2ecf20Sopenharmony_ci		host->host->host_no);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci    sbic_arm_read(host, SBIC_ASR);
3398c2ecf20Sopenharmony_ci    sbic_arm_read(host, SBIC_SSR);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci    /* setup sbic - WD33C93A */
3428c2ecf20Sopenharmony_ci    sbic_arm_write(host, SBIC_OWNID, OWNID_EAF | host->host->this_id);
3438c2ecf20Sopenharmony_ci    sbic_arm_write(host, SBIC_CMND, CMND_RESET);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci    /*
3468c2ecf20Sopenharmony_ci     * Command should cause a reset interrupt
3478c2ecf20Sopenharmony_ci     */
3488c2ecf20Sopenharmony_ci    timeout = 1000;
3498c2ecf20Sopenharmony_ci    do {
3508c2ecf20Sopenharmony_ci	if (readb(host->fast + INT_REG) & 8)
3518c2ecf20Sopenharmony_ci	    break;
3528c2ecf20Sopenharmony_ci	udelay(1);
3538c2ecf20Sopenharmony_ci    } while (--timeout);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci    if (timeout == 0)
3568c2ecf20Sopenharmony_ci	printk("scsi%d: timeout while resetting card\n",
3578c2ecf20Sopenharmony_ci		host->host->host_no);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci    sbic_arm_read(host, SBIC_ASR);
3608c2ecf20Sopenharmony_ci    if (sbic_arm_read(host, SBIC_SSR) != 0x01)
3618c2ecf20Sopenharmony_ci	printk(KERN_CRIT "scsi%d: WD33C93A didn't give enhanced reset interrupt\n",
3628c2ecf20Sopenharmony_ci		host->host->host_no);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci    sbic_arm_write(host, SBIC_CTRL, INIT_SBICDMA | CTRL_IDI);
3658c2ecf20Sopenharmony_ci    sbic_arm_write(host, SBIC_TIMEOUT, TIMEOUT_TIME);
3668c2ecf20Sopenharmony_ci    sbic_arm_write(host, SBIC_SYNCHTRANSFER, SYNCHTRANSFER_2DBA);
3678c2ecf20Sopenharmony_ci    sbic_arm_write(host, SBIC_SOURCEID, SOURCEID_ER | SOURCEID_DSP);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci    host->card.page_reg = 0x40;
3708c2ecf20Sopenharmony_ci    writeb(host->card.page_reg, host->fast + PAGE_REG);
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci    /* setup dmac - uPC71071 */
3738c2ecf20Sopenharmony_ci    dmac_write(host, DMAC_INIT, 0);
3748c2ecf20Sopenharmony_ci#ifdef USE_DMAC
3758c2ecf20Sopenharmony_ci    dmac_write(host, DMAC_INIT, INIT_8BIT);
3768c2ecf20Sopenharmony_ci    dmac_write(host, DMAC_CHANNEL, CHANNEL_0);
3778c2ecf20Sopenharmony_ci    dmac_write(host, DMAC_DEVCON0, INIT_DEVCON0);
3788c2ecf20Sopenharmony_ci    dmac_write(host, DMAC_DEVCON1, INIT_DEVCON1);
3798c2ecf20Sopenharmony_ci#endif
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci    host->SCpnt = NULL;
3828c2ecf20Sopenharmony_ci    host->scsi.phase = PHASE_IDLE;
3838c2ecf20Sopenharmony_ci    host->scsi.disconnectable = 0;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci    memset(host->busyluns, 0, sizeof(host->busyluns));
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci    for (i = 0; i < 8; i++) {
3888c2ecf20Sopenharmony_ci	host->device[i].sync_state = SYNC_NEGOCIATE;
3898c2ecf20Sopenharmony_ci	host->device[i].disconnect_ok = 1;
3908c2ecf20Sopenharmony_ci    }
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci    /* wait 25 cs.  SCSI standard says 250ms. */
3938c2ecf20Sopenharmony_ci    acornscsi_csdelay(25);
3948c2ecf20Sopenharmony_ci}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci/*=============================================================================================
3978c2ecf20Sopenharmony_ci * Utility routines (eg. debug)
3988c2ecf20Sopenharmony_ci */
3998c2ecf20Sopenharmony_ci#ifdef CONFIG_ACORNSCSI_CONSTANTS
4008c2ecf20Sopenharmony_cistatic char *acornscsi_interrupttype[] = {
4018c2ecf20Sopenharmony_ci  "rst",  "suc",  "p/a",  "3",
4028c2ecf20Sopenharmony_ci  "term", "5",	  "6",	  "7",
4038c2ecf20Sopenharmony_ci  "serv", "9",	  "a",	  "b",
4048c2ecf20Sopenharmony_ci  "c",	  "d",	  "e",	  "f"
4058c2ecf20Sopenharmony_ci};
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cistatic signed char acornscsi_map[] = {
4088c2ecf20Sopenharmony_ci  0,  1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,
4098c2ecf20Sopenharmony_ci -1,  2, -1, -1,  -1, -1,  3, -1,   4,	5,  6,	7,   8,  9, 10, 11,
4108c2ecf20Sopenharmony_ci 12, 13, 14, -1,  -1, -1, -1, -1,   4,	5,  6,	7,   8,  9, 10, 11,
4118c2ecf20Sopenharmony_ci -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,
4128c2ecf20Sopenharmony_ci 15, 16, 17, 18,  19, -1, -1, 20,   4,	5,  6,	7,   8,  9, 10, 11,
4138c2ecf20Sopenharmony_ci -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,
4148c2ecf20Sopenharmony_ci -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,
4158c2ecf20Sopenharmony_ci -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,
4168c2ecf20Sopenharmony_ci 21, 22, -1, -1,  -1, 23, -1, -1,   4,	5,  6,	7,   8,  9, 10, 11,
4178c2ecf20Sopenharmony_ci -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,
4188c2ecf20Sopenharmony_ci -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,
4198c2ecf20Sopenharmony_ci -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,
4208c2ecf20Sopenharmony_ci -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,
4218c2ecf20Sopenharmony_ci -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,
4228c2ecf20Sopenharmony_ci -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,
4238c2ecf20Sopenharmony_ci -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1
4248c2ecf20Sopenharmony_ci};
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_cistatic char *acornscsi_interruptcode[] = {
4278c2ecf20Sopenharmony_ci    /* 0 */
4288c2ecf20Sopenharmony_ci    "reset - normal mode",	/* 00 */
4298c2ecf20Sopenharmony_ci    "reset - advanced mode",	/* 01 */
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci    /* 2 */
4328c2ecf20Sopenharmony_ci    "sel",			/* 11 */
4338c2ecf20Sopenharmony_ci    "sel+xfer", 		/* 16 */
4348c2ecf20Sopenharmony_ci    "data-out", 		/* 18 */
4358c2ecf20Sopenharmony_ci    "data-in",			/* 19 */
4368c2ecf20Sopenharmony_ci    "cmd",			/* 1A */
4378c2ecf20Sopenharmony_ci    "stat",			/* 1B */
4388c2ecf20Sopenharmony_ci    "??-out",			/* 1C */
4398c2ecf20Sopenharmony_ci    "??-in",			/* 1D */
4408c2ecf20Sopenharmony_ci    "msg-out",			/* 1E */
4418c2ecf20Sopenharmony_ci    "msg-in",			/* 1F */
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci    /* 12 */
4448c2ecf20Sopenharmony_ci    "/ACK asserted",		/* 20 */
4458c2ecf20Sopenharmony_ci    "save-data-ptr",		/* 21 */
4468c2ecf20Sopenharmony_ci    "{re}sel",			/* 22 */
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci    /* 15 */
4498c2ecf20Sopenharmony_ci    "inv cmd",			/* 40 */
4508c2ecf20Sopenharmony_ci    "unexpected disconnect",	/* 41 */
4518c2ecf20Sopenharmony_ci    "sel timeout",		/* 42 */
4528c2ecf20Sopenharmony_ci    "P err",			/* 43 */
4538c2ecf20Sopenharmony_ci    "P err+ATN",		/* 44 */
4548c2ecf20Sopenharmony_ci    "bad status byte",		/* 47 */
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci    /* 21 */
4578c2ecf20Sopenharmony_ci    "resel, no id",		/* 80 */
4588c2ecf20Sopenharmony_ci    "resel",			/* 81 */
4598c2ecf20Sopenharmony_ci    "discon",			/* 85 */
4608c2ecf20Sopenharmony_ci};
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_cistatic
4638c2ecf20Sopenharmony_civoid print_scsi_status(unsigned int ssr)
4648c2ecf20Sopenharmony_ci{
4658c2ecf20Sopenharmony_ci    if (acornscsi_map[ssr] != -1)
4668c2ecf20Sopenharmony_ci	printk("%s:%s",
4678c2ecf20Sopenharmony_ci		acornscsi_interrupttype[(ssr >> 4)],
4688c2ecf20Sopenharmony_ci		acornscsi_interruptcode[acornscsi_map[ssr]]);
4698c2ecf20Sopenharmony_ci    else
4708c2ecf20Sopenharmony_ci	printk("%X:%X", ssr >> 4, ssr & 0x0f);
4718c2ecf20Sopenharmony_ci}
4728c2ecf20Sopenharmony_ci#endif
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_cistatic
4758c2ecf20Sopenharmony_civoid print_sbic_status(int asr, int ssr, int cmdphase)
4768c2ecf20Sopenharmony_ci{
4778c2ecf20Sopenharmony_ci#ifdef CONFIG_ACORNSCSI_CONSTANTS
4788c2ecf20Sopenharmony_ci    printk("sbic: %c%c%c%c%c%c ",
4798c2ecf20Sopenharmony_ci	    asr & ASR_INT ? 'I' : 'i',
4808c2ecf20Sopenharmony_ci	    asr & ASR_LCI ? 'L' : 'l',
4818c2ecf20Sopenharmony_ci	    asr & ASR_BSY ? 'B' : 'b',
4828c2ecf20Sopenharmony_ci	    asr & ASR_CIP ? 'C' : 'c',
4838c2ecf20Sopenharmony_ci	    asr & ASR_PE  ? 'P' : 'p',
4848c2ecf20Sopenharmony_ci	    asr & ASR_DBR ? 'D' : 'd');
4858c2ecf20Sopenharmony_ci    printk("scsi: ");
4868c2ecf20Sopenharmony_ci    print_scsi_status(ssr);
4878c2ecf20Sopenharmony_ci    printk(" ph %02X\n", cmdphase);
4888c2ecf20Sopenharmony_ci#else
4898c2ecf20Sopenharmony_ci    printk("sbic: %02X scsi: %X:%X ph: %02X\n",
4908c2ecf20Sopenharmony_ci	    asr, (ssr & 0xf0)>>4, ssr & 0x0f, cmdphase);
4918c2ecf20Sopenharmony_ci#endif
4928c2ecf20Sopenharmony_ci}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_cistatic void
4958c2ecf20Sopenharmony_ciacornscsi_dumplogline(AS_Host *host, int target, int line)
4968c2ecf20Sopenharmony_ci{
4978c2ecf20Sopenharmony_ci	unsigned long prev;
4988c2ecf20Sopenharmony_ci	signed int ptr;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	ptr = host->status_ptr[target] - STATUS_BUFFER_TO_PRINT;
5018c2ecf20Sopenharmony_ci	if (ptr < 0)
5028c2ecf20Sopenharmony_ci		ptr += STATUS_BUFFER_SIZE;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	printk("%c: %3s:", target == 8 ? 'H' : '0' + target,
5058c2ecf20Sopenharmony_ci		line == 0 ? "ph" : line == 1 ? "ssr" : "int");
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	prev = host->status[target][ptr].when;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	for (; ptr != host->status_ptr[target]; ptr = (ptr + 1) & (STATUS_BUFFER_SIZE - 1)) {
5108c2ecf20Sopenharmony_ci		unsigned long time_diff;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci		if (!host->status[target][ptr].when)
5138c2ecf20Sopenharmony_ci			continue;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci		switch (line) {
5168c2ecf20Sopenharmony_ci		case 0:
5178c2ecf20Sopenharmony_ci			printk("%c%02X", host->status[target][ptr].irq ? '-' : ' ',
5188c2ecf20Sopenharmony_ci					 host->status[target][ptr].ph);
5198c2ecf20Sopenharmony_ci			break;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci		case 1:
5228c2ecf20Sopenharmony_ci			printk(" %02X", host->status[target][ptr].ssr);
5238c2ecf20Sopenharmony_ci			break;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci		case 2:
5268c2ecf20Sopenharmony_ci			time_diff = host->status[target][ptr].when - prev;
5278c2ecf20Sopenharmony_ci			prev = host->status[target][ptr].when;
5288c2ecf20Sopenharmony_ci			if (time_diff == 0)
5298c2ecf20Sopenharmony_ci				printk("==^");
5308c2ecf20Sopenharmony_ci			else if (time_diff >= 100)
5318c2ecf20Sopenharmony_ci				printk("   ");
5328c2ecf20Sopenharmony_ci			else
5338c2ecf20Sopenharmony_ci				printk(" %02ld", time_diff);
5348c2ecf20Sopenharmony_ci			break;
5358c2ecf20Sopenharmony_ci		}
5368c2ecf20Sopenharmony_ci	}
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	printk("\n");
5398c2ecf20Sopenharmony_ci}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_cistatic
5428c2ecf20Sopenharmony_civoid acornscsi_dumplog(AS_Host *host, int target)
5438c2ecf20Sopenharmony_ci{
5448c2ecf20Sopenharmony_ci    do {
5458c2ecf20Sopenharmony_ci	acornscsi_dumplogline(host, target, 0);
5468c2ecf20Sopenharmony_ci	acornscsi_dumplogline(host, target, 1);
5478c2ecf20Sopenharmony_ci	acornscsi_dumplogline(host, target, 2);
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	if (target == 8)
5508c2ecf20Sopenharmony_ci	    break;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	target = 8;
5538c2ecf20Sopenharmony_ci    } while (1);
5548c2ecf20Sopenharmony_ci}
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_cistatic
5578c2ecf20Sopenharmony_cichar acornscsi_target(AS_Host *host)
5588c2ecf20Sopenharmony_ci{
5598c2ecf20Sopenharmony_ci	if (host->SCpnt)
5608c2ecf20Sopenharmony_ci		return '0' + host->SCpnt->device->id;
5618c2ecf20Sopenharmony_ci	return 'H';
5628c2ecf20Sopenharmony_ci}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci/*
5658c2ecf20Sopenharmony_ci * Prototype: cmdtype_t acornscsi_cmdtype(int command)
5668c2ecf20Sopenharmony_ci * Purpose  : differentiate READ from WRITE from other commands
5678c2ecf20Sopenharmony_ci * Params   : command - command to interpret
5688c2ecf20Sopenharmony_ci * Returns  : CMD_READ	- command reads data,
5698c2ecf20Sopenharmony_ci *	      CMD_WRITE - command writes data,
5708c2ecf20Sopenharmony_ci *	      CMD_MISC	- everything else
5718c2ecf20Sopenharmony_ci */
5728c2ecf20Sopenharmony_cistatic inline
5738c2ecf20Sopenharmony_cicmdtype_t acornscsi_cmdtype(int command)
5748c2ecf20Sopenharmony_ci{
5758c2ecf20Sopenharmony_ci    switch (command) {
5768c2ecf20Sopenharmony_ci    case WRITE_6:  case WRITE_10:  case WRITE_12:
5778c2ecf20Sopenharmony_ci	return CMD_WRITE;
5788c2ecf20Sopenharmony_ci    case READ_6:   case READ_10:   case READ_12:
5798c2ecf20Sopenharmony_ci	return CMD_READ;
5808c2ecf20Sopenharmony_ci    default:
5818c2ecf20Sopenharmony_ci	return CMD_MISC;
5828c2ecf20Sopenharmony_ci    }
5838c2ecf20Sopenharmony_ci}
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci/*
5868c2ecf20Sopenharmony_ci * Prototype: int acornscsi_datadirection(int command)
5878c2ecf20Sopenharmony_ci * Purpose  : differentiate between commands that have a DATA IN phase
5888c2ecf20Sopenharmony_ci *	      and a DATA OUT phase
5898c2ecf20Sopenharmony_ci * Params   : command - command to interpret
5908c2ecf20Sopenharmony_ci * Returns  : DATADIR_OUT - data out phase expected
5918c2ecf20Sopenharmony_ci *	      DATADIR_IN  - data in phase expected
5928c2ecf20Sopenharmony_ci */
5938c2ecf20Sopenharmony_cistatic
5948c2ecf20Sopenharmony_cidatadir_t acornscsi_datadirection(int command)
5958c2ecf20Sopenharmony_ci{
5968c2ecf20Sopenharmony_ci    switch (command) {
5978c2ecf20Sopenharmony_ci    case CHANGE_DEFINITION:	case COMPARE:		case COPY:
5988c2ecf20Sopenharmony_ci    case COPY_VERIFY:		case LOG_SELECT:	case MODE_SELECT:
5998c2ecf20Sopenharmony_ci    case MODE_SELECT_10:	case SEND_DIAGNOSTIC:	case WRITE_BUFFER:
6008c2ecf20Sopenharmony_ci    case FORMAT_UNIT:		case REASSIGN_BLOCKS:	case RESERVE:
6018c2ecf20Sopenharmony_ci    case SEARCH_EQUAL:		case SEARCH_HIGH:	case SEARCH_LOW:
6028c2ecf20Sopenharmony_ci    case WRITE_6:		case WRITE_10:		case WRITE_VERIFY:
6038c2ecf20Sopenharmony_ci    case UPDATE_BLOCK:		case WRITE_LONG:	case WRITE_SAME:
6048c2ecf20Sopenharmony_ci    case SEARCH_HIGH_12:	case SEARCH_EQUAL_12:	case SEARCH_LOW_12:
6058c2ecf20Sopenharmony_ci    case WRITE_12:		case WRITE_VERIFY_12:	case SET_WINDOW:
6068c2ecf20Sopenharmony_ci    case MEDIUM_SCAN:		case SEND_VOLUME_TAG:	case 0xea:
6078c2ecf20Sopenharmony_ci	return DATADIR_OUT;
6088c2ecf20Sopenharmony_ci    default:
6098c2ecf20Sopenharmony_ci	return DATADIR_IN;
6108c2ecf20Sopenharmony_ci    }
6118c2ecf20Sopenharmony_ci}
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci/*
6148c2ecf20Sopenharmony_ci * Purpose  : provide values for synchronous transfers with 33C93.
6158c2ecf20Sopenharmony_ci * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting
6168c2ecf20Sopenharmony_ci *	Modified by Russell King for 8MHz WD33C93A
6178c2ecf20Sopenharmony_ci */
6188c2ecf20Sopenharmony_cistatic struct sync_xfer_tbl {
6198c2ecf20Sopenharmony_ci    unsigned int period_ns;
6208c2ecf20Sopenharmony_ci    unsigned char reg_value;
6218c2ecf20Sopenharmony_ci} sync_xfer_table[] = {
6228c2ecf20Sopenharmony_ci    {	1, 0x20 },    { 249, 0x20 },	{ 374, 0x30 },
6238c2ecf20Sopenharmony_ci    { 499, 0x40 },    { 624, 0x50 },	{ 749, 0x60 },
6248c2ecf20Sopenharmony_ci    { 874, 0x70 },    { 999, 0x00 },	{   0,	  0 }
6258c2ecf20Sopenharmony_ci};
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci/*
6288c2ecf20Sopenharmony_ci * Prototype: int acornscsi_getperiod(unsigned char syncxfer)
6298c2ecf20Sopenharmony_ci * Purpose  : period for the synchronous transfer setting
6308c2ecf20Sopenharmony_ci * Params   : syncxfer SYNCXFER register value
6318c2ecf20Sopenharmony_ci * Returns  : period in ns.
6328c2ecf20Sopenharmony_ci */
6338c2ecf20Sopenharmony_cistatic
6348c2ecf20Sopenharmony_ciint acornscsi_getperiod(unsigned char syncxfer)
6358c2ecf20Sopenharmony_ci{
6368c2ecf20Sopenharmony_ci    int i;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci    syncxfer &= 0xf0;
6398c2ecf20Sopenharmony_ci    if (syncxfer == 0x10)
6408c2ecf20Sopenharmony_ci	syncxfer = 0;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci    for (i = 1; sync_xfer_table[i].period_ns; i++)
6438c2ecf20Sopenharmony_ci	if (syncxfer == sync_xfer_table[i].reg_value)
6448c2ecf20Sopenharmony_ci	    return sync_xfer_table[i].period_ns;
6458c2ecf20Sopenharmony_ci    return 0;
6468c2ecf20Sopenharmony_ci}
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci/*
6498c2ecf20Sopenharmony_ci * Prototype: int round_period(unsigned int period)
6508c2ecf20Sopenharmony_ci * Purpose  : return index into above table for a required REQ period
6518c2ecf20Sopenharmony_ci * Params   : period - time (ns) for REQ
6528c2ecf20Sopenharmony_ci * Returns  : table index
6538c2ecf20Sopenharmony_ci * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting
6548c2ecf20Sopenharmony_ci */
6558c2ecf20Sopenharmony_cistatic inline
6568c2ecf20Sopenharmony_ciint round_period(unsigned int period)
6578c2ecf20Sopenharmony_ci{
6588c2ecf20Sopenharmony_ci    int i;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci    for (i = 1; sync_xfer_table[i].period_ns; i++) {
6618c2ecf20Sopenharmony_ci	if ((period <= sync_xfer_table[i].period_ns) &&
6628c2ecf20Sopenharmony_ci	    (period > sync_xfer_table[i - 1].period_ns))
6638c2ecf20Sopenharmony_ci	    return i;
6648c2ecf20Sopenharmony_ci    }
6658c2ecf20Sopenharmony_ci    return 7;
6668c2ecf20Sopenharmony_ci}
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci/*
6698c2ecf20Sopenharmony_ci * Prototype: unsigned char calc_sync_xfer(unsigned int period, unsigned int offset)
6708c2ecf20Sopenharmony_ci * Purpose  : calculate value for 33c93s SYNC register
6718c2ecf20Sopenharmony_ci * Params   : period - time (ns) for REQ
6728c2ecf20Sopenharmony_ci *	      offset - offset in bytes between REQ/ACK
6738c2ecf20Sopenharmony_ci * Returns  : value for SYNC register
6748c2ecf20Sopenharmony_ci * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting
6758c2ecf20Sopenharmony_ci */
6768c2ecf20Sopenharmony_cistatic
6778c2ecf20Sopenharmony_ciunsigned char __maybe_unused calc_sync_xfer(unsigned int period,
6788c2ecf20Sopenharmony_ci					    unsigned int offset)
6798c2ecf20Sopenharmony_ci{
6808c2ecf20Sopenharmony_ci    return sync_xfer_table[round_period(period)].reg_value |
6818c2ecf20Sopenharmony_ci		((offset < SDTR_SIZE) ? offset : SDTR_SIZE);
6828c2ecf20Sopenharmony_ci}
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci/* ====================================================================================
6858c2ecf20Sopenharmony_ci * Command functions
6868c2ecf20Sopenharmony_ci */
6878c2ecf20Sopenharmony_ci/*
6888c2ecf20Sopenharmony_ci * Function: acornscsi_kick(AS_Host *host)
6898c2ecf20Sopenharmony_ci * Purpose : kick next command to interface
6908c2ecf20Sopenharmony_ci * Params  : host - host to send command to
6918c2ecf20Sopenharmony_ci * Returns : INTR_IDLE if idle, otherwise INTR_PROCESSING
6928c2ecf20Sopenharmony_ci * Notes   : interrupts are always disabled!
6938c2ecf20Sopenharmony_ci */
6948c2ecf20Sopenharmony_cistatic
6958c2ecf20Sopenharmony_ciintr_ret_t acornscsi_kick(AS_Host *host)
6968c2ecf20Sopenharmony_ci{
6978c2ecf20Sopenharmony_ci    int from_queue = 0;
6988c2ecf20Sopenharmony_ci    struct scsi_cmnd *SCpnt;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci    /* first check to see if a command is waiting to be executed */
7018c2ecf20Sopenharmony_ci    SCpnt = host->origSCpnt;
7028c2ecf20Sopenharmony_ci    host->origSCpnt = NULL;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci    /* retrieve next command */
7058c2ecf20Sopenharmony_ci    if (!SCpnt) {
7068c2ecf20Sopenharmony_ci	SCpnt = queue_remove_exclude(&host->queues.issue, host->busyluns);
7078c2ecf20Sopenharmony_ci	if (!SCpnt)
7088c2ecf20Sopenharmony_ci	    return INTR_IDLE;
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	from_queue = 1;
7118c2ecf20Sopenharmony_ci    }
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci    if (host->scsi.disconnectable && host->SCpnt) {
7148c2ecf20Sopenharmony_ci	queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt);
7158c2ecf20Sopenharmony_ci	host->scsi.disconnectable = 0;
7168c2ecf20Sopenharmony_ci#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
7178c2ecf20Sopenharmony_ci	DBG(host->SCpnt, printk("scsi%d.%c: moved command to disconnected queue\n",
7188c2ecf20Sopenharmony_ci		host->host->host_no, acornscsi_target(host)));
7198c2ecf20Sopenharmony_ci#endif
7208c2ecf20Sopenharmony_ci	host->SCpnt = NULL;
7218c2ecf20Sopenharmony_ci    }
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci    /*
7248c2ecf20Sopenharmony_ci     * If we have an interrupt pending, then we may have been reselected.
7258c2ecf20Sopenharmony_ci     * In this case, we don't want to write to the registers
7268c2ecf20Sopenharmony_ci     */
7278c2ecf20Sopenharmony_ci    if (!(sbic_arm_read(host, SBIC_ASR) & (ASR_INT|ASR_BSY|ASR_CIP))) {
7288c2ecf20Sopenharmony_ci	sbic_arm_write(host, SBIC_DESTID, SCpnt->device->id);
7298c2ecf20Sopenharmony_ci	sbic_arm_write(host, SBIC_CMND, CMND_SELWITHATN);
7308c2ecf20Sopenharmony_ci    }
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci    /*
7338c2ecf20Sopenharmony_ci     * claim host busy - all of these must happen atomically wrt
7348c2ecf20Sopenharmony_ci     * our interrupt routine.  Failure means command loss.
7358c2ecf20Sopenharmony_ci     */
7368c2ecf20Sopenharmony_ci    host->scsi.phase = PHASE_CONNECTING;
7378c2ecf20Sopenharmony_ci    host->SCpnt = SCpnt;
7388c2ecf20Sopenharmony_ci    host->scsi.SCp = SCpnt->SCp;
7398c2ecf20Sopenharmony_ci    host->dma.xfer_setup = 0;
7408c2ecf20Sopenharmony_ci    host->dma.xfer_required = 0;
7418c2ecf20Sopenharmony_ci    host->dma.xfer_done = 0;
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci#if (DEBUG & (DEBUG_ABORT|DEBUG_CONNECT))
7448c2ecf20Sopenharmony_ci    DBG(SCpnt,printk("scsi%d.%c: starting cmd %02X\n",
7458c2ecf20Sopenharmony_ci	    host->host->host_no, '0' + SCpnt->device->id,
7468c2ecf20Sopenharmony_ci	    SCpnt->cmnd[0]));
7478c2ecf20Sopenharmony_ci#endif
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci    if (from_queue) {
7508c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
7518c2ecf20Sopenharmony_ci	/*
7528c2ecf20Sopenharmony_ci	 * tagged queueing - allocate a new tag to this command
7538c2ecf20Sopenharmony_ci	 */
7548c2ecf20Sopenharmony_ci	if (SCpnt->device->simple_tags) {
7558c2ecf20Sopenharmony_ci	    SCpnt->device->current_tag += 1;
7568c2ecf20Sopenharmony_ci	    if (SCpnt->device->current_tag == 0)
7578c2ecf20Sopenharmony_ci		SCpnt->device->current_tag = 1;
7588c2ecf20Sopenharmony_ci	    SCpnt->tag = SCpnt->device->current_tag;
7598c2ecf20Sopenharmony_ci	} else
7608c2ecf20Sopenharmony_ci#endif
7618c2ecf20Sopenharmony_ci	    set_bit(SCpnt->device->id * 8 +
7628c2ecf20Sopenharmony_ci		    (u8)(SCpnt->device->lun & 0x07), host->busyluns);
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	host->stats.removes += 1;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	switch (acornscsi_cmdtype(SCpnt->cmnd[0])) {
7678c2ecf20Sopenharmony_ci	case CMD_WRITE:
7688c2ecf20Sopenharmony_ci	    host->stats.writes += 1;
7698c2ecf20Sopenharmony_ci	    break;
7708c2ecf20Sopenharmony_ci	case CMD_READ:
7718c2ecf20Sopenharmony_ci	    host->stats.reads += 1;
7728c2ecf20Sopenharmony_ci	    break;
7738c2ecf20Sopenharmony_ci	case CMD_MISC:
7748c2ecf20Sopenharmony_ci	    host->stats.miscs += 1;
7758c2ecf20Sopenharmony_ci	    break;
7768c2ecf20Sopenharmony_ci	}
7778c2ecf20Sopenharmony_ci    }
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci    return INTR_PROCESSING;
7808c2ecf20Sopenharmony_ci}
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci/*
7838c2ecf20Sopenharmony_ci * Function: void acornscsi_done(AS_Host *host, struct scsi_cmnd **SCpntp, unsigned int result)
7848c2ecf20Sopenharmony_ci * Purpose : complete processing for command
7858c2ecf20Sopenharmony_ci * Params  : host   - interface that completed
7868c2ecf20Sopenharmony_ci *	     result - driver byte of result
7878c2ecf20Sopenharmony_ci */
7888c2ecf20Sopenharmony_cistatic void acornscsi_done(AS_Host *host, struct scsi_cmnd **SCpntp,
7898c2ecf20Sopenharmony_ci			   unsigned int result)
7908c2ecf20Sopenharmony_ci{
7918c2ecf20Sopenharmony_ci	struct scsi_cmnd *SCpnt = *SCpntp;
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci    /* clean up */
7948c2ecf20Sopenharmony_ci    sbic_arm_write(host, SBIC_SOURCEID, SOURCEID_ER | SOURCEID_DSP);
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci    host->stats.fins += 1;
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci    if (SCpnt) {
7998c2ecf20Sopenharmony_ci	*SCpntp = NULL;
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	acornscsi_dma_cleanup(host);
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	SCpnt->result = result << 16 | host->scsi.SCp.Message << 8 | host->scsi.SCp.Status;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	/*
8068c2ecf20Sopenharmony_ci	 * In theory, this should not happen.  In practice, it seems to.
8078c2ecf20Sopenharmony_ci	 * Only trigger an error if the device attempts to report all happy
8088c2ecf20Sopenharmony_ci	 * but with untransferred buffers...  If we don't do something, then
8098c2ecf20Sopenharmony_ci	 * data loss will occur.  Should we check SCpnt->underflow here?
8108c2ecf20Sopenharmony_ci	 * It doesn't appear to be set to something meaningful by the higher
8118c2ecf20Sopenharmony_ci	 * levels all the time.
8128c2ecf20Sopenharmony_ci	 */
8138c2ecf20Sopenharmony_ci	if (result == DID_OK) {
8148c2ecf20Sopenharmony_ci		int xfer_warn = 0;
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci		if (SCpnt->underflow == 0) {
8178c2ecf20Sopenharmony_ci			if (host->scsi.SCp.ptr &&
8188c2ecf20Sopenharmony_ci			    acornscsi_cmdtype(SCpnt->cmnd[0]) != CMD_MISC)
8198c2ecf20Sopenharmony_ci				xfer_warn = 1;
8208c2ecf20Sopenharmony_ci		} else {
8218c2ecf20Sopenharmony_ci			if (host->scsi.SCp.scsi_xferred < SCpnt->underflow ||
8228c2ecf20Sopenharmony_ci			    host->scsi.SCp.scsi_xferred != host->dma.transferred)
8238c2ecf20Sopenharmony_ci				xfer_warn = 1;
8248c2ecf20Sopenharmony_ci		}
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci		/* ANSI standard says: (SCSI-2 Rev 10c Sect 5.6.6)
8278c2ecf20Sopenharmony_ci		 *  Targets which break data transfers into multiple
8288c2ecf20Sopenharmony_ci		 *  connections shall end each successful connection
8298c2ecf20Sopenharmony_ci		 *  (except possibly the last) with a SAVE DATA
8308c2ecf20Sopenharmony_ci		 *  POINTER - DISCONNECT message sequence.
8318c2ecf20Sopenharmony_ci		 *
8328c2ecf20Sopenharmony_ci		 * This makes it difficult to ensure that a transfer has
8338c2ecf20Sopenharmony_ci		 * completed.  If we reach the end of a transfer during
8348c2ecf20Sopenharmony_ci		 * the command, then we can only have finished the transfer.
8358c2ecf20Sopenharmony_ci		 * therefore, if we seem to have some data remaining, this
8368c2ecf20Sopenharmony_ci		 * is not a problem.
8378c2ecf20Sopenharmony_ci		 */
8388c2ecf20Sopenharmony_ci		if (host->dma.xfer_done)
8398c2ecf20Sopenharmony_ci			xfer_warn = 0;
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci		if (xfer_warn) {
8428c2ecf20Sopenharmony_ci		    switch (status_byte(SCpnt->result)) {
8438c2ecf20Sopenharmony_ci		    case CHECK_CONDITION:
8448c2ecf20Sopenharmony_ci		    case COMMAND_TERMINATED:
8458c2ecf20Sopenharmony_ci		    case BUSY:
8468c2ecf20Sopenharmony_ci		    case QUEUE_FULL:
8478c2ecf20Sopenharmony_ci		    case RESERVATION_CONFLICT:
8488c2ecf20Sopenharmony_ci			break;
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci		    default:
8518c2ecf20Sopenharmony_ci			scmd_printk(KERN_ERR, SCpnt,
8528c2ecf20Sopenharmony_ci				    "incomplete data transfer detected: "
8538c2ecf20Sopenharmony_ci				    "result=%08X", SCpnt->result);
8548c2ecf20Sopenharmony_ci			scsi_print_command(SCpnt);
8558c2ecf20Sopenharmony_ci			acornscsi_dumpdma(host, "done");
8568c2ecf20Sopenharmony_ci			acornscsi_dumplog(host, SCpnt->device->id);
8578c2ecf20Sopenharmony_ci			set_host_byte(SCpnt, DID_ERROR);
8588c2ecf20Sopenharmony_ci		    }
8598c2ecf20Sopenharmony_ci		}
8608c2ecf20Sopenharmony_ci	}
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	if (!SCpnt->scsi_done)
8638c2ecf20Sopenharmony_ci	    panic("scsi%d.H: null scsi_done function in acornscsi_done", host->host->host_no);
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	clear_bit(SCpnt->device->id * 8 +
8668c2ecf20Sopenharmony_ci		  (u8)(SCpnt->device->lun & 0x7), host->busyluns);
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	SCpnt->scsi_done(SCpnt);
8698c2ecf20Sopenharmony_ci    } else
8708c2ecf20Sopenharmony_ci	printk("scsi%d: null command in acornscsi_done", host->host->host_no);
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci    host->scsi.phase = PHASE_IDLE;
8738c2ecf20Sopenharmony_ci}
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci/* ====================================================================================
8768c2ecf20Sopenharmony_ci * DMA routines
8778c2ecf20Sopenharmony_ci */
8788c2ecf20Sopenharmony_ci/*
8798c2ecf20Sopenharmony_ci * Purpose  : update SCSI Data Pointer
8808c2ecf20Sopenharmony_ci * Notes    : this will only be one SG entry or less
8818c2ecf20Sopenharmony_ci */
8828c2ecf20Sopenharmony_cistatic
8838c2ecf20Sopenharmony_civoid acornscsi_data_updateptr(AS_Host *host, struct scsi_pointer *SCp, unsigned int length)
8848c2ecf20Sopenharmony_ci{
8858c2ecf20Sopenharmony_ci    SCp->ptr += length;
8868c2ecf20Sopenharmony_ci    SCp->this_residual -= length;
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci    if (SCp->this_residual == 0 && next_SCp(SCp) == 0)
8898c2ecf20Sopenharmony_ci	host->dma.xfer_done = 1;
8908c2ecf20Sopenharmony_ci}
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci/*
8938c2ecf20Sopenharmony_ci * Prototype: void acornscsi_data_read(AS_Host *host, char *ptr,
8948c2ecf20Sopenharmony_ci *				unsigned int start_addr, unsigned int length)
8958c2ecf20Sopenharmony_ci * Purpose  : read data from DMA RAM
8968c2ecf20Sopenharmony_ci * Params   : host - host to transfer from
8978c2ecf20Sopenharmony_ci *	      ptr  - DRAM address
8988c2ecf20Sopenharmony_ci *	      start_addr - host mem address
8998c2ecf20Sopenharmony_ci *	      length - number of bytes to transfer
9008c2ecf20Sopenharmony_ci * Notes    : this will only be one SG entry or less
9018c2ecf20Sopenharmony_ci */
9028c2ecf20Sopenharmony_cistatic
9038c2ecf20Sopenharmony_civoid acornscsi_data_read(AS_Host *host, char *ptr,
9048c2ecf20Sopenharmony_ci				 unsigned int start_addr, unsigned int length)
9058c2ecf20Sopenharmony_ci{
9068c2ecf20Sopenharmony_ci    extern void __acornscsi_in(void __iomem *, char *buf, int len);
9078c2ecf20Sopenharmony_ci    unsigned int page, offset, len = length;
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci    page = (start_addr >> 12);
9108c2ecf20Sopenharmony_ci    offset = start_addr & ((1 << 12) - 1);
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci    writeb((page & 0x3f) | host->card.page_reg, host->fast + PAGE_REG);
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci    while (len > 0) {
9158c2ecf20Sopenharmony_ci	unsigned int this_len;
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	if (len + offset > (1 << 12))
9188c2ecf20Sopenharmony_ci	    this_len = (1 << 12) - offset;
9198c2ecf20Sopenharmony_ci	else
9208c2ecf20Sopenharmony_ci	    this_len = len;
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	__acornscsi_in(host->base + (offset << 1), ptr, this_len);
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	offset += this_len;
9258c2ecf20Sopenharmony_ci	ptr += this_len;
9268c2ecf20Sopenharmony_ci	len -= this_len;
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	if (offset == (1 << 12)) {
9298c2ecf20Sopenharmony_ci	    offset = 0;
9308c2ecf20Sopenharmony_ci	    page ++;
9318c2ecf20Sopenharmony_ci	    writeb((page & 0x3f) | host->card.page_reg, host->fast + PAGE_REG);
9328c2ecf20Sopenharmony_ci	}
9338c2ecf20Sopenharmony_ci    }
9348c2ecf20Sopenharmony_ci    writeb(host->card.page_reg, host->fast + PAGE_REG);
9358c2ecf20Sopenharmony_ci}
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci/*
9388c2ecf20Sopenharmony_ci * Prototype: void acornscsi_data_write(AS_Host *host, char *ptr,
9398c2ecf20Sopenharmony_ci *				unsigned int start_addr, unsigned int length)
9408c2ecf20Sopenharmony_ci * Purpose  : write data to DMA RAM
9418c2ecf20Sopenharmony_ci * Params   : host - host to transfer from
9428c2ecf20Sopenharmony_ci *	      ptr  - DRAM address
9438c2ecf20Sopenharmony_ci *	      start_addr - host mem address
9448c2ecf20Sopenharmony_ci *	      length - number of bytes to transfer
9458c2ecf20Sopenharmony_ci * Notes    : this will only be one SG entry or less
9468c2ecf20Sopenharmony_ci */
9478c2ecf20Sopenharmony_cistatic
9488c2ecf20Sopenharmony_civoid acornscsi_data_write(AS_Host *host, char *ptr,
9498c2ecf20Sopenharmony_ci				 unsigned int start_addr, unsigned int length)
9508c2ecf20Sopenharmony_ci{
9518c2ecf20Sopenharmony_ci    extern void __acornscsi_out(void __iomem *, char *buf, int len);
9528c2ecf20Sopenharmony_ci    unsigned int page, offset, len = length;
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci    page = (start_addr >> 12);
9558c2ecf20Sopenharmony_ci    offset = start_addr & ((1 << 12) - 1);
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci    writeb((page & 0x3f) | host->card.page_reg, host->fast + PAGE_REG);
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci    while (len > 0) {
9608c2ecf20Sopenharmony_ci	unsigned int this_len;
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	if (len + offset > (1 << 12))
9638c2ecf20Sopenharmony_ci	    this_len = (1 << 12) - offset;
9648c2ecf20Sopenharmony_ci	else
9658c2ecf20Sopenharmony_ci	    this_len = len;
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	__acornscsi_out(host->base + (offset << 1), ptr, this_len);
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	offset += this_len;
9708c2ecf20Sopenharmony_ci	ptr += this_len;
9718c2ecf20Sopenharmony_ci	len -= this_len;
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	if (offset == (1 << 12)) {
9748c2ecf20Sopenharmony_ci	    offset = 0;
9758c2ecf20Sopenharmony_ci	    page ++;
9768c2ecf20Sopenharmony_ci	    writeb((page & 0x3f) | host->card.page_reg, host->fast + PAGE_REG);
9778c2ecf20Sopenharmony_ci	}
9788c2ecf20Sopenharmony_ci    }
9798c2ecf20Sopenharmony_ci    writeb(host->card.page_reg, host->fast + PAGE_REG);
9808c2ecf20Sopenharmony_ci}
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci/* =========================================================================================
9838c2ecf20Sopenharmony_ci * On-board DMA routines
9848c2ecf20Sopenharmony_ci */
9858c2ecf20Sopenharmony_ci#ifdef USE_DMAC
9868c2ecf20Sopenharmony_ci/*
9878c2ecf20Sopenharmony_ci * Prototype: void acornscsi_dmastop(AS_Host *host)
9888c2ecf20Sopenharmony_ci * Purpose  : stop all DMA
9898c2ecf20Sopenharmony_ci * Params   : host - host on which to stop DMA
9908c2ecf20Sopenharmony_ci * Notes    : This is called when leaving DATA IN/OUT phase,
9918c2ecf20Sopenharmony_ci *	      or when interface is RESET
9928c2ecf20Sopenharmony_ci */
9938c2ecf20Sopenharmony_cistatic inline
9948c2ecf20Sopenharmony_civoid acornscsi_dma_stop(AS_Host *host)
9958c2ecf20Sopenharmony_ci{
9968c2ecf20Sopenharmony_ci    dmac_write(host, DMAC_MASKREG, MASK_ON);
9978c2ecf20Sopenharmony_ci    dmac_clearintr(host);
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_DMA)
10008c2ecf20Sopenharmony_ci    DBG(host->SCpnt, acornscsi_dumpdma(host, "stop"));
10018c2ecf20Sopenharmony_ci#endif
10028c2ecf20Sopenharmony_ci}
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci/*
10058c2ecf20Sopenharmony_ci * Function: void acornscsi_dma_setup(AS_Host *host, dmadir_t direction)
10068c2ecf20Sopenharmony_ci * Purpose : setup DMA controller for data transfer
10078c2ecf20Sopenharmony_ci * Params  : host - host to setup
10088c2ecf20Sopenharmony_ci *	     direction - data transfer direction
10098c2ecf20Sopenharmony_ci * Notes   : This is called when entering DATA I/O phase, not
10108c2ecf20Sopenharmony_ci *	     while we're in a DATA I/O phase
10118c2ecf20Sopenharmony_ci */
10128c2ecf20Sopenharmony_cistatic
10138c2ecf20Sopenharmony_civoid acornscsi_dma_setup(AS_Host *host, dmadir_t direction)
10148c2ecf20Sopenharmony_ci{
10158c2ecf20Sopenharmony_ci    unsigned int address, length, mode;
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci    host->dma.direction = direction;
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci    dmac_write(host, DMAC_MASKREG, MASK_ON);
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci    if (direction == DMA_OUT) {
10228c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_NO_WRITE)
10238c2ecf20Sopenharmony_ci	if (NO_WRITE & (1 << host->SCpnt->device->id)) {
10248c2ecf20Sopenharmony_ci	    printk(KERN_CRIT "scsi%d.%c: I can't handle DMA_OUT!\n",
10258c2ecf20Sopenharmony_ci		    host->host->host_no, acornscsi_target(host));
10268c2ecf20Sopenharmony_ci	    return;
10278c2ecf20Sopenharmony_ci	}
10288c2ecf20Sopenharmony_ci#endif
10298c2ecf20Sopenharmony_ci	mode = DMAC_WRITE;
10308c2ecf20Sopenharmony_ci    } else
10318c2ecf20Sopenharmony_ci	mode = DMAC_READ;
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci    /*
10348c2ecf20Sopenharmony_ci     * Allocate some buffer space, limited to half the buffer size
10358c2ecf20Sopenharmony_ci     */
10368c2ecf20Sopenharmony_ci    length = min_t(unsigned int, host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2);
10378c2ecf20Sopenharmony_ci    if (length) {
10388c2ecf20Sopenharmony_ci	host->dma.start_addr = address = host->dma.free_addr;
10398c2ecf20Sopenharmony_ci	host->dma.free_addr = (host->dma.free_addr + length) &
10408c2ecf20Sopenharmony_ci				(DMAC_BUFFER_SIZE - 1);
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ci	/*
10438c2ecf20Sopenharmony_ci	 * Transfer data to DMA memory
10448c2ecf20Sopenharmony_ci	 */
10458c2ecf20Sopenharmony_ci	if (direction == DMA_OUT)
10468c2ecf20Sopenharmony_ci	    acornscsi_data_write(host, host->scsi.SCp.ptr, host->dma.start_addr,
10478c2ecf20Sopenharmony_ci				length);
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	length -= 1;
10508c2ecf20Sopenharmony_ci	dmac_write(host, DMAC_TXCNTLO, length);
10518c2ecf20Sopenharmony_ci	dmac_write(host, DMAC_TXCNTHI, length >> 8);
10528c2ecf20Sopenharmony_ci	dmac_write(host, DMAC_TXADRLO, address);
10538c2ecf20Sopenharmony_ci	dmac_write(host, DMAC_TXADRMD, address >> 8);
10548c2ecf20Sopenharmony_ci	dmac_write(host, DMAC_TXADRHI, 0);
10558c2ecf20Sopenharmony_ci	dmac_write(host, DMAC_MODECON, mode);
10568c2ecf20Sopenharmony_ci	dmac_write(host, DMAC_MASKREG, MASK_OFF);
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_DMA)
10598c2ecf20Sopenharmony_ci	DBG(host->SCpnt, acornscsi_dumpdma(host, "strt"));
10608c2ecf20Sopenharmony_ci#endif
10618c2ecf20Sopenharmony_ci	host->dma.xfer_setup = 1;
10628c2ecf20Sopenharmony_ci    }
10638c2ecf20Sopenharmony_ci}
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci/*
10668c2ecf20Sopenharmony_ci * Function: void acornscsi_dma_cleanup(AS_Host *host)
10678c2ecf20Sopenharmony_ci * Purpose : ensure that all DMA transfers are up-to-date & host->scsi.SCp is correct
10688c2ecf20Sopenharmony_ci * Params  : host - host to finish
10698c2ecf20Sopenharmony_ci * Notes   : This is called when a command is:
10708c2ecf20Sopenharmony_ci *		terminating, RESTORE_POINTERS, SAVE_POINTERS, DISCONNECT
10718c2ecf20Sopenharmony_ci *	   : This must not return until all transfers are completed.
10728c2ecf20Sopenharmony_ci */
10738c2ecf20Sopenharmony_cistatic
10748c2ecf20Sopenharmony_civoid acornscsi_dma_cleanup(AS_Host *host)
10758c2ecf20Sopenharmony_ci{
10768c2ecf20Sopenharmony_ci    dmac_write(host, DMAC_MASKREG, MASK_ON);
10778c2ecf20Sopenharmony_ci    dmac_clearintr(host);
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci    /*
10808c2ecf20Sopenharmony_ci     * Check for a pending transfer
10818c2ecf20Sopenharmony_ci     */
10828c2ecf20Sopenharmony_ci    if (host->dma.xfer_required) {
10838c2ecf20Sopenharmony_ci	host->dma.xfer_required = 0;
10848c2ecf20Sopenharmony_ci	if (host->dma.direction == DMA_IN)
10858c2ecf20Sopenharmony_ci	    acornscsi_data_read(host, host->dma.xfer_ptr,
10868c2ecf20Sopenharmony_ci				 host->dma.xfer_start, host->dma.xfer_length);
10878c2ecf20Sopenharmony_ci    }
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci    /*
10908c2ecf20Sopenharmony_ci     * Has a transfer been setup?
10918c2ecf20Sopenharmony_ci     */
10928c2ecf20Sopenharmony_ci    if (host->dma.xfer_setup) {
10938c2ecf20Sopenharmony_ci	unsigned int transferred;
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci	host->dma.xfer_setup = 0;
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_DMA)
10988c2ecf20Sopenharmony_ci	DBG(host->SCpnt, acornscsi_dumpdma(host, "cupi"));
10998c2ecf20Sopenharmony_ci#endif
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci	/*
11028c2ecf20Sopenharmony_ci	 * Calculate number of bytes transferred from DMA.
11038c2ecf20Sopenharmony_ci	 */
11048c2ecf20Sopenharmony_ci	transferred = dmac_address(host) - host->dma.start_addr;
11058c2ecf20Sopenharmony_ci	host->dma.transferred += transferred;
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	if (host->dma.direction == DMA_IN)
11088c2ecf20Sopenharmony_ci	    acornscsi_data_read(host, host->scsi.SCp.ptr,
11098c2ecf20Sopenharmony_ci				 host->dma.start_addr, transferred);
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	/*
11128c2ecf20Sopenharmony_ci	 * Update SCSI pointers
11138c2ecf20Sopenharmony_ci	 */
11148c2ecf20Sopenharmony_ci	acornscsi_data_updateptr(host, &host->scsi.SCp, transferred);
11158c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_DMA)
11168c2ecf20Sopenharmony_ci	DBG(host->SCpnt, acornscsi_dumpdma(host, "cupo"));
11178c2ecf20Sopenharmony_ci#endif
11188c2ecf20Sopenharmony_ci    }
11198c2ecf20Sopenharmony_ci}
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci/*
11228c2ecf20Sopenharmony_ci * Function: void acornscsi_dmacintr(AS_Host *host)
11238c2ecf20Sopenharmony_ci * Purpose : handle interrupts from DMAC device
11248c2ecf20Sopenharmony_ci * Params  : host - host to process
11258c2ecf20Sopenharmony_ci * Notes   : If reading, we schedule the read to main memory &
11268c2ecf20Sopenharmony_ci *	     allow the transfer to continue.
11278c2ecf20Sopenharmony_ci *	   : If writing, we fill the onboard DMA memory from main
11288c2ecf20Sopenharmony_ci *	     memory.
11298c2ecf20Sopenharmony_ci *	   : Called whenever DMAC finished it's current transfer.
11308c2ecf20Sopenharmony_ci */
11318c2ecf20Sopenharmony_cistatic
11328c2ecf20Sopenharmony_civoid acornscsi_dma_intr(AS_Host *host)
11338c2ecf20Sopenharmony_ci{
11348c2ecf20Sopenharmony_ci    unsigned int address, length, transferred;
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_DMA)
11378c2ecf20Sopenharmony_ci    DBG(host->SCpnt, acornscsi_dumpdma(host, "inti"));
11388c2ecf20Sopenharmony_ci#endif
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci    dmac_write(host, DMAC_MASKREG, MASK_ON);
11418c2ecf20Sopenharmony_ci    dmac_clearintr(host);
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci    /*
11448c2ecf20Sopenharmony_ci     * Calculate amount transferred via DMA
11458c2ecf20Sopenharmony_ci     */
11468c2ecf20Sopenharmony_ci    transferred = dmac_address(host) - host->dma.start_addr;
11478c2ecf20Sopenharmony_ci    host->dma.transferred += transferred;
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_ci    /*
11508c2ecf20Sopenharmony_ci     * Schedule DMA transfer off board
11518c2ecf20Sopenharmony_ci     */
11528c2ecf20Sopenharmony_ci    if (host->dma.direction == DMA_IN) {
11538c2ecf20Sopenharmony_ci	host->dma.xfer_start = host->dma.start_addr;
11548c2ecf20Sopenharmony_ci	host->dma.xfer_length = transferred;
11558c2ecf20Sopenharmony_ci	host->dma.xfer_ptr = host->scsi.SCp.ptr;
11568c2ecf20Sopenharmony_ci	host->dma.xfer_required = 1;
11578c2ecf20Sopenharmony_ci    }
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci    acornscsi_data_updateptr(host, &host->scsi.SCp, transferred);
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci    /*
11628c2ecf20Sopenharmony_ci     * Allocate some buffer space, limited to half the on-board RAM size
11638c2ecf20Sopenharmony_ci     */
11648c2ecf20Sopenharmony_ci    length = min_t(unsigned int, host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2);
11658c2ecf20Sopenharmony_ci    if (length) {
11668c2ecf20Sopenharmony_ci	host->dma.start_addr = address = host->dma.free_addr;
11678c2ecf20Sopenharmony_ci	host->dma.free_addr = (host->dma.free_addr + length) &
11688c2ecf20Sopenharmony_ci				(DMAC_BUFFER_SIZE - 1);
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	/*
11718c2ecf20Sopenharmony_ci	 * Transfer data to DMA memory
11728c2ecf20Sopenharmony_ci	 */
11738c2ecf20Sopenharmony_ci	if (host->dma.direction == DMA_OUT)
11748c2ecf20Sopenharmony_ci	    acornscsi_data_write(host, host->scsi.SCp.ptr, host->dma.start_addr,
11758c2ecf20Sopenharmony_ci				length);
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci	length -= 1;
11788c2ecf20Sopenharmony_ci	dmac_write(host, DMAC_TXCNTLO, length);
11798c2ecf20Sopenharmony_ci	dmac_write(host, DMAC_TXCNTHI, length >> 8);
11808c2ecf20Sopenharmony_ci	dmac_write(host, DMAC_TXADRLO, address);
11818c2ecf20Sopenharmony_ci	dmac_write(host, DMAC_TXADRMD, address >> 8);
11828c2ecf20Sopenharmony_ci	dmac_write(host, DMAC_TXADRHI, 0);
11838c2ecf20Sopenharmony_ci	dmac_write(host, DMAC_MASKREG, MASK_OFF);
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_DMA)
11868c2ecf20Sopenharmony_ci	DBG(host->SCpnt, acornscsi_dumpdma(host, "into"));
11878c2ecf20Sopenharmony_ci#endif
11888c2ecf20Sopenharmony_ci    } else {
11898c2ecf20Sopenharmony_ci	host->dma.xfer_setup = 0;
11908c2ecf20Sopenharmony_ci#if 0
11918c2ecf20Sopenharmony_ci	/*
11928c2ecf20Sopenharmony_ci	 * If the interface still wants more, then this is an error.
11938c2ecf20Sopenharmony_ci	 * We give it another byte, but we also attempt to raise an
11948c2ecf20Sopenharmony_ci	 * attention condition.  We continue giving one byte until
11958c2ecf20Sopenharmony_ci	 * the device recognises the attention.
11968c2ecf20Sopenharmony_ci	 */
11978c2ecf20Sopenharmony_ci	if (dmac_read(host, DMAC_STATUS) & STATUS_RQ0) {
11988c2ecf20Sopenharmony_ci	    acornscsi_abortcmd(host, host->SCpnt->tag);
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci	    dmac_write(host, DMAC_TXCNTLO, 0);
12018c2ecf20Sopenharmony_ci	    dmac_write(host, DMAC_TXCNTHI, 0);
12028c2ecf20Sopenharmony_ci	    dmac_write(host, DMAC_TXADRLO, 0);
12038c2ecf20Sopenharmony_ci	    dmac_write(host, DMAC_TXADRMD, 0);
12048c2ecf20Sopenharmony_ci	    dmac_write(host, DMAC_TXADRHI, 0);
12058c2ecf20Sopenharmony_ci	    dmac_write(host, DMAC_MASKREG, MASK_OFF);
12068c2ecf20Sopenharmony_ci	}
12078c2ecf20Sopenharmony_ci#endif
12088c2ecf20Sopenharmony_ci    }
12098c2ecf20Sopenharmony_ci}
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci/*
12128c2ecf20Sopenharmony_ci * Function: void acornscsi_dma_xfer(AS_Host *host)
12138c2ecf20Sopenharmony_ci * Purpose : transfer data between AcornSCSI and memory
12148c2ecf20Sopenharmony_ci * Params  : host - host to process
12158c2ecf20Sopenharmony_ci */
12168c2ecf20Sopenharmony_cistatic
12178c2ecf20Sopenharmony_civoid acornscsi_dma_xfer(AS_Host *host)
12188c2ecf20Sopenharmony_ci{
12198c2ecf20Sopenharmony_ci    host->dma.xfer_required = 0;
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_ci    if (host->dma.direction == DMA_IN)
12228c2ecf20Sopenharmony_ci	acornscsi_data_read(host, host->dma.xfer_ptr,
12238c2ecf20Sopenharmony_ci				host->dma.xfer_start, host->dma.xfer_length);
12248c2ecf20Sopenharmony_ci}
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci/*
12278c2ecf20Sopenharmony_ci * Function: void acornscsi_dma_adjust(AS_Host *host)
12288c2ecf20Sopenharmony_ci * Purpose : adjust DMA pointers & count for bytes transferred to
12298c2ecf20Sopenharmony_ci *	     SBIC but not SCSI bus.
12308c2ecf20Sopenharmony_ci * Params  : host - host to adjust DMA count for
12318c2ecf20Sopenharmony_ci */
12328c2ecf20Sopenharmony_cistatic
12338c2ecf20Sopenharmony_civoid acornscsi_dma_adjust(AS_Host *host)
12348c2ecf20Sopenharmony_ci{
12358c2ecf20Sopenharmony_ci    if (host->dma.xfer_setup) {
12368c2ecf20Sopenharmony_ci	signed long transferred;
12378c2ecf20Sopenharmony_ci#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE))
12388c2ecf20Sopenharmony_ci	DBG(host->SCpnt, acornscsi_dumpdma(host, "adji"));
12398c2ecf20Sopenharmony_ci#endif
12408c2ecf20Sopenharmony_ci	/*
12418c2ecf20Sopenharmony_ci	 * Calculate correct DMA address - DMA is ahead of SCSI bus while
12428c2ecf20Sopenharmony_ci	 * writing.
12438c2ecf20Sopenharmony_ci	 *  host->scsi.SCp.scsi_xferred is the number of bytes
12448c2ecf20Sopenharmony_ci	 *  actually transferred to/from the SCSI bus.
12458c2ecf20Sopenharmony_ci	 *  host->dma.transferred is the number of bytes transferred
12468c2ecf20Sopenharmony_ci	 *  over DMA since host->dma.start_addr was last set.
12478c2ecf20Sopenharmony_ci	 *
12488c2ecf20Sopenharmony_ci	 * real_dma_addr = host->dma.start_addr + host->scsi.SCp.scsi_xferred
12498c2ecf20Sopenharmony_ci	 *		   - host->dma.transferred
12508c2ecf20Sopenharmony_ci	 */
12518c2ecf20Sopenharmony_ci	transferred = host->scsi.SCp.scsi_xferred - host->dma.transferred;
12528c2ecf20Sopenharmony_ci	if (transferred < 0)
12538c2ecf20Sopenharmony_ci	    printk("scsi%d.%c: Ack! DMA write correction %ld < 0!\n",
12548c2ecf20Sopenharmony_ci		    host->host->host_no, acornscsi_target(host), transferred);
12558c2ecf20Sopenharmony_ci	else if (transferred == 0)
12568c2ecf20Sopenharmony_ci	    host->dma.xfer_setup = 0;
12578c2ecf20Sopenharmony_ci	else {
12588c2ecf20Sopenharmony_ci	    transferred += host->dma.start_addr;
12598c2ecf20Sopenharmony_ci	    dmac_write(host, DMAC_TXADRLO, transferred);
12608c2ecf20Sopenharmony_ci	    dmac_write(host, DMAC_TXADRMD, transferred >> 8);
12618c2ecf20Sopenharmony_ci	    dmac_write(host, DMAC_TXADRHI, transferred >> 16);
12628c2ecf20Sopenharmony_ci#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE))
12638c2ecf20Sopenharmony_ci	    DBG(host->SCpnt, acornscsi_dumpdma(host, "adjo"));
12648c2ecf20Sopenharmony_ci#endif
12658c2ecf20Sopenharmony_ci	}
12668c2ecf20Sopenharmony_ci    }
12678c2ecf20Sopenharmony_ci}
12688c2ecf20Sopenharmony_ci#endif
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_ci/* =========================================================================================
12718c2ecf20Sopenharmony_ci * Data I/O
12728c2ecf20Sopenharmony_ci */
12738c2ecf20Sopenharmony_cistatic int
12748c2ecf20Sopenharmony_ciacornscsi_write_pio(AS_Host *host, char *bytes, int *ptr, int len, unsigned int max_timeout)
12758c2ecf20Sopenharmony_ci{
12768c2ecf20Sopenharmony_ci	unsigned int asr, timeout = max_timeout;
12778c2ecf20Sopenharmony_ci	int my_ptr = *ptr;
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_ci	while (my_ptr < len) {
12808c2ecf20Sopenharmony_ci		asr = sbic_arm_read(host, SBIC_ASR);
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci		if (asr & ASR_DBR) {
12838c2ecf20Sopenharmony_ci			timeout = max_timeout;
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ci			sbic_arm_write(host, SBIC_DATA, bytes[my_ptr++]);
12868c2ecf20Sopenharmony_ci		} else if (asr & ASR_INT)
12878c2ecf20Sopenharmony_ci			break;
12888c2ecf20Sopenharmony_ci		else if (--timeout == 0)
12898c2ecf20Sopenharmony_ci			break;
12908c2ecf20Sopenharmony_ci		udelay(1);
12918c2ecf20Sopenharmony_ci	}
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci	*ptr = my_ptr;
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci	return (timeout == 0) ? -1 : 0;
12968c2ecf20Sopenharmony_ci}
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ci/*
12998c2ecf20Sopenharmony_ci * Function: void acornscsi_sendcommand(AS_Host *host)
13008c2ecf20Sopenharmony_ci * Purpose : send a command to a target
13018c2ecf20Sopenharmony_ci * Params  : host - host which is connected to target
13028c2ecf20Sopenharmony_ci */
13038c2ecf20Sopenharmony_cistatic void
13048c2ecf20Sopenharmony_ciacornscsi_sendcommand(AS_Host *host)
13058c2ecf20Sopenharmony_ci{
13068c2ecf20Sopenharmony_ci	struct scsi_cmnd *SCpnt = host->SCpnt;
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci    sbic_arm_write(host, SBIC_TRANSCNTH, 0);
13098c2ecf20Sopenharmony_ci    sbic_arm_writenext(host, 0);
13108c2ecf20Sopenharmony_ci    sbic_arm_writenext(host, SCpnt->cmd_len - host->scsi.SCp.sent_command);
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci    acornscsi_sbic_issuecmd(host, CMND_XFERINFO);
13138c2ecf20Sopenharmony_ci
13148c2ecf20Sopenharmony_ci    if (acornscsi_write_pio(host, SCpnt->cmnd,
13158c2ecf20Sopenharmony_ci	(int *)&host->scsi.SCp.sent_command, SCpnt->cmd_len, 1000000))
13168c2ecf20Sopenharmony_ci	printk("scsi%d: timeout while sending command\n", host->host->host_no);
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ci    host->scsi.phase = PHASE_COMMAND;
13198c2ecf20Sopenharmony_ci}
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_cistatic
13228c2ecf20Sopenharmony_civoid acornscsi_sendmessage(AS_Host *host)
13238c2ecf20Sopenharmony_ci{
13248c2ecf20Sopenharmony_ci    unsigned int message_length = msgqueue_msglength(&host->scsi.msgs);
13258c2ecf20Sopenharmony_ci    unsigned int msgnr;
13268c2ecf20Sopenharmony_ci    struct message *msg;
13278c2ecf20Sopenharmony_ci
13288c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_MESSAGES)
13298c2ecf20Sopenharmony_ci    printk("scsi%d.%c: sending message ",
13308c2ecf20Sopenharmony_ci	    host->host->host_no, acornscsi_target(host));
13318c2ecf20Sopenharmony_ci#endif
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_ci    switch (message_length) {
13348c2ecf20Sopenharmony_ci    case 0:
13358c2ecf20Sopenharmony_ci	acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT);
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci	acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "sending message 1");
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ci	sbic_arm_write(host, SBIC_DATA, NOP);
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_ci	host->scsi.last_message = NOP;
13428c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_MESSAGES)
13438c2ecf20Sopenharmony_ci	printk("NOP");
13448c2ecf20Sopenharmony_ci#endif
13458c2ecf20Sopenharmony_ci	break;
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci    case 1:
13488c2ecf20Sopenharmony_ci	acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT);
13498c2ecf20Sopenharmony_ci	msg = msgqueue_getmsg(&host->scsi.msgs, 0);
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_ci	acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "sending message 2");
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_ci	sbic_arm_write(host, SBIC_DATA, msg->msg[0]);
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci	host->scsi.last_message = msg->msg[0];
13568c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_MESSAGES)
13578c2ecf20Sopenharmony_ci	spi_print_msg(msg->msg);
13588c2ecf20Sopenharmony_ci#endif
13598c2ecf20Sopenharmony_ci	break;
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci    default:
13628c2ecf20Sopenharmony_ci	/*
13638c2ecf20Sopenharmony_ci	 * ANSI standard says: (SCSI-2 Rev 10c Sect 5.6.14)
13648c2ecf20Sopenharmony_ci	 * 'When a target sends this (MESSAGE_REJECT) message, it
13658c2ecf20Sopenharmony_ci	 *  shall change to MESSAGE IN phase and send this message
13668c2ecf20Sopenharmony_ci	 *  prior to requesting additional message bytes from the
13678c2ecf20Sopenharmony_ci	 *  initiator.  This provides an interlock so that the
13688c2ecf20Sopenharmony_ci	 *  initiator can determine which message byte is rejected.
13698c2ecf20Sopenharmony_ci	 */
13708c2ecf20Sopenharmony_ci	sbic_arm_write(host, SBIC_TRANSCNTH, 0);
13718c2ecf20Sopenharmony_ci	sbic_arm_writenext(host, 0);
13728c2ecf20Sopenharmony_ci	sbic_arm_writenext(host, message_length);
13738c2ecf20Sopenharmony_ci	acornscsi_sbic_issuecmd(host, CMND_XFERINFO);
13748c2ecf20Sopenharmony_ci
13758c2ecf20Sopenharmony_ci	msgnr = 0;
13768c2ecf20Sopenharmony_ci	while ((msg = msgqueue_getmsg(&host->scsi.msgs, msgnr++)) != NULL) {
13778c2ecf20Sopenharmony_ci	    unsigned int i;
13788c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_MESSAGES)
13798c2ecf20Sopenharmony_ci	    spi_print_msg(msg);
13808c2ecf20Sopenharmony_ci#endif
13818c2ecf20Sopenharmony_ci	    i = 0;
13828c2ecf20Sopenharmony_ci	    if (acornscsi_write_pio(host, msg->msg, &i, msg->length, 1000000))
13838c2ecf20Sopenharmony_ci		printk("scsi%d: timeout while sending message\n", host->host->host_no);
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci	    host->scsi.last_message = msg->msg[0];
13868c2ecf20Sopenharmony_ci	    if (msg->msg[0] == EXTENDED_MESSAGE)
13878c2ecf20Sopenharmony_ci		host->scsi.last_message |= msg->msg[2] << 8;
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_ci	    if (i != msg->length)
13908c2ecf20Sopenharmony_ci		break;
13918c2ecf20Sopenharmony_ci	}
13928c2ecf20Sopenharmony_ci	break;
13938c2ecf20Sopenharmony_ci    }
13948c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_MESSAGES)
13958c2ecf20Sopenharmony_ci    printk("\n");
13968c2ecf20Sopenharmony_ci#endif
13978c2ecf20Sopenharmony_ci}
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci/*
14008c2ecf20Sopenharmony_ci * Function: void acornscsi_readstatusbyte(AS_Host *host)
14018c2ecf20Sopenharmony_ci * Purpose : Read status byte from connected target
14028c2ecf20Sopenharmony_ci * Params  : host - host connected to target
14038c2ecf20Sopenharmony_ci */
14048c2ecf20Sopenharmony_cistatic
14058c2ecf20Sopenharmony_civoid acornscsi_readstatusbyte(AS_Host *host)
14068c2ecf20Sopenharmony_ci{
14078c2ecf20Sopenharmony_ci    acornscsi_sbic_issuecmd(host, CMND_XFERINFO|CMND_SBT);
14088c2ecf20Sopenharmony_ci    acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "reading status byte");
14098c2ecf20Sopenharmony_ci    host->scsi.SCp.Status = sbic_arm_read(host, SBIC_DATA);
14108c2ecf20Sopenharmony_ci}
14118c2ecf20Sopenharmony_ci
14128c2ecf20Sopenharmony_ci/*
14138c2ecf20Sopenharmony_ci * Function: unsigned char acornscsi_readmessagebyte(AS_Host *host)
14148c2ecf20Sopenharmony_ci * Purpose : Read one message byte from connected target
14158c2ecf20Sopenharmony_ci * Params  : host - host connected to target
14168c2ecf20Sopenharmony_ci */
14178c2ecf20Sopenharmony_cistatic
14188c2ecf20Sopenharmony_ciunsigned char acornscsi_readmessagebyte(AS_Host *host)
14198c2ecf20Sopenharmony_ci{
14208c2ecf20Sopenharmony_ci    unsigned char message;
14218c2ecf20Sopenharmony_ci
14228c2ecf20Sopenharmony_ci    acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT);
14238c2ecf20Sopenharmony_ci
14248c2ecf20Sopenharmony_ci    acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "for message byte");
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_ci    message = sbic_arm_read(host, SBIC_DATA);
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci    /* wait for MSGIN-XFER-PAUSED */
14298c2ecf20Sopenharmony_ci    acornscsi_sbic_wait(host, ASR_INT, ASR_INT, 1000, "for interrupt after message byte");
14308c2ecf20Sopenharmony_ci
14318c2ecf20Sopenharmony_ci    sbic_arm_read(host, SBIC_SSR);
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci    return message;
14348c2ecf20Sopenharmony_ci}
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci/*
14378c2ecf20Sopenharmony_ci * Function: void acornscsi_message(AS_Host *host)
14388c2ecf20Sopenharmony_ci * Purpose : Read complete message from connected target & action message
14398c2ecf20Sopenharmony_ci * Params  : host - host connected to target
14408c2ecf20Sopenharmony_ci */
14418c2ecf20Sopenharmony_cistatic
14428c2ecf20Sopenharmony_civoid acornscsi_message(AS_Host *host)
14438c2ecf20Sopenharmony_ci{
14448c2ecf20Sopenharmony_ci    unsigned char message[16];
14458c2ecf20Sopenharmony_ci    unsigned int msgidx = 0, msglen = 1;
14468c2ecf20Sopenharmony_ci
14478c2ecf20Sopenharmony_ci    do {
14488c2ecf20Sopenharmony_ci	message[msgidx] = acornscsi_readmessagebyte(host);
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_ci	switch (msgidx) {
14518c2ecf20Sopenharmony_ci	case 0:
14528c2ecf20Sopenharmony_ci	    if (message[0] == EXTENDED_MESSAGE ||
14538c2ecf20Sopenharmony_ci		(message[0] >= 0x20 && message[0] <= 0x2f))
14548c2ecf20Sopenharmony_ci		msglen = 2;
14558c2ecf20Sopenharmony_ci	    break;
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_ci	case 1:
14588c2ecf20Sopenharmony_ci	    if (message[0] == EXTENDED_MESSAGE)
14598c2ecf20Sopenharmony_ci		msglen += message[msgidx];
14608c2ecf20Sopenharmony_ci	    break;
14618c2ecf20Sopenharmony_ci	}
14628c2ecf20Sopenharmony_ci	msgidx += 1;
14638c2ecf20Sopenharmony_ci	if (msgidx < msglen) {
14648c2ecf20Sopenharmony_ci	    acornscsi_sbic_issuecmd(host, CMND_NEGATEACK);
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ci	    /* wait for next msg-in */
14678c2ecf20Sopenharmony_ci	    acornscsi_sbic_wait(host, ASR_INT, ASR_INT, 1000, "for interrupt after negate ack");
14688c2ecf20Sopenharmony_ci	    sbic_arm_read(host, SBIC_SSR);
14698c2ecf20Sopenharmony_ci	}
14708c2ecf20Sopenharmony_ci    } while (msgidx < msglen);
14718c2ecf20Sopenharmony_ci
14728c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_MESSAGES)
14738c2ecf20Sopenharmony_ci    printk("scsi%d.%c: message in: ",
14748c2ecf20Sopenharmony_ci	    host->host->host_no, acornscsi_target(host));
14758c2ecf20Sopenharmony_ci    spi_print_msg(message);
14768c2ecf20Sopenharmony_ci    printk("\n");
14778c2ecf20Sopenharmony_ci#endif
14788c2ecf20Sopenharmony_ci
14798c2ecf20Sopenharmony_ci    if (host->scsi.phase == PHASE_RECONNECTED) {
14808c2ecf20Sopenharmony_ci	/*
14818c2ecf20Sopenharmony_ci	 * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.17)
14828c2ecf20Sopenharmony_ci	 * 'Whenever a target reconnects to an initiator to continue
14838c2ecf20Sopenharmony_ci	 *  a tagged I/O process, the SIMPLE QUEUE TAG message shall
14848c2ecf20Sopenharmony_ci	 *  be sent immediately following the IDENTIFY message...'
14858c2ecf20Sopenharmony_ci	 */
14868c2ecf20Sopenharmony_ci	if (message[0] == SIMPLE_QUEUE_TAG)
14878c2ecf20Sopenharmony_ci	    host->scsi.reconnected.tag = message[1];
14888c2ecf20Sopenharmony_ci	if (acornscsi_reconnect_finish(host))
14898c2ecf20Sopenharmony_ci	    host->scsi.phase = PHASE_MSGIN;
14908c2ecf20Sopenharmony_ci    }
14918c2ecf20Sopenharmony_ci
14928c2ecf20Sopenharmony_ci    switch (message[0]) {
14938c2ecf20Sopenharmony_ci    case ABORT:
14948c2ecf20Sopenharmony_ci    case ABORT_TAG:
14958c2ecf20Sopenharmony_ci    case COMMAND_COMPLETE:
14968c2ecf20Sopenharmony_ci	if (host->scsi.phase != PHASE_STATUSIN) {
14978c2ecf20Sopenharmony_ci	    printk(KERN_ERR "scsi%d.%c: command complete following non-status in phase?\n",
14988c2ecf20Sopenharmony_ci		    host->host->host_no, acornscsi_target(host));
14998c2ecf20Sopenharmony_ci	    acornscsi_dumplog(host, host->SCpnt->device->id);
15008c2ecf20Sopenharmony_ci	}
15018c2ecf20Sopenharmony_ci	host->scsi.phase = PHASE_DONE;
15028c2ecf20Sopenharmony_ci	host->scsi.SCp.Message = message[0];
15038c2ecf20Sopenharmony_ci	break;
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci    case SAVE_POINTERS:
15068c2ecf20Sopenharmony_ci	/*
15078c2ecf20Sopenharmony_ci	 * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.20)
15088c2ecf20Sopenharmony_ci	 * 'The SAVE DATA POINTER message is sent from a target to
15098c2ecf20Sopenharmony_ci	 *  direct the initiator to copy the active data pointer to
15108c2ecf20Sopenharmony_ci	 *  the saved data pointer for the current I/O process.
15118c2ecf20Sopenharmony_ci	 */
15128c2ecf20Sopenharmony_ci	acornscsi_dma_cleanup(host);
15138c2ecf20Sopenharmony_ci	host->SCpnt->SCp = host->scsi.SCp;
15148c2ecf20Sopenharmony_ci	host->SCpnt->SCp.sent_command = 0;
15158c2ecf20Sopenharmony_ci	host->scsi.phase = PHASE_MSGIN;
15168c2ecf20Sopenharmony_ci	break;
15178c2ecf20Sopenharmony_ci
15188c2ecf20Sopenharmony_ci    case RESTORE_POINTERS:
15198c2ecf20Sopenharmony_ci	/*
15208c2ecf20Sopenharmony_ci	 * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.19)
15218c2ecf20Sopenharmony_ci	 * 'The RESTORE POINTERS message is sent from a target to
15228c2ecf20Sopenharmony_ci	 *  direct the initiator to copy the most recently saved
15238c2ecf20Sopenharmony_ci	 *  command, data, and status pointers for the I/O process
15248c2ecf20Sopenharmony_ci	 *  to the corresponding active pointers.  The command and
15258c2ecf20Sopenharmony_ci	 *  status pointers shall be restored to the beginning of
15268c2ecf20Sopenharmony_ci	 *  the present command and status areas.'
15278c2ecf20Sopenharmony_ci	 */
15288c2ecf20Sopenharmony_ci	acornscsi_dma_cleanup(host);
15298c2ecf20Sopenharmony_ci	host->scsi.SCp = host->SCpnt->SCp;
15308c2ecf20Sopenharmony_ci	host->scsi.phase = PHASE_MSGIN;
15318c2ecf20Sopenharmony_ci	break;
15328c2ecf20Sopenharmony_ci
15338c2ecf20Sopenharmony_ci    case DISCONNECT:
15348c2ecf20Sopenharmony_ci	/*
15358c2ecf20Sopenharmony_ci	 * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 6.4.2)
15368c2ecf20Sopenharmony_ci	 * 'On those occasions when an error or exception condition occurs
15378c2ecf20Sopenharmony_ci	 *  and the target elects to repeat the information transfer, the
15388c2ecf20Sopenharmony_ci	 *  target may repeat the transfer either issuing a RESTORE POINTERS
15398c2ecf20Sopenharmony_ci	 *  message or by disconnecting without issuing a SAVE POINTERS
15408c2ecf20Sopenharmony_ci	 *  message.  When reconnection is completed, the most recent
15418c2ecf20Sopenharmony_ci	 *  saved pointer values are restored.'
15428c2ecf20Sopenharmony_ci	 */
15438c2ecf20Sopenharmony_ci	acornscsi_dma_cleanup(host);
15448c2ecf20Sopenharmony_ci	host->scsi.phase = PHASE_DISCONNECT;
15458c2ecf20Sopenharmony_ci	break;
15468c2ecf20Sopenharmony_ci
15478c2ecf20Sopenharmony_ci    case MESSAGE_REJECT:
15488c2ecf20Sopenharmony_ci#if 0 /* this isn't needed any more */
15498c2ecf20Sopenharmony_ci	/*
15508c2ecf20Sopenharmony_ci	 * If we were negociating sync transfer, we don't yet know if
15518c2ecf20Sopenharmony_ci	 * this REJECT is for the sync transfer or for the tagged queue/wide
15528c2ecf20Sopenharmony_ci	 * transfer.  Re-initiate sync transfer negotiation now, and if
15538c2ecf20Sopenharmony_ci	 * we got a REJECT in response to SDTR, then it'll be set to DONE.
15548c2ecf20Sopenharmony_ci	 */
15558c2ecf20Sopenharmony_ci	if (host->device[host->SCpnt->device->id].sync_state == SYNC_SENT_REQUEST)
15568c2ecf20Sopenharmony_ci	    host->device[host->SCpnt->device->id].sync_state = SYNC_NEGOCIATE;
15578c2ecf20Sopenharmony_ci#endif
15588c2ecf20Sopenharmony_ci
15598c2ecf20Sopenharmony_ci	/*
15608c2ecf20Sopenharmony_ci	 * If we have any messages waiting to go out, then assert ATN now
15618c2ecf20Sopenharmony_ci	 */
15628c2ecf20Sopenharmony_ci	if (msgqueue_msglength(&host->scsi.msgs))
15638c2ecf20Sopenharmony_ci	    acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_ci	switch (host->scsi.last_message) {
15668c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
15678c2ecf20Sopenharmony_ci	case HEAD_OF_QUEUE_TAG:
15688c2ecf20Sopenharmony_ci	case ORDERED_QUEUE_TAG:
15698c2ecf20Sopenharmony_ci	case SIMPLE_QUEUE_TAG:
15708c2ecf20Sopenharmony_ci	    /*
15718c2ecf20Sopenharmony_ci	     * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.17)
15728c2ecf20Sopenharmony_ci	     *  If a target does not implement tagged queuing and a queue tag
15738c2ecf20Sopenharmony_ci	     *  message is received, it shall respond with a MESSAGE REJECT
15748c2ecf20Sopenharmony_ci	     *  message and accept the I/O process as if it were untagged.
15758c2ecf20Sopenharmony_ci	     */
15768c2ecf20Sopenharmony_ci	    printk(KERN_NOTICE "scsi%d.%c: disabling tagged queueing\n",
15778c2ecf20Sopenharmony_ci		    host->host->host_no, acornscsi_target(host));
15788c2ecf20Sopenharmony_ci	    host->SCpnt->device->simple_tags = 0;
15798c2ecf20Sopenharmony_ci	    set_bit(host->SCpnt->device->id * 8 +
15808c2ecf20Sopenharmony_ci		    (u8)(host->SCpnt->device->lun & 0x7), host->busyluns);
15818c2ecf20Sopenharmony_ci	    break;
15828c2ecf20Sopenharmony_ci#endif
15838c2ecf20Sopenharmony_ci	case EXTENDED_MESSAGE | (EXTENDED_SDTR << 8):
15848c2ecf20Sopenharmony_ci	    /*
15858c2ecf20Sopenharmony_ci	     * Target can't handle synchronous transfers
15868c2ecf20Sopenharmony_ci	     */
15878c2ecf20Sopenharmony_ci	    printk(KERN_NOTICE "scsi%d.%c: Using asynchronous transfer\n",
15888c2ecf20Sopenharmony_ci		    host->host->host_no, acornscsi_target(host));
15898c2ecf20Sopenharmony_ci	    host->device[host->SCpnt->device->id].sync_xfer = SYNCHTRANSFER_2DBA;
15908c2ecf20Sopenharmony_ci	    host->device[host->SCpnt->device->id].sync_state = SYNC_ASYNCHRONOUS;
15918c2ecf20Sopenharmony_ci	    sbic_arm_write(host, SBIC_SYNCHTRANSFER, host->device[host->SCpnt->device->id].sync_xfer);
15928c2ecf20Sopenharmony_ci	    break;
15938c2ecf20Sopenharmony_ci
15948c2ecf20Sopenharmony_ci	default:
15958c2ecf20Sopenharmony_ci	    break;
15968c2ecf20Sopenharmony_ci	}
15978c2ecf20Sopenharmony_ci	break;
15988c2ecf20Sopenharmony_ci
15998c2ecf20Sopenharmony_ci    case QUEUE_FULL:
16008c2ecf20Sopenharmony_ci	/* TODO: target queue is full */
16018c2ecf20Sopenharmony_ci	break;
16028c2ecf20Sopenharmony_ci
16038c2ecf20Sopenharmony_ci    case SIMPLE_QUEUE_TAG:
16048c2ecf20Sopenharmony_ci	/* tag queue reconnect... message[1] = queue tag.  Print something to indicate something happened! */
16058c2ecf20Sopenharmony_ci	printk("scsi%d.%c: reconnect queue tag %02X\n",
16068c2ecf20Sopenharmony_ci		host->host->host_no, acornscsi_target(host),
16078c2ecf20Sopenharmony_ci		message[1]);
16088c2ecf20Sopenharmony_ci	break;
16098c2ecf20Sopenharmony_ci
16108c2ecf20Sopenharmony_ci    case EXTENDED_MESSAGE:
16118c2ecf20Sopenharmony_ci	switch (message[2]) {
16128c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_ACORNSCSI_SYNC
16138c2ecf20Sopenharmony_ci	case EXTENDED_SDTR:
16148c2ecf20Sopenharmony_ci	    if (host->device[host->SCpnt->device->id].sync_state == SYNC_SENT_REQUEST) {
16158c2ecf20Sopenharmony_ci		/*
16168c2ecf20Sopenharmony_ci		 * We requested synchronous transfers.  This isn't quite right...
16178c2ecf20Sopenharmony_ci		 * We can only say if this succeeded if we proceed on to execute the
16188c2ecf20Sopenharmony_ci		 * command from this message.  If we get a MESSAGE PARITY ERROR,
16198c2ecf20Sopenharmony_ci		 * and the target retries fail, then we fallback to asynchronous mode
16208c2ecf20Sopenharmony_ci		 */
16218c2ecf20Sopenharmony_ci		host->device[host->SCpnt->device->id].sync_state = SYNC_COMPLETED;
16228c2ecf20Sopenharmony_ci		printk(KERN_NOTICE "scsi%d.%c: Using synchronous transfer, offset %d, %d ns\n",
16238c2ecf20Sopenharmony_ci			host->host->host_no, acornscsi_target(host),
16248c2ecf20Sopenharmony_ci			message[4], message[3] * 4);
16258c2ecf20Sopenharmony_ci		host->device[host->SCpnt->device->id].sync_xfer =
16268c2ecf20Sopenharmony_ci			calc_sync_xfer(message[3] * 4, message[4]);
16278c2ecf20Sopenharmony_ci	    } else {
16288c2ecf20Sopenharmony_ci		unsigned char period, length;
16298c2ecf20Sopenharmony_ci		/*
16308c2ecf20Sopenharmony_ci		 * Target requested synchronous transfers.  The agreement is only
16318c2ecf20Sopenharmony_ci		 * to be in operation AFTER the target leaves message out phase.
16328c2ecf20Sopenharmony_ci		 */
16338c2ecf20Sopenharmony_ci		acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
16348c2ecf20Sopenharmony_ci		period = max_t(unsigned int, message[3], sdtr_period / 4);
16358c2ecf20Sopenharmony_ci		length = min_t(unsigned int, message[4], sdtr_size);
16368c2ecf20Sopenharmony_ci		msgqueue_addmsg(&host->scsi.msgs, 5, EXTENDED_MESSAGE, 3,
16378c2ecf20Sopenharmony_ci				 EXTENDED_SDTR, period, length);
16388c2ecf20Sopenharmony_ci		host->device[host->SCpnt->device->id].sync_xfer =
16398c2ecf20Sopenharmony_ci			calc_sync_xfer(period * 4, length);
16408c2ecf20Sopenharmony_ci	    }
16418c2ecf20Sopenharmony_ci	    sbic_arm_write(host, SBIC_SYNCHTRANSFER, host->device[host->SCpnt->device->id].sync_xfer);
16428c2ecf20Sopenharmony_ci	    break;
16438c2ecf20Sopenharmony_ci#else
16448c2ecf20Sopenharmony_ci	    /* We do not accept synchronous transfers.  Respond with a
16458c2ecf20Sopenharmony_ci	     * MESSAGE_REJECT.
16468c2ecf20Sopenharmony_ci	     */
16478c2ecf20Sopenharmony_ci#endif
16488c2ecf20Sopenharmony_ci
16498c2ecf20Sopenharmony_ci	case EXTENDED_WDTR:
16508c2ecf20Sopenharmony_ci	    /* The WD33C93A is only 8-bit.  We respond with a MESSAGE_REJECT
16518c2ecf20Sopenharmony_ci	     * to a wide data transfer request.
16528c2ecf20Sopenharmony_ci	     */
16538c2ecf20Sopenharmony_ci	default:
16548c2ecf20Sopenharmony_ci	    acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
16558c2ecf20Sopenharmony_ci	    msgqueue_flush(&host->scsi.msgs);
16568c2ecf20Sopenharmony_ci	    msgqueue_addmsg(&host->scsi.msgs, 1, MESSAGE_REJECT);
16578c2ecf20Sopenharmony_ci	    break;
16588c2ecf20Sopenharmony_ci	}
16598c2ecf20Sopenharmony_ci	break;
16608c2ecf20Sopenharmony_ci
16618c2ecf20Sopenharmony_ci    default: /* reject message */
16628c2ecf20Sopenharmony_ci	printk(KERN_ERR "scsi%d.%c: unrecognised message %02X, rejecting\n",
16638c2ecf20Sopenharmony_ci		host->host->host_no, acornscsi_target(host),
16648c2ecf20Sopenharmony_ci		message[0]);
16658c2ecf20Sopenharmony_ci	acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
16668c2ecf20Sopenharmony_ci	msgqueue_flush(&host->scsi.msgs);
16678c2ecf20Sopenharmony_ci	msgqueue_addmsg(&host->scsi.msgs, 1, MESSAGE_REJECT);
16688c2ecf20Sopenharmony_ci	host->scsi.phase = PHASE_MSGIN;
16698c2ecf20Sopenharmony_ci	break;
16708c2ecf20Sopenharmony_ci    }
16718c2ecf20Sopenharmony_ci    acornscsi_sbic_issuecmd(host, CMND_NEGATEACK);
16728c2ecf20Sopenharmony_ci}
16738c2ecf20Sopenharmony_ci
16748c2ecf20Sopenharmony_ci/*
16758c2ecf20Sopenharmony_ci * Function: int acornscsi_buildmessages(AS_Host *host)
16768c2ecf20Sopenharmony_ci * Purpose : build the connection messages for a host
16778c2ecf20Sopenharmony_ci * Params  : host - host to add messages to
16788c2ecf20Sopenharmony_ci */
16798c2ecf20Sopenharmony_cistatic
16808c2ecf20Sopenharmony_civoid acornscsi_buildmessages(AS_Host *host)
16818c2ecf20Sopenharmony_ci{
16828c2ecf20Sopenharmony_ci#if 0
16838c2ecf20Sopenharmony_ci    /* does the device need resetting? */
16848c2ecf20Sopenharmony_ci    if (cmd_reset) {
16858c2ecf20Sopenharmony_ci	msgqueue_addmsg(&host->scsi.msgs, 1, BUS_DEVICE_RESET);
16868c2ecf20Sopenharmony_ci	return;
16878c2ecf20Sopenharmony_ci    }
16888c2ecf20Sopenharmony_ci#endif
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_ci    msgqueue_addmsg(&host->scsi.msgs, 1,
16918c2ecf20Sopenharmony_ci		     IDENTIFY(host->device[host->SCpnt->device->id].disconnect_ok,
16928c2ecf20Sopenharmony_ci			     host->SCpnt->device->lun));
16938c2ecf20Sopenharmony_ci
16948c2ecf20Sopenharmony_ci#if 0
16958c2ecf20Sopenharmony_ci    /* does the device need the current command aborted */
16968c2ecf20Sopenharmony_ci    if (cmd_aborted) {
16978c2ecf20Sopenharmony_ci	acornscsi_abortcmd(host->SCpnt->tag);
16988c2ecf20Sopenharmony_ci	return;
16998c2ecf20Sopenharmony_ci    }
17008c2ecf20Sopenharmony_ci#endif
17018c2ecf20Sopenharmony_ci
17028c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
17038c2ecf20Sopenharmony_ci    if (host->SCpnt->tag) {
17048c2ecf20Sopenharmony_ci	unsigned int tag_type;
17058c2ecf20Sopenharmony_ci
17068c2ecf20Sopenharmony_ci	if (host->SCpnt->cmnd[0] == REQUEST_SENSE ||
17078c2ecf20Sopenharmony_ci	    host->SCpnt->cmnd[0] == TEST_UNIT_READY ||
17088c2ecf20Sopenharmony_ci	    host->SCpnt->cmnd[0] == INQUIRY)
17098c2ecf20Sopenharmony_ci	    tag_type = HEAD_OF_QUEUE_TAG;
17108c2ecf20Sopenharmony_ci	else
17118c2ecf20Sopenharmony_ci	    tag_type = SIMPLE_QUEUE_TAG;
17128c2ecf20Sopenharmony_ci	msgqueue_addmsg(&host->scsi.msgs, 2, tag_type, host->SCpnt->tag);
17138c2ecf20Sopenharmony_ci    }
17148c2ecf20Sopenharmony_ci#endif
17158c2ecf20Sopenharmony_ci
17168c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_ACORNSCSI_SYNC
17178c2ecf20Sopenharmony_ci    if (host->device[host->SCpnt->device->id].sync_state == SYNC_NEGOCIATE) {
17188c2ecf20Sopenharmony_ci	host->device[host->SCpnt->device->id].sync_state = SYNC_SENT_REQUEST;
17198c2ecf20Sopenharmony_ci	msgqueue_addmsg(&host->scsi.msgs, 5,
17208c2ecf20Sopenharmony_ci			 EXTENDED_MESSAGE, 3, EXTENDED_SDTR,
17218c2ecf20Sopenharmony_ci			 sdtr_period / 4, sdtr_size);
17228c2ecf20Sopenharmony_ci    }
17238c2ecf20Sopenharmony_ci#endif
17248c2ecf20Sopenharmony_ci}
17258c2ecf20Sopenharmony_ci
17268c2ecf20Sopenharmony_ci/*
17278c2ecf20Sopenharmony_ci * Function: int acornscsi_starttransfer(AS_Host *host)
17288c2ecf20Sopenharmony_ci * Purpose : transfer data to/from connected target
17298c2ecf20Sopenharmony_ci * Params  : host - host to which target is connected
17308c2ecf20Sopenharmony_ci * Returns : 0 if failure
17318c2ecf20Sopenharmony_ci */
17328c2ecf20Sopenharmony_cistatic
17338c2ecf20Sopenharmony_ciint acornscsi_starttransfer(AS_Host *host)
17348c2ecf20Sopenharmony_ci{
17358c2ecf20Sopenharmony_ci    int residual;
17368c2ecf20Sopenharmony_ci
17378c2ecf20Sopenharmony_ci    if (!host->scsi.SCp.ptr /*&& host->scsi.SCp.this_residual*/) {
17388c2ecf20Sopenharmony_ci	printk(KERN_ERR "scsi%d.%c: null buffer passed to acornscsi_starttransfer\n",
17398c2ecf20Sopenharmony_ci		host->host->host_no, acornscsi_target(host));
17408c2ecf20Sopenharmony_ci	return 0;
17418c2ecf20Sopenharmony_ci    }
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_ci    residual = scsi_bufflen(host->SCpnt) - host->scsi.SCp.scsi_xferred;
17448c2ecf20Sopenharmony_ci
17458c2ecf20Sopenharmony_ci    sbic_arm_write(host, SBIC_SYNCHTRANSFER, host->device[host->SCpnt->device->id].sync_xfer);
17468c2ecf20Sopenharmony_ci    sbic_arm_writenext(host, residual >> 16);
17478c2ecf20Sopenharmony_ci    sbic_arm_writenext(host, residual >> 8);
17488c2ecf20Sopenharmony_ci    sbic_arm_writenext(host, residual);
17498c2ecf20Sopenharmony_ci    acornscsi_sbic_issuecmd(host, CMND_XFERINFO);
17508c2ecf20Sopenharmony_ci    return 1;
17518c2ecf20Sopenharmony_ci}
17528c2ecf20Sopenharmony_ci
17538c2ecf20Sopenharmony_ci/* =========================================================================================
17548c2ecf20Sopenharmony_ci * Connection & Disconnection
17558c2ecf20Sopenharmony_ci */
17568c2ecf20Sopenharmony_ci/*
17578c2ecf20Sopenharmony_ci * Function : acornscsi_reconnect(AS_Host *host)
17588c2ecf20Sopenharmony_ci * Purpose  : reconnect a previously disconnected command
17598c2ecf20Sopenharmony_ci * Params   : host - host specific data
17608c2ecf20Sopenharmony_ci * Remarks  : SCSI spec says:
17618c2ecf20Sopenharmony_ci *		'The set of active pointers is restored from the set
17628c2ecf20Sopenharmony_ci *		 of saved pointers upon reconnection of the I/O process'
17638c2ecf20Sopenharmony_ci */
17648c2ecf20Sopenharmony_cistatic
17658c2ecf20Sopenharmony_ciint acornscsi_reconnect(AS_Host *host)
17668c2ecf20Sopenharmony_ci{
17678c2ecf20Sopenharmony_ci    unsigned int target, lun, ok = 0;
17688c2ecf20Sopenharmony_ci
17698c2ecf20Sopenharmony_ci    target = sbic_arm_read(host, SBIC_SOURCEID);
17708c2ecf20Sopenharmony_ci
17718c2ecf20Sopenharmony_ci    if (!(target & 8))
17728c2ecf20Sopenharmony_ci	printk(KERN_ERR "scsi%d: invalid source id after reselection "
17738c2ecf20Sopenharmony_ci		"- device fault?\n",
17748c2ecf20Sopenharmony_ci		host->host->host_no);
17758c2ecf20Sopenharmony_ci
17768c2ecf20Sopenharmony_ci    target &= 7;
17778c2ecf20Sopenharmony_ci
17788c2ecf20Sopenharmony_ci    if (host->SCpnt && !host->scsi.disconnectable) {
17798c2ecf20Sopenharmony_ci	printk(KERN_ERR "scsi%d.%d: reconnected while command in "
17808c2ecf20Sopenharmony_ci		"progress to target %d?\n",
17818c2ecf20Sopenharmony_ci		host->host->host_no, target, host->SCpnt->device->id);
17828c2ecf20Sopenharmony_ci	host->SCpnt = NULL;
17838c2ecf20Sopenharmony_ci    }
17848c2ecf20Sopenharmony_ci
17858c2ecf20Sopenharmony_ci    lun = sbic_arm_read(host, SBIC_DATA) & 7;
17868c2ecf20Sopenharmony_ci
17878c2ecf20Sopenharmony_ci    host->scsi.reconnected.target = target;
17888c2ecf20Sopenharmony_ci    host->scsi.reconnected.lun = lun;
17898c2ecf20Sopenharmony_ci    host->scsi.reconnected.tag = 0;
17908c2ecf20Sopenharmony_ci
17918c2ecf20Sopenharmony_ci    if (host->scsi.disconnectable && host->SCpnt &&
17928c2ecf20Sopenharmony_ci	host->SCpnt->device->id == target && host->SCpnt->device->lun == lun)
17938c2ecf20Sopenharmony_ci	ok = 1;
17948c2ecf20Sopenharmony_ci
17958c2ecf20Sopenharmony_ci    if (!ok && queue_probetgtlun(&host->queues.disconnected, target, lun))
17968c2ecf20Sopenharmony_ci	ok = 1;
17978c2ecf20Sopenharmony_ci
17988c2ecf20Sopenharmony_ci    ADD_STATUS(target, 0x81, host->scsi.phase, 0);
17998c2ecf20Sopenharmony_ci
18008c2ecf20Sopenharmony_ci    if (ok) {
18018c2ecf20Sopenharmony_ci	host->scsi.phase = PHASE_RECONNECTED;
18028c2ecf20Sopenharmony_ci    } else {
18038c2ecf20Sopenharmony_ci	/* this doesn't seem to work */
18048c2ecf20Sopenharmony_ci	printk(KERN_ERR "scsi%d.%c: reselected with no command "
18058c2ecf20Sopenharmony_ci		"to reconnect with\n",
18068c2ecf20Sopenharmony_ci		host->host->host_no, '0' + target);
18078c2ecf20Sopenharmony_ci	acornscsi_dumplog(host, target);
18088c2ecf20Sopenharmony_ci	acornscsi_abortcmd(host, 0);
18098c2ecf20Sopenharmony_ci	if (host->SCpnt) {
18108c2ecf20Sopenharmony_ci	    queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt);
18118c2ecf20Sopenharmony_ci	    host->SCpnt = NULL;
18128c2ecf20Sopenharmony_ci	}
18138c2ecf20Sopenharmony_ci    }
18148c2ecf20Sopenharmony_ci    acornscsi_sbic_issuecmd(host, CMND_NEGATEACK);
18158c2ecf20Sopenharmony_ci    return !ok;
18168c2ecf20Sopenharmony_ci}
18178c2ecf20Sopenharmony_ci
18188c2ecf20Sopenharmony_ci/*
18198c2ecf20Sopenharmony_ci * Function: int acornscsi_reconnect_finish(AS_Host *host)
18208c2ecf20Sopenharmony_ci * Purpose : finish reconnecting a command
18218c2ecf20Sopenharmony_ci * Params  : host - host to complete
18228c2ecf20Sopenharmony_ci * Returns : 0 if failed
18238c2ecf20Sopenharmony_ci */
18248c2ecf20Sopenharmony_cistatic
18258c2ecf20Sopenharmony_ciint acornscsi_reconnect_finish(AS_Host *host)
18268c2ecf20Sopenharmony_ci{
18278c2ecf20Sopenharmony_ci    if (host->scsi.disconnectable && host->SCpnt) {
18288c2ecf20Sopenharmony_ci	host->scsi.disconnectable = 0;
18298c2ecf20Sopenharmony_ci	if (host->SCpnt->device->id  == host->scsi.reconnected.target &&
18308c2ecf20Sopenharmony_ci	    host->SCpnt->device->lun == host->scsi.reconnected.lun &&
18318c2ecf20Sopenharmony_ci	    host->SCpnt->tag         == host->scsi.reconnected.tag) {
18328c2ecf20Sopenharmony_ci#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
18338c2ecf20Sopenharmony_ci	    DBG(host->SCpnt, printk("scsi%d.%c: reconnected",
18348c2ecf20Sopenharmony_ci		    host->host->host_no, acornscsi_target(host)));
18358c2ecf20Sopenharmony_ci#endif
18368c2ecf20Sopenharmony_ci	} else {
18378c2ecf20Sopenharmony_ci	    queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt);
18388c2ecf20Sopenharmony_ci#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
18398c2ecf20Sopenharmony_ci	    DBG(host->SCpnt, printk("scsi%d.%c: had to move command "
18408c2ecf20Sopenharmony_ci		    "to disconnected queue\n",
18418c2ecf20Sopenharmony_ci		    host->host->host_no, acornscsi_target(host)));
18428c2ecf20Sopenharmony_ci#endif
18438c2ecf20Sopenharmony_ci	    host->SCpnt = NULL;
18448c2ecf20Sopenharmony_ci	}
18458c2ecf20Sopenharmony_ci    }
18468c2ecf20Sopenharmony_ci    if (!host->SCpnt) {
18478c2ecf20Sopenharmony_ci	host->SCpnt = queue_remove_tgtluntag(&host->queues.disconnected,
18488c2ecf20Sopenharmony_ci				host->scsi.reconnected.target,
18498c2ecf20Sopenharmony_ci				host->scsi.reconnected.lun,
18508c2ecf20Sopenharmony_ci				host->scsi.reconnected.tag);
18518c2ecf20Sopenharmony_ci#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
18528c2ecf20Sopenharmony_ci	DBG(host->SCpnt, printk("scsi%d.%c: had to get command",
18538c2ecf20Sopenharmony_ci		host->host->host_no, acornscsi_target(host)));
18548c2ecf20Sopenharmony_ci#endif
18558c2ecf20Sopenharmony_ci    }
18568c2ecf20Sopenharmony_ci
18578c2ecf20Sopenharmony_ci    if (!host->SCpnt)
18588c2ecf20Sopenharmony_ci	acornscsi_abortcmd(host, host->scsi.reconnected.tag);
18598c2ecf20Sopenharmony_ci    else {
18608c2ecf20Sopenharmony_ci	/*
18618c2ecf20Sopenharmony_ci	 * Restore data pointer from SAVED pointers.
18628c2ecf20Sopenharmony_ci	 */
18638c2ecf20Sopenharmony_ci	host->scsi.SCp = host->SCpnt->SCp;
18648c2ecf20Sopenharmony_ci#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
18658c2ecf20Sopenharmony_ci	printk(", data pointers: [%p, %X]",
18668c2ecf20Sopenharmony_ci		host->scsi.SCp.ptr, host->scsi.SCp.this_residual);
18678c2ecf20Sopenharmony_ci#endif
18688c2ecf20Sopenharmony_ci    }
18698c2ecf20Sopenharmony_ci#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
18708c2ecf20Sopenharmony_ci    printk("\n");
18718c2ecf20Sopenharmony_ci#endif
18728c2ecf20Sopenharmony_ci
18738c2ecf20Sopenharmony_ci    host->dma.transferred = host->scsi.SCp.scsi_xferred;
18748c2ecf20Sopenharmony_ci
18758c2ecf20Sopenharmony_ci    return host->SCpnt != NULL;
18768c2ecf20Sopenharmony_ci}
18778c2ecf20Sopenharmony_ci
18788c2ecf20Sopenharmony_ci/*
18798c2ecf20Sopenharmony_ci * Function: void acornscsi_disconnect_unexpected(AS_Host *host)
18808c2ecf20Sopenharmony_ci * Purpose : handle an unexpected disconnect
18818c2ecf20Sopenharmony_ci * Params  : host - host on which disconnect occurred
18828c2ecf20Sopenharmony_ci */
18838c2ecf20Sopenharmony_cistatic
18848c2ecf20Sopenharmony_civoid acornscsi_disconnect_unexpected(AS_Host *host)
18858c2ecf20Sopenharmony_ci{
18868c2ecf20Sopenharmony_ci    printk(KERN_ERR "scsi%d.%c: unexpected disconnect\n",
18878c2ecf20Sopenharmony_ci	    host->host->host_no, acornscsi_target(host));
18888c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_ABORT)
18898c2ecf20Sopenharmony_ci    acornscsi_dumplog(host, 8);
18908c2ecf20Sopenharmony_ci#endif
18918c2ecf20Sopenharmony_ci
18928c2ecf20Sopenharmony_ci    acornscsi_done(host, &host->SCpnt, DID_ERROR);
18938c2ecf20Sopenharmony_ci}
18948c2ecf20Sopenharmony_ci
18958c2ecf20Sopenharmony_ci/*
18968c2ecf20Sopenharmony_ci * Function: void acornscsi_abortcmd(AS_host *host, unsigned char tag)
18978c2ecf20Sopenharmony_ci * Purpose : abort a currently executing command
18988c2ecf20Sopenharmony_ci * Params  : host - host with connected command to abort
18998c2ecf20Sopenharmony_ci *	     tag  - tag to abort
19008c2ecf20Sopenharmony_ci */
19018c2ecf20Sopenharmony_cistatic
19028c2ecf20Sopenharmony_civoid acornscsi_abortcmd(AS_Host *host, unsigned char tag)
19038c2ecf20Sopenharmony_ci{
19048c2ecf20Sopenharmony_ci    host->scsi.phase = PHASE_ABORTED;
19058c2ecf20Sopenharmony_ci    sbic_arm_write(host, SBIC_CMND, CMND_ASSERTATN);
19068c2ecf20Sopenharmony_ci
19078c2ecf20Sopenharmony_ci    msgqueue_flush(&host->scsi.msgs);
19088c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
19098c2ecf20Sopenharmony_ci    if (tag)
19108c2ecf20Sopenharmony_ci	msgqueue_addmsg(&host->scsi.msgs, 2, ABORT_TAG, tag);
19118c2ecf20Sopenharmony_ci    else
19128c2ecf20Sopenharmony_ci#endif
19138c2ecf20Sopenharmony_ci	msgqueue_addmsg(&host->scsi.msgs, 1, ABORT);
19148c2ecf20Sopenharmony_ci}
19158c2ecf20Sopenharmony_ci
19168c2ecf20Sopenharmony_ci/* ==========================================================================================
19178c2ecf20Sopenharmony_ci * Interrupt routines.
19188c2ecf20Sopenharmony_ci */
19198c2ecf20Sopenharmony_ci/*
19208c2ecf20Sopenharmony_ci * Function: int acornscsi_sbicintr(AS_Host *host)
19218c2ecf20Sopenharmony_ci * Purpose : handle interrupts from SCSI device
19228c2ecf20Sopenharmony_ci * Params  : host - host to process
19238c2ecf20Sopenharmony_ci * Returns : INTR_PROCESS if expecting another SBIC interrupt
19248c2ecf20Sopenharmony_ci *	     INTR_IDLE if no interrupt
19258c2ecf20Sopenharmony_ci *	     INTR_NEXT_COMMAND if we have finished processing the command
19268c2ecf20Sopenharmony_ci */
19278c2ecf20Sopenharmony_cistatic
19288c2ecf20Sopenharmony_ciintr_ret_t acornscsi_sbicintr(AS_Host *host, int in_irq)
19298c2ecf20Sopenharmony_ci{
19308c2ecf20Sopenharmony_ci    unsigned int asr, ssr;
19318c2ecf20Sopenharmony_ci
19328c2ecf20Sopenharmony_ci    asr = sbic_arm_read(host, SBIC_ASR);
19338c2ecf20Sopenharmony_ci    if (!(asr & ASR_INT))
19348c2ecf20Sopenharmony_ci	return INTR_IDLE;
19358c2ecf20Sopenharmony_ci
19368c2ecf20Sopenharmony_ci    ssr = sbic_arm_read(host, SBIC_SSR);
19378c2ecf20Sopenharmony_ci
19388c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_PHASES)
19398c2ecf20Sopenharmony_ci    print_sbic_status(asr, ssr, host->scsi.phase);
19408c2ecf20Sopenharmony_ci#endif
19418c2ecf20Sopenharmony_ci
19428c2ecf20Sopenharmony_ci    ADD_STATUS(8, ssr, host->scsi.phase, in_irq);
19438c2ecf20Sopenharmony_ci
19448c2ecf20Sopenharmony_ci    if (host->SCpnt && !host->scsi.disconnectable)
19458c2ecf20Sopenharmony_ci	ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, in_irq);
19468c2ecf20Sopenharmony_ci
19478c2ecf20Sopenharmony_ci    switch (ssr) {
19488c2ecf20Sopenharmony_ci    case 0x00:				/* reset state - not advanced			*/
19498c2ecf20Sopenharmony_ci	printk(KERN_ERR "scsi%d: reset in standard mode but wanted advanced mode.\n",
19508c2ecf20Sopenharmony_ci		host->host->host_no);
19518c2ecf20Sopenharmony_ci	/* setup sbic - WD33C93A */
19528c2ecf20Sopenharmony_ci	sbic_arm_write(host, SBIC_OWNID, OWNID_EAF | host->host->this_id);
19538c2ecf20Sopenharmony_ci	sbic_arm_write(host, SBIC_CMND, CMND_RESET);
19548c2ecf20Sopenharmony_ci	return INTR_IDLE;
19558c2ecf20Sopenharmony_ci
19568c2ecf20Sopenharmony_ci    case 0x01:				/* reset state - advanced			*/
19578c2ecf20Sopenharmony_ci	sbic_arm_write(host, SBIC_CTRL, INIT_SBICDMA | CTRL_IDI);
19588c2ecf20Sopenharmony_ci	sbic_arm_write(host, SBIC_TIMEOUT, TIMEOUT_TIME);
19598c2ecf20Sopenharmony_ci	sbic_arm_write(host, SBIC_SYNCHTRANSFER, SYNCHTRANSFER_2DBA);
19608c2ecf20Sopenharmony_ci	sbic_arm_write(host, SBIC_SOURCEID, SOURCEID_ER | SOURCEID_DSP);
19618c2ecf20Sopenharmony_ci	msgqueue_flush(&host->scsi.msgs);
19628c2ecf20Sopenharmony_ci	return INTR_IDLE;
19638c2ecf20Sopenharmony_ci
19648c2ecf20Sopenharmony_ci    case 0x41:				/* unexpected disconnect aborted command	*/
19658c2ecf20Sopenharmony_ci	acornscsi_disconnect_unexpected(host);
19668c2ecf20Sopenharmony_ci	return INTR_NEXT_COMMAND;
19678c2ecf20Sopenharmony_ci    }
19688c2ecf20Sopenharmony_ci
19698c2ecf20Sopenharmony_ci    switch (host->scsi.phase) {
19708c2ecf20Sopenharmony_ci    case PHASE_CONNECTING:		/* STATE: command removed from issue queue	*/
19718c2ecf20Sopenharmony_ci	switch (ssr) {
19728c2ecf20Sopenharmony_ci	case 0x11:			/* -> PHASE_CONNECTED				*/
19738c2ecf20Sopenharmony_ci	    /* BUS FREE -> SELECTION */
19748c2ecf20Sopenharmony_ci	    host->scsi.phase = PHASE_CONNECTED;
19758c2ecf20Sopenharmony_ci	    msgqueue_flush(&host->scsi.msgs);
19768c2ecf20Sopenharmony_ci	    host->dma.transferred = host->scsi.SCp.scsi_xferred;
19778c2ecf20Sopenharmony_ci	    /* 33C93 gives next interrupt indicating bus phase */
19788c2ecf20Sopenharmony_ci	    asr = sbic_arm_read(host, SBIC_ASR);
19798c2ecf20Sopenharmony_ci	    if (!(asr & ASR_INT))
19808c2ecf20Sopenharmony_ci		break;
19818c2ecf20Sopenharmony_ci	    ssr = sbic_arm_read(host, SBIC_SSR);
19828c2ecf20Sopenharmony_ci	    ADD_STATUS(8, ssr, host->scsi.phase, 1);
19838c2ecf20Sopenharmony_ci	    ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, 1);
19848c2ecf20Sopenharmony_ci	    goto connected;
19858c2ecf20Sopenharmony_ci
19868c2ecf20Sopenharmony_ci	case 0x42:			/* select timed out				*/
19878c2ecf20Sopenharmony_ci					/* -> PHASE_IDLE				*/
19888c2ecf20Sopenharmony_ci	    acornscsi_done(host, &host->SCpnt, DID_NO_CONNECT);
19898c2ecf20Sopenharmony_ci	    return INTR_NEXT_COMMAND;
19908c2ecf20Sopenharmony_ci
19918c2ecf20Sopenharmony_ci	case 0x81:			/* -> PHASE_RECONNECTED or PHASE_ABORTED	*/
19928c2ecf20Sopenharmony_ci	    /* BUS FREE -> RESELECTION */
19938c2ecf20Sopenharmony_ci	    host->origSCpnt = host->SCpnt;
19948c2ecf20Sopenharmony_ci	    host->SCpnt = NULL;
19958c2ecf20Sopenharmony_ci	    msgqueue_flush(&host->scsi.msgs);
19968c2ecf20Sopenharmony_ci	    acornscsi_reconnect(host);
19978c2ecf20Sopenharmony_ci	    break;
19988c2ecf20Sopenharmony_ci
19998c2ecf20Sopenharmony_ci	default:
20008c2ecf20Sopenharmony_ci	    printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTING, SSR %02X?\n",
20018c2ecf20Sopenharmony_ci		    host->host->host_no, acornscsi_target(host), ssr);
20028c2ecf20Sopenharmony_ci	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
20038c2ecf20Sopenharmony_ci	    acornscsi_abortcmd(host, host->SCpnt->tag);
20048c2ecf20Sopenharmony_ci	}
20058c2ecf20Sopenharmony_ci	return INTR_PROCESSING;
20068c2ecf20Sopenharmony_ci
20078c2ecf20Sopenharmony_ci    connected:
20088c2ecf20Sopenharmony_ci    case PHASE_CONNECTED:		/* STATE: device selected ok			*/
20098c2ecf20Sopenharmony_ci	switch (ssr) {
20108c2ecf20Sopenharmony_ci#ifdef NONSTANDARD
20118c2ecf20Sopenharmony_ci	case 0x8a:			/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED	*/
20128c2ecf20Sopenharmony_ci	    /* SELECTION -> COMMAND */
20138c2ecf20Sopenharmony_ci	    acornscsi_sendcommand(host);
20148c2ecf20Sopenharmony_ci	    break;
20158c2ecf20Sopenharmony_ci
20168c2ecf20Sopenharmony_ci	case 0x8b:			/* -> PHASE_STATUS				*/
20178c2ecf20Sopenharmony_ci	    /* SELECTION -> STATUS */
20188c2ecf20Sopenharmony_ci	    acornscsi_readstatusbyte(host);
20198c2ecf20Sopenharmony_ci	    host->scsi.phase = PHASE_STATUSIN;
20208c2ecf20Sopenharmony_ci	    break;
20218c2ecf20Sopenharmony_ci#endif
20228c2ecf20Sopenharmony_ci
20238c2ecf20Sopenharmony_ci	case 0x8e:			/* -> PHASE_MSGOUT				*/
20248c2ecf20Sopenharmony_ci	    /* SELECTION ->MESSAGE OUT */
20258c2ecf20Sopenharmony_ci	    host->scsi.phase = PHASE_MSGOUT;
20268c2ecf20Sopenharmony_ci	    acornscsi_buildmessages(host);
20278c2ecf20Sopenharmony_ci	    acornscsi_sendmessage(host);
20288c2ecf20Sopenharmony_ci	    break;
20298c2ecf20Sopenharmony_ci
20308c2ecf20Sopenharmony_ci	/* these should not happen */
20318c2ecf20Sopenharmony_ci	case 0x85:			/* target disconnected				*/
20328c2ecf20Sopenharmony_ci	    acornscsi_done(host, &host->SCpnt, DID_ERROR);
20338c2ecf20Sopenharmony_ci	    break;
20348c2ecf20Sopenharmony_ci
20358c2ecf20Sopenharmony_ci	default:
20368c2ecf20Sopenharmony_ci	    printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTED, SSR %02X?\n",
20378c2ecf20Sopenharmony_ci		    host->host->host_no, acornscsi_target(host), ssr);
20388c2ecf20Sopenharmony_ci	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
20398c2ecf20Sopenharmony_ci	    acornscsi_abortcmd(host, host->SCpnt->tag);
20408c2ecf20Sopenharmony_ci	}
20418c2ecf20Sopenharmony_ci	return INTR_PROCESSING;
20428c2ecf20Sopenharmony_ci
20438c2ecf20Sopenharmony_ci    case PHASE_MSGOUT:			/* STATE: connected & sent IDENTIFY message	*/
20448c2ecf20Sopenharmony_ci	/*
20458c2ecf20Sopenharmony_ci	 * SCSI standard says that MESSAGE OUT phases can be followed by a
20468c2ecf20Sopenharmony_ci	 * DATA phase, STATUS phase, MESSAGE IN phase or COMMAND phase
20478c2ecf20Sopenharmony_ci	 */
20488c2ecf20Sopenharmony_ci	switch (ssr) {
20498c2ecf20Sopenharmony_ci	case 0x8a:			/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED	*/
20508c2ecf20Sopenharmony_ci	case 0x1a:			/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED	*/
20518c2ecf20Sopenharmony_ci	    /* MESSAGE OUT -> COMMAND */
20528c2ecf20Sopenharmony_ci	    acornscsi_sendcommand(host);
20538c2ecf20Sopenharmony_ci	    break;
20548c2ecf20Sopenharmony_ci
20558c2ecf20Sopenharmony_ci	case 0x8b:			/* -> PHASE_STATUS				*/
20568c2ecf20Sopenharmony_ci	case 0x1b:			/* -> PHASE_STATUS				*/
20578c2ecf20Sopenharmony_ci	    /* MESSAGE OUT -> STATUS */
20588c2ecf20Sopenharmony_ci	    acornscsi_readstatusbyte(host);
20598c2ecf20Sopenharmony_ci	    host->scsi.phase = PHASE_STATUSIN;
20608c2ecf20Sopenharmony_ci	    break;
20618c2ecf20Sopenharmony_ci
20628c2ecf20Sopenharmony_ci	case 0x8e:			/* -> PHASE_MSGOUT				*/
20638c2ecf20Sopenharmony_ci	    /* MESSAGE_OUT(MESSAGE_IN) ->MESSAGE OUT */
20648c2ecf20Sopenharmony_ci	    acornscsi_sendmessage(host);
20658c2ecf20Sopenharmony_ci	    break;
20668c2ecf20Sopenharmony_ci
20678c2ecf20Sopenharmony_ci	case 0x4f:			/* -> PHASE_MSGIN, PHASE_DISCONNECT		*/
20688c2ecf20Sopenharmony_ci	case 0x1f:			/* -> PHASE_MSGIN, PHASE_DISCONNECT		*/
20698c2ecf20Sopenharmony_ci	    /* MESSAGE OUT -> MESSAGE IN */
20708c2ecf20Sopenharmony_ci	    acornscsi_message(host);
20718c2ecf20Sopenharmony_ci	    break;
20728c2ecf20Sopenharmony_ci
20738c2ecf20Sopenharmony_ci	default:
20748c2ecf20Sopenharmony_ci	    printk(KERN_ERR "scsi%d.%c: PHASE_MSGOUT, SSR %02X?\n",
20758c2ecf20Sopenharmony_ci		    host->host->host_no, acornscsi_target(host), ssr);
20768c2ecf20Sopenharmony_ci	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
20778c2ecf20Sopenharmony_ci	}
20788c2ecf20Sopenharmony_ci	return INTR_PROCESSING;
20798c2ecf20Sopenharmony_ci
20808c2ecf20Sopenharmony_ci    case PHASE_COMMAND: 		/* STATE: connected & command sent		*/
20818c2ecf20Sopenharmony_ci	switch (ssr) {
20828c2ecf20Sopenharmony_ci	case 0x18:			/* -> PHASE_DATAOUT				*/
20838c2ecf20Sopenharmony_ci	    /* COMMAND -> DATA OUT */
20848c2ecf20Sopenharmony_ci	    if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len)
20858c2ecf20Sopenharmony_ci		acornscsi_abortcmd(host, host->SCpnt->tag);
20868c2ecf20Sopenharmony_ci	    acornscsi_dma_setup(host, DMA_OUT);
20878c2ecf20Sopenharmony_ci	    if (!acornscsi_starttransfer(host))
20888c2ecf20Sopenharmony_ci		acornscsi_abortcmd(host, host->SCpnt->tag);
20898c2ecf20Sopenharmony_ci	    host->scsi.phase = PHASE_DATAOUT;
20908c2ecf20Sopenharmony_ci	    return INTR_IDLE;
20918c2ecf20Sopenharmony_ci
20928c2ecf20Sopenharmony_ci	case 0x19:			/* -> PHASE_DATAIN				*/
20938c2ecf20Sopenharmony_ci	    /* COMMAND -> DATA IN */
20948c2ecf20Sopenharmony_ci	    if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len)
20958c2ecf20Sopenharmony_ci		acornscsi_abortcmd(host, host->SCpnt->tag);
20968c2ecf20Sopenharmony_ci	    acornscsi_dma_setup(host, DMA_IN);
20978c2ecf20Sopenharmony_ci	    if (!acornscsi_starttransfer(host))
20988c2ecf20Sopenharmony_ci		acornscsi_abortcmd(host, host->SCpnt->tag);
20998c2ecf20Sopenharmony_ci	    host->scsi.phase = PHASE_DATAIN;
21008c2ecf20Sopenharmony_ci	    return INTR_IDLE;
21018c2ecf20Sopenharmony_ci
21028c2ecf20Sopenharmony_ci	case 0x1b:			/* -> PHASE_STATUS				*/
21038c2ecf20Sopenharmony_ci	    /* COMMAND -> STATUS */
21048c2ecf20Sopenharmony_ci	    acornscsi_readstatusbyte(host);
21058c2ecf20Sopenharmony_ci	    host->scsi.phase = PHASE_STATUSIN;
21068c2ecf20Sopenharmony_ci	    break;
21078c2ecf20Sopenharmony_ci
21088c2ecf20Sopenharmony_ci	case 0x1e:			/* -> PHASE_MSGOUT				*/
21098c2ecf20Sopenharmony_ci	    /* COMMAND -> MESSAGE OUT */
21108c2ecf20Sopenharmony_ci	    acornscsi_sendmessage(host);
21118c2ecf20Sopenharmony_ci	    break;
21128c2ecf20Sopenharmony_ci
21138c2ecf20Sopenharmony_ci	case 0x1f:			/* -> PHASE_MSGIN, PHASE_DISCONNECT		*/
21148c2ecf20Sopenharmony_ci	    /* COMMAND -> MESSAGE IN */
21158c2ecf20Sopenharmony_ci	    acornscsi_message(host);
21168c2ecf20Sopenharmony_ci	    break;
21178c2ecf20Sopenharmony_ci
21188c2ecf20Sopenharmony_ci	default:
21198c2ecf20Sopenharmony_ci	    printk(KERN_ERR "scsi%d.%c: PHASE_COMMAND, SSR %02X?\n",
21208c2ecf20Sopenharmony_ci		    host->host->host_no, acornscsi_target(host), ssr);
21218c2ecf20Sopenharmony_ci	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
21228c2ecf20Sopenharmony_ci	}
21238c2ecf20Sopenharmony_ci	return INTR_PROCESSING;
21248c2ecf20Sopenharmony_ci
21258c2ecf20Sopenharmony_ci    case PHASE_DISCONNECT:		/* STATE: connected, received DISCONNECT msg	*/
21268c2ecf20Sopenharmony_ci	if (ssr == 0x85) {		/* -> PHASE_IDLE				*/
21278c2ecf20Sopenharmony_ci	    host->scsi.disconnectable = 1;
21288c2ecf20Sopenharmony_ci	    host->scsi.reconnected.tag = 0;
21298c2ecf20Sopenharmony_ci	    host->scsi.phase = PHASE_IDLE;
21308c2ecf20Sopenharmony_ci	    host->stats.disconnects += 1;
21318c2ecf20Sopenharmony_ci	} else {
21328c2ecf20Sopenharmony_ci	    printk(KERN_ERR "scsi%d.%c: PHASE_DISCONNECT, SSR %02X instead of disconnect?\n",
21338c2ecf20Sopenharmony_ci		    host->host->host_no, acornscsi_target(host), ssr);
21348c2ecf20Sopenharmony_ci	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
21358c2ecf20Sopenharmony_ci	}
21368c2ecf20Sopenharmony_ci	return INTR_NEXT_COMMAND;
21378c2ecf20Sopenharmony_ci
21388c2ecf20Sopenharmony_ci    case PHASE_IDLE:			/* STATE: disconnected				*/
21398c2ecf20Sopenharmony_ci	if (ssr == 0x81)		/* -> PHASE_RECONNECTED or PHASE_ABORTED	*/
21408c2ecf20Sopenharmony_ci	    acornscsi_reconnect(host);
21418c2ecf20Sopenharmony_ci	else {
21428c2ecf20Sopenharmony_ci	    printk(KERN_ERR "scsi%d.%c: PHASE_IDLE, SSR %02X while idle?\n",
21438c2ecf20Sopenharmony_ci		    host->host->host_no, acornscsi_target(host), ssr);
21448c2ecf20Sopenharmony_ci	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
21458c2ecf20Sopenharmony_ci	}
21468c2ecf20Sopenharmony_ci	return INTR_PROCESSING;
21478c2ecf20Sopenharmony_ci
21488c2ecf20Sopenharmony_ci    case PHASE_RECONNECTED:		/* STATE: device reconnected to initiator	*/
21498c2ecf20Sopenharmony_ci	/*
21508c2ecf20Sopenharmony_ci	 * Command reconnected - if MESGIN, get message - it may be
21518c2ecf20Sopenharmony_ci	 * the tag.  If not, get command out of disconnected queue
21528c2ecf20Sopenharmony_ci	 */
21538c2ecf20Sopenharmony_ci	/*
21548c2ecf20Sopenharmony_ci	 * If we reconnected and we're not in MESSAGE IN phase after IDENTIFY,
21558c2ecf20Sopenharmony_ci	 * reconnect I_T_L command
21568c2ecf20Sopenharmony_ci	 */
21578c2ecf20Sopenharmony_ci	if (ssr != 0x8f && !acornscsi_reconnect_finish(host))
21588c2ecf20Sopenharmony_ci	    return INTR_IDLE;
21598c2ecf20Sopenharmony_ci	ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, in_irq);
21608c2ecf20Sopenharmony_ci	switch (ssr) {
21618c2ecf20Sopenharmony_ci	case 0x88:			/* data out phase				*/
21628c2ecf20Sopenharmony_ci					/* -> PHASE_DATAOUT				*/
21638c2ecf20Sopenharmony_ci	    /* MESSAGE IN -> DATA OUT */
21648c2ecf20Sopenharmony_ci	    acornscsi_dma_setup(host, DMA_OUT);
21658c2ecf20Sopenharmony_ci	    if (!acornscsi_starttransfer(host))
21668c2ecf20Sopenharmony_ci		acornscsi_abortcmd(host, host->SCpnt->tag);
21678c2ecf20Sopenharmony_ci	    host->scsi.phase = PHASE_DATAOUT;
21688c2ecf20Sopenharmony_ci	    return INTR_IDLE;
21698c2ecf20Sopenharmony_ci
21708c2ecf20Sopenharmony_ci	case 0x89:			/* data in phase				*/
21718c2ecf20Sopenharmony_ci					/* -> PHASE_DATAIN				*/
21728c2ecf20Sopenharmony_ci	    /* MESSAGE IN -> DATA IN */
21738c2ecf20Sopenharmony_ci	    acornscsi_dma_setup(host, DMA_IN);
21748c2ecf20Sopenharmony_ci	    if (!acornscsi_starttransfer(host))
21758c2ecf20Sopenharmony_ci		acornscsi_abortcmd(host, host->SCpnt->tag);
21768c2ecf20Sopenharmony_ci	    host->scsi.phase = PHASE_DATAIN;
21778c2ecf20Sopenharmony_ci	    return INTR_IDLE;
21788c2ecf20Sopenharmony_ci
21798c2ecf20Sopenharmony_ci	case 0x8a:			/* command out					*/
21808c2ecf20Sopenharmony_ci	    /* MESSAGE IN -> COMMAND */
21818c2ecf20Sopenharmony_ci	    acornscsi_sendcommand(host);/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED	*/
21828c2ecf20Sopenharmony_ci	    break;
21838c2ecf20Sopenharmony_ci
21848c2ecf20Sopenharmony_ci	case 0x8b:			/* status in					*/
21858c2ecf20Sopenharmony_ci					/* -> PHASE_STATUSIN				*/
21868c2ecf20Sopenharmony_ci	    /* MESSAGE IN -> STATUS */
21878c2ecf20Sopenharmony_ci	    acornscsi_readstatusbyte(host);
21888c2ecf20Sopenharmony_ci	    host->scsi.phase = PHASE_STATUSIN;
21898c2ecf20Sopenharmony_ci	    break;
21908c2ecf20Sopenharmony_ci
21918c2ecf20Sopenharmony_ci	case 0x8e:			/* message out					*/
21928c2ecf20Sopenharmony_ci					/* -> PHASE_MSGOUT				*/
21938c2ecf20Sopenharmony_ci	    /* MESSAGE IN -> MESSAGE OUT */
21948c2ecf20Sopenharmony_ci	    acornscsi_sendmessage(host);
21958c2ecf20Sopenharmony_ci	    break;
21968c2ecf20Sopenharmony_ci
21978c2ecf20Sopenharmony_ci	case 0x8f:			/* message in					*/
21988c2ecf20Sopenharmony_ci	    acornscsi_message(host);	/* -> PHASE_MSGIN, PHASE_DISCONNECT		*/
21998c2ecf20Sopenharmony_ci	    break;
22008c2ecf20Sopenharmony_ci
22018c2ecf20Sopenharmony_ci	default:
22028c2ecf20Sopenharmony_ci	    printk(KERN_ERR "scsi%d.%c: PHASE_RECONNECTED, SSR %02X after reconnect?\n",
22038c2ecf20Sopenharmony_ci		    host->host->host_no, acornscsi_target(host), ssr);
22048c2ecf20Sopenharmony_ci	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
22058c2ecf20Sopenharmony_ci	}
22068c2ecf20Sopenharmony_ci	return INTR_PROCESSING;
22078c2ecf20Sopenharmony_ci
22088c2ecf20Sopenharmony_ci    case PHASE_DATAIN:			/* STATE: transferred data in			*/
22098c2ecf20Sopenharmony_ci	/*
22108c2ecf20Sopenharmony_ci	 * This is simple - if we disconnect then the DMA address & count is
22118c2ecf20Sopenharmony_ci	 * correct.
22128c2ecf20Sopenharmony_ci	 */
22138c2ecf20Sopenharmony_ci	switch (ssr) {
22148c2ecf20Sopenharmony_ci	case 0x19:			/* -> PHASE_DATAIN				*/
22158c2ecf20Sopenharmony_ci	case 0x89:			/* -> PHASE_DATAIN				*/
22168c2ecf20Sopenharmony_ci	    acornscsi_abortcmd(host, host->SCpnt->tag);
22178c2ecf20Sopenharmony_ci	    return INTR_IDLE;
22188c2ecf20Sopenharmony_ci
22198c2ecf20Sopenharmony_ci	case 0x1b:			/* -> PHASE_STATUSIN				*/
22208c2ecf20Sopenharmony_ci	case 0x4b:			/* -> PHASE_STATUSIN				*/
22218c2ecf20Sopenharmony_ci	case 0x8b:			/* -> PHASE_STATUSIN				*/
22228c2ecf20Sopenharmony_ci	    /* DATA IN -> STATUS */
22238c2ecf20Sopenharmony_ci	    host->scsi.SCp.scsi_xferred = scsi_bufflen(host->SCpnt) -
22248c2ecf20Sopenharmony_ci					  acornscsi_sbic_xfcount(host);
22258c2ecf20Sopenharmony_ci	    acornscsi_dma_stop(host);
22268c2ecf20Sopenharmony_ci	    acornscsi_readstatusbyte(host);
22278c2ecf20Sopenharmony_ci	    host->scsi.phase = PHASE_STATUSIN;
22288c2ecf20Sopenharmony_ci	    break;
22298c2ecf20Sopenharmony_ci
22308c2ecf20Sopenharmony_ci	case 0x1e:			/* -> PHASE_MSGOUT				*/
22318c2ecf20Sopenharmony_ci	case 0x4e:			/* -> PHASE_MSGOUT				*/
22328c2ecf20Sopenharmony_ci	case 0x8e:			/* -> PHASE_MSGOUT				*/
22338c2ecf20Sopenharmony_ci	    /* DATA IN -> MESSAGE OUT */
22348c2ecf20Sopenharmony_ci	    host->scsi.SCp.scsi_xferred = scsi_bufflen(host->SCpnt) -
22358c2ecf20Sopenharmony_ci					  acornscsi_sbic_xfcount(host);
22368c2ecf20Sopenharmony_ci	    acornscsi_dma_stop(host);
22378c2ecf20Sopenharmony_ci	    acornscsi_sendmessage(host);
22388c2ecf20Sopenharmony_ci	    break;
22398c2ecf20Sopenharmony_ci
22408c2ecf20Sopenharmony_ci	case 0x1f:			/* message in					*/
22418c2ecf20Sopenharmony_ci	case 0x4f:			/* message in					*/
22428c2ecf20Sopenharmony_ci	case 0x8f:			/* message in					*/
22438c2ecf20Sopenharmony_ci	    /* DATA IN -> MESSAGE IN */
22448c2ecf20Sopenharmony_ci	    host->scsi.SCp.scsi_xferred = scsi_bufflen(host->SCpnt) -
22458c2ecf20Sopenharmony_ci					  acornscsi_sbic_xfcount(host);
22468c2ecf20Sopenharmony_ci	    acornscsi_dma_stop(host);
22478c2ecf20Sopenharmony_ci	    acornscsi_message(host);	/* -> PHASE_MSGIN, PHASE_DISCONNECT		*/
22488c2ecf20Sopenharmony_ci	    break;
22498c2ecf20Sopenharmony_ci
22508c2ecf20Sopenharmony_ci	default:
22518c2ecf20Sopenharmony_ci	    printk(KERN_ERR "scsi%d.%c: PHASE_DATAIN, SSR %02X?\n",
22528c2ecf20Sopenharmony_ci		    host->host->host_no, acornscsi_target(host), ssr);
22538c2ecf20Sopenharmony_ci	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
22548c2ecf20Sopenharmony_ci	}
22558c2ecf20Sopenharmony_ci	return INTR_PROCESSING;
22568c2ecf20Sopenharmony_ci
22578c2ecf20Sopenharmony_ci    case PHASE_DATAOUT: 		/* STATE: transferred data out			*/
22588c2ecf20Sopenharmony_ci	/*
22598c2ecf20Sopenharmony_ci	 * This is more complicated - if we disconnect, the DMA could be 12
22608c2ecf20Sopenharmony_ci	 * bytes ahead of us.  We need to correct this.
22618c2ecf20Sopenharmony_ci	 */
22628c2ecf20Sopenharmony_ci	switch (ssr) {
22638c2ecf20Sopenharmony_ci	case 0x18:			/* -> PHASE_DATAOUT				*/
22648c2ecf20Sopenharmony_ci	case 0x88:			/* -> PHASE_DATAOUT				*/
22658c2ecf20Sopenharmony_ci	    acornscsi_abortcmd(host, host->SCpnt->tag);
22668c2ecf20Sopenharmony_ci	    return INTR_IDLE;
22678c2ecf20Sopenharmony_ci
22688c2ecf20Sopenharmony_ci	case 0x1b:			/* -> PHASE_STATUSIN				*/
22698c2ecf20Sopenharmony_ci	case 0x4b:			/* -> PHASE_STATUSIN				*/
22708c2ecf20Sopenharmony_ci	case 0x8b:			/* -> PHASE_STATUSIN				*/
22718c2ecf20Sopenharmony_ci	    /* DATA OUT -> STATUS */
22728c2ecf20Sopenharmony_ci	    host->scsi.SCp.scsi_xferred = scsi_bufflen(host->SCpnt) -
22738c2ecf20Sopenharmony_ci					  acornscsi_sbic_xfcount(host);
22748c2ecf20Sopenharmony_ci	    acornscsi_dma_stop(host);
22758c2ecf20Sopenharmony_ci	    acornscsi_dma_adjust(host);
22768c2ecf20Sopenharmony_ci	    acornscsi_readstatusbyte(host);
22778c2ecf20Sopenharmony_ci	    host->scsi.phase = PHASE_STATUSIN;
22788c2ecf20Sopenharmony_ci	    break;
22798c2ecf20Sopenharmony_ci
22808c2ecf20Sopenharmony_ci	case 0x1e:			/* -> PHASE_MSGOUT				*/
22818c2ecf20Sopenharmony_ci	case 0x4e:			/* -> PHASE_MSGOUT				*/
22828c2ecf20Sopenharmony_ci	case 0x8e:			/* -> PHASE_MSGOUT				*/
22838c2ecf20Sopenharmony_ci	    /* DATA OUT -> MESSAGE OUT */
22848c2ecf20Sopenharmony_ci	    host->scsi.SCp.scsi_xferred = scsi_bufflen(host->SCpnt) -
22858c2ecf20Sopenharmony_ci					  acornscsi_sbic_xfcount(host);
22868c2ecf20Sopenharmony_ci	    acornscsi_dma_stop(host);
22878c2ecf20Sopenharmony_ci	    acornscsi_dma_adjust(host);
22888c2ecf20Sopenharmony_ci	    acornscsi_sendmessage(host);
22898c2ecf20Sopenharmony_ci	    break;
22908c2ecf20Sopenharmony_ci
22918c2ecf20Sopenharmony_ci	case 0x1f:			/* message in					*/
22928c2ecf20Sopenharmony_ci	case 0x4f:			/* message in					*/
22938c2ecf20Sopenharmony_ci	case 0x8f:			/* message in					*/
22948c2ecf20Sopenharmony_ci	    /* DATA OUT -> MESSAGE IN */
22958c2ecf20Sopenharmony_ci	    host->scsi.SCp.scsi_xferred = scsi_bufflen(host->SCpnt) -
22968c2ecf20Sopenharmony_ci					  acornscsi_sbic_xfcount(host);
22978c2ecf20Sopenharmony_ci	    acornscsi_dma_stop(host);
22988c2ecf20Sopenharmony_ci	    acornscsi_dma_adjust(host);
22998c2ecf20Sopenharmony_ci	    acornscsi_message(host);	/* -> PHASE_MSGIN, PHASE_DISCONNECT		*/
23008c2ecf20Sopenharmony_ci	    break;
23018c2ecf20Sopenharmony_ci
23028c2ecf20Sopenharmony_ci	default:
23038c2ecf20Sopenharmony_ci	    printk(KERN_ERR "scsi%d.%c: PHASE_DATAOUT, SSR %02X?\n",
23048c2ecf20Sopenharmony_ci		    host->host->host_no, acornscsi_target(host), ssr);
23058c2ecf20Sopenharmony_ci	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
23068c2ecf20Sopenharmony_ci	}
23078c2ecf20Sopenharmony_ci	return INTR_PROCESSING;
23088c2ecf20Sopenharmony_ci
23098c2ecf20Sopenharmony_ci    case PHASE_STATUSIN:		/* STATE: status in complete			*/
23108c2ecf20Sopenharmony_ci	switch (ssr) {
23118c2ecf20Sopenharmony_ci	case 0x1f:			/* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */
23128c2ecf20Sopenharmony_ci	case 0x8f:			/* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */
23138c2ecf20Sopenharmony_ci	    /* STATUS -> MESSAGE IN */
23148c2ecf20Sopenharmony_ci	    acornscsi_message(host);
23158c2ecf20Sopenharmony_ci	    break;
23168c2ecf20Sopenharmony_ci
23178c2ecf20Sopenharmony_ci	case 0x1e:			/* -> PHASE_MSGOUT				*/
23188c2ecf20Sopenharmony_ci	case 0x8e:			/* -> PHASE_MSGOUT				*/
23198c2ecf20Sopenharmony_ci	    /* STATUS -> MESSAGE OUT */
23208c2ecf20Sopenharmony_ci	    acornscsi_sendmessage(host);
23218c2ecf20Sopenharmony_ci	    break;
23228c2ecf20Sopenharmony_ci
23238c2ecf20Sopenharmony_ci	default:
23248c2ecf20Sopenharmony_ci	    printk(KERN_ERR "scsi%d.%c: PHASE_STATUSIN, SSR %02X instead of MESSAGE_IN?\n",
23258c2ecf20Sopenharmony_ci		    host->host->host_no, acornscsi_target(host), ssr);
23268c2ecf20Sopenharmony_ci	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
23278c2ecf20Sopenharmony_ci	}
23288c2ecf20Sopenharmony_ci	return INTR_PROCESSING;
23298c2ecf20Sopenharmony_ci
23308c2ecf20Sopenharmony_ci    case PHASE_MSGIN:			/* STATE: message in				*/
23318c2ecf20Sopenharmony_ci	switch (ssr) {
23328c2ecf20Sopenharmony_ci	case 0x1e:			/* -> PHASE_MSGOUT				*/
23338c2ecf20Sopenharmony_ci	case 0x4e:			/* -> PHASE_MSGOUT				*/
23348c2ecf20Sopenharmony_ci	case 0x8e:			/* -> PHASE_MSGOUT				*/
23358c2ecf20Sopenharmony_ci	    /* MESSAGE IN -> MESSAGE OUT */
23368c2ecf20Sopenharmony_ci	    acornscsi_sendmessage(host);
23378c2ecf20Sopenharmony_ci	    break;
23388c2ecf20Sopenharmony_ci
23398c2ecf20Sopenharmony_ci	case 0x1f:			/* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */
23408c2ecf20Sopenharmony_ci	case 0x2f:
23418c2ecf20Sopenharmony_ci	case 0x4f:
23428c2ecf20Sopenharmony_ci	case 0x8f:
23438c2ecf20Sopenharmony_ci	    acornscsi_message(host);
23448c2ecf20Sopenharmony_ci	    break;
23458c2ecf20Sopenharmony_ci
23468c2ecf20Sopenharmony_ci	case 0x85:
23478c2ecf20Sopenharmony_ci	    printk("scsi%d.%c: strange message in disconnection\n",
23488c2ecf20Sopenharmony_ci		host->host->host_no, acornscsi_target(host));
23498c2ecf20Sopenharmony_ci	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
23508c2ecf20Sopenharmony_ci	    acornscsi_done(host, &host->SCpnt, DID_ERROR);
23518c2ecf20Sopenharmony_ci	    break;
23528c2ecf20Sopenharmony_ci
23538c2ecf20Sopenharmony_ci	default:
23548c2ecf20Sopenharmony_ci	    printk(KERN_ERR "scsi%d.%c: PHASE_MSGIN, SSR %02X after message in?\n",
23558c2ecf20Sopenharmony_ci		    host->host->host_no, acornscsi_target(host), ssr);
23568c2ecf20Sopenharmony_ci	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
23578c2ecf20Sopenharmony_ci	}
23588c2ecf20Sopenharmony_ci	return INTR_PROCESSING;
23598c2ecf20Sopenharmony_ci
23608c2ecf20Sopenharmony_ci    case PHASE_DONE:			/* STATE: received status & message		*/
23618c2ecf20Sopenharmony_ci	switch (ssr) {
23628c2ecf20Sopenharmony_ci	case 0x85:			/* -> PHASE_IDLE				*/
23638c2ecf20Sopenharmony_ci	    acornscsi_done(host, &host->SCpnt, DID_OK);
23648c2ecf20Sopenharmony_ci	    return INTR_NEXT_COMMAND;
23658c2ecf20Sopenharmony_ci
23668c2ecf20Sopenharmony_ci	case 0x1e:
23678c2ecf20Sopenharmony_ci	case 0x8e:
23688c2ecf20Sopenharmony_ci	    acornscsi_sendmessage(host);
23698c2ecf20Sopenharmony_ci	    break;
23708c2ecf20Sopenharmony_ci
23718c2ecf20Sopenharmony_ci	default:
23728c2ecf20Sopenharmony_ci	    printk(KERN_ERR "scsi%d.%c: PHASE_DONE, SSR %02X instead of disconnect?\n",
23738c2ecf20Sopenharmony_ci		    host->host->host_no, acornscsi_target(host), ssr);
23748c2ecf20Sopenharmony_ci	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
23758c2ecf20Sopenharmony_ci	}
23768c2ecf20Sopenharmony_ci	return INTR_PROCESSING;
23778c2ecf20Sopenharmony_ci
23788c2ecf20Sopenharmony_ci    case PHASE_ABORTED:
23798c2ecf20Sopenharmony_ci	switch (ssr) {
23808c2ecf20Sopenharmony_ci	case 0x85:
23818c2ecf20Sopenharmony_ci	    if (host->SCpnt)
23828c2ecf20Sopenharmony_ci		acornscsi_done(host, &host->SCpnt, DID_ABORT);
23838c2ecf20Sopenharmony_ci	    else {
23848c2ecf20Sopenharmony_ci		clear_bit(host->scsi.reconnected.target * 8 + host->scsi.reconnected.lun,
23858c2ecf20Sopenharmony_ci			  host->busyluns);
23868c2ecf20Sopenharmony_ci		host->scsi.phase = PHASE_IDLE;
23878c2ecf20Sopenharmony_ci	    }
23888c2ecf20Sopenharmony_ci	    return INTR_NEXT_COMMAND;
23898c2ecf20Sopenharmony_ci
23908c2ecf20Sopenharmony_ci	case 0x1e:
23918c2ecf20Sopenharmony_ci	case 0x2e:
23928c2ecf20Sopenharmony_ci	case 0x4e:
23938c2ecf20Sopenharmony_ci	case 0x8e:
23948c2ecf20Sopenharmony_ci	    acornscsi_sendmessage(host);
23958c2ecf20Sopenharmony_ci	    break;
23968c2ecf20Sopenharmony_ci
23978c2ecf20Sopenharmony_ci	default:
23988c2ecf20Sopenharmony_ci	    printk(KERN_ERR "scsi%d.%c: PHASE_ABORTED, SSR %02X?\n",
23998c2ecf20Sopenharmony_ci		    host->host->host_no, acornscsi_target(host), ssr);
24008c2ecf20Sopenharmony_ci	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
24018c2ecf20Sopenharmony_ci	}
24028c2ecf20Sopenharmony_ci	return INTR_PROCESSING;
24038c2ecf20Sopenharmony_ci
24048c2ecf20Sopenharmony_ci    default:
24058c2ecf20Sopenharmony_ci	printk(KERN_ERR "scsi%d.%c: unknown driver phase %d\n",
24068c2ecf20Sopenharmony_ci		host->host->host_no, acornscsi_target(host), ssr);
24078c2ecf20Sopenharmony_ci	acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
24088c2ecf20Sopenharmony_ci    }
24098c2ecf20Sopenharmony_ci    return INTR_PROCESSING;
24108c2ecf20Sopenharmony_ci}
24118c2ecf20Sopenharmony_ci
24128c2ecf20Sopenharmony_ci/*
24138c2ecf20Sopenharmony_ci * Prototype: void acornscsi_intr(int irq, void *dev_id)
24148c2ecf20Sopenharmony_ci * Purpose  : handle interrupts from Acorn SCSI card
24158c2ecf20Sopenharmony_ci * Params   : irq    - interrupt number
24168c2ecf20Sopenharmony_ci *	      dev_id - device specific data (AS_Host structure)
24178c2ecf20Sopenharmony_ci */
24188c2ecf20Sopenharmony_cistatic irqreturn_t
24198c2ecf20Sopenharmony_ciacornscsi_intr(int irq, void *dev_id)
24208c2ecf20Sopenharmony_ci{
24218c2ecf20Sopenharmony_ci    AS_Host *host = (AS_Host *)dev_id;
24228c2ecf20Sopenharmony_ci    intr_ret_t ret;
24238c2ecf20Sopenharmony_ci    int iostatus;
24248c2ecf20Sopenharmony_ci    int in_irq = 0;
24258c2ecf20Sopenharmony_ci
24268c2ecf20Sopenharmony_ci    do {
24278c2ecf20Sopenharmony_ci	ret = INTR_IDLE;
24288c2ecf20Sopenharmony_ci
24298c2ecf20Sopenharmony_ci	iostatus = readb(host->fast + INT_REG);
24308c2ecf20Sopenharmony_ci
24318c2ecf20Sopenharmony_ci	if (iostatus & 2) {
24328c2ecf20Sopenharmony_ci	    acornscsi_dma_intr(host);
24338c2ecf20Sopenharmony_ci	    iostatus = readb(host->fast + INT_REG);
24348c2ecf20Sopenharmony_ci	}
24358c2ecf20Sopenharmony_ci
24368c2ecf20Sopenharmony_ci	if (iostatus & 8)
24378c2ecf20Sopenharmony_ci	    ret = acornscsi_sbicintr(host, in_irq);
24388c2ecf20Sopenharmony_ci
24398c2ecf20Sopenharmony_ci	/*
24408c2ecf20Sopenharmony_ci	 * If we have a transfer pending, start it.
24418c2ecf20Sopenharmony_ci	 * Only start it if the interface has already started transferring
24428c2ecf20Sopenharmony_ci	 * it's data
24438c2ecf20Sopenharmony_ci	 */
24448c2ecf20Sopenharmony_ci	if (host->dma.xfer_required)
24458c2ecf20Sopenharmony_ci	    acornscsi_dma_xfer(host);
24468c2ecf20Sopenharmony_ci
24478c2ecf20Sopenharmony_ci	if (ret == INTR_NEXT_COMMAND)
24488c2ecf20Sopenharmony_ci	    ret = acornscsi_kick(host);
24498c2ecf20Sopenharmony_ci
24508c2ecf20Sopenharmony_ci	in_irq = 1;
24518c2ecf20Sopenharmony_ci    } while (ret != INTR_IDLE);
24528c2ecf20Sopenharmony_ci
24538c2ecf20Sopenharmony_ci    return IRQ_HANDLED;
24548c2ecf20Sopenharmony_ci}
24558c2ecf20Sopenharmony_ci
24568c2ecf20Sopenharmony_ci/*=============================================================================================
24578c2ecf20Sopenharmony_ci * Interfaces between interrupt handler and rest of scsi code
24588c2ecf20Sopenharmony_ci */
24598c2ecf20Sopenharmony_ci
24608c2ecf20Sopenharmony_ci/*
24618c2ecf20Sopenharmony_ci * Function : acornscsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
24628c2ecf20Sopenharmony_ci * Purpose  : queues a SCSI command
24638c2ecf20Sopenharmony_ci * Params   : cmd  - SCSI command
24648c2ecf20Sopenharmony_ci *	      done - function called on completion, with pointer to command descriptor
24658c2ecf20Sopenharmony_ci * Returns  : 0, or < 0 on error.
24668c2ecf20Sopenharmony_ci */
24678c2ecf20Sopenharmony_cistatic int acornscsi_queuecmd_lck(struct scsi_cmnd *SCpnt,
24688c2ecf20Sopenharmony_ci		       void (*done)(struct scsi_cmnd *))
24698c2ecf20Sopenharmony_ci{
24708c2ecf20Sopenharmony_ci    AS_Host *host = (AS_Host *)SCpnt->device->host->hostdata;
24718c2ecf20Sopenharmony_ci
24728c2ecf20Sopenharmony_ci    if (!done) {
24738c2ecf20Sopenharmony_ci	/* there should be some way of rejecting errors like this without panicing... */
24748c2ecf20Sopenharmony_ci	panic("scsi%d: queuecommand called with NULL done function [cmd=%p]",
24758c2ecf20Sopenharmony_ci		host->host->host_no, SCpnt);
24768c2ecf20Sopenharmony_ci	return -EINVAL;
24778c2ecf20Sopenharmony_ci    }
24788c2ecf20Sopenharmony_ci
24798c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_NO_WRITE)
24808c2ecf20Sopenharmony_ci    if (acornscsi_cmdtype(SCpnt->cmnd[0]) == CMD_WRITE && (NO_WRITE & (1 << SCpnt->device->id))) {
24818c2ecf20Sopenharmony_ci	printk(KERN_CRIT "scsi%d.%c: WRITE attempted with NO_WRITE flag set\n",
24828c2ecf20Sopenharmony_ci	    host->host->host_no, '0' + SCpnt->device->id);
24838c2ecf20Sopenharmony_ci	SCpnt->result = DID_NO_CONNECT << 16;
24848c2ecf20Sopenharmony_ci	done(SCpnt);
24858c2ecf20Sopenharmony_ci	return 0;
24868c2ecf20Sopenharmony_ci    }
24878c2ecf20Sopenharmony_ci#endif
24888c2ecf20Sopenharmony_ci
24898c2ecf20Sopenharmony_ci    SCpnt->scsi_done = done;
24908c2ecf20Sopenharmony_ci    SCpnt->host_scribble = NULL;
24918c2ecf20Sopenharmony_ci    SCpnt->result = 0;
24928c2ecf20Sopenharmony_ci    SCpnt->tag = 0;
24938c2ecf20Sopenharmony_ci    SCpnt->SCp.phase = (int)acornscsi_datadirection(SCpnt->cmnd[0]);
24948c2ecf20Sopenharmony_ci    SCpnt->SCp.sent_command = 0;
24958c2ecf20Sopenharmony_ci    SCpnt->SCp.scsi_xferred = 0;
24968c2ecf20Sopenharmony_ci
24978c2ecf20Sopenharmony_ci    init_SCp(SCpnt);
24988c2ecf20Sopenharmony_ci
24998c2ecf20Sopenharmony_ci    host->stats.queues += 1;
25008c2ecf20Sopenharmony_ci
25018c2ecf20Sopenharmony_ci    {
25028c2ecf20Sopenharmony_ci	unsigned long flags;
25038c2ecf20Sopenharmony_ci
25048c2ecf20Sopenharmony_ci	if (!queue_add_cmd_ordered(&host->queues.issue, SCpnt)) {
25058c2ecf20Sopenharmony_ci	    SCpnt->result = DID_ERROR << 16;
25068c2ecf20Sopenharmony_ci	    done(SCpnt);
25078c2ecf20Sopenharmony_ci	    return 0;
25088c2ecf20Sopenharmony_ci	}
25098c2ecf20Sopenharmony_ci	local_irq_save(flags);
25108c2ecf20Sopenharmony_ci	if (host->scsi.phase == PHASE_IDLE)
25118c2ecf20Sopenharmony_ci	    acornscsi_kick(host);
25128c2ecf20Sopenharmony_ci	local_irq_restore(flags);
25138c2ecf20Sopenharmony_ci    }
25148c2ecf20Sopenharmony_ci    return 0;
25158c2ecf20Sopenharmony_ci}
25168c2ecf20Sopenharmony_ci
25178c2ecf20Sopenharmony_ciDEF_SCSI_QCMD(acornscsi_queuecmd)
25188c2ecf20Sopenharmony_ci
25198c2ecf20Sopenharmony_ci/*
25208c2ecf20Sopenharmony_ci * Prototype: void acornscsi_reportstatus(struct scsi_cmnd **SCpntp1, struct scsi_cmnd **SCpntp2, int result)
25218c2ecf20Sopenharmony_ci * Purpose  : pass a result to *SCpntp1, and check if *SCpntp1 = *SCpntp2
25228c2ecf20Sopenharmony_ci * Params   : SCpntp1 - pointer to command to return
25238c2ecf20Sopenharmony_ci *	      SCpntp2 - pointer to command to check
25248c2ecf20Sopenharmony_ci *	      result  - result to pass back to mid-level done function
25258c2ecf20Sopenharmony_ci * Returns  : *SCpntp2 = NULL if *SCpntp1 is the same command structure as *SCpntp2.
25268c2ecf20Sopenharmony_ci */
25278c2ecf20Sopenharmony_cistatic inline void acornscsi_reportstatus(struct scsi_cmnd **SCpntp1,
25288c2ecf20Sopenharmony_ci					  struct scsi_cmnd **SCpntp2,
25298c2ecf20Sopenharmony_ci					  int result)
25308c2ecf20Sopenharmony_ci{
25318c2ecf20Sopenharmony_ci	struct scsi_cmnd *SCpnt = *SCpntp1;
25328c2ecf20Sopenharmony_ci
25338c2ecf20Sopenharmony_ci    if (SCpnt) {
25348c2ecf20Sopenharmony_ci	*SCpntp1 = NULL;
25358c2ecf20Sopenharmony_ci
25368c2ecf20Sopenharmony_ci	SCpnt->result = result;
25378c2ecf20Sopenharmony_ci	SCpnt->scsi_done(SCpnt);
25388c2ecf20Sopenharmony_ci    }
25398c2ecf20Sopenharmony_ci
25408c2ecf20Sopenharmony_ci    if (SCpnt == *SCpntp2)
25418c2ecf20Sopenharmony_ci	*SCpntp2 = NULL;
25428c2ecf20Sopenharmony_ci}
25438c2ecf20Sopenharmony_ci
25448c2ecf20Sopenharmony_cienum res_abort { res_not_running, res_success, res_success_clear, res_snooze };
25458c2ecf20Sopenharmony_ci
25468c2ecf20Sopenharmony_ci/*
25478c2ecf20Sopenharmony_ci * Prototype: enum res acornscsi_do_abort(struct scsi_cmnd *SCpnt)
25488c2ecf20Sopenharmony_ci * Purpose  : abort a command on this host
25498c2ecf20Sopenharmony_ci * Params   : SCpnt - command to abort
25508c2ecf20Sopenharmony_ci * Returns  : our abort status
25518c2ecf20Sopenharmony_ci */
25528c2ecf20Sopenharmony_cistatic enum res_abort acornscsi_do_abort(AS_Host *host, struct scsi_cmnd *SCpnt)
25538c2ecf20Sopenharmony_ci{
25548c2ecf20Sopenharmony_ci	enum res_abort res = res_not_running;
25558c2ecf20Sopenharmony_ci
25568c2ecf20Sopenharmony_ci	if (queue_remove_cmd(&host->queues.issue, SCpnt)) {
25578c2ecf20Sopenharmony_ci		/*
25588c2ecf20Sopenharmony_ci		 * The command was on the issue queue, and has not been
25598c2ecf20Sopenharmony_ci		 * issued yet.  We can remove the command from the queue,
25608c2ecf20Sopenharmony_ci		 * and acknowledge the abort.  Neither the devices nor the
25618c2ecf20Sopenharmony_ci		 * interface know about the command.
25628c2ecf20Sopenharmony_ci		 */
25638c2ecf20Sopenharmony_ci//#if (DEBUG & DEBUG_ABORT)
25648c2ecf20Sopenharmony_ci		printk("on issue queue ");
25658c2ecf20Sopenharmony_ci//#endif
25668c2ecf20Sopenharmony_ci		res = res_success;
25678c2ecf20Sopenharmony_ci	} else if (queue_remove_cmd(&host->queues.disconnected, SCpnt)) {
25688c2ecf20Sopenharmony_ci		/*
25698c2ecf20Sopenharmony_ci		 * The command was on the disconnected queue.  Simply
25708c2ecf20Sopenharmony_ci		 * acknowledge the abort condition, and when the target
25718c2ecf20Sopenharmony_ci		 * reconnects, we will give it an ABORT message.  The
25728c2ecf20Sopenharmony_ci		 * target should then disconnect, and we will clear
25738c2ecf20Sopenharmony_ci		 * the busylun bit.
25748c2ecf20Sopenharmony_ci		 */
25758c2ecf20Sopenharmony_ci//#if (DEBUG & DEBUG_ABORT)
25768c2ecf20Sopenharmony_ci		printk("on disconnected queue ");
25778c2ecf20Sopenharmony_ci//#endif
25788c2ecf20Sopenharmony_ci		res = res_success;
25798c2ecf20Sopenharmony_ci	} else if (host->SCpnt == SCpnt) {
25808c2ecf20Sopenharmony_ci		unsigned long flags;
25818c2ecf20Sopenharmony_ci
25828c2ecf20Sopenharmony_ci//#if (DEBUG & DEBUG_ABORT)
25838c2ecf20Sopenharmony_ci		printk("executing ");
25848c2ecf20Sopenharmony_ci//#endif
25858c2ecf20Sopenharmony_ci
25868c2ecf20Sopenharmony_ci		local_irq_save(flags);
25878c2ecf20Sopenharmony_ci		switch (host->scsi.phase) {
25888c2ecf20Sopenharmony_ci		/*
25898c2ecf20Sopenharmony_ci		 * If the interface is idle, and the command is 'disconnectable',
25908c2ecf20Sopenharmony_ci		 * then it is the same as on the disconnected queue.  We simply
25918c2ecf20Sopenharmony_ci		 * remove all traces of the command.  When the target reconnects,
25928c2ecf20Sopenharmony_ci		 * we will give it an ABORT message since the command could not
25938c2ecf20Sopenharmony_ci		 * be found.  When the target finally disconnects, we will clear
25948c2ecf20Sopenharmony_ci		 * the busylun bit.
25958c2ecf20Sopenharmony_ci		 */
25968c2ecf20Sopenharmony_ci		case PHASE_IDLE:
25978c2ecf20Sopenharmony_ci			if (host->scsi.disconnectable) {
25988c2ecf20Sopenharmony_ci				host->scsi.disconnectable = 0;
25998c2ecf20Sopenharmony_ci				host->SCpnt = NULL;
26008c2ecf20Sopenharmony_ci				res = res_success;
26018c2ecf20Sopenharmony_ci			}
26028c2ecf20Sopenharmony_ci			break;
26038c2ecf20Sopenharmony_ci
26048c2ecf20Sopenharmony_ci		/*
26058c2ecf20Sopenharmony_ci		 * If the command has connected and done nothing further,
26068c2ecf20Sopenharmony_ci		 * simply force a disconnect.  We also need to clear the
26078c2ecf20Sopenharmony_ci		 * busylun bit.
26088c2ecf20Sopenharmony_ci		 */
26098c2ecf20Sopenharmony_ci		case PHASE_CONNECTED:
26108c2ecf20Sopenharmony_ci			sbic_arm_write(host, SBIC_CMND, CMND_DISCONNECT);
26118c2ecf20Sopenharmony_ci			host->SCpnt = NULL;
26128c2ecf20Sopenharmony_ci			res = res_success_clear;
26138c2ecf20Sopenharmony_ci			break;
26148c2ecf20Sopenharmony_ci
26158c2ecf20Sopenharmony_ci		default:
26168c2ecf20Sopenharmony_ci			acornscsi_abortcmd(host, host->SCpnt->tag);
26178c2ecf20Sopenharmony_ci			res = res_snooze;
26188c2ecf20Sopenharmony_ci		}
26198c2ecf20Sopenharmony_ci		local_irq_restore(flags);
26208c2ecf20Sopenharmony_ci	} else if (host->origSCpnt == SCpnt) {
26218c2ecf20Sopenharmony_ci		/*
26228c2ecf20Sopenharmony_ci		 * The command will be executed next, but a command
26238c2ecf20Sopenharmony_ci		 * is currently using the interface.  This is similar to
26248c2ecf20Sopenharmony_ci		 * being on the issue queue, except the busylun bit has
26258c2ecf20Sopenharmony_ci		 * been set.
26268c2ecf20Sopenharmony_ci		 */
26278c2ecf20Sopenharmony_ci		host->origSCpnt = NULL;
26288c2ecf20Sopenharmony_ci//#if (DEBUG & DEBUG_ABORT)
26298c2ecf20Sopenharmony_ci		printk("waiting for execution ");
26308c2ecf20Sopenharmony_ci//#endif
26318c2ecf20Sopenharmony_ci		res = res_success_clear;
26328c2ecf20Sopenharmony_ci	} else
26338c2ecf20Sopenharmony_ci		printk("unknown ");
26348c2ecf20Sopenharmony_ci
26358c2ecf20Sopenharmony_ci	return res;
26368c2ecf20Sopenharmony_ci}
26378c2ecf20Sopenharmony_ci
26388c2ecf20Sopenharmony_ci/*
26398c2ecf20Sopenharmony_ci * Prototype: int acornscsi_abort(struct scsi_cmnd *SCpnt)
26408c2ecf20Sopenharmony_ci * Purpose  : abort a command on this host
26418c2ecf20Sopenharmony_ci * Params   : SCpnt - command to abort
26428c2ecf20Sopenharmony_ci * Returns  : one of SCSI_ABORT_ macros
26438c2ecf20Sopenharmony_ci */
26448c2ecf20Sopenharmony_ciint acornscsi_abort(struct scsi_cmnd *SCpnt)
26458c2ecf20Sopenharmony_ci{
26468c2ecf20Sopenharmony_ci	AS_Host *host = (AS_Host *) SCpnt->device->host->hostdata;
26478c2ecf20Sopenharmony_ci	int result;
26488c2ecf20Sopenharmony_ci
26498c2ecf20Sopenharmony_ci	host->stats.aborts += 1;
26508c2ecf20Sopenharmony_ci
26518c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_ABORT)
26528c2ecf20Sopenharmony_ci	{
26538c2ecf20Sopenharmony_ci		int asr, ssr;
26548c2ecf20Sopenharmony_ci		asr = sbic_arm_read(host, SBIC_ASR);
26558c2ecf20Sopenharmony_ci		ssr = sbic_arm_read(host, SBIC_SSR);
26568c2ecf20Sopenharmony_ci
26578c2ecf20Sopenharmony_ci		printk(KERN_WARNING "acornscsi_abort: ");
26588c2ecf20Sopenharmony_ci		print_sbic_status(asr, ssr, host->scsi.phase);
26598c2ecf20Sopenharmony_ci		acornscsi_dumplog(host, SCpnt->device->id);
26608c2ecf20Sopenharmony_ci	}
26618c2ecf20Sopenharmony_ci#endif
26628c2ecf20Sopenharmony_ci
26638c2ecf20Sopenharmony_ci	printk("scsi%d: ", host->host->host_no);
26648c2ecf20Sopenharmony_ci
26658c2ecf20Sopenharmony_ci	switch (acornscsi_do_abort(host, SCpnt)) {
26668c2ecf20Sopenharmony_ci	/*
26678c2ecf20Sopenharmony_ci	 * We managed to find the command and cleared it out.
26688c2ecf20Sopenharmony_ci	 * We do not expect the command to be executing on the
26698c2ecf20Sopenharmony_ci	 * target, but we have set the busylun bit.
26708c2ecf20Sopenharmony_ci	 */
26718c2ecf20Sopenharmony_ci	case res_success_clear:
26728c2ecf20Sopenharmony_ci//#if (DEBUG & DEBUG_ABORT)
26738c2ecf20Sopenharmony_ci		printk("clear ");
26748c2ecf20Sopenharmony_ci//#endif
26758c2ecf20Sopenharmony_ci		clear_bit(SCpnt->device->id * 8 +
26768c2ecf20Sopenharmony_ci			  (u8)(SCpnt->device->lun & 0x7), host->busyluns);
26778c2ecf20Sopenharmony_ci
26788c2ecf20Sopenharmony_ci	/*
26798c2ecf20Sopenharmony_ci	 * We found the command, and cleared it out.  Either
26808c2ecf20Sopenharmony_ci	 * the command is still known to be executing on the
26818c2ecf20Sopenharmony_ci	 * target, or the busylun bit is not set.
26828c2ecf20Sopenharmony_ci	 */
26838c2ecf20Sopenharmony_ci	case res_success:
26848c2ecf20Sopenharmony_ci//#if (DEBUG & DEBUG_ABORT)
26858c2ecf20Sopenharmony_ci		printk("success\n");
26868c2ecf20Sopenharmony_ci//#endif
26878c2ecf20Sopenharmony_ci		result = SUCCESS;
26888c2ecf20Sopenharmony_ci		break;
26898c2ecf20Sopenharmony_ci
26908c2ecf20Sopenharmony_ci	/*
26918c2ecf20Sopenharmony_ci	 * We did find the command, but unfortunately we couldn't
26928c2ecf20Sopenharmony_ci	 * unhook it from ourselves.  Wait some more, and if it
26938c2ecf20Sopenharmony_ci	 * still doesn't complete, reset the interface.
26948c2ecf20Sopenharmony_ci	 */
26958c2ecf20Sopenharmony_ci	case res_snooze:
26968c2ecf20Sopenharmony_ci//#if (DEBUG & DEBUG_ABORT)
26978c2ecf20Sopenharmony_ci		printk("snooze\n");
26988c2ecf20Sopenharmony_ci//#endif
26998c2ecf20Sopenharmony_ci		result = FAILED;
27008c2ecf20Sopenharmony_ci		break;
27018c2ecf20Sopenharmony_ci
27028c2ecf20Sopenharmony_ci	/*
27038c2ecf20Sopenharmony_ci	 * The command could not be found (either because it completed,
27048c2ecf20Sopenharmony_ci	 * or it got dropped.
27058c2ecf20Sopenharmony_ci	 */
27068c2ecf20Sopenharmony_ci	default:
27078c2ecf20Sopenharmony_ci	case res_not_running:
27088c2ecf20Sopenharmony_ci		acornscsi_dumplog(host, SCpnt->device->id);
27098c2ecf20Sopenharmony_ci		result = FAILED;
27108c2ecf20Sopenharmony_ci//#if (DEBUG & DEBUG_ABORT)
27118c2ecf20Sopenharmony_ci		printk("not running\n");
27128c2ecf20Sopenharmony_ci//#endif
27138c2ecf20Sopenharmony_ci		break;
27148c2ecf20Sopenharmony_ci	}
27158c2ecf20Sopenharmony_ci
27168c2ecf20Sopenharmony_ci	return result;
27178c2ecf20Sopenharmony_ci}
27188c2ecf20Sopenharmony_ci
27198c2ecf20Sopenharmony_ci/*
27208c2ecf20Sopenharmony_ci * Prototype: int acornscsi_reset(struct scsi_cmnd *SCpnt)
27218c2ecf20Sopenharmony_ci * Purpose  : reset a command on this host/reset this host
27228c2ecf20Sopenharmony_ci * Params   : SCpnt  - command causing reset
27238c2ecf20Sopenharmony_ci * Returns  : one of SCSI_RESET_ macros
27248c2ecf20Sopenharmony_ci */
27258c2ecf20Sopenharmony_ciint acornscsi_host_reset(struct scsi_cmnd *SCpnt)
27268c2ecf20Sopenharmony_ci{
27278c2ecf20Sopenharmony_ci	AS_Host *host = (AS_Host *)SCpnt->device->host->hostdata;
27288c2ecf20Sopenharmony_ci	struct scsi_cmnd *SCptr;
27298c2ecf20Sopenharmony_ci
27308c2ecf20Sopenharmony_ci    host->stats.resets += 1;
27318c2ecf20Sopenharmony_ci
27328c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_RESET)
27338c2ecf20Sopenharmony_ci    {
27348c2ecf20Sopenharmony_ci	int asr, ssr, devidx;
27358c2ecf20Sopenharmony_ci
27368c2ecf20Sopenharmony_ci	asr = sbic_arm_read(host, SBIC_ASR);
27378c2ecf20Sopenharmony_ci	ssr = sbic_arm_read(host, SBIC_SSR);
27388c2ecf20Sopenharmony_ci
27398c2ecf20Sopenharmony_ci	printk(KERN_WARNING "acornscsi_reset: ");
27408c2ecf20Sopenharmony_ci	print_sbic_status(asr, ssr, host->scsi.phase);
27418c2ecf20Sopenharmony_ci	for (devidx = 0; devidx < 9; devidx++)
27428c2ecf20Sopenharmony_ci	    acornscsi_dumplog(host, devidx);
27438c2ecf20Sopenharmony_ci    }
27448c2ecf20Sopenharmony_ci#endif
27458c2ecf20Sopenharmony_ci
27468c2ecf20Sopenharmony_ci    acornscsi_dma_stop(host);
27478c2ecf20Sopenharmony_ci
27488c2ecf20Sopenharmony_ci    /*
27498c2ecf20Sopenharmony_ci     * do hard reset.  This resets all devices on this host, and so we
27508c2ecf20Sopenharmony_ci     * must set the reset status on all commands.
27518c2ecf20Sopenharmony_ci     */
27528c2ecf20Sopenharmony_ci    acornscsi_resetcard(host);
27538c2ecf20Sopenharmony_ci
27548c2ecf20Sopenharmony_ci    while ((SCptr = queue_remove(&host->queues.disconnected)) != NULL)
27558c2ecf20Sopenharmony_ci	;
27568c2ecf20Sopenharmony_ci
27578c2ecf20Sopenharmony_ci    return SUCCESS;
27588c2ecf20Sopenharmony_ci}
27598c2ecf20Sopenharmony_ci
27608c2ecf20Sopenharmony_ci/*==============================================================================================
27618c2ecf20Sopenharmony_ci * initialisation & miscellaneous support
27628c2ecf20Sopenharmony_ci */
27638c2ecf20Sopenharmony_ci
27648c2ecf20Sopenharmony_ci/*
27658c2ecf20Sopenharmony_ci * Function: char *acornscsi_info(struct Scsi_Host *host)
27668c2ecf20Sopenharmony_ci * Purpose : return a string describing this interface
27678c2ecf20Sopenharmony_ci * Params  : host - host to give information on
27688c2ecf20Sopenharmony_ci * Returns : a constant string
27698c2ecf20Sopenharmony_ci */
27708c2ecf20Sopenharmony_ciconst
27718c2ecf20Sopenharmony_cichar *acornscsi_info(struct Scsi_Host *host)
27728c2ecf20Sopenharmony_ci{
27738c2ecf20Sopenharmony_ci    static char string[100], *p;
27748c2ecf20Sopenharmony_ci
27758c2ecf20Sopenharmony_ci    p = string;
27768c2ecf20Sopenharmony_ci
27778c2ecf20Sopenharmony_ci    p += sprintf(string, "%s at port %08lX irq %d v%d.%d.%d"
27788c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_ACORNSCSI_SYNC
27798c2ecf20Sopenharmony_ci    " SYNC"
27808c2ecf20Sopenharmony_ci#endif
27818c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
27828c2ecf20Sopenharmony_ci    " TAG"
27838c2ecf20Sopenharmony_ci#endif
27848c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_NO_WRITE)
27858c2ecf20Sopenharmony_ci    " NOWRITE (" __stringify(NO_WRITE) ")"
27868c2ecf20Sopenharmony_ci#endif
27878c2ecf20Sopenharmony_ci		, host->hostt->name, host->io_port, host->irq,
27888c2ecf20Sopenharmony_ci		VER_MAJOR, VER_MINOR, VER_PATCH);
27898c2ecf20Sopenharmony_ci    return string;
27908c2ecf20Sopenharmony_ci}
27918c2ecf20Sopenharmony_ci
27928c2ecf20Sopenharmony_cistatic int acornscsi_show_info(struct seq_file *m, struct Scsi_Host *instance)
27938c2ecf20Sopenharmony_ci{
27948c2ecf20Sopenharmony_ci    int devidx;
27958c2ecf20Sopenharmony_ci    struct scsi_device *scd;
27968c2ecf20Sopenharmony_ci    AS_Host *host;
27978c2ecf20Sopenharmony_ci
27988c2ecf20Sopenharmony_ci    host  = (AS_Host *)instance->hostdata;
27998c2ecf20Sopenharmony_ci
28008c2ecf20Sopenharmony_ci    seq_printf(m, "AcornSCSI driver v%d.%d.%d"
28018c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_ACORNSCSI_SYNC
28028c2ecf20Sopenharmony_ci    " SYNC"
28038c2ecf20Sopenharmony_ci#endif
28048c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
28058c2ecf20Sopenharmony_ci    " TAG"
28068c2ecf20Sopenharmony_ci#endif
28078c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_NO_WRITE)
28088c2ecf20Sopenharmony_ci    " NOWRITE (" __stringify(NO_WRITE) ")"
28098c2ecf20Sopenharmony_ci#endif
28108c2ecf20Sopenharmony_ci		"\n\n", VER_MAJOR, VER_MINOR, VER_PATCH);
28118c2ecf20Sopenharmony_ci
28128c2ecf20Sopenharmony_ci    seq_printf(m,	"SBIC: WD33C93A  Address: %p    IRQ : %d\n",
28138c2ecf20Sopenharmony_ci			host->base + SBIC_REGIDX, host->scsi.irq);
28148c2ecf20Sopenharmony_ci#ifdef USE_DMAC
28158c2ecf20Sopenharmony_ci    seq_printf(m,	"DMAC: uPC71071  Address: %p  IRQ : %d\n\n",
28168c2ecf20Sopenharmony_ci			host->base + DMAC_OFFSET, host->scsi.irq);
28178c2ecf20Sopenharmony_ci#endif
28188c2ecf20Sopenharmony_ci
28198c2ecf20Sopenharmony_ci    seq_printf(m,	"Statistics:\n"
28208c2ecf20Sopenharmony_ci			"Queued commands: %-10u    Issued commands: %-10u\n"
28218c2ecf20Sopenharmony_ci			"Done commands  : %-10u    Reads          : %-10u\n"
28228c2ecf20Sopenharmony_ci			"Writes         : %-10u    Others         : %-10u\n"
28238c2ecf20Sopenharmony_ci			"Disconnects    : %-10u    Aborts         : %-10u\n"
28248c2ecf20Sopenharmony_ci			"Resets         : %-10u\n\nLast phases:",
28258c2ecf20Sopenharmony_ci			host->stats.queues,		host->stats.removes,
28268c2ecf20Sopenharmony_ci			host->stats.fins,		host->stats.reads,
28278c2ecf20Sopenharmony_ci			host->stats.writes,		host->stats.miscs,
28288c2ecf20Sopenharmony_ci			host->stats.disconnects,	host->stats.aborts,
28298c2ecf20Sopenharmony_ci			host->stats.resets);
28308c2ecf20Sopenharmony_ci
28318c2ecf20Sopenharmony_ci    for (devidx = 0; devidx < 9; devidx ++) {
28328c2ecf20Sopenharmony_ci	unsigned int statptr, prev;
28338c2ecf20Sopenharmony_ci
28348c2ecf20Sopenharmony_ci	seq_printf(m, "\n%c:", devidx == 8 ? 'H' : ('0' + devidx));
28358c2ecf20Sopenharmony_ci	statptr = host->status_ptr[devidx] - 10;
28368c2ecf20Sopenharmony_ci
28378c2ecf20Sopenharmony_ci	if ((signed int)statptr < 0)
28388c2ecf20Sopenharmony_ci	    statptr += STATUS_BUFFER_SIZE;
28398c2ecf20Sopenharmony_ci
28408c2ecf20Sopenharmony_ci	prev = host->status[devidx][statptr].when;
28418c2ecf20Sopenharmony_ci
28428c2ecf20Sopenharmony_ci	for (; statptr != host->status_ptr[devidx]; statptr = (statptr + 1) & (STATUS_BUFFER_SIZE - 1)) {
28438c2ecf20Sopenharmony_ci	    if (host->status[devidx][statptr].when) {
28448c2ecf20Sopenharmony_ci		seq_printf(m, "%c%02X:%02X+%2ld",
28458c2ecf20Sopenharmony_ci			host->status[devidx][statptr].irq ? '-' : ' ',
28468c2ecf20Sopenharmony_ci			host->status[devidx][statptr].ph,
28478c2ecf20Sopenharmony_ci			host->status[devidx][statptr].ssr,
28488c2ecf20Sopenharmony_ci			(host->status[devidx][statptr].when - prev) < 100 ?
28498c2ecf20Sopenharmony_ci				(host->status[devidx][statptr].when - prev) : 99);
28508c2ecf20Sopenharmony_ci		prev = host->status[devidx][statptr].when;
28518c2ecf20Sopenharmony_ci	    }
28528c2ecf20Sopenharmony_ci	}
28538c2ecf20Sopenharmony_ci    }
28548c2ecf20Sopenharmony_ci
28558c2ecf20Sopenharmony_ci    seq_printf(m, "\nAttached devices:\n");
28568c2ecf20Sopenharmony_ci
28578c2ecf20Sopenharmony_ci    shost_for_each_device(scd, instance) {
28588c2ecf20Sopenharmony_ci	seq_printf(m, "Device/Lun TaggedQ      Sync\n");
28598c2ecf20Sopenharmony_ci	seq_printf(m, "     %d/%llu   ", scd->id, scd->lun);
28608c2ecf20Sopenharmony_ci	if (scd->tagged_supported)
28618c2ecf20Sopenharmony_ci		seq_printf(m, "%3sabled(%3d) ",
28628c2ecf20Sopenharmony_ci			     scd->simple_tags ? "en" : "dis",
28638c2ecf20Sopenharmony_ci			     scd->current_tag);
28648c2ecf20Sopenharmony_ci	else
28658c2ecf20Sopenharmony_ci		seq_printf(m, "unsupported  ");
28668c2ecf20Sopenharmony_ci
28678c2ecf20Sopenharmony_ci	if (host->device[scd->id].sync_xfer & 15)
28688c2ecf20Sopenharmony_ci		seq_printf(m, "offset %d, %d ns\n",
28698c2ecf20Sopenharmony_ci			     host->device[scd->id].sync_xfer & 15,
28708c2ecf20Sopenharmony_ci			     acornscsi_getperiod(host->device[scd->id].sync_xfer));
28718c2ecf20Sopenharmony_ci	else
28728c2ecf20Sopenharmony_ci		seq_printf(m, "async\n");
28738c2ecf20Sopenharmony_ci
28748c2ecf20Sopenharmony_ci    }
28758c2ecf20Sopenharmony_ci    return 0;
28768c2ecf20Sopenharmony_ci}
28778c2ecf20Sopenharmony_ci
28788c2ecf20Sopenharmony_cistatic struct scsi_host_template acornscsi_template = {
28798c2ecf20Sopenharmony_ci	.module			= THIS_MODULE,
28808c2ecf20Sopenharmony_ci	.show_info		= acornscsi_show_info,
28818c2ecf20Sopenharmony_ci	.name			= "AcornSCSI",
28828c2ecf20Sopenharmony_ci	.info			= acornscsi_info,
28838c2ecf20Sopenharmony_ci	.queuecommand		= acornscsi_queuecmd,
28848c2ecf20Sopenharmony_ci	.eh_abort_handler	= acornscsi_abort,
28858c2ecf20Sopenharmony_ci	.eh_host_reset_handler	= acornscsi_host_reset,
28868c2ecf20Sopenharmony_ci	.can_queue		= 16,
28878c2ecf20Sopenharmony_ci	.this_id		= 7,
28888c2ecf20Sopenharmony_ci	.sg_tablesize		= SG_ALL,
28898c2ecf20Sopenharmony_ci	.cmd_per_lun		= 2,
28908c2ecf20Sopenharmony_ci	.dma_boundary		= PAGE_SIZE - 1,
28918c2ecf20Sopenharmony_ci	.proc_name		= "acornscsi",
28928c2ecf20Sopenharmony_ci};
28938c2ecf20Sopenharmony_ci
28948c2ecf20Sopenharmony_cistatic int acornscsi_probe(struct expansion_card *ec, const struct ecard_id *id)
28958c2ecf20Sopenharmony_ci{
28968c2ecf20Sopenharmony_ci	struct Scsi_Host *host;
28978c2ecf20Sopenharmony_ci	AS_Host *ashost;
28988c2ecf20Sopenharmony_ci	int ret;
28998c2ecf20Sopenharmony_ci
29008c2ecf20Sopenharmony_ci	ret = ecard_request_resources(ec);
29018c2ecf20Sopenharmony_ci	if (ret)
29028c2ecf20Sopenharmony_ci		goto out;
29038c2ecf20Sopenharmony_ci
29048c2ecf20Sopenharmony_ci	host = scsi_host_alloc(&acornscsi_template, sizeof(AS_Host));
29058c2ecf20Sopenharmony_ci	if (!host) {
29068c2ecf20Sopenharmony_ci		ret = -ENOMEM;
29078c2ecf20Sopenharmony_ci		goto out_release;
29088c2ecf20Sopenharmony_ci	}
29098c2ecf20Sopenharmony_ci
29108c2ecf20Sopenharmony_ci	ashost = (AS_Host *)host->hostdata;
29118c2ecf20Sopenharmony_ci
29128c2ecf20Sopenharmony_ci	ashost->base = ecardm_iomap(ec, ECARD_RES_MEMC, 0, 0);
29138c2ecf20Sopenharmony_ci	ashost->fast = ecardm_iomap(ec, ECARD_RES_IOCFAST, 0, 0);
29148c2ecf20Sopenharmony_ci	if (!ashost->base || !ashost->fast) {
29158c2ecf20Sopenharmony_ci		ret = -ENOMEM;
29168c2ecf20Sopenharmony_ci		goto out_put;
29178c2ecf20Sopenharmony_ci	}
29188c2ecf20Sopenharmony_ci
29198c2ecf20Sopenharmony_ci	host->irq = ec->irq;
29208c2ecf20Sopenharmony_ci	ashost->host = host;
29218c2ecf20Sopenharmony_ci	ashost->scsi.irq = host->irq;
29228c2ecf20Sopenharmony_ci
29238c2ecf20Sopenharmony_ci	ec->irqaddr	= ashost->fast + INT_REG;
29248c2ecf20Sopenharmony_ci	ec->irqmask	= 0x0a;
29258c2ecf20Sopenharmony_ci
29268c2ecf20Sopenharmony_ci	ret = request_irq(host->irq, acornscsi_intr, 0, "acornscsi", ashost);
29278c2ecf20Sopenharmony_ci	if (ret) {
29288c2ecf20Sopenharmony_ci		printk(KERN_CRIT "scsi%d: IRQ%d not free: %d\n",
29298c2ecf20Sopenharmony_ci			host->host_no, ashost->scsi.irq, ret);
29308c2ecf20Sopenharmony_ci		goto out_put;
29318c2ecf20Sopenharmony_ci	}
29328c2ecf20Sopenharmony_ci
29338c2ecf20Sopenharmony_ci	memset(&ashost->stats, 0, sizeof (ashost->stats));
29348c2ecf20Sopenharmony_ci	queue_initialise(&ashost->queues.issue);
29358c2ecf20Sopenharmony_ci	queue_initialise(&ashost->queues.disconnected);
29368c2ecf20Sopenharmony_ci	msgqueue_initialise(&ashost->scsi.msgs);
29378c2ecf20Sopenharmony_ci
29388c2ecf20Sopenharmony_ci	acornscsi_resetcard(ashost);
29398c2ecf20Sopenharmony_ci
29408c2ecf20Sopenharmony_ci	ret = scsi_add_host(host, &ec->dev);
29418c2ecf20Sopenharmony_ci	if (ret)
29428c2ecf20Sopenharmony_ci		goto out_irq;
29438c2ecf20Sopenharmony_ci
29448c2ecf20Sopenharmony_ci	scsi_scan_host(host);
29458c2ecf20Sopenharmony_ci	goto out;
29468c2ecf20Sopenharmony_ci
29478c2ecf20Sopenharmony_ci out_irq:
29488c2ecf20Sopenharmony_ci	free_irq(host->irq, ashost);
29498c2ecf20Sopenharmony_ci	msgqueue_free(&ashost->scsi.msgs);
29508c2ecf20Sopenharmony_ci	queue_free(&ashost->queues.disconnected);
29518c2ecf20Sopenharmony_ci	queue_free(&ashost->queues.issue);
29528c2ecf20Sopenharmony_ci out_put:
29538c2ecf20Sopenharmony_ci	ecardm_iounmap(ec, ashost->fast);
29548c2ecf20Sopenharmony_ci	ecardm_iounmap(ec, ashost->base);
29558c2ecf20Sopenharmony_ci	scsi_host_put(host);
29568c2ecf20Sopenharmony_ci out_release:
29578c2ecf20Sopenharmony_ci	ecard_release_resources(ec);
29588c2ecf20Sopenharmony_ci out:
29598c2ecf20Sopenharmony_ci	return ret;
29608c2ecf20Sopenharmony_ci}
29618c2ecf20Sopenharmony_ci
29628c2ecf20Sopenharmony_cistatic void acornscsi_remove(struct expansion_card *ec)
29638c2ecf20Sopenharmony_ci{
29648c2ecf20Sopenharmony_ci	struct Scsi_Host *host = ecard_get_drvdata(ec);
29658c2ecf20Sopenharmony_ci	AS_Host *ashost = (AS_Host *)host->hostdata;
29668c2ecf20Sopenharmony_ci
29678c2ecf20Sopenharmony_ci	ecard_set_drvdata(ec, NULL);
29688c2ecf20Sopenharmony_ci	scsi_remove_host(host);
29698c2ecf20Sopenharmony_ci
29708c2ecf20Sopenharmony_ci	/*
29718c2ecf20Sopenharmony_ci	 * Put card into RESET state
29728c2ecf20Sopenharmony_ci	 */
29738c2ecf20Sopenharmony_ci	writeb(0x80, ashost->fast + PAGE_REG);
29748c2ecf20Sopenharmony_ci
29758c2ecf20Sopenharmony_ci	free_irq(host->irq, ashost);
29768c2ecf20Sopenharmony_ci
29778c2ecf20Sopenharmony_ci	msgqueue_free(&ashost->scsi.msgs);
29788c2ecf20Sopenharmony_ci	queue_free(&ashost->queues.disconnected);
29798c2ecf20Sopenharmony_ci	queue_free(&ashost->queues.issue);
29808c2ecf20Sopenharmony_ci	ecardm_iounmap(ec, ashost->fast);
29818c2ecf20Sopenharmony_ci	ecardm_iounmap(ec, ashost->base);
29828c2ecf20Sopenharmony_ci	scsi_host_put(host);
29838c2ecf20Sopenharmony_ci	ecard_release_resources(ec);
29848c2ecf20Sopenharmony_ci}
29858c2ecf20Sopenharmony_ci
29868c2ecf20Sopenharmony_cistatic const struct ecard_id acornscsi_cids[] = {
29878c2ecf20Sopenharmony_ci	{ MANU_ACORN, PROD_ACORN_SCSI },
29888c2ecf20Sopenharmony_ci	{ 0xffff, 0xffff },
29898c2ecf20Sopenharmony_ci};
29908c2ecf20Sopenharmony_ci
29918c2ecf20Sopenharmony_cistatic struct ecard_driver acornscsi_driver = {
29928c2ecf20Sopenharmony_ci	.probe		= acornscsi_probe,
29938c2ecf20Sopenharmony_ci	.remove		= acornscsi_remove,
29948c2ecf20Sopenharmony_ci	.id_table	= acornscsi_cids,
29958c2ecf20Sopenharmony_ci	.drv = {
29968c2ecf20Sopenharmony_ci		.name		= "acornscsi",
29978c2ecf20Sopenharmony_ci	},
29988c2ecf20Sopenharmony_ci};
29998c2ecf20Sopenharmony_ci
30008c2ecf20Sopenharmony_cistatic int __init acornscsi_init(void)
30018c2ecf20Sopenharmony_ci{
30028c2ecf20Sopenharmony_ci	return ecard_register_driver(&acornscsi_driver);
30038c2ecf20Sopenharmony_ci}
30048c2ecf20Sopenharmony_ci
30058c2ecf20Sopenharmony_cistatic void __exit acornscsi_exit(void)
30068c2ecf20Sopenharmony_ci{
30078c2ecf20Sopenharmony_ci	ecard_remove_driver(&acornscsi_driver);
30088c2ecf20Sopenharmony_ci}
30098c2ecf20Sopenharmony_ci
30108c2ecf20Sopenharmony_cimodule_init(acornscsi_init);
30118c2ecf20Sopenharmony_cimodule_exit(acornscsi_exit);
30128c2ecf20Sopenharmony_ci
30138c2ecf20Sopenharmony_ciMODULE_AUTHOR("Russell King");
30148c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("AcornSCSI driver");
30158c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
3016