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

📄 wd7000.c

📁 GNU Mach 微内核源代码, 基于美国卡内基美隆大学的 Mach 研究项目
💻 C
📖 第 1 页 / 共 3 页
字号:
/* $Id: wd7000.c,v 1.1 1999/04/26 05:55:18 tb Exp $ *  linux/drivers/scsi/wd7000.c * *  Copyright (C) 1992  Thomas Wuensche *	closely related to the aha1542 driver from Tommy Thorn *	( as close as different hardware allows on a lowlevel-driver :-) ) * *  Revised (and renamed) by John Boyd <boyd@cis.ohio-state.edu> to *  accommodate Eric Youngdale's modifications to scsi.c.  Nov 1992. * *  Additional changes to support scatter/gather.  Dec. 1992.  tw/jb * *  No longer tries to reset SCSI bus at boot (it wasn't working anyway). *  Rewritten to support multiple host adapters. *  Miscellaneous cleanup. *  So far, still doesn't do reset or abort correctly, since I have no idea *  how to do them with this board (8^(.                      Jan 1994 jb * * This driver now supports both of the two standard configurations (per * the 3.36 Owner's Manual, my latest reference) by the same method as * before; namely, by looking for a BIOS signature.  Thus, the location of * the BIOS signature determines the board configuration.  Until I have * time to do something more flexible, users should stick to one of the * following: * * Standard configuration for single-adapter systems: *    - BIOS at CE00h *    - I/O base address 350h *    - IRQ level 15 *    - DMA channel 6 * Standard configuration for a second adapter in a system: *    - BIOS at C800h *    - I/O base address 330h *    - IRQ level 11 *    - DMA channel 5 * * Anyone who can recompile the kernel is welcome to add others as need * arises, but unpredictable results may occur if there are conflicts. * In any event, if there are multiple adapters in a system, they MUST * use different I/O bases, IRQ levels, and DMA channels, since they will be * indistinguishable (and in direct conflict) otherwise. * *   As a point of information, the NO_OP command toggles the CMD_RDY bit * of the status port, and this fact could be used as a test for the I/O * base address (or more generally, board detection).  There is an interrupt * status port, so IRQ probing could also be done.  I suppose the full * DMA diagnostic could be used to detect the DMA channel being used.  I * haven't done any of this, though, because I think there's too much of * a chance that such explorations could be destructive, if some other * board's resources are used inadvertently.  So, call me a wimp, but I * don't want to try it.  The only kind of exploration I trust is memory * exploration, since it's more certain that reading memory won't be * destructive. * * More to my liking would be a LILO boot command line specification, such * as is used by the aha152x driver (and possibly others).  I'll look into * it, as I have time... * *   I get mail occasionally from people who either are using or are * considering using a WD7000 with Linux.  There is a variety of * nomenclature describing WD7000's.  To the best of my knowledge, the * following is a brief summary (from an old WD doc - I don't work for * them or anything like that): * * WD7000-FASST2: This is a WD7000 board with the real-mode SST ROM BIOS *        installed.  Last I heard, the BIOS was actually done by Columbia *        Data Products.  The BIOS is only used by this driver (and thus *        by Linux) to identify the board; none of it can be executed under *        Linux. * * WD7000-ASC: This is the original adapter board, with or without BIOS. *        The board uses a WD33C93 or WD33C93A SBIC, which in turn is *        controlled by an onboard Z80 processor.  The board interface *        visible to the host CPU is defined effectively by the Z80's *        firmware, and it is this firmware's revision level that is *        determined and reported by this driver.  (The version of the *        on-board BIOS is of no interest whatsoever.)  The host CPU has *        no access to the SBIC; hence the fact that it is a WD33C93 is *        also of no interest to this driver. * * WD7000-AX: * WD7000-MX: * WD7000-EX: These are newer versions of the WD7000-ASC.  The -ASC is *        largely built from discrete components; these boards use more *        integration.  The -AX is an ISA bus board (like the -ASC), *        the -MX is an MCA (i.e., PS/2) bus board), and the -EX is an *        EISA bus board. * *  At the time of my documentation, the -?X boards were "future" products, *  and were not yet available.  However, I vaguely recall that Thomas *  Wuensche had an -AX, so I believe at least it is supported by this *  driver.  I have no personal knowledge of either -MX or -EX boards. * *  P.S. Just recently, I've discovered (directly from WD and Future *  Domain) that all but the WD7000-EX have been out of production for *  two years now.  FD has production rights to the 7000-EX, and are *  producing it under a new name, and with a new BIOS.  If anyone has *  one of the FD boards, it would be nice to come up with a signature *  for it. *                                                           J.B. Jan 1994. * * *  Revisions by Miroslav Zagorac <zaga@fly.cc.fer.hr> * * -- 08/24/1996. -------------------------------------------------------------- *    Enhancement for wd7000_detect function has been made, so you don't have *    to enter BIOS ROM address in initialisation data (see struct Config). *    We cannot detect IRQ, DMA and I/O base address for now, so we have to *    enter them as arguments while wd_7000 is detected.  If someone has IRQ, *    DMA or an I/O base address set to some other value, he can enter them in *    a configuration without any problem. *    Also I wrote a function wd7000_setup, so now you can enter WD-7000 *    definition as kernel arguments, as in lilo.conf: * *       append="wd7000=IRQ,DMA,IO" * *   PS: If card BIOS ROM is disabled, function wd7000_detect now will recognize *       adapter, unlike the old one.  Anyway, BIOS ROM from WD7000 adapter is *       useless for Linux. B^) * * -- 09/06/1996. -------------------------------------------------------------- *    Auto detecting of an I/O base address from wd7000_detect function is *    removed, some little bugs too... * *    Thanks to Roger Scott for driver debugging. * * -- 06/07/1997. -------------------------------------------------------------- *    Added support for /proc file system (/proc/scsi/wd7000/[0...] files). *    Now, the driver can handle hard disks with capacity >1GB. * * -- 01/15/1998. -------------------------------------------------------------- *    Added support for BUS_ON and BUS_OFF parameters in config line. *    Miscellaneous cleanups.  Syntax of the append line is changed to: * *       append="wd7000=IRQ,DMA,IO[,BUS_ON[,BUS_OFF]]" * *    , where BUS_ON and BUS_OFF are time in nanoseconds. * * -- 03/01/1998. -------------------------------------------------------------- *    The WD7000 driver now works on kernels' >= 2.1.x * * -- 06/11/1998. -------------------------------------------------------------- *    Ugly init_scbs, alloc_scbs and free_scb functions are changed with *    scbs_init, scb_alloc and scb_free.  Now, source code is identical on *    2.0.xx and 2.1.xx kernels. *    WD7000 specific definitions are moved from this file to wd7000.h. * */#ifdef MODULE#  include <linux/module.h>#endif#if (LINUX_VERSION_CODE >= 0x020100)#  include <asm/spinlock.h>#endif#include <stdarg.h>#include <linux/kernel.h>#include <linux/head.h>#include <linux/types.h>#include <linux/string.h>#include <linux/sched.h>#include <linux/malloc.h>#include <asm/system.h>#include <asm/dma.h>#include <asm/io.h>#include <linux/ioport.h>#include <linux/proc_fs.h>#include <linux/blk.h>#include <linux/version.h>#include <linux/stat.h>#include "scsi.h"#include "hosts.h"#include "sd.h"#include <scsi/scsicam.h>#undef WD7000_DEBUG	/* general debug         */#define WD7000_DEFINES	/* This must be defined! */#include "wd7000.h"struct proc_dir_entry proc_scsi_wd7000 ={    PROC_SCSI_7000FASST,    6,    "wd7000",    S_IFDIR | S_IRUGO | S_IXUGO,    2};/* * (linear) base address for ROM BIOS */static const long wd7000_biosaddr[] = {    0xc0000, 0xc2000, 0xc4000, 0xc6000, 0xc8000, 0xca000, 0xcc000, 0xce000,    0xd0000, 0xd2000, 0xd4000, 0xd6000, 0xd8000, 0xda000, 0xdc000, 0xde000};#define NUM_ADDRS	(sizeof (wd7000_biosaddr) / sizeof (long))static const ushort wd7000_iobase[] = {    0x0300, 0x0308, 0x0310, 0x0318, 0x0320, 0x0328, 0x0330, 0x0338,    0x0340, 0x0348, 0x0350, 0x0358, 0x0360, 0x0368, 0x0370, 0x0378,    0x0380, 0x0388, 0x0390, 0x0398, 0x03a0, 0x03a8, 0x03b0, 0x03b8,    0x03c0, 0x03c8, 0x03d0, 0x03d8, 0x03e0, 0x03e8, 0x03f0, 0x03f8};#define NUM_IOPORTS	(sizeof (wd7000_iobase) / sizeof (ushort))static const short wd7000_irq[] = { 3, 4, 5, 7, 9, 10, 11, 12, 14, 15 };#define NUM_IRQS	(sizeof (wd7000_irq) / sizeof (short))static const short wd7000_dma[] = { 5, 6, 7 };#define NUM_DMAS	(sizeof (wd7000_dma) / sizeof (short))/* * The following is set up by wd7000_detect, and used thereafter by * wd7000_intr_handle to map the irq level to the corresponding Adapter. * Note that if SA_INTERRUPT is not used, wd7000_intr_handle must be * changed to pick up the IRQ level correctly. */static struct Scsi_Host *wd7000_host[IRQS];/* * Add here your configuration... */static Config configs[] ={    { 15,  6, 0x350, BUS_ON, BUS_OFF },	/* defaults for single adapter */    { 11,  5, 0x320, BUS_ON, BUS_OFF },	/* defaults for second adapter */    {  7,  6, 0x350, BUS_ON, BUS_OFF },	/* My configuration (Zaga)     */    { -1, -1, 0x0,   BUS_ON, BUS_OFF }	/* Empty slot                  */};#define NUM_CONFIGS (sizeof(configs)/sizeof(Config))static const Signature signatures[] ={    {"SSTBIOS", 0x0000d, 7}	/* "SSTBIOS" @ offset 0x0000d */};#define NUM_SIGNATURES (sizeof(signatures)/sizeof(Signature))/* *  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];/* *  END of data/declarations - code follows. */static void setup_error (char *mesg, int *ints){    if (ints[0] == 3)        printk ("wd7000_setup: \"wd7000=%d,%d,0x%x\" -> %s\n",                ints[1], ints[2], ints[3], mesg);    else if (ints[0] == 4)        printk ("wd7000_setup: \"wd7000=%d,%d,0x%x,%d\" -> %s\n",                ints[1], ints[2], ints[3], ints[4], mesg);    else        printk ("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. */void wd7000_setup (char *str, int *ints){    static short wd7000_card_num = 0;    short i, j;    if (wd7000_card_num >= NUM_CONFIGS) {	printk ("%s: Too many \"wd7000=\" configurations in "		"command line!\n", __FUNCTION__);	return;    }    if ((ints[0] < 3) || (ints[0] > 5))	printk ("%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;	}	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;	}	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;	}	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++)		for (j = i + 1; j < wd7000_card_num; j++)		    if (configs[i].irq == configs[j].irq) {	                setup_error ("duplicated IRQ!", ints);			return;		    }		    else if (configs[i].dma == configs[j].dma) {	                setup_error ("duplicated DMA channel!", ints);			return;		    }		    else if (configs[i].iobase == configs[j].iobase) {	                setup_error ("duplicated I/O base address!", ints);			return;		    }	}#ifdef WD7000_DEBUG	printk ("%s: IRQ=%d, DMA=%d, I/O=0x%x, BUS_ON=%dns, BUS_OFF=%dns\n",	        __FUNCTION__,		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);#endif	wd7000_card_num++;    }}/* * Since they're used a lot, I've redone the following from the macros * formerly in wd7000.h, hopefully to speed them up by getting rid of * all the shifting (it may not matter; GCC might have done as well anyway). * * xany2scsi and xscsi2int were not being used, and are no longer defined. * (They were simply 4-byte versions of these routines). */static inline void any2scsi (unchar *scsi, int any){    *scsi++ = ((i_u) any).u[2];    *scsi++ = ((i_u) any).u[1];    *scsi   = ((i_u) any).u[0];}static inline int scsi2int (unchar *scsi){    i_u result;    result.i = 0;		/* clears unused bytes */    result.u[2] = *scsi++;    result.u[1] = *scsi++;    result.u[0] = *scsi;    return (result.i);}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){    host->control |= DMA_EN;    outb (host->control, host->iobase + ASC_CONTROL);    set_dma_mode (host->dma, DMA_MODE_CASCADE);    enable_dma (host->dma);}static inline short WAIT (uint port, uint mask, uint allof, uint noneof){    register uint WAITbits;    register ulong WAITtimeout = jiffies + WAITnexttimeout;    while (jiffies <= WAITtimeout) {	WAITbits = inb (port) & mask;	if (((WAITbits & allof) == allof) && ((WAITbits & noneof) == 0))	    return (0);    }    return (1);}static inline void delay (uint how_long){    register ulong time = jiffies + how_long;    while (jiffies < time);}static inline int wd7000_command_out (Adapter *host, unchar *cmd, int len){    if (! WAIT (host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) {	for ( ; len--; cmd++)	    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);	return (1);    }    printk ("%s: WAIT failed (%d)\n", __FUNCTION__, len + 1);    return (0);}static inline void scbs_init (void){    short i;    for (i = 0; i < MAX_SCBS; i++)	memset ((void *) &(scbs[i]), 0, sizeof (Scb));}static inline Scb *scb_alloc (void){    Scb *scb = NULL;

⌨️ 快捷键说明

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