[RFC: 2.6 patch] remove the broken SCSI_ACORNSCSI_3 driver

From: Adrian Bunk
Date: Sun Jul 29 2007 - 11:10:23 EST


The SCSI_ACORNSCSI_3 driver:
- has been marked as BROKEN for more than one year and
- is still marked as BROKEN.

Drivers that had been marked as BROKEN for such a long time seem to be
unlikely to be revived in the forseeable future.

But if anyone wants to ever revive this driver, the code is still
present in the older kernel releases.

Signed-off-by: Adrian Bunk <bunk@xxxxxxxxx>

---

This patch has been sent on:
- 20 Feb 2007
- 21 Jan 2007
- 6 Jan 2007

drivers/scsi/arm/Kconfig | 29
drivers/scsi/arm/Makefile | 3
drivers/scsi/arm/acornscsi-io.S | 145 -
drivers/scsi/arm/acornscsi.c | 3130 --------------------------------
drivers/scsi/arm/acornscsi.h | 358 ---
5 files changed, 3665 deletions(-)

--- linux-2.6.20-rc3-mm1/drivers/scsi/arm/Kconfig.old 2007-01-06 20:40:26.000000000 +0100
+++ linux-2.6.20-rc3-mm1/drivers/scsi/arm/Kconfig 2007-01-06 20:49:49.000000000 +0100
@@ -1,35 +1,6 @@
#
# SCSI driver configuration for Acorn
#
-config SCSI_ACORNSCSI_3
- tristate "Acorn SCSI card (aka30) support"
- depends on ARCH_ACORN && SCSI && BROKEN
- select SCSI_SPI_ATTRS
- help
- This enables support for the Acorn SCSI card (aka30). If you have an
- Acorn system with one of these, say Y. If unsure, say N.
-
-config SCSI_ACORNSCSI_TAGGED_QUEUE
- bool "Support SCSI 2 Tagged queueing"
- depends on SCSI_ACORNSCSI_3
- help
- Say Y here to enable tagged queuing support on the Acorn SCSI card.
-
- This is a feature of SCSI-2 which improves performance: the host
- adapter can send several SCSI commands to a device's queue even if
- previous commands haven't finished yet. Some SCSI devices don't
- implement this properly, so the safe answer is N.
-
-config SCSI_ACORNSCSI_SYNC
- bool "Support SCSI 2 Synchronous Transfers"
- depends on SCSI_ACORNSCSI_3
- help
- Say Y here to enable synchronous transfer negotiation with all
- targets on the Acorn SCSI card.
-
- In general, this improves performance; however some SCSI devices
- don't implement it properly, so the safe answer is N.
-
config SCSI_ARXESCSI
tristate "ARXE SCSI support"
depends on ARCH_ACORN && SCSI
--- linux-2.6.20-rc3-mm1/drivers/scsi/arm/Makefile.old 2007-01-06 20:50:54.000000000 +0100
+++ linux-2.6.20-rc3-mm1/drivers/scsi/arm/Makefile 2007-01-06 20:51:50.000000000 +0100
@@ -2,9 +2,6 @@
# Makefile for drivers/scsi/arm
#

