blob: ee62d61330d836c3d13db7f77fb22932f874446f [file] [log] [blame]
#error Please convert me to Documentation/DMA-mapping.txt
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/pci.h>
#include <linux/string.h>
#include <linux/blk.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <asm/system.h>
#include "scsi.h"
#include "hosts.h"
#include "AM53C974.h"
#include "constants.h"
#include "sd.h"
/* AM53/79C974 (PCscsi) driver release 0.5
* The architecture and much of the code of this device
* driver was originally developed by Drew Eckhardt for
* the NCR5380. The following copyrights apply:
* For the architecture and all pieces of code which can also be found
* in the NCR5380 device driver:
* Copyright 1993, Drew Eckhardt
* Visionary Computing
* (Unix and Linux consulting and custom programming)
* drew@colorado.edu
* +1 (303) 666-5836
*
* The AM53C974_nobios_detect code was originally developed by
* Robin Cutshaw (robin@xfree86.org) and is used here in a
* slightly modified form.
*
* PCI detection rewritten by Martin Mares <mj@atrey.karlin.mff.cuni.cz>
*
* For the remaining code:
* Copyright 1994, D. Frieauff
* EMail: fri@rsx42sun0.dofn.de
* Phone: x49-7545-8-2256 , x49-7541-42305
*/
/*
* $Log: AM53C974.c,v $
*/
#ifdef AM53C974_DEBUG
#define DEB(x) x
#ifdef AM53C974_DEBUG_KEYWAIT
#define KEYWAIT() AM53C974_keywait()
#else
#define KEYWAIT()
#endif
#ifdef AM53C974_DEBUG_INIT
#define DEB_INIT(x) x
#else
#define DEB_INIT(x)
#endif
#ifdef AM53C974_DEBUG_MSG
#define DEB_MSG(x) x
#else
#define DEB_MSG(x)
#endif
#ifdef AM53C974_DEB_RESEL
#define DEB_RESEL(x) x
#else
#define DEB_RESEL(x)
#endif
#ifdef AM53C974_DEBUG_QUEUE
#define DEB_QUEUE(x) x
#define LIST(x,y) {printk("LINE:%d Adding %p to %p\n", __LINE__, (void*)(x), (void*)(y)); if ((x)==(y)) udelay(5); }
#define REMOVE(w,x,y,z) {printk("LINE:%d Removing: %p->%p %p->%p \n", __LINE__, (void*)(w), (void*)(x), (void*)(y), (void*)(z)); if ((x)==(y)) udelay(5); }
#else
#define DEB_QUEUE(x)
#define LIST(x,y)
#define REMOVE(w,x,y,z)
#endif
#ifdef AM53C974_DEBUG_INFO
#define DEB_INFO(x) x
#else
#define DEB_INFO(x)
#endif
#ifdef AM53C974_DEBUG_LINKED
#define DEB_LINKED(x) x
#else
#define DEB_LINKED(x)
#endif
#ifdef AM53C974_DEBUG_INTR
#define DEB_INTR(x) x
#else
#define DEB_INTR(x)
#endif
#else
#define DEB_INIT(x)
#define DEB(x)
#define DEB_QUEUE(x)
#define LIST(x,y)
#define REMOVE(w,x,y,z)
#define DEB_INFO(x)
#define DEB_LINKED(x)
#define DEB_INTR(x)
#define DEB_MSG(x)
#define DEB_RESEL(x)
#define KEYWAIT()
#endif
#ifdef AM53C974_DEBUG_ABORT
#define DEB_ABORT(x) x
#else
#define DEB_ABORT(x)
#endif
#ifdef VERBOSE_AM53C974_DEBUG
#define VDEB(x) x
#else
#define VDEB(x)
#endif
#define INSIDE(x,l,h) ( ((x) >= (l)) && ((x) <= (h)) )
#include <scsi/scsicam.h>
/***************************************************************************************
* Default setting of the controller's SCSI id. Edit and uncomment this only if your *
* BIOS does not correctly initialize the controller's SCSI id. *
* If you don't get a warning during boot, it is correctly initialized. *
****************************************************************************************/
/* #define AM53C974_SCSI_ID 7 */
/***************************************************************************************
* Default settings for sync. negotiation enable, transfer rate and sync. offset. *
* These settings can be replaced by LILO overrides (append) with the following syntax: *
* AM53C974=host-scsi-id, target-scsi-id, max-rate, max-offset *
* Sync. negotiation is disabled by default and will be enabled for those targets which *
* are specified in the LILO override *
****************************************************************************************/
#define DEFAULT_SYNC_NEGOTIATION_ENABLED 0 /* 0 or 1 */
#define DEFAULT_RATE 5 /* MHz, min: 3; max: 10 */
#define DEFAULT_SYNC_OFFSET 0 /* bytes, min: 0; max: 15; use 0 for async. mode */
/***************************************************************************************
* If defined, don't allow targets to disconnect during commands. This will reduce *
* performance, but may be worthwhile if you suspect the driver of corrupting data when *
* a disconnect happens. *
***************************************************************************************/
#define AM53C974_PROHIBIT_DISCONNECT
/* --------------------- don't edit below here --------------------- */
#define AM53C974_DRIVER_REVISION_MAJOR 0
#define AM53C974_DRIVER_REVISION_MINOR 5
#define SEPARATOR_LINE \
"--------------------------------------------------------------------------\n"
/* debug control */
/* #define AM53C974_DEBUG */
/* #define AM53C974_DEBUG_MSG */
/* #define AM53C974_DEBUG_KEYWAIT */
/* #define AM53C974_DEBUG_INIT */
/* #define AM53C974_DEBUG_QUEUE */
/* #define AM53C974_DEBUG_INFO */
/* #define AM53C974_DEBUG_LINKED */
/* #define VERBOSE_AM53C974_DEBUG */
/* #define AM53C974_DEBUG_INTR */
/* #define AM53C974_DEB_RESEL */
#define AM53C974_DEBUG_ABORT
/* #define AM53C974_OPTION_DEBUG_PROBE_ONLY */
/* special options/constants */
#define DEF_CLK 40 /* chip clock freq. in MHz */
#define MIN_PERIOD 4 /* for negotiation: min. number of clocks per cycle */
#define MAX_PERIOD 13 /* for negotiation: max. number of clocks per cycle */
#define MAX_OFFSET 15 /* for negotiation: max. offset (0=async) */
#define DEF_SCSI_TIMEOUT 245 /* STIMREG value, 40 Mhz */
#define DEF_STP 8 /* STPREG value assuming 5.0 MB/sec, FASTCLK, FASTSCSI */
#define DEF_SOF_RAD 0 /* REQ/ACK deassertion delay */
#define DEF_SOF_RAA 0 /* REQ/ACK assertion delay */
#define DEF_ETM 0 /* CNTLREG1, ext. timing mode */
#define DEF_PERE 1 /* CNTLREG1, parity error reporting */
#define DEF_CLKF 0 /* CLKFREG, 0=40 Mhz */
#define DEF_ENF 1 /* CNTLREG2, enable features */
#define DEF_ADIDCHK 0 /* CNTLREG3, additional ID check */
#define DEF_FASTSCSI 1 /* CNTLREG3, fast SCSI */
#define DEF_FASTCLK 1 /* CNTLREG3, fast clocking, 5 MB/sec at 40MHz chip clk */
#define DEF_GLITCH 1 /* CNTLREG4, glitch eater, 0=12ns, 1=35ns, 2=25ns, 3=off */
#define DEF_PWD 0 /* CNTLREG4, reduced power feature */
#define DEF_RAE 0 /* CNTLREG4, RAE active negation on REQ, ACK only */
#define DEF_RADE 1 /* 1CNTLREG4, active negation on REQ, ACK and data */
/*** SCSI block ***/
#define CTCLREG 0x00 /* r current transf. count, low byte */
#define CTCMREG 0x04 /* r current transf. count, middle byte */
#define CTCHREG 0x38 /* r current transf. count, high byte */
#define STCLREG 0x00 /* w start transf. count, low byte */
#define STCMREG 0x04 /* w start transf. count, middle byte */
#define STCHREG 0x38 /* w start transf. count, high byte */
#define FFREG 0x08 /* rw SCSI FIFO reg. */
#define STIMREG 0x14 /* w SCSI timeout reg. */
#define SDIDREG 0x10 /* w SCSI destination ID reg. */
#define SDIREG_MASK 0x07 /* mask */
#define STPREG 0x18 /* w synchronous transf. period reg. */
#define STPREG_STP 0x1F /* synchr. transfer period */
#define CLKFREG 0x24 /* w clock factor reg. */
#define CLKFREG_MASK 0x07 /* mask */
#define CMDREG 0x0C /* rw SCSI command reg. */
#define CMDREG_DMA 0x80 /* set DMA mode (set together with opcodes below) */
#define CMDREG_IT 0x10 /* information transfer */
#define CMDREG_ICCS 0x11 /* initiator command complete steps */
#define CMDREG_MA 0x12 /* message accepted */
#define CMDREG_TPB 0x98 /* transfer pad bytes, DMA mode only */
#define CMDREG_SATN 0x1A /* set ATN */
#define CMDREG_RATN 0x1B /* reset ATN */
#define CMDREG_SOAS 0x41 /* select without ATN steps */
#define CMDREG_SAS 0x42 /* select with ATN steps (1 msg byte) */
#define CMDREG_SASS 0x43 /* select with ATN and stop steps */
#define CMDREG_ESR 0x44 /* enable selection/reselection */
#define CMDREG_DSR 0x45 /* disable selection/reselection */
#define CMDREG_SA3S 0x46 /* select with ATN 3 steps (3 msg bytes) */
#define CMDREG_NOP 0x00 /* no operation */
#define CMDREG_CFIFO 0x01 /* clear FIFO */
#define CMDREG_RDEV 0x02 /* reset device */
#define CMDREG_RBUS 0x03 /* reset SCSI bus */
#define STATREG 0x10 /* r SCSI status reg. */
#define STATREG_INT 0x80 /* SCSI interrupt condition detected */
#define STATREG_IOE 0x40 /* SCSI illegal operation error detected */
#define STATREG_PE 0x20 /* SCSI parity error detected */
#define STATREG_CTZ 0x10 /* CTC reg decremented to zero */
#define STATREG_MSG 0x04 /* SCSI MSG phase (latched?) */
#define STATREG_CD 0x02 /* SCSI C/D phase (latched?) */
#define STATREG_IO 0x01 /* SCSI I/O phase (latched?) */
#define STATREG_PHASE 0x07 /* SCSI phase mask */
#define INSTREG 0x14 /* r interrupt status reg. */
#define INSTREG_SRST 0x80 /* SCSI reset detected */
#define INSTREG_ICMD 0x40 /* SCSI invalid command detected */
#define INSTREG_DIS 0x20 /* target disconnected or sel/resel timeout */
#define INSTREG_SR 0x10 /* device on bus has service request */
#define INSTREG_SO 0x08 /* successful operation */
#define INSTREG_RESEL 0x04 /* device reselected as initiator */
#define ISREG 0x18 /* r internal state reg. */
#define ISREG_SOF 0x08 /* synchronous offset flag (act. low) */
#define ISREG_IS 0x07 /* status of intermediate op. */
#define ISREG_OK_NO_STOP 0x04 /* selection successful */
#define ISREG_OK_STOP 0x01 /* selection successful */
#define CFIREG 0x1C /* r current FIFO/internal state reg. */
#define CFIREG_IS 0xE0 /* status of intermediate op. */
#define CFIREG_CF 0x1F /* number of bytes in SCSI FIFO */
#define SOFREG 0x1C /* w synchr. offset reg. */
#define SOFREG_RAD 0xC0 /* REQ/ACK deassertion delay (sync.) */
#define SOFREG_RAA 0x30 /* REQ/ACK assertion delay (sync.) */
#define SOFREG_SO 0x0F /* synch. offset (sync.) */
#define CNTLREG1 0x20 /* rw control register one */
#define CNTLREG1_ETM 0x80 /* set extended timing mode */
#define CNTLREG1_DISR 0x40 /* disable interrupt on SCSI reset */
#define CNTLREG1_PERE 0x10 /* enable parity error reporting */
#define CNTLREG1_SID 0x07 /* host adapter SCSI ID */
#define CNTLREG2 0x2C /* rw control register two */
#define CNTLREG2_ENF 0x40 /* enable features */
#define CNTLREG3 0x30 /* rw control register three */
#define CNTLREG3_ADIDCHK 0x80 /* additional ID check */
#define CNTLREG3_FASTSCSI 0x10 /* fast SCSI */
#define CNTLREG3_FASTCLK 0x08 /* fast SCSI clocking */
#define CNTLREG4 0x34 /* rw control register four */
#define CNTLREG4_GLITCH 0xC0 /* glitch eater */
#define CNTLREG4_PWD 0x20 /* reduced power feature */
#define CNTLREG4_RAE 0x08 /* write only, active negot. ctrl. */
#define CNTLREG4_RADE 0x04 /* active negot. ctrl. */
#define CNTLREG4_RES 0x10 /* reserved bit, must be 1 */
/*** DMA block ***/
#define DMACMD 0x40 /* rw command */
#define DMACMD_DIR 0x80 /* transfer direction (1=read from device) */
#define DMACMD_INTE_D 0x40 /* DMA transfer interrupt enable */
#define DMACMD_INTE_P 0x20 /* page transfer interrupt enable */
#define DMACMD_MDL 0x10 /* map to memory descriptor list */
#define DMACMD_DIAG 0x04 /* diagnostics, set to 0 */
#define DMACMD_IDLE 0x00 /* idle cmd */
#define DMACMD_BLAST 0x01 /* flush FIFO to memory */
#define DMACMD_ABORT 0x02 /* terminate DMA */
#define DMACMD_START 0x03 /* start DMA */
#define DMASTATUS 0x54 /* r status register */
#define DMASTATUS_BCMPLT 0x20 /* BLAST complete */
#define DMASTATUS_SCSIINT 0x10 /* SCSI interrupt pending */
#define DMASTATUS_DONE 0x08 /* DMA transfer terminated */
#define DMASTATUS_ABORT 0x04 /* DMA transfer aborted */
#define DMASTATUS_ERROR 0x02 /* DMA transfer error */
#define DMASTATUS_PWDN 0x02 /* power down indicator */
#define DMASTC 0x44 /* rw starting transfer count */
#define DMASPA 0x48 /* rw starting physical address */
#define DMAWBC 0x4C /* r working byte counter */
#define DMAWAC 0x50 /* r working address counter */
#define DMASMDLA 0x58 /* rw starting MDL address */
#define DMAWMAC 0x5C /* r working MDL counter */
/*** SCSI phases ***/
#define PHASE_MSGIN 0x07
#define PHASE_MSGOUT 0x06
#define PHASE_RES_1 0x05
#define PHASE_RES_0 0x04
#define PHASE_STATIN 0x03
#define PHASE_CMDOUT 0x02
#define PHASE_DATAIN 0x01
#define PHASE_DATAOUT 0x00
#define AM53C974_local_declare() unsigned long io_port
#define AM53C974_setio(instance) io_port = instance->io_port
#define AM53C974_read_8(addr) inb(io_port + (addr))
#define AM53C974_write_8(addr,x) outb((x), io_port + (addr))
#define AM53C974_read_16(addr) inw(io_port + (addr))
#define AM53C974_write_16(addr,x) outw((x), io_port + (addr))
#define AM53C974_read_32(addr) inl(io_port + (addr))
#define AM53C974_write_32(addr,x) outl((x), io_port + (addr))
#define AM53C974_poll_int() { do { statreg = AM53C974_read_8(STATREG); } \
while (!(statreg & STATREG_INT)) ; \
AM53C974_read_8(INSTREG) ; } /* clear int */
#define AM53C974_cfifo() (AM53C974_read_8(CFIREG) & CFIREG_CF)
/* These are "special" values for the tag parameter passed to AM53C974_select. */
#define TAG_NEXT -1 /* Use next free tag */
#define TAG_NONE -2 /* Establish I_T_L nexus instead of I_T_L_Q
* even on SCSI-II devices */
/************ LILO overrides *************/
typedef struct _override_t {
int host_scsi_id; /* SCSI id of the bus controller */
int target_scsi_id; /* SCSI id of target */
int max_rate; /* max. transfer rate */
int max_offset; /* max. sync. offset, 0 = asynchronous */
} override_t;
#ifdef AM53C974_DEBUG
static void AM53C974_print_phase(struct Scsi_Host *instance);
static void AM53C974_print_queues(struct Scsi_Host *instance);
#endif /* AM53C974_DEBUG */
static void AM53C974_print(struct Scsi_Host *instance);
static void AM53C974_keywait(void);
static __inline__ int AM53C974_pci_detect(Scsi_Host_Template * tpnt);
static int AM53C974_init(Scsi_Host_Template * tpnt, struct pci_dev *pdev);
static void AM53C974_config_after_reset(struct Scsi_Host *instance);
static __inline__ void initialize_SCp(Scsi_Cmnd * cmd);
static __inline__ void run_main(void);
static void AM53C974_main(void);
static void AM53C974_intr(int irq, void *dev_id, struct pt_regs *regs);
static void do_AM53C974_intr(int irq, void *dev_id, struct pt_regs *regs);
static void AM53C974_intr_disconnect(struct Scsi_Host *instance);
static int AM53C974_sync_neg(struct Scsi_Host *instance, int target, unsigned char *msg);
static __inline__ void AM53C974_set_async(struct Scsi_Host *instance, int target);
static __inline__ void AM53C974_set_sync(struct Scsi_Host *instance, int target);
static void AM53C974_information_transfer(struct Scsi_Host *instance,
unsigned char statreg, unsigned char isreg,
unsigned char instreg, unsigned char cfifo,
unsigned char dmastatus);
static int AM53C974_message(struct Scsi_Host *instance, Scsi_Cmnd * cmd, unsigned char msg);
static void AM53C974_select(struct Scsi_Host *instance, Scsi_Cmnd * cmd, int tag);
static void AM53C974_intr_reselect(struct Scsi_Host *instance, unsigned char statreg);
static __inline__ void AM53C974_transfer_dma(struct Scsi_Host *instance, short dir,
unsigned long length, char *data);
static void AM53C974_dma_blast(struct Scsi_Host *instance, unsigned char dmastatus,
unsigned char statreg);
static void AM53C974_intr_bus_reset(struct Scsi_Host *instance);
static struct Scsi_Host *first_instance;
static Scsi_Host_Template *the_template;
static struct Scsi_Host *first_host; /* Head of list of AMD boards */
static volatile int main_running;
static int commandline_current;
override_t overrides[7] =
{
{-1, 0, 0, 0},}; /* LILO overrides */
#ifdef AM53C974_DEBUG
static int deb_stop = 1;
static struct {
unsigned char value;
char *name;
} phases[] = {
{
PHASE_DATAOUT, "DATAOUT"
}, {
PHASE_DATAIN, "DATAIN"
}, {
PHASE_CMDOUT, "CMDOUT"
},
{
PHASE_STATIN, "STATIN"
}, {
PHASE_MSGOUT, "MSGOUT"
}, {
PHASE_MSGIN, "MSGIN"
},
{
PHASE_RES_0, "RESERVED 0"
}, {
PHASE_RES_1, "RESERVED 1"
}
};
/**************************************************************************
* Function : void AM53C974_print_phase(struct Scsi_Host *instance)
*
* Purpose : print the current SCSI phase for debugging purposes
*
* Input : instance - which AM53C974
**************************************************************************/
static void AM53C974_print_phase(struct Scsi_Host *instance)
{
AM53C974_local_declare();
unsigned char statreg, latched;
int i;
AM53C974_setio(instance);
latched = (AM53C974_read_8(CNTLREG2)) & CNTLREG2_ENF;
statreg = AM53C974_read_8(STATREG);
for (i = 0; (phases[i].value != PHASE_RES_1) &&
(phases[i].value != (statreg & STATREG_PHASE)); ++i);
if (latched)
printk("scsi%d : phase %s, latched at end of last command\n", instance->host_no, phases[i].name);
else
printk("scsi%d : phase %s, real time\n", instance->host_no, phases[i].name);
}
/**************************************************************************
* Function : void AM53C974_print_queues(struct Scsi_Host *instance)
*
* Purpose : print commands in the various queues
*
* Inputs : instance - which AM53C974
**************************************************************************/
static void AM53C974_print_queues(struct Scsi_Host *instance)
{
unsigned long flags;
struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *) instance->hostdata;
Scsi_Cmnd *ptr;
printk("AM53C974: coroutine is%s running.\n", main_running ? "" : "n't");
save_flags(flags);
cli();
if (!hostdata->connected) {
printk("scsi%d: no currently connected command\n", instance->host_no);
} else {
print_Scsi_Cmnd((Scsi_Cmnd *) hostdata->connected);
}
if (!hostdata->sel_cmd) {
printk("scsi%d: no currently arbitrating command\n", instance->host_no);
} else {
print_Scsi_Cmnd((Scsi_Cmnd *) hostdata->sel_cmd);
}
printk("scsi%d: issue_queue ", instance->host_no);
if (!hostdata->issue_queue)
printk("empty\n");
else {
printk(":\n");
for (ptr = (Scsi_Cmnd *) hostdata->issue_queue; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble)
print_Scsi_Cmnd(ptr);
}
printk("scsi%d: disconnected_queue ", instance->host_no);
if (!hostdata->disconnected_queue)
printk("empty\n");
else {
printk(":\n");
for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble)
print_Scsi_Cmnd(ptr);
}
restore_flags(flags);
}
#endif /* AM53C974_DEBUG */
/**************************************************************************
* Function : void AM53C974_print(struct Scsi_Host *instance)
*
* Purpose : dump the chip registers for debugging purposes
*
* Input : instance - which AM53C974
**************************************************************************/
static void AM53C974_print(struct Scsi_Host *instance)
{
AM53C974_local_declare();
unsigned long flags;
unsigned long ctcreg, dmastc, dmaspa, dmawbc, dmawac;
unsigned char cmdreg, statreg, isreg, cfireg, cntlreg[4], dmacmd,
dmastatus;
AM53C974_setio(instance);
save_flags(flags);
cli();
ctcreg = AM53C974_read_8(CTCHREG) << 16;
ctcreg |= AM53C974_read_8(CTCMREG) << 8;
ctcreg |= AM53C974_read_8(CTCLREG);
cmdreg = AM53C974_read_8(CMDREG);
statreg = AM53C974_read_8(STATREG);
isreg = AM53C974_read_8(ISREG);
cfireg = AM53C974_read_8(CFIREG);
cntlreg[0] = AM53C974_read_8(CNTLREG1);
cntlreg[1] = AM53C974_read_8(CNTLREG2);
cntlreg[2] = AM53C974_read_8(CNTLREG3);
cntlreg[3] = AM53C974_read_8(CNTLREG4);
dmacmd = AM53C974_read_8(DMACMD);
dmastc = AM53C974_read_32(DMASTC);
dmaspa = AM53C974_read_32(DMASPA);
dmawbc = AM53C974_read_32(DMAWBC);
dmawac = AM53C974_read_32(DMAWAC);
dmastatus = AM53C974_read_8(DMASTATUS);
restore_flags(flags);
printk("AM53C974 register dump:\n");
printk("IO base: 0x%04lx; CTCREG: 0x%04lx; CMDREG: 0x%02x; STATREG: 0x%02x; ISREG: 0x%02x\n",
io_port, ctcreg, cmdreg, statreg, isreg);
printk("CFIREG: 0x%02x; CNTLREG1-4: 0x%02x; 0x%02x; 0x%02x; 0x%02x\n",
cfireg, cntlreg[0], cntlreg[1], cntlreg[2], cntlreg[3]);
printk("DMACMD: 0x%02x; DMASTC: 0x%04lx; DMASPA: 0x%04lx\n", dmacmd, dmastc, dmaspa);
printk("DMAWBC: 0x%04lx; DMAWAC: 0x%04lx; DMASTATUS: 0x%02x\n", dmawbc, dmawac, dmastatus);
printk("---------------------------------------------------------\n");
}
/**************************************************************************
* Function : void AM53C974_keywait(void)
*
* Purpose : wait until a key is pressed, if it was the 'r' key leave singlestep mode;
* this function is used for debugging only
*
* Input : none
**************************************************************************/
static void AM53C974_keywait(void)
{
unsigned long flags;
#ifdef AM53C974_DEBUG
int key;
if (!deb_stop)
return;
#endif
save_flags(flags);
cli();
while ((inb_p(0x64) & 0x01) != 0x01);
#ifdef AM53C974_DEBUG
key = inb(0x60);
if (key == 0x93)
deb_stop = 0; /* don't stop if 'r' was pressed */
#endif
restore_flags(flags);
}
#ifndef MODULE
/**************************************************************************
* Function : AM53C974_setup(char *str)
*
* Purpose : LILO command line initialization of the overrides array,
*
* Input : str - parameter string.
*
* Returns : 1.
*
* NOTE : this function needs to be declared as an external function
* in init/main.c and included there in the bootsetups list
***************************************************************************/
static int AM53C974_setup(char *str)
{
int ints[5];
get_options(str, ARRAY_SIZE(ints), ints);
if (ints[0] < 4)
printk("AM53C974_setup: wrong number of parameters;\n correct syntax is: AM53C974=host-scsi-id, target-scsi-id, max-rate, max-offset\n");
else {
if (commandline_current < (sizeof(overrides) / sizeof(override_t))) {
if ((ints[1] < 0) || (ints[1] > 7) ||
(ints[2] < 0) || (ints[2] > 7) ||
(ints[1] == ints[2]) ||
(ints[3] < (DEF_CLK / MAX_PERIOD)) || (ints[3] > (DEF_CLK / MIN_PERIOD)) ||
(ints[4] < 0) || (ints[4] > MAX_OFFSET))
printk("AM53C974_setup: illegal parameter\n");
else {
overrides[commandline_current].host_scsi_id = ints[1];
overrides[commandline_current].target_scsi_id = ints[2];
overrides[commandline_current].max_rate = ints[3];
overrides[commandline_current].max_offset = ints[4];
commandline_current++;
}
} else
printk("AM53C974_setup: too many overrides\n");
}
return 1;
}
__setup("AM53C974=", AM53C974_setup);
#endif /* !MODULE */
/**************************************************************************
* Function : int AM53C974_pci_detect(Scsi_Host_Template *tpnt)
*
* Purpose : detects and initializes AM53C974 SCSI chips with PCI Bios
*
* Inputs : tpnt - host template
*
* Returns : number of host adapters detected
**************************************************************************/
static int __init AM53C974_pci_detect(Scsi_Host_Template * tpnt)
{
int count = 0; /* number of boards detected */
struct pci_dev *pdev = NULL;
unsigned short command;
while ((pdev = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_SCSI, pdev))) {
if (pci_enable_device(pdev))
continue;
pci_read_config_word(pdev, PCI_COMMAND, &command);
/* check whether device is I/O mapped -- should be */
if (!(command & PCI_COMMAND_IO))
continue;
pci_set_master (pdev);
/* everything seems OK now, so initialize */
if (AM53C974_init(tpnt, pdev))
count++;
}
return (count);
}
/**************************************************************************
* Function : int AM53C974_init(Scsi_Host_Template *tpnt, struct pci_dev *pdev)
*
* Purpose : initializes instance and corresponding AM53/79C974 chip,
*
* Inputs : tpnt - template, pci_config - PCI configuration,
*
* Returns : 1 on success, 0 on failure.
*
* NOTE: If no override for the controller's SCSI id is given and AM53C974_SCSI_ID
* is not defined we assume that the SCSI address of this controller is correctly
* set up by the BIOS (as reflected by contents of register CNTLREG1).
* This is the only BIOS assistance we need.
**************************************************************************/
static int __init AM53C974_init(Scsi_Host_Template * tpnt, struct pci_dev *pdev)
{
AM53C974_local_declare();
int i, j;
struct Scsi_Host *instance, *search;
struct AM53C974_hostdata *hostdata;
#ifdef AM53C974_OPTION_DEBUG_PROBE_ONLY
printk("AM53C974: probe only enabled, aborting initialization\n");
return 0;
#endif
instance = scsi_register(tpnt, sizeof(struct AM53C974_hostdata));
if (!instance) {
printk(KERN_WARNING "AM53C974: Unable to register host, aborting.\n");
return 0;
}
scsi_set_pci_device(instance, pdev);
hostdata = (struct AM53C974_hostdata *) instance->hostdata;
instance->base = 0;
instance->io_port = pci_resource_start(pdev, 0);
instance->irq = pdev->irq;
instance->dma_channel = -1;
AM53C974_setio(instance);
#ifdef AM53C974_SCSI_ID
instance->this_id = AM53C974_SCSI_ID;
AM53C974_write_8(CNTLREG1, instance->this_id & CNTLREG1_SID);
#else
instance->this_id = AM53C974_read_8(CNTLREG1) & CNTLREG1_SID;
if (instance->this_id != 7)
printk("scsi%d: WARNING: unusual hostadapter SCSI id %d; please verify!\n",
instance->host_no, instance->this_id);
#endif
for (i = 0; i < sizeof(hostdata->msgout); i++) {
hostdata->msgout[i] = NOP;
hostdata->last_message[i] = NOP;
}
for (i = 0; i < 8; i++) {
hostdata->busy[i] = 0;
hostdata->sync_per[i] = DEF_STP;
hostdata->sync_off[i] = 0;
hostdata->sync_neg[i] = 0;
hostdata->sync_en[i] = DEFAULT_SYNC_NEGOTIATION_ENABLED;
hostdata->max_rate[i] = DEFAULT_RATE;
hostdata->max_offset[i] = DEFAULT_SYNC_OFFSET;
}
/* overwrite defaults by LILO overrides */
for (i = 0; i < commandline_current; i++) {
if (overrides[i].host_scsi_id == instance->this_id) {
j = overrides[i].target_scsi_id;
hostdata->sync_en[j] = 1;
hostdata->max_rate[j] = overrides[i].max_rate;
hostdata->max_offset[j] = overrides[i].max_offset;
}
}
hostdata->sel_cmd = NULL;
hostdata->connected = NULL;
hostdata->issue_queue = NULL;
hostdata->disconnected_queue = NULL;
hostdata->in_reset = 0;
hostdata->aborted = 0;
hostdata->selecting = 0;
hostdata->disconnecting = 0;
hostdata->dma_busy = 0;
/* Set up an interrupt handler if we aren't already sharing an IRQ with another board */
for (search = first_host;
search && (((the_template != NULL) && (search->hostt != the_template)) ||
(search->irq != instance->irq) || (search == instance));
search = search->next);
if (!search) {
if (request_irq(instance->irq, do_AM53C974_intr, SA_SHIRQ, "AM53C974", instance)) {
printk("scsi%d: IRQ%d not free, detaching\n", instance->host_no, instance->irq);
scsi_unregister(instance);
return 0;
}
} else {
printk("scsi%d: using interrupt handler previously installed for scsi%d\n",
instance->host_no, search->host_no);
}
if (!the_template) {
the_template = instance->hostt;
first_instance = instance;
}
/* do hard reset */
AM53C974_write_8(CMDREG, CMDREG_RDEV); /* reset device */
udelay(5);
AM53C974_write_8(CMDREG, CMDREG_NOP);
AM53C974_write_8(CNTLREG1, CNTLREG1_DISR | instance->this_id);
AM53C974_write_8(CMDREG, CMDREG_RBUS); /* reset SCSI bus */
udelay(10);
AM53C974_config_after_reset(instance);
mdelay(500);
return (1);
}
/*********************************************************************
* Function : AM53C974_config_after_reset(struct Scsi_Host *instance) *
* *
* Purpose : initializes chip registers after reset *
* *
* Inputs : instance - which AM53C974 *
* *
* Returns : nothing *
**********************************************************************/
static void AM53C974_config_after_reset(struct Scsi_Host *instance)
{
AM53C974_local_declare();
AM53C974_setio(instance);
/* clear SCSI FIFO */
AM53C974_write_8(CMDREG, CMDREG_CFIFO);
/* configure device */
AM53C974_write_8(STIMREG, DEF_SCSI_TIMEOUT);
AM53C974_write_8(STPREG, DEF_STP & STPREG_STP);
AM53C974_write_8(SOFREG, (DEF_SOF_RAD << 6) | (DEF_SOF_RAA << 4));
AM53C974_write_8(CLKFREG, DEF_CLKF & CLKFREG_MASK);
AM53C974_write_8(CNTLREG1, (DEF_ETM << 7) | CNTLREG1_DISR | (DEF_PERE << 4) | instance->this_id);
AM53C974_write_8(CNTLREG2, (DEF_ENF << 6));
AM53C974_write_8(CNTLREG3, (DEF_ADIDCHK << 7) | (DEF_FASTSCSI << 4) | (DEF_FASTCLK << 3));
AM53C974_write_8(CNTLREG4, (DEF_GLITCH << 6) | (DEF_PWD << 5) | (DEF_RAE << 3) | (DEF_RADE << 2) | CNTLREG4_RES);
}
/***********************************************************************
* Function : const char *AM53C974_info(struct Scsi_Host *instance) *
* *
* Purpose : return device driver information *
* *
* Inputs : instance - which AM53C974 *
* *
* Returns : info string *
************************************************************************/
static const char *AM53C974_info(struct Scsi_Host *instance)
{
static char info[100];
sprintf(info, "AM53/79C974 PCscsi driver rev. %d.%d; host I/O address: 0x%lx; irq: %d\n",
AM53C974_DRIVER_REVISION_MAJOR, AM53C974_DRIVER_REVISION_MINOR,
instance->io_port, instance->irq);
return (info);
}
/**************************************************************************
* Function : int AM53C974_command (Scsi_Cmnd *SCpnt) *
* *
* Purpose : the unqueued SCSI command function, replaced by the *
* AM53C974_queue_command function *
* *
* Inputs : SCpnt - pointer to command structure *
* *
* Returns :status, see hosts.h for details *
***************************************************************************/
static int AM53C974_command(Scsi_Cmnd * SCpnt)
{
DEB(printk("AM53C974_command called\n"));
return 0;
}
/**************************************************************************
* Function : void initialize_SCp(Scsi_Cmnd *cmd) *
* *
* Purpose : initialize the saved data pointers for cmd to point to the *
* start of the buffer. *
* *
* Inputs : cmd - Scsi_Cmnd structure to have pointers reset. *
* *
* Returns : nothing *
**************************************************************************/
static __inline__ void initialize_SCp(Scsi_Cmnd * cmd)
{
if (cmd->use_sg) {
cmd->SCp.buffer = (struct scatterlist *) cmd->buffer;
cmd->SCp.buffers_residual = cmd->use_sg - 1;
cmd->SCp.ptr = (char *) cmd->SCp.buffer->address;
cmd->SCp.this_residual = cmd->SCp.buffer->length;
} else {
cmd->SCp.buffer = NULL;
cmd->SCp.buffers_residual = 0;
cmd->SCp.ptr = (char *) cmd->request_buffer;
cmd->SCp.this_residual = cmd->request_bufflen;
}
}
/**************************************************************************
* Function : run_main(void) *
* *
* Purpose : insure that the coroutine is running and will process our *
* request. main_running is checked/set here (in an inline *
* function rather than in AM53C974_main itself to reduce the *
* chances of stack overflow. *
* *
* *
* Inputs : none *
* *
* Returns : nothing *
**************************************************************************/
static __inline__ void run_main(void)
{
unsigned long flags;
save_flags(flags);
cli();
if (!main_running) {
/* main_running is cleared in AM53C974_main once it can't do
more work, and AM53C974_main exits with interrupts disabled. */
main_running = 1;
AM53C974_main();
}
restore_flags(flags);
}
/**************************************************************************
* Function : int AM53C974_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
*
* Purpose : writes SCSI command into AM53C974 FIFO
*
* Inputs : cmd - SCSI command, done - function called on completion, with
* a pointer to the command descriptor.
*
* Returns : status, see hosts.h for details
*
* Side effects :
* cmd is added to the per instance issue_queue, with minor
* twiddling done to the host specific fields of cmd. If the
* main coroutine is not running, it is restarted.
**************************************************************************/
static int AM53C974_queue_command(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
{
unsigned long flags;
struct Scsi_Host *instance = cmd->host;
struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *) instance->hostdata;
Scsi_Cmnd *tmp;
save_flags(flags);
cli();
DEB_QUEUE(printk(SEPARATOR_LINE));
DEB_QUEUE(printk("scsi%d: AM53C974_queue_command called\n", instance->host_no));
DEB_QUEUE(printk("cmd=%02x target=%02x lun=%02x bufflen=%d use_sg = %02x\n",
cmd->cmnd[0], cmd->target, cmd->lun, cmd->request_bufflen, cmd->use_sg));
/* We use the host_scribble field as a pointer to the next command in a queue */
cmd->host_scribble = NULL;
cmd->scsi_done = done;
cmd->result = 0;
cmd->device->disconnect = 0;
/* Insert the cmd into the issue queue. Note that REQUEST SENSE
* commands are added to the head of the queue since any command will
* clear the contingent allegiance condition that exists and the
* sense data is only guaranteed to be valid while the condition exists. */
if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) {
LIST(cmd, hostdata->issue_queue);
cmd->host_scribble = (unsigned char *) hostdata->issue_queue;
hostdata->issue_queue = cmd;
} else {
for (tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp->host_scribble;
tmp = (Scsi_Cmnd *) tmp->host_scribble);
LIST(cmd, tmp);
tmp->host_scribble = (unsigned char *) cmd;
}
DEB_QUEUE(printk("scsi%d : command added to %s of queue\n", instance->host_no,
(cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail"));
/* Run the coroutine if it isn't already running. */
run_main();
restore_flags(flags);
return 0;
}
/**************************************************************************
* Function : AM53C974_main (void)
*
* Purpose : AM53C974_main is a coroutine that runs as long as more work can
* be done on the AM53C974 host adapters in a system. Both
* AM53C974_queue_command() and AM53C974_intr() will try to start it
* in case it is not running.
*
* NOTE : AM53C974_main exits with interrupts *disabled*, the caller should
* reenable them. This prevents reentrancy and kernel stack overflow.
**************************************************************************/
static void AM53C974_main(void)
{
AM53C974_local_declare();
unsigned long flags;
Scsi_Cmnd *tmp, *prev;
struct Scsi_Host *instance;
struct AM53C974_hostdata *hostdata;
int done;
/* We run (with interrupts disabled) until we're sure that none of
* the host adapters have anything that can be done, at which point
* we set main_running to 0 and exit. */
save_flags(flags);
cli(); /* Freeze request queues */
do {
done = 1;
for (instance = first_instance; instance && instance->hostt == the_template;
instance = instance->next) {
hostdata = (struct AM53C974_hostdata *) instance->hostdata;
AM53C974_setio(instance);
/* start to select target if we are not connected and not in the
selection process */
if (!hostdata->connected && !hostdata->sel_cmd) {
/* Search through the issue_queue for a command destined for a target
that is not busy. */
for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, prev = NULL; tmp;
prev = tmp, tmp = (Scsi_Cmnd *) tmp->host_scribble) {
/* When we find one, remove it from the issue queue. */
if (!(hostdata->busy[tmp->target] & (1 << tmp->lun))) {
if (prev) {
REMOVE(prev, (Scsi_Cmnd *) (prev->host_scribble), tmp,
(Scsi_Cmnd *) (tmp->host_scribble));
prev->host_scribble = tmp->host_scribble;
} else {
REMOVE(-1, hostdata->issue_queue, tmp, tmp->host_scribble);
hostdata->issue_queue = (Scsi_Cmnd *) tmp->host_scribble;
}
tmp->host_scribble = NULL;
/* go into selection mode, disable reselection and wait for
SO interrupt which will continue with the selection process */
hostdata->selecting = 1;
hostdata->sel_cmd = tmp;
AM53C974_write_8(CMDREG, CMDREG_DSR);
break;
} /* if target/lun is not busy */
} /* for */
}
/* if (!hostdata->connected) */
else {
DEB(printk("main: connected; cmd = 0x%lx, sel_cmd = 0x%lx\n",
(long) hostdata->connected, (long) hostdata->sel_cmd));
}
} /* for instance */
} while (!done);
main_running = 0;
restore_flags(flags);
}
/************************************************************************
* Function : AM53C974_intr(int irq, void *dev_id, struct pt_regs *regs) *
* *
* Purpose : interrupt handler *
* *
* Inputs : irq - interrupt line, regs - ? *
* *
* Returns : nothing *
************************************************************************/
static void do_AM53C974_intr(int irq, void *dev_id, struct pt_regs *regs)
{
unsigned long flags;
struct Scsi_Host *dev = dev_id;
spin_lock_irqsave(dev->host_lock, flags);
AM53C974_intr(irq, dev_id, regs);
spin_unlock_irqrestore(dev->host_lock, flags);
}
/************************************************************************
* Function : AM53C974_intr(int irq, void *dev_id, struct pt_regs *regs) *
* *
* Purpose : interrupt handler *
* *
* Inputs : irq - interrupt line, regs - ? *
* *
* Returns : nothing *
************************************************************************/
static void AM53C974_intr(int irq, void *dev_id, struct pt_regs *regs)
{
AM53C974_local_declare();
struct Scsi_Host *instance;
struct AM53C974_hostdata *hostdata;
unsigned char cmdreg, dmastatus, statreg, isreg, instreg, cfifo;
/* find AM53C974 hostadapter responsible for this interrupt */
for (instance = first_instance; instance; instance = instance->next)
if ((instance->irq == irq) && (instance->hostt == the_template))
goto FOUND;
return;
/* found; now decode and process */
FOUND:
hostdata = (struct AM53C974_hostdata *) instance->hostdata;
AM53C974_setio(instance);
dmastatus = AM53C974_read_8(DMASTATUS);
DEB_INTR(printk(SEPARATOR_LINE));
DEB_INTR(printk("AM53C974 interrupt; dmastatus=0x%02x\n", dmastatus));
KEYWAIT();
/*** DMA related interrupts ***/
if (hostdata->connected && (dmastatus & (DMASTATUS_ERROR | DMASTATUS_PWDN |
DMASTATUS_ABORT))) {
/* DMA error or POWERDOWN */
printk("scsi%d: DMA error or powerdown; dmastatus: 0x%02x\n",
instance->host_no, dmastatus);
#ifdef AM53C974_DEBUG
deb_stop = 1;
#endif
panic("scsi%d: cannot recover\n", instance->host_no);
}
if (hostdata->connected && (dmastatus & DMASTATUS_DONE)) {
/* DMA transfer done */
unsigned long residual;
unsigned long flags;
save_flags(flags);
cli();
if (!(AM53C974_read_8(DMACMD) & DMACMD_DIR)) {
do {
dmastatus = AM53C974_read_8(DMASTATUS);
residual = AM53C974_read_8(CTCLREG) | (AM53C974_read_8(CTCMREG) << 8) |
(AM53C974_read_8(CTCHREG) << 16);
residual += AM53C974_read_8(CFIREG) & CFIREG_CF;
} while (!(dmastatus & DMASTATUS_SCSIINT) && residual);
residual = AM53C974_read_8(CTCLREG) | (AM53C974_read_8(CTCMREG) << 8) |
(AM53C974_read_8(CTCHREG) << 16);
residual += AM53C974_read_8(CFIREG) & CFIREG_CF;
} else
residual = 0;
hostdata->connected->SCp.ptr += hostdata->connected->SCp.this_residual - residual;
hostdata->connected->SCp.this_residual = residual;
AM53C974_write_8(DMACMD, DMACMD_IDLE);
/* if service request missed before, process it now (ugly) */
if (hostdata->dma_busy) {
hostdata->dma_busy = 0;
cmdreg = AM53C974_read_8(CMDREG);
statreg = AM53C974_read_8(STATREG);
isreg = AM53C974_read_8(ISREG);
instreg = AM53C974_read_8(INSTREG);
cfifo = AM53C974_cfifo();
AM53C974_information_transfer(instance, statreg, isreg, instreg, cfifo,
dmastatus);
}
restore_flags(flags);
}
if (!(dmastatus & DMASTATUS_SCSIINT)) {
return;
}
/*** SCSI related interrupts ***/
cmdreg = AM53C974_read_8(CMDREG);
statreg = AM53C974_read_8(STATREG);
isreg = AM53C974_read_8(ISREG);
instreg = AM53C974_read_8(INSTREG);
cfifo = AM53C974_cfifo();
DEB_INTR(printk("scsi%d: statreg: 0x%02x; isreg: 0x%02x; instreg: 0x%02x; cfifo: 0x%02x\n",
instance->host_no, statreg, isreg, instreg, cfifo));
if (statreg & STATREG_PE) {
/* parity error */
#ifdef AM53C974_DEBUG
deb_stop = 1;
#endif
printk("scsi%d : PARITY error\n", instance->host_no);
if (hostdata->connected)
hostdata->sync_off[hostdata->connected->target] = 0; /* setup asynchronous transfer */
hostdata->aborted = 1;
}
if (statreg & STATREG_IOE) {
/* illegal operation error */
#ifdef AM53C974_DEBUG
deb_stop = 1;
#endif
printk("scsi%d : ILLEGAL OPERATION error\n", instance->host_no);
printk("cmdreg: 0x%02x; dmacmd: 0x%02x; statreg: 0x%02x; \n"
"isreg: 0x%02x; instreg: 0x%02x; cfifo: 0x%02x\n",
cmdreg, AM53C974_read_8(DMACMD), statreg, isreg, instreg, cfifo);
}
if (hostdata->in_reset && (instreg & INSTREG_SRST)) {
unsigned long flags;
/* RESET INTERRUPT */
#ifdef AM53C974_DEBUG
deb_stop = 1;
#endif
DEB(printk("Bus reset interrupt received\n"));
AM53C974_intr_bus_reset(instance);
save_flags(flags);
cli();
if (hostdata->connected) {
hostdata->connected->result = DID_RESET << 16;
hostdata->connected->scsi_done((Scsi_Cmnd *) hostdata->connected);
hostdata->connected = NULL;
} else {
if (hostdata->sel_cmd) {
hostdata->sel_cmd->result = DID_RESET << 16;
hostdata->sel_cmd->scsi_done((Scsi_Cmnd *) hostdata->sel_cmd);
hostdata->sel_cmd = NULL;
}
}
restore_flags(flags);
if (hostdata->in_reset == 1)
goto EXIT;
else
return;
}
if (instreg & INSTREG_ICMD) {
/* INVALID COMMAND INTERRUPT */
#ifdef AM53C974_DEBUG
deb_stop = 1;
#endif
printk("scsi%d: Invalid command interrupt\n", instance->host_no);
printk("cmdreg: 0x%02x; dmacmd: 0x%02x; statreg: 0x%02x; dmastatus: 0x%02x; \n"
"isreg: 0x%02x; instreg: 0x%02x; cfifo: 0x%02x\n",
cmdreg, AM53C974_read_8(DMACMD), statreg, dmastatus, isreg, instreg, cfifo);
panic("scsi%d: cannot recover\n", instance->host_no);
}
if (instreg & INSTREG_DIS) {
unsigned long flags;
/* DISCONNECT INTERRUPT */
DEB_INTR(printk("Disconnect interrupt received; "));
save_flags(flags);
cli();
AM53C974_intr_disconnect(instance);
restore_flags(flags);
goto EXIT;
}
if (instreg & INSTREG_RESEL) {
unsigned long flags;
/* RESELECTION INTERRUPT */
DEB_INTR(printk("Reselection interrupt received\n"));
save_flags(flags);
cli();
AM53C974_intr_reselect(instance, statreg);
restore_flags(flags);
goto EXIT;
}
if (instreg & INSTREG_SO) {
DEB_INTR(printk("Successful operation interrupt received\n"));
if (hostdata->selecting) {
unsigned long flags;
DEB_INTR(printk("DSR completed, starting select\n"));
save_flags(flags);
cli();
AM53C974_select(instance, (Scsi_Cmnd *) hostdata->sel_cmd,
(hostdata->sel_cmd->cmnd[0] == REQUEST_SENSE) ?
TAG_NONE : TAG_NEXT);
hostdata->selecting = 0;
AM53C974_set_sync(instance, hostdata->sel_cmd->target);
restore_flags(flags);
return;
}
if (hostdata->sel_cmd != NULL) {
if (((isreg & ISREG_IS) != ISREG_OK_NO_STOP) &&
((isreg & ISREG_IS) != ISREG_OK_STOP)) {
unsigned long flags;
/* UNSUCCESSFUL SELECTION */
DEB_INTR(printk("unsuccessful selection\n"));
save_flags(flags);
cli();
hostdata->dma_busy = 0;
LIST(hostdata->sel_cmd, hostdata->issue_queue);
hostdata->sel_cmd->host_scribble = (unsigned char *) hostdata->issue_queue;
hostdata->issue_queue = hostdata->sel_cmd;
hostdata->sel_cmd = NULL;
hostdata->selecting = 0;
restore_flags(flags);
goto EXIT;
} else {
unsigned long flags;
/* SUCCESSFUL SELECTION */
DEB(printk("successful selection; cmd=0x%02lx\n", (long) hostdata->sel_cmd));
save_flags(flags);
cli();
hostdata->dma_busy = 0;
hostdata->disconnecting = 0;
hostdata->connected = hostdata->sel_cmd;
hostdata->sel_cmd = NULL;
hostdata->selecting = 0;
#ifdef SCSI2
if (!hostdata->connected->device->tagged_queue)
#endif
hostdata->busy[hostdata->connected->target] |= (1 << hostdata->connected->lun);
/* very strange -- use_sg is sometimes nonzero for request sense commands !! */
if ((hostdata->connected->cmnd[0] == REQUEST_SENSE) && hostdata->connected->use_sg) {
DEB(printk("scsi%d: REQUEST_SENSE command with nonzero use_sg\n", instance->host_no));
KEYWAIT();
hostdata->connected->use_sg = 0;
}
initialize_SCp((Scsi_Cmnd *) hostdata->connected);
hostdata->connected->SCp.phase = PHASE_CMDOUT;
AM53C974_information_transfer(instance, statreg, isreg, instreg, cfifo, dmastatus);
restore_flags(flags);
return;
}
} else {
unsigned long flags;
save_flags(flags);
cli();
AM53C974_information_transfer(instance, statreg, isreg, instreg, cfifo, dmastatus);
restore_flags(flags);
return;
}
}
if (instreg & INSTREG_SR) {
DEB_INTR(printk("Service request interrupt received, "));
if (hostdata->connected) {
unsigned long flags;
DEB_INTR(printk("calling information_transfer\n"));
save_flags(flags);
cli();
AM53C974_information_transfer(instance, statreg, isreg, instreg, cfifo, dmastatus);
restore_flags(flags);
} else {
printk("scsi%d: weird: service request when no command connected\n", instance->host_no);
AM53C974_write_8(CMDREG, CMDREG_CFIFO);
} /* clear FIFO */
return;
}
EXIT:
DEB_INTR(printk("intr: starting main\n"));
run_main();
DEB_INTR(printk("end of intr\n"));
}
/**************************************************************************
* Function : AM53C974_intr_disconnect(struct Scsi_Host *instance)
*
* Purpose : manage target disconnection
*
* Inputs : instance -- which AM53C974
*
* Returns : nothing
**************************************************************************/
static void AM53C974_intr_disconnect(struct Scsi_Host *instance)
{
AM53C974_local_declare();
struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *) instance->hostdata;
Scsi_Cmnd *cmd;
AM53C974_setio(instance);
if (hostdata->sel_cmd != NULL) {
/* normal selection timeout, typical for nonexisting targets */
cmd = (Scsi_Cmnd *) hostdata->sel_cmd;
DEB_INTR(printk("bad target\n"));
cmd->result = DID_BAD_TARGET << 16;
goto EXIT_FINISHED;
}
if (!hostdata->connected) {
/* can happen if controller was reset, a device tried to reconnect,
failed and disconnects now */
AM53C974_write_8(CMDREG, CMDREG_CFIFO);
return;
}
if (hostdata->disconnecting) {
/* target sent disconnect message, so we are prepared */
cmd = (Scsi_Cmnd *) hostdata->connected;
AM53C974_set_async(instance, cmd->target);
DEB_INTR(printk("scsi%d : disc. from cmnd %d for ta %d, lun %d\n",
instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
if (cmd->device->disconnect) {
/* target wants to reselect later */
DEB_INTR(printk("ok, re-enabling selection\n"));
LIST(cmd, hostdata->disconnected_queue);
cmd->host_scribble = (unsigned char *) hostdata->disconnected_queue;
hostdata->disconnected_queue = cmd;
DEB_QUEUE(printk("scsi%d : command for target %d lun %d this %d was moved from connected to"
" the disconnected_queue\n", instance->host_no, cmd->target,
cmd->lun, hostdata->disconnected_queue->SCp.this_residual));
DEB_QUEUE(AM53C974_print_queues(instance));
goto EXIT_UNFINISHED;
} else {
/* target does not want to reselect later, we are really finished */
#ifdef AM53C974_DEBUG
if (cmd->cmnd[0] == REQUEST_SENSE) {
int i;
printk("Request sense data dump:\n");
for (i = 0; i < cmd->request_bufflen; i++) {
printk("%02x ", *((char *) (cmd->request_buffer) + i));
if (i && !(i % 16))
printk("\n");
}
printk("\n");
}
#endif
goto EXIT_FINISHED;
} /* !cmd->device->disconnect */
} /* if (hostdata->disconnecting) */
/* no disconnect message received; unexpected disconnection */
cmd = (Scsi_Cmnd *) hostdata->connected;
if (cmd) {
#ifdef AM53C974_DEBUG
deb_stop = 1;
#endif
AM53C974_set_async(instance, cmd->target);
printk("scsi%d: Unexpected disconnect; phase: %d; target: %d; this_residual: %d; buffers_residual: %d; message: %d\n",
instance->host_no, cmd->SCp.phase, cmd->target, cmd->SCp.this_residual, cmd->SCp.buffers_residual,
cmd->SCp.Message);
printk("cmdreg: 0x%02x; statreg: 0x%02x; isreg: 0x%02x; cfifo: 0x%02x\n",
AM53C974_read_8(CMDREG), AM53C974_read_8(STATREG), AM53C974_read_8(ISREG),
AM53C974_read_8(CFIREG) & CFIREG_CF);
if ((hostdata->last_message[0] == EXTENDED_MESSAGE) &&
(hostdata->last_message[2] == EXTENDED_SDTR)) {
/* sync. negotiation was aborted, setup asynchronous transfer with target */
hostdata->sync_off[cmd->target] = 0;
}
if (hostdata->aborted || hostdata->msgout[0] == ABORT)
cmd->result = DID_ABORT << 16;
else
cmd->result = DID_ERROR << 16;
goto EXIT_FINISHED;
}
EXIT_FINISHED:
hostdata->aborted = 0;
hostdata->msgout[0] = NOP;
hostdata->sel_cmd = NULL;
hostdata->connected = NULL;
hostdata->selecting = 0;
hostdata->disconnecting = 0;
hostdata->dma_busy = 0;
hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
AM53C974_write_8(CMDREG, CMDREG_CFIFO);
DEB(printk("disconnect; issue_queue: 0x%lx, disconnected_queue: 0x%lx\n",
(long) hostdata->issue_queue, (long) hostdata->disconnected_queue));
cmd->scsi_done(cmd);
if (!hostdata->selecting) {
AM53C974_set_async(instance, cmd->target);
AM53C974_write_8(CMDREG, CMDREG_ESR);
} /* allow reselect */
return;
EXIT_UNFINISHED:
hostdata->msgout[0] = NOP;
hostdata->sel_cmd = NULL;
hostdata->connected = NULL;
hostdata->aborted = 0;
hostdata->selecting = 0;
hostdata->disconnecting = 0;
hostdata->dma_busy = 0;
DEB(printk("disconnect; issue_queue: 0x%lx, disconnected_queue: 0x%lx\n",
(long) hostdata->issue_queue, (long) hostdata->disconnected_queue));
if (!hostdata->selecting) {
AM53C974_set_async(instance, cmd->target);
AM53C974_write_8(CMDREG, CMDREG_ESR);
} /* allow reselect */
return;
}
/**************************************************************************
* Function : int AM53C974_sync_neg(struct Scsi_Host *instance, int target, unsigned char *msg)
*
* Purpose : setup message string for sync. negotiation
*
* Inputs : instance -- which AM53C974
* target -- which SCSI target to deal with
* msg -- input message string
*
* Returns : 0 if parameters accepted or 1 if not accepted
*
* Side effects: hostdata is changed
*
* Note: we assume here that fastclk is enabled
**************************************************************************/
static int AM53C974_sync_neg(struct Scsi_Host *instance, int target, unsigned char *msg)
{
AM53C974_local_declare();
struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *) instance->hostdata;
int period, offset, i, rate, rate_rem;
AM53C974_setio(instance);
period = (DEF_CLK * msg[3] * 8 + 1000) / 2000;
if (period < MIN_PERIOD) {
period = MIN_PERIOD;
hostdata->msgout[3] = period / 4;
} else if (period > MAX_PERIOD) {
period = MAX_PERIOD;
hostdata->msgout[3] = period / 4;
} else
hostdata->msgout[3] = msg[3];
offset = msg[4];
if (offset > MAX_OFFSET)
offset = MAX_OFFSET;
hostdata->msgout[4] = offset;
hostdata->sync_per[target] = period;
hostdata->sync_off[target] = offset;
for (i = 0; i < 3; i++)
hostdata->msgout[i] = msg[i];
if ((hostdata->msgout[3] != msg[3]) || (msg[4] != offset))
return (1);
rate = DEF_CLK / period;
rate_rem = 10 * (DEF_CLK - period * rate) / period;
if (offset)
printk("\ntarget %d: rate=%d.%d Mhz, synchronous, sync offset=%d bytes\n",
target, rate, rate_rem, offset);
else
printk("\ntarget %d: rate=%d.%d Mhz, asynchronous\n", target, rate, rate_rem);
return (0);
}
/**************************************************************************
* Function : AM53C974_set_async(struct Scsi_Host *instance, int target)
*
* Purpose : put controller into async. mode
*
* Inputs : instance -- which AM53C974
* target -- which SCSI target to deal with
*
* Returns : nothing
**************************************************************************/
static __inline__ void AM53C974_set_async(struct Scsi_Host *instance, int target)
{
AM53C974_local_declare();
struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *) instance->hostdata;
AM53C974_setio(instance);
AM53C974_write_8(STPREG, hostdata->sync_per[target]);
AM53C974_write_8(SOFREG, (DEF_SOF_RAD << 6) | (DEF_SOF_RAA << 4));
}
/**************************************************************************
* Function : AM53C974_set_sync(struct Scsi_Host *instance, int target)
*
* Purpose : put controller into sync. mode
*
* Inputs : instance -- which AM53C974
* target -- which SCSI target to deal with
*
* Returns : nothing
**************************************************************************/
static __inline__ void AM53C974_set_sync(struct Scsi_Host *instance, int target)
{
AM53C974_local_declare();
struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *) instance->hostdata;
AM53C974_setio(instance);
AM53C974_write_8(STPREG, hostdata->sync_per[target]);
AM53C974_write_8(SOFREG, (SOFREG_SO & hostdata->sync_off[target]) |
(DEF_SOF_RAD << 6) | (DEF_SOF_RAA << 4));
}
/***********************************************************************
* Function : AM53C974_information_transfer(struct Scsi_Host *instance, *
* unsigned char statreg, unsigned char isreg, *
* unsigned char instreg, unsigned char cfifo, *
* unsigned char dmastatus) *
* *
* Purpose : handle phase changes *
* *
* Inputs : instance - which AM53C974 *
* statreg - status register *
* isreg - internal state register *
* instreg - interrupt status register *
* cfifo - number of bytes in FIFO *
* dmastatus - dma status register *
* *
* Returns : nothing *
************************************************************************/
static void AM53C974_information_transfer(struct Scsi_Host *instance,
unsigned char statreg, unsigned char isreg,
unsigned char instreg, unsigned char cfifo,
unsigned char dmastatus)
{
AM53C974_local_declare();
struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *) instance->hostdata;
Scsi_Cmnd *cmd = (Scsi_Cmnd *) hostdata->connected;
int ret, i, len, residual = -1;
AM53C974_setio(instance);
DEB_INFO(printk(SEPARATOR_LINE));
switch (statreg & STATREG_PHASE) { /* scsi phase */
case PHASE_DATAOUT:
DEB_INFO(printk("Dataout phase; cmd=0x%lx, sel_cmd=0x%lx, this_residual=%d, buffers_residual=%d\n",
(long) hostdata->connected, (long) hostdata->sel_cmd, cmd->SCp.this_residual, cmd->SCp.buffers_residual));
cmd->SCp.phase = PHASE_DATAOUT;
goto PHASE_DATA_IO;
case PHASE_DATAIN:
DEB_INFO(printk("Datain phase; cmd=0x%lx, sel_cmd=0x%lx, this_residual=%d, buffers_residual=%d\n",
(long) hostdata->connected, (long) hostdata->sel_cmd, cmd->SCp.this_residual, cmd->SCp.buffers_residual));
cmd->SCp.phase = PHASE_DATAIN;
PHASE_DATA_IO:
if (hostdata->aborted) {
AM53C974_write_8(DMACMD, DMACMD_IDLE);
AM53C974_write_8(CMDREG, CMDREG_CFIFO);
AM53C974_write_8(CMDREG, CMDREG_SATN);
return;
}
if ((!cmd->SCp.this_residual) && cmd->SCp.buffers_residual) {
cmd->SCp.buffer++;
cmd->SCp.buffers_residual--;
cmd->SCp.ptr = (unsigned char *) cmd->SCp.buffer->address;
cmd->SCp.this_residual = cmd->SCp.buffer->length;
}
if (cmd->SCp.this_residual) {
if (!(AM53C974_read_8(DMACMD) & DMACMD_START)) {
hostdata->dma_busy = 0;
AM53C974_transfer_dma(instance, statreg & STATREG_IO,
(unsigned long) cmd->SCp.this_residual,
cmd->SCp.ptr);
} else
hostdata->dma_busy = 1;
}
return;
case PHASE_MSGIN:
DEB_INFO(printk("Message-In phase; cmd=0x%lx, sel_cmd=0x%lx\n",
(long) hostdata->connected, (long) hostdata->sel_cmd));
AM53C974_set_async(instance, cmd->target);
if (cmd->SCp.phase == PHASE_DATAIN)
AM53C974_dma_blast(instance, dmastatus, statreg);
if ((cmd->SCp.phase == PHASE_DATAOUT) && (AM53C974_read_8(DMACMD) & DMACMD_START)) {
AM53C974_write_8(DMACMD, DMACMD_IDLE);
residual = cfifo + (AM53C974_read_8(CTCLREG) | (AM53C974_read_8(CTCMREG) << 8) |
(AM53C974_read_8(CTCHREG) << 16));
cmd->SCp.ptr += cmd->SCp.this_residual - residual;
cmd->SCp.this_residual = residual;
if (cfifo) {
AM53C974_write_8(CMDREG, CMDREG_CFIFO);
cfifo = 0;
}
}
if (cmd->SCp.phase == PHASE_STATIN) {
while ((AM53C974_read_8(CFIREG) & CFIREG_CF) < 2);
cmd->SCp.Status = AM53C974_read_8(FFREG);
cmd->SCp.Message = AM53C974_read_8(FFREG);
DEB_INFO(printk("Message-In phase; status=0x%02x, message=0x%02x\n",
cmd->SCp.Status, cmd->SCp.Message));
ret = AM53C974_message(instance, cmd, cmd->SCp.Message);
} else {
if (!cfifo) {
AM53C974_write_8(CMDREG, CMDREG_IT);
AM53C974_poll_int();
cmd->SCp.Message = AM53C974_read_8(FFREG);
}
ret = AM53C974_message(instance, cmd, cmd->SCp.Message);
}
cmd->SCp.phase = PHASE_MSGIN;
AM53C974_set_sync(instance, cmd->target);
break;
case PHASE_MSGOUT:
DEB_INFO(printk("Message-Out phase; cfifo=%d; msgout[0]=0x%02x\n",
AM53C974_read_8(CFIREG) & CFIREG_CF, hostdata->msgout[0]));
AM53C974_write_8(DMACMD, DMACMD_IDLE);
AM53C974_set_async(instance, cmd->target);
for (i = 0; i < sizeof(hostdata->last_message); i++)
hostdata->last_message[i] = hostdata->msgout[i];
if ((hostdata->msgout[0] == 0) || INSIDE(hostdata->msgout[0], 0x02, 0x1F) ||
INSIDE(hostdata->msgout[0], 0x80, 0xFF))
len = 1;
else {
if (hostdata->msgout[0] == EXTENDED_MESSAGE) {
#ifdef AM53C974_DEBUG_INFO
printk("Extended message dump:\n");
for (i = 0; i < hostdata->msgout[1] + 2; i++) {
printk("%02x ", hostdata->msgout[i]);
if (i && !(i % 16))
printk("\n");
}
printk("\n");
#endif
len = hostdata->msgout[1] + 2;
} else
len = 2;
}
for (i = 0; i < len; i++)
AM53C974_write_8(FFREG, hostdata->msgout[i]);
AM53C974_write_8(CMDREG, CMDREG_IT);
cmd->SCp.phase = PHASE_MSGOUT;
hostdata->msgout[0] = NOP;
AM53C974_set_sync(instance, cmd->target);
break;
case PHASE_CMDOUT:
DEB_INFO(printk("Command-Out phase\n"));
AM53C974_set_async(instance, cmd->target);
for (i = 0; i < cmd->cmd_len; i++)
AM53C974_write_8(FFREG, cmd->cmnd[i]);
AM53C974_write_8(CMDREG, CMDREG_IT);
cmd->SCp.phase = PHASE_CMDOUT;
AM53C974_set_sync(instance, cmd->target);
break;
case PHASE_STATIN:
DEB_INFO(printk("Status phase\n"));
if (cmd->SCp.phase == PHASE_DATAIN)
AM53C974_dma_blast(instance, dmastatus, statreg);
AM53C974_set_async(instance, cmd->target);
if (cmd->SCp.phase == PHASE_DATAOUT) {
unsigned long residual;
if (AM53C974_read_8(DMACMD) & DMACMD_START) {
AM53C974_write_8(DMACMD, DMACMD_IDLE);
residual = cfifo + (AM53C974_read_8(CTCLREG) | (AM53C974_read_8(CTCMREG) << 8) |
(AM53C974_read_8(CTCHREG) << 16));
cmd->SCp.ptr += cmd->SCp.this_residual - residual;
cmd->SCp.this_residual = residual;
}
if (cfifo) {
AM53C974_write_8(CMDREG, CMDREG_CFIFO);
cfifo = 0;
}
}
cmd->SCp.phase = PHASE_STATIN;
AM53C974_write_8(CMDREG, CMDREG_ICCS); /* command complete */
break;
case PHASE_RES_0:
case PHASE_RES_1:
#ifdef AM53C974_DEBUG
deb_stop = 1;
#endif
DEB_INFO(printk("Reserved phase\n"));
break;
}
KEYWAIT();
}
/******************************************************************************
* Function : int AM53C974_message(struct Scsi_Host *instance, Scsi_Cmnd *cmd,
* unsigned char msg)
*
* Purpose : handle SCSI messages
*
* Inputs : instance -- which AM53C974
* cmd -- SCSI command the message belongs to
* msg -- message id byte
*
* Returns : 1 on success, 0 on failure.
**************************************************************************/
static int AM53C974_message(struct Scsi_Host *instance, Scsi_Cmnd * cmd,
unsigned char msg)
{
AM53C974_local_declare();
static unsigned char extended_msg[10];
unsigned char statreg;
int len, ret = 0;
unsigned char *p;
#ifdef AM53C974_DEBUG_MSG
int j;
#endif
struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *) instance->hostdata;
AM53C974_setio(instance);
DEB_MSG(printk(SEPARATOR_LINE));
/* Linking lets us reduce the time required to get the
* next command out to the device, hopefully this will
* mean we don't waste another revolution due to the delays
* required by ARBITRATION and another SELECTION.
* In the current implementation proposal, low level drivers
* merely have to start the next command, pointed to by
* next_link, done() is called as with unlinked commands. */
switch (msg) {
#ifdef LINKED
case LINKED_CMD_COMPLETE:
case LINKED_FLG_CMD_COMPLETE:
/* Accept message by releasing ACK */
DEB_LINKED(printk("scsi%d : target %d lun %d linked command complete.\n",
instance->host_no, cmd->target, cmd->lun));
/* Sanity check : A linked command should only terminate with
* one of these messages if there are more linked commands available. */
if (!cmd->next_link) {
printk("scsi%d : target %d lun %d linked command complete, no next_link\n"
instance->host_no, cmd->target, cmd->lun);
hostdata->aborted = 1;
AM53C974_write_8(CMDREG, CMDREG_SATN);
AM53C974_write_8(CMDREG, CMDREG_MA);
break;
}
if (hostdata->aborted) {
DEB_ABORT(printk("ATN set for cmnd %d upon reception of LINKED_CMD_COMPLETE or"
"LINKED_FLG_CMD_COMPLETE message\n", cmd->cmnd[0]));
AM53C974_write_8(CMDREG, CMDREG_SATN);
}
AM53C974_write_8(CMDREG, CMDREG_MA);
initialize_SCp(cmd->next_link);
/* The next command is still part of this process */
cmd->next_link->tag = cmd->tag;
cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
DEB_LINKED(printk("scsi%d : target %d lun %d linked request done, calling scsi_done().\n",
instance->host_no, cmd->target, cmd->lun));
cmd->scsi_done(cmd);
cmd = hostdata->connected;
break;
#endif /* def LINKED */
case ABORT:
case COMMAND_COMPLETE:
DEB_MSG(printk("scsi%d: command complete message received; cmd %d for target %d, lun %d\n",
instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
hostdata->disconnecting = 1;
cmd->device->disconnect = 0;
/* I'm not sure what the correct thing to do here is :
* If the command that just executed is NOT a request
* sense, the obvious thing to do is to set the result
* code to the values of the stored parameters.
* If it was a REQUEST SENSE command, we need some way
* to differentiate between the failure code of the original
* and the failure code of the REQUEST sense - the obvious
* case is success, where we fall through and leave the result
* code unchanged.
*
* The non-obvious place is where the REQUEST SENSE failed */
if (cmd->cmnd[0] != REQUEST_SENSE)
cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
else if (cmd->SCp.Status != GOOD)
cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
if (hostdata->aborted) {
AM53C974_write_8(CMDREG, CMDREG_SATN);
AM53C974_write_8(CMDREG, CMDREG_MA);
DEB_ABORT(printk("ATN set for cmnd %d upon reception of ABORT or"
"COMMAND_COMPLETE message\n", cmd->cmnd[0]));
break;
}
if ((cmd->cmnd[0] != REQUEST_SENSE) && (cmd->SCp.Status == CHECK_CONDITION)) {
DEB_MSG(printk("scsi%d : performing request sense\n", instance->host_no));
cmd->cmnd[0] = REQUEST_SENSE;
cmd->cmnd[1] &= 0xe0;
cmd->cmnd[2] = 0;
cmd->cmnd[3] = 0;
cmd->cmnd[4] = sizeof(cmd->sense_buffer);
cmd->cmnd[5] = 0;
cmd->SCp.buffer = NULL;
cmd->SCp.buffers_residual = 0;
cmd->SCp.ptr = (char *) cmd->sense_buffer;
cmd->SCp.this_residual = sizeof(cmd->sense_buffer);
LIST(cmd, hostdata->issue_queue);
cmd->host_scribble = (unsigned char *) hostdata->issue_queue;
hostdata->issue_queue = (Scsi_Cmnd *) cmd;
DEB_MSG(printk("scsi%d : REQUEST SENSE added to head of issue queue\n", instance->host_no));
}
/* Accept message by clearing ACK */
AM53C974_write_8(CMDREG, CMDREG_MA);
break;
case MESSAGE_REJECT:
DEB_MSG(printk("scsi%d: reject message received; cmd %d for target %d, lun %d\n",
instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
switch (hostdata->last_message[0]) {
case EXTENDED_MESSAGE:
if (hostdata->last_message[2] == EXTENDED_SDTR) {
/* sync. negotiation was rejected, setup asynchronous transfer with target */
printk("\ntarget %d: rate=%d Mhz, asynchronous (sync. negotiation rejected)\n",
cmd->target, DEF_CLK / DEF_STP);
hostdata->sync_off[cmd->target] = 0;
hostdata->sync_per[cmd->target] = DEF_STP;
}
break;
case HEAD_OF_QUEUE_TAG:
case ORDERED_QUEUE_TAG:
case SIMPLE_QUEUE_TAG:
cmd->device->tagged_queue = 0;
hostdata->busy[cmd->target] |= (1 << cmd->lun);
break;
default:
break;
}
if (hostdata->aborted)
AM53C974_write_8(CMDREG, CMDREG_SATN);
AM53C974_write_8(CMDREG, CMDREG_MA);
break;
case DISCONNECT:
DEB_MSG(printk("scsi%d: disconnect message received; cmd %d for target %d, lun %d\n",
instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
cmd->device->disconnect = 1;
hostdata->disconnecting = 1;
AM53C974_write_8(CMDREG, CMDREG_MA); /* Accept message by clearing ACK */
break;
case SAVE_POINTERS:
case RESTORE_POINTERS:
DEB_MSG(printk("scsi%d: save/restore pointers message received; cmd %d for target %d, lun %d\n",
instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
/* The SCSI data pointer is *IMPLICITLY* saved on a disconnect
* operation, in violation of the SCSI spec so we can safely
* ignore SAVE/RESTORE pointers calls.
*
* Unfortunately, some disks violate the SCSI spec and
* don't issue the required SAVE_POINTERS message before
* disconnecting, and we have to break spec to remain
* compatible. */
if (hostdata->aborted) {
DEB_ABORT(printk("ATN set for cmnd %d upon reception of SAVE/REST. POINTERS message\n",
cmd->cmnd[0]));
AM53C974_write_8(CMDREG, CMDREG_SATN);
}
AM53C974_write_8(CMDREG, CMDREG_MA);
break;
case EXTENDED_MESSAGE:
DEB_MSG(printk("scsi%d: extended message received; cmd %d for target %d, lun %d\n",
instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
/* Extended messages are sent in the following format :
* Byte
* 0 EXTENDED_MESSAGE == 1
* 1 length (includes one byte for code, doesn't include first two bytes)
* 2 code
* 3..length+1 arguments
*/
/* BEWARE!! THIS CODE IS EXTREMELY UGLY */
extended_msg[0] = EXTENDED_MESSAGE;
AM53C974_read_8(INSTREG); /* clear int */
AM53C974_write_8(CMDREG, CMDREG_MA); /* ack. msg byte, then wait for SO */
AM53C974_poll_int();
/* get length */
AM53C974_write_8(CMDREG, CMDREG_IT);
AM53C974_poll_int();
AM53C974_write_8(CMDREG, CMDREG_MA); /* ack. msg byte, then wait for SO */
AM53C974_poll_int();
extended_msg[1] = len = AM53C974_read_8(FFREG); /* get length */
p = extended_msg + 2;
/* read the remaining (len) bytes */
while (len) {
AM53C974_write_8(CMDREG, CMDREG_IT);
AM53C974_poll_int();
if (len > 1) {
AM53C974_write_8(CMDREG, CMDREG_MA); /* ack. msg byte, then wait for SO */
AM53C974_poll_int();
}
*p = AM53C974_read_8(FFREG);
p++;
len--;
}
#ifdef AM53C974_DEBUG_MSG
printk("scsi%d: received extended message: ", instance->host_no);
for (j = 0; j < extended_msg[1] + 2; j++) {
printk("0x%02x ", extended_msg[j]);
if (j && !(j % 16))
printk("\n");
}
printk("\n");
#endif
/* check message */
if (extended_msg[2] == EXTENDED_SDTR)
ret = AM53C974_sync_neg(instance, cmd->target, extended_msg);
if (ret || hostdata->aborted)
AM53C974_write_8(CMDREG, CMDREG_SATN);
AM53C974_write_8(CMDREG, CMDREG_MA);
break;
default:
printk("scsi%d: unknown message 0x%02x received\n", instance->host_no, msg);
#ifdef AM53C974_DEBUG
deb_stop = 1;
#endif
/* reject message */
hostdata->msgout[0] = MESSAGE_REJECT;
AM53C974_write_8(CMDREG, CMDREG_SATN);
AM53C974_write_8(CMDREG, CMDREG_MA);
return (0);
break;
} /* switch (msg) */
KEYWAIT();
return (1);
}
/**************************************************************************
* Function : AM53C974_select(struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag)
*
* Purpose : try to establish nexus for the command;
* start sync negotiation via start stop and transfer the command in
* cmdout phase in case of an inquiry or req. sense command with no
* sync. neg. performed yet
*
* Inputs : instance -- which AM53C974
* cmd -- command which requires the selection
* tag -- tagged queueing
*
* Returns : nothing
*
* Note: this function initializes the selection process, which is continued
* in the interrupt handler
**************************************************************************/
static void AM53C974_select(struct Scsi_Host *instance, Scsi_Cmnd * cmd, int tag)
{
AM53C974_local_declare();
struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *) instance->hostdata;
unsigned char cfifo, tmp[3];
unsigned int i, len, cmd_size = COMMAND_SIZE(cmd->cmnd[0]);
AM53C974_setio(instance);
cfifo = AM53C974_cfifo();
if (cfifo) {
printk("scsi%d: select error; %d residual bytes in FIFO\n", instance->host_no, cfifo);
AM53C974_write_8(CMDREG, CMDREG_CFIFO); /* clear FIFO */
}
#ifdef AM53C974_PROHIBIT_DISCONNECT
tmp[0] = IDENTIFY(0, cmd->lun);
#else
tmp[0] = IDENTIFY(1, cmd->lun);
#endif
#ifdef SCSI2
if (cmd->device->tagged_queue && (tag != TAG_NONE)) {
tmp[1] = SIMPLE_QUEUE_TAG;
if (tag == TAG_NEXT) {
/* 0 is TAG_NONE, used to imply no tag for this command */
if (cmd->device->current_tag == 0)
cmd->device->current_tag = 1;
cmd->tag = cmd->device->current_tag;
cmd->device->current_tag++;
} else
cmd->tag = (unsigned char) tag;
tmp[2] = cmd->tag;
hostdata->last_message[0] = SIMPLE_QUEUE_TAG;
len = 3;
AM53C974_write_8(FFREG, tmp[0]);
AM53C974_write_8(FFREG, tmp[1]);
AM53C974_write_8(FFREG, tmp[2]);
} else
#endif /* def SCSI2 */
{
len = 1;
AM53C974_write_8(FFREG, tmp[0]);
cmd->tag = 0;
}
/* in case of an inquiry or req. sense command with no sync. neg performed yet, we start
sync negotiation via start stops and transfer the command in cmdout phase */
if (((cmd->cmnd[0] == INQUIRY) || (cmd->cmnd[0] == REQUEST_SENSE)) &&
!(hostdata->sync_neg[cmd->target]) && hostdata->sync_en[cmd->target]) {
hostdata->sync_neg[cmd->target] = 1;
hostdata->msgout[0] = EXTENDED_MESSAGE;
hostdata->msgout[1] = 3;
hostdata->msgout[2] = EXTENDED_SDTR;
hostdata->msgout[3] = 250 / (int) hostdata->max_rate[cmd->target];
hostdata->msgout[4] = hostdata->max_offset[cmd->target];
len += 5;
}
AM53C974_write_8(SDIDREG, SDIREG_MASK & cmd->target); /* setup dest. id */
AM53C974_write_8(STIMREG, DEF_SCSI_TIMEOUT); /* setup timeout reg */
switch (len) {
case 1:
for (i = 0; i < cmd_size; i++)
AM53C974_write_8(FFREG, cmd->cmnd[i]);
AM53C974_write_8(CMDREG, CMDREG_SAS); /* select with ATN, 1 msg byte */
hostdata->msgout[0] = NOP;
break;
case 3:
for (i = 0; i < cmd_size; i++)
AM53C974_write_8(FFREG, cmd->cmnd[i]);
AM53C974_write_8(CMDREG, CMDREG_SA3S); /* select with ATN, 3 msg bytes */
hostdata->msgout[0] = NOP;
break;
default:
AM53C974_write_8(CMDREG, CMDREG_SASS); /* select with ATN, stop steps; continue in message out phase */
break;
}
}
/**************************************************************************
* Function : AM53C974_intr_select(struct Scsi_Host *instance, unsigned char statreg)
*
* Purpose : handle reselection
*
* Inputs : instance -- which AM53C974
* statreg -- status register
*
* Returns : nothing
*
* side effects: manipulates hostdata
**************************************************************************/
static void AM53C974_intr_reselect(struct Scsi_Host *instance, unsigned char statreg)
{
AM53C974_local_declare();
struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *) instance->hostdata;
unsigned char cfifo, msg[3], lun, t, target = 0;
#ifdef SCSI2
unsigned char tag;
#endif
Scsi_Cmnd *tmp = NULL, *prev;
AM53C974_setio(instance);
cfifo = AM53C974_cfifo();
if (hostdata->selecting) {
/* caught reselect interrupt in selection process;
put selecting command back into the issue queue and continue with the
reselecting command */
DEB_RESEL(printk("AM53C974_intr_reselect: in selection process\n"));
LIST(hostdata->sel_cmd, hostdata->issue_queue);
hostdata->sel_cmd->host_scribble = (unsigned char *) hostdata->issue_queue;
hostdata->issue_queue = hostdata->sel_cmd;
hostdata->sel_cmd = NULL;
hostdata->selecting = 0;
}
/* 2 bytes must be in the FIFO now */
if (cfifo != 2) {
printk("scsi %d: error: %d bytes in fifo, 2 expected\n", instance->host_no, cfifo);
hostdata->aborted = 1;
goto EXIT_ABORT;
}
/* determine target which reselected */
t = AM53C974_read_8(FFREG);
if (!(t & (1 << instance->this_id))) {
printk("scsi %d: error: invalid host id\n", instance->host_no);
hostdata->aborted = 1;
goto EXIT_ABORT;
}
t ^= (1 << instance->this_id);
target = 0;
while (t != 1) {
t >>= 1;
target++;
}
DEB_RESEL(printk("scsi %d: reselect; target: %d\n", instance->host_no, target));
if (hostdata->aborted)
goto EXIT_ABORT;
if ((statreg & STATREG_PHASE) != PHASE_MSGIN) {
printk("scsi %d: error: upon reselection interrupt not in MSGIN\n", instance->host_no);
hostdata->aborted = 1;
goto EXIT_ABORT;
}
msg[0] = AM53C974_read_8(FFREG);
if (!msg[0] & 0x80) {
printk("scsi%d: error: expecting IDENTIFY message, got ", instance->host_no);
print_msg(msg);
hostdata->aborted = 1;
goto EXIT_ABORT;
}
lun = (msg[0] & 0x07);
/* We need to add code for SCSI-II to track which devices have
* I_T_L_Q nexuses established, and which have simple I_T_L
* nexuses so we can chose to do additional data transfer. */
#ifdef SCSI2
#error "SCSI-II tagged queueing is not supported yet"
#endif
/* Find the command corresponding to the I_T_L or I_T_L_Q nexus we
* just reestablished, and remove it from the disconnected queue. */
for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue, prev = NULL;
tmp; prev = tmp, tmp = (Scsi_Cmnd *) tmp->host_scribble)
if ((target == tmp->target) && (lun == tmp->lun)
#ifdef SCSI2
&& (tag == tmp->tag)
#endif
) {
if (prev) {
REMOVE(prev, (Scsi_Cmnd *) (prev->host_scribble), tmp,
(Scsi_Cmnd *) (tmp->host_scribble));
prev->host_scribble = tmp->host_scribble;
} else {
REMOVE(-1, hostdata->disconnected_queue, tmp, tmp->host_scribble);
hostdata->disconnected_queue = (Scsi_Cmnd *) tmp->host_scribble;
}
tmp->host_scribble = NULL;
hostdata->connected = tmp;
break;
}
if (!tmp) {
#ifdef SCSI2
printk("scsi%d: warning : target %d lun %d tag %d not in disconnect_queue.\n",
instance->host_no, target, lun, tag);
#else
printk("scsi%d: warning : target %d lun %d not in disconnect_queue.\n",
instance->host_no, target, lun);
#endif
/* Since we have an established nexus that we can't do anything with, we must abort it. */
hostdata->aborted = 1;
DEB(AM53C974_keywait());
goto EXIT_ABORT;
} else
goto EXIT_OK;
EXIT_ABORT:
AM53C974_write_8(CMDREG, CMDREG_SATN);
AM53C974_write_8(CMDREG, CMDREG_MA);
return;
EXIT_OK:
DEB_RESEL(printk("scsi%d: nexus established, target = %d, lun = %d, tag = %d\n",
instance->host_no, target, tmp->lun, tmp->tag));
AM53C974_set_sync(instance, target);
AM53C974_write_8(SDIDREG, SDIREG_MASK & target); /* setup dest. id */
AM53C974_write_8(CMDREG, CMDREG_MA);
hostdata->dma_busy = 0;
hostdata->connected->SCp.phase = PHASE_CMDOUT;
}
/**************************************************************************
* Function : AM53C974_transfer_dma(struct Scsi_Host *instance, short dir,
* unsigned long length, char *data)
*
* Purpose : setup DMA transfer
*
* Inputs : instance -- which AM53C974
* dir -- direction flag, 0: write to device, read from memory;
* 1: read from device, write to memory
* length -- number of bytes to transfer to from buffer
* data -- pointer to data buffer
*
* Returns : nothing
**************************************************************************/
static __inline__ void AM53C974_transfer_dma(struct Scsi_Host *instance, short dir,
unsigned long length, char *data)
{
AM53C974_local_declare();
AM53C974_setio(instance);
AM53C974_write_8(CMDREG, CMDREG_NOP);
AM53C974_write_8(DMACMD, (dir << 7) | DMACMD_INTE_D); /* idle command */
AM53C974_write_8(STCLREG, (unsigned char) (length & 0xff));
AM53C974_write_8(STCMREG, (unsigned char) ((length & 0xff00) >> 8));
AM53C974_write_8(STCHREG, (unsigned char) ((length & 0xff0000) >> 16));
AM53C974_write_32(DMASTC, length & 0xffffff);
AM53C974_write_32(DMASPA, virt_to_bus(data));
AM53C974_write_8(CMDREG, CMDREG_IT | CMDREG_DMA);
AM53C974_write_8(DMACMD, (dir << 7) | DMACMD_INTE_D | DMACMD_START);
}
/**************************************************************************
* Function : AM53C974_dma_blast(struct Scsi_Host *instance, unsigned char dmastatus,
* unsigned char statreg)
*
* Purpose : cleanup DMA transfer
*
* Inputs : instance -- which AM53C974
* dmastatus -- dma status register
* statreg -- status register
*
* Returns : nothing
**************************************************************************/
static void AM53C974_dma_blast(struct Scsi_Host *instance, unsigned char dmastatus,
unsigned char statreg)
{
AM53C974_local_declare();
struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *) instance->hostdata;
unsigned long ctcreg;
int dir = statreg & STATREG_IO;
int cfifo, pio, i = 0;
AM53C974_setio(instance);
do {
cfifo = AM53C974_cfifo();
i++;
} while (cfifo && (i < 50000));
pio = (i == 50000) ? 1 : 0;
if (statreg & STATREG_CTZ) {
AM53C974_write_8(DMACMD, DMACMD_IDLE);
return;
}
if (dmastatus & DMASTATUS_DONE) {
AM53C974_write_8(DMACMD, DMACMD_IDLE);
return;
}
AM53C974_write_8(DMACMD, ((dir << 7) & DMACMD_DIR) | DMACMD_BLAST);
while (!(AM53C974_read_8(DMASTATUS) & DMASTATUS_BCMPLT));
AM53C974_write_8(DMACMD, DMACMD_IDLE);
if (pio) {
/* transfer residual bytes via PIO */
unsigned char *wac = (unsigned char *) AM53C974_read_32(DMAWAC);
printk("pio mode, residual=%d\n", AM53C974_read_8(CFIREG) & CFIREG_CF);
while (AM53C974_read_8(CFIREG) & CFIREG_CF)
*(wac++) = AM53C974_read_8(FFREG);
}
ctcreg = AM53C974_read_8(CTCLREG) | (AM53C974_read_8(CTCMREG) << 8) |
(AM53C974_read_8(CTCHREG) << 16);
hostdata->connected->SCp.ptr += hostdata->connected->SCp.this_residual - ctcreg;
hostdata->connected->SCp.this_residual = ctcreg;
}
/**************************************************************************
* Function : AM53C974_intr_bus_reset(struct Scsi_Host *instance)
*
* Purpose : handle bus reset interrupt
*
* Inputs : instance -- which AM53C974
*
* Returns : nothing
**************************************************************************/
static void AM53C974_intr_bus_reset(struct Scsi_Host *instance)
{
AM53C974_local_declare();
unsigned char cntlreg1;
AM53C974_setio(instance);
AM53C974_write_8(CMDREG, CMDREG_CFIFO);
AM53C974_write_8(CMDREG, CMDREG_NOP);
cntlreg1 = AM53C974_read_8(CNTLREG1);
AM53C974_write_8(CNTLREG1, cntlreg1 | CNTLREG1_DISR);
}
/**************************************************************************
* Function : int AM53C974_abort(Scsi_Cmnd *cmd)
*
* Purpose : abort a command
*
* Inputs : cmd - the Scsi_Cmnd to abort, code - code to set the
* host byte of the result field to, if zero DID_ABORTED is
* used.
*
* Returns : 0 - success, -1 on failure.
**************************************************************************/
static int AM53C974_abort(Scsi_Cmnd * cmd)
{
AM53C974_local_declare();
unsigned long flags;
struct Scsi_Host *instance = cmd->host;
struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *) instance->hostdata;
Scsi_Cmnd *tmp, **prev;
#ifdef AM53C974_DEBUG
deb_stop = 1;
#endif
save_flags(flags);
cli();
AM53C974_setio(instance);
DEB_ABORT(printk(SEPARATOR_LINE));
DEB_ABORT(printk("scsi%d : AM53C974_abort called -- trouble starts!!\n", instance->host_no));
DEB_ABORT(AM53C974_print(instance));
DEB_ABORT(AM53C974_keywait());
/* Case 1 : If the command is the currently executing command,
we'll set the aborted flag and return control so that the
information transfer routine can exit cleanly. */
if ((hostdata->connected == cmd) || (hostdata->sel_cmd == cmd)) {
DEB_ABORT(printk("scsi%d: aborting connected command\n", instance->host_no));
hostdata->aborted = 1;
hostdata->msgout[0] = ABORT;
restore_flags(flags);
return (SCSI_ABORT_PENDING);
}
/* Case 2 : If the command hasn't been issued yet,
we simply remove it from the issue queue. */
for (prev = (Scsi_Cmnd **) & (hostdata->issue_queue),
tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp;
prev = (Scsi_Cmnd **) & (tmp->host_scribble),
tmp = (Scsi_Cmnd *) tmp->host_scribble) {
if (cmd == tmp) {
DEB_ABORT(printk("scsi%d : abort removed command from issue queue.\n", instance->host_no));
REMOVE(5, *prev, tmp, tmp->host_scribble);
(*prev) = (Scsi_Cmnd *) tmp->host_scribble;
tmp->host_scribble = NULL;
tmp->result = DID_ABORT << 16;
restore_flags(flags);
tmp->done(tmp);
return (SCSI_ABORT_SUCCESS);
}
#ifdef AM53C974_DEBUG_ABORT
else {
if (prev == (Scsi_Cmnd **) tmp)
printk("scsi%d : LOOP\n", instance->host_no);
}
#endif
}
/* Case 3 : If any commands are connected, we're going to fail the abort
* and let the high level SCSI driver retry at a later time or
* issue a reset.
*
* Timeouts, and therefore aborted commands, will be highly unlikely
* and handling them cleanly in this situation would make the common
* case of noresets less efficient, and would pollute our code. So,
* we fail. */
if (hostdata->connected || hostdata->sel_cmd) {
DEB_ABORT(printk("scsi%d : abort failed, other command connected.\n", instance->host_no));
restore_flags(flags);
return (SCSI_ABORT_NOT_RUNNING);
}
/* Case 4: If the command is currently disconnected from the bus, and
* there are no connected commands, we reconnect the I_T_L or
* I_T_L_Q nexus associated with it, go into message out, and send
* an abort message. */
for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; tmp;
tmp = (Scsi_Cmnd *) tmp->host_scribble) {
if (cmd == tmp) {
DEB_ABORT(printk("scsi%d: aborting disconnected command\n", instance->host_no));
hostdata->aborted = 1;
hostdata->msgout[0] = ABORT;
hostdata->selecting = 1;
hostdata->sel_cmd = tmp;
AM53C974_write_8(CMDREG, CMDREG_DSR);
restore_flags(flags);
return (SCSI_ABORT_PENDING);
}
}
/* Case 5 : If we reached this point, the command was not found in any of
* the queues.
*
* We probably reached this point because of an unlikely race condition
* between the command completing successfully and the abortion code,
* so we won't panic, but we will notify the user in case something really
* broke. */
DEB_ABORT(printk("scsi%d : abort failed, command not found.\n", instance->host_no));
restore_flags(flags);
return (SCSI_ABORT_NOT_RUNNING);
}
/**************************************************************************
* Function : int AM53C974_reset(Scsi_Cmnd *cmd)
*
* Purpose : reset the SCSI controller and bus
*
* Inputs : cmd -- which command within the command block was responsible for the reset
*
* Returns : status (SCSI_ABORT_SUCCESS)
*
* FIXME(eric) the reset_flags are ignored.
**************************************************************************/
static int AM53C974_reset(Scsi_Cmnd * cmd, unsigned int reset_flags)
{
AM53C974_local_declare();
unsigned long flags;
int i;
struct Scsi_Host *instance = cmd->host;
struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *) instance->hostdata;
AM53C974_setio(instance);
save_flags(flags);
cli();
DEB(printk("AM53C974_reset called; "));
printk("AM53C974_reset called\n");
AM53C974_print(instance);
AM53C974_keywait();
/* do hard reset */
AM53C974_write_8(CMDREG, CMDREG_RDEV);
AM53C974_write_8(CMDREG, CMDREG_NOP);
hostdata->msgout[0] = NOP;
for (i = 0; i < 8; i++) {
hostdata->busy[i] = 0;
hostdata->sync_per[i] = DEF_STP;
hostdata->sync_off[i] = 0;
hostdata->sync_neg[i] = 0;
}
hostdata->last_message[0] = NOP;
hostdata->sel_cmd = NULL;
hostdata->connected = NULL;
hostdata->issue_queue = NULL;
hostdata->disconnected_queue = NULL;
hostdata->in_reset = 0;
hostdata->aborted = 0;
hostdata->selecting = 0;
hostdata->disconnecting = 0;
hostdata->dma_busy = 0;
/* reset bus */
AM53C974_write_8(CNTLREG1, CNTLREG1_DISR | instance->this_id); /* disable interrupt upon SCSI RESET */
AM53C974_write_8(CMDREG, CMDREG_RBUS); /* reset SCSI bus */
udelay(40);
AM53C974_config_after_reset(instance);
restore_flags(flags);
cmd->result = DID_RESET << 16;
cmd->scsi_done(cmd);
return SCSI_ABORT_SUCCESS;
}
/*
* AM53C974_release()
*
* Release resources allocated for a single AM53C974 adapter.
*/
static int AM53C974_release(struct Scsi_Host *shp)
{
free_irq(shp->irq, shp);
scsi_unregister(shp);
return 0;
}
/* You can specify overrides=a,b,c,d in the same format at AM53C974=a,b,c,d
on boot up */
MODULE_PARM(overrides, "1-32i");
MODULE_LICENSE("GPL");
static Scsi_Host_Template driver_template = AM53C974;
#include "scsi_module.c"