sim710.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:52k
- /*
- * sim710.c - Copyright (C) 1999 Richard Hirst <richard@sleepie.demon.co.uk>
- *
- *----------------------------------------------------------------------------
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *----------------------------------------------------------------------------
- *
- * MCA card detection code by Trent McNair.
- * Fixes to not explicitly nul bss data from Xavier Bestel.
- * Some multiboard fixes from Rolf Eike Beer.
- * Auto probing of EISA config space from Trevor Hemsley.
- *
- * Various bits of code in this driver have been copied from 53c7,8xx,c,
- * which is coyright Drew Eckhardt. The scripts for the SCSI chip are
- * compiled with the script compiler written by Drew.
- *
- * This is a simple driver for the NCR53c710. More complex drivers
- * for this chip (e.g. 53c7xx.c) require that the scsi chip be able to
- * do DMA block moves between memory and on-chip registers, which can
- * be a problem if those registers are in the I/O address space. There
- * can also be problems on hardware where the registers are memory
- * mapped, if the design is such that memory-to-memory transfers initiated
- * by the scsi chip cannot access the chip registers.
- *
- * This driver is designed to avoid these problems and is intended to
- * work with any Intel machines using 53c710 chips, including various
- * Compaq and NCR machines. It was initially written for the Tadpole
- * TP34V VME board which is 68030 based.
- *
- * The driver supports boot-time parameters similar to
- * sim710=addr:0x9000,irq:15
- * and insmod parameters similar to
- * sim710="addr:0x9000 irq:15"
- *
- * Multiple controllers can also be set up by command line, provided the
- * addr: parameter is specified first for each controller. e.g.
- * sim710="addr:0x9000 irq:15 addr:0x8000 irq:14"
- *
- * To seperate the different options, ' ', '+', and ',' can be used, except
- * that ',' can not be used in module parameters. ' ' can be a pain, because
- * it needs to be quoted, which causes problems with some installers.
- * The command line above is completely equivalent to
- * sim710="addr:0x9000+irq:15+addr:0x8000+irq:14"
- *
- * The complete list of options are:
- *
- * addr:0x9000 Specifies the base I/O port (or address) of the 53C710.
- * irq:15 Specifies the IRQ number used by the 53c710.
- * debug:0xffff Generates lots of debug output.
- * ignore:0x0a Makes the driver ignore SCSI IDs 0 and 2.
- * nodisc:0x70 Prevents disconnects from IDs 6, 5 and 4.
- * noneg:0x10 Prevents SDTR negotiation on ID 4.
- * disabled:1 Completely disables the driver. When present, overrides
- * all other options.
- *
- * The driver will auto-probe chip addresses and IRQs now, so typically no
- * parameters are needed. Auto-probing of addresses is disabled if any addr:
- * parameters are specified.
- *
- * Current limitations:
- *
- * o Async only
- * o Severely lacking in error recovery
- * o 'debug:' should be per host really.
- *
- */
- #include <linux/config.h>
- #include <linux/module.h>
- #include <linux/version.h>
- #include <linux/kernel.h>
- #include <linux/types.h>
- #include <linux/string.h>
- #include <linux/ioport.h>
- #include <linux/delay.h>
- #include <linux/sched.h>
- #include <linux/proc_fs.h>
- #include <linux/init.h>
- #include <linux/mca.h>
- #include <linux/interrupt.h>
- #include <asm/dma.h>
- #include <asm/system.h>
- #include <linux/spinlock.h>
- #include <asm/io.h>
- #include <asm/pgtable.h>
- #include <asm/byteorder.h>
- #include <linux/blk.h>
- /* All targets are I/O mapped at the moment */
- #define IO_MAPPED
- #if defined(CONFIG_MCA)
- /*
- * For each known microchannel card using the 53c710 we need a list
- * of possible IRQ and IO settings, as well as their corresponding
- * bit assignment in pos[]. This might get cumbersome if there
- * are more than a few cards (I only know of 2 at this point).
- */
- #define MCA_53C710_IDS { 0x01bb, 0x01ba, 0x004f }
- /* CARD ID 01BB and 01BA use the same pos values */
- #define MCA_01BB_IO_PORTS { 0x0000, 0x0000, 0x0800, 0x0C00, 0x1000, 0x1400,
- 0x1800, 0x1C00, 0x2000, 0x2400, 0x2800,
- 0x2C00, 0x3000, 0x3400, 0x3800, 0x3C00,
- 0x4000, 0x4400, 0x4800, 0x4C00, 0x5000 }
- #define MCA_01BB_IRQS { 3, 5, 11, 14 }
- /* CARD ID 004f */
- #define MCA_004F_IO_PORTS { 0x0000, 0x0200, 0x0300, 0x0400, 0x0500, 0x0600 }
- #define MCA_004F_IRQS { 5, 9, 14 }
- #endif
- #include "scsi.h"
- #include "hosts.h"
- #include "sim710.h"
- #include<linux/stat.h>
- #define DEBUG
- #undef DEBUG_LIMIT_INTS /* Define to 10 to hang driver after 10 ints */
- /* Debug options available via the "debug:0x1234" parameter */
- #define DEB_NONE 0x0000 /* Nothing */
- #define DEB_HALT 0x0001 /* Detailed trace of chip halt funtion */
- #define DEB_REGS 0x0002 /* All chip register read/writes */
- #define DEB_SYNC 0x0004 /* Sync/async negotiation */
- #define DEB_PMM 0x0008 /* Phase mis-match handling */
- #define DEB_INTS 0x0010 /* General interrupt trace */
- #define DEB_TOUT 0x0020 /* Selection timeouts */
- #define DEB_RESUME 0x0040 /* Resume addresses for the script */
- #define DEB_CMND 0x0080 /* Commands and status returned */
- #define DEB_FIXUP 0x0100 /* Fixup of scsi addresses */
- #define DEB_DISC 0x0200 /* Disconnect/reselect handling */
- #define DEB_ANY 0xffff /* Any and all debug options */
- #ifdef DEBUG
- #define DEB(m,x) if (sim710_debug & m) x
- int sim710_debug;
- #else
- #define DEB(m,x)
- #endif
- /* Redefine scsi_done to force renegotiation of (a)sync transfers
- * following any failed command.
- */
- #define SCSI_DONE(cmd) {
- DEB(DEB_CMND, printk("scsi%d: Complete %08xn",
- host->host_no, cmd->result));
- if (cmd->result)
- hostdata->negotiate |= (1 << cmd->target);
- cmd->scsi_done(cmd);
- }
- #ifndef offsetof
- #define offsetof(t, m) ((size_t) (&((t *)0)->m))
- #endif
- #define STATE_INITIALISED 0
- #define STATE_HALTED 1
- #define STATE_IDLE 2
- #define STATE_BUSY 3
- #define STATE_DISABLED 4
- #define MAXBOARDS 4 /* Increase this and the sizes of the
- arrays below, if you need more.. */
- #ifdef MODULE
- char *sim710; /* command line passed by insmod */
- MODULE_AUTHOR("Richard Hirst");
- MODULE_DESCRIPTION("Simple NCR53C710 driver");
- MODULE_LICENSE("GPL");
- MODULE_PARM(sim710, "s");
- #endif
- static int sim710_errors; /* Count of error interrupts */
- static int sim710_intrs; /* Count of all interrupts */
- static int ignore_ids[MAXBOARDS]; /* Accept all SCSI IDs */
- static int opt_nodisc[MAXBOARDS]; /* Allow disconnect on all IDs */
- static int opt_noneg[MAXBOARDS]; /* Allow SDTR negotiation on all IDs */
- static int hostdata_order; /* Encoded size of hostdata for free_pages() */
- static int no_of_boards; /* Actual number of boards/chips */
- static unsigned int bases[MAXBOARDS]; /* Base addresses of chips */
- static unsigned int irq_vectors[MAXBOARDS]; /* IRQ vectors used by chips */
- /* The SCSI Script!!! */
- #include "sim710_d.h"
- /* Now define offsets in the DSA, as (A_dsa_xxx/4) */
- #define DSA_SELECT (A_dsa_select/4)
- #define DSA_MSGOUT (A_dsa_msgout/4)
- #define DSA_CMND (A_dsa_cmnd/4)
- #define DSA_STATUS (A_dsa_status/4)
- #define DSA_MSGIN (A_dsa_msgin/4)
- #define DSA_DATAIN (A_dsa_datain/4)
- #define DSA_DATAOUT (A_dsa_dataout/4)
- #define DSA_SIZE (A_dsa_size/4)
- #define MAX_SG 128 /* Scatter/Gather elements */
- #define MAX_MSGOUT 8
- #define MAX_MSGIN 8
- #define MAX_CMND 12
- #define MAX_STATUS 1
- struct sim710_hostdata{
- int state;
- Scsi_Cmnd * issue_queue;
- Scsi_Cmnd * running;
- int chip;
- u8 negotiate;
- u8 reselected_identify;
- u8 msgin_buf[MAX_MSGIN];
- u8 msg_reject;
- u32 test1_src __attribute__ ((aligned (4)));
- u32 test1_dst;
- struct sim710_target {
- Scsi_Cmnd *cur_cmd;
- u32 resume_offset;
- u32 data_in_jump;
- u32 data_out_jump;
- u32 dsa[DSA_SIZE]; /* SCSI Script DSA area */
- u8 dsa_msgout[MAX_MSGOUT];
- u8 dsa_msgin[MAX_MSGIN];
- u8 dsa_cdb[MAX_CMND];
- u8 dsa_status[MAX_STATUS];
- } target[8];
- u32 script[sizeof(SCRIPT)/4] __attribute__ ((aligned (4)));
- };
- /* Template to request asynchronous transfers */
- static const unsigned char async_message[] = {
- EXTENDED_MESSAGE, 3 /* length */, EXTENDED_SDTR, 0, 0 /* asynchronous */};
- static void sim710_intr_handle(int irq, void *dev_id, struct pt_regs *regs);
- static void do_sim710_intr_handle(int irq, void *dev_id, struct pt_regs *regs);
- static __inline__ void run_process_issue_queue(struct sim710_hostdata *);
- static void process_issue_queue (struct sim710_hostdata *, unsigned long flags);
- static int full_reset(struct Scsi_Host * host);
- /*
- * Function : static void ncr_dump (struct Scsi_Host *host)
- *
- * Purpose : Dump (possibly) useful info
- *
- * Inputs : host - pointer to this host adapter's structure
- */
- static void
- ncr_dump (struct Scsi_Host *host)
- {
- unsigned long flags;
- struct sim710_hostdata *hostdata = (struct sim710_hostdata *)
- host->hostdata[0];
- save_flags(flags);
- cli();
- printk("scsi%d: Chip register contents:n", host->host_no);
- printk(" (script at virt %p, bus %lx)n",
- hostdata->script, virt_to_bus(hostdata->script));
- printk(" 00 sien: %02x sdid: %02x scntl1:%02x scntl0:%02xn"
- " 04 socl: %02x sodl: %02x sxfer: %02x scid: %02xn"
- " 08 sbcl: %02x sbdl: %02x sidl: %02x sfbr: %02xn"
- " 0C sstat2:%02x sstat1:%02x sstat0:%02x dstat: %02xn"
- " 10 dsa: %08xn"
- " 14 ctest3:%02x ctest2:%02x ctest1:%02x ctest0:%02xn"
- " 18 ctest7:%02x ctest6:%02x ctest5:%02x ctest4:%02xn"
- " 1C temp: %08xn"
- " 20 lcrc: %02x ctest8:%02x istat: %02x dfifo: %02xn"
- " 24 dbc: %08x dnad: %08x dsp: %08xn"
- " 30 dsps: %08x scratch:%08xn"
- " 38 dcntl: %02x dwt: %02x dien: %02x dmode: %02xn"
- " 3C adder: %08xn",
- NCR_read8(SIEN_REG), NCR_read8(SDID_REG), NCR_read8(SCNTL1_REG),
- NCR_read8(SCNTL0_REG), NCR_read8(SOCL_REG), NCR_read8(SODL_REG),
- NCR_read8(SXFER_REG), NCR_read8(SCID_REG), NCR_read8(SBCL_REG),
- NCR_read8(SBDL_REG), NCR_read8(SIDL_REG), NCR_read8(SFBR_REG),
- NCR_read8(SSTAT2_REG), NCR_read8(SSTAT1_REG), NCR_read8(SSTAT0_REG),
- NCR_read8(DSTAT_REG), NCR_read32(DSA_REG), NCR_read8(CTEST3_REG),
- NCR_read8(CTEST2_REG), NCR_read8(CTEST1_REG), NCR_read8(CTEST0_REG),
- NCR_read8(CTEST7_REG), NCR_read8(CTEST6_REG), NCR_read8(CTEST5_REG),
- NCR_read8(CTEST4_REG), NCR_read8(TEMP_REG), NCR_read8(LCRC_REG),
- NCR_read8(CTEST8_REG), NCR_read8(ISTAT_REG), NCR_read8(DFIFO_REG),
- NCR_read32(DBC_REG), NCR_read32(DNAD_REG), NCR_read32(DSP_REG),
- NCR_read32(DSPS_REG), NCR_read32(SCRATCH_REG), NCR_read8(DCNTL_REG),
- NCR_read8(DWT_REG), NCR_read8(DIEN_REG), NCR_read8(DMODE_REG),
- NCR_read32(ADDER_REG));
- restore_flags(flags);
- }
- /*
- * Function: int param_setup(char *str)
- */
- __init int
- param_setup(char *str)
- {
- char *cur = str;
- char *p, *pc, *pv;
- int val;
- int c;
- no_of_boards = 0;
- while (no_of_boards < MAXBOARDS && cur != NULL &&
- (pc = strchr(cur, ':')) != NULL) {
- char *pe;
- val = 0;
- pv = pc;
- c = *++pv;
- val = (int) simple_strtoul(pv, &pe, 0);
- if (!strncmp(cur, "addr:", 5)) {
- bases[no_of_boards++] = val;
- }
- #ifdef DEBUG
- else if (!strncmp(cur, "debug:", 6)) {
- sim710_debug = val;
- }
- #endif
- else if (no_of_boards == 0) {
- printk("sim710: Invalid parameters, addr: must come firstn");
- no_of_boards = -1;
- return 1;
- }
- else if (!strncmp(cur, "irq:", 4))
- irq_vectors[no_of_boards-1] = val;
- else if (!strncmp(cur, "ignore:", 7))
- ignore_ids[no_of_boards-1] = val;
- else if (!strncmp(cur, "nodisc:", 7))
- opt_nodisc[no_of_boards-1] = val;
- else if (!strncmp(cur, "noneg:", 6))
- opt_noneg[no_of_boards-1] = val;
- else if (!strncmp(cur, "disabled:", 9)) {
- no_of_boards = -1;
- return 1;
- }
- else {
- printk("sim710: unexpected boot option '%.*s'n", (int)(pc-cur+1), cur);
- no_of_boards = -1;
- return 1;
- }
- /* Allow ',', ' ', or '+' seperators. Used to be ',' at boot and
- * ' ' for module load, some installers crap out on the space and
- * insmod doesn't like the comma.
- */
- if ((p = strchr(cur, ',')) || (p = strchr(cur, ' ')) ||
- (p = strchr(cur, '+')))
- cur = p + 1;
- else
- break;
- }
- return 1;
- }
- #ifndef MODULE
- __setup("sim710=", param_setup);
- #endif
- /*
- * Function: static const char *sbcl_to_phase (int sbcl)
- */
- static const char *
- sbcl_to_phase (int sbcl) {
- switch (sbcl & SBCL_PHASE_MASK) {
- case SBCL_PHASE_DATAIN:
- return "DATAIN";
- case SBCL_PHASE_DATAOUT:
- return "DATAOUT";
- case SBCL_PHASE_MSGIN:
- return "MSGIN";
- case SBCL_PHASE_MSGOUT:
- return "MSGOUT";
- case SBCL_PHASE_CMDOUT:
- return "CMDOUT";
- case SBCL_PHASE_STATIN:
- return "STATUSIN";
- default:
- return "unknown";
- }
- }
- /*
- * Function : static int ncr_halt (struct Scsi_Host *host)
- *
- * Purpose : halts the SCSI SCRIPTS(tm) processor on the NCR chip
- *
- * Inputs : host - SCSI chip to halt
- *
- * Returns : 0 on success
- */
- static int
- ncr_halt (struct Scsi_Host *host)
- {
- unsigned long flags;
- unsigned char istat, tmp;
- struct sim710_hostdata *hostdata = (struct sim710_hostdata *)
- host->hostdata[0];
- int stage;
- int timeout;
- int res = 0;
- save_flags(flags);
- cli();
- /* Stage 0 : eat all interrupts
- Stage 1 : set ABORT
- Stage 2 : eat all but abort interrupts
- Stage 3 : eat all interrupts
- We loop for 50000 times with a delay of 10us which should give us
- about half a second.
- */
- for (stage = 0, timeout = 50000; timeout; timeout--) {
- if (stage == 1) {
- DEB(DEB_HALT, printk("ncr_halt: writing ISTAT_ABRTn"));
- NCR_write8(ISTAT_REG, ISTAT_ABRT);
- ++stage;
- }
- istat = NCR_read8 (ISTAT_REG);
- if (istat & ISTAT_SIP) {
- DEB(DEB_HALT, printk("ncr_halt: got ISTAT_SIP, istat=%02xn", istat));
- tmp = NCR_read8(SSTAT0_REG);
- DEB(DEB_HALT, printk("ncr_halt: got SSTAT0_REG=%02xn", tmp));
- } else if (istat & ISTAT_DIP) {
- DEB(DEB_HALT, printk("ncr_halt: got ISTAT_DIP, istat=%02xn", istat));
- tmp = NCR_read8(DSTAT_REG);
- DEB(DEB_HALT, printk("ncr_halt: got DSTAT_REG=%02xn", tmp));
- if (stage == 2) {
- if (tmp & DSTAT_ABRT) {
- DEB(DEB_HALT, printk("ncr_halt: got DSTAT_ABRT, clearing istatn"));
- NCR_write8(ISTAT_REG, 0);
- ++stage;
- } else {
- res = 1;
- break;
- }
- }
- }
- if (!(istat & (ISTAT_SIP|ISTAT_DIP))) {
- if (stage == 0)
- ++stage;
- else if (stage == 3)
- break;
- }
- udelay(10);
- }
- restore_flags(flags);
- if (timeout == 0 || res) {
- printk(KERN_ALERT "scsi%d: could not halt NCR chipn", host->host_no);
- return 1;
- }
- else {
- hostdata->state = STATE_HALTED;
- return 0;
- }
- }
- /*
- * Function : static void sim710_soft_reset (struct Scsi_Host *host)
- *
- * Purpose : perform a soft reset of the NCR53c7xx chip
- *
- * Inputs : host - pointer to this host adapter's structure
- *
- * Preconditions : sim710_init must have been called for this
- * host.
- *
- */
- static void
- sim710_soft_reset (struct Scsi_Host *host)
- {
- unsigned long flags;
- save_flags(flags);
- cli();
- /*
- * Do a soft reset of the chip so that everything is
- * reinitialized to the power-on state.
- *
- * Basically follow the procedure outlined in the NCR53c700
- * data manual under Chapter Six, How to Use, Steps Necessary to
- * Start SCRIPTS, with the exception of actually starting the
- * script and setting up the synchronous transfer gunk.
- */
- /* XXX Should we reset the scsi bus here? */
- NCR_write8(SCNTL1_REG, SCNTL1_RST); /* Reset the bus */
- udelay(50);
- NCR_write8(SCNTL1_REG, 0);
- udelay(500);
- NCR_write8(ISTAT_REG, ISTAT_10_SRST); /* Reset the chip */
- udelay(50);
- NCR_write8(ISTAT_REG, 0);
- mdelay(1000); /* Let devices recover */
- NCR_write32(SCRATCH_REG, 0);
- NCR_write8(DCNTL_REG, DCNTL_10_COM | DCNTL_700_CF_3);
- NCR_write8(CTEST7_REG, CTEST7_10_CDIS|CTEST7_STD);
- NCR_write8(DMODE_REG, DMODE_10_BL_8 | DMODE_10_FC2);
- NCR_write8(SCID_REG, 1 << host->this_id);
- NCR_write8(SBCL_REG, 0);
- NCR_write8(SXFER_REG, 0);
- NCR_write8(SCNTL1_REG, SCNTL1_ESR_700);
- NCR_write8(SCNTL0_REG, SCNTL0_EPC | SCNTL0_EPG_700 | SCNTL0_ARB1 |
- SCNTL0_ARB2);
- NCR_write8(DIEN_REG, DIEN_700_BF |
- DIEN_ABRT | DIEN_SSI | DIEN_SIR | DIEN_700_OPC);
- NCR_write8(SIEN_REG_700,
- SIEN_PAR | SIEN_700_STO | SIEN_RST | SIEN_UDC | SIEN_SGE | SIEN_MA);
- restore_flags(flags);
- }
- /*
- * Function : static void sim710_driver_init (struct Scsi_Host *host)
- *
- * Purpose : Initialize internal structures, as required on startup, or
- * after a SCSI bus reset.
- *
- * Inputs : host - pointer to this host adapter's structure
- */
- static void
- sim710_driver_init (struct Scsi_Host *host)
- {
- struct sim710_hostdata *hostdata = (struct sim710_hostdata *)
- host->hostdata[0];
- int i;
- hostdata->running = NULL;
- memcpy (hostdata->script, SCRIPT, sizeof(SCRIPT));
- for (i = 0; i < PATCHES; i++)
- hostdata->script[LABELPATCHES[i]] += virt_to_bus(hostdata->script);
- patch_abs_32 (hostdata->script, 0, reselected_identify,
- virt_to_bus((void *)&(hostdata->reselected_identify)));
- patch_abs_32 (hostdata->script, 0, msgin_buf,
- virt_to_bus((void *)&(hostdata->msgin_buf[0])));
- patch_abs_32 (hostdata->script, 0, msg_reject,
- virt_to_bus((void *)&(hostdata->msg_reject)));
- patch_abs_32 (hostdata->script, 0, test1_src,
- virt_to_bus((void *)&(hostdata->test1_src)));
- patch_abs_32 (hostdata->script, 0, test1_dst,
- virt_to_bus((void *)&(hostdata->test1_dst)));
- hostdata->state = STATE_INITIALISED;
- hostdata->negotiate = 0xff;
- }
- /* Handle incoming Synchronous data transfer request. If our negotiate
- * flag is set then this is a response to our request, otherwise it is
- * spurious request from the target. Don't really expect target initiated
- * SDTRs, because we always negotiate on the first command. Could still
- * get them though..
- * The chip is currently paused with ACK asserted on the last byte of the
- * SDTR.
- * resa is the resume address if the message is in response to our outgoing
- * SDTR. Only possible on initial identify.
- * resb is the resume address if the message exchange is initiated by the
- * target.
- */
- static u32
- handle_sdtr (struct Scsi_Host * host, Scsi_Cmnd * cmd, u32 resa, u32 resb)
- {
- struct sim710_hostdata *hostdata = (struct sim710_hostdata *)host->hostdata[0];
- struct sim710_target *targdata = hostdata->target + cmd->target;
- u32 resume_offset;
- if (resa && hostdata->negotiate & (1 << cmd->target)) {
- DEB(DEB_SYNC, printk("scsi%d: Response to host SDTR = %02x %02xn",
- host->host_no, hostdata->msgin_buf[3], hostdata->msgin_buf[4]));
- /* We always issue an SDTR with the identify, so we must issue
- * the CDB next.
- */
- resume_offset = resa;
- hostdata->negotiate &= ~(1 << cmd->target);
- }
- else {
- DEB(DEB_SYNC, printk("scsi%d: Target initiated SDTR = %02x %02xn",
- host->host_no, hostdata->msgin_buf[3], hostdata->msgin_buf[4]));
- memcpy(targdata->dsa_msgout, async_message, sizeof(async_message));
- targdata->dsa[DSA_MSGOUT] = sizeof(async_message);
- /* I guess the target could do this anytime; we have to send our
- * response, and then continue (sending the CDB if not already done).
- */
- resume_offset = resb;
- }
- return resume_offset;
- }
- /*
- * Function : static int datapath_residual (Scsi_Host *host)
- *
- * Purpose : return residual data count of what's in the chip.
- *
- * Inputs : host - SCSI host
- */
- static int
- datapath_residual (struct Scsi_Host *host) {
- int count, synchronous, sstat;
- unsigned int ddir;
- count = ((NCR_read8 (DFIFO_REG) & DFIFO_10_BO_MASK) -
- (NCR_read32 (DBC_REG) & DFIFO_10_BO_MASK)) & DFIFO_10_BO_MASK;
- synchronous = NCR_read8 (SXFER_REG) & SXFER_MO_MASK;
- ddir = NCR_read8 (CTEST0_REG_700) & CTEST0_700_DDIR;
- if (ddir) {
- /* Receive */
- if (synchronous)
- count += (NCR_read8 (SSTAT2_REG) & SSTAT2_FF_MASK) >> SSTAT2_FF_SHIFT;
- else
- if (NCR_read8 (SSTAT1_REG) & SSTAT1_ILF)
- ++count;
- } else {
- /* Send */
- sstat = NCR_read8 (SSTAT1_REG);
- if (sstat & SSTAT1_OLF)
- ++count;
- if (synchronous && (sstat & SSTAT1_ORF))
- ++count;
- }
- return count;
- }
- static u32
- handle_idd (struct Scsi_Host * host, Scsi_Cmnd * cmd)
- {
- struct sim710_hostdata *hostdata =
- (struct sim710_hostdata *)host->hostdata[0];
- struct sim710_target *targdata = hostdata->target + cmd->target;
- u32 resume_offset = 0, index;
- index = (u32)((u32 *)(bus_to_virt(NCR_read32(DSP_REG))) - hostdata->script);
- switch (index) {
- case Ent_wait_disc_complete/4 + 2:
- cmd->result = targdata->dsa_status[0];
- SCSI_DONE(cmd);
- targdata->cur_cmd = NULL;
- resume_offset = Ent_reselect;
- break;
- case Ent_wait_disc2/4 + 2:
- /* Disconnect after command - just wait for a reselect */
- targdata->resume_offset = Ent_resume_msgin2a;
- resume_offset = Ent_reselect;
- break;
- case Ent_wait_disc3/4 + 2:
- /* Disconnect after the data phase */
- targdata->resume_offset = Ent_resume_msgin3a;
- resume_offset = Ent_reselect;
- break;
- case Ent_wait_disc1/4 + 2:
- /* Disconnect before command - not expected */
- targdata->resume_offset = Ent_resume_msgin1a;
- resume_offset = Ent_reselect;
- break;
- default:
- printk("scsi%d: Unexpected Illegal Instruction, script[%04x]n",
- host->host_no, index);
- sim710_errors++;
- /* resume_offset is zero, which will cause host reset */
- }
- return resume_offset;
- }
- /* Handle a phase mismatch.
- */
- static u32
- handle_phase_mismatch (struct Scsi_Host * host, Scsi_Cmnd * cmd)
- {
- struct sim710_hostdata *hostdata =
- (struct sim710_hostdata *)host->hostdata[0];
- struct sim710_target *targdata = hostdata->target + cmd->target;
- u32 resume_offset = 0, index;
- unsigned char sbcl;
- sbcl = NCR_read8(SBCL_REG) & SBCL_PHASE_MASK;
- index = (u32)((u32 *)(bus_to_virt(NCR_read32(DSP_REG))) - hostdata->script);
- DEB(DEB_PMM, printk("scsi%d: Phase mismatch, phase %s (%x) at script[0x%x]n",
- host->host_no, sbcl_to_phase(sbcl), sbcl, index));
- DEB(DEB_PMM, print_command(cmd->cmnd));
- if (index == Ent_done_ident/4) {
- /* Sending initial message out - probably rejecting our sync
- * negotiation request.
- */
- NCR_write8(SOCL_REG, 0); /* Negate ATN */
- if (sbcl == SBCL_PHASE_MSGIN)
- resume_offset = Ent_resume_rej_ident;
- else if (sbcl == SBCL_PHASE_CMDOUT) {
- /* Some old devices (SQ555) switch to cmdout after the first
- * byte of an identify message, regardless of whether we
- * have more bytes to send!
- */
- printk("scsi%d: Unexpected switch to CMDOUT during IDENTIFYn",
- host->host_no);
- resume_offset = Ent_resume_cmd;
- }
- else if (sbcl == SBCL_PHASE_STATIN) {
- /* Some devices do this on parity error, at least */
- printk("scsi%d: Unexpected switch to STATUSIN on initial message outn",
- host->host_no);
- resume_offset = Ent_end_data_trans;
- }
- else {
- printk("scsi%d: Unexpected phase change to %s on initial msgoutn",
- host->host_no, sbcl_to_phase(sbcl));
- /* resume_offset is zero, which will cause a host reset */
- }
- hostdata->negotiate &= ~(1 << cmd->target);
- }
- else if (index > Ent_patch_input_data/4 &&
- index < Ent_patch_output_data/4) {
- /* DataIn transfer phase */
- u32 sg_id, oaddr, olen, naddr, nlen;
- int residual;
- sg_id = (index - Ent_patch_input_data/4 - 4) / 2;
- targdata->data_in_jump = hostdata->script[Ent_patch_input_data/4+1] =
- virt_to_bus(hostdata->script + Ent_patch_input_data/4 + sg_id * 2 + 2);
- olen = targdata->dsa[DSA_DATAIN + sg_id * 2];
- oaddr = targdata->dsa[DSA_DATAIN + sg_id * 2 + 1];
- residual = datapath_residual (host);
- if (residual)
- printk("scsi%d: Residual count %d on DataIn - NOT expected!!!",
- host->host_no, residual);
- naddr = NCR_read32(DNAD_REG) - residual;
- nlen = (NCR_read32(DBC_REG) & 0x00ffffff) + residual;
- DEB(DEB_PMM, printk("scsi%d: DIN sg %d, old %08x/%08x, new %08x/%08x (%d)n",
- host->host_no, sg_id, oaddr, olen, naddr, nlen, residual));
- if (oaddr+olen != naddr+nlen) {
- printk("scsi%d: PMM DIN counts error: 0x%x + 0x%x != 0x%x + 0x%x",
- host->host_no, oaddr, olen, naddr, nlen);
- }
- else {
- targdata->dsa[DSA_DATAIN + sg_id * 2] = nlen;
- targdata->dsa[DSA_DATAIN + sg_id * 2 + 1] = naddr;
- resume_offset = Ent_resume_pmm;
- }
- }
- else if (index > Ent_patch_output_data/4 &&
- index <= Ent_end_data_trans/4) {
- /* Dataout transfer phase */
- u32 sg_id, oaddr, olen, naddr, nlen;
- int residual;
- sg_id = (index - Ent_patch_output_data/4 - 4) / 2;
- targdata->data_out_jump = hostdata->script[Ent_patch_output_data/4+1] =
- virt_to_bus(hostdata->script + Ent_patch_output_data/4 + sg_id * 2 + 2);
- olen = targdata->dsa[DSA_DATAOUT + sg_id * 2];
- oaddr = targdata->dsa[DSA_DATAOUT + sg_id * 2 + 1];
- residual = datapath_residual (host);
- naddr = NCR_read32(DNAD_REG) - residual;
- nlen = (NCR_read32(DBC_REG) & 0x00ffffff) + residual;
- DEB(DEB_PMM, printk("scsi%d: DOUT sg %d, old %08x/%08x, new %08x/%08x (%d)n",
- host->host_no, sg_id, oaddr, olen, naddr, nlen, residual));
- if (oaddr+olen != naddr+nlen) {
- printk("scsi%d: PMM DOUT counts error: 0x%x + 0x%x != 0x%x + 0x%x",
- host->host_no, oaddr, olen, naddr, nlen);
- }
- else {
- targdata->dsa[DSA_DATAOUT + sg_id * 2] = nlen;
- targdata->dsa[DSA_DATAOUT + sg_id * 2 + 1] = naddr;
- resume_offset = Ent_resume_pmm;
- }
- }
- else if (sbcl == SBCL_PHASE_STATIN) {
- /* Change to Status In at some random point; probably wants to report a
- * parity error or similar.
- */
- printk("scsi%d: Unexpected phase change to STATUSIN at index 0x%xn",
- host->host_no, index);
- resume_offset = Ent_end_data_trans;
- }
- else {
- printk("scsi%d: Unexpected phase change to %s at index 0x%xn",
- host->host_no, sbcl_to_phase(sbcl), index);
- /* resume_offset is zero, which will cause a host reset */
- }
- /* Flush DMA FIFO */
- NCR_write8 (CTEST8_REG, CTEST8_10_CLF);
- while (NCR_read8 (CTEST8_REG) & CTEST8_10_CLF);
- return resume_offset;
- }
- static u32
- handle_script_int(struct Scsi_Host * host, Scsi_Cmnd * cmd)
- {
- struct sim710_hostdata *hostdata =
- (struct sim710_hostdata *)host->hostdata[0];
- struct sim710_target *targdata = hostdata->target + cmd->target;
- u32 dsps, resume_offset = 0;
- unsigned char sbcl;
- dsps = NCR_read32(DSPS_REG);
- switch (dsps) {
- case A_int_cmd_complete:
- cmd->result = targdata->dsa_status[0];
- SCSI_DONE(cmd);
- targdata->cur_cmd = NULL;
- resume_offset = Ent_reselect;
- break;
- case A_int_msg_sdtr1:
- resume_offset = handle_sdtr(host, cmd,
- Ent_resume_msgin1a, Ent_resume_msgin1b);
- break;
- case A_int_msg_sdtr2:
- resume_offset = handle_sdtr(host, cmd, 0, Ent_resume_msgin2b);
- break;
- case A_int_msg_sdtr3:
- resume_offset = handle_sdtr(host, cmd, 0, Ent_resume_msgin3b);
- break;
- case A_int_disc1:
- /* Disconnect before command - not expected */
- targdata->resume_offset = Ent_resume_msgin1a;
- resume_offset = Ent_reselect;
- break;
- case A_int_disc2:
- /* Disconnect after command - just wait for a reselect */
- targdata->resume_offset = Ent_resume_msgin2a;
- resume_offset = Ent_reselect;
- break;
- case A_int_disc3:
- /* Disconnect after the data phase */
- targdata->resume_offset = Ent_resume_msgin3a;
- resume_offset = Ent_reselect;
- break;
- case A_int_reselected:
- hostdata->script[Ent_patch_output_data/4+1] = targdata->data_out_jump;
- hostdata->script[Ent_patch_input_data/4+1] = targdata->data_in_jump;
- NCR_write32(DSA_REG, virt_to_bus(targdata->dsa));
- resume_offset = targdata->resume_offset;
- break;
- case A_int_data_bad_phase:
- sbcl = NCR_read8(SBCL_REG) & SBCL_PHASE_MASK;
- printk("scsi%d: int_data_bad_phase, phase %s (%x)n",
- host->host_no, sbcl_to_phase(sbcl), sbcl);
- break;
- case A_int_bad_msg1:
- case A_int_bad_msg2:
- case A_int_bad_msg3:
- case A_int_cmd_bad_phase:
- case A_int_no_msgout1:
- case A_int_no_msgout2:
- case A_int_no_msgout3:
- case A_int_not_cmd_complete:
- case A_int_sel_no_ident:
- case A_int_sel_not_cmd:
- case A_int_status_not_msgin:
- case A_int_resel_not_msgin:
- case A_int_selected:
- case A_int_not_rej:
- default:
- sbcl = NCR_read8(SBCL_REG) & SBCL_PHASE_MASK;
- printk("scsi%d: Unimplemented script interrupt: %08x, phase %sn",
- host->host_no, dsps, sbcl_to_phase(sbcl));
- sim710_errors++;
- /* resume_offset is zero, which will cause a host reset */
- }
- return resume_offset;
- }
- /* A quick wrapper for sim710_intr_handle to grab the spin lock */
- static void
- do_sim710_intr_handle(int irq, void *dev_id, struct pt_regs *regs)
- {
- unsigned long flags;
- spin_lock_irqsave(&io_request_lock, flags);
- sim710_intr_handle(irq, dev_id, regs);
- spin_unlock_irqrestore(&io_request_lock, flags);
- }
- /* A "high" level interrupt handler */
- static void
- sim710_intr_handle(int irq, void *dev_id, struct pt_regs *regs)
- {
- struct Scsi_Host * host = (struct Scsi_Host *)dev_id;
- struct sim710_hostdata *hostdata = (struct sim710_hostdata *)host->hostdata[0];
- Scsi_Cmnd * cmd;
- unsigned char istat, dstat;
- unsigned char sstat0;
- u32 scratch, dsps, resume_offset = 0;
- istat = NCR_read8(ISTAT_REG);
- if (!(istat & (ISTAT_SIP|ISTAT_DIP)))
- return;
- else {
- sim710_intrs++;
- dsps = NCR_read32(DSPS_REG);
- hostdata->state = STATE_HALTED;
- sstat0 = dstat = 0;
- scratch = NCR_read32(SCRATCH_REG);
- if (istat & ISTAT_SIP) {
- sstat0 = NCR_read8(SSTAT0_REG);
- }
- if (istat & ISTAT_DIP) {
- udelay(10); /* Some comment somewhere about 10cycles
- * between accesses to sstat0 and dstat ??? */
- dstat = NCR_read8(DSTAT_REG);
- }
- DEB(DEB_INTS, printk("scsi%d: Int %d, istat %02x, sstat0 %02x "
- "dstat %02x, dsp [%04x], scratch %02xn",
- host->host_no, sim710_intrs, istat, sstat0, dstat,
- (u32 *)(bus_to_virt(NCR_read32(DSP_REG))) - hostdata->script,
- scratch));
- if (scratch & 0x100) {
- u8 *p = hostdata->msgin_buf;
- DEB(DEB_INTS, printk(" msgin_buf: %02x %02x %02x %02xn",
- p[0], p[1], p[2], p[3]));
- }
- if ((dstat & DSTAT_SIR) && dsps == A_int_reselected) {
- /* Reselected. Identify the target from LCRC_REG, and
- * update current command. If we were trying to select
- * a device, then that command needs to go back on the
- * issue_queue for later.
- */
- unsigned char lcrc = NCR_read8(LCRC_REG_10);
- int id = 0;
- if (!(lcrc & 0x7f)) {
- printk("scsi%d: Reselected with LCRC = %02xn",
- host->host_no, lcrc);
- cmd = NULL;
- }
- else {
- while (!(lcrc & 1)) {
- id++;
- lcrc >>= 1;
- }
- DEB(DEB_DISC, printk("scsi%d: Reselected by ID %dn",
- host->host_no, id));
- if (hostdata->running) {
- /* Clear SIGP */
- (void)NCR_read8(CTEST2_REG_700);
- DEB(DEB_DISC, printk("scsi%d: Select of %d interrupted "
- "by reselect from %d (%p)n",
- host->host_no, hostdata->running->target,
- id, hostdata->target[id].cur_cmd));
- cmd = hostdata->running;
- hostdata->target[cmd->target].cur_cmd = NULL;
- cmd->SCp.ptr = (unsigned char *) hostdata->issue_queue;
- hostdata->issue_queue = cmd;
- }
- cmd = hostdata->running = hostdata->target[id].cur_cmd;
- }
- }
- else
- cmd = hostdata->running;
- if (!cmd) {
- printk("scsi%d: No active command!n", host->host_no);
- printk("scsi%d: Int %d, istat %02x, sstat0 %02x "
- "dstat %02x, dsp [%04x], scratch %02x, dsps %08xn",
- host->host_no, sim710_intrs, istat, sstat0, dstat,
- (u32 *)(bus_to_virt(NCR_read32(DSP_REG))) - hostdata->script,
- NCR_read32(SCRATCH_REG), dsps);
- /* resume_offset is zero, which will cause a host reset */
- }
- else if (sstat0 & SSTAT0_700_STO) {
- DEB(DEB_TOUT, printk("scsi%d: Selection timeoutn", host->host_no));
- cmd->result = DID_NO_CONNECT << 16;
- SCSI_DONE(cmd);
- hostdata->target[cmd->target].cur_cmd = NULL;
- resume_offset = Ent_reselect;
- }
- else if (sstat0 & (SSTAT0_SGE|SSTAT0_UDC|SSTAT0_RST|SSTAT0_PAR)) {
- printk("scsi%d: Serious error, sstat0 = %02xn", host->host_no,
- sstat0);
- sim710_errors++;
- /* resume_offset is zero, which will cause a host reset */
- }
- else if (dstat & (DSTAT_BF|DSTAT_ABRT|DSTAT_SSI|DSTAT_WTD)) {
- printk("scsi%d: Serious error, dstat = %02xn", host->host_no,
- dstat);
- sim710_errors++;
- /* resume_offset is zero, which will cause a host reset */
- }
- else if (dstat & DSTAT_SIR)
- resume_offset = handle_script_int(host, cmd);
- else if (sstat0 & SSTAT0_MA)
- resume_offset = handle_phase_mismatch(host, cmd);
- else if (dstat & DSTAT_IID) {
- /* This can be due to a quick reselect while doing a WAIT
- * DISCONNECT.
- */
- resume_offset = handle_idd(host, cmd);
- }
- else {
- sim710_errors++;
- printk("scsi%d: Spurious interrupt!n", host->host_no);
- /* resume_offset is zero, which will cause a host reset */
- }
- }
- if (resume_offset) {
- if (resume_offset == Ent_reselect) {
- hostdata->running = NULL;
- hostdata->state = STATE_IDLE;
- }
- else
- hostdata->state = STATE_BUSY;
- DEB(DEB_RESUME, printk("scsi%d: Resuming at script[0x%x]n",
- host->host_no, resume_offset/4));
- #ifdef DEBUG_LIMIT_INTS
- if (sim710_intrs < DEBUG_LIMIT_INTS)
- #endif
- {
- NCR_write32(SCRATCH_REG, 0);
- NCR_write32(DSP_REG, virt_to_bus(hostdata->script+resume_offset/4));
- }
- if (resume_offset == Ent_reselect)
- run_process_issue_queue(hostdata);
- }
- else {
- printk("scsi%d: Failed to handle interrupt. Failing commands "
- "and resetting SCSI bus and chipn", host->host_no);
- mdelay(1000); /* Give chance to read screen!! */
- full_reset(host);
- }
- }
- static void
- run_command (struct sim710_hostdata *hostdata, Scsi_Cmnd *cmd)
- {
- struct Scsi_Host *host = cmd->host;
- struct sim710_target *targdata = hostdata->target + cmd->target;
- int i, datain, dataout, sg_start;
- u32 *dip, *dop, dsa;
- DEB(DEB_CMND, printk("scsi%d: id%d starting ", host->host_no,
- cmd->target));
- DEB(DEB_CMND, print_command(cmd->cmnd));
- switch (cmd->cmnd[0]) {
- case INQUIRY:
- case MODE_SENSE:
- case READ_6:
- case READ_10:
- case READ_CAPACITY:
- case REQUEST_SENSE:
- case READ_BLOCK_LIMITS:
- case READ_TOC:
- datain = 1;
- dataout = 0;
- break;
- case MODE_SELECT:
- case WRITE_6:
- case WRITE_10:
- datain = 0;
- dataout = 1;
- break;
- case TEST_UNIT_READY:
- case ALLOW_MEDIUM_REMOVAL:
- case START_STOP:
- datain = dataout = 0;
- break;
- default:
- datain = dataout = 1;
- }
- memcpy(targdata->dsa_cdb, cmd->cmnd, MAX_CMND);
- targdata->dsa_msgout[0] =
- IDENTIFY((opt_nodisc[hostdata->chip] & (1<<cmd->target)) ? 0 : 1 ,0);
- if (hostdata->negotiate & (1 << cmd->target)) {
- if (opt_noneg[hostdata->chip] & (1 << cmd->target)) {
- hostdata->negotiate ^= (1 << cmd->target);
- targdata->dsa[DSA_MSGOUT] = 1;
- }
- else {
- DEB(DEB_SYNC, printk("scsi%d: Negotiating async transfers "
- "for ID %dn",
- host->host_no, cmd->target));
- memcpy(targdata->dsa_msgout+1, async_message, sizeof(async_message));
- targdata->dsa[DSA_MSGOUT] = sizeof(async_message) + 1;
- }
- }
- else
- targdata->dsa[DSA_MSGOUT] = 1;
- targdata->dsa_msgin[0] = 0xff;
- targdata->dsa_status[0] = 0xff;
- targdata->dsa[DSA_SELECT] = (1 << cmd->target) << 16;
- targdata->dsa[DSA_MSGOUT+1] = virt_to_bus(targdata->dsa_msgout);
- targdata->dsa[DSA_CMND] = cmd->cmd_len;
- targdata->dsa[DSA_CMND+1] = virt_to_bus(targdata->dsa_cdb);
- targdata->dsa[DSA_STATUS] = 1;
- targdata->dsa[DSA_STATUS+1] = virt_to_bus(targdata->dsa_status);
- targdata->dsa[DSA_MSGIN] = 1;
- targdata->dsa[DSA_MSGIN+1] = virt_to_bus(targdata->dsa_msgin);
- sg_start = (MAX_SG - (cmd->use_sg ? cmd->use_sg : 1)) * 2;
- dip = targdata->dsa + DSA_DATAIN + sg_start;
- dop = targdata->dsa + DSA_DATAOUT + sg_start;
- for (i = 0; cmd->use_sg ? (i < cmd->use_sg) : !i; i++) {
- u32 vbuf = cmd->use_sg ?
- (u32)(((struct scatterlist *)cmd->buffer)[i].address) :
- (u32)(cmd->request_buffer);
- u32 bbuf = virt_to_bus((void *)vbuf);
- u32 cnt = cmd->use_sg ?
- ((struct scatterlist *)cmd->buffer)[i].length :
- cmd->request_bufflen;
- if (datain) {
- *dip++ = cnt;
- *dip++ = bbuf;
- }
- if (dataout) {
- *dop++ = cnt;
- *dop++ = bbuf;
- }
- }
- targdata->data_out_jump = hostdata->script[Ent_patch_output_data/4+1] =
- virt_to_bus(hostdata->script + Ent_patch_output_data/4 + sg_start + 2);
- targdata->data_in_jump = hostdata->script[Ent_patch_input_data/4+1] =
- virt_to_bus(hostdata->script + Ent_patch_input_data/4 + sg_start + 2);
- for (i = 0, dsa = virt_to_bus(targdata->dsa); i < 4; i++) {
- u32 v = hostdata->script[Ent_patch_new_dsa/4 + i * 2];
- v &= ~0x0000ff00;
- v |= (dsa & 0xff) << 8;
- hostdata->script[Ent_patch_new_dsa/4 + i * 2] = v;
- dsa >>= 8;
- }
- hostdata->running = targdata->cur_cmd = cmd;
- hostdata->state = STATE_BUSY;
- NCR_write8(ISTAT_REG, ISTAT_10_SIGP);
- }
- static volatile int process_issue_queue_running = 0;
-
- static __inline__ void
- run_process_issue_queue(struct sim710_hostdata *hostdata)
- {
- unsigned long flags;
- save_flags (flags);
- cli();
- if (!process_issue_queue_running) {
- process_issue_queue_running = 1;
- process_issue_queue(hostdata, flags);
- /*
- * process_issue_queue_running is cleared in process_issue_queue
- * once it can't do more work, and process_issue_queue exits with
- * interrupts disabled.
- */
- }
- restore_flags (flags);
- }
- /*
- * Function : process_issue_queue (hostdata, flags)
- *
- * Purpose : Start next command for any idle target.
- *
- * NOTE : process_issue_queue exits with interrupts *disabled*, so the
- * caller must reenable them if it desires.
- *
- * NOTE : process_issue_queue should be called from both
- * sim710_queue_command() and from the interrupt handler
- * after command completion.
- */
- static void
- process_issue_queue (struct sim710_hostdata *hostdata, unsigned long flags)
- {
- Scsi_Cmnd *tmp, *prev;
- 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 process_issue_queue_running to 0 and exit.
- *
- * Interrupts are enabled before doing various other internal
- * instructions, after we've decided that we need to run through
- * the loop again.
- *
- */
- do {
- cli(); /* Freeze request queues */
- done = 1;
- if (hostdata->issue_queue) {
- if (hostdata->state == STATE_DISABLED) {
- tmp = (Scsi_Cmnd *) hostdata->issue_queue;
- hostdata->issue_queue = (Scsi_Cmnd *) tmp->SCp.ptr;
- tmp->result = (DID_BAD_TARGET << 16);
- tmp->scsi_done (tmp);
- done = 0;
- }
- else if (hostdata->state == STATE_IDLE) {
- for (tmp = hostdata->issue_queue, prev = NULL; tmp;
- prev = tmp, tmp = (Scsi_Cmnd *) tmp->SCp.ptr) {
- if (hostdata->target[tmp->target].cur_cmd == NULL) {
- if (prev)
- prev->SCp.ptr = tmp->SCp.ptr;
- else
- hostdata->issue_queue = (Scsi_Cmnd *) tmp->SCp.ptr;
- tmp->SCp.ptr = NULL;
- run_command (hostdata, tmp);
- done = 0;
- } /* if target/lun is not busy */
- } /* scan issue queue for work */
- } /* host is idle */
- } /* if hostdata->issue_queue */
- if (!done)
- restore_flags (flags);
- } while (!done);
- process_issue_queue_running = 0;
- }
- int
- sim710_queuecommand(Scsi_Cmnd * cmd, void (*done)(Scsi_Cmnd *))
- {
- struct Scsi_Host *host = cmd->host;
- struct sim710_hostdata *hostdata = (struct sim710_hostdata *)host->hostdata[0];
- Scsi_Cmnd *tmp;
- unsigned long flags;
- if (cmd->lun) {
- /* Silently ignore luns other than zero! */
- cmd->result = (DID_BAD_TARGET << 16);
- done(cmd);
- return 0;
- }
- DEB(DEB_CMND, printk("scsi%d: id%d queuing ", host->host_no,
- cmd->target));
- DEB(DEB_CMND, print_command(cmd->cmnd));
- cmd->scsi_done = done;
- cmd->host_scribble = NULL;
- cmd->SCp.ptr = NULL;
- cmd->SCp.buffer = NULL;
- save_flags(flags);
- cli();
- if (ignore_ids[hostdata->chip] & (1 << cmd->target)) {
- printk("scsi%d: ignoring target %dn", host->host_no, cmd->target);
- cmd->result = (DID_BAD_TARGET << 16);
- done(cmd);
- restore_flags (flags);
- return 0;
- }
- #ifdef DEBUG_LIMIT_INTS
- if (sim710_intrs > DEBUG_LIMIT_INTS) {
- cmd->result = (DID_BAD_TARGET << 16);
- done(cmd);
- restore_flags (flags);
- return 0;
- }
- #endif
- if (cmd->use_sg > MAX_SG)
- panic ("cmd->use_sg = %dn", cmd->use_sg);
- if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) {
- cmd->SCp.ptr = (unsigned char *) hostdata->issue_queue;
- hostdata->issue_queue = cmd;
- } else {
- for (tmp = hostdata->issue_queue; tmp->SCp.ptr;
- tmp = (Scsi_Cmnd *) tmp->SCp.ptr);
- tmp->SCp.ptr = (unsigned char *) cmd;
- }
- restore_flags (flags);
- run_process_issue_queue(hostdata);
- return 0;
- }
- __init int
- sim710_detect(Scsi_Host_Template * tpnt)
- {
- unsigned char scsi_id;
- unsigned int base_addr;
- struct Scsi_Host * host = NULL;
- struct sim710_hostdata *hostdata;
- unsigned long timeout;
- unsigned long irq_mask;
- int requested_irq;
- int probed_irq;
- u32 dsps;
- int chips = 0;
- int limit;
- int indx;
- int revision;
- int size;
- volatile u8 tmp;
- struct Scsi_Host *our_hosts[MAXBOARDS+1];
- #ifdef MODULE
- if (sim710)
- param_setup(sim710);
- #endif
- if (no_of_boards < 0) {
- printk("sim710: NCR53C710 driver disabledn");
- return 0;
- }
- #ifdef CONFIG_MCA
- /* If board details have been specified via boot/module parameters,
- * then don't bother probing.
- */
- if (no_of_boards == 0) {
- int slot;
- int pos[3];
- int mca_53c710_ids[] = MCA_53C710_IDS;
- int *id_to_check = mca_53c710_ids;
- static int io_004f_by_pos[] = MCA_004F_IO_PORTS;
- static int irq_004f_by_pos[] = MCA_004F_IRQS;
- static int io_01bb_by_pos[] = MCA_01BB_IO_PORTS;
- static int irq_01bb_by_pos[] = MCA_01BB_IRQS;
- while ( *id_to_check && no_of_boards < MAXBOARDS) {
- if (!MCA_bus)
- return 0;
- if ((slot = mca_find_adapter(*id_to_check, 0)) != MCA_NOTFOUND) {
- pos[0] = mca_read_stored_pos(slot, 2);
- pos[1] = mca_read_stored_pos(slot, 3);
- pos[2] = mca_read_stored_pos(slot, 4);
- /*
- * 01BB & 01BA port base by bits 7,6,5,4,3,2 in pos[2]
- *
- * 000000 <disabled> 001010 0x2800
- * 000001 <invalid> 001011 0x2C00
- * 000010 0x0800 001100 0x3000
- * 000011 0x0C00 001101 0x3400
- * 000100 0x1000 001110 0x3800
- * 000101 0x1400 001111 0x3C00
- * 000110 0x1800 010000 0x4000
- * 000111 0x1C00 010001 0x4400
- * 001000 0x2000 010010 0x4800
- * 001001 0x2400 010011 0x4C00
- * 010100 0x5000
- *
- * 00F4 port base by bits 3,2,1 in pos[0]
- *
- * 000 <disabled> 001 0x200
- * 010 0x300 011 0x400
- * 100 0x500 101 0x600
- *
- * 01BB & 01BA IRQ is specified in pos[0] bits 7 and 6:
- *
- * 00 3 10 11
- * 01 5 11 14
- *
- * 00F4 IRQ specified by bits 6,5,4 in pos[0]
- *
- * 100 5 101 9
- * 110 14
- */
- if ( *id_to_check == 0x01bb || *id_to_check == 0x01ba ) {
- bases[no_of_boards] = io_01bb_by_pos[(pos[2] & 0xFC) >> 2];
- irq_vectors[no_of_boards] =
- irq_01bb_by_pos[((pos[0] & 0xC0) >> 6)];
- if (bases[no_of_boards] == 0x0000)
- printk("sim710: NCR53C710 Adapter ID 0x01bb is disabled.n");
- else {
- no_of_boards++;
- if ( *id_to_check == 0x01bb )
- mca_set_adapter_name( slot,
- "NCR 3360/3430 SCSI SubSystem" );
- else
- mca_set_adapter_name(slot,
- "NCR Dual SIOP SCSI Host Adapter Board");
- }
- }
- else if ( *id_to_check == 0x004f ) {
- bases[no_of_boards] = io_004f_by_pos[((pos[0] & 0x0E) >> 1)];
- irq_vectors[no_of_boards] =
- irq_004f_by_pos[((pos[0] & 0x70) >> 4) - 4];
- if (bases[no_of_boards] == 0x0000)
- printk("sim710: NCR53C710 Adapter ID 0x004f is disabled.n");
- else {
- no_of_boards++;
- mca_set_adapter_name(slot,
- "NCR 53c710 SCSI Host Adapter Board");
- }
- }
- }
- id_to_check++;
- }
- }
- #endif
- #ifdef CONFIG_EISA
- /* Auto probe, if no boards specified in boot parameters */
- if (no_of_boards == 0) {
- int io_addr;
- /* reverse probe, so my on-board controller at 0x9000 is always scsi0 */
- for (io_addr = 0x9000; no_of_boards < MAXBOARDS && io_addr >= 0x1000; io_addr -= 0x1000) {
- if (request_region(io_addr, 0x40, "sim710") != NULL) {
- int id0 = inw(io_addr + 0xc80);
- int id1 = inw(io_addr + 0xc82);
- /* The on-board controller on my Proliant 2000 is 0x1044,
- * my EISA card is 0x1144.
- */
- if (id0 == 0x110e && (id1 == 0x1044 || id1 == 0x1144)) {
- bases[no_of_boards] = io_addr;
- #if 0
- /* This should detect the IRQ, but I havn't proved it for
- * myself. Leave the old probe code active for now, as
- * no-one has reported problems with it.
- */
- switch (inb(io_addr + 0xc88)) {
- case (0x00):
- irq_vectors[no_of_boards] = 11;
- break;
- case (0x01):
- irq_vectors[no_of_boards] = 14;
- break;
- case (0x02):
- irq_vectors[no_of_boards] = 15;
- break;
- case (0x03):
- irq_vectors[no_of_boards] = 10;
- break;
- case (0x04):
- irq_vectors[no_of_boards] = 9;
- break;
- default:
- printk("sim710.c: irq nastyn");
- }
- #endif
- no_of_boards++;
- }
- release_region(io_addr, 64);
- }
- }
- }
- #endif
- if (!no_of_boards) {
- printk("sim710: No NCR53C710 adapter found.n");
- return 0;
- }
- size = sizeof(struct sim710_hostdata);
- hostdata_order = 0;
- while (size > (PAGE_SIZE << hostdata_order))
- hostdata_order++;
- size = PAGE_SIZE << hostdata_order;
- DEB(DEB_ANY, printk("sim710: hostdata %d bytes, size %d, order %dn",
- sizeof(struct sim710_hostdata), size, hostdata_order));
- tpnt->proc_name = "sim710";
- memset(our_hosts, 0, sizeof(our_hosts));
- for (indx = 0; indx < no_of_boards; indx++) {
- unsigned long page = __get_free_pages(GFP_ATOMIC, hostdata_order);
- if(page == 0UL)
- {
- printk(KERN_WARNING "sim710: out of memory registering board %d.n", indx);
- break;
- }
- host = scsi_register(tpnt, 4);
- if(host == NULL) {
- free_pages(host->hostdata[0], hostdata_order);
- break;
- }
- our_hosts[chips] = host;
- host->hostdata[0] = page;
- hostdata = (struct sim710_hostdata *)host->hostdata[0];
- memset(hostdata, 0, size);
- scsi_id = 7;
- base_addr = bases[indx];
- requested_irq = irq_vectors[indx];
- printk("scsi%d: Configuring Sim710 (SCSI-ID %d) at %x, IRQ %dn",
- host->host_no, scsi_id, base_addr, requested_irq);
- DEB(DEB_ANY, printk("sim710: hostdata = %p (%d bytes), dsa0 = %pn",
- hostdata, sizeof(struct sim710_hostdata),
- hostdata->target[0].dsa));
- hostdata->chip = indx;
- host->irq = requested_irq;
- host->this_id = scsi_id;
- host->unique_id = base_addr;
- host->base = base_addr;
- hostdata->msg_reject = MESSAGE_REJECT;
- if (ncr_halt(host)) {
- free_pages(host->hostdata[0], hostdata_order);
- scsi_unregister (host);
- printk("scsi%d: Failed to initialise 53c710 at address %xn",
- host->host_no, base_addr);
- continue;
- }
- DEB(DEB_ANY,ncr_dump(host));
- revision = (NCR_read8(CTEST8_REG) & 0xF0) >> 4;
- printk("scsi%d: Revision 0x%xn",host->host_no,revision);
- sim710_soft_reset(host);
- sim710_driver_init(host);
- request_region((u32)host->base, 64, "sim710");
- /* Now run test1 */
- hostdata->test1_src = 0x53c710aa;
- hostdata->test1_dst = 0x76543210;
- NCR_write32(DSPS_REG, 0x89abcdef);
- irq_mask = probe_irq_on();
- NCR_write32(DSP_REG, virt_to_bus(hostdata->script+Ent_test1/4));
- timeout = 5;
- while (hostdata->test1_dst != hostdata->test1_src && timeout--)
- mdelay(100);
- tmp = NCR_read8(ISTAT_REG);
- tmp = NCR_read8(SSTAT0_REG);
- udelay(10);
- tmp = NCR_read8(DSTAT_REG);
- probed_irq = probe_irq_off(irq_mask);
- if (requested_irq == 0) {
- if (probed_irq > 0) {
- printk("scsi%d: Chip is using IRQ %dn", host->host_no,
- probed_irq);
- requested_irq = host->irq = probed_irq;
- }
- else {
- printk("scsi%d: Failed to probe for IRQ (returned %d)n",
- host->host_no, probed_irq);
- ncr_halt(host);
- free_pages(host->hostdata[0], hostdata_order);
- scsi_unregister (host);
- release_region((u32)host->base, 64);
- continue;
- }
- }
- else if (probed_irq > 0 && probed_irq != requested_irq)
- printk("scsi%d: WARNING requested IRQ %d, but probed as %dn",
- host->host_no, requested_irq, probed_irq);
- else if (probed_irq <= 0)
- printk("scsi%d: WARNING IRQ probe failed, (returned %d)n",
- host->host_no, probed_irq);
-
- dsps = NCR_read32(DSPS_REG);
- if (hostdata->test1_dst != 0x53c710aa || dsps != A_int_test1) {
- if (hostdata->test1_dst != 0x53c710aa)
- printk("scsi%d: test 1 FAILED: data: exp 0x53c710aa, got 0x%08xn",
- host->host_no, hostdata->test1_dst);
- if (dsps != A_int_test1)
- printk("scsi%d: test 1 FAILED: dsps: exp 0x%08x, got 0x%08xn",
- host->host_no, A_int_test1, dsps);
- ncr_dump(host);
- ncr_halt(host);
- free_pages(host->hostdata[0], hostdata_order);
- scsi_unregister (host);
- release_region((u32)host->base, 64);
- continue;
- }
- printk("scsi%d: test 1 completed ok.n", host->host_no);
- NCR_write32(DSP_REG, virt_to_bus(hostdata->script+Ent_reselect/4));
- hostdata->state = STATE_IDLE;
- chips++;
- }
- /* OK, now run down our_hosts[] calling request_irq(... SA_SHIRQ ...).
- * Couldn't call request_irq earlier, as probing would have failed.
- */
- for (indx = 0, limit = chips; indx < limit; indx++) {
- host = our_hosts[indx];
- if (request_irq(host->irq, do_sim710_intr_handle,
- SA_INTERRUPT | SA_SHIRQ, "sim710", host))
- {
- printk("scsi%d : IRQ%d not free, detachingn",
- host->host_no, host->irq);
- ncr_halt(host);
- free_pages(host->hostdata[0], hostdata_order);
- scsi_unregister (host);
- chips--;
- }
- }
- return chips;
- }
- int
- sim710_abort(Scsi_Cmnd * cmd)
- {
- struct Scsi_Host * host = cmd->host;
- printk("scsi%d: Unable to abort command for target %dn",
- host->host_no, cmd->target);
- return FAILED;
- }
- /*
- * This is a device reset. Need to select and send a Bus Device Reset msg.
- */
- int
- sim710_dev_reset(Scsi_Cmnd * SCpnt)
- {
- struct Scsi_Host * host = SCpnt->host;
- printk("scsi%d: Unable to send Bus Device Reset for target %dn",
- host->host_no, SCpnt->target);
- return FAILED;
- }
- /*
- * This is bus reset. We need to reset the bus and fail any active commands.
- */
- int
- sim710_bus_reset(Scsi_Cmnd * SCpnt)
- {
- struct Scsi_Host * host = SCpnt->host;
- printk("scsi%d: Unable to do SCSI bus resetn", host->host_no);
- return FAILED;
- }
- static int
- full_reset(struct Scsi_Host * host)
- {
- struct sim710_hostdata *hostdata = (struct sim710_hostdata *)
- host->hostdata[0];
- int target;
- Scsi_Cmnd *cmd;
- u32 istat, dstat = 0, sstat0 = 0, sstat1 = 0, dsp, dsps, scratch;
- unsigned long flags;
- save_flags(flags);
- cli();
- istat = NCR_read8(ISTAT_REG);
- if (istat & ISTAT_SIP) {
- sstat0 = NCR_read8(SSTAT0_REG);
- sstat1 = NCR_read8(SSTAT1_REG);
- udelay(10);
- }
- if (istat & ISTAT_DIP)
- dstat = NCR_read8(DSTAT_REG);
- if (ncr_halt(host)) {
- restore_flags(flags);
- return FAILED;
- }
- restore_flags(flags);
- dsp = NCR_read32(DSP_REG);
- dsps = NCR_read32(DSPS_REG);
- scratch = NCR_read32(SCRATCH_REG);
- printk("scsi%d: istat = %02x, sstat0 = %02x, sstat1 = %02x, dstat = %02xn",
- host->host_no, istat, sstat0, sstat1, dstat);
- printk("scsi%d: dsp = %08x (script[0x%04x]), dsps = %08x, scratch = %08xn",
- host->host_no, dsp,
- ((u32)bus_to_virt(dsp) - (u32)hostdata->script)/4, dsps, scratch);
- for (target = 0; target < 7; target++) {
- if ((cmd = hostdata->target[target].cur_cmd)) {
- printk("scsi%d: Failing command for ID%dn",
- host->host_no, target);
- cmd->result = DID_RESET << 16;
- cmd->scsi_done(cmd);
- hostdata->target[target].cur_cmd = NULL;
- }
- }
- sim710_soft_reset(host);
- sim710_driver_init(host);
- NCR_write32(DSP_REG, virt_to_bus(hostdata->script+Ent_reselect/4));
- hostdata->state = STATE_IDLE;
- run_process_issue_queue(hostdata);
- return SUCCESS;
- }
- /*
- * This is host reset. We need to reset the chip and the bus.
- */
- int
- sim710_host_reset(Scsi_Cmnd * SCpnt)
- {
- struct Scsi_Host * host = SCpnt->host;
- printk("scsi%d: >>>>>>>>>>>> Host reset <<<<<<<<<<<<n", host->host_no);
- return full_reset(host);
- }
- #ifdef MODULE
- int
- sim710_release(struct Scsi_Host *host)
- {
- ncr_halt(host);
- free_pages(host->hostdata[0], hostdata_order);
- free_irq(host->irq, host);
- release_region((u32)host->base, 64);
- return 1;
- }
- #endif
- static Scsi_Host_Template driver_template = SIM710_SCSI;
- #include "scsi_module.c"