-acornscsi_mod-objs := acornscsi.o acornscsi-io.o
-
-obj-$(CONFIG_SCSI_ACORNSCSI_3) += acornscsi_mod.o queue.o msgqueue.o
obj-$(CONFIG_SCSI_ARXESCSI) += arxescsi.o fas216.o queue.o msgqueue.o
obj-$(CONFIG_SCSI_CUMANA_1) += cumana_1.o
obj-$(CONFIG_SCSI_CUMANA_2) += cumana_2.o fas216.o queue.o msgqueue.o
--- linux-2.6.20-rc3-mm1/drivers/scsi/arm/acornscsi.h 2006-11-29 22:57:37.000000000 +0100
+++ /dev/null 2006-09-19 00:45:31.000000000 +0200
@@ -1,358 +0,0 @@
-/*
- * linux/drivers/acorn/scsi/acornscsi.h
- *
- * Copyright (C) 1997 Russell King
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Acorn SCSI driver
- */
-#ifndef ACORNSCSI_H
-#define ACORNSCSI_H
-
-/* SBIC registers */
-#define SBIC_OWNID 0
-#define OWNID_FS1 (1<<7)
-#define OWNID_FS2 (1<<6)
-#define OWNID_EHP (1<<4)
-#define OWNID_EAF (1<<3)
-
-#define SBIC_CTRL 1
-#define CTRL_DMAMODE (1<<7)
-#define CTRL_DMADBAMODE (1<<6)
-#define CTRL_DMABURST (1<<5)
-#define CTRL_DMAPOLLED 0
-#define CTRL_HHP (1<<4)
-#define CTRL_EDI (1<<3)
-#define CTRL_IDI (1<<2)
-#define CTRL_HA (1<<1)
-#define CTRL_HSP (1<<0)
-
-#define SBIC_TIMEOUT 2
-#define SBIC_TOTSECTS 3
-#define SBIC_TOTHEADS 4
-#define SBIC_TOTCYLH 5
-#define SBIC_TOTCYLL 6
-#define SBIC_LOGADDRH 7
-#define SBIC_LOGADDRM2 8
-#define SBIC_LOGADDRM1 9
-#define SBIC_LOGADDRL 10
-#define SBIC_SECTORNUM 11
-#define SBIC_HEADNUM 12
-#define SBIC_CYLH 13
-#define SBIC_CYLL 14
-#define SBIC_TARGETLUN 15
-#define TARGETLUN_TLV (1<<7)
-#define TARGETLUN_DOK (1<<6)
-
-#define SBIC_CMNDPHASE 16
-#define SBIC_SYNCHTRANSFER 17
-#define SYNCHTRANSFER_OF0 0x00
-#define SYNCHTRANSFER_OF1 0x01
-#define SYNCHTRANSFER_OF2 0x02
-#define SYNCHTRANSFER_OF3 0x03
-#define SYNCHTRANSFER_OF4 0x04
-#define SYNCHTRANSFER_OF5 0x05
-#define SYNCHTRANSFER_OF6 0x06
-#define SYNCHTRANSFER_OF7 0x07
-#define SYNCHTRANSFER_OF8 0x08
-#define SYNCHTRANSFER_OF9 0x09
-#define SYNCHTRANSFER_OF10 0x0A
-#define SYNCHTRANSFER_OF11 0x0B
-#define SYNCHTRANSFER_OF12 0x0C
-#define SYNCHTRANSFER_8DBA 0x00
-#define SYNCHTRANSFER_2DBA 0x20
-#define SYNCHTRANSFER_3DBA 0x30
-#define SYNCHTRANSFER_4DBA 0x40
-#define SYNCHTRANSFER_5DBA 0x50
-#define SYNCHTRANSFER_6DBA 0x60
-#define SYNCHTRANSFER_7DBA 0x70
-
-#define SBIC_TRANSCNTH 18
-#define SBIC_TRANSCNTM 19
-#define SBIC_TRANSCNTL 20
-#define SBIC_DESTID 21
-#define DESTID_SCC (1<<7)
-#define DESTID_DPD (1<<6)
-
-#define SBIC_SOURCEID 22
-#define SOURCEID_ER (1<<7)
-#define SOURCEID_ES (1<<6)
-#define SOURCEID_DSP (1<<5)
-#define SOURCEID_SIV (1<<4)
-
-#define SBIC_SSR 23
-#define SBIC_CMND 24
-#define CMND_RESET 0x00
-#define CMND_ABORT 0x01
-#define CMND_ASSERTATN 0x02
-#define CMND_NEGATEACK 0x03
-#define CMND_DISCONNECT 0x04
-#define CMND_RESELECT 0x05
-#define CMND_SELWITHATN 0x06
-#define CMND_SELECT 0x07
-#define CMND_SELECTATNTRANSFER 0x08
-#define CMND_SELECTTRANSFER 0x09
-#define CMND_RESELECTRXDATA 0x0A
-#define CMND_RESELECTTXDATA 0x0B
-#define CMND_WAITFORSELRECV 0x0C
-#define CMND_SENDSTATCMD 0x0D
-#define CMND_SENDDISCONNECT 0x0E
-#define CMND_SETIDI 0x0F
-#define CMND_RECEIVECMD 0x10
-#define CMND_RECEIVEDTA 0x11
-#define CMND_RECEIVEMSG 0x12
-#define CMND_RECEIVEUSP 0x13
-#define CMND_SENDCMD 0x14
-#define CMND_SENDDATA 0x15
-#define CMND_SENDMSG 0x16
-#define CMND_SENDUSP 0x17
-#define CMND_TRANSLATEADDR 0x18
-#define CMND_XFERINFO 0x20
-#define CMND_SBT (1<<7)
-
-#define SBIC_DATA 25
-#define SBIC_ASR 26
-#define ASR_INT (1<<7)
-#define ASR_LCI (1<<6)
-#define ASR_BSY (1<<5)
-#define ASR_CIP (1<<4)
-#define ASR_PE (1<<1)
-#define ASR_DBR (1<<0)
-
-/* DMAC registers */
-#define DMAC_INIT 0x00
-#define INIT_8BIT (1)
-
-#define DMAC_CHANNEL 0x80
-#define CHANNEL_0 0x00
-#define CHANNEL_1 0x01
-#define CHANNEL_2 0x02
-#define CHANNEL_3 0x03
-
-#define DMAC_TXCNTLO 0x01
-#define DMAC_TXCNTHI 0x81
-#define DMAC_TXADRLO 0x02
-#define DMAC_TXADRMD 0x82
-#define DMAC_TXADRHI 0x03
-
-#define DMAC_DEVCON0 0x04
-#define DEVCON0_AKL (1<<7)
-#define DEVCON0_RQL (1<<6)
-#define DEVCON0_EXW (1<<5)
-#define DEVCON0_ROT (1<<4)
-#define DEVCON0_CMP (1<<3)
-#define DEVCON0_DDMA (1<<2)
-#define DEVCON0_AHLD (1<<1)
-#define DEVCON0_MTM (1<<0)
-
-#define DMAC_DEVCON1 0x84
-#define DEVCON1_WEV (1<<1)
-#define DEVCON1_BHLD (1<<0)
-
-#define DMAC_MODECON 0x05
-#define MODECON_WOED 0x01
-#define MODECON_VERIFY 0x00
-#define MODECON_READ 0x04
-#define MODECON_WRITE 0x08
-#define MODECON_AUTOINIT 0x10
-#define MODECON_ADDRDIR 0x20
-#define MODECON_DEMAND 0x00
-#define MODECON_SINGLE 0x40
-#define MODECON_BLOCK 0x80
-#define MODECON_CASCADE 0xC0
-
-#define DMAC_STATUS 0x85
-#define STATUS_TC0 (1<<0)
-#define STATUS_RQ0 (1<<4)
-
-#define DMAC_TEMPLO 0x06
-#define DMAC_TEMPHI 0x86
-#define DMAC_REQREG 0x07
-#define DMAC_MASKREG 0x87
-#define MASKREG_M0 0x01
-#define MASKREG_M1 0x02
-#define MASKREG_M2 0x04
-#define MASKREG_M3 0x08
-
-/* miscellaneous internal variables */
-
-#define POD_SPACE(x) ((x) + 0xd0000)
-#define MASK_ON (MASKREG_M3|MASKREG_M2|MASKREG_M1|MASKREG_M0)
-#define MASK_OFF (MASKREG_M3|MASKREG_M2|MASKREG_M1)
-
-/*
- * SCSI driver phases
- */
-typedef enum {
- PHASE_IDLE, /* we're not planning on doing anything */
- PHASE_CONNECTING, /* connecting to a target */
- PHASE_CONNECTED, /* connected to a target */
- PHASE_MSGOUT, /* message out to device */
- PHASE_RECONNECTED, /* reconnected */
- PHASE_COMMANDPAUSED, /* command partly sent */
- PHASE_COMMAND, /* command all sent */
- PHASE_DATAOUT, /* data out to device */
- PHASE_DATAIN, /* data in from device */
- PHASE_STATUSIN, /* status in from device */
- PHASE_MSGIN, /* message in from device */
- PHASE_DONE, /* finished */
- PHASE_ABORTED, /* aborted */
- PHASE_DISCONNECT, /* disconnecting */
-} phase_t;
-
-/*
- * After interrupt, what to do now
- */
-typedef enum {
- INTR_IDLE, /* not expecting another IRQ */
- INTR_NEXT_COMMAND, /* start next command */
- INTR_PROCESSING, /* interrupt routine still processing */
-} intr_ret_t;
-
-/*
- * DMA direction
- */
-typedef enum {
- DMA_OUT, /* DMA from memory to chip */
- DMA_IN /* DMA from chip to memory */
-} dmadir_t;
-
-/*
- * Synchronous transfer state
- */
-typedef enum { /* Synchronous transfer state */
- SYNC_ASYNCHRONOUS, /* don't negociate synchronous transfers*/
- SYNC_NEGOCIATE, /* start negociation */
- SYNC_SENT_REQUEST, /* sent SDTR message */
- SYNC_COMPLETED, /* received SDTR reply */
-} syncxfer_t;
-
-/*
- * Command type
- */
-typedef enum { /* command type */
- CMD_READ, /* READ_6, READ_10, READ_12 */
- CMD_WRITE, /* WRITE_6, WRITE_10, WRITE_12 */
- CMD_MISC, /* Others */
-} cmdtype_t;
-
-/*
- * Data phase direction
- */
-typedef enum { /* Data direction */
- DATADIR_IN, /* Data in phase expected */
- DATADIR_OUT /* Data out phase expected */
-} datadir_t;
-
-#include "queue.h"
-#include "msgqueue.h"
-
-#define STATUS_BUFFER_SIZE 32
-/*
- * This is used to dump the previous states of the SBIC
- */
-struct status_entry {
- unsigned long when;
- unsigned char ssr;
- unsigned char ph;
- unsigned char irq;
- unsigned char unused;
-};
-
-#define ADD_STATUS(_q,_ssr,_ph,_irq) \
-({ \
- host->status[(_q)][host->status_ptr[(_q)]].when = jiffies; \
- host->status[(_q)][host->status_ptr[(_q)]].ssr = (_ssr); \
- host->status[(_q)][host->status_ptr[(_q)]].ph = (_ph); \
- host->status[(_q)][host->status_ptr[(_q)]].irq = (_irq); \
- host->status_ptr[(_q)] = (host->status_ptr[(_q)] + 1) & (STATUS_BUFFER_SIZE - 1); \
-})
-
-/*
- * AcornSCSI host specific data
- */
-typedef struct acornscsi_hostdata {
- /* miscellaneous */
- struct Scsi_Host *host; /* host */
- struct scsi_cmnd *SCpnt; /* currently processing command */
- struct scsi_cmnd *origSCpnt; /* original connecting command */
-
- /* driver information */
- struct {
- unsigned int io_port; /* base address of WD33C93 */
- unsigned int irq; /* interrupt */
- phase_t phase; /* current phase */
-
- struct {
- unsigned char target; /* reconnected target */
- unsigned char lun; /* reconnected lun */
- unsigned char tag; /* reconnected tag */
- } reconnected;
-
- struct scsi_pointer SCp; /* current commands data pointer */
-
- MsgQueue_t msgs;
-
- unsigned short last_message; /* last message to be sent */
- unsigned char disconnectable:1; /* this command can be disconnected */
- } scsi;
-
- /* statistics information */
- struct {
- unsigned int queues;
- unsigned int removes;
- unsigned int fins;
- unsigned int reads;
- unsigned int writes;
- unsigned int miscs;
- unsigned int disconnects;
- unsigned int aborts;
- unsigned int resets;
- } stats;
-
- /* queue handling */
- struct {
- Queue_t issue; /* issue queue */
- Queue_t disconnected; /* disconnected command queue */
- } queues;
-
- /* per-device info */
- struct {
- unsigned char sync_xfer; /* synchronous transfer (SBIC value) */
- syncxfer_t sync_state; /* sync xfer negociation state */
- unsigned char disconnect_ok:1; /* device can disconnect */
- } device[8];
- unsigned long busyluns[64 / sizeof(unsigned long)];/* array of bits indicating LUNs busy */
-
- /* DMA info */
- struct {
- unsigned int io_port; /* base address of DMA controller */
- unsigned int io_intr_clear; /* address of DMA interrupt clear */
- unsigned int free_addr; /* next free address */
- unsigned int start_addr; /* start address of current transfer */
- dmadir_t direction; /* dma direction */
- unsigned int transferred; /* number of bytes transferred */
- unsigned int xfer_start; /* scheduled DMA transfer start */
- unsigned int xfer_length; /* scheduled DMA transfer length */
- char *xfer_ptr; /* pointer to area */
- unsigned char xfer_required:1; /* set if we need to transfer something */
- unsigned char xfer_setup:1; /* set if DMA is setup */
- unsigned char xfer_done:1; /* set if DMA reached end of BH list */
- } dma;
-
- /* card info */
- struct {
- unsigned int io_intr; /* base address of interrupt id reg */
- unsigned int io_page; /* base address of page reg */
- unsigned int io_ram; /* base address of RAM access */
- unsigned char page_reg; /* current setting of page reg */
- } card;
-
- unsigned char status_ptr[9];
- struct status_entry status[9][STATUS_BUFFER_SIZE];
-} AS_Host;
-
-#endif /* ACORNSCSI_H */
--- linux-2.6.20-rc3-mm1/drivers/scsi/arm/acornscsi.c 2007-01-05 14:25:47.000000000 +0100
+++ /dev/null 2006-09-19 00:45:31.000000000 +0200
@@ -1,3130 +0,0 @@
-/*
- * linux/drivers/acorn/scsi/acornscsi.c
- *
- * Acorn SCSI 3 driver
- * By R.M.King.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Abandoned using the Select and Transfer command since there were
- * some nasty races between our software and the target devices that
- * were not easy to solve, and the device errata had a lot of entries
- * for this command, some of them quite nasty...
- *
- * Changelog:
- * 26-Sep-1997 RMK Re-jigged to use the queue module.
- * Re-coded state machine to be based on driver
- * state not scsi state. Should be easier to debug.
- * Added acornscsi_release to clean up properly.
- * Updated proc/scsi reporting.
- * 05-Oct-1997 RMK Implemented writing to SCSI devices.
- * 06-Oct-1997 RMK Corrected small (non-serious) bug with the connect/
- * reconnect race condition causing a warning message.
- * 12-Oct-1997 RMK Added catch for re-entering interrupt routine.
- * 15-Oct-1997 RMK Improved handling of commands.
- * 27-Jun-1998 RMK Changed asm/delay.h to linux/delay.h.
- * 13-Dec-1998 RMK Better abort code and command handling. Extra state
- * transitions added to allow dodgy devices to work.
- */
-#define DEBUG_NO_WRITE 1
-#define DEBUG_QUEUES 2
-#define DEBUG_DMA 4
-#define DEBUG_ABORT 8
-#define DEBUG_DISCON 16
-#define DEBUG_CONNECT 32
-#define DEBUG_PHASES 64
-#define DEBUG_WRITE 128
-#define DEBUG_LINK 256
-#define DEBUG_MESSAGES 512
-#define DEBUG_RESET 1024
-#define DEBUG_ALL (DEBUG_RESET|DEBUG_MESSAGES|DEBUG_LINK|DEBUG_WRITE|\
- DEBUG_PHASES|DEBUG_CONNECT|DEBUG_DISCON|DEBUG_ABORT|\
- DEBUG_DMA|DEBUG_QUEUES)
-
-/* DRIVER CONFIGURATION
- *
- * SCSI-II Tagged queue support.
- *
- * I don't have any SCSI devices that support it, so it is totally untested
- * (except to make sure that it doesn't interfere with any non-tagging
- * devices). It is not fully implemented either - what happens when a
- * tagging device reconnects???
- *
- * You can tell if you have a device that supports tagged queueing my
- * cating (eg) /proc/scsi/acornscsi/0 and see if the SCSI revision is reported
- * as '2 TAG'.
- *
- * Also note that CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE is normally set in the config
- * scripts, but disabled here. Once debugged, remove the #undef, otherwise to debug,
- * comment out the undef.
- */
-#undef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
-/*
- * SCSI-II Linked command support.
- *
- * The higher level code doesn't support linked commands yet, and so the option
- * is undef'd here.
- */
-#undef CONFIG_SCSI_ACORNSCSI_LINK
-/*
- * SCSI-II Synchronous transfer support.
- *
- * Tried and tested...
- *
- * SDTR_SIZE - maximum number of un-acknowledged bytes (0 = off, 12 = max)
- * SDTR_PERIOD - period of REQ signal (min=125, max=1020)
- * DEFAULT_PERIOD - default REQ period.
- */
-#define SDTR_SIZE 12
-#define SDTR_PERIOD 125
-#define DEFAULT_PERIOD 500
-
-/*
- * Debugging information
- *
- * DEBUG - bit mask from list above
- * DEBUG_TARGET - is defined to the target number if you want to debug
- * a specific target. [only recon/write/dma].
- */
-#define DEBUG (DEBUG_RESET|DEBUG_WRITE|DEBUG_NO_WRITE)
-/* only allow writing to SCSI device 0 */
-#define NO_WRITE 0xFE
-/*#define DEBUG_TARGET 2*/
-/*
- * Select timeout time (in 10ms units)
- *
- * This is the timeout used between the start of selection and the WD33C93
- * chip deciding that the device isn't responding.
- */
-#define TIMEOUT_TIME 10
-/*
- * Define this if you want to have verbose explaination of SCSI
- * status/messages.
- */
-#undef CONFIG_ACORNSCSI_CONSTANTS
-/*
- * Define this if you want to use the on board DMAC [don't remove this option]
- * If not set, then use PIO mode (not currently supported).
- */
-#define USE_DMAC
-
-/*
- * ====================================================================================
- */
-
-#ifdef DEBUG_TARGET
-#define DBG(cmd,xxx...) \
- if (cmd->device->id == DEBUG_TARGET) { \
- xxx; \
- }
-#else
-#define DBG(cmd,xxx...) xxx
-#endif
-
-#ifndef STRINGIFY
-#define STRINGIFY(x) #x
-#endif
-#define STRx(x) STRINGIFY(x)
-#define NO_WRITE_STR STRx(NO_WRITE)
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/signal.h>
-#include <linux/errno.h>
-#include <linux/proc_fs.h>
-#include <linux/ioport.h>
-#include <linux/blkdev.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/bitops.h>
-
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/ecard.h>
-
-#include "../scsi.h"
-#include <scsi/scsi_dbg.h>
-#include <scsi/scsi_host.h>
-#include <scsi/scsi_transport_spi.h>
-#include "acornscsi.h"
-#include "msgqueue.h"
-#include "scsi.h"
-
-#include <scsi/scsicam.h>
-
-#define VER_MAJOR 2
-#define VER_MINOR 0
-#define VER_PATCH 6
-
-#ifndef ABORT_TAG
-#define ABORT_TAG 0xd
-#else
-#error "Yippee! ABORT TAG is now defined! Remove this error!"
-#endif
-
-#ifdef CONFIG_SCSI_ACORNSCSI_LINK
-#error SCSI2 LINKed commands not supported (yet)!
-#endif
-
-#ifdef USE_DMAC
-/*
- * DMAC setup parameters
- */
-#define INIT_DEVCON0 (DEVCON0_RQL|DEVCON0_EXW|DEVCON0_CMP)
-#define INIT_DEVCON1 (DEVCON1_BHLD)
-#define DMAC_READ (MODECON_READ)
-#define DMAC_WRITE (MODECON_WRITE)
-#define INIT_SBICDMA (CTRL_DMABURST)
-
-#define scsi_xferred have_data_in
-
-/*
- * Size of on-board DMA buffer
- */
-#define DMAC_BUFFER_SIZE 65536
-#endif
-
-#define STATUS_BUFFER_TO_PRINT 24
-
-unsigned int sdtr_period = SDTR_PERIOD;
-unsigned int sdtr_size = SDTR_SIZE;
-
-static void acornscsi_done(AS_Host *host, struct scsi_cmnd **SCpntp,
- unsigned int result);
-static int acornscsi_reconnect_finish(AS_Host *host);
-static void acornscsi_dma_cleanup(AS_Host *host);
-static void acornscsi_abortcmd(AS_Host *host, unsigned char tag);
-
-/* ====================================================================================
- * Miscellaneous
- */
-
-static inline void
-sbic_arm_write(unsigned int io_port, int reg, int value)
-{
- __raw_writeb(reg, io_port);
- __raw_writeb(value, io_port + 4);
-}
-
-#define sbic_arm_writenext(io,val) \
- __raw_writeb((val), (io) + 4)
-
-static inline
-int sbic_arm_read(unsigned int io_port, int reg)
-{
- if(reg == SBIC_ASR)
- return __raw_readl(io_port) & 255;
- __raw_writeb(reg, io_port);
- return __raw_readl(io_port + 4) & 255;
-}
-
-#define sbic_arm_readnext(io) \
- __raw_readb((io) + 4)
-
-#ifdef USE_DMAC
-#define dmac_read(io_port,reg) \
- inb((io_port) + (reg))
-
-#define dmac_write(io_port,reg,value) \
- ({ outb((value), (io_port) + (reg)); })
-
-#define dmac_clearintr(io_port) \
- ({ outb(0, (io_port)); })
-
-static inline
-unsigned int dmac_address(unsigned int io_port)
-{
- return dmac_read(io_port, DMAC_TXADRHI) << 16 |
- dmac_read(io_port, DMAC_TXADRMD) << 8 |
- dmac_read(io_port, DMAC_TXADRLO);
-}
-
-static
-void acornscsi_dumpdma(AS_Host *host, char *where)
-{
- unsigned int mode, addr, len;
-
- mode = dmac_read(host->dma.io_port, DMAC_MODECON);
- addr = dmac_address(host->dma.io_port);
- len = dmac_read(host->dma.io_port, DMAC_TXCNTHI) << 8 |
- dmac_read(host->dma.io_port, DMAC_TXCNTLO);
-
- printk("scsi%d: %s: DMAC %02x @%06x+%04x msk %02x, ",
- host->host->host_no, where,
- mode, addr, (len + 1) & 0xffff,
- dmac_read(host->dma.io_port, DMAC_MASKREG));
-
- printk("DMA @%06x, ", host->dma.start_addr);
- printk("BH @%p +%04x, ", host->scsi.SCp.ptr,
- host->scsi.SCp.this_residual);
- printk("DT @+%04x ST @+%04x", host->dma.transferred,
- host->scsi.SCp.scsi_xferred);
- printk("\n");
-}
-#endif
-
-static
-unsigned long acornscsi_sbic_xfcount(AS_Host *host)
-{
- unsigned long length;
-
- length = sbic_arm_read(host->scsi.io_port, SBIC_TRANSCNTH) << 16;
- length |= sbic_arm_readnext(host->scsi.io_port) << 8;
- length |= sbic_arm_readnext(host->scsi.io_port);
-
- return length;
-}
-
-static int
-acornscsi_sbic_wait(AS_Host *host, int stat_mask, int stat, int timeout, char *msg)
-{
- int asr;
-
- do {
- asr = sbic_arm_read(host->scsi.io_port, SBIC_ASR);
-
- if ((asr & stat_mask) == stat)
- return 0;
-
- udelay(1);
- } while (--timeout);
-
- printk("scsi%d: timeout while %s\n", host->host->host_no, msg);
-
- return -1;
-}
-
-static
-int acornscsi_sbic_issuecmd(AS_Host *host, int command)
-{
- if (acornscsi_sbic_wait(host, ASR_CIP, 0, 1000, "issuing command"))
- return -1;
-
- sbic_arm_write(host->scsi.io_port, SBIC_CMND, command);
-
- return 0;
-}
-
-static void
-acornscsi_csdelay(unsigned int cs)
-{
- unsigned long target_jiffies, flags;
-
- target_jiffies = jiffies + 1 + cs * HZ / 100;
-
- local_save_flags(flags);
- local_irq_enable();
-
- while (time_before(jiffies, target_jiffies)) barrier();
-
- local_irq_restore(flags);
-}
-
-static
-void acornscsi_resetcard(AS_Host *host)
-{
- unsigned int i, timeout;
-
- /* assert reset line */
- host->card.page_reg = 0x80;
- outb(host->card.page_reg, host->card.io_page);
-
- /* wait 3 cs. SCSI standard says 25ms. */
- acornscsi_csdelay(3);
-
- host->card.page_reg = 0;
- outb(host->card.page_reg, host->card.io_page);
-
- /*
- * Should get a reset from the card
- */
- timeout = 1000;
- do {
- if (inb(host->card.io_intr) & 8)
- break;
- udelay(1);
- } while (--timeout);
-
- if (timeout == 0)
- printk("scsi%d: timeout while resetting card\n",
- host->host->host_no);
-
- sbic_arm_read(host->scsi.io_port, SBIC_ASR);
- sbic_arm_read(host->scsi.io_port, SBIC_SSR);
-
- /* setup sbic - WD33C93A */
- sbic_arm_write(host->scsi.io_port, SBIC_OWNID, OWNID_EAF | host->host->this_id);
- sbic_arm_write(host->scsi.io_port, SBIC_CMND, CMND_RESET);
-
- /*
- * Command should cause a reset interrupt
- */
- timeout = 1000;
- do {
- if (inb(host->card.io_intr) & 8)
- break;
- udelay(1);
- } while (--timeout);
-
- if (timeout == 0)
- printk("scsi%d: timeout while resetting card\n",
- host->host->host_no);
-
- sbic_arm_read(host->scsi.io_port, SBIC_ASR);
- if (sbic_arm_read(host->scsi.io_port, SBIC_SSR) != 0x01)
- printk(KERN_CRIT "scsi%d: WD33C93A didn't give enhanced reset interrupt\n",
- host->host->host_no);
-
- sbic_arm_write(host->scsi.io_port, SBIC_CTRL, INIT_SBICDMA | CTRL_IDI);
- sbic_arm_write(host->scsi.io_port, SBIC_TIMEOUT, TIMEOUT_TIME);
- sbic_arm_write(host->scsi.io_port, SBIC_SYNCHTRANSFER, SYNCHTRANSFER_2DBA);
- sbic_arm_write(host->scsi.io_port, SBIC_SOURCEID, SOURCEID_ER | SOURCEID_DSP);
-
- host->card.page_reg = 0x40;
- outb(host->card.page_reg, host->card.io_page);
-
- /* setup dmac - uPC71071 */
- dmac_write(host->dma.io_port, DMAC_INIT, 0);
-#ifdef USE_DMAC
- dmac_write(host->dma.io_port, DMAC_INIT, INIT_8BIT);
- dmac_write(host->dma.io_port, DMAC_CHANNEL, CHANNEL_0);
- dmac_write(host->dma.io_port, DMAC_DEVCON0, INIT_DEVCON0);
- dmac_write(host->dma.io_port, DMAC_DEVCON1, INIT_DEVCON1);
-#endif
-
- host->SCpnt = NULL;
- host->scsi.phase = PHASE_IDLE;
- host->scsi.disconnectable = 0;
-
- memset(host->busyluns, 0, sizeof(host->busyluns));
-
- for (i = 0; i < 8; i++) {
- host->device[i].sync_state = SYNC_NEGOCIATE;
- host->device[i].disconnect_ok = 1;
- }
-
- /* wait 25 cs. SCSI standard says 250ms. */
- acornscsi_csdelay(25);
-}
-
-/*=============================================================================================
- * Utility routines (eg. debug)
- */
-#ifdef CONFIG_ACORNSCSI_CONSTANTS
-static char *acornscsi_interrupttype[] = {
- "rst", "suc", "p/a", "3",
- "term", "5", "6", "7",
- "serv", "9", "a", "b",
- "c", "d", "e", "f"
-};
-
-static signed char acornscsi_map[] = {
- 0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 2, -1, -1, -1, -1, 3, -1, 4, 5, 6, 7, 8, 9, 10, 11,
- 12, 13, 14, -1, -1, -1, -1, -1, 4, 5, 6, 7, 8, 9, 10, 11,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- 15, 16, 17, 18, 19, -1, -1, 20, 4, 5, 6, 7, 8, 9, 10, 11,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- 21, 22, -1, -1, -1, 23, -1, -1, 4, 5, 6, 7, 8, 9, 10, 11,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
-};
-
-static char *acornscsi_interruptcode[] = {
- /* 0 */
- "reset - normal mode", /* 00 */
- "reset - advanced mode", /* 01 */
-
- /* 2 */
- "sel", /* 11 */
- "sel+xfer", /* 16 */
- "data-out", /* 18 */
- "data-in", /* 19 */
- "cmd", /* 1A */
- "stat", /* 1B */
- "??-out", /* 1C */
- "??-in", /* 1D */
- "msg-out", /* 1E */
- "msg-in", /* 1F */
-
- /* 12 */
- "/ACK asserted", /* 20 */
- "save-data-ptr", /* 21 */
- "{re}sel", /* 22 */
-
- /* 15 */
- "inv cmd", /* 40 */
- "unexpected disconnect", /* 41 */
- "sel timeout", /* 42 */
- "P err", /* 43 */
- "P err+ATN", /* 44 */
- "bad status byte", /* 47 */
-
- /* 21 */
- "resel, no id", /* 80 */
- "resel", /* 81 */
- "discon", /* 85 */
-};
-
-static
-void print_scsi_status(unsigned int ssr)
-{
- if (acornscsi_map[ssr] != -1)
- printk("%s:%s",
- acornscsi_interrupttype[(ssr >> 4)],
- acornscsi_interruptcode[acornscsi_map[ssr]]);
- else
- printk("%X:%X", ssr >> 4, ssr & 0x0f);
-}
-#endif
-
-static
-void print_sbic_status(int asr, int ssr, int cmdphase)
-{
-#ifdef CONFIG_ACORNSCSI_CONSTANTS
- printk("sbic: %c%c%c%c%c%c ",
- asr & ASR_INT ? 'I' : 'i',
- asr & ASR_LCI ? 'L' : 'l',
- asr & ASR_BSY ? 'B' : 'b',
- asr & ASR_CIP ? 'C' : 'c',
- asr & ASR_PE ? 'P' : 'p',
- asr & ASR_DBR ? 'D' : 'd');
- printk("scsi: ");
- print_scsi_status(ssr);
- printk(" ph %02X\n", cmdphase);
-#else
- printk("sbic: %02X scsi: %X:%X ph: %02X\n",
- asr, (ssr & 0xf0)>>4, ssr & 0x0f, cmdphase);
-#endif
-}
-
-static void
-acornscsi_dumplogline(AS_Host *host, int target, int line)
-{
- unsigned long prev;
- signed int ptr;
-
- ptr = host->status_ptr[target] - STATUS_BUFFER_TO_PRINT;
- if (ptr < 0)
- ptr += STATUS_BUFFER_SIZE;
-
- printk("%c: %3s:", target == 8 ? 'H' : '0' + target,
- line == 0 ? "ph" : line == 1 ? "ssr" : "int");
-
- prev = host->status[target][ptr].when;
-
- for (; ptr != host->status_ptr[target]; ptr = (ptr + 1) & (STATUS_BUFFER_SIZE - 1)) {
- unsigned long time_diff;
-
- if (!host->status[target][ptr].when)
- continue;
-
- switch (line) {
- case 0:
- printk("%c%02X", host->status[target][ptr].irq ? '-' : ' ',
- host->status[target][ptr].ph);
- break;
-
- case 1:
- printk(" %02X", host->status[target][ptr].ssr);
- break;
-
- case 2:
- time_diff = host->status[target][ptr].when - prev;
- prev = host->status[target][ptr].when;
- if (time_diff == 0)
- printk("==^");
- else if (time_diff >= 100)
- printk(" ");
- else
- printk(" %02ld", time_diff);
- break;
- }
- }
-
- printk("\n");
-}
-
-static
-void acornscsi_dumplog(AS_Host *host, int target)
-{
- do {
- acornscsi_dumplogline(host, target, 0);
- acornscsi_dumplogline(host, target, 1);
- acornscsi_dumplogline(host, target, 2);
-
- if (target == 8)
- break;
-
- target = 8;
- } while (1);
-}
-
-static
-char acornscsi_target(AS_Host *host)
-{
- if (host->SCpnt)
- return '0' + host->SCpnt->device->id;
- return 'H';
-}
-
-/*
- * Prototype: cmdtype_t acornscsi_cmdtype(int command)
- * Purpose : differentiate READ from WRITE from other commands
- * Params : command - command to interpret
- * Returns : CMD_READ - command reads data,
- * CMD_WRITE - command writes data,
- * CMD_MISC - everything else
- */
-static inline
-cmdtype_t acornscsi_cmdtype(int command)
-{
- switch (command) {
- case WRITE_6: case WRITE_10: case WRITE_12:
- return CMD_WRITE;
- case READ_6: case READ_10: case READ_12:
- return CMD_READ;
- default:
- return CMD_MISC;
- }
-}
-
-/*
- * Prototype: int acornscsi_datadirection(int command)
- * Purpose : differentiate between commands that have a DATA IN phase
- * and a DATA OUT phase
- * Params : command - command to interpret
- * Returns : DATADIR_OUT - data out phase expected
- * DATADIR_IN - data in phase expected
- */
-static
-datadir_t acornscsi_datadirection(int command)
-{
- switch (command) {
- case CHANGE_DEFINITION: case COMPARE: case COPY:
- case COPY_VERIFY: case LOG_SELECT: case MODE_SELECT:
- case MODE_SELECT_10: case SEND_DIAGNOSTIC: case WRITE_BUFFER:
- case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE:
- case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW:
- case WRITE_6: case WRITE_10: case WRITE_VERIFY:
- case UPDATE_BLOCK: case WRITE_LONG: case WRITE_SAME:
- case SEARCH_HIGH_12: case SEARCH_EQUAL_12: case SEARCH_LOW_12:
- case WRITE_12: case WRITE_VERIFY_12: case SET_WINDOW:
- case MEDIUM_SCAN: case SEND_VOLUME_TAG: case 0xea:
- return DATADIR_OUT;
- default:
- return DATADIR_IN;
- }
-}
-
-/*
- * Purpose : provide values for synchronous transfers with 33C93.
- * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting
- * Modified by Russell King for 8MHz WD33C93A
- */
-static struct sync_xfer_tbl {
- unsigned int period_ns;
- unsigned char reg_value;
-} sync_xfer_table[] = {
- { 1, 0x20 }, { 249, 0x20 }, { 374, 0x30 },
- { 499, 0x40 }, { 624, 0x50 }, { 749, 0x60 },
- { 874, 0x70 }, { 999, 0x00 }, { 0, 0 }
-};
-
-/*
- * Prototype: int acornscsi_getperiod(unsigned char syncxfer)
- * Purpose : period for the synchronous transfer setting
- * Params : syncxfer SYNCXFER register value
- * Returns : period in ns.
- */
-static
-int acornscsi_getperiod(unsigned char syncxfer)
-{
- int i;
-
- syncxfer &= 0xf0;
- if (syncxfer == 0x10)
- syncxfer = 0;
-
- for (i = 1; sync_xfer_table[i].period_ns; i++)
- if (syncxfer == sync_xfer_table[i].reg_value)
- return sync_xfer_table[i].period_ns;
- return 0;
-}
-
-/*
- * Prototype: int round_period(unsigned int period)
- * Purpose : return index into above table for a required REQ period
- * Params : period - time (ns) for REQ
- * Returns : table index
- * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting
- */
-static inline
-int round_period(unsigned int period)
-{
- int i;
-
- for (i = 1; sync_xfer_table[i].period_ns; i++) {
- if ((period <= sync_xfer_table[i].period_ns) &&
- (period > sync_xfer_table[i - 1].period_ns))
- return i;
- }
- return 7;
-}
-
-/*
- * Prototype: unsigned char calc_sync_xfer(unsigned int period, unsigned int offset)
- * Purpose : calculate value for 33c93s SYNC register
- * Params : period - time (ns) for REQ
- * offset - offset in bytes between REQ/ACK
- * Returns : value for SYNC register
- * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting
- */
-static
-unsigned char calc_sync_xfer(unsigned int period, unsigned int offset)
-{
- return sync_xfer_table[round_period(period)].reg_value |
- ((offset < SDTR_SIZE) ? offset : SDTR_SIZE);
-}
-
-/* ====================================================================================
- * Command functions
- */
-/*
- * Function: acornscsi_kick(AS_Host *host)
- * Purpose : kick next command to interface
- * Params : host - host to send command to
- * Returns : INTR_IDLE if idle, otherwise INTR_PROCESSING
- * Notes : interrupts are always disabled!
- */
-static
-intr_ret_t acornscsi_kick(AS_Host *host)
-{
- int from_queue = 0;
- struct scsi_cmnd *SCpnt;
-
- /* first check to see if a command is waiting to be executed */
- SCpnt = host->origSCpnt;
- host->origSCpnt = NULL;
-
- /* retrieve next command */
- if (!SCpnt) {
- SCpnt = queue_remove_exclude(&host->queues.issue, host->busyluns);
- if (!SCpnt)
- return INTR_IDLE;
-
- from_queue = 1;
- }
-
- if (host->scsi.disconnectable && host->SCpnt) {
- queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt);
- host->scsi.disconnectable = 0;
-#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
- DBG(host->SCpnt, printk("scsi%d.%c: moved command to disconnected queue\n",
- host->host->host_no, acornscsi_target(host)));
-#endif
- host->SCpnt = NULL;
- }
-
- /*
- * If we have an interrupt pending, then we may have been reselected.
- * In this case, we don't want to write to the registers
- */
- if (!(sbic_arm_read(host->scsi.io_port, SBIC_ASR) & (ASR_INT|ASR_BSY|ASR_CIP))) {
- sbic_arm_write(host->scsi.io_port, SBIC_DESTID, SCpnt->device->id);
- sbic_arm_write(host->scsi.io_port, SBIC_CMND, CMND_SELWITHATN);
- }
-
- /*
- * claim host busy - all of these must happen atomically wrt
- * our interrupt routine. Failure means command loss.
- */
- host->scsi.phase = PHASE_CONNECTING;
- host->SCpnt = SCpnt;
- host->scsi.SCp = SCpnt->SCp;
- host->dma.xfer_setup = 0;
- host->dma.xfer_required = 0;
- host->dma.xfer_done = 0;
-
-#if (DEBUG & (DEBUG_ABORT|DEBUG_CONNECT))
- DBG(SCpnt,printk("scsi%d.%c: starting cmd %02X\n",
- host->host->host_no, '0' + SCpnt->device->id,
- SCpnt->cmnd[0]));
-#endif
-
- if (from_queue) {
-#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
- /*
- * tagged queueing - allocate a new tag to this command
- */
- if (SCpnt->device->simple_tags) {
- SCpnt->device->current_tag += 1;
- if (SCpnt->device->current_tag == 0)
- SCpnt->device->current_tag = 1;
- SCpnt->tag = SCpnt->device->current_tag;
- } else
-#endif
- set_bit(SCpnt->device->id * 8 + SCpnt->device->lun, host->busyluns);
-
- host->stats.removes += 1;
-
- switch (acornscsi_cmdtype(SCpnt->cmnd[0])) {
- case CMD_WRITE:
- host->stats.writes += 1;
- break;
- case CMD_READ:
- host->stats.reads += 1;
- break;
- case CMD_MISC:
- host->stats.miscs += 1;
- break;
- }
- }
-
- return INTR_PROCESSING;
-}
-
-/*
- * Function: void acornscsi_done(AS_Host *host, struct scsi_cmnd **SCpntp, unsigned int result)
- * Purpose : complete processing for command
- * Params : host - interface that completed
- * result - driver byte of result
- */
-static void acornscsi_done(AS_Host *host, struct scsi_cmnd **SCpntp,
- unsigned int result)
-{
- struct scsi_cmnd *SCpnt = *SCpntp;
-
- /* clean up */
- sbic_arm_write(host->scsi.io_port, SBIC_SOURCEID, SOURCEID_ER | SOURCEID_DSP);
-
- host->stats.fins += 1;
-
- if (SCpnt) {
- *SCpntp = NULL;
-
- acornscsi_dma_cleanup(host);
-
- SCpnt->result = result << 16 | host->scsi.SCp.Message << 8 | host->scsi.SCp.Status;
-
- /*
- * In theory, this should not happen. In practice, it seems to.
- * Only trigger an error if the device attempts to report all happy
- * but with untransferred buffers... If we don't do something, then
- * data loss will occur. Should we check SCpnt->underflow here?
- * It doesn't appear to be set to something meaningful by the higher
- * levels all the time.
- */
- if (result == DID_OK) {
- int xfer_warn = 0;
-
- if (SCpnt->underflow == 0) {
- if (host->scsi.SCp.ptr &&
- acornscsi_cmdtype(SCpnt->cmnd[0]) != CMD_MISC)
- xfer_warn = 1;
- } else {
- if (host->scsi.SCp.scsi_xferred < SCpnt->underflow ||
- host->scsi.SCp.scsi_xferred != host->dma.transferred)
- xfer_warn = 1;
- }
-
- /* ANSI standard says: (SCSI-2 Rev 10c Sect 5.6.6)
- * Targets which break data transfers into multiple
- * connections shall end each successful connection
- * (except possibly the last) with a SAVE DATA
- * POINTER - DISCONNECT message sequence.
- *
- * This makes it difficult to ensure that a transfer has
- * completed. If we reach the end of a transfer during
- * the command, then we can only have finished the transfer.
- * therefore, if we seem to have some data remaining, this
- * is not a problem.
- */
- if (host->dma.xfer_done)
- xfer_warn = 0;
-
- if (xfer_warn) {
- switch (status_byte(SCpnt->result)) {
- case CHECK_CONDITION:
- case COMMAND_TERMINATED:
- case BUSY:
- case QUEUE_FULL:
- case RESERVATION_CONFLICT:
- break;
-
- default:
- printk(KERN_ERR "scsi%d.H: incomplete data transfer detected: result=%08X command=",
- host->host->host_no, SCpnt->result);
- __scsi_print_command(SCpnt->cmnd);
- acornscsi_dumpdma(host, "done");
- acornscsi_dumplog(host, SCpnt->device->id);
- SCpnt->result &= 0xffff;
- SCpnt->result |= DID_ERROR << 16;
- }
- }
- }
-
- if (!SCpnt->scsi_done)
- panic("scsi%d.H: null scsi_done function in acornscsi_done", host->host->host_no);
-
- clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, host->busyluns);
-
- SCpnt->scsi_done(SCpnt);
- } else
- printk("scsi%d: null command in acornscsi_done", host->host->host_no);
-
- host->scsi.phase = PHASE_IDLE;
-}
-
-/* ====================================================================================
- * DMA routines
- */
-/*
- * Purpose : update SCSI Data Pointer
- * Notes : this will only be one SG entry or less
- */
-static
-void acornscsi_data_updateptr(AS_Host *host, struct scsi_pointer *SCp, unsigned int length)
-{
- SCp->ptr += length;
- SCp->this_residual -= length;
-
- if (SCp->this_residual == 0 && next_SCp(SCp) == 0)
- host->dma.xfer_done = 1;
-}
-
-/*
- * Prototype: void acornscsi_data_read(AS_Host *host, char *ptr,
- * unsigned int start_addr, unsigned int length)
- * Purpose : read data from DMA RAM
- * Params : host - host to transfer from
- * ptr - DRAM address
- * start_addr - host mem address
- * length - number of bytes to transfer
- * Notes : this will only be one SG entry or less
- */
-static
-void acornscsi_data_read(AS_Host *host, char *ptr,
- unsigned int start_addr, unsigned int length)
-{
- extern void __acornscsi_in(int port, char *buf, int len);
- unsigned int page, offset, len = length;
-
- page = (start_addr >> 12);
- offset = start_addr & ((1 << 12) - 1);
-
- outb((page & 0x3f) | host->card.page_reg, host->card.io_page);
-
- while (len > 0) {
- unsigned int this_len;
-
- if (len + offset > (1 << 12))
- this_len = (1 << 12) - offset;
- else
- this_len = len;
-
- __acornscsi_in(host->card.io_ram + (offset << 1), ptr, this_len);
-
- offset += this_len;
- ptr += this_len;
- len -= this_len;
-
- if (offset == (1 << 12)) {
- offset = 0;
- page ++;
- outb((page & 0x3f) | host->card.page_reg, host->card.io_page);
- }
- }
- outb(host->card.page_reg, host->card.io_page);
-}
-
-/*
- * Prototype: void acornscsi_data_write(AS_Host *host, char *ptr,
- * unsigned int start_addr, unsigned int length)
- * Purpose : write data to DMA RAM
- * Params : host - host to transfer from
- * ptr - DRAM address
- * start_addr - host mem address
- * length - number of bytes to transfer
- * Notes : this will only be one SG entry or less
- */
-static
-void acornscsi_data_write(AS_Host *host, char *ptr,
- unsigned int start_addr, unsigned int length)
-{
- extern void __acornscsi_out(int port, char *buf, int len);
- unsigned int page, offset, len = length;
-
- page = (start_addr >> 12);
- offset = start_addr & ((1 << 12) - 1);
-
- outb((page & 0x3f) | host->card.page_reg, host->card.io_page);
-
- while (len > 0) {
- unsigned int this_len;
-
- if (len + offset > (1 << 12))
- this_len = (1 << 12) - offset;
- else
- this_len = len;
-
- __acornscsi_out(host->card.io_ram + (offset << 1), ptr, this_len);
-
- offset += this_len;
- ptr += this_len;
- len -= this_len;
-
- if (offset == (1 << 12)) {
- offset = 0;
- page ++;
- outb((page & 0x3f) | host->card.page_reg, host->card.io_page);
- }
- }
- outb(host->card.page_reg, host->card.io_page);
-}
-
-/* =========================================================================================
- * On-board DMA routines
- */
-#ifdef USE_DMAC
-/*
- * Prototype: void acornscsi_dmastop(AS_Host *host)
- * Purpose : stop all DMA
- * Params : host - host on which to stop DMA
- * Notes : This is called when leaving DATA IN/OUT phase,
- * or when interface is RESET
- */
-static inline
-void acornscsi_dma_stop(AS_Host *host)
-{
- dmac_write(host->dma.io_port, DMAC_MASKREG, MASK_ON);
- dmac_clearintr(host->dma.io_intr_clear);
-
-#if (DEBUG & DEBUG_DMA)
- DBG(host->SCpnt, acornscsi_dumpdma(host, "stop"));
-#endif
-}
-
-/*
- * Function: void acornscsi_dma_setup(AS_Host *host, dmadir_t direction)
- * Purpose : setup DMA controller for data transfer
- * Params : host - host to setup
- * direction - data transfer direction
- * Notes : This is called when entering DATA I/O phase, not
- * while we're in a DATA I/O phase
- */
-static
-void acornscsi_dma_setup(AS_Host *host, dmadir_t direction)
-{
- unsigned int address, length, mode;
-
- host->dma.direction = direction;
-
- dmac_write(host->dma.io_port, DMAC_MASKREG, MASK_ON);
-
- if (direction == DMA_OUT) {
-#if (DEBUG & DEBUG_NO_WRITE)
- if (NO_WRITE & (1 << host->SCpnt->device->id)) {
- printk(KERN_CRIT "scsi%d.%c: I can't handle DMA_OUT!\n",
- host->host->host_no, acornscsi_target(host));
- return;
- }
-#endif
- mode = DMAC_WRITE;
- } else
- mode = DMAC_READ;
-
- /*
- * Allocate some buffer space, limited to half the buffer size
- */
- length = min_t(unsigned int, host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2);
- if (length) {
- host->dma.start_addr = address = host->dma.free_addr;
- host->dma.free_addr = (host->dma.free_addr + length) &
- (DMAC_BUFFER_SIZE - 1);
-
- /*
- * Transfer data to DMA memory
- */
- if (direction == DMA_OUT)
- acornscsi_data_write(host, host->scsi.SCp.ptr, host->dma.start_addr,
- length);
-
- length -= 1;
- dmac_write(host->dma.io_port, DMAC_TXCNTLO, length);
- dmac_write(host->dma.io_port, DMAC_TXCNTHI, length >> 8);
- dmac_write(host->dma.io_port, DMAC_TXADRLO, address);
- dmac_write(host->dma.io_port, DMAC_TXADRMD, address >> 8);
- dmac_write(host->dma.io_port, DMAC_TXADRHI, 0);
- dmac_write(host->dma.io_port, DMAC_MODECON, mode);
- dmac_write(host->dma.io_port, DMAC_MASKREG, MASK_OFF);
-
-#if (DEBUG & DEBUG_DMA)
- DBG(host->SCpnt, acornscsi_dumpdma(host, "strt"));
-#endif
- host->dma.xfer_setup = 1;
- }
-}
-
-/*
- * Function: void acornscsi_dma_cleanup(AS_Host *host)
- * Purpose : ensure that all DMA transfers are up-to-date & host->scsi.SCp is correct
- * Params : host - host to finish
- * Notes : This is called when a command is:
- * terminating, RESTORE_POINTERS, SAVE_POINTERS, DISCONECT
- * : This must not return until all transfers are completed.
- */
-static
-void acornscsi_dma_cleanup(AS_Host *host)
-{
- dmac_write(host->dma.io_port, DMAC_MASKREG, MASK_ON);
- dmac_clearintr(host->dma.io_intr_clear);
-
- /*
- * Check for a pending transfer
- */
- if (host->dma.xfer_required) {
- host->dma.xfer_required = 0;
- if (host->dma.direction == DMA_IN)
- acornscsi_data_read(host, host->dma.xfer_ptr,
- host->dma.xfer_start, host->dma.xfer_length);
- }
-
- /*
- * Has a transfer been setup?
- */
- if (host->dma.xfer_setup) {
- unsigned int transferred;
-
- host->dma.xfer_setup = 0;
-
-#if (DEBUG & DEBUG_DMA)
- DBG(host->SCpnt, acornscsi_dumpdma(host, "cupi"));
-#endif
-
- /*
- * Calculate number of bytes transferred from DMA.
- */
- transferred = dmac_address(host->dma.io_port) - host->dma.start_addr;
- host->dma.transferred += transferred;
-
- if (host->dma.direction == DMA_IN)
- acornscsi_data_read(host, host->scsi.SCp.ptr,
- host->dma.start_addr, transferred);
-
- /*
- * Update SCSI pointers
- */
- acornscsi_data_updateptr(host, &host->scsi.SCp, transferred);
-#if (DEBUG & DEBUG_DMA)
- DBG(host->SCpnt, acornscsi_dumpdma(host, "cupo"));
-#endif
- }
-}
-
-/*
- * Function: void acornscsi_dmacintr(AS_Host *host)
- * Purpose : handle interrupts from DMAC device
- * Params : host - host to process
- * Notes : If reading, we schedule the read to main memory &
- * allow the transfer to continue.
- * : If writing, we fill the onboard DMA memory from main
- * memory.
- * : Called whenever DMAC finished it's current transfer.
- */
-static
-void acornscsi_dma_intr(AS_Host *host)
-{
- unsigned int address, length, transferred;
-
-#if (DEBUG & DEBUG_DMA)
- DBG(host->SCpnt, acornscsi_dumpdma(host, "inti"));
-#endif
-
- dmac_write(host->dma.io_port, DMAC_MASKREG, MASK_ON);
- dmac_clearintr(host->dma.io_intr_clear);
-
- /*
- * Calculate amount transferred via DMA
- */
- transferred = dmac_address(host->dma.io_port) - host->dma.start_addr;
- host->dma.transferred += transferred;
-
- /*
- * Schedule DMA transfer off board
- */
- if (host->dma.direction == DMA_IN) {
- host->dma.xfer_start = host->dma.start_addr;
- host->dma.xfer_length = transferred;
- host->dma.xfer_ptr = host->scsi.SCp.ptr;
- host->dma.xfer_required = 1;
- }
-
- acornscsi_data_updateptr(host, &host->scsi.SCp, transferred);
-
- /*
- * Allocate some buffer space, limited to half the on-board RAM size
- */
- length = min_t(unsigned int, host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2);
- if (length) {
- host->dma.start_addr = address = host->dma.free_addr;
- host->dma.free_addr = (host->dma.free_addr + length) &
- (DMAC_BUFFER_SIZE - 1);
-
- /*
- * Transfer data to DMA memory
- */
- if (host->dma.direction == DMA_OUT)
- acornscsi_data_write(host, host->scsi.SCp.ptr, host->dma.start_addr,
- length);
-
- length -= 1;
- dmac_write(host->dma.io_port, DMAC_TXCNTLO, length);
- dmac_write(host->dma.io_port, DMAC_TXCNTHI, length >> 8);
- dmac_write(host->dma.io_port, DMAC_TXADRLO, address);
- dmac_write(host->dma.io_port, DMAC_TXADRMD, address >> 8);
- dmac_write(host->dma.io_port, DMAC_TXADRHI, 0);
- dmac_write(host->dma.io_port, DMAC_MASKREG, MASK_OFF);
-
-#if (DEBUG & DEBUG_DMA)
- DBG(host->SCpnt, acornscsi_dumpdma(host, "into"));
-#endif
- } else {
- host->dma.xfer_setup = 0;
-#if 0
- /*
- * If the interface still wants more, then this is an error.
- * We give it another byte, but we also attempt to raise an
- * attention condition. We continue giving one byte until
- * the device recognises the attention.
- */
- if (dmac_read(host->dma.io_port, DMAC_STATUS) & STATUS_RQ0) {
- acornscsi_abortcmd(host, host->SCpnt->tag);
-
- dmac_write(host->dma.io_port, DMAC_TXCNTLO, 0);
- dmac_write(host->dma.io_port, DMAC_TXCNTHI, 0);
- dmac_write(host->dma.io_port, DMAC_TXADRLO, 0);
- dmac_write(host->dma.io_port, DMAC_TXADRMD, 0);
- dmac_write(host->dma.io_port, DMAC_TXADRHI, 0);
- dmac_write(host->dma.io_port, DMAC_MASKREG, MASK_OFF);
- }
-#endif
- }
-}
-
-/*
- * Function: void acornscsi_dma_xfer(AS_Host *host)
- * Purpose : transfer data between AcornSCSI and memory
- * Params : host - host to process
- */
-static
-void acornscsi_dma_xfer(AS_Host *host)
-{
- host->dma.xfer_required = 0;
-
- if (host->dma.direction == DMA_IN)
- acornscsi_data_read(host, host->dma.xfer_ptr,
- host->dma.xfer_start, host->dma.xfer_length);
-}
-
-/*
- * Function: void acornscsi_dma_adjust(AS_Host *host)
- * Purpose : adjust DMA pointers & count for bytes transferred to
- * SBIC but not SCSI bus.
- * Params : host - host to adjust DMA count for
- */
-static
-void acornscsi_dma_adjust(AS_Host *host)
-{
- if (host->dma.xfer_setup) {
- signed long transferred;
-#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE))
- DBG(host->SCpnt, acornscsi_dumpdma(host, "adji"));
-#endif
- /*
- * Calculate correct DMA address - DMA is ahead of SCSI bus while
- * writing.
- * host->scsi.SCp.scsi_xferred is the number of bytes
- * actually transferred to/from the SCSI bus.
- * host->dma.transferred is the number of bytes transferred
- * over DMA since host->dma.start_addr was last set.
- *
- * real_dma_addr = host->dma.start_addr + host->scsi.SCp.scsi_xferred
- * - host->dma.transferred
- */
- transferred = host->scsi.SCp.scsi_xferred - host->dma.transferred;
- if (transferred < 0)
- printk("scsi%d.%c: Ack! DMA write correction %ld < 0!\n",
- host->host->host_no, acornscsi_target(host), transferred);
- else if (transferred == 0)
- host->dma.xfer_setup = 0;
- else {
- transferred += host->dma.start_addr;
- dmac_write(host->dma.io_port, DMAC_TXADRLO, transferred);
- dmac_write(host->dma.io_port, DMAC_TXADRMD, transferred >> 8);
- dmac_write(host->dma.io_port, DMAC_TXADRHI, transferred >> 16);
-#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE))
- DBG(host->SCpnt, acornscsi_dumpdma(host, "adjo"));
-#endif
- }
- }
-}
-#endif
-
-/* =========================================================================================
- * Data I/O
- */
-static int
-acornscsi_write_pio(AS_Host *host, char *bytes, int *ptr, int len, unsigned int max_timeout)
-{
- unsigned int asr, timeout = max_timeout;
- int my_ptr = *ptr;
-
- while (my_ptr < len) {
- asr = sbic_arm_read(host->scsi.io_port, SBIC_ASR);
-
- if (asr & ASR_DBR) {
- timeout = max_timeout;
-
- sbic_arm_write(host->scsi.io_port, SBIC_DATA, bytes[my_ptr++]);
- } else if (asr & ASR_INT)
- break;
- else if (--timeout == 0)
- break;
- udelay(1);
- }
-
- *ptr = my_ptr;
-
- return (timeout == 0) ? -1 : 0;
-}
-
-/*
- * Function: void acornscsi_sendcommand(AS_Host *host)
- * Purpose : send a command to a target
- * Params : host - host which is connected to target
- */
-static void
-acornscsi_sendcommand(AS_Host *host)
-{
- struct scsi_cmnd *SCpnt = host->SCpnt;
-
- sbic_arm_write(host->scsi.io_port, SBIC_TRANSCNTH, 0);
- sbic_arm_writenext(host->scsi.io_port, 0);
- sbic_arm_writenext(host->scsi.io_port, SCpnt->cmd_len - host->scsi.SCp.sent_command);
-
- acornscsi_sbic_issuecmd(host, CMND_XFERINFO);
-
- if (acornscsi_write_pio(host, SCpnt->cmnd,
- (int *)&host->scsi.SCp.sent_command, SCpnt->cmd_len, 1000000))
- printk("scsi%d: timeout while sending command\n", host->host->host_no);
-
- host->scsi.phase = PHASE_COMMAND;
-}
-
-static
-void acornscsi_sendmessage(AS_Host *host)
-{
- unsigned int message_length = msgqueue_msglength(&host->scsi.msgs);
- unsigned int msgnr;
- struct message *msg;
-
-#if (DEBUG & DEBUG_MESSAGES)
- printk("scsi%d.%c: sending message ",
- host->host->host_no, acornscsi_target(host));
-#endif
-
- switch (message_length) {
- case 0:
- acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT);
-
- acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "sending message 1");
-
- sbic_arm_write(host->scsi.io_port, SBIC_DATA, NOP);
-
- host->scsi.last_message = NOP;
-#if (DEBUG & DEBUG_MESSAGES)
- printk("NOP");
-#endif
- break;
-
- case 1:
- acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT);
- msg = msgqueue_getmsg(&host->scsi.msgs, 0);
-
- acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "sending message 2");
-
- sbic_arm_write(host->scsi.io_port, SBIC_DATA, msg->msg[0]);
-
- host->scsi.last_message = msg->msg[0];
-#if (DEBUG & DEBUG_MESSAGES)
- spi_print_msg(msg->msg);
-#endif
- break;
-
- default:
- /*
- * ANSI standard says: (SCSI-2 Rev 10c Sect 5.6.14)
- * 'When a target sends this (MESSAGE_REJECT) message, it
- * shall change to MESSAGE IN phase and send this message
- * prior to requesting additional message bytes from the
- * initiator. This provides an interlock so that the
- * initiator can determine which message byte is rejected.
- */
- sbic_arm_write(host->scsi.io_port, SBIC_TRANSCNTH, 0);
- sbic_arm_writenext(host->scsi.io_port, 0);
- sbic_arm_writenext(host->scsi.io_port, message_length);
- acornscsi_sbic_issuecmd(host, CMND_XFERINFO);
-
- msgnr = 0;
- while ((msg = msgqueue_getmsg(&host->scsi.msgs, msgnr++)) != NULL) {
- unsigned int i;
-#if (DEBUG & DEBUG_MESSAGES)
- spi_print_msg(msg);
-#endif
- i = 0;
- if (acornscsi_write_pio(host, msg->msg, &i, msg->length, 1000000))
- printk("scsi%d: timeout while sending message\n", host->host->host_no);
-
- host->scsi.last_message = msg->msg[0];
- if (msg->msg[0] == EXTENDED_MESSAGE)
- host->scsi.last_message |= msg->msg[2] << 8;
-
- if (i != msg->length)
- break;
- }
- break;
- }
-#if (DEBUG & DEBUG_MESSAGES)
- printk("\n");
-#endif
-}
-
-/*
- * Function: void acornscsi_readstatusbyte(AS_Host *host)
- * Purpose : Read status byte from connected target
- * Params : host - host connected to target
- */
-static
-void acornscsi_readstatusbyte(AS_Host *host)
-{
- acornscsi_sbic_issuecmd(host, CMND_XFERINFO|CMND_SBT);
- acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "reading status byte");
- host->scsi.SCp.Status = sbic_arm_read(host->scsi.io_port, SBIC_DATA);
-}
-
-/*
- * Function: unsigned char acornscsi_readmessagebyte(AS_Host *host)
- * Purpose : Read one message byte from connected target
- * Params : host - host connected to target
- */
-static
-unsigned char acornscsi_readmessagebyte(AS_Host *host)
-{
- unsigned char message;
-
- acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT);
-
- acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "for message byte");
-
- message = sbic_arm_read(host->scsi.io_port, SBIC_DATA);
-
- /* wait for MSGIN-XFER-PAUSED */
- acornscsi_sbic_wait(host, ASR_INT, ASR_INT, 1000, "for interrupt after message byte");
-
- sbic_arm_read(host->scsi.io_port, SBIC_SSR);
-
- return message;
-}
-
-/*
- * Function: void acornscsi_message(AS_Host *host)
- * Purpose : Read complete message from connected target & action message
- * Params : host - host connected to target
- */
-static
-void acornscsi_message(AS_Host *host)
-{
- unsigned char message[16];
- unsigned int msgidx = 0, msglen = 1;
-
- do {
- message[msgidx] = acornscsi_readmessagebyte(host);
-
- switch (msgidx) {
- case 0:
- if (message[0] == EXTENDED_MESSAGE ||
- (message[0] >= 0x20 && message[0] <= 0x2f))
- msglen = 2;
- break;
-
- case 1:
- if (message[0] == EXTENDED_MESSAGE)
- msglen += message[msgidx];
- break;
- }
- msgidx += 1;
- if (msgidx < msglen) {
- acornscsi_sbic_issuecmd(host, CMND_NEGATEACK);
-
- /* wait for next msg-in */
- acornscsi_sbic_wait(host, ASR_INT, ASR_INT, 1000, "for interrupt after negate ack");
- sbic_arm_read(host->scsi.io_port, SBIC_SSR);
- }
- } while (msgidx < msglen);
-
-#if (DEBUG & DEBUG_MESSAGES)
- printk("scsi%d.%c: message in: ",
- host->host->host_no, acornscsi_target(host));
- spi_print_msg(message);
- printk("\n");
-#endif
-
- if (host->scsi.phase == PHASE_RECONNECTED) {
- /*
- * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.17)
- * 'Whenever a target reconnects to an initiator to continue
- * a tagged I/O process, the SIMPLE QUEUE TAG message shall
- * be sent immediately following the IDENTIFY message...'
- */
- if (message[0] == SIMPLE_QUEUE_TAG)
- host->scsi.reconnected.tag = message[1];
- if (acornscsi_reconnect_finish(host))
- host->scsi.phase = PHASE_MSGIN;
- }
-
- switch (message[0]) {
- case ABORT:
- case ABORT_TAG:
- case COMMAND_COMPLETE:
- if (host->scsi.phase != PHASE_STATUSIN) {
- printk(KERN_ERR "scsi%d.%c: command complete following non-status in phase?\n",
- host->host->host_no, acornscsi_target(host));
- acornscsi_dumplog(host, host->SCpnt->device->id);
- }
- host->scsi.phase = PHASE_DONE;
- host->scsi.SCp.Message = message[0];
- break;
-
- case SAVE_POINTERS:
- /*
- * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.20)
- * 'The SAVE DATA POINTER message is sent from a target to
- * direct the initiator to copy the active data pointer to
- * the saved data pointer for the current I/O process.
- */
- acornscsi_dma_cleanup(host);
- host->SCpnt->SCp = host->scsi.SCp;
- host->SCpnt->SCp.sent_command = 0;
- host->scsi.phase = PHASE_MSGIN;
- break;
-
- case RESTORE_POINTERS:
- /*
- * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.19)
- * 'The RESTORE POINTERS message is sent from a target to
- * direct the initiator to copy the most recently saved
- * command, data, and status pointers for the I/O process
- * to the corresponding active pointers. The command and
- * status pointers shall be restored to the beginning of
- * the present command and status areas.'
- */
- acornscsi_dma_cleanup(host);
- host->scsi.SCp = host->SCpnt->SCp;
- host->scsi.phase = PHASE_MSGIN;
- break;
-
- case DISCONNECT:
- /*
- * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 6.4.2)
- * 'On those occasions when an error or exception condition occurs
- * and the target elects to repeat the information transfer, the
- * target may repeat the transfer either issuing a RESTORE POINTERS
- * message or by disconnecting without issuing a SAVE POINTERS
- * message. When reconnection is completed, the most recent
- * saved pointer values are restored.'
- */
- acornscsi_dma_cleanup(host);
- host->scsi.phase = PHASE_DISCONNECT;
- break;
-
- case MESSAGE_REJECT:
-#if 0 /* this isn't needed any more */
- /*
- * If we were negociating sync transfer, we don't yet know if
- * this REJECT is for the sync transfer or for the tagged queue/wide
- * transfer. Re-initiate sync transfer negociation now, and if
- * we got a REJECT in response to SDTR, then it'll be set to DONE.
- */
- if (host->device[host->SCpnt->device->id].sync_state == SYNC_SENT_REQUEST)
- host->device[host->SCpnt->device->id].sync_state = SYNC_NEGOCIATE;
-#endif
-
- /*
- * If we have any messages waiting to go out, then assert ATN now
- */
- if (msgqueue_msglength(&host->scsi.msgs))
- acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
-
- switch (host->scsi.last_message) {
-#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
- case HEAD_OF_QUEUE_TAG:
- case ORDERED_QUEUE_TAG:
- case SIMPLE_QUEUE_TAG:
- /*
- * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.17)
- * If a target does not implement tagged queuing and a queue tag
- * message is received, it shall respond with a MESSAGE REJECT
- * message and accept the I/O process as if it were untagged.
- */
- printk(KERN_NOTICE "scsi%d.%c: disabling tagged queueing\n",
- host->host->host_no, acornscsi_target(host));
- host->SCpnt->device->simple_tags = 0;
- set_bit(host->SCpnt->device->id * 8 + host->SCpnt->device->lun, host->busyluns);
- break;
-#endif
- case EXTENDED_MESSAGE | (EXTENDED_SDTR << 8):
- /*
- * Target can't handle synchronous transfers
- */
- printk(KERN_NOTICE "scsi%d.%c: Using asynchronous transfer\n",
- host->host->host_no, acornscsi_target(host));
- host->device[host->SCpnt->device->id].sync_xfer = SYNCHTRANSFER_2DBA;
- host->device[host->SCpnt->device->id].sync_state = SYNC_ASYNCHRONOUS;
- sbic_arm_write(host->scsi.io_port, SBIC_SYNCHTRANSFER, host->device[host->SCpnt->device->id].sync_xfer);
- break;
-
- default:
- break;
- }
- break;
-
- case QUEUE_FULL:
- /* TODO: target queue is full */
- break;
-
- case SIMPLE_QUEUE_TAG:
- /* tag queue reconnect... message[1] = queue tag. Print something to indicate something happened! */
- printk("scsi%d.%c: reconnect queue tag %02X\n",
- host->host->host_no, acornscsi_target(host),
- message[1]);
- break;
-
- case EXTENDED_MESSAGE:
- switch (message[2]) {
-#ifdef CONFIG_SCSI_ACORNSCSI_SYNC
- case EXTENDED_SDTR:
- if (host->device[host->SCpnt->device->id].sync_state == SYNC_SENT_REQUEST) {
- /*
- * We requested synchronous transfers. This isn't quite right...
- * We can only say if this succeeded if we proceed on to execute the
- * command from this message. If we get a MESSAGE PARITY ERROR,
- * and the target retries fail, then we fallback to asynchronous mode
- */
- host->device[host->SCpnt->device->id].sync_state = SYNC_COMPLETED;
- printk(KERN_NOTICE "scsi%d.%c: Using synchronous transfer, offset %d, %d ns\n",
- host->host->host_no, acornscsi_target(host),
- message[4], message[3] * 4);
- host->device[host->SCpnt->device->id].sync_xfer =
- calc_sync_xfer(message[3] * 4, message[4]);
- } else {
- unsigned char period, length;
- /*
- * Target requested synchronous transfers. The agreement is only
- * to be in operation AFTER the target leaves message out phase.
- */
- acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
- period = max_t(unsigned int, message[3], sdtr_period / 4);
- length = min_t(unsigned int, message[4], sdtr_size);
- msgqueue_addmsg(&host->scsi.msgs, 5, EXTENDED_MESSAGE, 3,
- EXTENDED_SDTR, period, length);
- host->device[host->SCpnt->device->id].sync_xfer =
- calc_sync_xfer(period * 4, length);
- }
- sbic_arm_write(host->scsi.io_port, SBIC_SYNCHTRANSFER, host->device[host->SCpnt->device->id].sync_xfer);
- break;
-#else
- /* We do not accept synchronous transfers. Respond with a
- * MESSAGE_REJECT.
- */
-#endif
-
- case EXTENDED_WDTR:
- /* The WD33C93A is only 8-bit. We respond with a MESSAGE_REJECT
- * to a wide data transfer request.
- */
- default:
- acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
- msgqueue_flush(&host->scsi.msgs);
- msgqueue_addmsg(&host->scsi.msgs, 1, MESSAGE_REJECT);
- break;
- }
- break;
-
-#ifdef CONFIG_SCSI_ACORNSCSI_LINK
- case LINKED_CMD_COMPLETE:
- case LINKED_FLG_CMD_COMPLETE:
- /*
- * We don't support linked commands yet
- */
- if (0) {
-#if (DEBUG & DEBUG_LINK)
- printk("scsi%d.%c: lun %d tag %d linked command complete\n",
- host->host->host_no, acornscsi_target(host), host->SCpnt->tag);
-#endif
- /*
- * A linked command should only terminate with one of these messages
- * if there are more linked commands available.
- */
- if (!host->SCpnt->next_link) {
- printk(KERN_WARNING "scsi%d.%c: lun %d tag %d linked command complete, but no next_link\n",
- instance->host_no, acornscsi_target(host), host->SCpnt->tag);
- acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
- msgqueue_addmsg(&host->scsi.msgs, 1, ABORT);
- } else {
- struct scsi_cmnd *SCpnt = host->SCpnt;
-
- acornscsi_dma_cleanup(host);
-
- host->SCpnt = host->SCpnt->next_link;
- host->SCpnt->tag = SCpnt->tag;
- SCpnt->result = DID_OK | host->scsi.SCp.Message << 8 | host->Scsi.SCp.Status;
- SCpnt->done(SCpnt);
-
- /* initialise host->SCpnt->SCp */
- }
- break;
- }
-#endif
-
- default: /* reject message */
- printk(KERN_ERR "scsi%d.%c: unrecognised message %02X, rejecting\n",
- host->host->host_no, acornscsi_target(host),
- message[0]);
- acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
- msgqueue_flush(&host->scsi.msgs);
- msgqueue_addmsg(&host->scsi.msgs, 1, MESSAGE_REJECT);
- host->scsi.phase = PHASE_MSGIN;
- break;
- }
- acornscsi_sbic_issuecmd(host, CMND_NEGATEACK);
-}
-
-/*
- * Function: int acornscsi_buildmessages(AS_Host *host)
- * Purpose : build the connection messages for a host
- * Params : host - host to add messages to
- */
-static
-void acornscsi_buildmessages(AS_Host *host)
-{
-#if 0
- /* does the device need resetting? */
- if (cmd_reset) {
- msgqueue_addmsg(&host->scsi.msgs, 1, BUS_DEVICE_RESET);
- return;
- }
-#endif
-
- msgqueue_addmsg(&host->scsi.msgs, 1,
- IDENTIFY(host->device[host->SCpnt->device->id].disconnect_ok,
- host->SCpnt->device->lun));
-
-#if 0
- /* does the device need the current command aborted */
- if (cmd_aborted) {
- acornscsi_abortcmd(host->SCpnt->tag);
- return;
- }
-#endif
-
-#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
- if (host->SCpnt->tag) {
- unsigned int tag_type;
-
- if (host->SCpnt->cmnd[0] == REQUEST_SENSE ||
- host->SCpnt->cmnd[0] == TEST_UNIT_READY ||
- host->SCpnt->cmnd[0] == INQUIRY)
- tag_type = HEAD_OF_QUEUE_TAG;
- else
- tag_type = SIMPLE_QUEUE_TAG;
- msgqueue_addmsg(&host->scsi.msgs, 2, tag_type, host->SCpnt->tag);
- }
-#endif
-
-#ifdef CONFIG_SCSI_ACORNSCSI_SYNC
- if (host->device[host->SCpnt->device->id].sync_state == SYNC_NEGOCIATE) {
- host->device[host->SCpnt->device->id].sync_state = SYNC_SENT_REQUEST;
- msgqueue_addmsg(&host->scsi.msgs, 5,
- EXTENDED_MESSAGE, 3, EXTENDED_SDTR,
- sdtr_period / 4, sdtr_size);
- }
-#endif
-}
-
-/*
- * Function: int acornscsi_starttransfer(AS_Host *host)
- * Purpose : transfer data to/from connected target
- * Params : host - host to which target is connected
- * Returns : 0 if failure
- */
-static
-int acornscsi_starttransfer(AS_Host *host)
-{
- int residual;
-
- if (!host->scsi.SCp.ptr /*&& host->scsi.SCp.this_residual*/) {
- printk(KERN_ERR "scsi%d.%c: null buffer passed to acornscsi_starttransfer\n",
- host->host->host_no, acornscsi_target(host));
- return 0;
- }
-
- residual = host->SCpnt->request_bufflen - host->scsi.SCp.scsi_xferred;
-
- sbic_arm_write(host->scsi.io_port, SBIC_SYNCHTRANSFER, host->device[host->SCpnt->device->id].sync_xfer);
- sbic_arm_writenext(host->scsi.io_port, residual >> 16);
- sbic_arm_writenext(host->scsi.io_port, residual >> 8);
- sbic_arm_writenext(host->scsi.io_port, residual);
- acornscsi_sbic_issuecmd(host, CMND_XFERINFO);
- return 1;
-}
-
-/* =========================================================================================
- * Connection & Disconnection
- */
-/*
- * Function : acornscsi_reconnect(AS_Host *host)
- * Purpose : reconnect a previously disconnected command
- * Params : host - host specific data
- * Remarks : SCSI spec says:
- * 'The set of active pointers is restored from the set
- * of saved pointers upon reconnection of the I/O process'
- */
-static
-int acornscsi_reconnect(AS_Host *host)
-{
- unsigned int target, lun, ok = 0;
-
- target = sbic_arm_read(host->scsi.io_port, SBIC_SOURCEID);
-
- if (!(target & 8))
- printk(KERN_ERR "scsi%d: invalid source id after reselection "
- "- device fault?\n",
- host->host->host_no);
-
- target &= 7;
-
- if (host->SCpnt && !host->scsi.disconnectable) {
- printk(KERN_ERR "scsi%d.%d: reconnected while command in "
- "progress to target %d?\n",
- host->host->host_no, target, host->SCpnt->device->id);
- host->SCpnt = NULL;
- }
-
- lun = sbic_arm_read(host->scsi.io_port, SBIC_DATA) & 7;
-
- host->scsi.reconnected.target = target;
- host->scsi.reconnected.lun = lun;
- host->scsi.reconnected.tag = 0;
-
- if (host->scsi.disconnectable && host->SCpnt &&
- host->SCpnt->device->id == target && host->SCpnt->device->lun == lun)
- ok = 1;
-
- if (!ok && queue_probetgtlun(&host->queues.disconnected, target, lun))
- ok = 1;
-
- ADD_STATUS(target, 0x81, host->scsi.phase, 0);
-
- if (ok) {
- host->scsi.phase = PHASE_RECONNECTED;
- } else {
- /* this doesn't seem to work */
- printk(KERN_ERR "scsi%d.%c: reselected with no command "
- "to reconnect with\n",
- host->host->host_no, '0' + target);
- acornscsi_dumplog(host, target);
- acornscsi_abortcmd(host, 0);
- if (host->SCpnt) {
- queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt);
- host->SCpnt = NULL;
- }
- }
- acornscsi_sbic_issuecmd(host, CMND_NEGATEACK);
- return !ok;
-}
-
-/*
- * Function: int acornscsi_reconect_finish(AS_Host *host)
- * Purpose : finish reconnecting a command
- * Params : host - host to complete
- * Returns : 0 if failed
- */
-static
-int acornscsi_reconnect_finish(AS_Host *host)
-{
- if (host->scsi.disconnectable && host->SCpnt) {
- host->scsi.disconnectable = 0;
- if (host->SCpnt->device->id == host->scsi.reconnected.target &&
- host->SCpnt->device->lun == host->scsi.reconnected.lun &&
- host->SCpnt->tag == host->scsi.reconnected.tag) {
-#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
- DBG(host->SCpnt, printk("scsi%d.%c: reconnected",
- host->host->host_no, acornscsi_target(host)));
-#endif
- } else {
- queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt);
-#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
- DBG(host->SCpnt, printk("scsi%d.%c: had to move command "
- "to disconnected queue\n",
- host->host->host_no, acornscsi_target(host)));
-#endif
- host->SCpnt = NULL;
- }
- }
- if (!host->SCpnt) {
- host->SCpnt = queue_remove_tgtluntag(&host->queues.disconnected,
- host->scsi.reconnected.target,
- host->scsi.reconnected.lun,
- host->scsi.reconnected.tag);
-#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
- DBG(host->SCpnt, printk("scsi%d.%c: had to get command",
- host->host->host_no, acornscsi_target(host)));
-#endif
- }
-
- if (!host->SCpnt)
- acornscsi_abortcmd(host, host->scsi.reconnected.tag);
- else {
- /*
- * Restore data pointer from SAVED pointers.
- */
- host->scsi.SCp = host->SCpnt->SCp;
-#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
- printk(", data pointers: [%p, %X]",
- host->scsi.SCp.ptr, host->scsi.SCp.this_residual);
-#endif
- }
-#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
- printk("\n");
-#endif
-
- host->dma.transferred = host->scsi.SCp.scsi_xferred;
-
- return host->SCpnt != NULL;
-}
-
-/*
- * Function: void acornscsi_disconnect_unexpected(AS_Host *host)
- * Purpose : handle an unexpected disconnect
- * Params : host - host on which disconnect occurred
- */
-static
-void acornscsi_disconnect_unexpected(AS_Host *host)
-{
- printk(KERN_ERR "scsi%d.%c: unexpected disconnect\n",
- host->host->host_no, acornscsi_target(host));
-#if (DEBUG & DEBUG_ABORT)
- acornscsi_dumplog(host, 8);
-#endif
-
- acornscsi_done(host, &host->SCpnt, DID_ERROR);
-}
-
-/*
- * Function: void acornscsi_abortcmd(AS_host *host, unsigned char tag)
- * Purpose : abort a currently executing command
- * Params : host - host with connected command to abort
- * tag - tag to abort
- */
-static
-void acornscsi_abortcmd(AS_Host *host, unsigned char tag)
-{
- host->scsi.phase = PHASE_ABORTED;
- sbic_arm_write(host->scsi.io_port, SBIC_CMND, CMND_ASSERTATN);
-
- msgqueue_flush(&host->scsi.msgs);
-#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
- if (tag)
- msgqueue_addmsg(&host->scsi.msgs, 2, ABORT_TAG, tag);
- else
-#endif
- msgqueue_addmsg(&host->scsi.msgs, 1, ABORT);
-}
-
-/* ==========================================================================================
- * Interrupt routines.
- */
-/*
- * Function: int acornscsi_sbicintr(AS_Host *host)
- * Purpose : handle interrupts from SCSI device
- * Params : host - host to process
- * Returns : INTR_PROCESS if expecting another SBIC interrupt
- * INTR_IDLE if no interrupt
- * INTR_NEXT_COMMAND if we have finished processing the command
- */
-static
-intr_ret_t acornscsi_sbicintr(AS_Host *host, int in_irq)
-{
- unsigned int asr, ssr;
-
- asr = sbic_arm_read(host->scsi.io_port, SBIC_ASR);
- if (!(asr & ASR_INT))
- return INTR_IDLE;
-
- ssr = sbic_arm_read(host->scsi.io_port, SBIC_SSR);
-
-#if (DEBUG & DEBUG_PHASES)
- print_sbic_status(asr, ssr, host->scsi.phase);
-#endif
-
- ADD_STATUS(8, ssr, host->scsi.phase, in_irq);
-
- if (host->SCpnt && !host->scsi.disconnectable)
- ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, in_irq);
-
- switch (ssr) {
- case 0x00: /* reset state - not advanced */
- printk(KERN_ERR "scsi%d: reset in standard mode but wanted advanced mode.\n",
- host->host->host_no);
- /* setup sbic - WD33C93A */
- sbic_arm_write(host->scsi.io_port, SBIC_OWNID, OWNID_EAF | host->host->this_id);
- sbic_arm_write(host->scsi.io_port, SBIC_CMND, CMND_RESET);
- return INTR_IDLE;
-
- case 0x01: /* reset state - advanced */
- sbic_arm_write(host->scsi.io_port, SBIC_CTRL, INIT_SBICDMA | CTRL_IDI);
- sbic_arm_write(host->scsi.io_port, SBIC_TIMEOUT, TIMEOUT_TIME);
- sbic_arm_write(host->scsi.io_port, SBIC_SYNCHTRANSFER, SYNCHTRANSFER_2DBA);
- sbic_arm_write(host->scsi.io_port, SBIC_SOURCEID, SOURCEID_ER | SOURCEID_DSP);
- msgqueue_flush(&host->scsi.msgs);
- return INTR_IDLE;
-
- case 0x41: /* unexpected disconnect aborted command */
- acornscsi_disconnect_unexpected(host);
- return INTR_NEXT_COMMAND;
- }
-
- switch (host->scsi.phase) {
- case PHASE_CONNECTING: /* STATE: command removed from issue queue */
- switch (ssr) {
- case 0x11: /* -> PHASE_CONNECTED */
- /* BUS FREE -> SELECTION */
- host->scsi.phase = PHASE_CONNECTED;
- msgqueue_flush(&host->scsi.msgs);
- host->dma.transferred = host->scsi.SCp.scsi_xferred;
- /* 33C93 gives next interrupt indicating bus phase */
- asr = sbic_arm_read(host->scsi.io_port, SBIC_ASR);
- if (!(asr & ASR_INT))
- break;
- ssr = sbic_arm_read(host->scsi.io_port, SBIC_SSR);
- ADD_STATUS(8, ssr, host->scsi.phase, 1);
- ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, 1);
- goto connected;
-
- case 0x42: /* select timed out */
- /* -> PHASE_IDLE */
- acornscsi_done(host, &host->SCpnt, DID_NO_CONNECT);
- return INTR_NEXT_COMMAND;
-
- case 0x81: /* -> PHASE_RECONNECTED or PHASE_ABORTED */
- /* BUS FREE -> RESELECTION */
- host->origSCpnt = host->SCpnt;
- host->SCpnt = NULL;
- msgqueue_flush(&host->scsi.msgs);
- acornscsi_reconnect(host);
- break;
-
- default:
- printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTING, SSR %02X?\n",
- host->host->host_no, acornscsi_target(host), ssr);
- acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
- acornscsi_abortcmd(host, host->SCpnt->tag);
- }
- return INTR_PROCESSING;
-
- connected:
- case PHASE_CONNECTED: /* STATE: device selected ok */
- switch (ssr) {
-#ifdef NONSTANDARD
- case 0x8a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */
- /* SELECTION -> COMMAND */
- acornscsi_sendcommand(host);
- break;
-
- case 0x8b: /* -> PHASE_STATUS */
- /* SELECTION -> STATUS */
- acornscsi_readstatusbyte(host);
- host->scsi.phase = PHASE_STATUSIN;
- break;
-#endif
-
- case 0x8e: /* -> PHASE_MSGOUT */
- /* SELECTION ->MESSAGE OUT */
- host->scsi.phase = PHASE_MSGOUT;
- acornscsi_buildmessages(host);
- acornscsi_sendmessage(host);
- break;
-
- /* these should not happen */
- case 0x85: /* target disconnected */
- acornscsi_done(host, &host->SCpnt, DID_ERROR);
- break;
-
- default:
- printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTED, SSR %02X?\n",
- host->host->host_no, acornscsi_target(host), ssr);
- acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
- acornscsi_abortcmd(host, host->SCpnt->tag);
- }
- return INTR_PROCESSING;
-
- case PHASE_MSGOUT: /* STATE: connected & sent IDENTIFY message */
- /*
- * SCSI standard says that MESSAGE OUT phases can be followed by a
- * DATA phase, STATUS phase, MESSAGE IN phase or COMMAND phase
- */
- switch (ssr) {
- case 0x8a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */
- case 0x1a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */
- /* MESSAGE OUT -> COMMAND */
- acornscsi_sendcommand(host);
- break;
-
- case 0x8b: /* -> PHASE_STATUS */
- case 0x1b: /* -> PHASE_STATUS */
- /* MESSAGE OUT -> STATUS */
- acornscsi_readstatusbyte(host);
- host->scsi.phase = PHASE_STATUSIN;
- break;
-
- case 0x8e: /* -> PHASE_MSGOUT */
- /* MESSAGE_OUT(MESSAGE_IN) ->MESSAGE OUT */
- acornscsi_sendmessage(host);
- break;
-
- case 0x4f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */
- case 0x1f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */
- /* MESSAGE OUT -> MESSAGE IN */
- acornscsi_message(host);
- break;
-
- default:
- printk(KERN_ERR "scsi%d.%c: PHASE_MSGOUT, SSR %02X?\n",
- host->host->host_no, acornscsi_target(host), ssr);
- acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
- }
- return INTR_PROCESSING;
-
- case PHASE_COMMAND: /* STATE: connected & command sent */
- switch (ssr) {
- case 0x18: /* -> PHASE_DATAOUT */
- /* COMMAND -> DATA OUT */
- if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len)
- acornscsi_abortcmd(host, host->SCpnt->tag);
- acornscsi_dma_setup(host, DMA_OUT);
- if (!acornscsi_starttransfer(host))
- acornscsi_abortcmd(host, host->SCpnt->tag);
- host->scsi.phase = PHASE_DATAOUT;
- return INTR_IDLE;
-
- case 0x19: /* -> PHASE_DATAIN */
- /* COMMAND -> DATA IN */
- if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len)
- acornscsi_abortcmd(host, host->SCpnt->tag);
- acornscsi_dma_setup(host, DMA_IN);
- if (!acornscsi_starttransfer(host))
- acornscsi_abortcmd(host, host->SCpnt->tag);
- host->scsi.phase = PHASE_DATAIN;
- return INTR_IDLE;
-
- case 0x1b: /* -> PHASE_STATUS */
- /* COMMAND -> STATUS */
- acornscsi_readstatusbyte(host);
- host->scsi.phase = PHASE_STATUSIN;
- break;
-
- case 0x1e: /* -> PHASE_MSGOUT */
- /* COMMAND -> MESSAGE OUT */
- acornscsi_sendmessage(host);
- break;
-
- case 0x1f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */
- /* COMMAND -> MESSAGE IN */
- acornscsi_message(host);
- break;
-
- default:
- printk(KERN_ERR "scsi%d.%c: PHASE_COMMAND, SSR %02X?\n",
- host->host->host_no, acornscsi_target(host), ssr);
- acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
- }
- return INTR_PROCESSING;
-
- case PHASE_DISCONNECT: /* STATE: connected, received DISCONNECT msg */
- if (ssr == 0x85) { /* -> PHASE_IDLE */
- host->scsi.disconnectable = 1;
- host->scsi.reconnected.tag = 0;
- host->scsi.phase = PHASE_IDLE;
- host->stats.disconnects += 1;
- } else {
- printk(KERN_ERR "scsi%d.%c: PHASE_DISCONNECT, SSR %02X instead of disconnect?\n",
- host->host->host_no, acornscsi_target(host), ssr);
- acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
- }
- return INTR_NEXT_COMMAND;
-
- case PHASE_IDLE: /* STATE: disconnected */
- if (ssr == 0x81) /* -> PHASE_RECONNECTED or PHASE_ABORTED */
- acornscsi_reconnect(host);
- else {
- printk(KERN_ERR "scsi%d.%c: PHASE_IDLE, SSR %02X while idle?\n",
- host->host->host_no, acornscsi_target(host), ssr);
- acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
- }
- return INTR_PROCESSING;
-
- case PHASE_RECONNECTED: /* STATE: device reconnected to initiator */
- /*
- * Command reconnected - if MESGIN, get message - it may be
- * the tag. If not, get command out of disconnected queue
- */
- /*
- * If we reconnected and we're not in MESSAGE IN phase after IDENTIFY,
- * reconnect I_T_L command
- */
- if (ssr != 0x8f && !acornscsi_reconnect_finish(host))
- return INTR_IDLE;
- ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, in_irq);
- switch (ssr) {
- case 0x88: /* data out phase */
- /* -> PHASE_DATAOUT */
- /* MESSAGE IN -> DATA OUT */
- acornscsi_dma_setup(host, DMA_OUT);
- if (!acornscsi_starttransfer(host))
- acornscsi_abortcmd(host, host->SCpnt->tag);
- host->scsi.phase = PHASE_DATAOUT;
- return INTR_IDLE;
-
- case 0x89: /* data in phase */
- /* -> PHASE_DATAIN */
- /* MESSAGE IN -> DATA IN */
- acornscsi_dma_setup(host, DMA_IN);
- if (!acornscsi_starttransfer(host))
- acornscsi_abortcmd(host, host->SCpnt->tag);
- host->scsi.phase = PHASE_DATAIN;
- return INTR_IDLE;
-
- case 0x8a: /* command out */
- /* MESSAGE IN -> COMMAND */
- acornscsi_sendcommand(host);/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */
- break;
-
- case 0x8b: /* status in */
- /* -> PHASE_STATUSIN */
- /* MESSAGE IN -> STATUS */
- acornscsi_readstatusbyte(host);
- host->scsi.phase = PHASE_STATUSIN;
- break;
-
- case 0x8e: /* message out */
- /* -> PHASE_MSGOUT */
- /* MESSAGE IN -> MESSAGE OUT */
- acornscsi_sendmessage(host);
- break;
-
- case 0x8f: /* message in */
- acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */
- break;
-
- default:
- printk(KERN_ERR "scsi%d.%c: PHASE_RECONNECTED, SSR %02X after reconnect?\n",
- host->host->host_no, acornscsi_target(host), ssr);
- acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
- }
- return INTR_PROCESSING;
-
- case PHASE_DATAIN: /* STATE: transferred data in */
- /*
- * This is simple - if we disconnect then the DMA address & count is
- * correct.
- */
- switch (ssr) {
- case 0x19: /* -> PHASE_DATAIN */
- case 0x89: /* -> PHASE_DATAIN */
- acornscsi_abortcmd(host, host->SCpnt->tag);
- return INTR_IDLE;
-
- case 0x1b: /* -> PHASE_STATUSIN */
- case 0x4b: /* -> PHASE_STATUSIN */
- case 0x8b: /* -> PHASE_STATUSIN */
- /* DATA IN -> STATUS */
- host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
- acornscsi_sbic_xfcount(host);
- acornscsi_dma_stop(host);
- acornscsi_readstatusbyte(host);
- host->scsi.phase = PHASE_STATUSIN;
- break;
-
- case 0x1e: /* -> PHASE_MSGOUT */
- case 0x4e: /* -> PHASE_MSGOUT */
- case 0x8e: /* -> PHASE_MSGOUT */
- /* DATA IN -> MESSAGE OUT */
- host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
- acornscsi_sbic_xfcount(host);
- acornscsi_dma_stop(host);
- acornscsi_sendmessage(host);
- break;
-
- case 0x1f: /* message in */
- case 0x4f: /* message in */
- case 0x8f: /* message in */
- /* DATA IN -> MESSAGE IN */
- host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
- acornscsi_sbic_xfcount(host);
- acornscsi_dma_stop(host);
- acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */
- break;
-
- default:
- printk(KERN_ERR "scsi%d.%c: PHASE_DATAIN, SSR %02X?\n",
- host->host->host_no, acornscsi_target(host), ssr);
- acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
- }
- return INTR_PROCESSING;
-
- case PHASE_DATAOUT: /* STATE: transferred data out */
- /*
- * This is more complicated - if we disconnect, the DMA could be 12
- * bytes ahead of us. We need to correct this.
- */
- switch (ssr) {
- case 0x18: /* -> PHASE_DATAOUT */
- case 0x88: /* -> PHASE_DATAOUT */
- acornscsi_abortcmd(host, host->SCpnt->tag);
- return INTR_IDLE;
-
- case 0x1b: /* -> PHASE_STATUSIN */
- case 0x4b: /* -> PHASE_STATUSIN */
- case 0x8b: /* -> PHASE_STATUSIN */
- /* DATA OUT -> STATUS */
- host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
- acornscsi_sbic_xfcount(host);
- acornscsi_dma_stop(host);
- acornscsi_dma_adjust(host);
- acornscsi_readstatusbyte(host);
- host->scsi.phase = PHASE_STATUSIN;
- break;
-
- case 0x1e: /* -> PHASE_MSGOUT */
- case 0x4e: /* -> PHASE_MSGOUT */
- case 0x8e: /* -> PHASE_MSGOUT */
- /* DATA OUT -> MESSAGE OUT */
- host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
- acornscsi_sbic_xfcount(host);
- acornscsi_dma_stop(host);
- acornscsi_dma_adjust(host);
- acornscsi_sendmessage(host);
- break;
-
- case 0x1f: /* message in */
- case 0x4f: /* message in */
- case 0x8f: /* message in */
- /* DATA OUT -> MESSAGE IN */
- host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
- acornscsi_sbic_xfcount(host);
- acornscsi_dma_stop(host);
- acornscsi_dma_adjust(host);
- acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */
- break;
-
- default:
- printk(KERN_ERR "scsi%d.%c: PHASE_DATAOUT, SSR %02X?\n",
- host->host->host_no, acornscsi_target(host), ssr);
- acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
- }
- return INTR_PROCESSING;
-
- case PHASE_STATUSIN: /* STATE: status in complete */
- switch (ssr) {
- case 0x1f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */
- case 0x8f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */
- /* STATUS -> MESSAGE IN */
- acornscsi_message(host);
- break;
-
- case 0x1e: /* -> PHASE_MSGOUT */
- case 0x8e: /* -> PHASE_MSGOUT */
- /* STATUS -> MESSAGE OUT */
- acornscsi_sendmessage(host);
- break;
-
- default:
- printk(KERN_ERR "scsi%d.%c: PHASE_STATUSIN, SSR %02X instead of MESSAGE_IN?\n",
- host->host->host_no, acornscsi_target(host), ssr);
- acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
- }
- return INTR_PROCESSING;
-
- case PHASE_MSGIN: /* STATE: message in */
- switch (ssr) {
- case 0x1e: /* -> PHASE_MSGOUT */
- case 0x4e: /* -> PHASE_MSGOUT */
- case 0x8e: /* -> PHASE_MSGOUT */
- /* MESSAGE IN -> MESSAGE OUT */
- acornscsi_sendmessage(host);
- break;
-
- case 0x1f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */
- case 0x2f:
- case 0x4f:
- case 0x8f:
- acornscsi_message(host);
- break;
-
- case 0x85:
- printk("scsi%d.%c: strange message in disconnection\n",
- host->host->host_no, acornscsi_target(host));
- acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
- acornscsi_done(host, &host->SCpnt, DID_ERROR);
- break;
-
- default:
- printk(KERN_ERR "scsi%d.%c: PHASE_MSGIN, SSR %02X after message in?\n",
- host->host->host_no, acornscsi_target(host), ssr);
- acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
- }
- return INTR_PROCESSING;
-
- case PHASE_DONE: /* STATE: received status & message */
- switch (ssr) {
- case 0x85: /* -> PHASE_IDLE */
- acornscsi_done(host, &host->SCpnt, DID_OK);
- return INTR_NEXT_COMMAND;
-
- case 0x1e:
- case 0x8e:
- acornscsi_sendmessage(host);
- break;
-
- default:
- printk(KERN_ERR "scsi%d.%c: PHASE_DONE, SSR %02X instead of disconnect?\n",
- host->host->host_no, acornscsi_target(host), ssr);
- acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
- }
- return INTR_PROCESSING;
-
- case PHASE_ABORTED:
- switch (ssr) {
- case 0x85:
- if (host->SCpnt)
- acornscsi_done(host, &host->SCpnt, DID_ABORT);
- else {
- clear_bit(host->scsi.reconnected.target * 8 + host->scsi.reconnected.lun,
- host->busyluns);
- host->scsi.phase = PHASE_IDLE;
- }
- return INTR_NEXT_COMMAND;
-
- case 0x1e:
- case 0x2e:
- case 0x4e:
- case 0x8e:
- acornscsi_sendmessage(host);
- break;
-
- default:
- printk(KERN_ERR "scsi%d.%c: PHASE_ABORTED, SSR %02X?\n",
- host->host->host_no, acornscsi_target(host), ssr);
- acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
- }
- return INTR_PROCESSING;
-
- default:
- printk(KERN_ERR "scsi%d.%c: unknown driver phase %d\n",
- host->host->host_no, acornscsi_target(host), ssr);
- acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
- }
- return INTR_PROCESSING;
-}
-
-/*
- * Prototype: void acornscsi_intr(int irq, void *dev_id)
- * Purpose : handle interrupts from Acorn SCSI card
- * Params : irq - interrupt number
- * dev_id - device specific data (AS_Host structure)
- */
-static irqreturn_t
-acornscsi_intr(int irq, void *dev_id)
-{
- AS_Host *host = (AS_Host *)dev_id;
- intr_ret_t ret;
- int iostatus;
- int in_irq = 0;
-
- do {
- ret = INTR_IDLE;
-
- iostatus = inb(host->card.io_intr);
-
- if (iostatus & 2) {
- acornscsi_dma_intr(host);
- iostatus = inb(host->card.io_intr);
- }
-
- if (iostatus & 8)
- ret = acornscsi_sbicintr(host, in_irq);
-
- /*
- * If we have a transfer pending, start it.
- * Only start it if the interface has already started transferring
- * it's data
- */
- if (host->dma.xfer_required)
- acornscsi_dma_xfer(host);
-
- if (ret == INTR_NEXT_COMMAND)
- ret = acornscsi_kick(host);
-
- in_irq = 1;
- } while (ret != INTR_IDLE);
-
- return IRQ_HANDLED;
-}
-
-/*=============================================================================================
- * Interfaces between interrupt handler and rest of scsi code
- */
-
-/*
- * Function : acornscsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
- * Purpose : queues a SCSI command
- * Params : cmd - SCSI command
- * done - function called on completion, with pointer to command descriptor
- * Returns : 0, or < 0 on error.
- */
-int acornscsi_queuecmd(struct scsi_cmnd *SCpnt,
- void (*done)(struct scsi_cmnd *))
-{
- AS_Host *host = (AS_Host *)SCpnt->device->host->hostdata;
-
- if (!done) {
- /* there should be some way of rejecting errors like this without panicing... */
- panic("scsi%d: queuecommand called with NULL done function [cmd=%p]",
- host->host->host_no, SCpnt);
- return -EINVAL;
- }
-
-#if (DEBUG & DEBUG_NO_WRITE)
- if (acornscsi_cmdtype(SCpnt->cmnd[0]) == CMD_WRITE && (NO_WRITE & (1 << SCpnt->device->id))) {
- printk(KERN_CRIT "scsi%d.%c: WRITE attempted with NO_WRITE flag set\n",
- host->host->host_no, '0' + SCpnt->device->id);
- SCpnt->result = DID_NO_CONNECT << 16;
- done(SCpnt);
- return 0;
- }
-#endif
-
- SCpnt->scsi_done = done;
- SCpnt->host_scribble = NULL;
- SCpnt->result = 0;
- SCpnt->tag = 0;
- SCpnt->SCp.phase = (int)acornscsi_datadirection(SCpnt->cmnd[0]);
- SCpnt->SCp.sent_command = 0;
- SCpnt->SCp.scsi_xferred = 0;
-
- init_SCp(SCpnt);
-
- host->stats.queues += 1;
-
- {
- unsigned long flags;
-
- if (!queue_add_cmd_ordered(&host->queues.issue, SCpnt)) {
- SCpnt->result = DID_ERROR << 16;
- done(SCpnt);
- return 0;
- }
- local_irq_save(flags);
- if (host->scsi.phase == PHASE_IDLE)
- acornscsi_kick(host);
- local_irq_restore(flags);
- }
- return 0;
-}
-
-/*
- * Prototype: void acornscsi_reportstatus(struct scsi_cmnd **SCpntp1, struct scsi_cmnd **SCpntp2, int result)
- * Purpose : pass a result to *SCpntp1, and check if *SCpntp1 = *SCpntp2
- * Params : SCpntp1 - pointer to command to return
- * SCpntp2 - pointer to command to check
- * result - result to pass back to mid-level done function
- * Returns : *SCpntp2 = NULL if *SCpntp1 is the same command structure as *SCpntp2.
- */
-static inline void acornscsi_reportstatus(struct scsi_cmnd **SCpntp1,
- struct scsi_cmnd **SCpntp2,
- int result)
-{
- struct scsi_cmnd *SCpnt = *SCpntp1;
-
- if (SCpnt) {
- *SCpntp1 = NULL;
-
- SCpnt->result = result;
- SCpnt->scsi_done(SCpnt);
- }
-
- if (SCpnt == *SCpntp2)
- *SCpntp2 = NULL;
-}
-
-enum res_abort { res_not_running, res_success, res_success_clear, res_snooze };
-
-/*
- * Prototype: enum res acornscsi_do_abort(struct scsi_cmnd *SCpnt)
- * Purpose : abort a command on this host
- * Params : SCpnt - command to abort
- * Returns : our abort status
- */
-static enum res_abort acornscsi_do_abort(AS_Host *host, struct scsi_cmnd *SCpnt)
-{
- enum res_abort res = res_not_running;
-
- if (queue_remove_cmd(&host->queues.issue, SCpnt)) {
- /*
- * The command was on the issue queue, and has not been
- * issued yet. We can remove the command from the queue,
- * and acknowledge the abort. Neither the devices nor the
- * interface know about the command.
- */
-//#if (DEBUG & DEBUG_ABORT)
- printk("on issue queue ");
-//#endif
- res = res_success;
- } else if (queue_remove_cmd(&host->queues.disconnected, SCpnt)) {
- /*
- * The command was on the disconnected queue. Simply
- * acknowledge the abort condition, and when the target
- * reconnects, we will give it an ABORT message. The
- * target should then disconnect, and we will clear
- * the busylun bit.
- */
-//#if (DEBUG & DEBUG_ABORT)
- printk("on disconnected queue ");
-//#endif
- res = res_success;
- } else if (host->SCpnt == SCpnt) {
- unsigned long flags;
-
-//#if (DEBUG & DEBUG_ABORT)
- printk("executing ");
-//#endif
-
- local_irq_save(flags);
- switch (host->scsi.phase) {
- /*
- * If the interface is idle, and the command is 'disconnectable',
- * then it is the same as on the disconnected queue. We simply
- * remove all traces of the command. When the target reconnects,
- * we will give it an ABORT message since the command could not
- * be found. When the target finally disconnects, we will clear
- * the busylun bit.
- */
- case PHASE_IDLE:
- if (host->scsi.disconnectable) {
- host->scsi.disconnectable = 0;
- host->SCpnt = NULL;
- res = res_success;
- }
- break;
-
- /*
- * If the command has connected and done nothing further,
- * simply force a disconnect. We also need to clear the
- * busylun bit.
- */
- case PHASE_CONNECTED:
- sbic_arm_write(host->scsi.io_port, SBIC_CMND, CMND_DISCONNECT);
- host->SCpnt = NULL;
- res = res_success_clear;
- break;
-
- default:
- acornscsi_abortcmd(host, host->SCpnt->tag);
- res = res_snooze;
- }
- local_irq_restore(flags);
- } else if (host->origSCpnt == SCpnt) {
- /*
- * The command will be executed next, but a command
- * is currently using the interface. This is similar to
- * being on the issue queue, except the busylun bit has
- * been set.
- */
- host->origSCpnt = NULL;
-//#if (DEBUG & DEBUG_ABORT)
- printk("waiting for execution ");
-//#endif
- res = res_success_clear;
- } else
- printk("unknown ");
-
- return res;
-}
-
-/*
- * Prototype: int acornscsi_abort(struct scsi_cmnd *SCpnt)
- * Purpose : abort a command on this host
- * Params : SCpnt - command to abort
- * Returns : one of SCSI_ABORT_ macros
- */
-int acornscsi_abort(struct scsi_cmnd *SCpnt)
-{
- AS_Host *host = (AS_Host *) SCpnt->device->host->hostdata;
- int result;
-
- host->stats.aborts += 1;
-
-#if (DEBUG & DEBUG_ABORT)
- {
- int asr, ssr;
- asr = sbic_arm_read(host->scsi.io_port, SBIC_ASR);
- ssr = sbic_arm_read(host->scsi.io_port, SBIC_SSR);
-
- printk(KERN_WARNING "acornscsi_abort: ");
- print_sbic_status(asr, ssr, host->scsi.phase);
- acornscsi_dumplog(host, SCpnt->device->id);
- }
-#endif
-
- printk("scsi%d: ", host->host->host_no);
-
- switch (acornscsi_do_abort(host, SCpnt)) {
- /*
- * We managed to find the command and cleared it out.
- * We do not expect the command to be executing on the
- * target, but we have set the busylun bit.
- */
- case res_success_clear:
-//#if (DEBUG & DEBUG_ABORT)
- printk("clear ");
-//#endif
- clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, host->busyluns);
-
- /*
- * We found the command, and cleared it out. Either
- * the command is still known to be executing on the
- * target, or the busylun bit is not set.
- */
- case res_success:
-//#if (DEBUG & DEBUG_ABORT)
- printk("success\n");
-//#endif
- SCpnt->result = DID_ABORT << 16;
- SCpnt->scsi_done(SCpnt);
- result = SCSI_ABORT_SUCCESS;
- break;
-
- /*
- * We did find the command, but unfortunately we couldn't
- * unhook it from ourselves. Wait some more, and if it
- * still doesn't complete, reset the interface.
- */
- case res_snooze:
-//#if (DEBUG & DEBUG_ABORT)
- printk("snooze\n");
-//#endif
- result = SCSI_ABORT_SNOOZE;
- break;
-
- /*
- * The command could not be found (either because it completed,
- * or it got dropped.
- */
- default:
- case res_not_running:
- acornscsi_dumplog(host, SCpnt->device->id);
-#if (DEBUG & DEBUG_ABORT)
- result = SCSI_ABORT_SNOOZE;
-#else
- result = SCSI_ABORT_NOT_RUNNING;
-#endif
-//#if (DEBUG & DEBUG_ABORT)
- printk("not running\n");
-//#endif
- break;
- }
-
- return result;
-}
-
-/*
- * Prototype: int acornscsi_reset(struct scsi_cmnd *SCpnt, unsigned int reset_flags)
- * Purpose : reset a command on this host/reset this host
- * Params : SCpnt - command causing reset
- * result - what type of reset to perform
- * Returns : one of SCSI_RESET_ macros
- */
-int acornscsi_reset(struct scsi_cmnd *SCpnt, unsigned int reset_flags)
-{
- AS_Host *host = (AS_Host *)SCpnt->device->host->hostdata;
- struct scsi_cmnd *SCptr;
-
- host->stats.resets += 1;
-
-#if (DEBUG & DEBUG_RESET)
- {
- int asr, ssr;
-
- asr = sbic_arm_read(host->scsi.io_port, SBIC_ASR);
- ssr = sbic_arm_read(host->scsi.io_port, SBIC_SSR);
-
- printk(KERN_WARNING "acornscsi_reset: ");
- print_sbic_status(asr, ssr, host->scsi.phase);
- acornscsi_dumplog(host, SCpnt->device->id);
- }
-#endif
-
- acornscsi_dma_stop(host);
-
- SCptr = host->SCpnt;
-
- /*
- * do hard reset. This resets all devices on this host, and so we
- * must set the reset status on all commands.
- */
- acornscsi_resetcard(host);
-
- /*
- * report reset on commands current connected/disconnected
- */
- acornscsi_reportstatus(&host->SCpnt, &SCptr, DID_RESET);
-
- while ((SCptr = queue_remove(&host->queues.disconnected)) != NULL)
- acornscsi_reportstatus(&SCptr, &SCpnt, DID_RESET);
-
- if (SCpnt) {
- SCpnt->result = DID_RESET << 16;
- SCpnt->scsi_done(SCpnt);
- }
-
- return SCSI_RESET_BUS_RESET | SCSI_RESET_HOST_RESET | SCSI_RESET_SUCCESS;
-}
-
-/*==============================================================================================
- * initialisation & miscellaneous support
- */
-
-/*
- * Function: char *acornscsi_info(struct Scsi_Host *host)
- * Purpose : return a string describing this interface
- * Params : host - host to give information on
- * Returns : a constant string
- */
-const
-char *acornscsi_info(struct Scsi_Host *host)
-{
- static char string[100], *p;
-
- p = string;
-
- p += sprintf(string, "%s at port %08lX irq %d v%d.%d.%d"
-#ifdef CONFIG_SCSI_ACORNSCSI_SYNC
- " SYNC"
-#endif
-#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
- " TAG"
-#endif
-#ifdef CONFIG_SCSI_ACORNSCSI_LINK
- " LINK"
-#endif
-#if (DEBUG & DEBUG_NO_WRITE)
- " NOWRITE ("NO_WRITE_STR")"
-#endif
- , host->hostt->name, host->io_port, host->irq,
- VER_MAJOR, VER_MINOR, VER_PATCH);
- return string;
-}
-
-int acornscsi_proc_info(struct Scsi_Host *instance, char *buffer, char **start, off_t offset,
- int length, int inout)
-{
- int pos, begin = 0, devidx;
- struct scsi_device *scd;
- AS_Host *host;
- char *p = buffer;
-
- if (inout == 1)
- return -EINVAL;
-
- host = (AS_Host *)instance->hostdata;
-
- p += sprintf(p, "AcornSCSI driver v%d.%d.%d"
-#ifdef CONFIG_SCSI_ACORNSCSI_SYNC
- " SYNC"
-#endif
-#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
- " TAG"
-#endif
-#ifdef CONFIG_SCSI_ACORNSCSI_LINK
- " LINK"
-#endif
-#if (DEBUG & DEBUG_NO_WRITE)
- " NOWRITE ("NO_WRITE_STR")"
-#endif
- "\n\n", VER_MAJOR, VER_MINOR, VER_PATCH);
-
- p += sprintf(p, "SBIC: WD33C93A Address: %08X IRQ : %d\n",
- host->scsi.io_port, host->scsi.irq);
-#ifdef USE_DMAC
- p += sprintf(p, "DMAC: uPC71071 Address: %08X IRQ : %d\n\n",
- host->dma.io_port, host->scsi.irq);
-#endif
-
- p += sprintf(p, "Statistics:\n"
- "Queued commands: %-10u Issued commands: %-10u\n"
- "Done commands : %-10u Reads : %-10u\n"
- "Writes : %-10u Others : %-10u\n"
- "Disconnects : %-10u Aborts : %-10u\n"
- "Resets : %-10u\n\nLast phases:",
- host->stats.queues, host->stats.removes,
- host->stats.fins, host->stats.reads,
- host->stats.writes, host->stats.miscs,
- host->stats.disconnects, host->stats.aborts,
- host->stats.resets);
-
- for (devidx = 0; devidx < 9; devidx ++) {
- unsigned int statptr, prev;
-
- p += sprintf(p, "\n%c:", devidx == 8 ? 'H' : ('0' + devidx));
- statptr = host->status_ptr[devidx] - 10;
-
- if ((signed int)statptr < 0)
- statptr += STATUS_BUFFER_SIZE;
-
- prev = host->status[devidx][statptr].when;
-
- for (; statptr != host->status_ptr[devidx]; statptr = (statptr + 1) & (STATUS_BUFFER_SIZE - 1)) {
- if (host->status[devidx][statptr].when) {
- p += sprintf(p, "%c%02X:%02X+%2ld",
- host->status[devidx][statptr].irq ? '-' : ' ',
- host->status[devidx][statptr].ph,
- host->status[devidx][statptr].ssr,
- (host->status[devidx][statptr].when - prev) < 100 ?
- (host->status[devidx][statptr].when - prev) : 99);
- prev = host->status[devidx][statptr].when;
- }
- }
- }
-
- p += sprintf(p, "\nAttached devices:\n");
-
- shost_for_each_device(scd, instance) {
- p += sprintf(p, "Device/Lun TaggedQ Sync\n");
- p += sprintf(p, " %d/%d ", scd->id, scd->lun);
- if (scd->tagged_supported)
- p += sprintf(p, "%3sabled(%3d) ",
- scd->simple_tags ? "en" : "dis",
- scd->current_tag);
- else
- p += sprintf(p, "unsupported ");
-
- if (host->device[scd->id].sync_xfer & 15)
- p += sprintf(p, "offset %d, %d ns\n",
- host->device[scd->id].sync_xfer & 15,
- acornscsi_getperiod(host->device[scd->id].sync_xfer));
- else
- p += sprintf(p, "async\n");
-
- pos = p - buffer;
- if (pos + begin < offset) {
- begin += pos;
- p = buffer;
- }
- pos = p - buffer;
- if (pos + begin > offset + length) {
- scsi_device_put(scd);
- break;
- }
- }
-
- pos = p - buffer;
-
- *start = buffer + (offset - begin);
- pos -= offset - begin;
-
- if (pos > length)
- pos = length;
-
- return pos;
-}
-
-static struct scsi_host_template acornscsi_template = {
- .module = THIS_MODULE,
- .proc_info = acornscsi_proc_info,
- .name = "AcornSCSI",
- .info = acornscsi_info,
- .queuecommand = acornscsi_queuecmd,
-#warning fixme
- .abort = acornscsi_abort,
- .reset = acornscsi_reset,
- .can_queue = 16,
- .this_id = 7,
- .sg_tablesize = SG_ALL,
- .cmd_per_lun = 2,
- .unchecked_isa_dma = 0,
- .use_clustering = DISABLE_CLUSTERING,
- .proc_name = "acornscsi",
-};
-
-static int __devinit
-acornscsi_probe(struct expansion_card *ec, const struct ecard_id *id)
-{
- struct Scsi_Host *host;
- AS_Host *ashost;
- int ret = -ENOMEM;
-
- host = scsi_host_alloc(&acornscsi_template, sizeof(AS_Host));
- if (!host)
- goto out;
-
- ashost = (AS_Host *)host->hostdata;
-
- host->io_port = ecard_address(ec, ECARD_MEMC, 0);
- host->irq = ec->irq;
-
- ashost->host = host;
- ashost->scsi.io_port = ioaddr(host->io_port + 0x800);
- ashost->scsi.irq = host->irq;
- ashost->card.io_intr = POD_SPACE(host->io_port) + 0x800;
- ashost->card.io_page = POD_SPACE(host->io_port) + 0xc00;
- ashost->card.io_ram = ioaddr(host->io_port);
- ashost->dma.io_port = host->io_port + 0xc00;
- ashost->dma.io_intr_clear = POD_SPACE(host->io_port) + 0x800;
-
- ec->irqaddr = (char *)ioaddr(ashost->card.io_intr);
- ec->irqmask = 0x0a;
-
- ret = -EBUSY;
- if (!request_region(host->io_port + 0x800, 2, "acornscsi(sbic)"))
- goto err_1;
- if (!request_region(ashost->card.io_intr, 1, "acornscsi(intr)"))
- goto err_2;
- if (!request_region(ashost->card.io_page, 1, "acornscsi(page)"))
- goto err_3;
-#ifdef USE_DMAC
- if (!request_region(ashost->dma.io_port, 256, "acornscsi(dmac)"))
- goto err_4;
-#endif
- if (!request_region(host->io_port, 2048, "acornscsi(ram)"))
- goto err_5;
-
- ret = request_irq(host->irq, acornscsi_intr, IRQF_DISABLED, "acornscsi", ashost);
- if (ret) {
- printk(KERN_CRIT "scsi%d: IRQ%d not free: %d\n",
- host->host_no, ashost->scsi.irq, ret);
- goto err_6;
- }
-
- memset(&ashost->stats, 0, sizeof (ashost->stats));
- queue_initialise(&ashost->queues.issue);
- queue_initialise(&ashost->queues.disconnected);
- msgqueue_initialise(&ashost->scsi.msgs);
-
- acornscsi_resetcard(ashost);
-
- ret = scsi_add_host(host, &ec->dev);
- if (ret)
- goto err_7;
-
- scsi_scan_host(host);
- goto out;
-
- err_7:
- free_irq(host->irq, ashost);
- err_6:
- release_region(host->io_port, 2048);
- err_5:
-#ifdef USE_DMAC
- release_region(ashost->dma.io_port, 256);
-#endif
- err_4:
- release_region(ashost->card.io_page, 1);
- err_3:
- release_region(ashost->card.io_intr, 1);
- err_2:
- release_region(host->io_port + 0x800, 2);
- err_1:
- scsi_host_put(host);
- out:
- return ret;
-}
-
-static void __devexit acornscsi_remove(struct expansion_card *ec)
-{
- struct Scsi_Host *host = ecard_get_drvdata(ec);
- AS_Host *ashost = (AS_Host *)host->hostdata;
-
- ecard_set_drvdata(ec, NULL);
- scsi_remove_host(host);
-
- /*
- * Put card into RESET state
- */
- outb(0x80, ashost->card.io_page);
-
- free_irq(host->irq, ashost);
-
- release_region(host->io_port + 0x800, 2);
- release_region(ashost->card.io_intr, 1);
- release_region(ashost->card.io_page, 1);
- release_region(ashost->dma.io_port, 256);
- release_region(host->io_port, 2048);
-
- msgqueue_free(&ashost->scsi.msgs);
- queue_free(&ashost->queues.disconnected);
- queue_free(&ashost->queues.issue);
- scsi_host_put(host);
-}
-
-static const struct ecard_id acornscsi_cids[] = {
- { MANU_ACORN, PROD_ACORN_SCSI },
- { 0xffff, 0xffff },
-};
-
-static struct ecard_driver acornscsi_driver = {
- .probe = acornscsi_probe,
- .remove = __devexit_p(acornscsi_remove),
- .id_table = acornscsi_cids,
- .drv = {
- .name = "acornscsi",
- },
-};
-
-static int __init acornscsi_init(void)
-{
- return ecard_register_driver(&acornscsi_driver);
-}
-
-static void __exit acornscsi_exit(void)
-{
- ecard_remove_driver(&acornscsi_driver);
-}
-
-module_init(acornscsi_init);
-module_exit(acornscsi_exit);
-
-MODULE_AUTHOR("Russell King");
-MODULE_DESCRIPTION("AcornSCSI driver");
-MODULE_LICENSE("GPL");
--- linux-2.6.20-rc3-mm1/drivers/scsi/arm/acornscsi-io.S 2006-11-29 22:57:37.000000000 +0100
+++ /dev/null 2006-09-19 00:45:31.000000000 +0200
@@ -1,145 +0,0 @@
-/*
- * linux/drivers/acorn/scsi/acornscsi-io.S: Acorn SCSI card IO
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-#include <linux/linkage.h>
-
-#include <asm/assembler.h>
-#include <asm/hardware.h>
-
-#if (IO_BASE == (PCIO_BASE & 0xff000000))
-#define ADDR(off,reg) \
- tst off, $0x80000000 ;\
- mov reg, $IO_BASE ;\
- orreq reg, reg, $(PCIO_BASE & 0x00ff0000)
-#else
-#define ADDR(off,reg) \
- tst off, $0x80000000 ;\
- movne reg, $IO_BASE ;\
- moveq reg, $(PCIO_BASE & 0xff000000) ;\
- orreq reg, reg, $(PCIO_BASE & 0x00ff0000)
-#endif
-
-@ Purpose: transfer a block of data from the acorn scsi card to memory
-@ Proto : void acornscsi_in(unsigned int addr_start, char *buffer, int length)
-@ Returns: nothing
-
- .align
-ENTRY(__acornscsi_in)
- stmfd sp!, {r4 - r7, lr}
- bic r0, r0, #3
- mov lr, #0xff
- orr lr, lr, #0xff00
-acornscsi_in16lp:
- subs r2, r2, #16
- bmi acornscsi_in8
- ldmia r0!, {r3, r4, r5, r6}
- and r3, r3, lr
- orr r3, r3, r4, lsl #16
- and r4, r5, lr
- orr r4, r4, r6, lsl #16
- ldmia r0!, {r5, r6, r7, ip}
- and r5, r5, lr
- orr r5, r5, r6, lsl #16
- and r6, r7, lr
- orr r6, r6, ip, lsl #16
- stmia r1!, {r3 - r6}
- bne acornscsi_in16lp
- LOADREGS(fd, sp!, {r4 - r7, pc})
-
-acornscsi_in8: adds r2, r2, #8
- bmi acornscsi_in4
- ldmia r0!, {r3, r4, r5, r6}
- and r3, r3, lr
- orr r3, r3, r4, lsl #16
- and r4, r5, lr
- orr r4, r4, r6, lsl #16
- stmia r1!, {r3 - r4}
- LOADREGS(eqfd, sp!, {r4 - r7, pc})
- sub r2, r2, #8
-
-acornscsi_in4: adds r2, r2, #4
- bmi acornscsi_in2
- ldmia r0!, {r3, r4}
- and r3, r3, lr
- orr r3, r3, r4, lsl #16
- str r3, [r1], #4
- LOADREGS(eqfd, sp!, {r4 - r7, pc})
- sub r2, r2, #4
-
-acornscsi_in2: adds r2, r2, #2
- ldr r3, [r0], #4
- and r3, r3, lr
- strb r3, [r1], #1
- mov r3, r3, lsr #8
- strplb r3, [r1], #1
- LOADREGS(fd, sp!, {r4 - r7, pc})
-
-@ Purpose: transfer a block of data from memory to the acorn scsi card
-@ Proto : void acornscsi_in(unsigned int addr_start, char *buffer, int length)
-@ Returns: nothing
-
-ENTRY(__acornscsi_out)
- stmfd sp!, {r4 - r6, lr}
- bic r0, r0, #3
-acornscsi_out16lp:
- subs r2, r2, #16
- bmi acornscsi_out8
- ldmia r1!, {r4, r6, ip, lr}
- mov r3, r4, lsl #16
- orr r3, r3, r3, lsr #16
- mov r4, r4, lsr #16
- orr r4, r4, r4, lsl #16
- mov r5, r6, lsl #16
- orr r5, r5, r5, lsr #16
- mov r6, r6, lsr #16
- orr r6, r6, r6, lsl #16
- stmia r0!, {r3, r4, r5, r6}
- mov r3, ip, lsl #16
- orr r3, r3, r3, lsr #16
- mov r4, ip, lsr #16
- orr r4, r4, r4, lsl #16
- mov ip, lr, lsl #16
- orr ip, ip, ip, lsr #16
- mov lr, lr, lsr #16
- orr lr, lr, lr, lsl #16
- stmia r0!, {r3, r4, ip, lr}
- bne acornscsi_out16lp
- LOADREGS(fd, sp!, {r4 - r6, pc})
-
-acornscsi_out8: adds r2, r2, #8
- bmi acornscsi_out4
- ldmia r1!, {r4, r6}
- mov r3, r4, lsl #16
- orr r3, r3, r3, lsr #16
- mov r4, r4, lsr #16
- orr r4, r4, r4, lsl #16
- mov r5, r6, lsl #16
- orr r5, r5, r5, lsr #16
- mov r6, r6, lsr #16
- orr r6, r6, r6, lsl #16
- stmia r0!, {r3, r4, r5, r6}
- LOADREGS(eqfd, sp!, {r4 - r6, pc})
-
- sub r2, r2, #8
-acornscsi_out4: adds r2, r2, #4
- bmi acornscsi_out2
- ldr r4, [r1], #4
- mov r3, r4, lsl #16
- orr r3, r3, r3, lsr #16
- mov r4, r4, lsr #16
- orr r4, r4, r4, lsl #16
- stmia r0!, {r3, r4}
- LOADREGS(eqfd, sp!, {r4 - r6, pc})
-
- sub r2, r2, #4
-acornscsi_out2: adds r2, r2, #2
- ldr r3, [r1], #2
- strb r3, [r0], #1
- mov r3, r3, lsr #8
- strplb r3, [r0], #1
- LOADREGS(fd, sp!, {r4 - r6, pc})
-
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/