wd33c93.c

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

C
2,078
字号
/* * Copyright (c) 1996 John Shifflett, GeoLog Consulting *    john@geolog.com *    jshiffle@netcom.com * * 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, 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. *//* * Drew Eckhardt's excellent 'Generic NCR5380' sources from Linux-PC * provided much of the inspiration and some of the code for this * driver. Everything I know about Amiga DMA was gleaned from careful * reading of Hamish Mcdonald's original wd33c93 driver; in fact, I * borrowed shamelessly from all over that source. Thanks Hamish! * * _This_ driver is (I feel) an improvement over the old one in * several respects: * *    -  Target Disconnection/Reconnection  is now supported. Any *          system with more than one device active on the SCSI bus *          will benefit from this. The driver defaults to what I *          call 'adaptive disconnect' - meaning that each command *          is evaluated individually as to whether or not it should *          be run with the option to disconnect/reselect (if the *          device chooses), or as a "SCSI-bus-hog". * *    -  Synchronous data transfers are now supported. Because of *          a few devices that choke after telling the driver that *          they can do sync transfers, we don't automatically use *          this faster protocol - it can be enabled via the command- *          line on a device-by-device basis. * *    -  Runtime operating parameters can now be specified through *       the 'amiboot' or the 'insmod' command line. For amiboot do: *          "amiboot [usual stuff] wd33c93=blah,blah,blah" *       The defaults should be good for most people. See the comment *       for 'setup_strings' below for more details. * *    -  The old driver relied exclusively on what the Western Digital *          docs call "Combination Level 2 Commands", which are a great *          idea in that the CPU is relieved of a lot of interrupt *          overhead. However, by accepting a certain (user-settable) *          amount of additional interrupts, this driver achieves *          better control over the SCSI bus, and data transfers are *          almost as fast while being much easier to define, track, *          and debug. * * * TODO: *       more speed. linked commands. * * * People with bug reports, wish-lists, complaints, comments, * or improvements are asked to pah-leeez email me (John Shifflett) * at john@geolog.com or jshiffle@netcom.com! I'm anxious to get * this thing into as good a shape as possible, and I'm positive * there are lots of lurking bugs and "Stupid Places". * * Updates: * * Added support for pre -A chips, which don't have advanced features * and will generate CSR_RESEL rather than CSR_RESEL_AM. *	Richard Hirst <richard@sleepie.demon.co.uk>  August 2000 */#include <linux/config.h>#include <linux/module.h>#include <linux/sched.h>#include <linux/string.h>#include <linux/delay.h>#include <linux/version.h>#include <linux/init.h>#include <linux/blkdev.h>#include <asm/irq.h>#include <scsi/scsi.h>#include <scsi/scsi_cmnd.h>#include <scsi/scsi_device.h>#include <scsi/scsi_host.h>#include "wd33c93.h"#define WD33C93_VERSION    "1.26"#define WD33C93_DATE       "22/Feb/2003"MODULE_AUTHOR("John Shifflett");MODULE_DESCRIPTION("Generic WD33C93 SCSI driver");MODULE_LICENSE("GPL");/* * 'setup_strings' is a single string used to pass operating parameters and * settings from the kernel/module command-line to the driver. 'setup_args[]' * is an array of strings that define the compile-time default values for * these settings. If Linux boots with an amiboot or insmod command-line, * those settings are combined with 'setup_args[]'. Note that amiboot * command-lines are prefixed with "wd33c93=" while insmod uses a * "setup_strings=" prefix. The driver recognizes the following keywords * (lower case required) and arguments: * * -  nosync:bitmask -bitmask is a byte where the 1st 7 bits correspond with *                    the 7 possible SCSI devices. Set a bit to negotiate for *                    asynchronous transfers on that device. To maintain *                    backwards compatibility, a command-line such as *                    "wd33c93=255" will be automatically translated to *                    "wd33c93=nosync:0xff". * -  nodma:x        -x = 1 to disable DMA, x = 0 to enable it. Argument is *                    optional - if not present, same as "nodma:1". * -  period:ns      -ns is the minimum # of nanoseconds in a SCSI data transfer *                    period. Default is 500; acceptable values are 250 - 1000. * -  disconnect:x   -x = 0 to never allow disconnects, 2 to always allow them. *                    x = 1 does 'adaptive' disconnects, which is the default *                    and generally the best choice. * -  debug:x        -If 'DEBUGGING_ON' is defined, x is a bit mask that causes *                    various types of debug output to printed - see the DB_xxx *                    defines in wd33c93.h * -  clock:x        -x = clock input in MHz for WD33c93 chip. Normal values *                    would be from 8 through 20. Default is 8. * -  next           -No argument. Used to separate blocks of keywords when *                    there's more than one host adapter in the system. * * Syntax Notes: * -  Numeric arguments can be decimal or the '0x' form of hex notation. There *    _must_ be a colon between a keyword and its numeric argument, with no *    spaces. * -  Keywords are separated by commas, no spaces, in the standard kernel *    command-line manner. * -  A keyword in the 'nth' comma-separated command-line member will overwrite *    the 'nth' element of setup_args[]. A blank command-line member (in *    other words, a comma with no preceding keyword) will _not_ overwrite *    the corresponding setup_args[] element. * -  If a keyword is used more than once, the first one applies to the first *    SCSI host found, the second to the second card, etc, unless the 'next' *    keyword is used to change the order. * * Some amiboot examples (for insmod, use 'setup_strings' instead of 'wd33c93'): * -  wd33c93=nosync:255 * -  wd33c93=nodma * -  wd33c93=nodma:1 * -  wd33c93=disconnect:2,nosync:0x08,period:250 * -  wd33c93=debug:0x1c *//* Normally, no defaults are specified */static char *setup_args[] = { "", "", "", "", "", "", "", "", "" };static char *setup_strings;MODULE_PARM(setup_strings, "s");static void wd33c93_execute(struct Scsi_Host *instance);#ifdef CONFIG_WD33C93_PIOstatic inline ucharread_wd33c93(const wd33c93_regs regs, uchar reg_num){	uchar data;	outb(reg_num, regs.SASR);	data = inb(regs.SCMD);	return data;}static inline unsigned longread_wd33c93_count(const wd33c93_regs regs){	unsigned long value;	outb(WD_TRANSFER_COUNT_MSB, regs.SASR);	value = inb(regs.SCMD) << 16;	value |= inb(regs.SCMD) << 8;	value |= inb(regs.SCMD);	return value;}static inline ucharread_aux_stat(const wd33c93_regs regs){	return inb(regs.SASR);}static inline voidwrite_wd33c93(const wd33c93_regs regs, uchar reg_num, uchar value){      outb(reg_num, regs.SASR);      outb(value, regs.SCMD);}static inline voidwrite_wd33c93_count(const wd33c93_regs regs, unsigned long value){	outb(WD_TRANSFER_COUNT_MSB, regs.SASR);	outb((value >> 16) & 0xff, regs.SCMD);	outb((value >> 8) & 0xff, regs.SCMD);	outb( value & 0xff, regs.SCMD);}#define write_wd33c93_cmd(regs, cmd) \	write_wd33c93((regs), WD_COMMAND, (cmd))static inline voidwrite_wd33c93_cdb(const wd33c93_regs regs, uint len, uchar cmnd[]){	int i;	outb(WD_CDB_1, regs.SASR);	for (i=0; i<len; i++)		outb(cmnd[i], regs.SCMD);}#else /* CONFIG_WD33C93_PIO */static inline ucharread_wd33c93(const wd33c93_regs regs, uchar reg_num){	*regs.SASR = reg_num;	mb();	return (*regs.SCMD);}static unsigned longread_wd33c93_count(const wd33c93_regs regs){	unsigned long value;	*regs.SASR = WD_TRANSFER_COUNT_MSB;	mb();	value = *regs.SCMD << 16;	value |= *regs.SCMD << 8;	value |= *regs.SCMD;	mb();	return value;}static inline ucharread_aux_stat(const wd33c93_regs regs){	return *regs.SASR;}static inline voidwrite_wd33c93(const wd33c93_regs regs, uchar reg_num, uchar value){	*regs.SASR = reg_num;	mb();	*regs.SCMD = value;	mb();}static voidwrite_wd33c93_count(const wd33c93_regs regs, unsigned long value){	*regs.SASR = WD_TRANSFER_COUNT_MSB;	mb();	*regs.SCMD = value >> 16;	*regs.SCMD = value >> 8;	*regs.SCMD = value;	mb();}static inline voidwrite_wd33c93_cmd(const wd33c93_regs regs, uchar cmd){	*regs.SASR = WD_COMMAND;	mb();	*regs.SCMD = cmd;	mb();}static inline voidwrite_wd33c93_cdb(const wd33c93_regs regs, uint len, uchar cmnd[]){	int i;	*regs.SASR = WD_CDB_1;	for (i = 0; i < len; i++)		*regs.SCMD = cmnd[i];}#endif /* CONFIG_WD33C93_PIO */static inline ucharread_1_byte(const wd33c93_regs regs){	uchar asr;	uchar x = 0;	write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);	write_wd33c93_cmd(regs, WD_CMD_TRANS_INFO | 0x80);	do {		asr = read_aux_stat(regs);		if (asr & ASR_DBR)			x = read_wd33c93(regs, WD_DATA);	} while (!(asr & ASR_INT));	return x;}static struct sx_period sx_table[] = {	{1, 0x20},	{252, 0x20},	{376, 0x30},	{500, 0x40},	{624, 0x50},	{752, 0x60},	{876, 0x70},	{1000, 0x00},	{0, 0}};static intround_period(unsigned int period){	int x;	for (x = 1; sx_table[x].period_ns; x++) {		if ((period <= sx_table[x - 0].period_ns) &&		    (period > sx_table[x - 1].period_ns)) {			return x;		}	}	return 7;}static ucharcalc_sync_xfer(unsigned int period, unsigned int offset){	uchar result;	period *= 4;		/* convert SDTR code to ns */	result = sx_table[round_period(period)].reg_value;	result |= (offset < OPTIMUM_SX_OFF) ? offset : OPTIMUM_SX_OFF;	return result;}intwd33c93_queuecommand(struct scsi_cmnd *cmd,		void (*done)(struct scsi_cmnd *)){	struct WD33C93_hostdata *hostdata;	struct scsi_cmnd *tmp;	hostdata = (struct WD33C93_hostdata *) cmd->device->host->hostdata;	DB(DB_QUEUE_COMMAND,	   printk("Q-%d-%02x-%ld( ", cmd->device->id, cmd->cmnd[0], cmd->pid))/* Set up a few fields in the scsi_cmnd structure for our own use: *  - host_scribble is the pointer to the next cmd in the input queue *  - scsi_done points to the routine we call when a cmd is finished *  - result is what you'd expect */	cmd->host_scribble = NULL;	cmd->scsi_done = done;	cmd->result = 0;/* We use the Scsi_Pointer structure that's included with each command * as a scratchpad (as it's intended to be used!). The handy thing about * the SCp.xxx fields is that they're always associated with a given * cmd, and are preserved across disconnect-reselect. This means we * can pretty much ignore SAVE_POINTERS and RESTORE_POINTERS messages * if we keep all the critical pointers and counters in SCp: *  - SCp.ptr is the pointer into the RAM buffer *  - SCp.this_residual is the size of that buffer *  - SCp.buffer points to the current scatter-gather buffer *  - SCp.buffers_residual tells us how many S.G. buffers there are *  - SCp.have_data_in is not used *  - SCp.sent_command is not used *  - SCp.phase records this command's SRCID_ER bit setting */	if (cmd->use_sg) {		cmd->SCp.buffer = (struct scatterlist *) cmd->buffer;		cmd->SCp.buffers_residual = cmd->use_sg - 1;		cmd->SCp.ptr = page_address(cmd->SCp.buffer->page) +		    cmd->SCp.buffer->offset;		cmd->SCp.this_residual = cmd->SCp.buffer->length;	} else {		cmd->SCp.buffer = NULL;		cmd->SCp.buffers_residual = 0;		cmd->SCp.ptr = (char *) cmd->request_buffer;		cmd->SCp.this_residual = cmd->request_bufflen;	}/* WD docs state that at the conclusion of a "LEVEL2" command, the * status byte can be retrieved from the LUN register. Apparently, * this is the case only for *uninterrupted* LEVEL2 commands! If * there are any unexpected phases entered, even if they are 100% * legal (different devices may choose to do things differently), * the LEVEL2 command sequence is exited. This often occurs prior * to receiving the status byte, in which case the driver does a * status phase interrupt and gets the status byte on its own. * While such a command can then be "resumed" (ie restarted to * finish up as a LEVEL2 command), the LUN register will NOT be * a valid status byte at the command's conclusion, and we must * use the byte obtained during the earlier interrupt. Here, we * preset SCp.Status to an illegal value (0xff) so that when * this command finally completes, we can tell where the actual * status byte is stored. */	cmd->SCp.Status = ILLEGAL_STATUS_BYTE;	/*	 * Add the cmd to the end of 'input_Q'. Note that REQUEST SENSE	 * commands are added to the head of the queue so that the desired	 * sense data is not lost before REQUEST_SENSE executes.	 */	spin_lock_irq(&hostdata->lock);

⌨️ 快捷键说明

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