URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [scsi/] [AM53C974.c] - Rev 1774
Go to most recent revision | Compare with Previous | Blame | View Log
#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: not supported by cvs2svn $ */ #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; spin_lock_irqsave(&io_request_lock, flags); AM53C974_intr(irq, dev_id, regs); spin_unlock_irqrestore(&io_request_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"
Go to most recent revision | Compare with Previous | Blame | View Log