📄 sim710.c
字号:
/* * 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. * * 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" * * 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. * * Current limitations: * * o Async only * o Severely lacking in error recovery * o Auto detection of IRQs and chip addresses only on MCA architectures * */#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 <asm/dma.h>#include <asm/system.h>#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,17)#include <linux/spinlock.h>#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,93)#include <asm/spinlock.h>#endif#include <asm/io.h>#include <asm/pgtable.h>#include <asm/byteorder.h>#include <linux/blk.h>#ifdef CONFIG_TP34V_SCSI#include <asm/tp34vhw.h>#define MEM_MAPPED#elif defined(CONFIG_MCA)#define IO_MAPPED/* * 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 }#else/* Assume an Intel platform */#define IO_MAPPED#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) xint sim710_debug = 0;#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 %08x\n", \ 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 2 /* Increase this and the sizes of the arrays below, if you need more.. */#ifdef MODULEchar *sim710; /* command line passed by insmod */MODULE_AUTHOR("Richard Hirst");MODULE_DESCRIPTION("Simple NCR53C710 driver");MODULE_PARM(sim710, "s");#endifstatic int sim710_errors = 0; /* Count of error interrupts */static int sim710_intrs = 0; /* Count of all interrupts */static int ignore_ids = 0; /* Accept all SCSI IDs */static int opt_nodisc = 0; /* Allow disconnect on all IDs */static int opt_noneg = 0; /* Allow SDTR negotiation on all IDs */#ifdef CONFIG_TP34V_SCSI/* Special hardwired case for Tadpole TP34V at the moment, otherwise * boot parameters 'sim710=addr:0x8000,irq:15' (for example) must be given. */static int no_of_boards = 2;static unsigned int bases[MAXBOARDS] = { TP34V_SCSI0_BASE, TP34V_SCSI1_BASE};static unsigned int irq_vectors[MAXBOARDS] = { TP34V_SCSI0_VECTOR, TP34V_SCSI1_VECTOR};static unsigned int irq_index[MAXBOARDS] = { TP34V_SCSI0_IRQ_INDEX, TP34V_SCSI1_IRQ_INDEX};#else/* All other cases use boot/module params, or auto-detect */static int no_of_boards = 0;static unsigned int bases[MAXBOARDS] = { 0};static unsigned int irq_vectors[MAXBOARDS] = { 0};#endif/* 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 1struct sim710_hostdata{ int state; Scsi_Cmnd * issue_queue; Scsi_Cmnd * running; int chip; u8 negotiate; u8 reselected_identify; u8 msgin_buf[MAX_MSGIN]; 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: int param_setup(char *str) */#ifdef MODULE#define ARG_SEP ' '#else#define ARG_SEP ','#endifstatic intparam_setup(char *str){ char *cur = str; char *pc, *pv; int val; int base; int c; no_of_boards = 0; while (cur != NULL && (pc = strchr(cur, ':')) != NULL) { char *pe; val = 0; pv = pc; c = *++pv; if (c == 'n') val = 0; else if (c == 'y') val = 1; else { base = 0; val = (int) simple_strtoul(pv, &pe, base); } if (!strncmp(cur, "addr:", 5)) { bases[0] = val; no_of_boards = 1; } else if (!strncmp(cur, "irq:", 4)) irq_vectors[0] = val; else if (!strncmp(cur, "ignore:", 7)) ignore_ids = val; else if (!strncmp(cur, "nodisc:", 7)) opt_nodisc = val; else if (!strncmp(cur, "noneg:", 6)) opt_noneg = val; else if (!strncmp(cur, "disabled:", 5)) { no_of_boards = -1; return 1; }#ifdef DEBUG else if (!strncmp(cur, "debug:", 6)) { sim710_debug = val; }#endif else printk("sim710: unexpected boot option '%.*s' ignored\n", (int)(pc-cur+1), cur); if ((cur = strchr(cur, ARG_SEP)) != NULL) ++cur; } return 1;}#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,13)#ifndef MODULE__setup("sim710=", param_setup);#endif#else/* Old boot param syntax support */voidsim710_setup(char *str, int *ints){ param_setup(str);}#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 void disable (struct Scsi_Host *host) */static voiddisable (struct Scsi_Host *host){ struct sim710_hostdata *hostdata = (struct sim710_hostdata *) host->hostdata[0]; hostdata->state = STATE_DISABLED; printk (KERN_ALERT "scsi%d : disabled. Unload and reload\n", host->host_no);}/* * 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 intncr_halt (struct Scsi_Host *host){ unsigned long flags; unsigned char istat, tmp; struct sim710_hostdata *hostdata = (struct sim710_hostdata *) host->hostdata[0]; int stage; 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 */ for (stage = 0;;) { if (stage == 1) { DEB(DEB_HALT, printk("ncr_halt: writing ISTAT_ABRT\n")); 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=%02x\n", istat)); tmp = NCR_read8(SSTAT0_REG); DEB(DEB_HALT, printk("ncr_halt: got SSTAT0_REG=%02x\n", tmp)); } else if (istat & ISTAT_DIP) { DEB(DEB_HALT, printk("ncr_halt: got ISTAT_DIP, istat=%02x\n", istat)); tmp = NCR_read8(DSTAT_REG); DEB(DEB_HALT, printk("ncr_halt: got DSTAT_REG=%02x\n", tmp)); if (stage == 2) { if (tmp & DSTAT_ABRT) { DEB(DEB_HALT, printk("ncr_halt: got DSTAT_ABRT, clearing istat\n")); NCR_write8(ISTAT_REG, 0); ++stage; } else { printk(KERN_ALERT "scsi%d : could not halt NCR chip\n", host->host_no); disable (host); } } } if (!(istat & (ISTAT_SIP|ISTAT_DIP))) { if (stage == 0) ++stage; else if (stage == 3) break; } } hostdata->state = STATE_HALTED; restore_flags(flags); 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 voidsim710_soft_reset (struct Scsi_Host *host){ unsigned long flags;#ifdef CONFIG_TP34V_SCSI struct sim710_hostdata *hostdata = (struct sim710_hostdata *) host->hostdata[0];#endif save_flags(flags); cli();#ifdef CONFIG_TP34V_SCSI tpvic.loc_icr[irq_index[hostdata->chip]].icr = 0x80;#endif /* * 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_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 |
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -