📄 esp.c
字号:
/* $Id: esp.c,v 1.98 2000/11/02 22:34:16 davem Exp $ * esp.c: EnhancedScsiProcessor Sun SCSI driver code. * * Copyright (C) 1995, 1998 David S. Miller (davem@caip.rutgers.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. */#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 <linux/spinlock.h>#include "scsi.h"#include "hosts.h"#include "esp.h"#include <asm/sbus.h>#include <asm/dma.h>#include <asm/system.h>#include <asm/ptrace.h>#include <asm/pgtable.h>#include <asm/oplib.h>#include <asm/io.h>#include <asm/irq.h>#ifndef __sparc_v9__#include <asm/machines.h>#include <asm/idprom.h>#endif#include <linux/module.h>#define DEBUG_ESP/* #define DEBUG_ESP_HME *//* #define DEBUG_ESP_DATA *//* #define DEBUG_ESP_QUEUE *//* #define DEBUG_ESP_DISCONNECT *//* #define DEBUG_ESP_STATUS *//* #define DEBUG_ESP_PHASES *//* #define DEBUG_ESP_WORKBUS *//* #define DEBUG_STATE_MACHINE *//* #define DEBUG_ESP_CMDS *//* #define DEBUG_ESP_IRQS *//* #define DEBUG_SDTR *//* #define DEBUG_ESP_SG *//* Use the following to sprinkle debugging messages in a way which * suits you if combinations of the above become too verbose when * trying to track down a specific problem. *//* #define DEBUG_ESP_MISC */#if defined(DEBUG_ESP)#define ESPLOG(foo) printk foo#else#define ESPLOG(foo)#endif /* (DEBUG_ESP) */#if defined(DEBUG_ESP_HME)#define ESPHME(foo) printk foo#else#define ESPHME(foo)#endif#if defined(DEBUG_ESP_DATA)#define ESPDATA(foo) printk foo#else#define ESPDATA(foo)#endif#if defined(DEBUG_ESP_QUEUE)#define ESPQUEUE(foo) printk foo#else#define ESPQUEUE(foo)#endif#if defined(DEBUG_ESP_DISCONNECT)#define ESPDISC(foo) printk foo#else#define ESPDISC(foo)#endif#if defined(DEBUG_ESP_STATUS)#define ESPSTAT(foo) printk foo#else#define ESPSTAT(foo)#endif#if defined(DEBUG_ESP_PHASES)#define ESPPHASE(foo) printk foo#else#define ESPPHASE(foo)#endif#if defined(DEBUG_ESP_WORKBUS)#define ESPBUS(foo) printk foo#else#define ESPBUS(foo)#endif#if defined(DEBUG_ESP_IRQS)#define ESPIRQ(foo) printk foo#else#define ESPIRQ(foo)#endif#if defined(DEBUG_SDTR)#define ESPSDTR(foo) printk foo#else#define ESPSDTR(foo)#endif#if defined(DEBUG_ESP_MISC)#define ESPMISC(foo) printk foo#else#define ESPMISC(foo)#endif/* 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. */static struct esp *espchain;static spinlock_t espchain_lock = SPIN_LOCK_UNLOCKED;static int esps_running = 0;/* Forward declarations. */static void esp_intr(int irq, void *dev_id, struct pt_regs *pregs);/* Debugging routines */struct esp_cmdstrings { u8 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(u8 espcmd){ u8 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(u8 statreg){ u8 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(u8 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(u8 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 esp *esp, u8 cmd){ esp->espcmdlog[esp->espcmdent] = cmd; esp->espcmdent = (esp->espcmdent + 1) & 31; sbus_writeb(cmd, esp->eregs + ESP_CMD);}#else#define esp_cmd(__esp, __cmd) \ sbus_writeb((__cmd), ((__esp)->eregs) + ESP_CMD)#endif#define ESP_INTSOFF(__dregs) \ sbus_writel(sbus_readl((__dregs)+DMA_CSR)&~(DMA_INT_ENAB), (__dregs)+DMA_CSR)#define ESP_INTSON(__dregs) \ sbus_writel(sbus_readl((__dregs)+DMA_CSR)|DMA_INT_ENAB, (__dregs)+DMA_CSR)#define ESP_IRQ_P(__dregs) \ (sbus_readl((__dregs)+DMA_CSR) & (DMA_HNDL_INTR|DMA_HNDL_ERROR))/* How we use the various Linux SCSI data structures for operation. * * struct scsi_cmnd: * * We keep track of the synchronous 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/buses. */static void esp_reset_dma(struct esp *esp){ unsigned long flags; int can_do_burst16, can_do_burst32, can_do_burst64; int can_do_sbus64; u32 tmp; can_do_burst16 = (esp->bursts & DMA_BURST16) != 0; can_do_burst32 = (esp->bursts & DMA_BURST32) != 0; can_do_burst64 = 0; can_do_sbus64 = 0; if (sbus_can_dma_64bit(esp->sdev)) can_do_sbus64 = 1; if (sbus_can_burst64(esp->sdev)) can_do_burst64 = (esp->bursts & DMA_BURST64) != 0; /* Punt the DVMA into a known state. */ if (esp->dma->revision != dvmahme) { tmp = sbus_readl(esp->dregs + DMA_CSR);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -