Commit c04591a2 authored by Linus Torvalds's avatar Linus Torvalds

Import 1.3.59

parent b6e9c719
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 <Use default setting>
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.
VERSION = 1
PATCHLEVEL = 3
SUBLEVEL = 58
SUBLEVEL = 59
ARCH = i386
......
......@@ -16,7 +16,7 @@ all: kernel.o head.o
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
......
......@@ -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_NET_VENDOR_3COM=y
# 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
......
......@@ -436,16 +436,19 @@ static void make_request(int major,int rw, struct buffer_head * bh)
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 @@ void ll_rw_page(int rw, kdev_t dev, unsigned long page, char * buffer)
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
......
......@@ -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,29 +114,40 @@ extern unsigned long get_cmos_time(void);
*/
/*
* 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
*/
......@@ -193,27 +208,27 @@ extern unsigned long get_cmos_time(void);
#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 @@ extern unsigned long get_cmos_time(void);
#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 apm_bios_struct * user_list = NULL;
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 @@ static int apm_setup(void)
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)
......
......@@ -65,7 +65,6 @@ static void mouse_interrupt(int irq, struct pt_regs *regs)
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 @@ static int read_mouse(struct inode * inode, struct file * file, char * buffer, i
int dx;
int dy;
unsigned char buttons;
/* long flags; */
if (count < 3)
return -EINVAL;
......@@ -186,7 +186,8 @@ static int read_mouse(struct inode * inode, struct file * file, char * buffer, i
* 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 @@ static int read_mouse(struct inode * inode, struct file * file, char * buffer, i
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);
......
......@@ -77,12 +77,9 @@ extern unsigned long video_mem_term;
* 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 @@ static inline void scr_writew(unsigned short val, unsigned short * addr)
* 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);
}
}
......
/* 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 <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/ptrace.h>
#include <linux/errno.h>
#include <linux/in.h>
#include <linux/ioport.h>
#include <linux/malloc.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/bios32.h>
#include <linux/timer.h>
#include <asm/bitops.h>
#include <asm/io.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
/* 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:
*/
......@@ -29,6 +29,7 @@ if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then
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
......
......@@ -226,6 +226,14 @@ else
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
......
......@@ -68,6 +68,7 @@ extern int ni52_probe(struct device *);
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 @@ ethif_probe(struct device *dev)
return 1; /* ENXIO */
if (1
#if defined(CONFIG_VORTEX)
&& tc59x_probe(dev)
#endif
#if defined(CONFIG_SEEQ8005)
&& seeq8005_probe(dev)
#endif
......
......@@ -325,7 +325,7 @@ int tok_probe(struct device *dev)
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;
......
#include <linux/config.h>
#include <linux/delay.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/bios32.h>
#include <linux/pci.h>
#include <linux/string.h>
#include <linux/blk.h>
#include <asm/io.h>
#include <asm/system.h>
#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 <marks@schooner.sys.hou.compaq.com>) */
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 <marks@schooner.sys.hou.compaq.com> */
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 <marks@schooner.sys.hou.compaq.com> */
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;
}
/* 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 <linux/scsicam.h>
/***************************************************************************************
* 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 <linux/pci.h> */
#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 */
......@@ -37,5 +37,6 @@ dep_tristate 'UltraStor SCSI support' CONFIG_SCSI_ULTRASTOR $CONFIG_SCSI
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
......@@ -121,6 +121,10 @@ else
endif
endif
ifeq ($(CONFIG_SCSI_AM53C974),y)
L_OBJS += AM53C974.o
endif
ifeq ($(CONFIG_SCSI_BUSLOGIC),y)
L_OBJS += BusLogic.o
else
......
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
......@@ -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 @@ void aha152x_intr(int irqno, struct pt_regs * regs)
aha152x_panic(shpnt, "unknown lun");
}
make_acklow(shpnt);
getphase(shpnt);
#if defined(DEBUG_QUEUES)
if(HOSTDATA(shpnt)->debug & debug_queues)
......@@ -1430,6 +1431,8 @@ void aha152x_intr(int irqno, struct pt_regs * regs)
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 @@ void aha152x_intr(int irqno, struct pt_regs * regs)
SETBITS(DMACNTRL0, INTEN);
return;
}
}
/* Check, if we aren't busy with a command */
if(!CURRENT_SC)
......@@ -1946,6 +1950,7 @@ void aha152x_intr(int irqno, struct pt_regs * regs)
printk("d+, ");
#endif
append_SC(&DISCONNECTED_SC, CURRENT_SC);
CURRENT_SC->SCp.phase |= 1<<16;
CURRENT_SC = NULL;
restore_flags(flags);
......
......@@ -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 @@ int aha152x_proc_info(char *buffer, char **start, off_t offset, int length, int
(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;
......
......@@ -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
......@@ -226,6 +230,9 @@ static Scsi_Host_Template builtin_scsi_hosts[] =
#ifdef CONFIG_SCSI_EATA
EATA,
#endif
#ifdef CONFIG_SCSI_AM53C974
AM53C974,
#endif
#ifdef CONFIG_SCSI_DEBUG
SCSI_DEBUG,
#endif
......
......@@ -410,8 +410,8 @@ sb16_dsp_prepare_for_input (int dev, int bsize, int bcount)
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 @@ sb16_dsp_prepare_for_output (int dev, int bsize, int bcount)
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)
......
......@@ -1378,26 +1378,23 @@ Jazz16_set_dma16 (int dma)
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 @@ sb_dsp_init (long mem_start, struct address_info *hw_config)
}
}
}
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;
}
......
......@@ -998,13 +998,12 @@ static inline void recover_reusable_buffer_heads(void)
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 @@ static struct buffer_head * create_buffers(unsigned long page, unsigned long siz
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 @@ static int bread_page(unsigned long address, kdev_t dev, int b[], int size)
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)) {
if (rw == READ)
ll_rw_block(READ, 1, &tmp);
wait_on_buffer(tmp);
}
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;
}
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 @@ void mark_buffer_uptodate(struct buffer_head * bh, int on)
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,38 +1244,11 @@ int generic_readpage(struct inode * inode, struct page * page)
/* 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; j<PAGE_SIZE ; i++, j+= size) {
bh[i] = NULL;
if (b[i])
bh[i] = getblk(dev, b[i], size);
}
for (i=0, j=0; j<PAGE_SIZE ; i++, j += size, address += size) {
if (bh[i]) {
memcpy(bh[i]->b_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
* is used to determine what kind of buffers we want.
......
#include <linux/config.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
/*
* 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 <linux/symtab_begin.h>
......@@ -12,11 +20,14 @@ static struct symbol_table procfs_syms = {
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 <linux/symtab_end.h>
};
......
......@@ -297,11 +297,7 @@ void proc_root_init(void)
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
......
......@@ -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
......
......@@ -598,8 +598,8 @@ extern struct buffer_head * breada(kdev_t dev,int block, int size,
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;
......
......@@ -4,6 +4,9 @@
#ifndef _LINUX_MM_H
#include <linux/mm.h>
#endif
#ifndef _LINUX_PAGEMAP_H
#include <linux/pagemap.h>
#endif
/*
* Unlocked, temporary IO buffer_heads gets moved to the reuse_list
......@@ -29,39 +32,8 @@ extern inline void lock_buffer(struct buffer_head * bh)
__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;
void unlock_buffer(struct buffer_head *);
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);
}
}
/*
* super-block locking. Again, interrupts may only unlock
......
......@@ -122,7 +122,8 @@ typedef struct page {
error:1,
referenced:1,
locked:1,
unused:3,
free_after:1,
unused:2,
reserved:1;
struct wait_queue *wait;
struct page *next;
......
......@@ -118,6 +118,7 @@ enum scsi_directory_inos {
PROC_SCSI_ULTRASTOR,
PROC_SCSI_7000FASST,
PROC_SCSI_EATA2X,
PROC_SCSI_AM53C974,
PROC_SCSI_SSC,
PROC_SCSI_SCSI_DEBUG,
PROC_SCSI_NOT_PRESENT,
......
......@@ -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 @@ struct swap_info_struct {
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 @@ struct swap_info_struct {
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 @@ struct sysinfo;
/* 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);
......
......@@ -70,6 +70,7 @@ extern void generic_NCR5380_setup(char *str, int *intr);
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);
......@@ -239,6 +240,9 @@ struct {
#ifdef CONFIG_SCSI_BUSLOGIC
{ "BusLogic=", BusLogic_Setup},
#endif
#ifdef CONFIG_SCSI_AM53C974
{ "AM53C974=", AM53C974_setup},
#endif
#ifdef CONFIG_SCSI_NCR53C406A
{ "ncr53c406a=", NCR53c406a_setup},
#endif
......
......@@ -86,19 +86,6 @@ extern unsigned char aux_device_present, kbd_read_mask;
#include <linux/smp.h>
#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 @@ struct symbol_table symbol_table = {
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 @@ struct symbol_table symbol_table = {
*/
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),
......
......@@ -168,8 +168,7 @@ void update_vm_cache(struct inode * inode, unsigned long pos, const char * buf,
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);
......
......@@ -181,7 +181,7 @@ unsigned long __get_free_pages(int priority, unsigned long order, unsigned long
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;
}
......
......@@ -28,10 +28,11 @@
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) {
......@@ -58,8 +59,17 @@ void rw_swap_page(int rw, unsigned long entry, char * buf)
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 @@ void rw_swap_page(int rw, unsigned long entry, char * buf)
printk("rw_swap_page: lock already cleared\n");
wake_up(&lock_queue);
}
......@@ -42,6 +42,10 @@ int min_free_pages = 20;
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
......
......@@ -31,6 +31,46 @@ static struct {
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,18 +84,10 @@ unsigned long get_swap_page(void)
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))
offset = scan_swap_map(p);
if (!offset)
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;
......@@ -64,7 +96,6 @@ unsigned long get_swap_page(void)
}
return entry;
}
}
type = p->next;
if (!wrapped) {
if (type < 0 || p->prio != swap_info[type].prio) {
......@@ -380,6 +411,7 @@ asmlinkage int sys_swapon(const char * specialfile, int swap_flags)
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,6 +431,7 @@ asmlinkage int sys_swapon(const char * specialfile, int swap_flags)
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 */
......
......@@ -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 <linux/mm.h>
......@@ -68,7 +68,7 @@ static void init_swap_timer(void);
* 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 @@ static inline int try_to_swap_out(struct task_struct * tsk, struct vm_area_struc
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 try_to_swap_out(struct task_struct * tsk, struct vm_area_struc
*/
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 @@ static inline int swap_out_pmd(struct task_struct * tsk, struct vm_area_struct *
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_pmd(struct task_struct * tsk, struct vm_area_struct *
}
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 @@ static inline int swap_out_pgd(struct task_struct * tsk, struct vm_area_struct *
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 inline int swap_out_pgd(struct task_struct * tsk, struct vm_area_struct *
}
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 @@ static int swap_out_vma(struct task_struct * tsk, struct vm_area_struct * vma,
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 @@ static int swap_out_vma(struct task_struct * tsk, struct vm_area_struct * vma,
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 @@ static int swap_out_process(struct task_struct * p, unsigned long limit)
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 @@ static int swap_out_process(struct task_struct * p, unsigned long limit)
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 @@ static int swap_out(unsigned int priority, unsigned long limit)
}
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 @@ static int swap_out(unsigned int priority, unsigned long limit)
* 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 @@ int try_to_free_page(int priority, unsigned long limit)
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 try_to_free_page(int priority, unsigned long limit)
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 @@ int kswapd(void *unused)
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 @@ int kswapd(void *unused)
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);
......
......@@ -263,7 +263,8 @@ function int () {
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 int () {
#
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 define_hex () {
#
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"
......
......@@ -37,6 +37,7 @@ BEGIN{
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 @@ BEGIN{
}
}
# 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 @@ BEGIN{
hasdep=0
hasconfig=0
needsconfig=0
incomment=0
cmd=""
LASTFILE=FILENAME
depname=FILENAME
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment