wd7000.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,669 行 · 第 1/4 页

C
1,669
字号
/* * Driver data structures: *   - mb and scbs are required for interfacing with the host adapter. *     An SCB has extra fields not visible to the adapter; mb's *     _cannot_ do this, since the adapter assumes they are contiguous in *     memory, 4 bytes each, with ICMBs following OGMBs, and uses this fact *     to access them. *   - An icb is for host-only (non-SCSI) commands.  ICBs are 16 bytes each; *     the additional bytes are used only by the driver. *   - For now, a pool of SCBs are kept in global storage by this driver, *     and are allocated and freed as needed. * *  The 7000-FASST2 marks OGMBs empty as soon as it has _started_ a command, *  not when it has finished.  Since the SCB must be around for completion, *  problems arise when SCBs correspond to OGMBs, which may be reallocated *  earlier (or delayed unnecessarily until a command completes). *  Mailboxes are used as transient data structures, simply for *  carrying SCB addresses to/from the 7000-FASST2. * *  Note also since SCBs are not "permanently" associated with mailboxes, *  there is no need to keep a global list of scsi_cmnd pointers indexed *  by OGMB.   Again, SCBs reference their scsi_cmnds directly, so mailbox *  indices need not be involved. *//* *  WD7000-specific scatter/gather element structure */typedef struct sgb {	unchar len[3];	unchar ptr[3];		/* Also SCSI-style - MSB first */} Sgb;typedef struct scb {		/* Command Control Block 5.4.1               */	unchar op;		/* Command Control Block Operation Code      */	unchar idlun;		/* op=0,2:Target Id, op=1:Initiator Id       */	/* Outbound data transfer, length is checked */	/* Inbound data transfer, length is checked  */	/* Logical Unit Number                       */	unchar cdb[12];		/* SCSI Command Block                        */	volatile unchar status;	/* SCSI Return Status                        */	volatile unchar vue;	/* Vendor Unique Error Code                  */	unchar maxlen[3];	/* Maximum Data Transfer Length              */	unchar dataptr[3];	/* SCSI Data Block Pointer                   */	unchar linkptr[3];	/* Next Command Link Pointer                 */	unchar direc;		/* Transfer Direction                        */	unchar reserved2[6];	/* SCSI Command Descriptor Block             */	/* end of hardware SCB                       */	struct scsi_cmnd *SCpnt;/* scsi_cmnd using this SCB                  */	Sgb sgb[WD7000_SG];	/* Scatter/gather list for this SCB          */	Adapter *host;		/* host adapter                              */	struct scb *next;	/* for lists of scbs                         */} Scb;/* *  This driver is written to allow host-only commands to be executed. *  These use a 16-byte block called an ICB.  The format is extended by the *  driver to 18 bytes, to support the status returned in the ICMB and *  an execution phase code. * *  There are other formats besides these; these are the ones I've tried *  to use.  Formats for some of the defined ICB opcodes are not defined *  (notably, get/set unsolicited interrupt status) in my copy of the OEM *  manual, and others are ambiguous/hard to follow. */#define ICB_OP_MASK           0x80	/* distinguishes scbs from icbs        */#define ICB_OP_OPEN_RBUF      0x80	/* open receive buffer                 */#define ICB_OP_RECV_CMD       0x81	/* receive command from initiator      */#define ICB_OP_RECV_DATA      0x82	/* receive data from initiator         */#define ICB_OP_RECV_SDATA     0x83	/* receive data with status from init. */#define ICB_OP_SEND_DATA      0x84	/* send data with status to initiator  */#define ICB_OP_SEND_STAT      0x86	/* send command status to initiator    */					/* 0x87 is reserved                    */#define ICB_OP_READ_INIT      0x88	/* read initialization bytes           */#define ICB_OP_READ_ID        0x89	/* read adapter's SCSI ID              */#define ICB_OP_SET_UMASK      0x8A	/* set unsolicited interrupt mask      */#define ICB_OP_GET_UMASK      0x8B	/* read unsolicited interrupt mask     */#define ICB_OP_GET_REVISION   0x8C	/* read firmware revision level        */#define ICB_OP_DIAGNOSTICS    0x8D	/* execute diagnostics                 */#define ICB_OP_SET_EPARMS     0x8E	/* set execution parameters            */#define ICB_OP_GET_EPARMS     0x8F	/* read execution parameters           */typedef struct icbRecvCmd {	unchar op;	unchar IDlun;		/* Initiator SCSI ID/lun     */	unchar len[3];		/* command buffer length     */	unchar ptr[3];		/* command buffer address    */	unchar rsvd[7];		/* reserved                  */	volatile unchar vue;	/* vendor-unique error code  */	volatile unchar status;	/* returned (icmb) status    */	volatile unchar phase;	/* used by interrupt handler */} IcbRecvCmd;typedef struct icbSendStat {	unchar op;	unchar IDlun;		/* Target SCSI ID/lun                  */	unchar stat;		/* (outgoing) completion status byte 1 */	unchar rsvd[12];	/* reserved                            */	volatile unchar vue;	/* vendor-unique error code            */	volatile unchar status;	/* returned (icmb) status              */	volatile unchar phase;	/* used by interrupt handler           */} IcbSendStat;typedef struct icbRevLvl {	unchar op;	volatile unchar primary;	/* primary revision level (returned)   */	volatile unchar secondary;	/* secondary revision level (returned) */	unchar rsvd[12];	/* reserved                            */	volatile unchar vue;	/* vendor-unique error code            */	volatile unchar status;	/* returned (icmb) status              */	volatile unchar phase;	/* used by interrupt handler           */} IcbRevLvl;typedef struct icbUnsMask {	/* I'm totally guessing here */	unchar op;	volatile unchar mask[14];	/* mask bits                 */#if 0	unchar rsvd[12];	/* reserved                  */#endif	volatile unchar vue;	/* vendor-unique error code  */	volatile unchar status;	/* returned (icmb) status    */	volatile unchar phase;	/* used by interrupt handler */} IcbUnsMask;typedef struct icbDiag {	unchar op;	unchar type;		/* diagnostics type code (0-3) */	unchar len[3];		/* buffer length               */	unchar ptr[3];		/* buffer address              */	unchar rsvd[7];		/* reserved                    */	volatile unchar vue;	/* vendor-unique error code    */	volatile unchar status;	/* returned (icmb) status      */	volatile unchar phase;	/* used by interrupt handler   */} IcbDiag;#define ICB_DIAG_POWERUP   0	/* Power-up diags only       */#define ICB_DIAG_WALKING   1	/* walking 1's pattern       */#define ICB_DIAG_DMA       2	/* DMA - system memory diags */#define ICB_DIAG_FULL      3	/* do both 1 & 2             */typedef struct icbParms {	unchar op;	unchar rsvd1;		/* reserved                  */	unchar len[3];		/* parms buffer length       */	unchar ptr[3];		/* parms buffer address      */	unchar idx[2];		/* index (MSB-LSB)           */	unchar rsvd2[5];	/* reserved                  */	volatile unchar vue;	/* vendor-unique error code  */	volatile unchar status;	/* returned (icmb) status    */	volatile unchar phase;	/* used by interrupt handler */} IcbParms;typedef struct icbAny {	unchar op;	unchar data[14];	/* format-specific data      */	volatile unchar vue;	/* vendor-unique error code  */	volatile unchar status;	/* returned (icmb) status    */	volatile unchar phase;	/* used by interrupt handler */} IcbAny;typedef union icb {	unchar op;		/* ICB opcode                     */	IcbRecvCmd recv_cmd;	/* format for receive command     */	IcbSendStat send_stat;	/* format for send status         */	IcbRevLvl rev_lvl;	/* format for get revision level  */	IcbDiag diag;		/* format for execute diagnostics */	IcbParms eparms;	/* format for get/set exec parms  */	IcbAny icb;		/* generic format                 */	unchar data[18];} Icb;#ifdef MODULEstatic char *wd7000;MODULE_PARM(wd7000, "s");#endif/* *  Driver SCB structure pool. * *  The SCBs declared here are shared by all host adapters; hence, this *  structure is not part of the Adapter structure. */static Scb scbs[MAX_SCBS];static Scb *scbfree;		/* free list         */static int freescbs = MAX_SCBS;	/* free list counter */static spinlock_t scbpool_lock;	/* guards the scb free list and count *//* *  END of data/declarations - code follows. */static void __init setup_error(char *mesg, int *ints){	if (ints[0] == 3)		printk(KERN_ERR "wd7000_setup: \"wd7000=%d,%d,0x%x\" -> %s\n", ints[1], ints[2], ints[3], mesg);	else if (ints[0] == 4)		printk(KERN_ERR "wd7000_setup: \"wd7000=%d,%d,0x%x,%d\" -> %s\n", ints[1], ints[2], ints[3], ints[4], mesg);	else		printk(KERN_ERR "wd7000_setup: \"wd7000=%d,%d,0x%x,%d,%d\" -> %s\n", ints[1], ints[2], ints[3], ints[4], ints[5], mesg);}/* * Note: You can now set these options from the kernel's "command line". * The syntax is: * *     wd7000=<IRQ>,<DMA>,<IO>[,<BUS_ON>[,<BUS_OFF>]] * * , where BUS_ON and BUS_OFF are in nanoseconds. BIOS default values * are 8000ns for BUS_ON and 1875ns for BUS_OFF. * eg: *     wd7000=7,6,0x350 * * will configure the driver for a WD-7000 controller * using IRQ 15 with a DMA channel 6, at IO base address 0x350. */static int __init wd7000_setup(char *str){	static short wd7000_card_num;	/* .bss will zero this */	short i;	int ints[6];	(void) get_options(str, ARRAY_SIZE(ints), ints);	if (wd7000_card_num >= NUM_CONFIGS) {		printk(KERN_ERR "%s: Too many \"wd7000=\" configurations in " "command line!\n", __FUNCTION__);		return 0;	}	if ((ints[0] < 3) || (ints[0] > 5)) {		printk(KERN_ERR "%s: Error in command line!  " "Usage: wd7000=<IRQ>,<DMA>,IO>[,<BUS_ON>" "[,<BUS_OFF>]]\n", __FUNCTION__);	} else {		for (i = 0; i < NUM_IRQS; i++)			if (ints[1] == wd7000_irq[i])				break;		if (i == NUM_IRQS) {			setup_error("invalid IRQ.", ints);			return 0;		} else			configs[wd7000_card_num].irq = ints[1];		for (i = 0; i < NUM_DMAS; i++)			if (ints[2] == wd7000_dma[i])				break;		if (i == NUM_DMAS) {			setup_error("invalid DMA channel.", ints);			return 0;		} else			configs[wd7000_card_num].dma = ints[2];		for (i = 0; i < NUM_IOPORTS; i++)			if (ints[3] == wd7000_iobase[i])				break;		if (i == NUM_IOPORTS) {			setup_error("invalid I/O base address.", ints);			return 0;		} else			configs[wd7000_card_num].iobase = ints[3];		if (ints[0] > 3) {			if ((ints[4] < 500) || (ints[4] > 31875)) {				setup_error("BUS_ON value is out of range (500" " to 31875 nanoseconds)!", ints);				configs[wd7000_card_num].bus_on = BUS_ON;			} else				configs[wd7000_card_num].bus_on = ints[4] / 125;		} else			configs[wd7000_card_num].bus_on = BUS_ON;		if (ints[0] > 4) {			if ((ints[5] < 500) || (ints[5] > 31875)) {				setup_error("BUS_OFF value is out of range (500" " to 31875 nanoseconds)!", ints);				configs[wd7000_card_num].bus_off = BUS_OFF;			} else				configs[wd7000_card_num].bus_off = ints[5] / 125;		} else			configs[wd7000_card_num].bus_off = BUS_OFF;		if (wd7000_card_num) {			for (i = 0; i < (wd7000_card_num - 1); i++) {				int j = i + 1;				for (; j < wd7000_card_num; j++)					if (configs[i].irq == configs[j].irq) {						setup_error("duplicated IRQ!", ints);						return 0;					}				if (configs[i].dma == configs[j].dma) {					setup_error("duplicated DMA " "channel!", ints);					return 0;				}				if (configs[i].iobase == configs[j].iobase) {					setup_error("duplicated I/O " "base address!", ints);					return 0;				}			}		}		dprintk(KERN_DEBUG "wd7000_setup: IRQ=%d, DMA=%d, I/O=0x%x, "			"BUS_ON=%dns, BUS_OFF=%dns\n", configs[wd7000_card_num].irq, configs[wd7000_card_num].dma, configs[wd7000_card_num].iobase, configs[wd7000_card_num].bus_on * 125, configs[wd7000_card_num].bus_off * 125);		wd7000_card_num++;	}	return 1;}__setup("wd7000=", wd7000_setup);static inline void any2scsi(unchar * scsi, int any){	*scsi++ = (unsigned)any >> 16;	*scsi++ = (unsigned)any >> 8;	*scsi++ = any;}static inline int scsi2int(unchar * scsi){	return (scsi[0] << 16) | (scsi[1] << 8) | scsi[2];}static inline void wd7000_enable_intr(Adapter * host){	host->control |= INT_EN;	outb(host->control, host->iobase + ASC_CONTROL);}static inline void wd7000_enable_dma(Adapter * host){	unsigned long flags;	host->control |= DMA_EN;	outb(host->control, host->iobase + ASC_CONTROL);	flags = claim_dma_lock();	set_dma_mode(host->dma, DMA_MODE_CASCADE);	enable_dma(host->dma);	release_dma_lock(flags);}#define WAITnexttimeout 200	/* 2 seconds */static inline short WAIT(unsigned port, unsigned mask, unsigned allof, unsigned noneof){	unsigned WAITbits;	unsigned long WAITtimeout = jiffies + WAITnexttimeout;	while (time_before_eq(jiffies, WAITtimeout)) {		WAITbits = inb(port) & mask;		if (((WAITbits & allof) == allof) && ((WAITbits & noneof) == 0))			return (0);	}	return (1);}static inline int command_out(Adapter * host, unchar * cmd, int len){	if (!WAIT(host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) {		while (len--) {			do {				outb(*cmd, host->iobase + ASC_COMMAND);				WAIT(host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0);			} while (inb(host->iobase + ASC_STAT) & CMD_REJ);			cmd++;		}		return (1);	}	printk(KERN_WARNING "wd7000 command_out: WAIT failed(%d)\n", len + 1);	return (0);}/* *  This version of alloc_scbs is in preparation for supporting multiple *  commands per lun and command chaining, by queueing pending commands. *  We will need to allocate Scbs in blocks since they will wait to be *  executed so there is the possibility of deadlock otherwise. *  Also, to keep larger requests from being starved by smaller requests, *  we limit access to this routine with an internal busy flag, so that *  the satisfiability of a request is not dependent on the size of the *  request. */static inline Scb *alloc_scbs(struct Scsi_Host *host, int needed){	Scb *scb, *p = NULL;	unsigned long flags;	unsigned long timeout = jiffies + WAITnexttimeout;	unsigned long now;	int i;	if (needed <= 0)		return (NULL);	/* sanity check */	spin_unlock_irq(host->host_lock);      retry:	while (freescbs < needed) {		timeout = jiffies + WAITnexttimeout;		do {			/* FIXME: can we actually just yield here ?? */			for (now = jiffies; now == jiffies;)				cpu_relax();	/* wait a jiffy */		} while (freescbs < needed && time_before_eq(jiffies, timeout));		/*		 *  If we get here with enough free Scbs, we can take them.		 *  Otherwise, we timed out and didn't get enough.		 */		if (freescbs < needed) {			printk(KERN_ERR "wd7000: can't get enough free SCBs.\n");

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?