⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 esp.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* $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 + -