diff -u --recursive --new-file v1.3.58/linux/Documentation/networking/vortex.txt linux/Documentation/networking/vortex.txt --- v1.3.58/linux/Documentation/networking/vortex.txt Thu Jan 1 02:00:00 1970 +++ linux/Documentation/networking/vortex.txt Mon Jan 22 22:52:34 1996 @@ -0,0 +1,35 @@ +This document describes the usage and errata of the 3Com "Vortex" device +driver for Linux. + +This driver supports the following hardware: + 3c590, 3c592, 3c595, 3c597 + +When loaded as a module the following variables may be set: + name type description + debug int The debug message level, 0 (no messages) to 6 (wordy). + options int[] The media type override and card operation settings + (See list below.) + +An example of loading the vortex module is + insmod 3c59x.o debug=1 options=0,,12 +This sets the debug message level to minimal messages, sets the first card to +the 10baseT transceiver, the second to the EEPROM-set transceiver, and the +third card to operate in full-duplex mode using its 100baseTx transceiver. +(Note: card ordering is set by the PCI BIOS.) + +Possible media type settings + 0 10baseT + 1 10Mbs AUI + 2 undefined + 3 10base2 (BNC) + 4 100base-TX + 5 100base-FX + 6 MII (not yet available) + 7 + + 8 Full-duplex bit + 8 10baseT full-duplex + 12 100baseTx full-duplex + 16 Bus-master enable bit (experimental use only!) + +Details of the device driver implementation are at the top of the source file. diff -u --recursive --new-file v1.3.58/linux/Makefile linux/Makefile --- v1.3.58/linux/Makefile Tue Jan 23 21:15:36 1996 +++ linux/Makefile Mon Jan 22 22:17:03 1996 @@ -1,6 +1,6 @@ VERSION = 1 PATCHLEVEL = 3 -SUBLEVEL = 58 +SUBLEVEL = 59 ARCH = i386 diff -u --recursive --new-file v1.3.58/linux/arch/alpha/kernel/Makefile linux/arch/alpha/kernel/Makefile --- v1.3.58/linux/arch/alpha/kernel/Makefile Tue Jan 23 21:15:36 1996 +++ linux/arch/alpha/kernel/Makefile Tue Jan 23 20:49:51 1996 @@ -16,7 +16,7 @@ O_TARGET := kernel.o O_OBJS := entry.o traps.o process.o osf_sys.o irq.o signal.o setup.o \ - bios32.o ptrace.o time.o apecs.o lca.o ksyms.c + bios32.o ptrace.o time.o apecs.o lca.o ksyms.o all: kernel.o head.o diff -u --recursive --new-file v1.3.58/linux/arch/i386/defconfig linux/arch/i386/defconfig --- v1.3.58/linux/arch/i386/defconfig Tue Jan 23 21:15:36 1996 +++ linux/arch/i386/defconfig Tue Jan 23 21:57:16 1996 @@ -6,7 +6,7 @@ # Loadable module support # CONFIG_MODULES=y -CONFIG_MODVERSIONS=y +# CONFIG_MODVERSIONS is not set # CONFIG_KERNELD is not set # @@ -88,6 +88,7 @@ # CONFIG_EL1 is not set # CONFIG_EL2 is not set CONFIG_EL3=y +# CONFIG_VORTEX is not set # CONFIG_NET_ISA is not set # CONFIG_NET_EISA is not set # CONFIG_NET_POCKET is not set diff -u --recursive --new-file v1.3.58/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c --- v1.3.58/linux/drivers/block/ll_rw_blk.c Tue Dec 26 04:45:36 1995 +++ linux/drivers/block/ll_rw_blk.c Mon Jan 22 22:16:06 1996 @@ -436,16 +436,19 @@ add_request(major+blk_dev,req); } +/* + * Swap partitions are now read via brw_page. ll_rw_page is an + * asynchronous function now --- we must call wait_on_page afterwards + * if synchronous IO is required. + */ void ll_rw_page(int rw, kdev_t dev, unsigned long page, char * buffer) { - struct request * req; unsigned int major = MAJOR(dev); - unsigned long sector = page * (PAGE_SIZE / 512); - struct semaphore sem = MUTEX_LOCKED; - + int block = page; + if (major >= MAX_BLKDEV || !(blk_dev[major].request_fn)) { printk("Trying to read nonexistent block-device %s (%ld)\n", - kdevname(dev), sector); + kdevname(dev), page); return; } switch (rw) { @@ -461,19 +464,10 @@ default: panic("ll_rw_page: bad block dev cmd, must be R/W"); } - req = get_request_wait(NR_REQUEST, dev); -/* fill up the request-info, and add it to the queue */ - req->cmd = rw; - req->errors = 0; - req->sector = sector; - req->nr_sectors = PAGE_SIZE / 512; - req->current_nr_sectors = PAGE_SIZE / 512; - req->buffer = buffer; - req->sem = &sem; - req->bh = NULL; - req->next = NULL; - add_request(major+blk_dev,req); - down(&sem); + if (mem_map[MAP_NR(buffer)].locked) + panic ("ll_rw_page: page already locked"); + mem_map[MAP_NR(buffer)].locked = 1; + brw_page(rw, (unsigned long) buffer, dev, &block, PAGE_SIZE, 0); } /* This function can be used to request a number of buffers from a block diff -u --recursive --new-file v1.3.58/linux/drivers/char/apm_bios.c linux/drivers/char/apm_bios.c --- v1.3.58/linux/drivers/char/apm_bios.c Tue Jan 23 21:15:40 1996 +++ linux/drivers/char/apm_bios.c Mon Jan 22 22:55:23 1996 @@ -20,6 +20,10 @@ * January 1996, Rik Faith (faith@cs.unc.edu): * Make /proc/apm easy to format (bump driver version) * + * History: + * 0.6b: first version in official kernel, Linux 1.3.46 + * 0.7: changed /proc/apm format, Linux 1.3.58 + * 0.8: fixed gcc 2.7.[12] compilation problems, Linux 1.3.59 * * Reference: * @@ -110,30 +114,41 @@ */ /* - * define to have debug messages + * Define to have debug messages. */ #undef APM_DEBUG /* - * define to always call the APM BIOS busy routine even if the clock was - * not slowed by the idle routine + * Define to always call the APM BIOS busy routine even if the clock was + * not slowed by the idle routine. */ #define ALWAYS_CALL_BUSY /* - * define to disable interrupts in APM BIOS calls (the CPU Idle BIOS call - * should turn interrupts on before it does a 'hlt') + * Define to disable interrupts in APM BIOS calls (the CPU Idle BIOS call + * should turn interrupts on before it does a 'hlt'). */ #define APM_NOINTS /* - * define to make the APM BIOS calls zero all data segment registers (do - * that an incorrect BIOS implementation will cause a kernel panic if it - * tries to write to arbitrary memory) + * Define to make the APM BIOS calls zero all data segment registers (do + * that if an incorrect BIOS implementation will cause a kernel panic if it + * tries to write to arbitrary memory). */ #define APM_ZERO_SEGS /* + * Define to make all set_limit calls use 64k limits. The APM 1.1 BIOS is + * supposed to provide limit information that it recognizes. Many machines + * do this correctly, but many others do not restrict themselves to their + * claimed limit. When this happens, they will cause a segmentation + * violation in the kernel at boot time. Most BIOS's, however, will + * respect a 64k limit, so we use that. If you want to be pedantic and + * hold your BIOS to its claims, then undefine this. + */ +#define APM_RELAX_SEGMENTS + +/* * Need to poll the APM BIOS every second */ #define APM_CHECK_TIMEOUT (HZ) @@ -193,27 +208,27 @@ #define APM_SET_CPU_IDLE(error) \ APM_BIOS_CALL(al) \ : "=a" (error) \ - : "0" (0x5305) \ + : "a" (0x5305) \ APM_BIOS_CALL_END #endif #define APM_SET_CPU_BUSY(error) \ APM_BIOS_CALL(al) \ : "=a" (error) \ - : "0" (0x5306) \ + : "a" (0x5306) \ APM_BIOS_CALL_END #define APM_SET_POWER_STATE(state, error) \ APM_BIOS_CALL(al) \ : "=a" (error) \ - : "0" (0x5307), "b" (0x0001), "c" (state) \ + : "a" (0x5307), "b" (0x0001), "c" (state) \ APM_BIOS_CALL_END #ifdef CONFIG_APM_DISPLAY_BLANK #define APM_SET_DISPLAY_POWER_STATE(state, error) \ APM_BIOS_CALL(al) \ : "=a" (error) \ - : "0" (0x5307), "b" (0x01ff), "c" (state) \ + : "a" (0x5307), "b" (0x01ff), "c" (state) \ APM_BIOS_CALL_END #endif @@ -221,32 +236,32 @@ #define APM_ENABLE_POWER_MANAGEMENT(device, error) \ APM_BIOS_CALL(al) \ : "=a" (error) \ - : "0" (0x5308), "b" (device), "c" (1) \ + : "a" (0x5308), "b" (device), "c" (1) \ APM_BIOS_CALL_END #endif #define APM_GET_POWER_STATUS(bx, cx, dx, error) \ APM_BIOS_CALL(al) \ : "=a" (error), "=b" (bx), "=c" (cx), "=d" (dx) \ - : "0" (0x530a), "1" (1) \ + : "a" (0x530a), "b" (1) \ APM_BIOS_CALL_END #define APM_GET_EVENT(event, error) \ APM_BIOS_CALL(al) \ : "=a" (error), "=b" (event) \ - : "0" (0x530b) \ + : "a" (0x530b) \ APM_BIOS_CALL_END #define APM_DRIVER_VERSION(ver, ax, error) \ APM_BIOS_CALL(bl) \ : "=a" (ax), "=b" (error) \ - : "0" (0x530e), "1" (0), "c" (ver) \ + : "a" (0x530e), "b" (0), "c" (ver) \ APM_BIOS_CALL_END #define APM_ENGAGE_POWER_MANAGEMENT(device, error) \ APM_BIOS_CALL(al) \ : "=a" (error) \ - : "0" (0x530f), "b" (device), "c" (1) \ + : "a" (0x530f), "b" (device), "c" (1) \ APM_BIOS_CALL_END /* @@ -292,7 +307,7 @@ static struct timer_list apm_timer; -static char driver_version[] = "0.7";/* no spaces */ +static char driver_version[] = "0.8";/* no spaces */ #ifdef APM_DEBUG static char * apm_event_name[] = { @@ -1025,13 +1040,18 @@ set_limit(gdt[APM_CS_16 >> 3], 64 * 1024); set_limit(gdt[APM_DS >> 3], 64 * 1024); } else { - set_limit(gdt[APM_CS >> 3], apm_bios_info.cseg_len); - /* This is not clear from the spec, but at least one - machine needs CS_16 to be a 64k segment, and the DEC - Hinote Ultra CT475 (and others?) needs DS to be a 64k - segment. */ +#ifdef APM_RELAX_SEGMENTS + /* For ASUS motherboard, Award BIOS rev 110 (and others?) */ + set_limit(gdt[APM_CS >> 3], 64 * 1024); + /* For some unknown machine. */ set_limit(gdt[APM_CS_16 >> 3], 64 * 1024); + /* For the DEC Hinote Ultra CT475 (and others?) */ set_limit(gdt[APM_DS >> 3], 64 * 1024); +#else + set_limit(gdt[APM_CS >> 3], apm_bios_info.cseg_len); + set_limit(gdt[APM_CS_16 >> 3], 64 * 1024); + set_limit(gdt[APM_DS >> 3], apm_bios_info.dseg_len); +#endif apm_bios_info.version = 0x0101; error = apm_driver_version(&apm_bios_info.version); if (error != 0) diff -u --recursive --new-file v1.3.58/linux/drivers/char/busmouse.c linux/drivers/char/busmouse.c --- v1.3.58/linux/drivers/char/busmouse.c Sun Dec 17 11:43:17 1995 +++ linux/drivers/char/busmouse.c Tue Jan 23 21:41:34 1996 @@ -65,7 +65,6 @@ char dx, dy; unsigned char buttons; - MSE_INT_OFF(); outb(MSE_READ_X_LOW, MSE_CONTROL_PORT); dx = (inb(MSE_DATA_PORT) & 0xf); outb(MSE_READ_X_HIGH, MSE_CONTROL_PORT); @@ -171,6 +170,7 @@ int dx; int dy; unsigned char buttons; + /* long flags; */ if (count < 3) return -EINVAL; @@ -186,7 +186,8 @@ * so paging in put_user() does not effect mouse tracking. */ - MSE_INT_OFF(); + /* save_flags(flags); cli(); */ + disable_irq(mouse_irq); dx = mouse.dx; dy = mouse.dy; if (dx < -127) @@ -201,7 +202,8 @@ mouse.dx -= dx; mouse.dy -= dy; mouse.ready = 0; - MSE_INT_ON(); + enable_irq(mouse_irq); + /* restore_flags(flags); */ put_user(buttons | 0x80, buffer); put_user((char)dx, buffer + 1); diff -u --recursive --new-file v1.3.58/linux/drivers/char/selection.h linux/drivers/char/selection.h --- v1.3.58/linux/drivers/char/selection.h Wed Nov 8 07:11:31 1995 +++ linux/drivers/char/selection.h Wed Jan 24 00:21:21 1996 @@ -77,12 +77,9 @@ * of the VC's backing store, or the "shadow screen" memory where the screen * contents are kept, as the TGA frame buffer is *not* char/attr cells. * - * The "(unsigned long) addr < video_mem_term" tests for an Alpha kernel - * virtual address less than the end of the "shadow scrren" memory. This - * indicates we really want to write to the screen, so, we do... :-) - * - * NOTE: we must guarantee that video_mem_term is less than *any* VC's backing - * store; to do that, we must allocate it earlier than any VC's are done. + * We must test for an Alpha kernel virtual address that falls within + * the "shadow screen" memory. This condition indicates we really want + * to write to the screen, so, we do... :-) * * NOTE also: there's only *TWO* operations: to put/get a character/attribute. * All the others needed by VGA support go away, as Not Applicable for TGA. @@ -94,7 +91,8 @@ * if so, then render the char/attr onto the real screen. */ *addr = val; - if ((unsigned long)addr < video_mem_term) { + if ((unsigned long)addr < video_mem_term && + (unsigned long)addr >= video_mem_base) { tga_blitc(val, (unsigned long) addr); } } diff -u --recursive --new-file v1.3.58/linux/drivers/net/3c59x.c linux/drivers/net/3c59x.c --- v1.3.58/linux/drivers/net/3c59x.c Thu Jan 1 02:00:00 1970 +++ linux/drivers/net/3c59x.c Tue Jan 23 21:51:28 1996 @@ -0,0 +1,1033 @@ +/* 3c59x.c: A 3Com 3c590/3c595 "Vortex" ethernet driver for linux. */ +/* + Written 1995 by Donald Becker. + + This software may be used and distributed according to the terms + of the GNU Public License, incorporated herein by reference. + + This driver is for the 3Com "Vortex" series ethercards. Members of + the series include the 3c590 PCI EtherLink III and 3c595-Tx PCI Fast + EtherLink. It also works with the 10Mbs-only 3c590 PCI EtherLink III. + + The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O + Center of Excellence in Space Data and Information Sciences + Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 +*/ + +static char *version = "3c59x.c:v0.11 1/21/96 becker@cesdis.gsfc.nasa.gov\n"; + +/* "Knobs" that turn on special features. */ +/* Use bus master transfers instead of programmed-I/O for the Tx process. + This is disabled by default! */ +#define VORTEX_BUS_MASTER + +/* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */ +#define VORTEX_DEBUG 1 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* The total size is twice that of the original EtherLinkIII series: the + runtime register window, window 1, is now always mapped in. */ +#define VORTEX_TOTAL_SIZE 0x20 + +#ifdef HAVE_DEVLIST +struct netdev_entry tc59x_drv = +{"Vortex", vortex_pci_probe, VORTEX_TOTAL_SIZE, NULL}; +#endif + +#ifdef VORTEX_DEBUG +int vortex_debug = VORTEX_DEBUG; +#else +int vortex_debug = 1; +#endif + +static int product_ids[] = {0x5900, 0x5950, 0x5951, 0x5952, 0, 0}; +static const char *product_names[] = { + "3c590 Vortex 10Mbps", + "3c595 Vortex 100baseTX", + "3c595 Vortex 100baseT4", + "3c595 Vortex 100base-MII", + "EISA Vortex 3c597", +}; +#define DEMON_INDEX 5 /* Caution! Must be consistent with above! */ + +/* + Theory of Operation + +I. Board Compatibility + +This device driver is designed for the 3Com FastEtherLink, 3Com's PCI to +10/100baseT adapter. It also works with the 3c590, a similar product +with only a 10Mbs interface. + +II. Board-specific settings + +PCI bus devices are configured by the system at boot time, so no jumpers +need to be set on the board. The system BIOS should be set to assign the +PCI INTA signal to an otherwise unused system IRQ line. While it's +physically possible to shared PCI interrupt lines, the 1.2.0 kernel doesn't +support it. + +III. Driver operation + +The 3c59x series use an interface that's very similar to the previous 3c5x9 +series. The primary interface is two programmed-I/O FIFOs, with an +alternate single-contiguous-region bus-master transfer (see next). + +One extension that is advertised in a very large font is that the adapters +are capable of being bus masters. Unfortunately this capability is only for +a single contiguous region making it less useful than the list of transfer +regions available with the DEC Tulip or AMD PCnet. Given the significant +performance impact of taking an extra interrupt for each transfer, using +DMA transfers is a win only with large blocks. + +IIIC. Synchronization +The driver runs as two independent, single-threaded flows of control. One +is the send-packet routine, which enforces single-threaded use by the +dev->tbusy flag. The other thread is the interrupt handler, which is single +threaded by the hardware and other software. + +IV. Notes + +Thanks to Cameron Spitzer and Terry Murphy of 3Com for providing both +3c590 and 3c595 boards. +The name "Vortex" is the internal 3Com project name for the PCI ASIC, and +the not-yet-released (3/95) EISA version is called "Demon". According to +Terry these names come from rides at the local amusement park. + +The new chips support both ethernet (1.5K) and FDDI (4.5K) packet sizes! +This driver only supports ethernet packets because of the skbuff allocation +limit of 4K. +*/ + +#define TCOM_VENDOR_ID 0x10B7 /* 3Com's manufacturer's ID. */ + +/* Operational defintions. + These are not used by other compilation units and thus are not + exported in a ".h" file. + + First the windows. There are eight register windows, with the command + and status registers available in each. + */ +#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD) +#define EL3_CMD 0x0e +#define EL3_STATUS 0x0e + +/* The top five bits written to EL3_CMD are a command, the lower + 11 bits are the parameter, if applicable. + Note that 11 parameters bits was fine for ethernet, but the new chip + can handle FDDI lenght frames (~4500 octets) and now parameters count + 32-bit 'Dwords' rather than octets. */ + +enum vortex_cmd { + TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11, + RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11, + TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11, + FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11, + SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11, + SetTxThreshold = 18<<11, SetTxStart = 19<<11, + StartDMAUp = 20<<11, StartDMADown = (20<<11)+1, StatsEnable = 21<<11, + StatsDisable = 22<<11, StopCoax = 23<<11,}; + +/* The SetRxFilter command accepts the following classes: */ +enum RxFilter { + RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 }; + +/* Bits in the general status register. */ +enum vortex_status { + IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004, + TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020, + IntReq = 0x0040, StatsFull = 0x0080, DMADone = 1<<8, + DMAInProgress = 1<<11, /* DMA controller is still busy.*/ + CmdInProgress = 1<<12, /* EL3_CMD is still busy.*/ +}; + +/* Register window 1 offsets, the window used in normal operation. + On the Vortex this window is always mapped at offsets 0x10-0x1f. */ +enum Window1 { + TX_FIFO = 0x10, RX_FIFO = 0x10, RxErrors = 0x14, + RxStatus = 0x18, Timer=0x1A, TxStatus = 0x1B, + TxFree = 0x1C, /* Remaining free bytes in Tx buffer. */ +}; +enum Window0 { + Wn0EepromCmd = 10, /* Window 0: EEPROM command register. */ +}; +enum Win0_EEPROM_bits { + EEPROM_Read = 0x80, EEPROM_WRITE = 0x40, EEPROM_ERASE = 0xC0, + EEPROM_EWENB = 0x30, /* Enable erasing/writing for 10 msec. */ + EEPROM_EWDIS = 0x00, /* Disable EWENB before 10 msec timeout. */ +}; +/* EEPROM locations. */ +enum eeprom_offset { + PhysAddr01=0, PhysAddr23=1, PhysAddr45=2, ModelID=3, + EtherLink3ID=7, IFXcvrIO=8, IRQLine=9, + NodeAddr01=10, NodeAddr23=11, NodeAddr45=12, + DriverTune=13, Checksum=15}; + +enum Window3 { /* Window 3: MAC/config bits. */ + Wn3_Config=0, Wn3_MAC_Ctrl=6, Wn3_Options=8, +}; +union wn3_config { + int i; + struct w3_config_fields { + unsigned int ram_size:3, ram_width:1, ram_speed:2, rom_size:2; + int pad8:8; + unsigned int ram_split:2, pad18:2, xcvr:3, pad21:1, autoselect:1; + int pad24:8; + } u; +}; + +enum Window4 { + Wn4_Media = 0x0A, /* Window 4: Various transcvr/media bits. */ +}; +enum Win4_Media_bits { + Media_TP = 0x00C0, /* Enable link beat and jabber for 10baseT. */ +}; +enum Window7 { /* Window 7: Bus Master control. */ + Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12, +}; + +struct vortex_private { + char devname[8]; /* "ethN" string, also for kernel debug. */ + const char *product_name; + struct device *next_module; + struct enet_statistics stats; +#ifdef VORTEX_BUS_MASTER + struct sk_buff *tx_skb; /* Packet being eaten by bus master ctrl. */ +#endif + struct timer_list timer; /* Media selection timer. */ + int options; /* User-settable driver options (none yet). */ + unsigned int media_override:3, full_duplex:1, bus_master:1, autoselect:1; +}; + +static char *if_names[] = { + "10baseT", "10Mbs AUI", "undefined", "10base2", + "100baseTX", "100baseFX", "MII", "undefined"}; + +static int vortex_scan(struct device *dev); +static int vortex_found_device(struct device *dev, int ioaddr, int irq, + int product_index, int options); +static int vortex_probe1(struct device *dev); +static int vortex_open(struct device *dev); +static void vortex_timer(unsigned long arg); +static int vortex_start_xmit(struct sk_buff *skb, struct device *dev); +static int vortex_rx(struct device *dev); +static void vortex_interrupt(int irq, struct pt_regs *regs); +static int vortex_close(struct device *dev); +static void update_stats(int addr, struct device *dev); +static struct enet_statistics *vortex_get_stats(struct device *dev); +static void set_multicast_list(struct device *dev); + + +/* Unlike the other PCI cards the 59x cards don't need a large contiguous + memory region, so making the driver a loadable module is feasible. + + Unfortuneately maximizing the shared code between the integrated and + module version of the driver results in a complicated set of initialization + procedures. + init_module() -- modules / tc59x_init() -- built-in + The wrappers for vortex_scan() + vortex_scan() The common routine that scans for PCI and EISA cards + vortex_found_device() Allocate a device structure when we find a card. + Different versions exist for modules and built-in. + vortex_probe1() Fill in the device structure -- this is seperated + so that the modules code can put it in dev->init. +*/ +/* This driver uses 'options' to pass the media type, full-duplex flag, etc. */ +/* Note: this is the only limit on the number of cards supported!! */ +int options[8] = { -1, -1, -1, -1, -1, -1, -1, -1,}; + +#ifdef MODULE +static int debug = -1; +/* A list of all installed Vortex devices, for removing the driver module. */ +static struct device *root_vortex_dev = NULL; + +int +init_module(void) +{ + int cards_found; + + if (debug >= 0) + vortex_debug = debug; + if (vortex_debug) + printk(version); + + root_vortex_dev = NULL; + cards_found = vortex_scan(0); + return cards_found < 0 ? cards_found : 0; +} + +#else +unsigned long tc59x_probe(struct device *dev) +{ + int cards_found = 0; + + cards_found = vortex_scan(dev); + + if (vortex_debug > 0 && cards_found) + printk(version); + + return cards_found ? 0 : -ENODEV; +} +#endif /* not MODULE */ + +static int vortex_scan(struct device *dev) +{ + int cards_found = 0; + + if (pcibios_present()) { + int pci_index; + for (pci_index = 0; pci_index < 8; pci_index++) { + unsigned char pci_bus, pci_device_fn, pci_irq_line, pci_latency; + unsigned int pci_ioaddr; + unsigned short pci_command; + int index; + + for (index = 0; product_ids[index]; index++) { + if ( ! pcibios_find_device(TCOM_VENDOR_ID, product_ids[index], + pci_index, &pci_bus, + &pci_device_fn)) + break; + } + if ( ! product_ids[index]) + break; + + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq_line); + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &pci_ioaddr); + /* Remove I/O space marker in bit 0. */ + pci_ioaddr &= ~3; + +#ifdef VORTEX_BUS_MASTER + /* Get and check the bus-master and latency values. + Some PCI BIOSes fail to set the master-enable bit, and + the latency timer must be set to the maximum value to avoid + data corruption that occurs when the timer expires during + a transfer. Yes, it's a bug. */ + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, &pci_command); + if ( ! (pci_command & PCI_COMMAND_MASTER)) { + printk(" PCI Master Bit has not been set! Setting...\n"); + pci_command |= PCI_COMMAND_MASTER; + pcibios_write_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, pci_command); + } + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, &pci_latency); + if (pci_latency != 255) { + printk(" Overriding PCI latency timer (CFLT) setting of %d, new value is 255.\n", pci_latency); + pcibios_write_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, 255); + } +#endif /* VORTEX_BUS_MASTER */ + vortex_found_device(dev, pci_ioaddr, pci_irq_line, + index, options[cards_found]); + dev = 0; + cards_found++; + } + } + + /* Now check all slots of the EISA bus. */ + if (EISA_bus) { + static int ioaddr = 0x1000; + for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) { + /* Check the standard EISA ID register for an encoded '3Com'. */ + if (inw(ioaddr + 0xC80) != 0x6d50) + continue; + /* Check for a product that we support. */ + if ((inw(ioaddr + 0xC82) & 0xFFF0) != 0x5970 + && (inw(ioaddr + 0xC82) & 0xFFF0) != 0x5920) + continue; + vortex_found_device(dev, ioaddr, inw(ioaddr + 0xC88) >> 12, + DEMON_INDEX, options[cards_found]); + dev = 0; + cards_found++; + } + } + + return cards_found; +} + +static int vortex_found_device(struct device *dev, int ioaddr, int irq, + int product_index, int options) +{ + struct vortex_private *vp; + +#ifdef MODULE + /* Allocate and fill new device structure. */ + int dev_size = sizeof(struct device) + + sizeof(struct vortex_private); + + dev = (struct device *) kmalloc(dev_size, GFP_KERNEL); + memset(dev, 0, dev_size); + dev->priv = ((void *)dev) + sizeof(struct device); + vp = (struct vortex_private *)dev->priv; + dev->name = vp->devname; /* An empty string. */ + dev->base_addr = ioaddr; + dev->irq = irq; + dev->init = vortex_probe1; + vp->product_name = product_names[product_index]; + vp->options = options; + if (options >= 0) { + vp->media_override = options & 7; + vp->full_duplex = (options & 8) ? 1 : 0; + vp->bus_master = (options & 16) ? 1 : 0; + } else { + vp->media_override = 7; + vp->full_duplex = 0; + vp->bus_master = 0; + } + ether_setup(dev); + vp->next_module = root_vortex_dev; + root_vortex_dev = dev; + if (register_netdev(dev) != 0) + return -EIO; +#else /* not a MODULE */ + if (dev) { + dev->priv = kmalloc(sizeof (struct vortex_private), GFP_KERNEL); + memset(dev->priv, 0, sizeof (struct vortex_private)); + } + dev = init_etherdev(dev, sizeof(struct vortex_private)); + dev->base_addr = ioaddr; + dev->irq = irq; + vp = (struct vortex_private *)dev->priv; + vp->product_name = product_names[product_index]; + vp->options = options; + vortex_probe1(dev); +#endif /* MODULE */ + return 0; +} + +static int vortex_probe1(struct device *dev) +{ + int ioaddr = dev->base_addr; + struct vortex_private *vp = (struct vortex_private *)dev->priv; + int i; + + printk("%s: 3Com %s at %#3x,", dev->name, + vp->product_name, ioaddr); + + /* Read the station address from the EEPROM. */ + EL3WINDOW(0); + for (i = 0; i < 3; i++) { + short *phys_addr = (short *)dev->dev_addr; + int timer; + outw(EEPROM_Read + PhysAddr01 + i, ioaddr + Wn0EepromCmd); + /* Pause for at least 162 us. for the read to take place. */ + for (timer = 0; timer < 162*4 + 400; timer++) { + SLOW_DOWN_IO; + if ((inw(ioaddr + Wn0EepromCmd) & 0x8000) == 0) + break; + } + phys_addr[i] = htons(inw(ioaddr + 12)); + } + for (i = 0; i < 6; i++) + printk("%c%2.2x", i ? ':' : ' ', dev->dev_addr[i]); + printk(", IRQ %d\n", dev->irq); + /* Tell them about an invalid IRQ. */ + if (vortex_debug && (dev->irq <= 0 || dev->irq > 15)) + printk(" *** Warning: this IRQ is unlikely to work!\n"); + + { + char *ram_split[] = {"5:3", "3:1", "1:1", "invalid"}; + union wn3_config config; + EL3WINDOW(3); + config.i = inl(ioaddr + Wn3_Config); + if (vortex_debug > 1) + printk(" Internal config register is %4.4x, transceivers %#x.\n", + config.i, inw(ioaddr + Wn3_Options)); + printk(" %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n", + 8 << config.u.ram_size, + config.u.ram_width ? "word" : "byte", + ram_split[config.u.ram_split], + config.u.autoselect ? "autoselect/" : "", + if_names[config.u.xcvr]); + dev->if_port = config.u.xcvr; + vp->autoselect = config.u.autoselect; + } + + /* We do a request_region() only to register /proc/ioports info. */ + request_region(ioaddr, VORTEX_TOTAL_SIZE, vp->product_name); + + /* The 3c59x-specific entries in the device structure. */ + dev->open = &vortex_open; + dev->hard_start_xmit = &vortex_start_xmit; + dev->stop = &vortex_close; + dev->get_stats = &vortex_get_stats; + dev->set_multicast_list = &set_multicast_list; +#if defined (HAVE_SET_MAC_ADDR) && 0 + dev->set_mac_address = &set_mac_address; +#endif + + return 0; +} + + +static int +vortex_open(struct device *dev) +{ + int ioaddr = dev->base_addr; + struct vortex_private *vp = (struct vortex_private *)dev->priv; + union wn3_config config; + int i; + + /* Before initializing select the active media port. */ + EL3WINDOW(3); + if (vp->full_duplex) + outb(0x20, ioaddr + Wn3_MAC_Ctrl); /* Set the full-duplex bit. */ + config.i = inl(ioaddr + Wn3_Config); + + if (vp->media_override != 7) { + if (vortex_debug > 1) + printk("%s: Media override to transceiver %d (%s).\n", + dev->name, vp->media_override, if_names[vp->media_override]); + config.u.xcvr = vp->media_override; + dev->if_port = vp->media_override; + outl(config.i, ioaddr + Wn3_Config); + } + + if (vortex_debug > 1) { + printk("%s: vortex_open() InternalConfig %8.8x.\n", + dev->name, config.i); + } + + outw(TxReset, ioaddr + EL3_CMD); + for (i = 20; i >= 0 ; i--) + if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress) + break; + + outw(RxReset, ioaddr + EL3_CMD); + /* Wait a few ticks for the RxReset command to complete. */ + for (i = 20; i >= 0 ; i--) + if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress) + break; + + outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); + + if (irq2dev_map[dev->irq] != NULL + || (irq2dev_map[dev->irq] = dev) == NULL + || dev->irq == 0 + || request_irq(dev->irq, &vortex_interrupt, 0, vp->product_name)) { + return -EAGAIN; + } + + if (vortex_debug > 1) { + EL3WINDOW(4); + printk("%s: vortex_open() irq %d media status %4.4x.\n", + dev->name, dev->irq, inw(ioaddr + Wn4_Media)); + } + + /* Set the station address and mask in window 2 each time opened. */ + EL3WINDOW(2); + for (i = 0; i < 6; i++) + outb(dev->dev_addr[i], ioaddr + i); + for (; i < 12; i+=2) + outw(0, ioaddr + i); + + if (dev->if_port == 3) + /* Start the thinnet transceiver. We should really wait 50ms...*/ + outw(StartCoax, ioaddr + EL3_CMD); + else if (dev->if_port == 0) { + /* 10baseT interface, enabled link beat and jabber check. */ + EL3WINDOW(4); + outw(inw(ioaddr + Wn4_Media) | Media_TP, ioaddr + Wn4_Media); + } + + /* Switch to the stats window, and clear all stats by reading. */ + outw(StatsDisable, ioaddr + EL3_CMD); + EL3WINDOW(6); + for (i = 0; i < 10; i++) + inb(ioaddr + i); + inw(ioaddr + 10); + inw(ioaddr + 12); + /* New: On the Vortex we must also clear the BadSSD counter. */ + EL3WINDOW(3); + inb(ioaddr + 12); + + /* Switch to register set 7 for normal use. */ + EL3WINDOW(7); + + /* Accept b-case and phys addr only. */ + outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD); + outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + + outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ + outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ + /* Allow status bits to be seen. */ + outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD); + /* Ack all pending events, and set active indicator mask. */ + outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, + ioaddr + EL3_CMD); + outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull + | DMADone, ioaddr + EL3_CMD); + +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + + if (vp->autoselect) { + init_timer(&vp->timer); + vp->timer.expires = (14*HZ)/10; /* 1.4 sec. */ + vp->timer.data = (unsigned long)dev; + vp->timer.function = &vortex_timer; /* timer handler */ + add_timer(&vp->timer); + } + return 0; +} + +static void vortex_timer(unsigned long data) +{ + struct device *dev = (struct device *)data; + if (vortex_debug > 2) + printk("%s: Media selection timer tick happened.\n", dev->name); + /* ToDo: active media selection here! */ +} + +static int +vortex_start_xmit(struct sk_buff *skb, struct device *dev) +{ + struct vortex_private *vp = (struct vortex_private *)dev->priv; + int ioaddr = dev->base_addr; + + /* Transmitter timeout, serious problems. */ + if (dev->tbusy) { + int tickssofar = jiffies - dev->trans_start; + if (tickssofar < 40) + return 1; + printk("%s: transmit timed out, tx_status %2.2x status %4.4x.\n", + dev->name, inb(ioaddr + TxStatus), inw(ioaddr + EL3_STATUS)); + vp->stats.tx_errors++; + /* Issue TX_RESET and TX_START commands. */ + outw(TxReset, ioaddr + EL3_CMD); + { + int i; + for (i = 20; i >= 0 ; i--) + if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress) + break; + } + outw(TxEnable, ioaddr + EL3_CMD); + dev->trans_start = jiffies; + dev->tbusy = 0; + return 0; + } + + if (skb == NULL || skb->len <= 0) { + printk("%s: Obsolete driver layer request made: skbuff==NULL.\n", + dev->name); + dev_tint(dev); + return 0; + } + + /* Block a timer-based transmit from overlapping. This could better be + done with atomic_swap(1, dev->tbusy), but set_bit() works as well. + If this ever occurs the queue layer is doing something evil! */ + if (set_bit(0, (void*)&dev->tbusy) != 0) { + printk("%s: Transmitter access conflict.\n", dev->name); + return 1; + } + + /* Put out the doubleword header... */ + outl(skb->len, ioaddr + TX_FIFO); +#ifdef VORTEX_BUS_MASTER + if (vp->bus_master) { + /* Set the bus-master controller to transfer the packet. */ + outl((int)(skb->data), ioaddr + Wn7_MasterAddr); + outw((skb->len + 3) & ~3, ioaddr + Wn7_MasterLen); + vp->tx_skb = skb; + outw(StartDMADown, ioaddr + EL3_CMD); + } else { + /* ... and the packet rounded to a doubleword. */ + outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); + dev_kfree_skb (skb, FREE_WRITE); + if (inw(ioaddr + TxFree) > 1536) { + dev->tbusy = 0; + } else + /* Interrupt us when the FIFO has room for max-sized packet. */ + outw(SetTxThreshold + 1536, ioaddr + EL3_CMD); + } +#else + /* ... and the packet rounded to a doubleword. */ + outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); + dev_kfree_skb (skb, FREE_WRITE); + if (inw(ioaddr + TxFree) > 1536) { + dev->tbusy = 0; + } else + /* Interrupt us when the FIFO has room for max-sized packet. */ + outw(SetTxThreshold + 1536, ioaddr + EL3_CMD); +#endif /* bus master */ + + dev->trans_start = jiffies; + + /* Clear the Tx status stack. */ + { + short tx_status; + int i = 4; + + while (--i > 0 && (tx_status = inb(ioaddr + TxStatus)) > 0) { + if (tx_status & 0x3C) { /* A Tx-disabling error occured. */ + if (vortex_debug > 2) + printk("%s: Tx error, status %2.2x.\n", + dev->name, tx_status); + if (tx_status & 0x04) vp->stats.tx_fifo_errors++; + if (tx_status & 0x38) vp->stats.tx_aborted_errors++; + if (tx_status & 0x30) { + int j; + outw(TxReset, ioaddr + EL3_CMD); + for (j = 20; j >= 0 ; j--) + if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress) + break; + } + outw(TxEnable, ioaddr + EL3_CMD); + } + outb(0x00, ioaddr + TxStatus); /* Pop the status stack. */ + } + } + return 0; +} + +/* The interrupt handler does all of the Rx thread work and cleans up + after the Tx thread. */ +static void vortex_interrupt(int irq, struct pt_regs *regs) +{ + struct device *dev = (struct device *)(irq2dev_map[irq]); + struct vortex_private *lp; + int ioaddr, status; + int latency; + int i = 0; + + if (dev == NULL) { + printk ("vortex_interrupt(): irq %d for unknown device.\n", irq); + return; + } + + if (dev->interrupt) + printk("%s: Re-entering the interrupt handler.\n", dev->name); + dev->interrupt = 1; + + ioaddr = dev->base_addr; + latency = inb(ioaddr + Timer); + lp = (struct vortex_private *)dev->priv; + + status = inw(ioaddr + EL3_STATUS); + + if (vortex_debug > 4) + printk("%s: interrupt, status %4.4x, timer %d.\n", dev->name, + status, latency); + if ((status & 0xE000) != 0xE000) { + static int donedidthis=0; + /* Some interrupt controllers store a bogus interrupt from boot-time. + Ignore a single early interrupt, but don't hang the machine for + other interrupt problems. */ + if (donedidthis++ > 1) { + printk("%s: Bogus interrupt, bailing. Status %4.4x, start=%d.\n", + dev->name, status, dev->start); + free_irq(dev->irq); + } + } + + do { + if (vortex_debug > 5) + printk("%s: In interrupt loop, status %4.4x.\n", + dev->name, status); + if (status & RxComplete) + vortex_rx(dev); + + if (status & TxAvailable) { + if (vortex_debug > 5) + printk(" TX room bit was handled.\n"); + /* There's room in the FIFO for a full-sized packet. */ + outw(AckIntr | TxAvailable, ioaddr + EL3_CMD); + dev->tbusy = 0; + mark_bh(NET_BH); + } +#ifdef VORTEX_BUS_MASTER + if (status & DMADone) { + outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */ + dev->tbusy = 0; + mark_bh(NET_BH); + } +#endif + if (status & (AdapterFailure | RxEarly | StatsFull)) { + /* Handle all uncommon interrupts at once. */ + if (status & RxEarly) { /* Rx early is unused. */ + vortex_rx(dev); + outw(AckIntr | RxEarly, ioaddr + EL3_CMD); + } + if (status & StatsFull) { /* Empty statistics. */ + static int DoneDidThat = 0; + if (vortex_debug > 4) + printk("%s: Updating stats.\n", dev->name); + update_stats(ioaddr, dev); + /* DEBUG HACK: Disable statistics as an interrupt source. */ + /* This occurs when we have the wrong media type! */ + if (DoneDidThat == 0 && + inw(ioaddr + EL3_STATUS) & StatsFull) { + int win, reg; + printk("%s: Updating stats failed, disabling stats as an" + " interrupt source.\n", dev->name); + for (win = 0; win < 8; win++) { + EL3WINDOW(win); + printk("\n Vortex window %d:", win); + for (reg = 0; reg < 16; reg++) + printk(" %2.2x", inb(ioaddr+reg)); + } + EL3WINDOW(7); + outw(SetIntrEnb | 0x18, ioaddr + EL3_CMD); + DoneDidThat++; + } + } + if (status & AdapterFailure) { + /* Adapter failure requires Rx reset and reinit. */ + outw(RxReset, ioaddr + EL3_CMD); + /* Set the Rx filter to the current state. */ + outw(SetRxFilter | RxStation | RxBroadcast + | (dev->flags & IFF_ALLMULTI ? RxMulticast : 0) + | (dev->flags & IFF_PROMISC ? RxProm : 0), + ioaddr + EL3_CMD); + outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */ + outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD); + } + } + + if (++i > 10) { + printk("%s: Infinite loop in interrupt, status %4.4x. " + "Disabling functions (%4.4x).\n", + dev->name, status, SetStatusEnb | ((~status) & 0xFE)); + /* Disable all pending interrupts. */ + outw(SetStatusEnb | ((~status) & 0xFE), ioaddr + EL3_CMD); + outw(AckIntr | 0xFF, ioaddr + EL3_CMD); + break; + } + /* Acknowledge the IRQ. */ + outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); + + } while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete)); + + if (vortex_debug > 4) + printk("%s: exiting interrupt, status %4.4x.\n", dev->name, status); + + dev->interrupt = 0; + return; +} + +static int +vortex_rx(struct device *dev) +{ + struct vortex_private *vp = (struct vortex_private *)dev->priv; + int ioaddr = dev->base_addr; + int i; + short rx_status; + + if (vortex_debug > 5) + printk(" In rx_packet(), status %4.4x, rx_status %4.4x.\n", + inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus)); + while ((rx_status = inw(ioaddr + RxStatus)) > 0) { + if (rx_status & 0x4000) { /* Error, update stats. */ + unsigned char rx_error = inb(ioaddr + RxErrors); + if (vortex_debug > 4) + printk(" Rx error: status %2.2x.\n", rx_error); + vp->stats.rx_errors++; + if (rx_error & 0x01) vp->stats.rx_over_errors++; + if (rx_error & 0x02) vp->stats.rx_length_errors++; + if (rx_error & 0x04) vp->stats.rx_frame_errors++; + if (rx_error & 0x08) vp->stats.rx_crc_errors++; + if (rx_error & 0x10) vp->stats.rx_length_errors++; + } else { + /* The packet length: up to 4.5K!. */ + short pkt_len = rx_status & 0x1fff; + struct sk_buff *skb; + + skb = dev_alloc_skb(pkt_len + 5); + if (vortex_debug > 4) + printk("Receiving packet size %d status %4.4x.\n", + pkt_len, rx_status); + if (skb != NULL) { + skb->dev = dev; + skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ + /* 'skb_put()' points to the start of sk_buff data area. */ + insl(ioaddr + RX_FIFO, skb_put(skb, pkt_len), + (pkt_len + 3) >> 2); + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ + /* Wait a limited time to go to next packet. */ + for (i = 200; i >= 0; i--) + if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress) + break; + vp->stats.rx_packets++; + continue; + } else if (vortex_debug) + printk("%s: Couldn't allocate a sk_buff of size %d.\n", + dev->name, pkt_len); + } + vp->stats.rx_dropped++; + outw(RxDiscard, ioaddr + EL3_CMD); + /* Wait a limited time to skip this packet. */ + for (i = 200; i >= 0; i--) + if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress) + break; + } + + return 0; +} + +static int +vortex_close(struct device *dev) +{ + int ioaddr = dev->base_addr; + + dev->start = 0; + dev->tbusy = 1; + + if (vortex_debug > 1) + printk("%s: vortex_close() status %4.4x, Tx status %2.2x.\n", + dev->name, inw(ioaddr + EL3_STATUS), inb(ioaddr + TxStatus)); + + /* Turn off statistics ASAP. We update lp->stats below. */ + outw(StatsDisable, ioaddr + EL3_CMD); + + /* Disable the receiver and transmitter. */ + outw(RxDisable, ioaddr + EL3_CMD); + outw(TxDisable, ioaddr + EL3_CMD); + + if (dev->if_port == 3) + /* Turn off thinnet power. Green! */ + outw(StopCoax, ioaddr + EL3_CMD); + else if (dev->if_port == 0) { + /* Disable link beat and jabber, if_port may change ere next open(). */ + EL3WINDOW(4); + outw(inw(ioaddr + Wn4_Media) & ~Media_TP, ioaddr + Wn4_Media); + } + + free_irq(dev->irq); + /* Mmmm, we should diable all interrupt sources here. */ + irq2dev_map[dev->irq] = 0; + + update_stats(ioaddr, dev); +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + + return 0; +} + +static struct enet_statistics * +vortex_get_stats(struct device *dev) +{ + struct vortex_private *vp = (struct vortex_private *)dev->priv; + unsigned long flags; + + save_flags(flags); + cli(); + update_stats(dev->base_addr, dev); + restore_flags(flags); + return &vp->stats; +} + +/* Update statistics. + Unlike with the EL3 we need not worry about interrupts changing + the window setting from underneath us, but we must still guard + against a race condition with a StatsUpdate interrupt updating the + table. This is done by checking that the ASM (!) code generated uses + atomic updates with '+='. + */ +static void update_stats(int ioaddr, struct device *dev) +{ + struct vortex_private *vp = (struct vortex_private *)dev->priv; + + /* Unlike the 3c5x9 we need not turn off stats updates while reading. */ + /* Switch to the stats window, and read everything. */ + EL3WINDOW(6); + vp->stats.tx_carrier_errors += inb(ioaddr + 0); + vp->stats.tx_heartbeat_errors += inb(ioaddr + 1); + /* Multiple collisions. */ inb(ioaddr + 2); + vp->stats.collisions += inb(ioaddr + 3); + vp->stats.tx_window_errors += inb(ioaddr + 4); + vp->stats.rx_fifo_errors += inb(ioaddr + 5); + vp->stats.tx_packets += inb(ioaddr + 6); + vp->stats.tx_packets += (inb(ioaddr + 9)&15) << 8; + /* Rx packets */ inb(ioaddr + 7); /* Must read to clear */ + /* Tx deferrals */ inb(ioaddr + 8); + /* Don't bother with register 9, an extention of registers 6&7. + If we do use the 6&7 values the atomic update assumption above + is invalid. */ + inw(ioaddr + 10); /* Total Rx and Tx octets. */ + inw(ioaddr + 12); + /* New: On the Vortex we must also clear the BadSSD counter. */ + EL3WINDOW(3); + inb(ioaddr + 12); + + /* We change back to window 7 (not 1) with the Vortex. */ + EL3WINDOW(7); + return; +} + +/* There are two version of set_multicast_list() to support both v1.2 and + v1.4 kernels. */ +static void +set_multicast_list(struct device *dev) +{ + short ioaddr = dev->base_addr; + + if ((dev->mc_list) || (dev->flags & IFF_ALLMULTI)) { + outw(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD); + if (vortex_debug > 3) { + printk("%s: Setting Rx multicast mode, %d addresses.\n", + dev->name, dev->mc_count); + } + } else if (dev->flags & IFF_PROMISC) { + outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm, + ioaddr + EL3_CMD); + } else + outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD); +} + + +#ifdef MODULE +void +cleanup_module(void) +{ + struct device *next_dev; + + /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ + while (root_vortex_dev) { + next_dev = ((struct vortex_private *)root_vortex_dev->priv)->next_module; + unregister_netdev(root_vortex_dev); + release_region(root_vortex_dev->base_addr, VORTEX_TOTAL_SIZE); + kfree(root_vortex_dev); + root_vortex_dev = next_dev; + } +} +#endif /* MODULE */ + +/* + * Local variables: + * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 3c59x.c -o 3c59x.o" + * c-indent-level: 4 + * tab-width: 4 + * End: + */ diff -u --recursive --new-file v1.3.58/linux/drivers/net/Config.in linux/drivers/net/Config.in --- v1.3.58/linux/drivers/net/Config.in Tue Jan 23 21:15:42 1996 +++ linux/drivers/net/Config.in Mon Jan 22 22:52:34 1996 @@ -29,6 +29,7 @@ tristate '3c507 support' CONFIG_EL16 fi tristate '3c509/3c579 support' CONFIG_EL3 + tristate '3c590 series (592/595/597) "Vortex" support' CONFIG_VORTEX fi bool 'Other ISA cards' CONFIG_NET_ISA if [ "$CONFIG_NET_ISA" = "y" ]; then diff -u --recursive --new-file v1.3.58/linux/drivers/net/Makefile linux/drivers/net/Makefile --- v1.3.58/linux/drivers/net/Makefile Tue Jan 23 21:15:42 1996 +++ linux/drivers/net/Makefile Mon Jan 22 22:52:34 1996 @@ -226,6 +226,14 @@ endif endif +ifeq ($(CONFIG_VORTEX),y) +L_OBJS += 3c59x.o +else + ifeq ($(CONFIG_VORTEX),m) + M_OBJS += 3c59x.o + endif +endif + ifeq ($(CONFIG_EEXPRESS),y) L_OBJS += eexpress.o else diff -u --recursive --new-file v1.3.58/linux/drivers/net/Space.c linux/drivers/net/Space.c --- v1.3.58/linux/drivers/net/Space.c Sat Nov 25 19:04:40 1995 +++ linux/drivers/net/Space.c Mon Jan 22 22:52:34 1996 @@ -68,6 +68,7 @@ extern int ni65_probe(struct device *); extern int SK_init(struct device *); extern int seeq8005_probe(struct device *); +extern int tc59x_probe(struct device *); /* Detachable devices ("pocket adaptors") */ extern int atp_init(struct device *); @@ -83,6 +84,9 @@ return 1; /* ENXIO */ if (1 +#if defined(CONFIG_VORTEX) + && tc59x_probe(dev) +#endif #if defined(CONFIG_SEEQ8005) && seeq8005_probe(dev) #endif diff -u --recursive --new-file v1.3.58/linux/drivers/net/ibmtr.c linux/drivers/net/ibmtr.c --- v1.3.58/linux/drivers/net/ibmtr.c Thu Nov 9 11:23:49 1995 +++ linux/drivers/net/ibmtr.c Mon Jan 22 22:16:17 1996 @@ -325,7 +325,7 @@ if (intr==2) irq=10; if (intr==3) irq=11; while(!(ti->mmio + ACA_OFFSET + ACA_RW + RRR_EVEN)); - ti->sram=(unsigned char *)((unsigned long)(ti->mmio + ACA_OFFSET + ACA_RW + RRR_EVEN) <<12); + ti->sram=(unsigned char *)((unsigned long)(*(ti->mmio + ACA_OFFSET + ACA_RW + RRR_EVEN)) <<12); ti->global_int_enable=PIOaddr+ADAPTINTREL; break; diff -u --recursive --new-file v1.3.58/linux/drivers/scsi/AM53C974.c linux/drivers/scsi/AM53C974.c --- v1.3.58/linux/drivers/scsi/AM53C974.c Thu Jan 1 02:00:00 1970 +++ linux/drivers/scsi/AM53C974.c Tue Jan 23 21:52:43 1996 @@ -0,0 +1,2243 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "scsi.h" +#include "hosts.h" +#include "AM53C974.h" +#include "constants.h" +#include "sd.h" + +/* AM53/79C974 (PCscsi) driver release 0.5 + * + * The architecture and much of the code of this device + * driver was originally developed by Drew Eckhardt for + * the NCR5380. The following copyrights apply: + * For the architecture and all pieces of code which can also be found + * in the NCR5380 device driver: + * Copyright 1993, Drew Eckhardt + * Visionary Computing + * (Unix and Linux consulting and custom programming) + * drew@colorado.edu + * +1 (303) 666-5836 + * + * The AM53C974_nobios_detect code was origininally developed by + * Robin Cutshaw (robin@xfree86.org) and is used here in a + * slightly modified form. + * + * For the remaining code: + * Copyright 1994, D. Frieauff + * EMail: fri@rsx42sun0.dofn.de + * Phone: x49-7545-8-2256 , x49-7541-42305 + */ + +/* + * $Log: AM53C974.c,v $ + */ + +#ifdef AM53C974_DEBUG + #define DEB(x) x + #ifdef AM53C974_DEBUG_KEYWAIT + #define KEYWAIT() AM53C974_keywait() + #else + #define KEYWAIT() + #endif + #ifdef AM53C974_DEBUG_INIT + #define DEB_INIT(x) x + #else + #define DEB_INIT(x) + #endif + #ifdef AM53C974_DEBUG_MSG + #define DEB_MSG(x) x + #else + #define DEB_MSG(x) + #endif + #ifdef AM53C974_DEB_RESEL + #define DEB_RESEL(x) x + #else + #define DEB_RESEL(x) + #endif + #ifdef AM53C974_DEBUG_QUEUE + #define DEB_QUEUE(x) x + #define LIST(x,y) {printk("LINE:%d Adding %p to %p\n", __LINE__, (void*)(x), (void*)(y)); if ((x)==(y)) udelay(5); } + #define REMOVE(w,x,y,z) {printk("LINE:%d Removing: %p->%p %p->%p \n", __LINE__, (void*)(w), (void*)(x), (void*)(y), (void*)(z)); if ((x)==(y)) udelay(5); } + #else + #define DEB_QUEUE(x) + #define LIST(x,y) + #define REMOVE(w,x,y,z) + #endif + #ifdef AM53C974_DEBUG_INFO + #define DEB_INFO(x) x + #else + #define DEB_INFO(x) + #endif + #ifdef AM53C974_DEBUG_LINKED + #define DEB_LINKED(x) x + #else + #define DEB_LINKED(x) + #endif + #ifdef AM53C974_DEBUG_INTR + #define DEB_INTR(x) x + #else + #define DEB_INTR(x) + #endif +#else + #define DEB_INIT(x) + #define DEB(x) + #define DEB_QUEUE(x) + #define LIST(x,y) + #define REMOVE(w,x,y,z) + #define DEB_INFO(x) + #define DEB_LINKED(x) + #define DEB_INTR(x) + #define DEB_MSG(x) + #define DEB_RESEL(x) + #define KEYWAIT() +#endif + #ifdef AM53C974_DEBUG_ABORT + #define DEB_ABORT(x) x + #else + #define DEB_ABORT(x) + #endif + +#ifdef VERBOSE_AM53C974_DEBUG +#define VDEB(x) x +#else +#define VDEB(x) +#endif + +#define INSIDE(x,l,h) ( ((x) >= (l)) && ((x) <= (h)) ) + +#ifdef AM53C974_DEBUG +static void AM53C974_print_pci(struct Scsi_Host *instance); +static void AM53C974_print_phase(struct Scsi_Host *instance); +static void AM53C974_print_queues(struct Scsi_Host *instance); +#endif /* AM53C974_DEBUG */ +static void AM53C974_print(struct Scsi_Host *instance); +static void AM53C974_keywait(void); +static int AM53C974_bios_detect(Scsi_Host_Template *tpnt); +static int AM53C974_nobios_detect(Scsi_Host_Template *tpnt); +static int AM53C974_init(Scsi_Host_Template *tpnt, pci_config_t pci_config); +static void AM53C974_config_after_reset(struct Scsi_Host *instance); +static __inline__ void initialize_SCp(Scsi_Cmnd *cmd); +static __inline__ void run_main(void); +static void AM53C974_main (void); +static void AM53C974_intr(int irq, struct pt_regs *regs); +static void AM53C974_intr_disconnect(struct Scsi_Host *instance); +static int AM53C974_sync_neg(struct Scsi_Host *instance, int target, unsigned char *msg); +static __inline__ void AM53C974_set_async(struct Scsi_Host *instance, int target); +static __inline__ void AM53C974_set_sync(struct Scsi_Host *instance, int target); +static void AM53C974_information_transfer(struct Scsi_Host *instance, + unsigned char statreg, unsigned char isreg, + unsigned char instreg, unsigned char cfifo, + unsigned char dmastatus); +static int AM53C974_message(struct Scsi_Host *instance, Scsi_Cmnd *cmd, unsigned char msg); +static void AM53C974_select(struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag); +static void AM53C974_intr_reselect(struct Scsi_Host *instance, unsigned char statreg); +static __inline__ void AM53C974_transfer_dma(struct Scsi_Host *instance, short dir, + unsigned long length, char *data); +static void AM53C974_dma_blast(struct Scsi_Host *instance, unsigned char dmastatus, + unsigned char statreg); +static void AM53C974_intr_bus_reset(struct Scsi_Host *instance); + +static struct Scsi_Host *first_instance = NULL; +static Scsi_Host_Template *the_template = NULL; +static struct Scsi_Host *first_host = NULL; /* Head of list of AMD boards */ +static volatile int main_running = 0; +static int commandline_current = 0; +override_t overrides[7] = { {-1, 0, 0, 0}, }; /* LILO overrides */ + +#ifdef AM53C974_DEBUG +static int deb_stop = 1; + +/************************************************************************** + * Function : void AM53C974_print_pci(struct Scsi_Host *instance) + * + * Purpose : dump the PCI registers for debugging purposes + * + * Input : instance - which AM53C974 + **************************************************************************/ +static void AM53C974_print_pci(struct Scsi_Host *instance) +{ +int i; +unsigned short vendor_id, device_id, command, status, scratch[8]; +unsigned long class_revision, base; +unsigned char irq, cache_line_size, latency_timer, header_type; + +AM53C974_PCIREG_OPEN(); + +for (i = 0; i < 8; i++) *(scratch + i) = AM53C974_PCIREG_READ_WORD(instance, PCI_SCRATCH_REG_0 + 2*i); +vendor_id = AM53C974_PCIREG_READ_WORD(instance, PCI_VENDOR_ID); +device_id = AM53C974_PCIREG_READ_WORD(instance, PCI_DEVICE_ID); +command = AM53C974_PCIREG_READ_WORD(instance, PCI_COMMAND); +status = AM53C974_PCIREG_READ_WORD(instance, PCI_STATUS); +class_revision = AM53C974_PCIREG_READ_DWORD(instance, PCI_CLASS_REVISION); +cache_line_size = AM53C974_PCIREG_READ_BYTE(instance, PCI_CACHE_LINE_SIZE); +latency_timer = AM53C974_PCIREG_READ_BYTE(instance, PCI_LATENCY_TIMER); +header_type = AM53C974_PCIREG_READ_BYTE(instance, PCI_HEADER_TYPE); +base = AM53C974_PCIREG_READ_DWORD(instance, PCI_BASE_ADDRESS_0); +irq = AM53C974_PCIREG_READ_BYTE(instance, PCI_INTERRUPT_LINE); + +AM53C974_PCIREG_CLOSE(); + + +printk("------------- start of PCI register dump -------------\n"); +printk("PCI_VENDOR_ID: 0x%x\n", vendor_id); +printk("PCI_DEVICE_ID: 0x%x\n", device_id); +printk("PCI_COMMAND: 0x%x\n", command); +printk("PCI_STATUS: 0x%x\n", status); +printk("PCI_CLASS_REVISION: 0x%lx\n", class_revision); +printk("PCI_CACHE_LINE_SIZE: 0x%x\n", cache_line_size); +printk("PCI_LATENCY_TIMER: 0x%x\n", latency_timer); +printk("PCI_HEADER_TYPE: 0x%x\n", header_type); +printk("PCI_BASE_ADDRESS_0: 0x%lx\n", base); +printk("PCI_INTERRUPT_LINE: %d\n", irq); +for (i = 0; i < 8; i++) printk("PCI_SCRATCH_%d: 0x%x\n", i, scratch[i]); +printk("------------- end of PCI register dump -------------\n\n"); +} + +static struct { + unsigned char value; + char *name; +} phases[] = { +{PHASE_DATAOUT, "DATAOUT"}, {PHASE_DATAIN, "DATAIN"}, {PHASE_CMDOUT, "CMDOUT"}, +{PHASE_STATIN, "STATIN"}, {PHASE_MSGOUT, "MSGOUT"}, {PHASE_MSGIN, "MSGIN"}, +{PHASE_RES_0, "RESERVED 0"}, {PHASE_RES_1, "RESERVED 1"}}; + +/************************************************************************** + * Function : void AM53C974_print_phase(struct Scsi_Host *instance) + * + * Purpose : print the current SCSI phase for debugging purposes + * + * Input : instance - which AM53C974 + **************************************************************************/ +static void AM53C974_print_phase(struct Scsi_Host *instance) +{ +AM53C974_local_declare(); +unsigned char statreg, latched; +int i; +AM53C974_setio(instance); + +latched = (AM53C974_read_8(CNTLREG2)) & CNTLREG2_ENF; +statreg = AM53C974_read_8(STATREG); +for (i = 0; (phases[i].value != PHASE_RES_1) && + (phases[i].value != (statreg & STATREG_PHASE)); ++i); +if (latched) + printk("scsi%d : phase %s, latched at end of last command\n", instance->host_no, phases[i].name); + else + printk("scsi%d : phase %s, real time\n", instance->host_no, phases[i].name); +} + +/************************************************************************** + * Function : void AM53C974_print_queues(struct Scsi_Host *instance) + * + * Purpose : print commands in the various queues + * + * Inputs : instance - which AM53C974 + **************************************************************************/ +static void AM53C974_print_queues(struct Scsi_Host *instance) +{ +struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata; +Scsi_Cmnd *ptr; + +printk("AM53C974: coroutine is%s running.\n", main_running ? "" : "n't"); + +cli(); + +if (!hostdata->connected) { + printk ("scsi%d: no currently connected command\n", instance->host_no); } + else { + print_Scsi_Cmnd ((Scsi_Cmnd *)hostdata->connected); } +if (!hostdata->sel_cmd) { + printk ("scsi%d: no currently arbitrating command\n", instance->host_no); } + else { + print_Scsi_Cmnd ((Scsi_Cmnd *)hostdata->sel_cmd); } + +printk ("scsi%d: issue_queue ", instance->host_no); +if (!hostdata->issue_queue) + printk("empty\n"); + else { + printk(":\n"); + for (ptr = (Scsi_Cmnd *)hostdata->issue_queue; ptr; ptr = (Scsi_Cmnd *)ptr->host_scribble) + print_Scsi_Cmnd (ptr); } + +printk ("scsi%d: disconnected_queue ", instance->host_no); +if (!hostdata->disconnected_queue) + printk("empty\n"); + else { + printk(":\n"); + for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr; ptr = (Scsi_Cmnd *)ptr->host_scribble) + print_Scsi_Cmnd (ptr); } + +sti(); +} + +#endif /* AM53C974_DEBUG */ + +/************************************************************************** + * Function : void AM53C974_print(struct Scsi_Host *instance) + * + * Purpose : dump the chip registers for debugging purposes + * + * Input : instance - which AM53C974 + **************************************************************************/ +static void AM53C974_print(struct Scsi_Host *instance) +{ +AM53C974_local_declare(); +unsigned long ctcreg, dmastc, dmaspa, dmawbc, dmawac; +unsigned char cmdreg, statreg, isreg, cfireg, cntlreg[4], dmacmd, dmastatus; +AM53C974_setio(instance); + +cli(); +ctcreg = AM53C974_read_8(CTCHREG) << 16; +ctcreg |= AM53C974_read_8(CTCMREG) << 8; +ctcreg |= AM53C974_read_8(CTCLREG); +cmdreg = AM53C974_read_8(CMDREG); +statreg = AM53C974_read_8(STATREG); +isreg = AM53C974_read_8(ISREG); +cfireg = AM53C974_read_8(CFIREG); +cntlreg[0] = AM53C974_read_8(CNTLREG1); +cntlreg[1] = AM53C974_read_8(CNTLREG2); +cntlreg[2] = AM53C974_read_8(CNTLREG3); +cntlreg[3] = AM53C974_read_8(CNTLREG4); +dmacmd = AM53C974_read_8(DMACMD); +dmastc = AM53C974_read_32(DMASTC); +dmaspa = AM53C974_read_32(DMASPA); +dmawbc = AM53C974_read_32(DMAWBC); +dmawac = AM53C974_read_32(DMAWAC); +dmastatus = AM53C974_read_8(DMASTATUS); +sti(); + +printk("AM53C974 register dump:\n"); +printk("IO base: 0x%04lx; CTCREG: 0x%04lx; CMDREG: 0x%02x; STATREG: 0x%02x; ISREG: 0x%02x\n", + io_port, ctcreg, cmdreg, statreg, isreg); +printk("CFIREG: 0x%02x; CNTLREG1-4: 0x%02x; 0x%02x; 0x%02x; 0x%02x\n", + cfireg, cntlreg[0], cntlreg[1], cntlreg[2], cntlreg[3]); +printk("DMACMD: 0x%02x; DMASTC: 0x%04lx; DMASPA: 0x%04lx\n", dmacmd, dmastc, dmaspa); +printk("DMAWBC: 0x%04lx; DMAWAC: 0x%04lx; DMASTATUS: 0x%02x\n", dmawbc, dmawac, dmastatus); +printk("---------------------------------------------------------\n"); +} + +/************************************************************************** +* Function : void AM53C974_keywait(void) +* +* Purpose : wait until a key is pressed, if it was the 'r' key leave singlestep mode; +* this function is used for debugging only +* +* Input : none +**************************************************************************/ +static void AM53C974_keywait(void) +{ +#ifdef AM53C974_DEBUG +int key; + +if (!deb_stop) return; +#endif + +cli(); +while ((inb_p(0x64) & 0x01) != 0x01) ; +#ifdef AM53C974_DEBUG +key = inb(0x60); +if (key == 0x93) deb_stop = 0; /* don't stop if 'r' was pressed */ +#endif +sti(); +} + +/************************************************************************** +* Function : AM53C974_setup(char *str, int *ints) +* +* Purpose : LILO command line initialization of the overrides array, +* +* Inputs : str - unused, ints - array of integer parameters with ints[0] +* equal to the number of ints. +* +* NOTE : this function needs to be declared as an external function +* in init/main.c and included there in the bootsetups list +***************************************************************************/ +void AM53C974_setup(char *str, int *ints) +{ +if (ints[0] < 4) + printk("AM53C974_setup: wrong number of parameters;\n correct syntax is: AM53C974=host-scsi-id, target-scsi-id, max-rate, max-offset\n"); + else { + if (commandline_current < (sizeof(overrides) / sizeof(override_t))) { + if ((ints[1] < 0) || (ints[1] > 7) || + (ints[2] < 0) || (ints[2] > 7) || + (ints[1] == ints[2]) || + (ints[3] < (DEF_CLK / MAX_PERIOD)) || (ints[3] > (DEF_CLK / MIN_PERIOD)) || + (ints[4] < 0) || (ints[4] > MAX_OFFSET)) + printk("AM53C974_setup: illegal parameter\n"); + else { + overrides[commandline_current].host_scsi_id = ints[1]; + overrides[commandline_current].target_scsi_id = ints[2]; + overrides[commandline_current].max_rate = ints[3]; + overrides[commandline_current].max_offset = ints[4]; + commandline_current++; } + } + else + printk("AM53C974_setup: too many overrides\n"); + } +} + +#if defined (CONFIG_PCI) +/************************************************************************** +* Function : int AM53C974_bios_detect(Scsi_Host_Template *tpnt) +* +* Purpose : detects and initializes AM53C974 SCSI chips with PCI Bios +* +* Inputs : tpnt - host template +* +* Returns : number of host adapters detected +**************************************************************************/ +int AM53C974_bios_detect(Scsi_Host_Template *tpnt) +{ +int count = 0; /* number of boards detected */ +int pci_index; +pci_config_t pci_config; + +for (pci_index = 0; pci_index <= 16; ++pci_index) { + unsigned char pci_bus, pci_device_fn; + if (pcibios_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_SCSI, pci_index, &pci_bus, &pci_device_fn) != 0) + break; + + pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID, &pci_config._vendor); + pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID, &pci_config._device); + pcibios_read_config_word(pci_bus, pci_device_fn, PCI_COMMAND, &pci_config._command); + pcibios_read_config_word(pci_bus, pci_device_fn, PCI_STATUS, &pci_config._status); + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_CLASS_REVISION, &pci_config._class_revision); + pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_CACHE_LINE_SIZE, &pci_config._cache_line_size); + pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER, &pci_config._latency_timer); + pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_HEADER_TYPE, &pci_config._header_type); + pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_BIST, &pci_config._bist); + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0, &pci_config._base0); + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, &pci_config._base1); + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_2, &pci_config._base2); + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_3, &pci_config._base3); + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_4, &pci_config._base4); + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_5, &pci_config._base5); + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_ROM_ADDRESS, &pci_config._baserom); + pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_INTERRUPT_LINE, &pci_config._int_line); + pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_INTERRUPT_PIN, &pci_config._int_pin); + pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_MIN_GNT, &pci_config._min_gnt); + pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_MAX_LAT, &pci_config._max_lat); + pci_config._pcibus = 0xFFFFFFFF; + pci_config._cardnum = 0xFFFFFFFF; + + /* check whether device is I/O mapped -- should be */ + if (!(pci_config._command & PCI_COMMAND_IO)) continue; + + /* PCI Spec 2.1 states that it is either the driver's or the PCI card's responsibility + to set the PCI Master Enable Bit if needed. + (from Mark Stockton ) */ + if (!(pci_config._command & PCI_COMMAND_MASTER)) { + pci_config._command |= PCI_COMMAND_MASTER; + printk("PCI Master Bit has not been set. Setting...\n"); + pcibios_write_config_word(pci_bus, pci_device_fn, PCI_COMMAND, pci_config._command); } + + /* everything seems OK now, so initialize */ + if (AM53C974_init(tpnt, pci_config)) count++ ; + } +return (count); +} +#endif + +/************************************************************************** +* Function : int AM53C974_nobios_detect(Scsi_Host_Template *tpnt) +* +* Purpose : detects and initializes AM53C974 SCSI chips using PCI config 2 +* +* Inputs : tpnt - host template +* +* Returns : number of host adapters detected +* +* NOTE : This code assumes the controller on PCI bus 0. +* +* Origin: Robin Cutshaw (robin@xfree86.org) +**************************************************************************/ +int AM53C974_nobios_detect(Scsi_Host_Template *tpnt) +{ +int count = 0; /* number of boards detected */ +pci_config_t pci_config; + +/* first try PCI config method 1 */ +for (pci_config._pcibus = 0; pci_config._pcibus < 0x10; pci_config._pcibus++) { + for (pci_config._cardnum = 0; pci_config._cardnum < 0x20; pci_config._cardnum++) { + unsigned long config_cmd; + config_cmd = 0x80000000 | (pci_config._pcibus<<16) | (pci_config._cardnum<<11); + + outl(config_cmd, 0xCF8); /* ioreg 0 */ + pci_config._device_vendor = inl(0xCFC); + + if ((pci_config._vendor == PCI_VENDOR_ID_AMD) && (pci_config._device == PCI_DEVICE_ID_AMD_SCSI)) { + outl(config_cmd | PCI_COMMAND, 0xCF8); pci_config._status_command = inl(0xCFC); + outl(config_cmd | PCI_CLASS_REVISION, 0xCF8); pci_config._class_revision = inl(0xCFC); + outl(config_cmd | PCI_CACHE_LINE_SIZE, 0xCF8); pci_config._bist_header_latency_cache = inl(0xCFC); + outl(config_cmd | PCI_BASE_ADDRESS_0, 0xCF8); pci_config._base0 = inl(0xCFC); + outl(config_cmd | PCI_BASE_ADDRESS_1, 0xCF8); pci_config._base1 = inl(0xCFC); + outl(config_cmd | PCI_BASE_ADDRESS_2, 0xCF8); pci_config._base2 = inl(0xCFC); + outl(config_cmd | PCI_BASE_ADDRESS_3, 0xCF8); pci_config._base3 = inl(0xCFC); + outl(config_cmd | PCI_BASE_ADDRESS_4, 0xCF8); pci_config._base4 = inl(0xCFC); + outl(config_cmd | PCI_BASE_ADDRESS_5, 0xCF8); pci_config._base5 = inl(0xCFC); + outl(config_cmd | PCI_ROM_ADDRESS, 0xCF8); pci_config._baserom = inl(0xCFC); + outl(config_cmd | PCI_INTERRUPT_LINE, 0xCF8); pci_config._max_min_ipin_iline = inl(0xCFC); + + /* check whether device is I/O mapped -- should be */ + if (!(pci_config._command & PCI_COMMAND_IO)) continue; + + /* PCI Spec 2.1 states that it is either the driver's or the PCI card's responsibility + to set the PCI Master Enable Bit if needed. + From Mark Stockton */ + if (!(pci_config._command & PCI_COMMAND_MASTER)) { + pci_config._command |= PCI_COMMAND_MASTER; + printk("Config 1; PCI Master Bit has not been set. Setting...\n"); + outl(config_cmd | PCI_COMMAND, 0xCF8); outw(pci_config._command, 0xCFC); } + + /* everything seems OK now, so initialize */ + if (AM53C974_init(tpnt, pci_config)) count++ ; + } + } + } +outb(0, 0xCF8); /* is this really necessary? */ + +/* try PCI config method 2, if no device was detected by method 1 */ +if (!count) { + AM53C974_PCIREG_OPEN(); + + pci_config._pcibus = 0xFFFFFFFF; + pci_config._cardnum = 0xFFFFFFFF; + + for (pci_config._ioaddr = 0xC000; pci_config._ioaddr < 0xD000; pci_config._ioaddr += 0x0100) { + pci_config._device_vendor = inl(pci_config._ioaddr); + + if ((pci_config._vendor == PCI_VENDOR_ID_AMD) && (pci_config._device == PCI_DEVICE_ID_AMD_SCSI)) { + pci_config._status_command = inl(pci_config._ioaddr + PCI_COMMAND); + pci_config._class_revision = inl(pci_config._ioaddr + PCI_CLASS_REVISION); + pci_config._bist_header_latency_cache = inl(pci_config._ioaddr + PCI_CACHE_LINE_SIZE); + pci_config._base0 = inl(pci_config._ioaddr + PCI_BASE_ADDRESS_0); + pci_config._base1 = inl(pci_config._ioaddr + PCI_BASE_ADDRESS_1); + pci_config._base2 = inl(pci_config._ioaddr + PCI_BASE_ADDRESS_2); + pci_config._base3 = inl(pci_config._ioaddr + PCI_BASE_ADDRESS_3); + pci_config._base4 = inl(pci_config._ioaddr + PCI_BASE_ADDRESS_4); + pci_config._base5 = inl(pci_config._ioaddr + PCI_BASE_ADDRESS_5); + pci_config._baserom = inl(pci_config._ioaddr + PCI_ROM_ADDRESS); + pci_config._max_min_ipin_iline = inl(pci_config._ioaddr + PCI_INTERRUPT_LINE); + + /* check whether device is I/O mapped -- should be */ + if (!(pci_config._command & PCI_COMMAND_IO)) continue; + + /* PCI Spec 2.1 states that it is either the driver's or the PCI card's responsibility + to set the PCI Master Enable Bit if needed. + From Mark Stockton */ + if (!(pci_config._command & PCI_COMMAND_MASTER)) { + pci_config._command |= PCI_COMMAND_MASTER; + printk("Config 2; PCI Master Bit has not been set. Setting...\n"); + outw(pci_config._command, pci_config._ioaddr + PCI_COMMAND); } + + /* everything seems OK now, so initialize */ + if (AM53C974_init(tpnt, pci_config)) count++ ; + } + } + AM53C974_PCIREG_CLOSE(); + } + +return(count); +} + +/************************************************************************** +* Function : int AM53C974_detect(Scsi_Host_Template *tpnt) +* +* Purpose : detects and initializes AM53C974 SCSI chips +* +* Inputs : tpnt - host template +* +* Returns : number of host adapters detected +**************************************************************************/ +int AM53C974_detect(Scsi_Host_Template *tpnt) +{ +int count; /* number of boards detected */ + +#if defined (CONFIG_PCI) +if (pcibios_present()) + count = AM53C974_bios_detect(tpnt); + else +#endif +count = AM53C974_nobios_detect(tpnt); +return (count); +} + +/************************************************************************** +* Function : int AM53C974_init(Scsi_Host_Template *tpnt, pci_config_t pci_config) +* +* Purpose : initializes instance and corresponding AM53/79C974 chip, +* +* Inputs : tpnt - template, pci_config - PCI configuration, +* +* Returns : 1 on success, 0 on failure. +* +* NOTE: If no override for the controller's SCSI id is given and AM53C974_SCSI_ID +* is not defined we assume that the SCSI address of this controller is correctly +* set up by the BIOS (as reflected by contents of register CNTLREG1). +* This is the only BIOS assistance we need. +**************************************************************************/ +static int AM53C974_init(Scsi_Host_Template *tpnt, pci_config_t pci_config) +{ +AM53C974_local_declare(); +int i, j; +struct Scsi_Host *instance, *search; +struct AM53C974_hostdata *hostdata; + +#ifdef AM53C974_OPTION_DEBUG_PROBE_ONLY + printk ("AM53C974: probe only enabled, aborting initialization\n"); + return -1; +#endif + +instance = scsi_register(tpnt, sizeof(struct AM53C974_hostdata)); +hostdata = (struct AM53C974_hostdata *)instance->hostdata; +instance->base = NULL; +instance->io_port = pci_config._base0 & (pci_config._base0 & 0x1 ? + 0xFFFFFFFC : 0xFFFFFFF0); +instance->irq = pci_config._int_line; +instance->dma_channel = -1; +AM53C974_setio(instance); + +#ifdef AM53C974_SCSI_ID +instance->this_id = AM53C974_SCSI_ID; +AM53C974_write_8(CNTLREG1, instance->this_id & CNTLREG1_SID); +#else +instance->this_id = AM53C974_read_8(CNTLREG1) & CNTLREG1_SID; +if (instance->this_id != 7) + printk("scsi%d: WARNING: unusual hostadapter SCSI id %d; please verify!\n", + instance->host_no, instance->this_id); +#endif + +for (i = 0; i < sizeof(hostdata->msgout); i++) { + hostdata->msgout[i] = NOP; + hostdata->last_message[i] = NOP; } +for (i = 0; i < 8; i++) { + hostdata->busy[i] = 0; + hostdata->sync_per[i] = DEF_STP; + hostdata->sync_off[i] = 0; + hostdata->sync_neg[i] = 0; + hostdata->sync_en[i] = DEFAULT_SYNC_NEGOTIATION_ENABLED; + hostdata->max_rate[i] = DEFAULT_RATE; + hostdata->max_offset[i] = DEFAULT_SYNC_OFFSET; } + +/* overwrite defaults by LILO overrides */ +for (i = 0; i < commandline_current; i++) { + if (overrides[i].host_scsi_id == instance->this_id) { + j = overrides[i].target_scsi_id; + hostdata->sync_en[j] = 1; + hostdata->max_rate[j] = overrides[i].max_rate; + hostdata->max_offset[j] = overrides[i].max_offset; + } + } + +hostdata->sel_cmd = NULL; +hostdata->connected = NULL; +hostdata->issue_queue = NULL; +hostdata->disconnected_queue = NULL; +hostdata->in_reset = 0; +hostdata->aborted = 0; +hostdata->selecting = 0; +hostdata->disconnecting = 0; +hostdata->dma_busy = 0; + +/* Set up an interrupt handler if we aren't already sharing an IRQ with another board */ +for (search = first_host; + search && ( ((the_template != NULL) && (search->hostt != the_template)) || + (search->irq != instance->irq) || (search == instance) ); + search = search->next); +if (!search) { + if (request_irq(instance->irq, AM53C974_intr, SA_INTERRUPT, "AM53C974")) { + printk("scsi%d: IRQ%d not free, detaching\n", instance->host_no, instance->irq); + scsi_unregister(instance); + return -1; } + } + else { + printk("scsi%d: using interrupt handler previously installed for scsi%d\n", + instance->host_no, search->host_no); } + +if (!the_template) { + the_template = instance->hostt; + first_instance = instance; } + +/* do hard reset */ +AM53C974_write_8(CMDREG, CMDREG_RDEV); /* reset device */ +udelay(5); +AM53C974_write_8(CMDREG, CMDREG_NOP); +AM53C974_write_8(CNTLREG1, CNTLREG1_DISR | instance->this_id); +AM53C974_write_8(CMDREG, CMDREG_RBUS); /* reset SCSI bus */ +udelay(10); +AM53C974_config_after_reset(instance); + +return(0); +} + +/********************************************************************* +* Function : AM53C974_config_after_reset(struct Scsi_Host *instance) * +* * +* Purpose : initializes chip registers after reset * +* * +* Inputs : instance - which AM53C974 * +* * +* Returns : nothing * +**********************************************************************/ +static void AM53C974_config_after_reset(struct Scsi_Host *instance) +{ +AM53C974_local_declare(); +AM53C974_setio(instance); + +/* clear SCSI FIFO */ +AM53C974_write_8(CMDREG, CMDREG_CFIFO); + +/* configure device */ +AM53C974_write_8(STIMREG, DEF_SCSI_TIMEOUT); +AM53C974_write_8(STPREG, DEF_STP & STPREG_STP); +AM53C974_write_8(SOFREG, (DEF_SOF_RAD<<6) | (DEF_SOF_RAA<<4)); +AM53C974_write_8(CLKFREG, DEF_CLKF & CLKFREG_MASK); +AM53C974_write_8(CNTLREG1, (DEF_ETM<<7) | CNTLREG1_DISR | (DEF_PERE<<4) | instance->this_id); +AM53C974_write_8(CNTLREG2, (DEF_ENF<<6)); +AM53C974_write_8(CNTLREG3, (DEF_ADIDCHK<<7) | (DEF_FASTSCSI<<4) | (DEF_FASTCLK<<3)); +AM53C974_write_8(CNTLREG4, (DEF_GLITCH<<6) | (DEF_PWD<<5) | (DEF_RAE<<3) | (DEF_RADE<<2) | CNTLREG4_RES); +} + +/*********************************************************************** +* Function : const char *AM53C974_info(struct Scsi_Host *instance) * +* * +* Purpose : return device driver information * +* * +* Inputs : instance - which AM53C974 * +* * +* Returns : info string * +************************************************************************/ +const char *AM53C974_info(struct Scsi_Host *instance) +{ +static char info[100]; + +sprintf(info, "AM53/79C974 PCscsi driver rev. %d.%d; host I/O address: 0x%x; irq: %d\n", + AM53C974_DRIVER_REVISION_MAJOR, AM53C974_DRIVER_REVISION_MINOR, + instance->io_port, instance->irq); +return (info); +} + +/************************************************************************** +* Function : int AM53C974_command (Scsi_Cmnd *SCpnt) * +* * +* Purpose : the unqueued SCSI command function, replaced by the * +* AM53C974_queue_command function * +* * +* Inputs : SCpnt - pointer to command structure * +* * +* Returns :status, see hosts.h for details * +***************************************************************************/ +int AM53C974_command(Scsi_Cmnd *SCpnt) +{ +DEB(printk("AM53C974_command called\n")); +return 0; +} + +/************************************************************************** +* Function : void initialize_SCp(Scsi_Cmnd *cmd) * +* * +* Purpose : initialize the saved data pointers for cmd to point to the * +* start of the buffer. * +* * +* Inputs : cmd - Scsi_Cmnd structure to have pointers reset. * +* * +* Returns : nothing * +**************************************************************************/ +static __inline__ void initialize_SCp(Scsi_Cmnd *cmd) +{ +if (cmd->use_sg) { + cmd->SCp.buffer = (struct scatterlist *)cmd->buffer; + cmd->SCp.buffers_residual = cmd->use_sg - 1; + cmd->SCp.ptr = (char *)cmd->SCp.buffer->address; + 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; } +} + +/************************************************************************** +* Function : run_main(void) * +* * +* Purpose : insure that the coroutine is running and will process our * +* request. main_running is checked/set here (in an inline * +* function rather than in AM53C974_main itself to reduce the * +* chances of stack overflow. * +* * +* * +* Inputs : none * +* * +* Returns : nothing * +**************************************************************************/ +static __inline__ void run_main(void) +{ +cli(); +if (!main_running) { + /* main_running is cleared in AM53C974_main once it can't do + more work, and AM53C974_main exits with interrupts disabled. */ + main_running = 1; + AM53C974_main(); + sti(); } + else + sti(); +} + +/************************************************************************** +* Function : int AM53C974_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) +* +* Purpose : writes SCSI command into AM53C974 FIFO +* +* Inputs : cmd - SCSI command, done - function called on completion, with +* a pointer to the command descriptor. +* +* Returns : status, see hosts.h for details +* +* Side effects : +* cmd is added to the per instance issue_queue, with minor +* twiddling done to the host specific fields of cmd. If the +* main coroutine is not running, it is restarted. +**************************************************************************/ +int AM53C974_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) +{ +struct Scsi_Host *instance = cmd->host; +struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata; +Scsi_Cmnd *tmp; + +cli(); +DEB_QUEUE(printk(SEPARATOR_LINE)); +DEB_QUEUE(printk("scsi%d: AM53C974_queue_command called\n", instance->host_no)); +DEB_QUEUE(printk("cmd=%02x target=%02x lun=%02x bufflen=%d use_sg = %02x\n", + cmd->cmnd[0], cmd->target, cmd->lun, cmd->request_bufflen, cmd->use_sg)); + +/* We use the host_scribble field as a pointer to the next command in a queue */ +cmd->host_scribble = NULL; +cmd->scsi_done = done; +cmd->result = 0; +cmd->device->disconnect = 0; + +/* Insert the cmd into the issue queue. Note that REQUEST SENSE + * commands are added to the head of the queue since any command will + * clear the contingent allegiance condition that exists and the + * sense data is only guaranteed to be valid while the condition exists. */ +if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) { + LIST(cmd, hostdata->issue_queue); + cmd->host_scribble = (unsigned char *)hostdata->issue_queue; + hostdata->issue_queue = cmd; } + else { + for (tmp = (Scsi_Cmnd *)hostdata->issue_queue; tmp->host_scribble; + tmp = (Scsi_Cmnd *)tmp->host_scribble); + LIST(cmd, tmp); + tmp->host_scribble = (unsigned char *)cmd; } + +DEB_QUEUE(printk("scsi%d : command added to %s of queue\n", instance->host_no, + (cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail")); + +/* Run the coroutine if it isn't already running. */ +run_main(); +return 0; +} + +/************************************************************************** + * Function : AM53C974_main (void) + * + * Purpose : AM53C974_main is a coroutine that runs as long as more work can + * be done on the AM53C974 host adapters in a system. Both + * AM53C974_queue_command() and AM53C974_intr() will try to start it + * in case it is not running. + * + * NOTE : AM53C974_main exits with interrupts *disabled*, the caller should + * reenable them. This prevents reentrancy and kernel stack overflow. + **************************************************************************/ +static void AM53C974_main(void) +{ +AM53C974_local_declare(); +Scsi_Cmnd *tmp, *prev; +struct Scsi_Host *instance; +struct AM53C974_hostdata *hostdata; +int done; + +/* We run (with interrupts disabled) until we're sure that none of + * the host adapters have anything that can be done, at which point + * we set main_running to 0 and exit. */ + +do { + cli(); /* Freeze request queues */ + done = 1; + for (instance = first_instance; instance && instance->hostt == the_template; + instance = instance->next) { + hostdata = (struct AM53C974_hostdata *)instance->hostdata; + AM53C974_setio(instance); + /* start to select target if we are not connected and not in the + selection process */ + if (!hostdata->connected && !hostdata->sel_cmd) { + /* Search through the issue_queue for a command destined for a target + that is not busy. */ + for (tmp = (Scsi_Cmnd *)hostdata->issue_queue, prev = NULL; tmp; + prev = tmp, tmp = (Scsi_Cmnd *)tmp->host_scribble) { + /* When we find one, remove it from the issue queue. */ + if (!(hostdata->busy[tmp->target] & (1 << tmp->lun))) { + if (prev) { + REMOVE(prev, (Scsi_Cmnd *)(prev->host_scribble), tmp, + (Scsi_Cmnd *)(tmp->host_scribble)); + prev->host_scribble = tmp->host_scribble; } + else { + REMOVE(-1, hostdata->issue_queue, tmp, tmp->host_scribble); + hostdata->issue_queue = (Scsi_Cmnd *)tmp->host_scribble; } + tmp->host_scribble = NULL; + + /* go into selection mode, disable reselection and wait for + SO interrupt which will continue with the selection process */ + hostdata->selecting = 1; + hostdata->sel_cmd = tmp; + AM53C974_write_8(CMDREG, CMDREG_DSR); + break; + } /* if target/lun is not busy */ + + } /* for */ + } /* if (!hostdata->connected) */ + else { + DEB(printk("main: connected; cmd = 0x%lx, sel_cmd = 0x%lx\n", + (long)hostdata->connected, (long)hostdata->sel_cmd)); + } + } /* for instance */ + } while (!done); +main_running = 0; +} + +/********************************************************************* +* Function : AM53C974_intr(int irq, struct pt_regs *regs) * +* * +* Purpose : interrupt handler * +* * +* Inputs : irq - interrupt line, regs - ? * +* * +* Returns : nothing * +**********************************************************************/ +static void AM53C974_intr(int irq, struct pt_regs *regs) +{ +AM53C974_local_declare(); +struct Scsi_Host *instance; +struct AM53C974_hostdata *hostdata; +unsigned char cmdreg, dmastatus, statreg, isreg, instreg, cfifo; + +/* find AM53C974 hostadapter responsible for this interrupt */ +for (instance = first_instance; instance; instance = instance->next) + if ((instance->irq == irq) && (instance->hostt == the_template)) goto FOUND; +sti(); +return; + +/* found; now decode and process */ +FOUND: +hostdata = (struct AM53C974_hostdata *)instance->hostdata; +AM53C974_setio(instance); +dmastatus = AM53C974_read_8(DMASTATUS); + +DEB_INTR(printk(SEPARATOR_LINE)); +DEB_INTR(printk("AM53C974 interrupt; dmastatus=0x%02x\n", dmastatus)); +KEYWAIT(); + +/*** DMA related interrupts ***/ +if (hostdata->connected && (dmastatus & (DMASTATUS_ERROR | DMASTATUS_PWDN | + DMASTATUS_ABORT))) { + /* DMA error or POWERDOWN */ + printk("scsi%d: DMA error or powerdown; dmastatus: 0x%02x\n", + instance->host_no, dmastatus); +#ifdef AM53C974_DEBUG + deb_stop = 1; +#endif + panic("scsi%d: cannot recover\n", instance->host_no); } + +if (hostdata->connected && (dmastatus & DMASTATUS_DONE)) { + /* DMA transfer done */ + unsigned long residual; + cli(); + if (!(AM53C974_read_8(DMACMD) & DMACMD_DIR)) { + do { + dmastatus = AM53C974_read_8(DMASTATUS); + residual = AM53C974_read_8(CTCLREG) | (AM53C974_read_8(CTCMREG) << 8) | + (AM53C974_read_8(CTCHREG) << 16); + residual += AM53C974_read_8(CFIREG) & CFIREG_CF; + } while (!(dmastatus & DMASTATUS_SCSIINT) && residual); + residual = AM53C974_read_8(CTCLREG) | (AM53C974_read_8(CTCMREG) << 8) | + (AM53C974_read_8(CTCHREG) << 16); + residual += AM53C974_read_8(CFIREG) & CFIREG_CF; + } + else + residual = 0; + hostdata->connected->SCp.ptr += hostdata->connected->SCp.this_residual - residual; + hostdata->connected->SCp.this_residual = residual; + + AM53C974_write_8(DMACMD, DMACMD_IDLE); + + /* if service request missed before, process it now (ugly) */ + if (hostdata->dma_busy) { + hostdata->dma_busy = 0; + cmdreg = AM53C974_read_8(CMDREG); + statreg = AM53C974_read_8(STATREG); + isreg = AM53C974_read_8(ISREG); + instreg = AM53C974_read_8(INSTREG); + cfifo = AM53C974_cfifo(); + AM53C974_information_transfer(instance, statreg, isreg, instreg, cfifo, + dmastatus); } + sti(); + } + +if (!(dmastatus & DMASTATUS_SCSIINT)) { + sti(); + return; } + +/*** SCSI related interrupts ***/ +cmdreg = AM53C974_read_8(CMDREG); +statreg = AM53C974_read_8(STATREG); +isreg = AM53C974_read_8(ISREG); +instreg = AM53C974_read_8(INSTREG); +cfifo = AM53C974_cfifo(); + +DEB_INTR(printk("scsi%d: statreg: 0x%02x; isreg: 0x%02x; instreg: 0x%02x; cfifo: 0x%02x\n", + instance->host_no, statreg, isreg, instreg, cfifo)); + +if (statreg & STATREG_PE) { + /* parity error */ +#ifdef AM53C974_DEBUG + deb_stop = 1; +#endif + printk("scsi%d : PARITY error\n", instance->host_no); + if (hostdata->connected) hostdata->sync_off[hostdata->connected->target] = 0; /* setup asynchronous transfer */ + hostdata->aborted = 1; } + +if (statreg & STATREG_IOE) { + /* illegal operation error */ +#ifdef AM53C974_DEBUG + deb_stop = 1; +#endif + printk("scsi%d : ILLEGAL OPERATION error\n", instance->host_no); + printk("cmdreg: 0x%02x; dmacmd: 0x%02x; statreg: 0x%02x; \n" + "isreg: 0x%02x; instreg: 0x%02x; cfifo: 0x%02x\n", + cmdreg, AM53C974_read_8(DMACMD), statreg, isreg, instreg, cfifo); } +if (hostdata->in_reset && (instreg & INSTREG_SRST)) { + /* RESET INTERRUPT */ +#ifdef AM53C974_DEBUG + deb_stop = 1; +#endif + DEB(printk("Bus reset interrupt received\n")); + AM53C974_intr_bus_reset(instance); + cli(); + if (hostdata->connected) { + hostdata->connected->result = DID_RESET << 16; + hostdata->connected->scsi_done((Scsi_Cmnd *)hostdata->connected); + hostdata->connected = NULL; } + else { + if (hostdata->sel_cmd) { + hostdata->sel_cmd->result = DID_RESET << 16; + hostdata->sel_cmd->scsi_done((Scsi_Cmnd *)hostdata->sel_cmd); + hostdata->sel_cmd = NULL; } + } + sti(); + if (hostdata->in_reset == 1) goto EXIT; + else return; + } + +if (instreg & INSTREG_ICMD) { + /* INVALID COMMAND INTERRUPT */ +#ifdef AM53C974_DEBUG + deb_stop = 1; +#endif + printk("scsi%d: Invalid command interrupt\n", instance->host_no); + printk("cmdreg: 0x%02x; dmacmd: 0x%02x; statreg: 0x%02x; dmastatus: 0x%02x; \n" + "isreg: 0x%02x; instreg: 0x%02x; cfifo: 0x%02x\n", + cmdreg, AM53C974_read_8(DMACMD), statreg, dmastatus, isreg, instreg, cfifo); + panic("scsi%d: cannot recover\n", instance->host_no); } + +if (instreg & INSTREG_DIS) { + /* DISCONNECT INTERRUPT */ + DEB_INTR(printk("Disconnect interrupt received; ")); + cli(); + AM53C974_intr_disconnect(instance); + sti(); + goto EXIT; } + +if (instreg & INSTREG_RESEL) { + /* RESELECTION INTERRUPT */ + DEB_INTR(printk("Reselection interrupt received\n")); + cli(); + AM53C974_intr_reselect(instance, statreg); + sti(); + goto EXIT; } + +if (instreg & INSTREG_SO) { + DEB_INTR(printk("Successful operation interrupt received\n")); + if (hostdata->selecting) { + DEB_INTR(printk("DSR completed, starting select\n")); + cli(); + AM53C974_select(instance, (Scsi_Cmnd *)hostdata->sel_cmd, + (hostdata->sel_cmd->cmnd[0] == REQUEST_SENSE) ? + TAG_NONE : TAG_NEXT); + hostdata->selecting = 0; + AM53C974_set_sync(instance, hostdata->sel_cmd->target); + sti(); + return; } + + if (hostdata->sel_cmd != NULL) { + if ( ((isreg & ISREG_IS) != ISREG_OK_NO_STOP) && + ((isreg & ISREG_IS) != ISREG_OK_STOP) ) { + /* UNSUCCESSFUL SELECTION */ + DEB_INTR(printk("unsuccessful selection\n")); + cli(); + hostdata->dma_busy = 0; + LIST(hostdata->sel_cmd, hostdata->issue_queue); + hostdata->sel_cmd->host_scribble = (unsigned char *)hostdata->issue_queue; + hostdata->issue_queue = hostdata->sel_cmd; + hostdata->sel_cmd = NULL; + hostdata->selecting = 0; + sti(); + goto EXIT; } + else { + /* SUCCESSFUL SELECTION */ + DEB(printk("successful selection; cmd=0x%02lx\n", (long)hostdata->sel_cmd)); + cli(); + hostdata->dma_busy = 0; + hostdata->disconnecting = 0; + hostdata->connected = hostdata->sel_cmd; + hostdata->sel_cmd = NULL; + hostdata->selecting = 0; +#ifdef SCSI2 + if (!hostdata->connected->device->tagged_queue) +#endif + hostdata->busy[hostdata->connected->target] |= (1 << hostdata->connected->lun); + /* very strange -- use_sg is sometimes nonzero for request sense commands !! */ + if ((hostdata->connected->cmnd[0] == REQUEST_SENSE) && hostdata->connected->use_sg) { + DEB(printk("scsi%d: REQUEST_SENSE command with nonzero use_sg\n", instance->host_no)); + KEYWAIT(); + hostdata->connected->use_sg = 0; } + initialize_SCp((Scsi_Cmnd *)hostdata->connected); + hostdata->connected->SCp.phase = PHASE_CMDOUT; + AM53C974_information_transfer(instance, statreg, isreg, instreg, cfifo, dmastatus); + sti(); + return; } + } + else { + cli(); + AM53C974_information_transfer(instance, statreg, isreg, instreg, cfifo, dmastatus); + sti(); + return; } + } + +if (instreg & INSTREG_SR) { + DEB_INTR(printk("Service request interrupt received, ")); + if (hostdata->connected) { + DEB_INTR(printk("calling information_transfer\n")); + cli(); + AM53C974_information_transfer(instance, statreg, isreg, instreg, cfifo, dmastatus); + sti(); } + else { + printk("scsi%d: weird: service request when no command connected\n", instance->host_no); + AM53C974_write_8(CMDREG, CMDREG_CFIFO); } /* clear FIFO */ + return; + } + +EXIT: + DEB_INTR(printk("intr: starting main\n")); + run_main(); + DEB_INTR(printk("end of intr\n")); +} + +/************************************************************************** +* Function : AM53C974_intr_disconnect(struct Scsi_Host *instance) +* +* Purpose : manage target disconnection +* +* Inputs : instance -- which AM53C974 +* +* Returns : nothing +**************************************************************************/ +static void AM53C974_intr_disconnect(struct Scsi_Host *instance) +{ +AM53C974_local_declare(); +struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata; +Scsi_Cmnd *cmd; +AM53C974_setio(instance); + +if (hostdata->sel_cmd != NULL) { + /* normal selection timeout, typical for nonexisting targets */ + cmd = (Scsi_Cmnd *)hostdata->sel_cmd; + DEB_INTR(printk("bad target\n")); + cmd->result = DID_BAD_TARGET << 16; + goto EXIT_FINISHED; } + +if (!hostdata->connected) { + /* can happen if controller was reset, a device tried to reconnect, + failed and disconnects now */ + AM53C974_write_8(CMDREG, CMDREG_CFIFO); + return; } + +if (hostdata->disconnecting) { + /* target sent disconnect message, so we are prepared */ + cmd = (Scsi_Cmnd *)hostdata->connected; + AM53C974_set_async(instance, cmd->target); + DEB_INTR(printk("scsi%d : disc. from cmnd %d for ta %d, lun %d\n", + instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun)); + if (cmd->device->disconnect) { + /* target wants to reselect later */ + DEB_INTR(printk("ok, re-enabling selection\n")); + LIST(cmd,hostdata->disconnected_queue); + cmd->host_scribble = (unsigned char *)hostdata->disconnected_queue; + hostdata->disconnected_queue = cmd; + DEB_QUEUE(printk("scsi%d : command for target %d lun %d this %d was moved from connected to" + " the disconnected_queue\n", instance->host_no, cmd->target, + cmd->lun, hostdata->disconnected_queue->SCp.this_residual)); + DEB_QUEUE(AM53C974_print_queues(instance)); + goto EXIT_UNFINISHED; } + else { + /* target does not want to reselect later, we are really finished */ +#ifdef AM53C974_DEBUG + if (cmd->cmnd[0] == REQUEST_SENSE) { + int i; + printk("Request sense data dump:\n"); + for (i = 0; i < cmd->request_bufflen; i++) { + printk("%02x ", *((char *)(cmd->request_buffer) + i)); + if (i && !(i % 16)) printk("\n"); } + printk("\n"); } +#endif + goto EXIT_FINISHED; } /* !cmd->device->disconnect */ + } /* if (hostdata->disconnecting) */ + +/* no disconnect message received; unexpected disconnection */ +cmd = (Scsi_Cmnd *)hostdata->connected; +if (cmd) { +#ifdef AM53C974_DEBUG + deb_stop = 1; +#endif + AM53C974_set_async(instance, cmd->target); + printk("scsi%d: Unexpected disconnect; phase: %d; target: %d; this_residual: %d; buffers_residual: %d; message: %d\n", + instance->host_no, cmd->SCp.phase, cmd->target, cmd->SCp.this_residual, cmd->SCp.buffers_residual, + cmd->SCp.Message); + printk("cmdreg: 0x%02x; statreg: 0x%02x; isreg: 0x%02x; cfifo: 0x%02x\n", + AM53C974_read_8(CMDREG), AM53C974_read_8(STATREG), AM53C974_read_8(ISREG), + AM53C974_read_8(CFIREG) & CFIREG_CF); + + if ((hostdata->last_message[0] == EXTENDED_MESSAGE) && + (hostdata->last_message[2] == EXTENDED_SDTR)) { + /* sync. negotiation was aborted, setup asynchronous transfer with target */ + hostdata->sync_off[cmd->target] = 0; } + if (hostdata->aborted || hostdata->msgout[0] == ABORT) + cmd->result = DID_ABORT << 16; + else + cmd->result = DID_ERROR << 16; + goto EXIT_FINISHED; } + +EXIT_FINISHED: +hostdata->aborted = 0; +hostdata->msgout[0] = NOP; +hostdata->sel_cmd = NULL; +hostdata->connected = NULL; +hostdata->selecting = 0; +hostdata->disconnecting = 0; +hostdata->dma_busy = 0; +hostdata->busy[cmd->target] &= ~(1 << cmd->lun); +AM53C974_write_8(CMDREG, CMDREG_CFIFO); +DEB(printk("disconnect; issue_queue: 0x%lx, disconnected_queue: 0x%lx\n", + (long)hostdata->issue_queue, (long)hostdata->disconnected_queue)); +cmd->scsi_done(cmd); + +if (!hostdata->selecting) { + AM53C974_set_async(instance, cmd->target); + AM53C974_write_8(CMDREG, CMDREG_ESR); } /* allow reselect */ +return; + +EXIT_UNFINISHED: +hostdata->msgout[0] = NOP; +hostdata->sel_cmd = NULL; +hostdata->connected = NULL; +hostdata->aborted = 0; +hostdata->selecting = 0; +hostdata->disconnecting = 0; +hostdata->dma_busy = 0; +DEB(printk("disconnect; issue_queue: 0x%lx, disconnected_queue: 0x%lx\n", + (long)hostdata->issue_queue, (long)hostdata->disconnected_queue)); +if (!hostdata->selecting) { + AM53C974_set_async(instance, cmd->target); + AM53C974_write_8(CMDREG, CMDREG_ESR); } /* allow reselect */ +return; +} + +/************************************************************************** +* Function : int AM53C974_sync_neg(struct Scsi_Host *instance, int target, unsigned char *msg) +* +* Purpose : setup message string for sync. negotiation +* +* Inputs : instance -- which AM53C974 +* target -- which SCSI target to deal with +* msg -- input message string +* +* Returns : 0 if parameters accepted or 1 if not accepted +* +* Side effects: hostdata is changed +* +* Note: we assume here that fastclk is enabled +**************************************************************************/ +static int AM53C974_sync_neg(struct Scsi_Host *instance, int target, unsigned char *msg) +{ +AM53C974_local_declare(); +struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata; +int period, offset, i, rate, rate_rem; +AM53C974_setio(instance); + +period = (DEF_CLK * msg[3] * 8 + 1000) / 2000; +if (period < MIN_PERIOD) { + period = MIN_PERIOD; + hostdata->msgout[3] = period / 4; } + else + if (period > MAX_PERIOD) { + period = MAX_PERIOD; + hostdata->msgout[3] = period / 4; } + else + hostdata->msgout[3] = msg[3]; +offset = msg[4]; +if (offset > MAX_OFFSET) offset = MAX_OFFSET; +hostdata->msgout[4] = offset; +hostdata->sync_per[target] = period; +hostdata->sync_off[target] = offset; +for (i = 0; i < 3; i++) hostdata->msgout[i] = msg[i]; +if ((hostdata->msgout[3] != msg[3]) || (msg[4] != offset)) return(1); + +rate = DEF_CLK / period; +rate_rem = 10 * (DEF_CLK - period * rate) / period; + +if (offset) + printk("\ntarget %d: rate=%d.%d Mhz, synchronous, sync offset=%d bytes\n", + target, rate, rate_rem, offset); + else + printk("\ntarget %d: rate=%d.%d Mhz, asynchronous\n", target, rate, rate_rem); + +return(0); +} + +/************************************************************************** +* Function : AM53C974_set_async(struct Scsi_Host *instance, int target) +* +* Purpose : put controller into async. mode +* +* Inputs : instance -- which AM53C974 +* target -- which SCSI target to deal with +* +* Returns : nothing +**************************************************************************/ +static __inline__ void AM53C974_set_async(struct Scsi_Host *instance, int target) +{ +AM53C974_local_declare(); +struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata; +AM53C974_setio(instance); + +AM53C974_write_8(STPREG, hostdata->sync_per[target]); +AM53C974_write_8(SOFREG, (DEF_SOF_RAD<<6) | (DEF_SOF_RAA<<4)); +} + +/************************************************************************** +* Function : AM53C974_set_sync(struct Scsi_Host *instance, int target) +* +* Purpose : put controller into sync. mode +* +* Inputs : instance -- which AM53C974 +* target -- which SCSI target to deal with +* +* Returns : nothing +**************************************************************************/ +static __inline__ void AM53C974_set_sync(struct Scsi_Host *instance, int target) +{ +AM53C974_local_declare(); +struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata; +AM53C974_setio(instance); + +AM53C974_write_8(STPREG, hostdata->sync_per[target]); +AM53C974_write_8(SOFREG, (SOFREG_SO & hostdata->sync_off[target]) | + (DEF_SOF_RAD<<6) | (DEF_SOF_RAA<<4)); +} + +/*********************************************************************** +* Function : AM53C974_information_transfer(struct Scsi_Host *instance, * +* unsigned char statreg, unsigned char isreg, * +* unsigned char instreg, unsigned char cfifo, * +* unsigned char dmastatus) * +* * +* Purpose : handle phase changes * +* * +* Inputs : instance - which AM53C974 * +* statreg - stus register * +* isreg - internal state register * +* instreg - interrupt status register * +* cfifo - number of bytes in FIFO * +* dmastatus - dma status register * +* * +* Returns : nothing * +************************************************************************/ +static void AM53C974_information_transfer(struct Scsi_Host *instance, + unsigned char statreg, unsigned char isreg, + unsigned char instreg, unsigned char cfifo, + unsigned char dmastatus) +{ +AM53C974_local_declare(); +struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata; +Scsi_Cmnd *cmd = (Scsi_Cmnd *)hostdata->connected; +int ret, i, len, residual=-1; +AM53C974_setio(instance); + +DEB_INFO(printk(SEPARATOR_LINE)); +switch (statreg & STATREG_PHASE) { /* scsi phase */ + case PHASE_DATAOUT: + DEB_INFO(printk("Dataout phase; cmd=0x%lx, sel_cmd=0x%lx, this_residual=%d, buffers_residual=%d\n", + (long)hostdata->connected, (long)hostdata->sel_cmd, cmd->SCp.this_residual, cmd->SCp.buffers_residual)); + cmd->SCp.phase = PHASE_DATAOUT; + goto PHASE_DATA_IO; + + case PHASE_DATAIN: + DEB_INFO(printk("Datain phase; cmd=0x%lx, sel_cmd=0x%lx, this_residual=%d, buffers_residual=%d\n", + (long)hostdata->connected, (long)hostdata->sel_cmd, cmd->SCp.this_residual, cmd->SCp.buffers_residual)); + cmd->SCp.phase = PHASE_DATAIN; + PHASE_DATA_IO: + if (hostdata->aborted) { + AM53C974_write_8(DMACMD, DMACMD_IDLE); + AM53C974_write_8(CMDREG, CMDREG_CFIFO); + AM53C974_write_8(CMDREG, CMDREG_SATN); + return; } + if ((!cmd->SCp.this_residual) && cmd->SCp.buffers_residual) { + cmd->SCp.buffer++; + cmd->SCp.buffers_residual--; + cmd->SCp.ptr = (unsigned char *)cmd->SCp.buffer->address; + cmd->SCp.this_residual = cmd->SCp.buffer->length; } + if (cmd->SCp.this_residual) { + if (!(AM53C974_read_8(DMACMD) & DMACMD_START)) { + hostdata->dma_busy = 0; + AM53C974_transfer_dma(instance, statreg & STATREG_IO, + (unsigned long)cmd->SCp.this_residual, + cmd->SCp.ptr); } + else + hostdata->dma_busy = 1; + } + return; + + case PHASE_MSGIN: + DEB_INFO(printk("Message-In phase; cmd=0x%lx, sel_cmd=0x%lx\n", + (long)hostdata->connected, (long)hostdata->sel_cmd)); + AM53C974_set_async(instance, cmd->target); + if (cmd->SCp.phase == PHASE_DATAIN) + AM53C974_dma_blast(instance, dmastatus, statreg); + if ((cmd->SCp.phase == PHASE_DATAOUT) && (AM53C974_read_8(DMACMD) & DMACMD_START)) { + AM53C974_write_8(DMACMD, DMACMD_IDLE); + residual = cfifo + (AM53C974_read_8(CTCLREG) | (AM53C974_read_8(CTCMREG) << 8) | + (AM53C974_read_8(CTCHREG) << 16)); + cmd->SCp.ptr += cmd->SCp.this_residual - residual; + cmd->SCp.this_residual = residual; + if (cfifo) { AM53C974_write_8(CMDREG, CMDREG_CFIFO); cfifo = 0; } + } + if (cmd->SCp.phase == PHASE_STATIN) { + while ((AM53C974_read_8(CFIREG) & CFIREG_CF) < 2) ; + cmd->SCp.Status = AM53C974_read_8(FFREG); + cmd->SCp.Message = AM53C974_read_8(FFREG); + DEB_INFO(printk("Message-In phase; status=0x%02x, message=0x%02x\n", + cmd->SCp.Status, cmd->SCp.Message)); + ret = AM53C974_message(instance, cmd, cmd->SCp.Message); } + else { + if (!cfifo) { + AM53C974_write_8(CMDREG, CMDREG_IT); + AM53C974_poll_int(); + cmd->SCp.Message = AM53C974_read_8(FFREG); + } + ret = AM53C974_message(instance, cmd, cmd->SCp.Message); + } + cmd->SCp.phase = PHASE_MSGIN; + AM53C974_set_sync(instance, cmd->target); + break; + case PHASE_MSGOUT: + DEB_INFO(printk("Message-Out phase; cfifo=%d; msgout[0]=0x%02x\n", + AM53C974_read_8(CFIREG) & CFIREG_CF, hostdata->msgout[0])); + AM53C974_write_8(DMACMD, DMACMD_IDLE); + AM53C974_set_async(instance, cmd->target); + for (i = 0; i < sizeof(hostdata->last_message); i++) + hostdata->last_message[i] = hostdata->msgout[i]; + if ((hostdata->msgout[0] == 0) || INSIDE(hostdata->msgout[0], 0x02, 0x1F) || + INSIDE(hostdata->msgout[0], 0x80, 0xFF)) + len = 1; + else { + if (hostdata->msgout[0] == EXTENDED_MESSAGE) { +#ifdef AM53C974_DEBUG_INFO + printk("Extended message dump:\n"); + for (i = 0; i < hostdata->msgout[1] + 2; i++) { + printk("%02x ", hostdata->msgout[i]); + if (i && !(i % 16)) printk("\n"); } + printk("\n"); +#endif + len = hostdata->msgout[1] + 2; } + else + len = 2; + } + for (i = 0; i < len; i++) AM53C974_write_8(FFREG, hostdata->msgout[i]); + AM53C974_write_8(CMDREG, CMDREG_IT); + cmd->SCp.phase = PHASE_MSGOUT; + hostdata->msgout[0] = NOP; + AM53C974_set_sync(instance, cmd->target); + break; + + case PHASE_CMDOUT: + DEB_INFO(printk("Command-Out phase\n")); + AM53C974_set_async(instance, cmd->target); + for (i = 0; i < cmd->cmd_len; i++) AM53C974_write_8(FFREG, cmd->cmnd[i]); + AM53C974_write_8(CMDREG, CMDREG_IT); + cmd->SCp.phase = PHASE_CMDOUT; + AM53C974_set_sync(instance, cmd->target); + break; + + case PHASE_STATIN: + DEB_INFO(printk("Status phase\n")); + if (cmd->SCp.phase == PHASE_DATAIN) + AM53C974_dma_blast(instance, dmastatus, statreg); + AM53C974_set_async(instance, cmd->target); + if (cmd->SCp.phase == PHASE_DATAOUT) { + unsigned long residual; + + if (AM53C974_read_8(DMACMD) & DMACMD_START) { + AM53C974_write_8(DMACMD, DMACMD_IDLE); + residual = cfifo + (AM53C974_read_8(CTCLREG) | (AM53C974_read_8(CTCMREG) << 8) | + (AM53C974_read_8(CTCHREG) << 16)); + cmd->SCp.ptr += cmd->SCp.this_residual - residual; + cmd->SCp.this_residual = residual; } + if (cfifo) { AM53C974_write_8(CMDREG, CMDREG_CFIFO); cfifo = 0; } + } + cmd->SCp.phase = PHASE_STATIN; + AM53C974_write_8(CMDREG, CMDREG_ICCS); /* command complete */ + break; + + case PHASE_RES_0: + case PHASE_RES_1: +#ifdef AM53C974_DEBUG + deb_stop = 1; +#endif + DEB_INFO(printk("Reserved phase\n")); + break; + } +KEYWAIT(); +} + +/****************************************************************************** +* Function : int AM53C974_message(struct Scsi_Host *instance, Scsi_Cmnd *cmd, +* unsigned char msg) +* +* Purpose : handle SCSI messages +* +* Inputs : instance -- which AM53C974 +* cmd -- SCSI command the message belongs to +* msg -- message id byte +* +* Returns : 1 on success, 0 on failure. +**************************************************************************/ +static int AM53C974_message(struct Scsi_Host *instance, Scsi_Cmnd *cmd, + unsigned char msg) +{ +AM53C974_local_declare(); +static unsigned char extended_msg[10]; +unsigned char statreg; +int len, ret = 0; +unsigned char *p; +#ifdef AM53C974_DEBUG_MSG +int j; +#endif +struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata; +AM53C974_setio(instance); + +DEB_MSG(printk(SEPARATOR_LINE)); + +/* Linking lets us reduce the time required to get the + * next command out to the device, hopefully this will + * mean we don't waste another revolution due to the delays + * required by ARBITRATION and another SELECTION. + * In the current implementation proposal, low level drivers + * merely have to start the next command, pointed to by + * next_link, done() is called as with unlinked commands. */ +switch (msg) { +#ifdef LINKED + case LINKED_CMD_COMPLETE: + case LINKED_FLG_CMD_COMPLETE: + /* Accept message by releasing ACK */ + DEB_LINKED(printk("scsi%d : target %d lun %d linked command complete.\n", + instance->host_no, cmd->target, cmd->lun)); + /* Sanity check : A linked command should only terminate with + * one of these messages if there are more linked commands available. */ + if (!cmd->next_link) { + printk("scsi%d : target %d lun %d linked command complete, no next_link\n" + instance->host_no, cmd->target, cmd->lun); + hostdata->aborted = 1; + AM53C974_write_8(CMDREG, CMDREG_SATN); + AM53C974_write_8(CMDREG, CMDREG_MA); + break; } + if (hostdata->aborted) { + DEB_ABORT(printk("ATN set for cmnd %d upon reception of LINKED_CMD_COMPLETE or" + "LINKED_FLG_CMD_COMPLETE message\n", cmd->cmnd[0])); + AM53C974_write_8(CMDREG, CMDREG_SATN); } + AM53C974_write_8(CMDREG, CMDREG_MA); + + initialize_SCp(cmd->next_link); + /* The next command is still part of this process */ + cmd->next_link->tag = cmd->tag; + cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); + DEB_LINKED(printk("scsi%d : target %d lun %d linked request done, calling scsi_done().\n", + instance->host_no, cmd->target, cmd->lun)); + cmd->scsi_done(cmd); + cmd = hostdata->connected; + break; + +#endif /* def LINKED */ + + case ABORT: + case COMMAND_COMPLETE: + DEB_MSG(printk("scsi%d: command complete message received; cmd %d for target %d, lun %d\n", + instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun)); + hostdata->disconnecting = 1; + cmd->device->disconnect = 0; + + /* I'm not sure what the correct thing to do here is : + * + * If the command that just executed is NOT a request + * sense, the obvious thing to do is to set the result + * code to the values of the stored parameters. + * If it was a REQUEST SENSE command, we need some way + * to differentiate between the failure code of the original + * and the failure code of the REQUEST sense - the obvious + * case is success, where we fall through and leave the result + * code unchanged. + * + * The non-obvious place is where the REQUEST SENSE failed */ + if (cmd->cmnd[0] != REQUEST_SENSE) + cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); + else if (cmd->SCp.Status != GOOD) + cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16); + if (hostdata->aborted) { + AM53C974_write_8(CMDREG, CMDREG_SATN); + AM53C974_write_8(CMDREG, CMDREG_MA); + DEB_ABORT(printk("ATN set for cmnd %d upon reception of ABORT or" + "COMMAND_COMPLETE message\n", cmd->cmnd[0])); + break; } + if ((cmd->cmnd[0] != REQUEST_SENSE) && (cmd->SCp.Status == CHECK_CONDITION)) { + DEB_MSG(printk("scsi%d : performing request sense\n", instance->host_no)); + cmd->cmnd[0] = REQUEST_SENSE; + cmd->cmnd[1] &= 0xe0; + cmd->cmnd[2] = 0; + cmd->cmnd[3] = 0; + cmd->cmnd[4] = sizeof(cmd->sense_buffer); + cmd->cmnd[5] = 0; + cmd->SCp.buffer = NULL; + cmd->SCp.buffers_residual = 0; + cmd->SCp.ptr = (char *)cmd->sense_buffer; + cmd->SCp.this_residual = sizeof(cmd->sense_buffer); + LIST(cmd,hostdata->issue_queue); + cmd->host_scribble = (unsigned char *)hostdata->issue_queue; + hostdata->issue_queue = (Scsi_Cmnd *)cmd; + DEB_MSG(printk("scsi%d : REQUEST SENSE added to head of issue queue\n",instance->host_no)); + } + + /* Accept message by clearing ACK */ + AM53C974_write_8(CMDREG, CMDREG_MA); + break; + + case MESSAGE_REJECT: + DEB_MSG(printk("scsi%d: reject message received; cmd %d for target %d, lun %d\n", + instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun)); + switch (hostdata->last_message[0]) { + case EXTENDED_MESSAGE: + if (hostdata->last_message[2] == EXTENDED_SDTR) { + /* sync. negotiation was rejected, setup asynchronous transfer with target */ + printk("\ntarget %d: rate=%d Mhz, asynchronous (sync. negotiation rejected)\n", + cmd->target, DEF_CLK / DEF_STP); + hostdata->sync_off[cmd->target] = 0; + hostdata->sync_per[cmd->target] = DEF_STP; } + break; + case HEAD_OF_QUEUE_TAG: + case ORDERED_QUEUE_TAG: + case SIMPLE_QUEUE_TAG: + cmd->device->tagged_queue = 0; + hostdata->busy[cmd->target] |= (1 << cmd->lun); + break; + default: + break; + } + if (hostdata->aborted) AM53C974_write_8(CMDREG, CMDREG_SATN); + AM53C974_write_8(CMDREG, CMDREG_MA); + break; + + case DISCONNECT: + DEB_MSG(printk("scsi%d: disconnect message received; cmd %d for target %d, lun %d\n", + instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun)); + cmd->device->disconnect = 1; + hostdata->disconnecting = 1; + AM53C974_write_8(CMDREG, CMDREG_MA); /* Accept message by clearing ACK */ + break; + + case SAVE_POINTERS: + case RESTORE_POINTERS: + DEB_MSG(printk("scsi%d: save/restore pointers message received; cmd %d for target %d, lun %d\n", + instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun)); + /* The SCSI data pointer is *IMPLICITLY* saved on a disconnect + * operation, in violation of the SCSI spec so we can safely + * ignore SAVE/RESTORE pointers calls. + * + * Unfortunately, some disks violate the SCSI spec and + * don't issue the required SAVE_POINTERS message before + * disconnecting, and we have to break spec to remain + * compatible. */ + if (hostdata->aborted) { + DEB_ABORT(printk("ATN set for cmnd %d upon reception of SAVE/REST. POINTERS message\n", + cmd->cmnd[0])); + AM53C974_write_8(CMDREG, CMDREG_SATN); } + AM53C974_write_8(CMDREG, CMDREG_MA); + break; + + case EXTENDED_MESSAGE: + DEB_MSG(printk("scsi%d: extended message received; cmd %d for target %d, lun %d\n", + instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun)); + /* Extended messages are sent in the following format : + * Byte + * 0 EXTENDED_MESSAGE == 1 + * 1 length (includes one byte for code, doesn't include first two bytes) + * 2 code + * 3..length+1 arguments + */ + /* BEWARE!! THIS CODE IS EXTREMELY UGLY */ + extended_msg[0] = EXTENDED_MESSAGE; + AM53C974_read_8(INSTREG) ; /* clear int */ + AM53C974_write_8(CMDREG, CMDREG_MA); /* ack. msg byte, then wait for SO */ + AM53C974_poll_int(); + /* get length */ + AM53C974_write_8(CMDREG, CMDREG_IT); + AM53C974_poll_int(); + AM53C974_write_8(CMDREG, CMDREG_MA); /* ack. msg byte, then wait for SO */ + AM53C974_poll_int(); + extended_msg[1] = len = AM53C974_read_8(FFREG); /* get length */ + p = extended_msg+2; + /* read the remaining (len) bytes */ + while (len) { + AM53C974_write_8(CMDREG, CMDREG_IT); + AM53C974_poll_int(); + if (len > 1) { + AM53C974_write_8(CMDREG, CMDREG_MA); /* ack. msg byte, then wait for SO */ + AM53C974_poll_int(); } + *p = AM53C974_read_8(FFREG); + p++; len--; } + +#ifdef AM53C974_DEBUG_MSG + printk("scsi%d: received extended message: ", instance->host_no); + for (j = 0; j < extended_msg[1] + 2; j++) { + printk("0x%02x ", extended_msg[j]); + if (j && !(j % 16)) printk("\n"); } + printk("\n"); +#endif + + /* check message */ + if (extended_msg[2] == EXTENDED_SDTR) + ret = AM53C974_sync_neg(instance, cmd->target, extended_msg); + if (ret || hostdata->aborted) AM53C974_write_8(CMDREG, CMDREG_SATN); + + AM53C974_write_8(CMDREG, CMDREG_MA); + break; + + default: + printk("scsi%d: unknown message 0x%02x received\n",instance->host_no, msg); +#ifdef AM53C974_DEBUG + deb_stop = 1; +#endif + /* reject message */ + hostdata->msgout[0] = MESSAGE_REJECT; + AM53C974_write_8(CMDREG, CMDREG_SATN); + AM53C974_write_8(CMDREG, CMDREG_MA); + return(0); + break; + + } /* switch (msg) */ +KEYWAIT(); +return(1); +} + +/************************************************************************** +* Function : AM53C974_select(struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag) +* +* Purpose : try to establish nexus for the command; +* start sync negotiation via start stop and transfer the command in +* cmdout phase in case of an inquiry or req. sense command with no +* sync. neg. performed yet +* +* Inputs : instance -- which AM53C974 +* cmd -- command which requires the selection +* tag -- tagged queueing +* +* Returns : nothing +* +* Note: this function initializes the selection process, which is continued +* in the interrupt handler +**************************************************************************/ +static void AM53C974_select(struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag) +{ +AM53C974_local_declare(); +struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata; +unsigned char cfifo, tmp[3]; +unsigned int i, len, cmd_size = COMMAND_SIZE(cmd->cmnd[0]); +AM53C974_setio(instance); + +cfifo = AM53C974_cfifo(); +if (cfifo) { + printk("scsi%d: select error; %d residual bytes in FIFO\n", instance->host_no, cfifo); + AM53C974_write_8(CMDREG, CMDREG_CFIFO); /* clear FIFO */ + } + +tmp[0] = IDENTIFY(1, cmd->lun); + +#ifdef SCSI2 +if (cmd->device->tagged_queue && (tag != TAG_NONE)) { + tmp[1] = SIMPLE_QUEUE_TAG; + if (tag == TAG_NEXT) { + /* 0 is TAG_NONE, used to imply no tag for this command */ + if (cmd->device->current_tag == 0) cmd->device->current_tag = 1; + cmd->tag = cmd->device->current_tag; + cmd->device->current_tag++; } + else + cmd->tag = (unsigned char)tag; + tmp[2] = cmd->tag; + hostdata->last_message[0] = SIMPLE_QUEUE_TAG; + len = 3; + AM53C974_write_8(FFREG, tmp[0]); + AM53C974_write_8(FFREG, tmp[1]); + AM53C974_write_8(FFREG, tmp[2]); + } + else +#endif /* def SCSI2 */ + { + len = 1; + AM53C974_write_8(FFREG, tmp[0]); + cmd->tag = 0; } + +/* in case of an inquiry or req. sense command with no sync. neg performed yet, we start + sync negotiation via start stops and transfer the command in cmdout phase */ +if (((cmd->cmnd[0] == INQUIRY) || (cmd->cmnd[0] == REQUEST_SENSE)) && + !(hostdata->sync_neg[cmd->target]) && hostdata->sync_en[cmd->target]) { + hostdata->sync_neg[cmd->target] = 1; + hostdata->msgout[0] = EXTENDED_MESSAGE; + hostdata->msgout[1] = 3; + hostdata->msgout[2] = EXTENDED_SDTR; + hostdata->msgout[3] = 250 / (int)hostdata->max_rate[cmd->target]; + hostdata->msgout[4] = hostdata->max_offset[cmd->target]; + len += 5; } + +AM53C974_write_8(SDIDREG, SDIREG_MASK & cmd->target); /* setup dest. id */ +AM53C974_write_8(STIMREG, DEF_SCSI_TIMEOUT); /* setup timeout reg */ +switch (len) { + case 1: + for (i = 0; i < cmd_size; i++) AM53C974_write_8(FFREG, cmd->cmnd[i]); + AM53C974_write_8(CMDREG, CMDREG_SAS); /* select with ATN, 1 msg byte */ + hostdata->msgout[0] = NOP; + break; + case 3: + for (i = 0; i < cmd_size; i++) AM53C974_write_8(FFREG, cmd->cmnd[i]); + AM53C974_write_8(CMDREG, CMDREG_SA3S); /* select with ATN, 3 msg bytes */ + hostdata->msgout[0] = NOP; + break; + default: + AM53C974_write_8(CMDREG, CMDREG_SASS); /* select with ATN, stop steps; continue in message out phase */ + break; + } +} + +/************************************************************************** +* Function : AM53C974_intr_select(struct Scsi_Host *instance, unsigned char statreg) +* +* Purpose : handle reselection +* +* Inputs : instance -- which AM53C974 +* statreg -- status register +* +* Returns : nothing +* +* side effects: manipulates hostdata +**************************************************************************/ +static void AM53C974_intr_reselect(struct Scsi_Host *instance, unsigned char statreg) +{ +AM53C974_local_declare(); +struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata; +unsigned char cfifo, msg[3], lun, t, target = 0; +#ifdef SCSI2 + unsigned char tag; +#endif +Scsi_Cmnd *tmp = NULL, *prev; +AM53C974_setio(instance); + +cfifo = AM53C974_cfifo(); + +if (hostdata->selecting) { + /* caught reselect interrupt in selection process; + put selecting command back into the issue queue and continue with the + reselecting command */ + DEB_RESEL(printk("AM53C974_intr_reselect: in selection process\n")); + LIST(hostdata->sel_cmd, hostdata->issue_queue); + hostdata->sel_cmd->host_scribble = (unsigned char *)hostdata->issue_queue; + hostdata->issue_queue = hostdata->sel_cmd; + hostdata->sel_cmd = NULL; + hostdata->selecting = 0; } + +/* 2 bytes must be in the FIFO now */ +if (cfifo != 2) { + printk("scsi %d: error: %d bytes in fifo, 2 expected\n", instance->host_no, cfifo); + hostdata->aborted = 1; + goto EXIT_ABORT; } + +/* determine target which reselected */ +t = AM53C974_read_8(FFREG); +if (!(t & (1 << instance->this_id))) { + printk("scsi %d: error: invalid host id\n", instance->host_no); + hostdata->aborted = 1; + goto EXIT_ABORT; } +t ^= (1 << instance->this_id); +target = 0; while (t != 1) { t >>= 1; target++; } +DEB_RESEL(printk("scsi %d: reselect; target: %d\n", instance->host_no, target)); + +if (hostdata->aborted) goto EXIT_ABORT; + +if ((statreg & STATREG_PHASE) != PHASE_MSGIN) { + printk("scsi %d: error: upon reselection interrupt not in MSGIN\n", instance->host_no); + hostdata->aborted = 1; + goto EXIT_ABORT; } + +msg[0] = AM53C974_read_8(FFREG); +if (!msg[0] & 0x80) { + printk("scsi%d: error: expecting IDENTIFY message, got ", instance->host_no); + print_msg(msg); + hostdata->aborted = 1; + goto EXIT_ABORT; } + +lun = (msg[0] & 0x07); + +/* We need to add code for SCSI-II to track which devices have + * I_T_L_Q nexuses established, and which have simple I_T_L + * nexuses so we can chose to do additional data transfer. */ +#ifdef SCSI2 +#error "SCSI-II tagged queueing is not supported yet" +#endif + +/* Find the command corresponding to the I_T_L or I_T_L_Q nexus we + * just reestablished, and remove it from the disconnected queue. */ +for (tmp = (Scsi_Cmnd *)hostdata->disconnected_queue, prev = NULL; + tmp; prev = tmp, tmp = (Scsi_Cmnd *)tmp->host_scribble) + if ((target == tmp->target) && (lun == tmp->lun) +#ifdef SCSI2 + && (tag == tmp->tag) +#endif + ) { + if (prev) { + REMOVE(prev, (Scsi_Cmnd *)(prev->host_scribble), tmp, + (Scsi_Cmnd *)(tmp->host_scribble)); + prev->host_scribble = tmp->host_scribble; } + else { + REMOVE(-1, hostdata->disconnected_queue, tmp, tmp->host_scribble); + hostdata->disconnected_queue = (Scsi_Cmnd *)tmp->host_scribble; } + tmp->host_scribble = NULL; + hostdata->connected = tmp; + break; } + +if (!tmp) { +#ifdef SCSI2 + printk("scsi%d: warning : target %d lun %d tag %d not in disconnect_queue.\n", + instance->host_no, target, lun, tag); +#else + printk("scsi%d: warning : target %d lun %d not in disconnect_queue.\n", + instance->host_no, target, lun); +#endif + /* Since we have an established nexus that we can't do anything with, we must abort it. */ + hostdata->aborted = 1; + DEB(AM53C974_keywait()); + goto EXIT_ABORT; } + else + goto EXIT_OK; + +EXIT_ABORT: +AM53C974_write_8(CMDREG, CMDREG_SATN); +AM53C974_write_8(CMDREG, CMDREG_MA); +return; + +EXIT_OK: +DEB_RESEL(printk("scsi%d: nexus established, target = %d, lun = %d, tag = %d\n", + instance->host_no, target, tmp->lun, tmp->tag)); +AM53C974_set_sync(instance, target); +AM53C974_write_8(SDIDREG, SDIREG_MASK & target); /* setup dest. id */ +AM53C974_write_8(CMDREG, CMDREG_MA); +hostdata->dma_busy = 0; +hostdata->connected->SCp.phase = PHASE_CMDOUT; +} + +/************************************************************************** +* Function : AM53C974_transfer_dma(struct Scsi_Host *instance, short dir, +* unsigned long length, char *data) +* +* Purpose : setup DMA transfer +* +* Inputs : instance -- which AM53C974 +* dir -- direction flag, 0: write to device, read from memory; +* 1: read from device, write to memory +* length -- number of bytes to transfer to from buffer +* data -- pointer to data buffer +* +* Returns : nothing +**************************************************************************/ +static __inline__ void AM53C974_transfer_dma(struct Scsi_Host *instance, short dir, + unsigned long length, char *data) +{ +AM53C974_local_declare(); +AM53C974_setio(instance); + +AM53C974_write_8(CMDREG, CMDREG_NOP); +AM53C974_write_8(DMACMD, (dir << 7) | DMACMD_INTE_D); /* idle command */ +AM53C974_write_8(STCLREG, (unsigned char)(length & 0xff)); +AM53C974_write_8(STCMREG, (unsigned char)((length & 0xff00) >> 8)); +AM53C974_write_8(STCHREG, (unsigned char)((length & 0xff0000) >> 16)); +AM53C974_write_32(DMASTC, length & 0xffffff); +AM53C974_write_32(DMASPA, (unsigned long)data); +AM53C974_write_8(CMDREG, CMDREG_IT | CMDREG_DMA); +AM53C974_write_8(DMACMD, (dir << 7) | DMACMD_INTE_D | DMACMD_START); +} + +/************************************************************************** +* Function : AM53C974_dma_blast(struct Scsi_Host *instance, unsigned char dmastatus, +* unsigned char statreg) +* +* Purpose : cleanup DMA transfer +* +* Inputs : instance -- which AM53C974 +* dmastatus -- dma status register +* statreg -- status register +* +* Returns : nothing +**************************************************************************/ +static void AM53C974_dma_blast(struct Scsi_Host *instance, unsigned char dmastatus, + unsigned char statreg) +{ +AM53C974_local_declare(); +struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata; +unsigned long ctcreg; +int dir = statreg & STATREG_IO; +int cfifo, pio, i = 0; +AM53C974_setio(instance); + +do { + cfifo = AM53C974_cfifo(); + i++; + } while (cfifo && (i < 50000)); +pio = (i == 50000) ? 1: 0; + +if (statreg & STATREG_CTZ) { AM53C974_write_8(DMACMD, DMACMD_IDLE); return; } + +if (dmastatus & DMASTATUS_DONE) { AM53C974_write_8(DMACMD, DMACMD_IDLE); return; } + +AM53C974_write_8(DMACMD, ((dir << 7) & DMACMD_DIR) | DMACMD_BLAST); +while(!(AM53C974_read_8(DMASTATUS) & DMASTATUS_BCMPLT)) ; +AM53C974_write_8(DMACMD, DMACMD_IDLE); + +if (pio) { + /* transfer residual bytes via PIO */ + unsigned char *wac = (unsigned char *)AM53C974_read_32(DMAWAC); + printk("pio mode, residual=%d\n", AM53C974_read_8(CFIREG) & CFIREG_CF); + while (AM53C974_read_8(CFIREG) & CFIREG_CF) *(wac++) = AM53C974_read_8(FFREG); + } + +ctcreg = AM53C974_read_8(CTCLREG) | (AM53C974_read_8(CTCMREG) << 8) | + (AM53C974_read_8(CTCHREG) << 16); + +hostdata->connected->SCp.ptr += hostdata->connected->SCp.this_residual - ctcreg; +hostdata->connected->SCp.this_residual = ctcreg; +} + +/************************************************************************** +* Function : AM53C974_intr_bus_reset(struct Scsi_Host *instance) +* +* Purpose : handle bus reset interrupt +* +* Inputs : instance -- which AM53C974 +* +* Returns : nothing +**************************************************************************/ +static void AM53C974_intr_bus_reset(struct Scsi_Host *instance) +{ +AM53C974_local_declare(); +unsigned char cntlreg1; +AM53C974_setio(instance); + +AM53C974_write_8(CMDREG, CMDREG_CFIFO); +AM53C974_write_8(CMDREG, CMDREG_NOP); + +cntlreg1 = AM53C974_read_8(CNTLREG1); +AM53C974_write_8(CNTLREG1, cntlreg1 | CNTLREG1_DISR); +} + +/************************************************************************** +* Function : int AM53C974_abort(Scsi_Cmnd *cmd) +* +* Purpose : abort a command +* +* Inputs : cmd - the Scsi_Cmnd to abort, code - code to set the +* host byte of the result field to, if zero DID_ABORTED is +* used. +* +* Returns : 0 - success, -1 on failure. + **************************************************************************/ +int AM53C974_abort(Scsi_Cmnd *cmd) +{ +AM53C974_local_declare(); +struct Scsi_Host *instance = cmd->host; +struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata; +Scsi_Cmnd *tmp, **prev; + +#ifdef AM53C974_DEBUG + deb_stop = 1; +#endif +cli(); +AM53C974_setio(instance); + +DEB_ABORT(printk(SEPARATOR_LINE)); +DEB_ABORT(printk("scsi%d : AM53C974_abort called -- trouble starts!!\n", instance->host_no)); +DEB_ABORT(AM53C974_print(instance)); +DEB_ABORT(AM53C974_keywait()); + +/* Case 1 : If the command is the currently executing command, + we'll set the aborted flag and return control so that the + information transfer routine can exit cleanly. */ +if ((hostdata->connected == cmd) || (hostdata->sel_cmd == cmd)) { + DEB_ABORT(printk("scsi%d: aborting connected command\n", instance->host_no)); + hostdata->aborted = 1; + hostdata->msgout[0] = ABORT; + sti(); + return(SCSI_ABORT_PENDING); } + +/* Case 2 : If the command hasn't been issued yet, + we simply remove it from the issue queue. */ +for (prev = (Scsi_Cmnd **)&(hostdata->issue_queue), + tmp = (Scsi_Cmnd *)hostdata->issue_queue; tmp; + prev = (Scsi_Cmnd **)&(tmp->host_scribble), + tmp = (Scsi_Cmnd *)tmp->host_scribble) { + if (cmd == tmp) { + DEB_ABORT(printk("scsi%d : abort removed command from issue queue.\n", instance->host_no)); + REMOVE(5, *prev, tmp, tmp->host_scribble); + (*prev) = (Scsi_Cmnd *)tmp->host_scribble; + tmp->host_scribble = NULL; + tmp->result = DID_ABORT << 16; + sti(); + tmp->done(tmp); + return(SCSI_ABORT_SUCCESS); } +#ifdef AM53C974_DEBUG_ABORT + else { + if (prev == (Scsi_Cmnd **)tmp) + printk("scsi%d : LOOP\n", instance->host_no); + } +#endif + } + +/* Case 3 : If any commands are connected, we're going to fail the abort + * and let the high level SCSI driver retry at a later time or + * issue a reset. + * + * Timeouts, and therefore aborted commands, will be highly unlikely + * and handling them cleanly in this situation would make the common + * case of noresets less efficient, and would pollute our code. So, + * we fail. */ +if (hostdata->connected || hostdata->sel_cmd) { + DEB_ABORT(printk("scsi%d : abort failed, other command connected.\n", instance->host_no)); + sti(); + return(SCSI_ABORT_NOT_RUNNING); } + +/* Case 4: If the command is currently disconnected from the bus, and + * there are no connected commands, we reconnect the I_T_L or + * I_T_L_Q nexus associated with it, go into message out, and send + * an abort message. */ +for (tmp = (Scsi_Cmnd *)hostdata->disconnected_queue; tmp; + tmp = (Scsi_Cmnd *)tmp->host_scribble) { + if (cmd == tmp) { + DEB_ABORT(printk("scsi%d: aborting disconnected command\n", instance->host_no)); + hostdata->aborted = 1; + hostdata->msgout[0] = ABORT; + hostdata->selecting = 1; + hostdata->sel_cmd = tmp; + AM53C974_write_8(CMDREG, CMDREG_DSR); + sti(); + return(SCSI_ABORT_PENDING); } + } + +/* Case 5 : If we reached this point, the command was not found in any of + * the queues. + * + * We probably reached this point because of an unlikely race condition + * between the command completing successfully and the abortion code, + * so we won't panic, but we will notify the user in case something really + * broke. */ +DEB_ABORT(printk("scsi%d : abort failed, command not found.\n", instance->host_no)); +sti(); +return(SCSI_ABORT_NOT_RUNNING); +} + +/************************************************************************** +* Function : int AM53C974_reset(Scsi_Cmnd *cmd) +* +* Purpose : reset the SCSI controller and bus +* +* Inputs : cmd -- which command within the command block was responsible for the reset +* +* Returns : status (SCSI_ABORT_SUCCESS) +**************************************************************************/ +int AM53C974_reset(Scsi_Cmnd *cmd) +{ +AM53C974_local_declare(); +int i; +struct Scsi_Host *instance = cmd->host; +struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata; +AM53C974_setio(instance); + +cli(); +DEB(printk("AM53C974_reset called; ")); + +printk("AM53C974_reset called\n"); +AM53C974_print(instance); +AM53C974_keywait(); + +/* do hard reset */ +AM53C974_write_8(CMDREG, CMDREG_RDEV); +AM53C974_write_8(CMDREG, CMDREG_NOP); +hostdata->msgout[0] = NOP; +for (i = 0; i < 8; i++) { + hostdata->busy[i] = 0; + hostdata->sync_per[i] = DEF_STP; + hostdata->sync_off[i] = 0; + hostdata->sync_neg[i] = 0; } +hostdata->last_message[0] = NOP; +hostdata->sel_cmd = NULL; +hostdata->connected = NULL; +hostdata->issue_queue = NULL; +hostdata->disconnected_queue = NULL; +hostdata->in_reset = 0; +hostdata->aborted = 0; +hostdata->selecting = 0; +hostdata->disconnecting = 0; +hostdata->dma_busy = 0; + +/* reset bus */ +AM53C974_write_8(CNTLREG1, CNTLREG1_DISR | instance->this_id); /* disable interrupt upon SCSI RESET */ +AM53C974_write_8(CMDREG, CMDREG_RBUS); /* reset SCSI bus */ +udelay(40); +AM53C974_config_after_reset(instance); + +sti(); +cmd->result = DID_RESET << 16; +cmd->scsi_done(cmd); +return SCSI_ABORT_SUCCESS; +} diff -u --recursive --new-file v1.3.58/linux/drivers/scsi/AM53C974.h linux/drivers/scsi/AM53C974.h --- v1.3.58/linux/drivers/scsi/AM53C974.h Thu Jan 1 02:00:00 1970 +++ linux/drivers/scsi/AM53C974.h Tue Jan 23 21:34:30 1996 @@ -0,0 +1,413 @@ +/* AM53/79C974 (PCscsi) driver release 0.5 + * + * The architecture and much of the code of this device + * driver was originally developed by Drew Eckhardt for + * the NCR5380. The following copyrights apply: + * For the architecture and all parts similar to the NCR5380: + * Copyright 1993, Drew Eckhardt + * Visionary Computing + * (Unix and Linux consulting and custom programming) + * drew@colorado.edu + * +1 (303) 666-5836 + * + * The AM53C974_nobios_detect code was origininally developed by + * Robin Cutshaw (robin@xfree86.org) and is used here in a + * modified form. + * + * For the other parts: + * Copyright 1994, D. Frieauff + * EMail: fri@rsx42sun0.dofn.de + * Phone: x49-7545-8-2256 , x49-7541-42305 + */ + +/* + * $Log: AM53C974.h,v $ + */ + +#ifndef AM53C974_H +#define AM53C974_H + +#include + +/*************************************************************************************** +* Default setting of the controller's SCSI id. Edit and uncomment this only if your * +* BIOS does not correctly initialize the controller's SCSI id. * +* If you don't get a warning during boot, it is correctly initialized. * +****************************************************************************************/ +/* #define AM53C974_SCSI_ID 7 */ + +/*************************************************************************************** +* Default settings for sync. negotiation enable, transfer rate and sync. offset. * +* These settings can be replaced by LILO overrides (append) with the following syntax: * +* AM53C974=host-scsi-id, target-scsi-id, max-rate, max-offset * +* Sync. negotiation is disabled by default and will be enabled for those targets which * +* are specified in the LILO override * +****************************************************************************************/ +#define DEFAULT_SYNC_NEGOTIATION_ENABLED 0 /* 0 or 1 */ +#define DEFAULT_RATE 5 /* MHz, min: 3; max: 10 */ +#define DEFAULT_SYNC_OFFSET 0 /* bytes, min: 0; max: 15; use 0 for async. mode */ + + +/* --------------------- don't edit below here --------------------- */ + +#define AM53C974_DRIVER_REVISION_MAJOR 0 +#define AM53C974_DRIVER_REVISION_MINOR 5 +#define SEPARATOR_LINE \ +"--------------------------------------------------------------------------\n" + +/* debug control */ +/* #define AM53C974_DEBUG */ +/* #define AM53C974_DEBUG_MSG */ +/* #define AM53C974_DEBUG_KEYWAIT */ +/* #define AM53C974_DEBUG_INIT */ +/* #define AM53C974_DEBUG_QUEUE */ +/* #define AM53C974_DEBUG_INFO */ +/* #define AM53C974_DEBUG_LINKED */ +/* #define VERBOSE_AM53C974_DEBUG */ +/* #define AM53C974_DEBUG_INTR */ +/* #define AM53C974_DEB_RESEL */ +#define AM53C974_DEBUG_ABORT +/* #define AM53C974_OPTION_DEBUG_PROBE_ONLY */ + +/* special options/constants */ +#define DEF_CLK 40 /* chip clock freq. in MHz */ +#define MIN_PERIOD 4 /* for negotiation: min. number of clocks per cycle */ +#define MAX_PERIOD 13 /* for negotiation: max. number of clocks per cycle */ +#define MAX_OFFSET 15 /* for negotiation: max. offset (0=async) */ + +#define DEF_SCSI_TIMEOUT 245 /* STIMREG value, 40 Mhz */ +#define DEF_STP 8 /* STPREG value assuming 5.0 MB/sec, FASTCLK, FASTSCSI */ +#define DEF_SOF_RAD 0 /* REQ/ACK deassertion delay */ +#define DEF_SOF_RAA 0 /* REQ/ACK assertion delay */ +#define DEF_ETM 0 /* CNTLREG1, ext. timing mode */ +#define DEF_PERE 1 /* CNTLREG1, parity error reporting */ +#define DEF_CLKF 0 /* CLKFREG, 0=40 Mhz */ +#define DEF_ENF 1 /* CNTLREG2, enable features */ +#define DEF_ADIDCHK 0 /* CNTLREG3, additional ID check */ +#define DEF_FASTSCSI 1 /* CNTLREG3, fast SCSI */ +#define DEF_FASTCLK 1 /* CNTLREG3, fast clocking, 5 MB/sec at 40MHz chip clk */ +#define DEF_GLITCH 1 /* CNTLREG4, glitch eater, 0=12ns, 1=35ns, 2=25ns, 3=off */ +#define DEF_PWD 0 /* CNTLREG4, reduced power feature */ +#define DEF_RAE 0 /* CNTLREG4, RAE active negation on REQ, ACK only */ +#define DEF_RADE 1 /* 1CNTLREG4, active negation on REQ, ACK and data */ + +/*** PCI block ***/ +/* standard registers are defined in */ +#ifndef PCI_VENDOR_ID_AMD +#define PCI_VENDOR_ID_AMD 0x1022 +#define PCI_DEVICE_ID_AMD_SCSI 0x2020 +#endif +#define PCI_BASE_MASK 0xFFFFFFE0 +#define PCI_COMMAND_PERREN 0x40 +#define PCI_SCRATCH_REG_0 0x40 /* 16 bits */ +#define PCI_SCRATCH_REG_1 0x42 /* 16 bits */ +#define PCI_SCRATCH_REG_2 0x44 /* 16 bits */ +#define PCI_SCRATCH_REG_3 0x46 /* 16 bits */ +#define PCI_SCRATCH_REG_4 0x48 /* 16 bits */ +#define PCI_SCRATCH_REG_5 0x4A /* 16 bits */ +#define PCI_SCRATCH_REG_6 0x4C /* 16 bits */ +#define PCI_SCRATCH_REG_7 0x4E /* 16 bits */ + +/*** SCSI block ***/ +#define CTCLREG 0x00 /* r current transf. count, low byte */ +#define CTCMREG 0x04 /* r current transf. count, middle byte */ +#define CTCHREG 0x38 /* r current transf. count, high byte */ +#define STCLREG 0x00 /* w start transf. count, low byte */ +#define STCMREG 0x04 /* w start transf. count, middle byte */ +#define STCHREG 0x38 /* w start transf. count, high byte */ +#define FFREG 0x08 /* rw SCSI FIFO reg. */ +#define STIMREG 0x14 /* w SCSI timeout reg. */ + +#define SDIDREG 0x10 /* w SCSI destination ID reg. */ +#define SDIREG_MASK 0x07 /* mask */ + +#define STPREG 0x18 /* w synchronous transf. period reg. */ +#define STPREG_STP 0x1F /* synchr. transfer period */ + +#define CLKFREG 0x24 /* w clock factor reg. */ +#define CLKFREG_MASK 0x07 /* mask */ + +#define CMDREG 0x0C /* rw SCSI command reg. */ +#define CMDREG_DMA 0x80 /* set DMA mode (set together with opcodes below) */ +#define CMDREG_IT 0x10 /* information transfer */ +#define CMDREG_ICCS 0x11 /* initiator command complete steps */ +#define CMDREG_MA 0x12 /* message accepted */ +#define CMDREG_TPB 0x98 /* transfer pad bytes, DMA mode only */ +#define CMDREG_SATN 0x1A /* set ATN */ +#define CMDREG_RATN 0x1B /* reset ATN */ +#define CMDREG_SOAS 0x41 /* select without ATN steps */ +#define CMDREG_SAS 0x42 /* select with ATN steps (1 msg byte) */ +#define CMDREG_SASS 0x43 /* select with ATN and stop steps */ +#define CMDREG_ESR 0x44 /* enable selection/reselection */ +#define CMDREG_DSR 0x45 /* disable selection/reselection */ +#define CMDREG_SA3S 0x46 /* select with ATN 3 steps (3 msg bytes) */ +#define CMDREG_NOP 0x00 /* no operation */ +#define CMDREG_CFIFO 0x01 /* clear FIFO */ +#define CMDREG_RDEV 0x02 /* reset device */ +#define CMDREG_RBUS 0x03 /* reset SCSI bus */ + +#define STATREG 0x10 /* r SCSI status reg. */ +#define STATREG_INT 0x80 /* SCSI interrupt condition detected */ +#define STATREG_IOE 0x40 /* SCSI illegal operation error detected */ +#define STATREG_PE 0x20 /* SCSI parity error detected */ +#define STATREG_CTZ 0x10 /* CTC reg decremented to zero */ +#define STATREG_MSG 0x04 /* SCSI MSG phase (latched?) */ +#define STATREG_CD 0x02 /* SCSI C/D phase (latched?) */ +#define STATREG_IO 0x01 /* SCSI I/O phase (latched?) */ +#define STATREG_PHASE 0x07 /* SCSI phase mask */ + +#define INSTREG 0x14 /* r interrupt status reg. */ +#define INSTREG_SRST 0x80 /* SCSI reset detected */ +#define INSTREG_ICMD 0x40 /* SCSI invalid command detected */ +#define INSTREG_DIS 0x20 /* target disconnected or sel/resel timeout*/ +#define INSTREG_SR 0x10 /* device on bus has service request */ +#define INSTREG_SO 0x08 /* successful operation */ +#define INSTREG_RESEL 0x04 /* device reselected as initiator */ + +#define ISREG 0x18 /* r internal state reg. */ +#define ISREG_SOF 0x08 /* synchronous offset flag (act. low) */ +#define ISREG_IS 0x07 /* status of intermediate op. */ +#define ISREG_OK_NO_STOP 0x04 /* selection successful */ +#define ISREG_OK_STOP 0x01 /* selection successful */ + +#define CFIREG 0x1C /* r current FIFO/internal state reg. */ +#define CFIREG_IS 0xE0 /* status of intermediate op. */ +#define CFIREG_CF 0x1F /* number of bytes in SCSI FIFO */ + +#define SOFREG 0x1C /* w synchr. offset reg. */ +#define SOFREG_RAD 0xC0 /* REQ/ACK deassertion delay (sync.) */ +#define SOFREG_RAA 0x30 /* REQ/ACK assertion delay (sync.) */ +#define SOFREG_SO 0x0F /* synch. offset (sync.) */ + +#define CNTLREG1 0x20 /* rw control register one */ +#define CNTLREG1_ETM 0x80 /* set extended timing mode */ +#define CNTLREG1_DISR 0x40 /* disable interrupt on SCSI reset */ +#define CNTLREG1_PERE 0x10 /* enable parity error reporting */ +#define CNTLREG1_SID 0x07 /* host adapter SCSI ID */ + +#define CNTLREG2 0x2C /* rw control register two */ +#define CNTLREG2_ENF 0x40 /* enable features */ + +#define CNTLREG3 0x30 /* rw control register three */ +#define CNTLREG3_ADIDCHK 0x80 /* additional ID check */ +#define CNTLREG3_FASTSCSI 0x10 /* fast SCSI */ +#define CNTLREG3_FASTCLK 0x08 /* fast SCSI clocking */ + +#define CNTLREG4 0x34 /* rw control register four */ +#define CNTLREG4_GLITCH 0xC0 /* glitch eater */ +#define CNTLREG4_PWD 0x20 /* reduced power feature */ +#define CNTLREG4_RAE 0x08 /* write only, active negot. ctrl. */ +#define CNTLREG4_RADE 0x04 /* active negot. ctrl. */ +#define CNTLREG4_RES 0x10 /* reserved bit, must be 1 */ + +/*** DMA block ***/ +#define DMACMD 0x40 /* rw command */ +#define DMACMD_DIR 0x80 /* transfer direction (1=read from device) */ +#define DMACMD_INTE_D 0x40 /* DMA transfer interrupt enable */ +#define DMACMD_INTE_P 0x20 /* page transfer interrupt enable */ +#define DMACMD_MDL 0x10 /* map to memory descriptor list */ +#define DMACMD_DIAG 0x04 /* diagnostics, set to 0 */ +#define DMACMD_IDLE 0x00 /* idle cmd */ +#define DMACMD_BLAST 0x01 /* flush FIFO to memory */ +#define DMACMD_ABORT 0x02 /* terminate DMA */ +#define DMACMD_START 0x03 /* start DMA */ + +#define DMASTATUS 0x54 /* r status register */ +#define DMASTATUS_BCMPLT 0x20 /* BLAST complete */ +#define DMASTATUS_SCSIINT 0x10 /* SCSI interrupt pending */ +#define DMASTATUS_DONE 0x08 /* DMA transfer terminated */ +#define DMASTATUS_ABORT 0x04 /* DMA transfer aborted */ +#define DMASTATUS_ERROR 0x02 /* DMA transfer error */ +#define DMASTATUS_PWDN 0x02 /* power down indicator */ + +#define DMASTC 0x44 /* rw starting transfer count */ +#define DMASPA 0x48 /* rw starting physical address */ +#define DMAWBC 0x4C /* r working byte counter */ +#define DMAWAC 0x50 /* r working address counter */ +#define DMASMDLA 0x58 /* rw starting MDL address */ +#define DMAWMAC 0x5C /* r working MDL counter */ + +/*** SCSI phases ***/ +#define PHASE_MSGIN 0x07 +#define PHASE_MSGOUT 0x06 +#define PHASE_RES_1 0x05 +#define PHASE_RES_0 0x04 +#define PHASE_STATIN 0x03 +#define PHASE_CMDOUT 0x02 +#define PHASE_DATAIN 0x01 +#define PHASE_DATAOUT 0x00 + +struct AM53C974_hostdata { + volatile unsigned in_reset:1; /* flag, says bus reset pending */ + volatile unsigned aborted:1; /* flag, says aborted */ + volatile unsigned selecting:1; /* selection started, but not yet finished */ + volatile unsigned disconnecting: 1; /* disconnection started, but not yet finished */ + volatile unsigned dma_busy:1; /* dma busy when service request for info transfer received */ + volatile unsigned char msgout[10]; /* message to output in MSGOUT_PHASE */ + volatile unsigned char last_message[10]; /* last message OUT */ + volatile Scsi_Cmnd *issue_queue; /* waiting to be issued */ + volatile Scsi_Cmnd *disconnected_queue; /* waiting for reconnect */ + volatile Scsi_Cmnd *sel_cmd; /* command for selection */ + volatile Scsi_Cmnd *connected; /* currently connected command */ + volatile unsigned char busy[8]; /* index = target, bit = lun */ + unsigned char sync_per[8]; /* synchronous transfer period (in effect) */ + unsigned char sync_off[8]; /* synchronous offset (in effect) */ + unsigned char sync_neg[8]; /* sync. negotiation performed (in effect) */ + unsigned char sync_en[8]; /* sync. negotiation performed (in effect) */ + unsigned char max_rate[8]; /* max. transfer rate (setup) */ + unsigned char max_offset[8]; /* max. sync. offset (setup), only valid if corresponding sync_en is nonzero */ + }; + +#define AM53C974 { \ + NULL, /* pointer to next in list */ \ + NULL, /* long * usage_count */ \ + NULL, /* struct proc_dir_entry *proc_dir */ \ + NULL, /* int (*proc_info)(char *, char **, off_t, int, int, int); */ \ + "AM53C974", /* name */ \ + AM53C974_detect, /* int (* detect)(struct SHT *) */ \ + NULL, /* int (*release)(struct Scsi_Host *) */ \ + AM53C974_info, /* const char *(* info)(struct Scsi_Host *) */ \ + AM53C974_command, /* int (* command)(Scsi_Cmnd *) */ \ + AM53C974_queue_command, /* int (* queuecommand)(Scsi_Cmnd *, \ + void (*done)(Scsi_Cmnd *)) */ \ + AM53C974_abort, /* int (* abort)(Scsi_Cmnd *) */ \ + AM53C974_reset, /* int (* reset)(Scsi_Cmnd *) */ \ + NULL, /* int (* slave_attach)(int, int) */ \ + scsicam_bios_param, /* int (* bios_param)(Disk *, int, int[]) */ \ + 12, /* can_queue */ \ + -1, /* this_id */ \ + SG_ALL, /* sg_tablesize */ \ + 1, /* cmd_per_lun */ \ + 0, /* present, i.e. how many adapters of this kind */ \ + 0, /* unchecked_isa_dma */ \ + DISABLE_CLUSTERING /* use_clustering */ \ + } + +void AM53C974_setup(char *str, int *ints); +int AM53C974_detect(Scsi_Host_Template *tpnt); +int AM53C974_biosparm(Disk *disk, int dev, int *info_array); +const char *AM53C974_info(struct Scsi_Host *); +int AM53C974_command(Scsi_Cmnd *SCpnt); +int AM53C974_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)); +int AM53C974_abort(Scsi_Cmnd *cmd); +int AM53C974_reset (Scsi_Cmnd *cmd); + +#define AM53C974_local_declare() unsigned long io_port +#define AM53C974_setio(instance) io_port = instance->io_port +#define AM53C974_read_8(addr) inb(io_port + (addr)) +#define AM53C974_write_8(addr,x) outb((x), io_port + (addr)) +#define AM53C974_read_16(addr) inw(io_port + (addr)) +#define AM53C974_write_16(addr,x) outw((x), io_port + (addr)) +#define AM53C974_read_32(addr) inl(io_port + (addr)) +#define AM53C974_write_32(addr,x) outl((x), io_port + (addr)) + +#define AM53C974_poll_int() { do { statreg = AM53C974_read_8(STATREG); } \ + while (!(statreg & STATREG_INT)) ; \ + AM53C974_read_8(INSTREG) ; } /* clear int */ +#define AM53C974_cfifo() (AM53C974_read_8(CFIREG) & CFIREG_CF) + +/* These are "special" values for the tag parameter passed to AM53C974_select. */ +#define TAG_NEXT -1 /* Use next free tag */ +#define TAG_NONE -2 /* Establish I_T_L nexus instead of I_T_L_Q + * even on SCSI-II devices */ + +/************ LILO overrides *************/ +typedef struct _override_t { + int host_scsi_id; /* SCSI id of the bus controller */ + int target_scsi_id; /* SCSI id of target */ + int max_rate; /* max. transfer rate */ + int max_offset; /* max. sync. offset, 0 = asynchronous */ + } override_t; + +/************ PCI stuff *************/ +#define AM53C974_PCIREG_OPEN() outb(0xF1, 0xCF8); outb(0, 0xCFA) +#define AM53C974_PCIREG_CLOSE() outb(0, 0xCF8) +#define AM53C974_PCIREG_READ_BYTE(instance,a) ( inb((a) + (instance)->io_port) ) +#define AM53C974_PCIREG_READ_WORD(instance,a) ( inw((a) + (instance)->io_port) ) +#define AM53C974_PCIREG_READ_DWORD(instance,a) ( inl((a) + (instance)->io_port) ) +#define AM53C974_PCIREG_WRITE_BYTE(instance,x,a) ( outb((x), (a) + (instance)->io_port) ) +#define AM53C974_PCIREG_WRITE_WORD(instance,x,a) ( outw((x), (a) + (instance)->io_port) ) +#define AM53C974_PCIREG_WRITE_DWORD(instance,x,a) ( outl((x), (a) + (instance)->io_port) ) + +typedef struct _pci_config_t { + /* start of official PCI config space header */ + union { + unsigned int device_vendor; + struct { + unsigned short vendor; + unsigned short device; + } dv; + } dv_id; +#define _device_vendor dv_id.device_vendor +#define _vendor dv_id.dv.vendor +#define _device dv_id.dv.device + union { + unsigned int status_command; + struct { + unsigned short command; + unsigned short status; + } sc; + } stat_cmd; +#define _status_command stat_cmd.status_command +#define _command stat_cmd.sc.command +#define _status stat_cmd.sc.status + union { + unsigned int class_revision; + struct { + unsigned char rev_id; + unsigned char prog_if; + unsigned char sub_class; + unsigned char base_class; + } cr; + } class_rev; +#define _class_revision class_rev.class_revision +#define _rev_id class_rev.cr.rev_id +#define _prog_if class_rev.cr.prog_if +#define _sub_class class_rev.cr.sub_class +#define _base_class class_rev.cr.base_class + union { + unsigned int bist_header_latency_cache; + struct { + unsigned char cache_line_size; + unsigned char latency_timer; + unsigned char header_type; + unsigned char bist; + } bhlc; + } bhlc; +#define _bist_header_latency_cache bhlc.bist_header_latency_cache +#define _cache_line_size bhlc.bhlc.cache_line_size +#define _latency_timer bhlc.bhlc.latency_timer +#define _header_type bhlc.bhlc.header_type +#define _bist bhlc.bhlc.bist + unsigned int _base0; + unsigned int _base1; + unsigned int _base2; + unsigned int _base3; + unsigned int _base4; + unsigned int _base5; + unsigned int rsvd1; + unsigned int rsvd2; + unsigned int _baserom; + unsigned int rsvd3; + unsigned int rsvd4; + union { + unsigned int max_min_ipin_iline; + struct { + unsigned char int_line; + unsigned char int_pin; + unsigned char min_gnt; + unsigned char max_lat; + } mmii; + } mmii; +#define _max_min_ipin_iline mmii.max_min_ipin_iline +#define _int_line mmii.mmii.int_line +#define _int_pin mmii.mmii.int_pin +#define _min_gnt mmii.mmii.min_gnt +#define _max_lat mmii.mmii.max_lat + /* end of official PCI config space header */ + unsigned short _ioaddr; /* config type 1 - private I/O addr */ + unsigned int _pcibus; /* config type 2 - private bus id */ + unsigned int _cardnum; /* config type 2 - private card number */ +} pci_config_t; + +#endif /* AM53C974_H */ diff -u --recursive --new-file v1.3.58/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in --- v1.3.58/linux/drivers/scsi/Config.in Tue Jan 23 21:15:42 1996 +++ linux/drivers/scsi/Config.in Tue Jan 23 21:35:41 1996 @@ -37,5 +37,6 @@ dep_tristate '7000FASST SCSI support' CONFIG_SCSI_7000FASST $CONFIG_SCSI dep_tristate 'EATA ISA/EISA (DPT PM2011/021/012/022/122/322) support' CONFIG_SCSI_EATA $CONFIG_SCSI dep_tristate 'NCR53c406a SCSI support' CONFIG_SCSI_NCR53C406A $CONFIG_SCSI +bool 'AM53/79C974 PCI SCSI support' CONFIG_SCSI_AM53C974 #dep_tristate 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG $CONFIG_SCSI endmenu diff -u --recursive --new-file v1.3.58/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile --- v1.3.58/linux/drivers/scsi/Makefile Tue Jan 23 21:15:42 1996 +++ linux/drivers/scsi/Makefile Tue Jan 23 21:34:30 1996 @@ -76,9 +76,9 @@ ifeq ($(CONFIG_SCSI_ADVANSYS),y) L_OBJS += advansys.o else - ifeq ($(CONFIG_SCSI_ADVANSYS),m) - M_OBJS += advansys.o - endif + ifeq ($(CONFIG_SCSI_ADVANSYS),m) + M_OBJS += advansys.o + endif endif ifeq ($(CONFIG_SCSI_QLOGIC),y) @@ -119,6 +119,10 @@ ifeq ($(CONFIG_SCSI_AIC7XXX),m) M_OBJS += aic7xxx.o endif +endif + +ifeq ($(CONFIG_SCSI_AM53C974),y) +L_OBJS += AM53C974.o endif ifeq ($(CONFIG_SCSI_BUSLOGIC),y) diff -u --recursive --new-file v1.3.58/linux/drivers/scsi/README.AM53C974 linux/drivers/scsi/README.AM53C974 --- v1.3.58/linux/drivers/scsi/README.AM53C974 Thu Jan 1 02:00:00 1970 +++ linux/drivers/scsi/README.AM53C974 Tue Jan 23 21:34:30 1996 @@ -0,0 +1,232 @@ +SUBJECT +------- +AM53/79C974 PC-SCSI Driver + + +DISCLAIMER +---------- +*** THIS SHOULD BE CONSIDERED AS BETA SOFTWARE *** +*** USE AT YOUR OWN RISK! *** + + +Copyright +--------- +The architecture and much of the code of this device driver was +originally developed by Drew Eckhardt for the NCR5380. The +following copyrights apply: + +For the architecture and all pieces of code which can also be found +in the NCR5380 device driver: +Copyright 1993, Drew Eckhardt + Visionary Computing + (Unix and Linux consulting and custom programming) + drew@colorado.edu + +1 (303) 666-5836 + +The AM53C974_nobios_detect code was origininally developed by +Robin Cutshaw (robin@xfree86.org) and is used here in a +slightly modified form. + +For the remaining code: + Copyright 1994, D. Frieauff + EMail: fri@rsx42sun0.dofn.de + Phone: x49-7545-8-2256 , x49-7541-42305 + + +Version +------- +AM53/79C974 (PC-SCSI) Linux driver ALPHA release 0.5, 19 November 1995 + + +Changelog +--------- +0.1 -> 0.2: + - Extended message handling re-written to eliminate 'invalid message 17' bug + - Parameters of AM53C974_intr adapted + - Debug messages structured + - Spelling improved +0.2 -> 0.3: + - README file updated -- please read this file up to the end! + - Automatic scanning of io_port and irq implemented; no need for BIOS32 + anymore + - Improved configuration (now via LILO parameter string) + - Cleanup of probing and initialisation code + - Improved sync. negotiation (can be setup individually for every device) + - Improved/ debugged code for reception of ext. messages +0.3 -> 0.4: + - Improved PCI probing and initialisation code + - Compatibility changes for Linux 1.3.x +0.4 -> 0.5: + - Compatibility changes for Linux 1.3.42 + +Bugs & Todo +----------- + - Add proc info function + - Support SCSI-2 tagged queuing + - Finalize abort code + +Features +-------- +This driver supports asynchronous and synchronous SCSI-I and SCSI-II devices. +It is capable of transfer rate and synchronous negotiation (see below). +The driver supportes scatter-gather. Transfers are DMA based, but do not +(yet) make use of the AM53/79C974 MDL mode. +Max. transfer rate is 10MHz (whatever this is in real life). The transfer +rate is negotiated with each device (see dmesg output). +The AM53/79C974 has a 96-byte DMA FIFO to the PCI bus and a 16-byte SCSI +FIFO. It provides active negation and glitch suppression functions. +Burst DMA transfer rate is 132 MBytes/sec. + + +Configuration +------------- +The following communication characteristics can be set individually for every +SCSI device on the bus: + - enable/disable sync. negotiation + - transfer rate + - asynchronous or synchronous communication + - in case of sync. communication, the sync. offset + +The sync. offset specifies the number of bytes that can be sent or +reveived from the SCSI bus without ACK resp. REQ signal. +CAUTION: USING SYNCHRONOUS MODE ON LONG SCSI CABLES MAY CAUSE + COMMUNICATION PROBLEMS LEADING TO LOSS OF DATA. + +The default setting of the SCSI communication parameters is as follows: + - no negotiation + - 5.0 MHz transfer rate + - asynchronous mode + - zero offset +The parameters can be modified by passing a string with the following syntax to +the kernel: AM53C974=host-scsi-id,target-scsi-id,max-rate,max-offset +The parameters will be used by the driver as negotiation basis. +The range of the rate parameter is 3 to 10 MHz. +The range of the sync. offset parameter is 0 to 15 bytes. A value of 0 denotes +asynchronous comm. mode. +If the target cannot cope with the specified transfer rate, sync. mode or sync. +offset, the negotiation result will differ from the specified values. +The negotiation result is printed out at the end of the negotiation process +(to read it, use the dmesg program or the appropriate syslog). +The parameter strings (blank separated) can be passed to the kernel at the +LILO prompt, or as part of the LILO configuration file. + +For example, the string "AM53C974=7,2,8,15" would be interpreted as follows: + "For communication between the controller with SCSI-ID 7 and the device with + SCSI-ID 2 a transfer rate of 8MHz in synchronous mode with max. 15 bytes offset + should be negotiated". + +As an example, here my LILO configuration file: + boot = /dev/sda + compact + #prompt + delay = 50 # optional, for systems that boot very quickly + vga = normal # force sane state + ramdisk = 0 # paranoia setting + root = current # use "current" root + image = /usr/src/linux/arch/i386/boot/zImage + label = linux + append = "AM53C974=7,0,10,0 AM53C974=7,1,10,0 AM53C974=7,2,10,15 AM53C974=7,4,10,0 AM53C974=7,5,10,0" + read-only + other = /dev/sda4 + label = os2 + other = /dev/sdb3 + loader = /boot/any_d.b + table = /dev/sdb + label = setup + +The same parameters at the LILO prompt: + LILO boot: linux AM53C974=7,0,10,0 AM53C974=7,1,10,0 AM53C974=7,2,10,15 AM53C974=7,4,10,0 AM53C974=7,5,10,0 + +You can override parameters specified in the LILO configuration file by +parameters specified on the LILO command line. + + +BIOS usage +---------- +Version 0.4 of the driver will use the BIOS, if available. Otherwise +it will try its internal PCI scan and access routines. +The driver assumes that the controller's SCSI-ID (usually 7) has been +correctly loaded by the BIOS into the controller's register during +system boot. If the driver detects that the controller's SCSI ID is not +'7' it will print out a warning. If this happens to you please correct +seeting of the controller's SCSI-ID. If it is wrong, then edit the +AM53C974_SCSI_ID definition in file AM53C974.h accordingly. + + +Test environment +---------------- +This driver was tested on a Compaq XL566 with the following SCSI configuration: +2 x HP C2247 fixed disk (internal, rate=10MHz, async.) +1 x Micropolis 1624 fixed disk (external, rate=8MHz, sync., offset=15 bytes) +1 x Wangtek W5525ES cartridge streamer (internal, rate=5MHz, async.) +1 x Toshiba XM-3301B CD-ROM (external, rate=5MHz, async.) + + +Known problems +-------------- + - Compaq/Matsushita CD-ROM: + Use of this device with AM53C974 driver version 0.2 caused the kernel to + hang during Linux boot. If you encounter the problem, don't enable sync. + negotiation with the CD-ROM, i.e. simply don't specify comm. parameters + for this device on the LILO commandline or configuration file. + The driver will thus use its default for the CD-ROM, which is 5MHz + transfer rate asynch and no sync. negotiation. + - Some disks cause problems. + + +What to do if there is a SCSI problem possibly related to the driver +-------------------------------------------------------------------- +Read Klaus Liedl's WWW page (http://www-c724.uibk.ac.at/XL/). +In case this does not help: +Send me a complete description of the problem, including your SCSI +configuration plus as much debugging information as possible. +Don't wait until I ask you for this information. To enable the +generation of debugging output, remove the comments from the following +definitions in the AM53C974.h file: + AM53C974_DEBUG + AM53C974_DEBUG_MSG + AM53C974_DEBUG_KEYWAIT + AM53C974_DEBUG_INFO + AM53C974_DEBUG_INTR +With these definitions enabled, the driver will enter single-step mode during +Linux boot. Use the spacebar for stepping. +Take note of at least the last 10 printout sections (marked by dashes) before +the crash/hangup or whatever happens and send me all of this information via +email. If the system can boot, use the syslogd daemon to record the debugging +output. Maybe you can use the ramdisk for this purpose too (if necessary, kindly +ask K. Liedl (Klaus.Liedl@uibk.ac.at) for support, he knows how to do it -- +I never tried). Stay in email contact with me. Be aware that the following +weeks/months could be the worst of your life. +Note: If single-stepping takes up too much time, you can try to let the driver +catch the problem by pressing the 'r' key. The driver will automatically enter +single-step mode if it has detected something weird. + + +Author's Contact Adress +----------------------- +Email: fri@rsx42sun0.dofn.de +Phone: x49-7545-2256 (office), x49-7541-42305 (home) +Home address: D. Frieauff, Stockerholzstr. 27, 88048 Friedrichshafen, Germany + + +!!!! Important Notice !!!! +----------------------------- +- Klaus Liedl maintains an excellent WWW page about Linux on Compaq XL. + It includes an FAQ, lots of tips & tricks as well as downloadable + boot disk images. The URL is: http://www-c724.uibk.ac.at/XL/ +- Volunteer wanted for further maintenance of this driver software. I + don't have the time anymore to do serious support as some of you will know. + +Literature +---------- + - AMD AM53C974 PC-SCSI Technical Manual, publication #18624B + - Amendment to the AMD AM53C974 PC-SCSI Technical Manual + - AMD AM79C974 PC-NET Datasheet, publication #18681 + - Amendment to the AMD AM79C974 PC-NET Datasheet + + +THANKS to +--------- + - Drew Eckhardt, Robin Cutshaw, K. Liedl, Robert J. Pappas, A. Grenier, + Mark Stockton, David C. Niemi, Ben Craft, and many others who have helped + diff -u --recursive --new-file v1.3.58/linux/drivers/scsi/aha152x.c linux/drivers/scsi/aha152x.c --- v1.3.58/linux/drivers/scsi/aha152x.c Sun Jan 14 16:30:11 1996 +++ linux/drivers/scsi/aha152x.c Tue Jan 23 21:20:34 1996 @@ -20,9 +20,12 @@ * General Public License for more details. * * - * $Id: aha152x.c,v 1.13 1996/01/09 02:15:53 fischer Exp $ + * $Id: aha152x.c,v 1.14 1996/01/17 15:11:20 fischer Exp fischer $ * * $Log: aha152x.c,v $ + * Revision 1.14 1996/01/17 15:11:20 fischer + * - fixed lockup in MESSAGE IN phase after reconnection + * * Revision 1.13 1996/01/09 02:15:53 fischer * - some cleanups * - moved request_irq behind controller initialization @@ -1402,8 +1405,6 @@ aha152x_panic(shpnt, "unknown lun"); } - make_acklow(shpnt); - getphase(shpnt); #if defined(DEBUG_QUEUES) if(HOSTDATA(shpnt)->debug & debug_queues) @@ -1430,6 +1431,8 @@ CURRENT_SC->SCp.phase &= ~disconnected; restore_flags(flags); + make_acklow(shpnt); + if(getphase(shpnt)!=P_MSGI) { SETPORT(SIMODE0, 0); SETPORT(SIMODE1, ENPHASEMIS|ENBUSFREE); #if defined(DEBUG_RACE) @@ -1438,6 +1441,7 @@ SETBITS(DMACNTRL0, INTEN); return; } + } /* Check, if we aren't busy with a command */ if(!CURRENT_SC) @@ -1946,6 +1950,7 @@ printk("d+, "); #endif append_SC(&DISCONNECTED_SC, CURRENT_SC); + CURRENT_SC->SCp.phase |= 1<<16; CURRENT_SC = NULL; restore_flags(flags); diff -u --recursive --new-file v1.3.58/linux/drivers/scsi/aha152x.h linux/drivers/scsi/aha152x.h --- v1.3.58/linux/drivers/scsi/aha152x.h Sun Jan 14 16:30:11 1996 +++ linux/drivers/scsi/aha152x.h Tue Jan 23 21:20:34 1996 @@ -2,7 +2,7 @@ #define _AHA152X_H /* - * $Id: aha152x.h,v 1.13 1995/12/16 12:27:23 fischer Exp $ + * $Id: aha152x.h,v 1.14 1996/01/17 15:13:36 fischer Exp fischer $ */ #if defined(__KERNEL__) @@ -23,7 +23,7 @@ (unless we support more than 1 cmd_per_lun this should do) */ #define AHA152X_MAXQUEUE 7 -#define AHA152X_REVID "Adaptec 152x SCSI driver; $Revision: 1.13 $" +#define AHA152X_REVID "Adaptec 152x SCSI driver; $Revision: 1.14 $" extern struct proc_dir_entry proc_scsi_aha152x; diff -u --recursive --new-file v1.3.58/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c --- v1.3.58/linux/drivers/scsi/hosts.c Tue Jan 23 21:15:43 1996 +++ linux/drivers/scsi/hosts.c Tue Jan 23 21:34:30 1996 @@ -125,6 +125,10 @@ #include "NCR53c406a.h" #endif +#ifdef CONFIG_SCSI_AM53C974 +#include "AM53C974.h" +#endif + #ifdef CONFIG_SCSI_DEBUG #include "scsi_debug.h" #endif @@ -225,6 +229,9 @@ #endif #ifdef CONFIG_SCSI_EATA EATA, +#endif +#ifdef CONFIG_SCSI_AM53C974 + AM53C974, #endif #ifdef CONFIG_SCSI_DEBUG SCSI_DEBUG, diff -u --recursive --new-file v1.3.58/linux/drivers/sound/sb16_dsp.c linux/drivers/sound/sb16_dsp.c --- v1.3.58/linux/drivers/sound/sb16_dsp.c Tue Jan 23 21:15:48 1996 +++ linux/drivers/sound/sb16_dsp.c Tue Jan 23 22:31:47 1996 @@ -410,8 +410,8 @@ audio_devs[my_dev]->dmachan1 = dsp_16bit ? dma16 : dma8; dsp_count = 0; dsp_cleanup (); - sb_dsp_command (0xd0); /* Halt DMA until trigger() is called */ trigger_bits = 0; + sb_dsp_command (0xd4); return 0; } @@ -421,20 +421,14 @@ audio_devs[my_dev]->dmachan1 = dsp_16bit ? dma16 : dma8; dsp_count = 0; dsp_cleanup (); - sb_dsp_command (0xd0); /* Halt DMA until trigger() is called */ trigger_bits = 0; + sb_dsp_command (0xd4); return 0; } static void sb16_dsp_trigger (int dev, int bits) { - if (bits != 0) - bits = 1; - - if (bits == trigger_bits) /* No change */ - return; - trigger_bits = bits; if (!bits) diff -u --recursive --new-file v1.3.58/linux/drivers/sound/sb_dsp.c linux/drivers/sound/sb_dsp.c --- v1.3.58/linux/drivers/sound/sb_dsp.c Tue Jan 23 21:15:48 1996 +++ linux/drivers/sound/sb_dsp.c Tue Jan 23 22:30:38 1996 @@ -1378,26 +1378,23 @@ initialize_ProSonic16 (); } -long -sb_dsp_init (long mem_start, struct address_info *hw_config) +static void +dsp_get_vers (struct address_info *hw_config) { int i; - int ess_major = 0, ess_minor = 0; - int mixer_type = 0; + unsigned long flags; + save_flags (flags); + cli (); sb_osp = hw_config->osp; sbc_major = sbc_minor = 0; - sb_dsp_command (0xe1); /* - * Get version - */ + sb_dsp_command (0xe1); /* Get version */ - for (i = 1000; i; i--) + for (i = 100000; i; i--) { if (inb (DSP_DATA_AVAIL) & 0x80) - { /* - * wait for Data Ready - */ + { if (sbc_major == 0) sbc_major = inb (DSP_READ); else @@ -1407,10 +1404,28 @@ } } } + restore_flags (flags); +} + +long +sb_dsp_init (long mem_start, struct address_info *hw_config) +{ + int i; + int ess_major = 0, ess_minor = 0; + + int mixer_type = 0; + + dsp_get_vers (hw_config); + + if (sbc_major == 0) + { + sb_reset_dsp (); + dsp_get_vers (hw_config); + } if (sbc_major == 0) { - printk ("\n\nFailed to get SB version (%x) - possible I/O conflict\n\n", + printk ("\n\nFailed to get SB version (%x) - possible I/O conflict?\n\n", inb (DSP_DATA_AVAIL)); sbc_major = 1; } diff -u --recursive --new-file v1.3.58/linux/fs/buffer.c linux/fs/buffer.c --- v1.3.58/linux/fs/buffer.c Tue Jan 23 21:15:49 1996 +++ linux/fs/buffer.c Tue Jan 23 20:47:33 1996 @@ -998,13 +998,12 @@ unsigned long flags; save_flags(flags); - cli(); while (reuse_list) { + cli(); bh = reuse_list; reuse_list = bh->b_next_free; restore_flags(flags); put_unused_buffer_head(bh); - cli(); } } @@ -1062,7 +1061,7 @@ return NULL; } -static int bread_page(unsigned long address, kdev_t dev, int b[], int size) +int brw_page(int rw, unsigned long address, kdev_t dev, int b[], int size, int bmap) { struct buffer_head *bh, *prev, *next, *arr[MAX_BUF_PER_PAGE]; int block, nr; @@ -1085,35 +1084,55 @@ next->b_blocknr = block; next->b_count = 1; next->b_flushtime = 0; - clear_bit(BH_Dirty, &next->b_state); - clear_bit(BH_Req, &next->b_state); set_bit(BH_Uptodate, &next->b_state); - - if (!block) { + + /* When we use bmap, we define block zero to represent + a hole. ll_rw_page, however, may legitimately + access block zero, and we need to distinguish the + two cases. + */ + if (bmap && !block) { memset(next->b_data, 0, size); + next->b_count--; continue; } tmp = get_hash_table(dev, block, size); if (tmp) { if (!buffer_uptodate(tmp)) { - ll_rw_block(READ, 1, &tmp); + if (rw == READ) + ll_rw_block(READ, 1, &tmp); wait_on_buffer(tmp); } - memcpy(next->b_data, tmp->b_data, size); + if (rw == READ) + memcpy(next->b_data, tmp->b_data, size); + else { + memcpy(tmp->b_data, next->b_data, size); + set_bit(BH_Dirty, &tmp->b_state); + } brelse(tmp); + next->b_count--; continue; } - clear_bit(BH_Uptodate, &next->b_state); + if (rw == READ) + clear_bit(BH_Uptodate, &next->b_state); + else + set_bit(BH_Dirty, &next->b_state); arr[nr++] = next; } while (prev = next, (next = next->b_this_page) != NULL); prev->b_this_page = bh; if (nr) - ll_rw_block(READ, nr, arr); + ll_rw_block(rw, nr, arr); else { page->locked = 0; page->uptodate = 1; wake_up(&page->wait); + next = bh; + do { + next->b_next_free = reuse_list; + reuse_list = next; + next = next->b_this_page; + } while (next != bh); } ++current->maj_flt; return 0; @@ -1138,6 +1157,64 @@ clear_bit(BH_Uptodate, &bh->b_state); } +void unlock_buffer(struct buffer_head * bh) +{ + struct buffer_head *tmp; + unsigned long flags; + struct page *page; + + clear_bit(BH_Lock, &bh->b_state); + wake_up(&bh->b_wait); + + if (!test_bit(BH_FreeOnIO, &bh->b_state)) + return; + page = mem_map + MAP_NR(bh->b_data); + if (!page->locked) { + printk ("Whoops: unlock_buffer: " + "async io complete on unlocked page\n"); + return; + } + if (bh->b_count != 1) { + printk ("Whoops: unlock_buffer: b_count != 1 on async io.\n"); + return; + } + /* Async buffer_heads are here only as labels for IO, and get + thrown away once the IO for this page is complete. IO is + deemed complete once all buffers have been visited + (b_count==0) and are now unlocked. */ + bh->b_count--; + for (tmp = bh; tmp=tmp->b_this_page, tmp!=bh; ) { + if (test_bit(BH_Lock, &tmp->b_state) || tmp->b_count) + return; + } + + /* OK, go ahead and complete the async IO on this page. */ + save_flags(flags); + page->locked = 0; + wake_up(&page->wait); + cli(); + tmp = bh; + do { + if (!test_bit(BH_FreeOnIO, &tmp->b_state)) { + printk ("Whoops: unlock_buffer: " + "async IO mismatch on page.\n"); + restore_flags(flags); + return; + } + tmp->b_next_free = reuse_list; + reuse_list = tmp; + clear_bit(BH_FreeOnIO, &tmp->b_state); + tmp = tmp->b_this_page; + } while (tmp != bh); + restore_flags(flags); + if (page->free_after) { + extern int nr_async_pages; + nr_async_pages--; + page->free_after = 0; + free_page(page_address(page)); + } +} + /* * Generic "readpage" function for block devices that have the normal * bmap functionality. This is most of the block device filesystems. @@ -1167,37 +1244,10 @@ /* IO start */ page->count++; address = page_address(page); - bread_page(address, inode->i_dev, nr, inode->i_sb->s_blocksize); + brw_page(READ, address, inode->i_dev, nr, inode->i_sb->s_blocksize, 1); free_page(address); return 0; } - -#if 0 -/* - * bwrite_page writes a page out to the buffer cache and/or the physical device. - * It's used for mmap writes (the same way bread_page() is used for mmap reads). - */ -void bwrite_page(unsigned long address, kdev_t dev, int b[], int size) -{ - struct buffer_head * bh[MAX_BUF_PER_PAGE]; - int i, j; - - for (i=0, j=0; jb_data, (void *) address, size); - mark_buffer_uptodate(bh[i], 1); - mark_buffer_dirty(bh[i], 0); - brelse(bh[i]); - } else - memset((void *) address, 0, size); /* ???!?!! */ - } -} -#endif /* * Try to increase the number of buffers available: the size argument diff -u --recursive --new-file v1.3.58/linux/fs/proc/procfs_syms.c linux/fs/proc/procfs_syms.c --- v1.3.58/linux/fs/proc/procfs_syms.c Tue Jan 23 21:15:51 1996 +++ linux/fs/proc/procfs_syms.c Tue Jan 23 21:50:18 1996 @@ -1,8 +1,16 @@ -#include #include #include #include +/* + * This is all required so that if we load all of scsi as a module, + * that the scsi code will be able to talk to the /proc/scsi handling + * in the procfs. + */ +extern int (* dispatch_scsi_info_ptr) (int ino, char *buffer, char **start, + off_t offset, int length, int inout); +extern struct inode_operations proc_scsi_inode_operations; + static struct symbol_table procfs_syms = { /* Should this be surrounded with "#ifdef CONFIG_MODULES" ? */ #include @@ -12,11 +20,14 @@ X(generate_cluster), X(proc_net_inode_operations), X(proc_net), -#ifdef CONFIG_SCSI /* Ugh... */ + + /* + * This is required so that if we load scsi later, that the + * scsi code can attach to /proc/scsi in the correct manner. + */ X(proc_scsi), X(proc_scsi_inode_operations), X(dispatch_scsi_info_ptr), -#endif #include }; diff -u --recursive --new-file v1.3.58/linux/fs/proc/root.c linux/fs/proc/root.c --- v1.3.58/linux/fs/proc/root.c Sun Jan 14 16:30:14 1996 +++ linux/fs/proc/root.c Tue Jan 23 21:12:45 1996 @@ -297,11 +297,7 @@ 64, &proc_self_inode_operations, }); proc_register(&proc_root, &proc_net); - -#ifdef CONFIG_SCSI proc_register(&proc_root, &proc_scsi); -#endif - proc_register(&proc_root, &proc_sys_root); #ifdef CONFIG_DEBUG_MALLOC diff -u --recursive --new-file v1.3.58/linux/include/linux/blk.h linux/include/linux/blk.h --- v1.3.58/linux/include/linux/blk.h Tue Jan 23 21:15:52 1996 +++ linux/include/linux/blk.h Wed Jan 24 00:20:30 1996 @@ -13,14 +13,14 @@ #define NR_REQUEST 64 /* - * This is used in the elevator algorithm: Note that - * reads always go before writes. This is natural: reads - * are much more time-critical than writes. + * This is used in the elevator algorithm. We don't prioritise reads + * over writes any more --- although reads are more time-critical than + * writes, by treating them equally we increase filesystem throughput. + * This turns out to give better overall performance. -- sct */ #define IN_ORDER(s1,s2) \ -((s1)->cmd < (s2)->cmd || ((s1)->cmd == (s2)->cmd && \ ((s1)->rq_dev < (s2)->rq_dev || (((s1)->rq_dev == (s2)->rq_dev && \ -(s1)->sector < (s2)->sector))))) +(s1)->sector < (s2)->sector))) /* * These will have to be changed to be aware of different buffer diff -u --recursive --new-file v1.3.58/linux/include/linux/fs.h linux/include/linux/fs.h --- v1.3.58/linux/include/linux/fs.h Tue Jan 23 21:15:52 1996 +++ linux/include/linux/fs.h Mon Jan 22 22:16:07 1996 @@ -598,8 +598,8 @@ extern int generic_readpage(struct inode *, struct page *); extern int generic_file_read(struct inode *, struct file *, char *, int); extern int generic_mmap(struct inode *, struct file *, struct vm_area_struct *); +extern int brw_page(int, unsigned long, kdev_t, int [], int, int); -extern void bwrite_page(unsigned long addr,kdev_t dev,int b[],int size); extern void put_super(kdev_t dev); unsigned long generate_cluster(kdev_t dev, int b[], int size); extern kdev_t ROOT_DEV; diff -u --recursive --new-file v1.3.58/linux/include/linux/locks.h linux/include/linux/locks.h --- v1.3.58/linux/include/linux/locks.h Tue Jan 23 21:15:52 1996 +++ linux/include/linux/locks.h Tue Jan 23 20:47:33 1996 @@ -4,6 +4,9 @@ #ifndef _LINUX_MM_H #include #endif +#ifndef _LINUX_PAGEMAP_H +#include +#endif /* * Unlocked, temporary IO buffer_heads gets moved to the reuse_list @@ -29,39 +32,8 @@ __wait_on_buffer(bh); } -extern inline void unlock_buffer(struct buffer_head * bh) -{ - struct buffer_head *tmp = bh; - int page_locked = 0; - unsigned long flags; - - clear_bit(BH_Lock, &bh->b_state); - wake_up(&bh->b_wait); - do { - if (test_bit(BH_Lock, &tmp->b_state)) { - page_locked = 1; - break; - } - tmp=tmp->b_this_page; - } while (tmp && tmp != bh); - save_flags(flags); - if (!page_locked) { - struct page *page = mem_map + MAP_NR(bh->b_data); - page->locked = 0; - wake_up(&page->wait); - tmp = bh; - cli(); - do { - if (test_bit(BH_FreeOnIO, &tmp->b_state)) { - tmp->b_next_free = reuse_list; - reuse_list = tmp; - clear_bit(BH_FreeOnIO, &tmp->b_state); - } - tmp = tmp->b_this_page; - } while (tmp && tmp != bh); - restore_flags(flags); - } -} +void unlock_buffer(struct buffer_head *); + /* * super-block locking. Again, interrupts may only unlock diff -u --recursive --new-file v1.3.58/linux/include/linux/mm.h linux/include/linux/mm.h --- v1.3.58/linux/include/linux/mm.h Tue Jan 23 21:15:53 1996 +++ linux/include/linux/mm.h Mon Jan 22 22:45:24 1996 @@ -122,7 +122,8 @@ error:1, referenced:1, locked:1, - unused:3, + free_after:1, + unused:2, reserved:1; struct wait_queue *wait; struct page *next; diff -u --recursive --new-file v1.3.58/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h --- v1.3.58/linux/include/linux/proc_fs.h Tue Jan 23 21:15:53 1996 +++ linux/include/linux/proc_fs.h Tue Jan 23 21:34:30 1996 @@ -118,6 +118,7 @@ PROC_SCSI_ULTRASTOR, PROC_SCSI_7000FASST, PROC_SCSI_EATA2X, + PROC_SCSI_AM53C974, PROC_SCSI_SSC, PROC_SCSI_SCSI_DEBUG, PROC_SCSI_NOT_PRESENT, diff -u --recursive --new-file v1.3.58/linux/include/linux/swap.h linux/include/linux/swap.h --- v1.3.58/linux/include/linux/swap.h Sun Jan 14 16:30:14 1996 +++ linux/include/linux/swap.h Mon Jan 22 22:16:07 1996 @@ -12,6 +12,8 @@ #define SWP_USED 1 #define SWP_WRITEOK 3 +#define SWAP_CLUSTER_MAX 32 + struct swap_info_struct { unsigned int flags; kdev_t swap_device; @@ -20,6 +22,8 @@ unsigned char * swap_lockmap; int lowest_bit; int highest_bit; + int cluster_next; + int cluster_nr; int prio; /* swap priority */ int pages; unsigned long max; @@ -28,6 +32,7 @@ extern int nr_swap_pages; extern int nr_free_pages; +extern int nr_async_pages; extern int min_free_pages; extern int free_pages_low; extern int free_pages_high; @@ -40,17 +45,17 @@ /* linux/ipc/shm.c */ extern int shm_swap (int, unsigned long); -/* linux/mm/swap_clock.c */ -extern int try_to_free_page(int, unsigned long); +/* linux/mm/vmscan.c */ +extern int try_to_free_page(int, unsigned long, int); -/* linux/mm/swap_io.c */ -extern void rw_swap_page(int, unsigned long, char *); +/* linux/mm/page_io.c */ +extern void rw_swap_page(int, unsigned long, char *, int); #define read_swap_page(nr,buf) \ - rw_swap_page(READ,(nr),(buf)) + rw_swap_page(READ,(nr),(buf),1) #define write_swap_page(nr,buf) \ - rw_swap_page(WRITE,(nr),(buf)) + rw_swap_page(WRITE,(nr),(buf),1) -/* linux/mm/swap_mman.c */ +/* linux/mm/page_alloc.c */ extern void swap_in(struct task_struct *, struct vm_area_struct *, pte_t *, unsigned long, int); diff -u --recursive --new-file v1.3.58/linux/init/main.c linux/init/main.c --- v1.3.58/linux/init/main.c Tue Jan 23 21:15:54 1996 +++ linux/init/main.c Tue Jan 23 21:34:30 1996 @@ -70,6 +70,7 @@ extern void aha152x_setup(char *str, int *ints); extern void aha1542_setup(char *str, int *ints); extern void aic7xxx_setup(char *str, int *ints); +extern void AM53C974_setup(char *str, int *ints); extern void BusLogic_Setup(char *str, int *ints); extern void fdomain_setup(char *str, int *ints); extern void NCR53c406a_setup(char *str, int *ints); @@ -238,6 +239,9 @@ #endif #ifdef CONFIG_SCSI_BUSLOGIC { "BusLogic=", BusLogic_Setup}, +#endif +#ifdef CONFIG_SCSI_AM53C974 + { "AM53C974=", AM53C974_setup}, #endif #ifdef CONFIG_SCSI_NCR53C406A { "ncr53c406a=", NCR53c406a_setup}, diff -u --recursive --new-file v1.3.58/linux/kernel/ksyms.c linux/kernel/ksyms.c --- v1.3.58/linux/kernel/ksyms.c Tue Jan 23 21:15:54 1996 +++ linux/kernel/ksyms.c Tue Jan 23 21:12:17 1996 @@ -86,19 +86,6 @@ #include #endif -#ifndef CONFIG_SCSI -#if defined(CONFIG_PROC_FS) -/* - * This is all required so that if we load all of scsi as a module, - * that the scsi code will be able to talk to the /proc/scsi handling - * in the procfs. - */ -extern int (* dispatch_scsi_info_ptr) (int ino, char *buffer, char **start, - off_t offset, int length, int inout); -extern struct inode_operations proc_scsi_inode_operations; -#endif -#endif - extern char *get_options(char *str, int *ints); extern void set_device_ro(int dev,int flag); extern struct file_operations * get_blkfops(unsigned int); @@ -210,6 +197,7 @@ X(__bforget), X(ll_rw_block), X(__wait_on_buffer), + X(unlock_buffer), X(dcache_lookup), X(dcache_add), X(aout_core_dump), @@ -435,14 +423,6 @@ */ X(gendisk_head), X(resetup_one_dev), -#if defined(CONFIG_PROC_FS) - /* - * This is required so that if we load scsi later, that the - * scsi code can attach to /proc/scsi in the correct manner. - */ - X(proc_scsi_inode_operations), - X(dispatch_scsi_info_ptr), -#endif #endif /* Added to make file system as module */ X(set_writetime), diff -u --recursive --new-file v1.3.58/linux/mm/filemap.c linux/mm/filemap.c --- v1.3.58/linux/mm/filemap.c Tue Jan 23 21:15:55 1996 +++ linux/mm/filemap.c Tue Jan 23 20:47:33 1996 @@ -168,8 +168,7 @@ if (page) { unsigned long addr; - if (!page->uptodate) - sleep_on(&page->wait); + wait_on_page(page); addr = page_address(page); memcpy((void *) ((pos & ~PAGE_MASK) + addr), buf, count); free_page(addr); diff -u --recursive --new-file v1.3.58/linux/mm/page_alloc.c linux/mm/page_alloc.c --- v1.3.58/linux/mm/page_alloc.c Sun Jan 14 16:30:16 1996 +++ linux/mm/page_alloc.c Mon Jan 22 22:16:07 1996 @@ -181,7 +181,7 @@ return 0; } restore_flags(flags); - if (priority != GFP_BUFFER && try_to_free_page(priority, limit)) + if (priority != GFP_BUFFER && try_to_free_page(priority, limit, 1)) goto repeat; return 0; } diff -u --recursive --new-file v1.3.58/linux/mm/page_io.c linux/mm/page_io.c --- v1.3.58/linux/mm/page_io.c Sun Jan 14 16:30:16 1996 +++ linux/mm/page_io.c Mon Jan 22 22:16:07 1996 @@ -28,11 +28,12 @@ static struct wait_queue * lock_queue = NULL; -void rw_swap_page(int rw, unsigned long entry, char * buf) +void rw_swap_page(int rw, unsigned long entry, char * buf, int wait) { unsigned long type, offset; struct swap_info_struct * p; - + struct page *page; + type = SWP_TYPE(entry); if (type >= nr_swapfiles) { printk("Internal error: bad swap-device\n"); @@ -58,8 +59,17 @@ kstat.pswpin++; else kstat.pswpout++; + page = mem_map + MAP_NR(buf); + wait_on_page(page); if (p->swap_device) { + if (!wait) { + page->count++; + page->free_after = 1; + nr_async_pages++; + } ll_rw_page(rw,p->swap_device,offset,buf); + if (wait) + wait_on_page(page); } else if (p->swap_file) { struct inode *swapf = p->swap_file; unsigned int zones[PAGE_SIZE/512]; @@ -105,4 +115,3 @@ printk("rw_swap_page: lock already cleared\n"); wake_up(&lock_queue); } - diff -u --recursive --new-file v1.3.58/linux/mm/swap.c linux/mm/swap.c --- v1.3.58/linux/mm/swap.c Tue Jan 23 21:15:55 1996 +++ linux/mm/swap.c Mon Jan 22 22:16:07 1996 @@ -42,6 +42,10 @@ int free_pages_low = 30; int free_pages_high = 40; +/* We track the number of pages currently being asynchronously swapped + out, so that we don't try to swap TOO many pages out at once */ +int nr_async_pages = 0; + /* * Constants for the page aging mechanism: the maximum age (actually, * the maximum "youthfulness"); the quanta by which pages rejuvinate diff -u --recursive --new-file v1.3.58/linux/mm/swapfile.c linux/mm/swapfile.c --- v1.3.58/linux/mm/swapfile.c Sun Jan 14 16:30:16 1996 +++ linux/mm/swapfile.c Mon Jan 22 22:16:07 1996 @@ -31,6 +31,46 @@ struct swap_info_struct swap_info[MAX_SWAPFILES]; +static inline int scan_swap_map(struct swap_info_struct *si) +{ + int offset; + /* + * We try to cluster swap pages by allocating them + * sequentially in swap. Once we've allocated + * SWAP_CLUSTER_MAX pages this way, however, we resort to + * first-free allocation, starting a new cluster. This + * prevents us from scattering swap pages all over the entire + * swap partition, so that we reduce overall disk seek times + * between swap pages. -- sct */ + if (si->cluster_nr) { + while (si->cluster_next <= si->highest_bit) { + offset = si->cluster_next++; + if (si->swap_map[offset]) + continue; + if (test_bit(offset, si->swap_lockmap)) + continue; + si->cluster_nr--; + goto got_page; + } + } + si->cluster_nr = SWAP_CLUSTER_MAX; + for (offset = si->lowest_bit; offset <= si->highest_bit ; offset++) { + if (si->swap_map[offset]) + continue; + if (test_bit(offset, si->swap_lockmap)) + continue; + si->lowest_bit = offset; +got_page: + si->swap_map[offset] = 1; + nr_swap_pages--; + if (offset == si->highest_bit) + si->highest_bit--; + si->cluster_next = offset; + return offset; + } + return 0; +} + unsigned long get_swap_page(void) { struct swap_info_struct * p; @@ -44,26 +84,17 @@ while (1) { p = &swap_info[type]; if ((p->flags & SWP_WRITEOK) == SWP_WRITEOK) { - for (offset = p->lowest_bit; offset <= p->highest_bit ; offset++) { - if (p->swap_map[offset]) - continue; - if (test_bit(offset, p->swap_lockmap)) - continue; - p->swap_map[offset] = 1; - nr_swap_pages--; - if (offset == p->highest_bit) - p->highest_bit--; - p->lowest_bit = offset; - entry = SWP_ENTRY(type,offset); - - type = swap_info[type].next; - if (type < 0 || p->prio != swap_info[type].prio) { - swap_list.next = swap_list.head; - } else { - swap_list.next = type; - } - return entry; + offset = scan_swap_map(p); + if (!offset) + continue; + entry = SWP_ENTRY(type,offset); + type = swap_info[type].next; + if (type < 0 || p->prio != swap_info[type].prio) { + swap_list.next = swap_list.head; + } else { + swap_list.next = type; } + return entry; } type = p->next; if (!wrapped) { @@ -380,6 +411,7 @@ p->swap_lockmap = NULL; p->lowest_bit = 0; p->highest_bit = 0; + p->cluster_nr = 0; p->max = 1; p->next = -1; if (swap_flags & SWAP_FLAG_PREFER) { @@ -399,7 +431,8 @@ if (S_ISBLK(swap_inode->i_mode)) { p->swap_device = swap_inode->i_rdev; - + set_blocksize(p->swap_device, PAGE_SIZE); + filp.f_inode = swap_inode; filp.f_mode = 3; /* read write */ error = blkdev_open(swap_inode, &filp); diff -u --recursive --new-file v1.3.58/linux/mm/vmscan.c linux/mm/vmscan.c --- v1.3.58/linux/mm/vmscan.c Tue Jan 23 21:15:55 1996 +++ linux/mm/vmscan.c Mon Jan 22 22:16:08 1996 @@ -5,7 +5,7 @@ * * Swap reorganised 29.12.95, Stephen Tweedie. * kswapd added: 7.1.96 sct - * Version: $Id: vmscan.c,v 1.3.2.3 1996/01/17 02:43:11 linux Exp $ + * Version: $Id: vmscan.c,v 1.4.2.2 1996/01/20 18:22:47 linux Exp $ */ #include @@ -68,7 +68,7 @@ * have died while we slept). */ static inline int try_to_swap_out(struct task_struct * tsk, struct vm_area_struct* vma, - unsigned long address, pte_t * page_table, unsigned long limit) + unsigned long address, pte_t * page_table, unsigned long limit, int wait) { pte_t pte; unsigned long entry; @@ -114,7 +114,7 @@ set_pte(page_table, __pte(entry)); invalidate_page(vma, address); tsk->nswap++; - write_swap_page(entry, (char *) page); + rw_swap_page(WRITE, entry, (char *) page, wait); } free_page(page); return 1; /* we slept: the process may not exist any more */ @@ -154,7 +154,7 @@ */ static inline int swap_out_pmd(struct task_struct * tsk, struct vm_area_struct * vma, - pmd_t *dir, unsigned long address, unsigned long end, unsigned long limit) + pmd_t *dir, unsigned long address, unsigned long end, unsigned long limit, int wait) { pte_t * pte; unsigned long pmd_end; @@ -176,7 +176,7 @@ do { int result; tsk->swap_address = address + PAGE_SIZE; - result = try_to_swap_out(tsk, vma, address, pte, limit); + result = try_to_swap_out(tsk, vma, address, pte, limit, wait); if (result) return result; address += PAGE_SIZE; @@ -186,7 +186,7 @@ } static inline int swap_out_pgd(struct task_struct * tsk, struct vm_area_struct * vma, - pgd_t *dir, unsigned long address, unsigned long end, unsigned long limit) + pgd_t *dir, unsigned long address, unsigned long end, unsigned long limit, int wait) { pmd_t * pmd; unsigned long pgd_end; @@ -206,7 +206,7 @@ end = pgd_end; do { - int result = swap_out_pmd(tsk, vma, pmd, address, end, limit); + int result = swap_out_pmd(tsk, vma, pmd, address, end, limit, wait); if (result) return result; address = (address + PMD_SIZE) & PMD_MASK; @@ -216,7 +216,7 @@ } static int swap_out_vma(struct task_struct * tsk, struct vm_area_struct * vma, - pgd_t *pgdir, unsigned long start, unsigned long limit) + pgd_t *pgdir, unsigned long start, unsigned long limit, int wait) { unsigned long end; @@ -227,7 +227,7 @@ end = vma->vm_end; while (start < end) { - int result = swap_out_pgd(tsk, vma, pgdir, start, end, limit); + int result = swap_out_pgd(tsk, vma, pgdir, start, end, limit, wait); if (result) return result; start = (start + PGDIR_SIZE) & PGDIR_MASK; @@ -236,7 +236,7 @@ return 0; } -static int swap_out_process(struct task_struct * p, unsigned long limit) +static int swap_out_process(struct task_struct * p, unsigned long limit, int wait) { unsigned long address; struct vm_area_struct* vma; @@ -257,7 +257,7 @@ address = vma->vm_start; for (;;) { - int result = swap_out_vma(p, vma, pgd_offset(p->mm, address), address, limit); + int result = swap_out_vma(p, vma, pgd_offset(p->mm, address), address, limit, wait); if (result) return result; vma = vma->vm_next; @@ -269,7 +269,7 @@ return 0; } -static int swap_out(unsigned int priority, unsigned long limit) +static int swap_out(unsigned int priority, unsigned long limit, int wait) { static int swap_task; int loop, counter; @@ -308,7 +308,7 @@ } if (!--p->swap_cnt) swap_task++; - switch (swap_out_process(p, limit)) { + switch (swap_out_process(p, limit, wait)) { case 0: if (p->swap_cnt) swap_task++; @@ -327,7 +327,7 @@ * to be. This works out OK, because we now do proper aging on page * contents. */ -int try_to_free_page(int priority, unsigned long limit) +int try_to_free_page(int priority, unsigned long limit, int wait) { static int state = 0; int i=6; @@ -343,7 +343,7 @@ return 1; state = 2; default: - if (swap_out(i, limit)) + if (swap_out(i, limit, wait)) return 1; state = 0; } while(i--); @@ -359,7 +359,7 @@ int kswapd(void *unused) { int i; - char *revision="$Revision: 1.3.2.3 $", *s, *e; + char *revision="$Revision: 1.4.2.2 $", *s, *e; current->session = 1; current->pgrp = 1; @@ -400,7 +400,7 @@ swapstats.wakeups++; /* Do the background pageout: */ for (i=0; i < kswapd_ctl.maxpages; i++) - try_to_free_page(GFP_KERNEL, ~0UL); + try_to_free_page(GFP_KERNEL, ~0UL, 0); } } @@ -410,8 +410,8 @@ void swap_tick(void) { - if (nr_free_pages < free_pages_low || - (nr_free_pages < free_pages_high && + if ((nr_free_pages + nr_async_pages) < free_pages_low || + ((nr_free_pages + nr_async_pages) < free_pages_high && jiffies >= next_swap_jiffies)) { if (!kswapd_awake && kswapd_ctl.maxpages > 0) { wake_up(&kswapd_wait); diff -u --recursive --new-file v1.3.58/linux/scripts/Configure linux/scripts/Configure --- v1.3.58/linux/scripts/Configure Tue Jan 23 21:15:56 1996 +++ linux/scripts/Configure Tue Jan 23 21:41:34 1996 @@ -263,7 +263,8 @@ while :; do readln "$1 ($2) [$def] " "$def" case "$ans" in - [1-9] | [1-9][0-9] | [1-9][0-9][0-9] | [1-9][0-9][0-9][0-9] ) + -1 | [1-9] | [1-9][0-9] |\ + [1-9][0-9][0-9] | [1-9][0-9][0-9][0-9] | [1-9][0-9][0-9][0-9][0-9]) define_int "$2" "$ans" break;; * ) help "$2" @@ -278,7 +279,7 @@ # function define_hex () { echo "$1=$2" >>$CONFIG - echo "#define $1 0x$2" >>$CONFIG_H + echo "#define $1 0x${2#*[x,X]}" >>$CONFIG_H eval "$1=$2" } @@ -289,10 +290,15 @@ # function hex () { def=$(eval echo "\${$2:-$3}") + def=${def#*[x,X]} while :; do readln "$1 ($2) [$def] " "$def" + ans=${ans#*[x,X]} case "$ans" in - [1-9] | [1-9][0-9] | [1-9][0-9][0-9] | [1-9][0-9][0-9][0-9] ) + [0-9,a-f,A-F] |\ + [0-9,a-f,A-F][0-9,a-f,A-F] |\ + [0-9,a-f,A-F][0-9,a-f,A-F][0-9,a-f,A-F] |\ + [0-9,a-f,A-F][0-9,a-f,A-F][0-9,a-f,A-F][0-9,a-f,A-F])\ define_hex "$2" "$ans" break;; * ) help "$2" diff -u --recursive --new-file v1.3.58/linux/scripts/depend.awk linux/scripts/depend.awk --- v1.3.58/linux/scripts/depend.awk Wed Nov 8 07:11:47 1995 +++ linux/scripts/depend.awk Tue Jan 23 21:20:34 1996 @@ -37,6 +37,7 @@ hasdep=0 hasconfig=0 needsconfig=0 + incomment=0 if(!(TOPDIR=ENVIRON["TOPDIR"])) { print "Environment variable TOPDIR is not set" exit 1 @@ -48,6 +49,30 @@ } } +# eliminate comments +{ + # remove all comments fully contained on a single line + gsub("\\/\\*.*\\*\\/", "") + if (incomment) { + if ($0 ~ /\*\//) { + incomment = 0; + gsub(".*\\*\\/", "") + } else { + next + } + } else { + # start of multi-line comment + if ($0 ~ /\/\*/) + { + incomment = 1; + sub("\\/\\*.*", "") + } else if ($0 ~ /\*\//) { + incomment = 0; + sub(".*\\*\\/", "") + } + } +} + /^[ ]*#[ ]*if.*[^A-Za-z_]CONFIG_/ { needsconfig=1 if (!hasconfig) { @@ -64,6 +89,7 @@ hasdep=0 hasconfig=0 needsconfig=0 + incomment=0 cmd="" LASTFILE=FILENAME depname=FILENAME