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 + -
显示快捷键?