📄 ncr53c9x.c
字号:
/* NCR53C9x.c: Generic SCSI driver code for NCR53C9x chips. * * Originally esp.c : EnhancedScsiProcessor Sun SCSI driver code. * * Copyright (C) 1995, 1998 David S. Miller (davem@caip.rutgers.edu) * * Most DMA dependencies put in driver specific files by * Jesper Skov (jskov@cygnus.co.uk) * * Set up to use esp_read/esp_write (preprocessor macros in NCR53c9x.h) by * Tymm Twillman (tymm@coe.missouri.edu) *//* TODO: * * 1) Maybe disable parity checking in config register one for SCSI1 * targets. (Gilmore says parity error on the SBus can lock up * old sun4c's) * 2) Add support for DMA2 pipelining. * 3) Add tagged queueing. * 4) Maybe change use of "esp" to something more "NCR"'ish. */#ifdef MODULE#include <linux/module.h>#endif#include <linux/config.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/types.h>#include <linux/string.h>#include <linux/malloc.h>#include <linux/blk.h>#include <linux/proc_fs.h>#include <linux/stat.h>#include <linux/init.h>#include "scsi.h"#include "hosts.h"#include "NCR53C9x.h"#include <asm/system.h>#include <asm/ptrace.h>#include <asm/pgtable.h>#include <asm/io.h>#include <asm/irq.h>/* Command phase enumeration. */enum { not_issued = 0x00, /* Still in the issue_SC queue. */ /* Various forms of selecting a target. */#define in_slct_mask 0x10 in_slct_norm = 0x10, /* ESP is arbitrating, normal selection */ in_slct_stop = 0x11, /* ESP will select, then stop with IRQ */ in_slct_msg = 0x12, /* select, then send a message */ in_slct_tag = 0x13, /* select and send tagged queue msg */ in_slct_sneg = 0x14, /* select and acquire sync capabilities */ /* Any post selection activity. */#define in_phases_mask 0x20 in_datain = 0x20, /* Data is transferring from the bus */ in_dataout = 0x21, /* Data is transferring to the bus */ in_data_done = 0x22, /* Last DMA data operation done (maybe) */ in_msgin = 0x23, /* Eating message from target */ in_msgincont = 0x24, /* Eating more msg bytes from target */ in_msgindone = 0x25, /* Decide what to do with what we got */ in_msgout = 0x26, /* Sending message to target */ in_msgoutdone = 0x27, /* Done sending msg out */ in_cmdbegin = 0x28, /* Sending cmd after abnormal selection */ in_cmdend = 0x29, /* Done sending slow cmd */ in_status = 0x2a, /* Was in status phase, finishing cmd */ in_freeing = 0x2b, /* freeing the bus for cmd cmplt or disc */ in_the_dark = 0x2c, /* Don't know what bus phase we are in */ /* Special states, ie. not normal bus transitions... */#define in_spec_mask 0x80 in_abortone = 0x80, /* Aborting one command currently */ in_abortall = 0x81, /* Blowing away all commands we have */ in_resetdev = 0x82, /* SCSI target reset in progress */ in_resetbus = 0x83, /* SCSI bus reset in progress */ in_tgterror = 0x84, /* Target did something stupid */};enum { /* Zero has special meaning, see skipahead[12]. *//*0*/ do_never,/*1*/ do_phase_determine,/*2*/ do_reset_bus,/*3*/ do_reset_complete,/*4*/ do_work_bus,/*5*/ do_intr_end};/* The master ring of all esp hosts we are managing in this driver. */struct NCR_ESP *espchain = 0;int nesps = 0, esps_in_use = 0, esps_running = 0;void esp_intr(int irq, void *dev_id, struct pt_regs *pregs);/* Debugging routines */struct esp_cmdstrings { unchar cmdchar; char *text;} esp_cmd_strings[] = { /* Miscellaneous */ { ESP_CMD_NULL, "ESP_NOP", }, { ESP_CMD_FLUSH, "FIFO_FLUSH", }, { ESP_CMD_RC, "RSTESP", }, { ESP_CMD_RS, "RSTSCSI", }, /* Disconnected State Group */ { ESP_CMD_RSEL, "RESLCTSEQ", }, { ESP_CMD_SEL, "SLCTNATN", }, { ESP_CMD_SELA, "SLCTATN", }, { ESP_CMD_SELAS, "SLCTATNSTOP", }, { ESP_CMD_ESEL, "ENSLCTRESEL", }, { ESP_CMD_DSEL, "DISSELRESEL", }, { ESP_CMD_SA3, "SLCTATN3", }, { ESP_CMD_RSEL3, "RESLCTSEQ", }, /* Target State Group */ { ESP_CMD_SMSG, "SNDMSG", }, { ESP_CMD_SSTAT, "SNDSTATUS", }, { ESP_CMD_SDATA, "SNDDATA", }, { ESP_CMD_DSEQ, "DISCSEQ", }, { ESP_CMD_TSEQ, "TERMSEQ", }, { ESP_CMD_TCCSEQ, "TRGTCMDCOMPSEQ", }, { ESP_CMD_DCNCT, "DISC", }, { ESP_CMD_RMSG, "RCVMSG", }, { ESP_CMD_RCMD, "RCVCMD", }, { ESP_CMD_RDATA, "RCVDATA", }, { ESP_CMD_RCSEQ, "RCVCMDSEQ", }, /* Initiator State Group */ { ESP_CMD_TI, "TRANSINFO", }, { ESP_CMD_ICCSEQ, "INICMDSEQCOMP", }, { ESP_CMD_MOK, "MSGACCEPTED", }, { ESP_CMD_TPAD, "TPAD", }, { ESP_CMD_SATN, "SATN", }, { ESP_CMD_RATN, "RATN", },};#define NUM_ESP_COMMANDS ((sizeof(esp_cmd_strings)) / (sizeof(struct esp_cmdstrings)))/* Print textual representation of an ESP command */static inline void esp_print_cmd(unchar espcmd){ unchar dma_bit = espcmd & ESP_CMD_DMA; int i; espcmd &= ~dma_bit; for(i=0; i<NUM_ESP_COMMANDS; i++) if(esp_cmd_strings[i].cmdchar == espcmd) break; if(i==NUM_ESP_COMMANDS) printk("ESP_Unknown"); else printk("%s%s", esp_cmd_strings[i].text, ((dma_bit) ? "+DMA" : ""));}/* Print the status register's value */static inline void esp_print_statreg(unchar statreg){ unchar phase; printk("STATUS<"); phase = statreg & ESP_STAT_PMASK; printk("%s,", (phase == ESP_DOP ? "DATA-OUT" : (phase == ESP_DIP ? "DATA-IN" : (phase == ESP_CMDP ? "COMMAND" : (phase == ESP_STATP ? "STATUS" : (phase == ESP_MOP ? "MSG-OUT" : (phase == ESP_MIP ? "MSG_IN" : "unknown"))))))); if(statreg & ESP_STAT_TDONE) printk("TRANS_DONE,"); if(statreg & ESP_STAT_TCNT) printk("TCOUNT_ZERO,"); if(statreg & ESP_STAT_PERR) printk("P_ERROR,"); if(statreg & ESP_STAT_SPAM) printk("SPAM,"); if(statreg & ESP_STAT_INTR) printk("IRQ,"); printk(">");}/* Print the interrupt register's value */static inline void esp_print_ireg(unchar intreg){ printk("INTREG< "); if(intreg & ESP_INTR_S) printk("SLCT_NATN "); if(intreg & ESP_INTR_SATN) printk("SLCT_ATN "); if(intreg & ESP_INTR_RSEL) printk("RSLCT "); if(intreg & ESP_INTR_FDONE) printk("FDONE "); if(intreg & ESP_INTR_BSERV) printk("BSERV "); if(intreg & ESP_INTR_DC) printk("DISCNCT "); if(intreg & ESP_INTR_IC) printk("ILL_CMD "); if(intreg & ESP_INTR_SR) printk("SCSI_BUS_RESET "); printk(">");}/* Print the sequence step registers contents */static inline void esp_print_seqreg(unchar stepreg){ stepreg &= ESP_STEP_VBITS; printk("STEP<%s>", (stepreg == ESP_STEP_ASEL ? "SLCT_ARB_CMPLT" : (stepreg == ESP_STEP_SID ? "1BYTE_MSG_SENT" : (stepreg == ESP_STEP_NCMD ? "NOT_IN_CMD_PHASE" : (stepreg == ESP_STEP_PPC ? "CMD_BYTES_LOST" : (stepreg == ESP_STEP_FINI4 ? "CMD_SENT_OK" : "UNKNOWN"))))));}static char *phase_string(int phase){ switch(phase) { case not_issued: return "UNISSUED"; case in_slct_norm: return "SLCTNORM"; case in_slct_stop: return "SLCTSTOP"; case in_slct_msg: return "SLCTMSG"; case in_slct_tag: return "SLCTTAG"; case in_slct_sneg: return "SLCTSNEG"; case in_datain: return "DATAIN"; case in_dataout: return "DATAOUT"; case in_data_done: return "DATADONE"; case in_msgin: return "MSGIN"; case in_msgincont: return "MSGINCONT"; case in_msgindone: return "MSGINDONE"; case in_msgout: return "MSGOUT"; case in_msgoutdone: return "MSGOUTDONE"; case in_cmdbegin: return "CMDBEGIN"; case in_cmdend: return "CMDEND"; case in_status: return "STATUS"; case in_freeing: return "FREEING"; case in_the_dark: return "CLUELESS"; case in_abortone: return "ABORTONE"; case in_abortall: return "ABORTALL"; case in_resetdev: return "RESETDEV"; case in_resetbus: return "RESETBUS"; case in_tgterror: return "TGTERROR"; default: return "UNKNOWN"; };}#ifdef DEBUG_STATE_MACHINEstatic inline void esp_advance_phase(Scsi_Cmnd *s, int newphase){ ESPLOG(("<%s>", phase_string(newphase))); s->SCp.sent_command = s->SCp.phase; s->SCp.phase = newphase;}#else#define esp_advance_phase(__s, __newphase) \ (__s)->SCp.sent_command = (__s)->SCp.phase; \ (__s)->SCp.phase = (__newphase);#endif#ifdef DEBUG_ESP_CMDSextern inline void esp_cmd(struct NCR_ESP *esp, struct ESP_regs *eregs, unchar cmd){ esp->espcmdlog[esp->espcmdent] = cmd; esp->espcmdent = (esp->espcmdent + 1) & 31; esp_write(eregs->esp_cmnd, cmd);}#else#define esp_cmd(__esp, __eregs, __cmd) esp_write((__eregs)->esp_cmnd, (__cmd))#endif/* How we use the various Linux SCSI data structures for operation. * * struct scsi_cmnd: * * We keep track of the syncronous capabilities of a target * in the device member, using sync_min_period and * sync_max_offset. These are the values we directly write * into the ESP registers while running a command. If offset * is zero the ESP will use asynchronous transfers. * If the borken flag is set we assume we shouldn't even bother * trying to negotiate for synchronous transfer as this target * is really stupid. If we notice the target is dropping the * bus, and we have been allowing it to disconnect, we clear * the disconnect flag. *//* Manipulation of the ESP command queues. Thanks to the aha152x driver * and its author, Juergen E. Fischer, for the methods used here. * Note that these are per-ESP queues, not global queues like * the aha152x driver uses. */static inline void append_SC(Scsi_Cmnd **SC, Scsi_Cmnd *new_SC){ Scsi_Cmnd *end; new_SC->host_scribble = (unsigned char *) NULL; if(!*SC) *SC = new_SC; else { for(end=*SC;end->host_scribble;end=(Scsi_Cmnd *)end->host_scribble) ; end->host_scribble = (unsigned char *) new_SC; }}static inline void prepend_SC(Scsi_Cmnd **SC, Scsi_Cmnd *new_SC){ new_SC->host_scribble = (unsigned char *) *SC; *SC = new_SC;}static inline Scsi_Cmnd *remove_first_SC(Scsi_Cmnd **SC){ Scsi_Cmnd *ptr; ptr = *SC; if(ptr) *SC = (Scsi_Cmnd *) (*SC)->host_scribble; return ptr;}static inline Scsi_Cmnd *remove_SC(Scsi_Cmnd **SC, int target, int lun){ Scsi_Cmnd *ptr, *prev; for(ptr = *SC, prev = NULL; ptr && ((ptr->target != target) || (ptr->lun != lun)); prev = ptr, ptr = (Scsi_Cmnd *) ptr->host_scribble) ; if(ptr) { if(prev) prev->host_scribble=ptr->host_scribble; else *SC=(Scsi_Cmnd *)ptr->host_scribble; } return ptr;}/* Resetting various pieces of the ESP scsi driver chipset *//* Reset the ESP chip, _not_ the SCSI bus. */static void esp_reset_esp(struct NCR_ESP *esp, struct ESP_regs *eregs){ int family_code, version, i; volatile int trash; /* Now reset the ESP chip */ esp_cmd(esp, eregs, ESP_CMD_RC); esp_cmd(esp, eregs, ESP_CMD_NULL | ESP_CMD_DMA); if(esp->erev == fast) esp_write(eregs->esp_cfg2, ESP_CONFIG2_FENAB); esp_cmd(esp, eregs, ESP_CMD_NULL | ESP_CMD_DMA); /* This is the only point at which it is reliable to read * the ID-code for a fast ESP chip variant. */ esp->max_period = ((35 * esp->ccycle) / 1000); if(esp->erev == fast) { char *erev2string[] = { "Emulex FAS236", "Emulex FPESP100A", "fast", "QLogic FAS366", "Emulex FAS216", "Symbios Logic 53CF9x-2", "unknown!" }; version = esp_read(eregs->esp_uid); family_code = (version & 0xf8) >> 3; if(family_code == 0x02) { if ((version & 7) == 2) esp->erev = fas216; else esp->erev = fas236; } else if(family_code == 0x0a) esp->erev = fas366; /* Version is usually '5'. */ else if(family_code == 0x00) { if ((version & 7) == 2) esp->erev = fas100a; /* NCR53C9X */ else esp->erev = espunknown; } else if(family_code == 0x14) { if ((version & 7) == 2) esp->erev = fsc; else esp->erev = espunknown; } else if(family_code == 0x00) { if ((version & 7) == 2) esp->erev = fas100a; /* NCR53C9X */ else esp->erev = espunknown; } else esp->erev = espunknown; ESPLOG(("esp%d: FAST chip is %s (family=%d, version=%d)\n", esp->esp_id, erev2string[esp->erev - fas236], family_code, (version & 7))); esp->min_period = ((4 * esp->ccycle) / 1000); } else { esp->min_period = ((5 * esp->ccycle) / 1000); } /* Reload the configuration registers */ esp_write(eregs->esp_cfact, esp->cfact); esp->prev_stp = 0; esp_write(eregs->esp_stp, 0); esp->prev_soff = 0; esp_write(eregs->esp_soff, 0); esp_write(eregs->esp_timeo, esp->neg_defp); esp->max_period = (esp->max_period + 3)>>2; esp->min_period = (esp->min_period + 3)>>2; esp_write(eregs->esp_cfg1, esp->config1); switch(esp->erev) { case esp100: /* nothing to do */ break; case esp100a: esp_write(eregs->esp_cfg2, esp->config2); break; case esp236: /* Slow 236 */ esp_write(eregs->esp_cfg2, esp->config2); esp->prev_cfg3 = esp->config3[0]; esp_write(eregs->esp_cfg3, esp->prev_cfg3); break; case fas366: panic("esp: FAS366 support not present, please notify " "jongk@cs.utwente.nl");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -